mirror of
https://github.com/NeuralNomadsAI/CodeNomad.git
synced 2026-10-21 13:54:39 +00:00
Recover pending Forms and Permissions through a fixed presence-owned RPC in the existing bundled plugin, without warming historical native Locations. Validate established execution-host origin, complete coverage, directory/Git ownership, WSL aliases and every placement before admitting capability. Keep generic RPC forwarding closed and preserve non-authoritative failures. Observe native compaction before event ownership routing and defer expensive legacy discovery before inventory reads and at forwarding. Preserve concurrent holds, bounded missed-end probes, exact settlement reconciliation grants, per-kind mutation fences, global/idle Forms and settled-request tombstones. Never replay ambiguous mutations. Share presence observations, not registrations or execution authority. Integrate current dev receipt and event-ownership changes. Reassert the native connection after permission receipt preparation and before granting settlement reconciliation or dispatching mutations. Reject overflowing raw JSON numbers, including unknown metadata, without altering native numeric codec strings. Use eight-directory UI batches with the unchanged request deadline and URL budget rather than weakening ownership checks or extending timeouts. Add scoped broker, reader, presence, proxy, compaction and UI lifecycle regressions plus an opt-in isolated native reader fixture. Independent Gatekeeper reviews are published on the PR; R1's numeric overflow finding is fixed and rechecked on the final head before merge. The private reader is qualified for native 2.0.22 / Effect rc.112 only. The global minimum and already-recommended 2.0.22 remain unchanged. Unsupported runtimes retain guarded legacy recovery. This mitigates CodeNomad's load amplification, not the upstream Bus issue or previously retained Locations; installed-application freeze resolution, deployment and daemon restart are not claimed. Preserve bootstrap-warming and bounded-scan limitations.
183 lines
13 KiB
JavaScript
183 lines
13 KiB
JavaScript
// Opt-in, in-process correctness proof. No listener, service discovery, user database or runtime mutation.
|
|
// Run with Bun and absolute paths: <published-2.0.22-study-directory> <fresh-output-directory>.
|
|
import assert from "node:assert/strict"
|
|
import fs from "node:fs/promises"
|
|
import path from "node:path"
|
|
import { createHash } from "node:crypto"
|
|
import { fileURLToPath, pathToFileURL } from "node:url"
|
|
import { build } from "esbuild"
|
|
|
|
const [study, output] = process.argv.slice(2)
|
|
assert(study && output && path.isAbsolute(study) && path.isAbsolute(output), "Supply absolute private package and fresh output directories")
|
|
assert(globalThis.Bun, "This isolated published Core fixture requires Bun")
|
|
await fs.mkdir(output) // Refuse overwriting an earlier receipt or fixture.
|
|
const repository = fileURLToPath(new URL("../", import.meta.url))
|
|
const packageRoot = path.join(study, "node_modules")
|
|
const packages = {}
|
|
for (const name of ["core", "server", "plugin", "client", "schema", "protocol", "util"]) {
|
|
const filename = path.join(packageRoot, "@opencode", name, "package.json")
|
|
const content = await fs.readFile(filename, "utf8")
|
|
const metadata = JSON.parse(content)
|
|
assert.equal(metadata.version, "2.0.22")
|
|
packages[name] = { version: metadata.version, packageSha256: createHash("sha256").update(content).digest("hex") }
|
|
}
|
|
assert.equal(JSON.parse(await fs.readFile(path.join(packageRoot, "effect/package.json"), "utf8")).version, "4.0.0-rc.112")
|
|
const setupReceipt = JSON.parse(await fs.readFile(path.join(study, "setup-receipt.json"), "utf8"))
|
|
const sourceReceipt = JSON.parse(await fs.readFile(path.join(study, "source-receipt.json"), "utf8"))
|
|
for (const entry of sourceReceipt.published) {
|
|
const content = await fs.readFile(path.join(packageRoot, "@opencode", entry.package, entry.file))
|
|
assert.equal(createHash("sha256").update(content).digest("hex"), entry.sha256, `Published implementation changed: ${entry.file}`)
|
|
}
|
|
const bundle = path.join(output, "plugin.mjs")
|
|
await build({
|
|
entryPoints: [path.join(repository, "packages/server/src/opencode/session-pruning/desktop-plugin.ts")],
|
|
outfile: bundle, bundle: true, platform: "node", format: "esm", target: "node22",
|
|
banner: { js: 'import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);' },
|
|
// The checkout's node_modules can lag its existing 2.0.22 lockfile. Use the already verified private packages, no install.
|
|
plugins: [{ name: "exact-existing-native-packages", setup(build) {
|
|
build.onResolve({ filter: /^(?:@opencode\/|effect(?:\/|$))/ }, async args => {
|
|
if (args.pluginData?.exactNative) return
|
|
return build.resolve(args.path, { resolveDir: study, kind: args.kind, pluginData: { exactNative: true } })
|
|
})
|
|
} }],
|
|
})
|
|
const localPlugin = path.join(output, "local-plugin")
|
|
const presence = path.join(output, "backend-presence")
|
|
const bootstrap = path.join(output, "bootstrap")
|
|
const idle = [1, 2, 3].map(n => path.join(output, `idle-${n}`))
|
|
for (const directory of [localPlugin, presence, bootstrap, ...idle]) await fs.mkdir(directory)
|
|
await fs.writeFile(path.join(localPlugin, "index.mjs"), `import {desktopPlugin} from ${JSON.stringify(pathToFileURL(bundle).href)}; export default desktopPlugin(${JSON.stringify(presence)});`)
|
|
for (const key of ["HOME", "USERPROFILE", "XDG_CONFIG_HOME", "XDG_CACHE_HOME", "XDG_DATA_HOME", "XDG_STATE_HOME", "OPENCODE_CONFIG_DIR"]) process.env[key] = output
|
|
process.env.OPENCODE_DISABLE_AUTOUPDATE = "true"
|
|
process.env.OPENCODE_DB = ":memory:"
|
|
const native = (name, file) => import(pathToFileURL(path.join(packageRoot, "@opencode", name, "dist", `${file}.js`)).href)
|
|
const { Context, Effect, Layer, RcMap, Logger } = await import(pathToFileURL(path.join(packageRoot, "effect/dist/index.js")).href)
|
|
const { HttpEffect, HttpRouter, HttpServer } = await import(pathToFileURL(path.join(packageRoot, "effect/dist/unstable/http/index.js")).href)
|
|
const { Global } = await native("util", "global")
|
|
const { Location } = await native("core", "location")
|
|
const { LocationServiceMap } = await native("core", "location-service-map")
|
|
const { Form } = await native("core", "form")
|
|
const { Permission } = await native("core", "permission")
|
|
const { Session } = await native("core", "session")
|
|
const { Plugin } = await native("core", "plugin")
|
|
const { Config } = await native("core", "config")
|
|
const { createRoutes } = await native("server", "routes")
|
|
const locations = Object.fromEntries(["data", "cache", "config", "state", "tmp", "bin", "log", "repos"].map(name => [name, path.join(output, name)]))
|
|
const ref = directory => Location.Ref.make({ directory })
|
|
const rpc = "/api/rpc/codenomad.pending-requests/snapshot"
|
|
const leaseA = path.join(presence, "abc-123.lease"), leaseB = path.join(presence, "def-456.lease")
|
|
const receipt = await Effect.runPromise(Effect.scoped(Effect.gen(function* () {
|
|
const configured = Layer.unwrap(Effect.gen(function* () {
|
|
const location = yield* Location.Service
|
|
return Config.layer({ project: false, global: false, content: JSON.stringify({ plugins: location.directory === bootstrap ? [localPlugin] : [] }) })
|
|
}))
|
|
const context = yield* Layer.build(createRoutes({
|
|
password: "isolated-stage1-only", database: { path: ":memory:" }, app: { version: packages.core.version },
|
|
models: { fetch: false }, config: { directory: locations.config, project: false, content: "{}" },
|
|
fs: { filewatcher: false }, events: { persist: false },
|
|
}, () => [], [Global.node.replace(Global.layerWith(locations)), Config.node.replace(Config.node.mapLayer(() => configured))]).pipe(Layer.provide(HttpServer.layerServices)))
|
|
const map = Context.get(context, LocationServiceMap.Service)
|
|
const keys = () => RcMap.keys(map.rcMap).pipe(Effect.map(keys => Array.from(keys).map(ref => ({ directory: ref.directory, workspaceID: ref.workspaceID }))))
|
|
const root = yield* map.contextEffect(ref(bootstrap))
|
|
yield* Context.get(root, Plugin.Service).awaitActivation
|
|
const rootSession = yield* Context.get(context, Session.Service).create({ location: ref(bootstrap), title: "Existing Promise RPC check" })
|
|
const seeded = []
|
|
for (const directory of idle) {
|
|
const location = ref(directory), loaded = yield* map.contextEffect(location)
|
|
yield* Context.get(loaded, Plugin.Service).awaitActivation
|
|
assert(!(yield* Context.get(loaded, Plugin.Service).list()).some(plugin => plugin.id === "codenomad-session-pruning"))
|
|
const session = yield* Context.get(context, Session.Service).create({ location, title: "Idle queue fixture", permissions: [{ action: "*", resource: "*", effect: "ask" }] })
|
|
const forms = Context.get(loaded, Form.Service)
|
|
const global = yield* forms.create({ sessionID: "global", title: "Global", fields: [{ key: "number", type: "number", minimum: -Infinity, maximum: Infinity, default: NaN }] })
|
|
const form = yield* forms.create({ sessionID: session.id, title: "Idle", fields: [{ key: "answer", type: "string" }] })
|
|
const permission = yield* Context.get(loaded, Permission.Service).ask({ sessionID: session.id, action: "stage1_fixture", resources: ["fixture-only"] })
|
|
assert.equal(permission.effect, "ask")
|
|
seeded.push({ directory, formIDs: [global.id, form.id], permissionID: permission.id })
|
|
}
|
|
const handler = Context.get(context, HttpRouter.HttpRouter).asHttpEffect().pipe(HttpEffect.toWebHandlerWith(context))
|
|
const requests = []
|
|
const request = (route = rpc, input = { directories: idle }, authorized = true, directory = bootstrap) => Effect.promise(async () => {
|
|
const url = new URL(route, "http://isolated.invalid")
|
|
url.searchParams.set("location[directory]", directory)
|
|
requests.push({ path: url.pathname, directory })
|
|
const response = await handler(new Request(url, { method: "POST", headers: {
|
|
"content-type": "application/json", ...(authorized ? { authorization: `Basic ${btoa("opencode:isolated-stage1-only")}` } : {}),
|
|
}, body: JSON.stringify({ input }) }))
|
|
const text = await response.text()
|
|
assert(text, `Empty HTTP ${response.status} from ${route}`)
|
|
return { status: response.status, body: JSON.parse(text) }
|
|
})
|
|
const waitStatus = Effect.fn("fixture.waitStatus")(function* (status) {
|
|
let last
|
|
for (let attempt = 0; attempt < 40; attempt++) {
|
|
const result = yield* request()
|
|
last = result
|
|
if (result.status === status) return result
|
|
yield* Effect.sleep("200 millis")
|
|
}
|
|
throw new Error(`Presence did not reconcile to HTTP ${status}: ${JSON.stringify({ last, plugins: (yield* Context.get(root, Plugin.Service).list()).filter(plugin => plugin.id === "codenomad-session-pruning") })}`)
|
|
})
|
|
const absent = yield* request()
|
|
assert.equal(absent.status, 400)
|
|
const absentPromise = yield* request("/api/rpc/codenomad.session-pruning/preview", { sessionID: rootSession.id, messageID: "msg_fixture_missing" })
|
|
assert.equal(absentPromise.status, 400)
|
|
yield* Effect.promise(() => fs.writeFile(leaseA, ""))
|
|
yield* waitStatus(200)
|
|
const promise = yield* request("/api/rpc/codenomad.session-pruning/preview", { sessionID: rootSession.id, messageID: "msg_fixture_missing" })
|
|
assert.equal(promise.status, 200)
|
|
assert.deepEqual(promise.body.output, { status: "blocked", reason: "unavailable" })
|
|
const cold = Array.from({ length: 100 }, (_, n) => path.join(output, `cold-${n}`))
|
|
const before = yield* keys()
|
|
const unauthorized = yield* request(rpc, { directories: idle }, false, cold[0])
|
|
assert.equal(unauthorized.status, 401)
|
|
const snapshots = []
|
|
for (const batch of [cold.slice(0, 60), cold.slice(60)]) {
|
|
const snapshot = yield* request(rpc, { directories: [bootstrap, ...idle, ...batch] })
|
|
assert.equal(snapshot.status, 200)
|
|
assert.equal(snapshot.body.output.originDirectory, bootstrap)
|
|
for (const item of seeded) {
|
|
const placement = snapshot.body.output.data.find(entry => entry.directory === item.directory)
|
|
assert.equal(placement.status, "complete")
|
|
assert.equal(placement.locations.length, 1)
|
|
assert.deepEqual(placement.locations[0].forms.map(form => form.id), item.formIDs)
|
|
assert.deepEqual(placement.locations[0].permissions.map(permission => permission.id), [item.permissionID])
|
|
const numeric = placement.locations[0].forms.find(form => form.sessionID === "global").fields[0]
|
|
assert.equal(numeric.minimum, "-Infinity")
|
|
assert.equal(numeric.maximum, "Infinity")
|
|
assert.equal(numeric.default, "NaN")
|
|
}
|
|
assert(batch.every(directory => snapshot.body.output.data.some(entry => entry.directory === directory && entry.status === "complete" && entry.locations.length === 0)))
|
|
snapshots.push(snapshot)
|
|
}
|
|
assert.deepEqual(yield* keys(), before)
|
|
assert.equal((yield* request(rpc, { directories: Array(65).fill(bootstrap) })).status, 400)
|
|
assert.equal((yield* request(rpc, { directories: [bootstrap], version: "2.0.22" })).status, 400)
|
|
const nonAuthoritative = yield* request(rpc, { directories: ["relative"] })
|
|
assert.equal(nonAuthoritative.status, 400)
|
|
assert.equal(nonAuthoritative.body.type, "unavailable")
|
|
assert.equal((yield* request(rpc, { directories: idle }, true, idle[0])).status, 400)
|
|
yield* Effect.promise(() => fs.writeFile(leaseB, ""))
|
|
const expired = new Date(Date.now() - 60_000)
|
|
yield* Effect.promise(() => fs.utimes(leaseA, expired, expired))
|
|
yield* Effect.sleep("2500 millis")
|
|
assert.equal((yield* request()).status, 200, "Another live backend must keep registration alive")
|
|
yield* Effect.promise(() => fs.utimes(leaseB, expired, expired))
|
|
yield* waitStatus(400)
|
|
assert.equal((yield* request("/api/rpc/codenomad.session-pruning/preview", { sessionID: rootSession.id, messageID: "msg_fixture_missing" })).status, 400)
|
|
yield* Effect.promise(() => fs.writeFile(leaseA, "returned"))
|
|
yield* waitStatus(200)
|
|
assert.equal((yield* request("/api/rpc/codenomad.session-pruning/preview", { sessionID: rootSession.id, messageID: "msg_fixture_missing" })).status, 200)
|
|
yield* Effect.promise(() => fs.unlink(leaseA))
|
|
yield* waitStatus(400)
|
|
assert.deepEqual(yield* keys(), before)
|
|
return { status: "passed", pid: process.pid, bun: Bun.version, nativePackages: packages, publishedIntegrity: setupReceipt, publishedSources: sourceReceipt.published,
|
|
transport: "authenticated unmodified published production HTTP router, in-process, no listener", database: ":memory:",
|
|
productionEntry: "packages/server/src/opencode/session-pruning/desktop-plugin.ts", bundleSha256: createHash("sha256").update(yield* Effect.promise(() => fs.readFile(bundle))).digest("hex"),
|
|
before, after: yield* keys(), coldCount: 100, coldConstructed: 0, seeded, snapshots, promise, nonAuthoritative,
|
|
lifecycle: { absent: true, present: true, multipleBackendsRetained: true, expired: true, returned: true, stoppedAgain: true },
|
|
separateBundledEffectCopy: "4.0.0-rc.112", unauthorizedStatus: unauthorized.status, requests }
|
|
})).pipe(Effect.provide(Logger.layer([Logger.make(() => {})]))))
|
|
await fs.writeFile(path.join(output, "receipt.json"), JSON.stringify(receipt, null, 2))
|
|
console.log(JSON.stringify({ status: receipt.status, before: receipt.before.length, after: receipt.after.length, coldConstructed: 0, forms: 6, globalForms: 3, permissions: 3, lifecycle: receipt.lifecycle, output }, null, 2))
|
|
// ponytail: all owned scopes and receipts are closed; don't retain native package process handles in a one-shot fixture.
|
|
process.exit(0)
|