@remnic/plugin-pi 9.3.724 → 9.3.726
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/publisher.js +27 -11
- package/dist/publisher.js.map +1 -1
- package/package.json +2 -2
package/dist/publisher.js
CHANGED
|
@@ -661,22 +661,38 @@ function snapshotFiles(paths) {
|
|
|
661
661
|
function restorePublishSnapshot(extensionRoot, rootExisted, snapshots) {
|
|
662
662
|
if (!rootExisted && !canCleanNewExtensionRoot(extensionRoot)) return;
|
|
663
663
|
for (const snapshot of snapshots) {
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
664
|
+
restoreOwnedFile(snapshot);
|
|
665
|
+
}
|
|
666
|
+
if (!rootExisted) {
|
|
667
|
+
removeEmptyDirectory(extensionRoot);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
function restoreOwnedFile(snapshot) {
|
|
671
|
+
if (!snapshot.existed) {
|
|
672
|
+
assertSafeExistingPath(snapshot.path);
|
|
673
|
+
fs.rmSync(snapshot.path, { force: true });
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
fs.mkdirSync(path.dirname(snapshot.path), { recursive: true });
|
|
677
|
+
const tmpPath = `${snapshot.path}.tmp-${process.pid}-${Date.now()}`;
|
|
678
|
+
try {
|
|
679
|
+
fs.writeFileSync(tmpPath, snapshot.content ?? Buffer.alloc(0), {
|
|
680
|
+
mode: snapshot.mode ?? 420
|
|
681
|
+
});
|
|
671
682
|
if (snapshot.mode !== void 0) {
|
|
672
683
|
try {
|
|
673
|
-
fs.chmodSync(
|
|
684
|
+
fs.chmodSync(tmpPath, snapshot.mode);
|
|
674
685
|
} catch {
|
|
675
686
|
}
|
|
676
687
|
}
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
688
|
+
rejectSymlinkPath(snapshot.path);
|
|
689
|
+
fs.renameSync(tmpPath, snapshot.path);
|
|
690
|
+
} catch (err) {
|
|
691
|
+
try {
|
|
692
|
+
if (fs.existsSync(tmpPath)) fs.unlinkSync(tmpPath);
|
|
693
|
+
} catch {
|
|
694
|
+
}
|
|
695
|
+
throw err;
|
|
680
696
|
}
|
|
681
697
|
}
|
|
682
698
|
function snapshotDirs(paths) {
|
package/dist/publisher.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/publisher.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport { spawnSync } from \"node:child_process\";\nimport path from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport os from \"node:os\";\n\nimport {\n type MemoryExtensionPublisher,\n type PublishContext,\n type PublishResult,\n type PublisherCapabilities,\n type TokenEntry,\n getConnectorToken,\n loadTokenStore,\n saveTokenStore,\n} from \"@remnic/core\";\n\nimport {\n resolveOmpAgentHome,\n resolveOmpConfigRoot,\n resolveOmpExtensionRoot,\n resolvePiAgentHome,\n resolvePiExtensionRoot,\n} from \"./paths.js\";\n\nconst DEFAULT_DAEMON_PORT = 4318;\nconst BASE_OWNED_FILES = [\"remnic.config.json\", \"index.ts\", \"README.md\"] as const;\nconst EXTENSION_OWNED_TEMP_FILE_SUFFIX = /\\.tmp-\\d+-\\d+$/u;\n\ntype FileSnapshot = {\n path: string;\n existed: boolean;\n content?: Buffer;\n mode?: number;\n};\n\ntype DirSnapshot = {\n path: string;\n existed: boolean;\n};\n\n/**\n * Host-specific parameters for a Pi-family memory extension publisher.\n *\n * The Remnic runtime extension is host-neutral (it only uses Pi's extension\n * hooks, which omp preserves as a superset), so the only things that vary\n * between hosts are *where* the extension is installed, *which* connector\n * token it uses, and *how* it is labelled. Everything else — atomic writes,\n * rollback, symlink guards, config merge — is shared.\n */\nexport interface HostPublisherDescriptor {\n readonly hostId: string;\n readonly connectorId: string;\n readonly displayName: string;\n readonly tokenGenerateHint: string;\n resolveAgentHome(env: NodeJS.ProcessEnv): string;\n resolveExtensionRoot(env: NodeJS.ProcessEnv): string;\n /**\n * Optional: every agent home `unpublish` should sweep for a stale extension,\n * beyond the one resolved from the current env. Hosts with env-sensitive\n * install locations (e.g. omp profiles) provide this so `remnic connectors\n * remove` cleans up even when the remove-time env differs from install time.\n */\n listRemovalAgentHomes?(env: NodeJS.ProcessEnv): string[];\n}\n\nconst PI_HOST: HostPublisherDescriptor = {\n hostId: \"pi\",\n connectorId: \"pi\",\n displayName: \"Pi Coding Agent\",\n tokenGenerateHint: \"remnic token generate pi\",\n resolveAgentHome: resolvePiAgentHome,\n resolveExtensionRoot: resolvePiExtensionRoot,\n};\n\nconst OMP_HOST: HostPublisherDescriptor = {\n hostId: \"omp\",\n connectorId: \"omp\",\n displayName: \"Oh My Pi (omp)\",\n tokenGenerateHint: \"remnic token generate omp\",\n resolveAgentHome: resolveOmpAgentHome,\n resolveExtensionRoot: resolveOmpExtensionRoot,\n listRemovalAgentHomes: ompRemovalAgentHomes,\n};\n\n/**\n * Every omp agent home a stale extension might live under, so `unpublish` cleans\n * up regardless of the profile/env active at remove time: the env-resolved home,\n * the base `<configRoot>/agent`, an explicit `PI_CODING_AGENT_DIR`, and every\n * existing `<configRoot>/profiles/<name>/agent`. Symlinked profile dirs are\n * skipped defensively.\n */\nfunction ompRemovalAgentHomes(env: NodeJS.ProcessEnv): string[] {\n const homes = new Set<string>([resolveOmpAgentHome(env)]);\n const configRoot = resolveOmpConfigRoot(env);\n homes.add(path.join(configRoot, \"agent\"));\n\n const explicit = env.PI_CODING_AGENT_DIR?.trim();\n if (explicit) homes.add(path.resolve(explicit));\n\n const profilesDir = path.join(configRoot, \"profiles\");\n let entries: fs.Dirent[] = [];\n try {\n entries = fs.readdirSync(profilesDir, { withFileTypes: true });\n } catch {\n entries = [];\n }\n for (const entry of entries) {\n if (entry.isDirectory() && !entry.isSymbolicLink()) {\n homes.add(path.join(profilesDir, entry.name, \"agent\"));\n }\n }\n return [...homes];\n}\n\n/**\n * Shared publisher for Pi-family hosts. Concrete hosts (Pi, omp) subclass this\n * with a {@link HostPublisherDescriptor}; the install/rollback machinery is\n * identical across hosts.\n */\nexport class HostMemoryExtensionPublisher implements MemoryExtensionPublisher {\n static readonly capabilities: PublisherCapabilities = {\n // Real publisher: writes host config + wrapper + readme, just no\n // instructions.md/skills/citation/read-path-template artefacts. The\n // explicit flag prevents the parity gate from mis-inferring \"all flags\n // false ⇒ stub\" for this host (#1518).\n isStub: false,\n instructionsMd: false,\n skillsFolder: false,\n citationFormat: false,\n readPathTemplate: false,\n };\n\n protected constructor(private readonly host: HostPublisherDescriptor) {}\n\n /**\n * File basenames this publisher owns inside the extension root. The shared\n * set is config + wrapper + readme; subclasses add host-specific files\n * (e.g. omp's pre-bundle loader + package manifest). Used for snapshot,\n * atomic-write rollback, and unpublish cleanup.\n */\n protected get ownedFileNames(): readonly string[] {\n return BASE_OWNED_FILES;\n }\n\n /**\n * Directory names this publisher owns inside the extension root (build\n * outputs). Recursively removed on unpublish and on publish rollback when\n * newly created.\n */\n protected get ownedDirNames(): readonly string[] {\n return [];\n }\n\n /**\n * Whether the generated wrapper must use a bun-buildable import specifier\n * (relative path) instead of a file:// URL. omp pre-bundles the wrapper with\n * `bun build`, which cannot resolve file:// specifiers; pi loads the wrapper\n * directly via tsx and keeps the file:// URL.\n */\n protected get usesBundledWrapper(): boolean {\n return false;\n }\n\n /**\n * Hook for subclasses to write host-specific files and run install-time\n * build steps after the shared config/wrapper/readme are written. Runs\n * inside the publish try-block: a throw triggers full rollback.\n */\n protected finalizePublish(\n _ctx: PublishContext,\n _extensionRoot: string,\n _paths: { configPath: string; wrapperPath: string; pluginPiDistPath: string },\n ): void {\n // No-op by default; subclasses override.\n }\n\n get hostId(): string {\n return this.host.hostId;\n }\n\n async resolveExtensionRoot(env?: NodeJS.ProcessEnv): Promise<string> {\n return this.host.resolveExtensionRoot(env ?? process.env);\n }\n\n async isHostAvailable(): Promise<boolean> {\n // Pi-family agents auto-discover extensions from their agent extensions\n // directory. The directory can be created before the agent has been\n // launched, so availability should not block first-time installation.\n return true;\n }\n\n async renderInstructions(ctx: PublishContext): Promise<string> {\n const namespace = ctx.config.namespace ?? \"default\";\n const daemonUrl = resolveDaemonUrl(ctx);\n return [\n `# Remnic for ${this.host.displayName}`,\n \"\",\n `Remnic provides memory, retrieval, observation, MCP tools, and long-context compaction coordination for ${this.host.displayName}.`,\n \"\",\n \"## Installed Capabilities\",\n \"\",\n \"- Recall relevant Remnic context in the `context` hook before agent turns.\",\n '- Observe user, assistant, and tool messages with `sourceFormat: \"pi\"`.',\n \"- Coordinate `session_before_compact` with Remnic LCM flush and checkpoint recording.\",\n \"- Register Remnic MCP tools as host tools when daemon authentication is configured.\",\n \"- Persist lightweight dedupe state in custom entries via `appendEntry`.\",\n \"\",\n \"## Runtime\",\n \"\",\n `- Remnic daemon: \\`${daemonUrl}\\``,\n `- Namespace: \\`${namespace}\\``,\n `- Memory directory: \\`${ctx.config.memoryDir}\\``,\n \"\",\n \"The private `remnic.config.json` file stores the daemon URL, namespace, and connector auth token with owner-only permissions.\",\n ].join(\"\\n\");\n }\n\n async publish(ctx: PublishContext): Promise<PublishResult> {\n const extensionRoot = await this.resolveExtensionRoot();\n const agentHome = this.host.resolveAgentHome(process.env);\n assertSafeExtensionRoot(extensionRoot, agentHome);\n const filesWritten: string[] = [];\n const skipped: string[] = [];\n\n ctx.log.info(`Publishing ${this.host.displayName} memory extension to ${extensionRoot}`);\n\n const ownedFilePaths = this.ownedFileNames.map((fileName) => path.join(extensionRoot, fileName));\n const configPath = ownedFilePaths[0];\n const wrapperPath = ownedFilePaths[1];\n const readmePath = ownedFilePaths[2];\n const pluginPiDistPath = resolveExtensionModulePath();\n const rootExisted = fs.existsSync(extensionRoot);\n const fileSnapshots = snapshotFiles(ownedFilePaths);\n const dirSnapshots = snapshotDirs(\n this.ownedDirNames.map((dirName) => path.join(extensionRoot, dirName)),\n );\n const priorTokenEntry =\n ctx.rollbackTokenEntry === undefined\n ? snapshotTokenEntry(this.host.connectorId)\n : cloneTokenEntry(ctx.rollbackTokenEntry);\n\n const token = getConnectorToken(this.host.connectorId);\n if (!token) {\n skipped.push(\n `auth token unavailable; run \\`${this.host.tokenGenerateHint}\\` and reinstall the connector`,\n );\n }\n\n try {\n const priorConfig = readPriorConfig(configPath);\n const config: Record<string, unknown> = {\n recallMode: \"auto\",\n recallTopK: 8,\n recallBudgetChars: 12000,\n recallEnabled: true,\n observeEnabled: true,\n observeSkipExtraction: false,\n compactionEnabled: true,\n mcpToolsEnabled: true,\n statusEnabled: true,\n requestTimeoutMs: 60000,\n startupRequestTimeoutMs: 1000,\n ...priorConfig,\n remnicDaemonUrl: resolveDaemonUrl(ctx),\n };\n if (token) {\n config.authToken = token;\n }\n if (ctx.config.namespace) {\n config.namespace = ctx.config.namespace;\n }\n\n mkdirExtensionRoot(extensionRoot, agentHome);\n\n atomicWriteFile(configPath, `${JSON.stringify(config, null, 2)}\\n`, 0o600);\n filesWritten.push(configPath);\n\n atomicWriteFile(\n wrapperPath,\n renderWrapper(\n pluginPiDistPath,\n configPath,\n this.usesBundledWrapper ? extensionRoot : undefined,\n ),\n 0o644,\n );\n filesWritten.push(wrapperPath);\n\n atomicWriteFile(readmePath, `${await this.renderInstructions(ctx)}\\n`, 0o644);\n filesWritten.push(readmePath);\n\n this.finalizePublish(ctx, extensionRoot, { configPath, wrapperPath, pluginPiDistPath });\n for (let i = BASE_OWNED_FILES.length; i < ownedFilePaths.length; i++) {\n filesWritten.push(ownedFilePaths[i]);\n }\n } catch (err) {\n try {\n // Remove newly created owned dirs (e.g. dist-bundle) BEFORE\n // restorePublishSnapshot's removeEmptyDirectory check, otherwise a\n // first-time publish that created dist-bundle would leave an empty\n // extension root behind on rollback.\n restoreDirSnapshots(dirSnapshots);\n restorePublishSnapshot(extensionRoot, rootExisted, fileSnapshots);\n } catch (restoreErr) {\n ctx.log.warn(\n `${this.host.displayName} extension rollback failed: ${restoreErr instanceof Error ? restoreErr.message : String(restoreErr)}`,\n );\n }\n // A failed omp pre-bundle (bun missing or `bun build` failing) is\n // recoverable: the runtime loader self-heals dist-bundle on first load,\n // and the connector token is already committed by the CLI. Rolling it\n // back here would leave the connector registered with no credential and\n // block a non-`--force` reinstall (AGENTS.md #14 — don't destroy\n // committed state before the new state is confirmed). File/dir rollback\n // above still runs, so a failed first-time publish still cleans its root.\n if (!(err instanceof OmpPreBundleError)) {\n try {\n restoreTokenEntry(priorTokenEntry, this.host.connectorId);\n } catch (tokenErr) {\n ctx.log.warn(\n `${this.host.displayName} connector token rollback failed: ${tokenErr instanceof Error ? tokenErr.message : String(tokenErr)}`,\n );\n }\n }\n throw err;\n }\n\n return {\n hostId: this.host.hostId,\n extensionRoot,\n filesWritten,\n skipped,\n };\n }\n\n async unpublish(): Promise<void> {\n const agentHomes = this.host.listRemovalAgentHomes\n ? this.host.listRemovalAgentHomes(process.env)\n : [this.host.resolveAgentHome(process.env)];\n\n const ownedFileNames = this.ownedFileNames;\n const ownedDirNames = this.ownedDirNames;\n const seen = new Set<string>();\n for (const agentHome of agentHomes) {\n const extensionRoot = path.join(path.resolve(agentHome), \"extensions\", \"remnic\");\n if (seen.has(extensionRoot)) continue;\n seen.add(extensionRoot);\n if (!fs.existsSync(extensionRoot)) continue;\n\n assertSafeExtensionRoot(extensionRoot, agentHome);\n const removableFiles = removableOwnedExtensionFiles(\n extensionOwnedUnpublishPaths(extensionRoot, ownedFileNames),\n );\n for (const filePath of removableFiles) {\n fs.rmSync(filePath, { force: true });\n }\n for (const dirName of ownedDirNames) {\n const dirPath = path.join(extensionRoot, dirName);\n let stat: fs.Stats;\n try {\n stat = fs.lstatSync(dirPath);\n } catch (err) {\n if (err && typeof err === \"object\" && \"code\" in err && err.code === \"ENOENT\") continue;\n throw err;\n }\n if (stat.isSymbolicLink()) {\n throw new Error(`Extension path must not be a symlink: ${dirPath}`);\n }\n if (stat.isDirectory()) {\n fs.rmSync(dirPath, { recursive: true, force: true });\n }\n }\n removeEmptyDirectory(extensionRoot);\n }\n }\n}\n\n/** Publisher for upstream Pi (`~/.pi/agent/extensions/remnic`). */\nexport class PiMemoryExtensionPublisher extends HostMemoryExtensionPublisher {\n constructor() {\n super(PI_HOST);\n }\n}\n\n/**\n * Marks a failure originating from the omp pre-bundle step (bun missing, or the\n * `bun build` itself failing). {@link HostMemoryExtensionPublisher.publish}\n * catches this and rolls back the written files but SKIPS the connector-token\n * rollback: the pre-bundle runs after the install (config + wrapper + token) is\n * already committed, and the runtime loader self-heals `dist-bundle` on first\n * load, so destroying the just-generated token would leave the connector\n * registered with no credential and block a non-`--force` reinstall\n * (AGENTS.md #14 — don't destroy committed state before the new state is\n * confirmed). The message is preserved verbatim so existing `/requires \\`bun\\`/`\n * and `/bun build failed/` assertions still match.\n */\nclass OmpPreBundleError extends Error {}\n\n/** Publisher for Oh My Pi / omp (`~/.omp/agent/extensions/remnic`). */\nexport class OmpMemoryExtensionPublisher extends HostMemoryExtensionPublisher {\n constructor() {\n super(OMP_HOST);\n }\n\n protected get ownedFileNames(): readonly string[] {\n return [...BASE_OWNED_FILES, \"loader.js\", \"package.json\", \"postinstall-bundle.cjs\"];\n }\n\n protected get ownedDirNames(): readonly string[] {\n return [\"dist-bundle\"];\n }\n\n // omp pre-bundles index.ts with `bun build`; the wrapper must use a relative\n // import specifier (bun's bundler cannot resolve file:// URLs).\n protected get usesBundledWrapper(): boolean {\n return true;\n }\n\n protected finalizePublish(\n ctx: PublishContext,\n extensionRoot: string,\n paths: { configPath: string; wrapperPath: string; pluginPiDistPath: string },\n ): void {\n // Resolve once so the install-time build and the generated loader share the\n // same bun path — a loader that hardcodes \"bun\" cannot self-heal when bun\n // is reachable only via REMNIC_OMP_BUN_BIN or a common absolute install\n // path that is not on omp's PATH at runtime.\n const bunBin = resolveBunBinary();\n if (!bunBin) {\n // OmpPreBundleError so publish() keeps the connector token intact (see\n // the class doc); the runtime loader self-heals the bundle once bun is\n // installed.\n throw new OmpPreBundleError(\n \"Remnic omp extension requires `bun` to pre-bundle the extension: omp's embedded \" +\n \"runtime cannot resolve bare npm specifiers from the extension's node_modules. \" +\n \"Install bun from https://bun.sh, then re-run `remnic connectors install omp`.\",\n );\n }\n\n const loaderPath = path.join(extensionRoot, \"loader.js\");\n const packageJsonPath = path.join(extensionRoot, \"package.json\");\n\n const postinstallPath = path.join(extensionRoot, \"postinstall-bundle.cjs\");\n\n atomicWriteFile(loaderPath, renderOmpLoader(paths.pluginPiDistPath, bunBin), 0o644);\n // Cross-platform postinstall helper (Node-only) so npm's default cmd.exe\n // shell on Windows re-bundles after `npm install`; the POSIX one-liner it\n // replaces only ran under bash.\n atomicWriteFile(postinstallPath, renderOmpPostinstall(bunBin), 0o644);\n atomicWriteFile(packageJsonPath, renderOmpPackageJson(), 0o644);\n\n try {\n this.runBundleBuild(ctx, extensionRoot, bunBin);\n } catch (err) {\n // OmpPreBundleError so publish() keeps the connector token intact (see\n // the class doc); the runtime loader self-heals the bundle on next load.\n const message = err instanceof Error ? err.message : String(err);\n throw new OmpPreBundleError(message);\n }\n }\n\n /**\n * Pre-bundles the omp extension with `bun build` so omp's embedded runtime\n * never resolves bare npm specifiers (e.g. @sinclair/typebox) from the\n * extension's node_modules at load time. The bundle is written to a temp\n * directory and swapped into dist-bundle/ on success. The pre-existing\n * dist-bundle is renamed aside (not removed) before the swap, so a failure\n * during the final rename restores the previously working bundle rather than\n * leaving the install with no bundle at all.\n *\n * Override in tests to skip the real bun invocation.\n */\n protected runBundleBuild(ctx: PublishContext, extensionRoot: string, bunBin: string): void {\n const sourceEntry = path.join(extensionRoot, \"index.ts\");\n const tmpOutDir = path.join(extensionRoot, `.dist-bundle.tmp-${process.pid}-${Date.now()}`);\n const finalOutDir = path.join(extensionRoot, \"dist-bundle\");\n\n const result = spawnSync(bunBin, [\"build\", sourceEntry, \"--target=bun\", `--outdir=${tmpOutDir}`], {\n cwd: extensionRoot,\n encoding: \"utf-8\",\n });\n\n if (result.error || result.status !== 0) {\n try {\n fs.rmSync(tmpOutDir, { recursive: true, force: true });\n } catch {\n // best-effort tmp cleanup\n }\n const detail =\n (typeof result.stderr === \"string\" ? result.stderr.trim() : \"\") ||\n (result.error instanceof Error ? result.error.message : \"\") ||\n `bun exited with status ${result.status ?? \"null\"}`;\n throw new Error(\n `Remnic omp extension: bun build failed (${detail}). Resolve the error and re-run ` +\n \"`remnic connectors install omp`, or build manually with \" +\n \"`bun build index.ts --target=bun --outdir=dist-bundle` inside \" +\n `${extensionRoot}.`,\n );\n }\n\n // Swap the freshly built bundle into place without ever leaving the install\n // bundle-less. Rename the existing dist-bundle aside, move the new one in,\n // and only then discard the backup. On any failure mid-swap, restore the\n // backup so the previously working bundle survives (the publish-level\n // rollback only removes newly created dirs — it never restores a removed\n // dist-bundle, so we must not remove it here).\n let backupDir: string | null = null;\n try {\n if (fs.existsSync(finalOutDir)) {\n backupDir = path.join(extensionRoot, `.dist-bundle.bak-${process.pid}-${Date.now()}`);\n fs.renameSync(finalOutDir, backupDir);\n }\n fs.renameSync(tmpOutDir, finalOutDir);\n if (backupDir) {\n try {\n fs.rmSync(backupDir, { recursive: true, force: true });\n } catch {\n // best-effort backup cleanup; leaving it does not break the install\n }\n }\n } catch (err) {\n try {\n if (fs.existsSync(tmpOutDir)) fs.rmSync(tmpOutDir, { recursive: true, force: true });\n } catch {\n // best-effort tmp cleanup\n }\n // Restore the previously working bundle if we moved it aside and the\n // final swap did not land.\n if (backupDir && fs.existsSync(backupDir) && !fs.existsSync(finalOutDir)) {\n try {\n fs.renameSync(backupDir, finalOutDir);\n } catch {\n // best-effort restore; the loader's self-heal rebuilds on next start\n }\n }\n throw new Error(\n `Remnic omp extension: failed to finalize bundle output — ${err instanceof Error ? err.message : String(err)}.`,\n );\n }\n\n ctx.log.info(`Pre-bundled omp extension into ${finalOutDir}`);\n }\n}\n\nfunction extensionOwnedPaths(extensionRoot: string, ownedFileNames: readonly string[]): string[] {\n return ownedFileNames.map((fileName) => path.join(extensionRoot, fileName));\n}\n\nfunction extensionOwnedUnpublishPaths(extensionRoot: string, ownedFileNames: readonly string[]): string[] {\n const ownedBaseNames = new Set(ownedFileNames);\n const ownedPaths = extensionOwnedPaths(extensionRoot, ownedFileNames);\n for (const fileName of fs.readdirSync(extensionRoot)) {\n const match = EXTENSION_OWNED_TEMP_FILE_SUFFIX.exec(fileName);\n if (match && ownedBaseNames.has(fileName.slice(0, match.index))) {\n ownedPaths.push(path.join(extensionRoot, fileName));\n }\n }\n return ownedPaths;\n}\n\nfunction resolveDaemonUrl(ctx: PublishContext): string {\n if (ctx.config.daemonUrl && ctx.config.daemonUrl.trim().length > 0) {\n return trimTrailingSlashes(ctx.config.daemonUrl.trim());\n }\n return `http://127.0.0.1:${ctx.config.daemonPort ?? DEFAULT_DAEMON_PORT}`;\n}\n\nfunction trimTrailingSlashes(value: string): string {\n let end = value.length;\n while (end > 0 && value.charCodeAt(end - 1) === 47) end -= 1;\n return value.slice(0, end);\n}\n\nfunction resolveExtensionModulePath(): string {\n const moduleDir = path.dirname(fileURLToPath(import.meta.url));\n const built = path.join(moduleDir, \"index.js\");\n if (fs.existsSync(built)) return built;\n\n const source = path.join(moduleDir, \"index.ts\");\n if (fs.existsSync(source)) return source;\n\n return built;\n}\n\n/**\n * Resolves the import specifier the omp wrapper uses to reach the\n * `@remnic/plugin-pi` dist entry from the generated `index.ts`. omp pre-bundles\n * that wrapper with `bun build`, whose bundler cannot resolve `file://`\n * specifiers (\"Could not resolve: file://…\" on Bun 1.2–1.3, verified), so the\n * specifier must be a relative path. On Windows, when the extension directory\n * and the plugin-pi install sit on different drives, `path.relative` cannot\n * express a relative path and returns an absolute drive path (e.g. `D:\\…`);\n * prefixing `./` then yields an invalid module specifier that fails `bun build`\n * with a cryptic error. Detect that layout and fail fast with an actionable\n * message instead. (Cross-drive omp installs are unsupported because neither a\n * relative specifier nor a `file://` URL is acceptable to `bun build`.) Drive\n * roots are compared case-insensitively so a same-drive Windows install is not\n * falsely rejected when the agent home and the plugin-pi install report the\n * drive letter in different casing (`C:\\\\` vs `c:\\\\`).\n *\n * Exported so the cross-drive guard can be exercised on non-Windows hosts via\n * `path.win32`.\n */\nexport function resolveOmpWrapperImportSpecifier(\n extensionModulePath: string,\n wrapperDir: string,\n pathApi: typeof path = path,\n): string {\n // Windows drive roots are case-insensitive: `C:\\\\…` (e.g. from the omp agent\n // home) and `c:\\\\…` (e.g. from fileURLToPath(import.meta.url)) are the SAME\n // drive, and path.win32.relative yields a valid relative specifier between\n // them. Compare the parsed roots case-insensitively so a same-drive install\n // isn't falsely rejected as \"different drives\". posix roots (`/`) are\n // unaffected by toLowerCase().\n if (pathApi.parse(wrapperDir).root.toLowerCase() !== pathApi.parse(extensionModulePath).root.toLowerCase()) {\n throw new Error(\n \"Remnic omp extension cannot pre-bundle: the extension directory \" +\n `(${wrapperDir}) and the @remnic/plugin-pi install (${extensionModulePath}) ` +\n \"are on different drives, so no relative import specifier can be generated \" +\n \"for `bun build` (and `bun build` cannot resolve a `file://` specifier). \" +\n \"Move the omp agent home and the Remnic install onto the same drive.\",\n );\n }\n let rel = pathApi.relative(wrapperDir, extensionModulePath);\n rel = rel.split(pathApi.sep).join(\"/\");\n return rel.startsWith(\".\") ? rel : `./${rel}`;\n}\n\nfunction renderWrapper(\n extensionModulePath: string,\n configPath: string,\n wrapperDir?: string,\n): string {\n // omp pre-bundles this entry with `bun build`, whose bundler cannot resolve\n // `file://` specifiers — it exits with \"Could not resolve: file://...\" on\n // Bun 1.2–1.3 (verified). When the wrapper will be bun-built, emit a relative\n // specifier resolved against the wrapper's directory; bun, tsx, and Node ESM\n // all resolve relative specifiers. For tsx-loaded wrappers (pi) the file://\n // URL is retained.\n let importSpecifier: string;\n if (wrapperDir) {\n importSpecifier = resolveOmpWrapperImportSpecifier(extensionModulePath, wrapperDir);\n } else {\n importSpecifier = pathToFileURL(extensionModulePath).href;\n }\n return [\n `import { createRemnicPiExtension } from ${JSON.stringify(importSpecifier)};`,\n \"\",\n `export default createRemnicPiExtension({ configPath: ${JSON.stringify(configPath)} });`,\n \"\",\n ].join(\"\\n\");\n}\n\n/**\n * Generates the self-healing `loader.js` that omp loads via the package\n * manifest's `omp.extensions` entry. It mtime-compares the pre-bundled\n * `dist-bundle/index.js` against `index.ts` and the underlying @remnic/plugin-pi\n * dist, rebuilds via `bun build` when stale (e.g. after an `npm update`), then\n * imports the self-contained bundle so omp's embedded runtime never resolves\n * bare npm specifiers at load time.\n */\nfunction renderOmpLoader(pluginPiDistPath: string, bunBin: string): string {\n return [\n \"// Auto-generated by Remnic's OmpMemoryExtensionPublisher.\",\n \"// omp's embedded runtime cannot resolve bare npm specifiers from this\",\n \"// extension's node_modules, so we pre-bundle with `bun build` and import\",\n \"// the self-contained bundle here. Rebuilt automatically when index.ts or\",\n \"// the underlying @remnic/plugin-pi dist changes.\",\n \"\",\n 'import { existsSync, renameSync, rmSync, statSync } from \"node:fs\";',\n 'import { spawnSync } from \"node:child_process\";',\n 'import { dirname, join } from \"node:path\";',\n 'import { fileURLToPath, pathToFileURL } from \"node:url\";',\n \"\",\n 'const here = dirname(fileURLToPath(import.meta.url));',\n 'const bundleDir = join(here, \"dist-bundle\");',\n 'const bundleEntry = join(bundleDir, \"index.js\");',\n 'const sourceEntry = join(here, \"index.ts\");',\n `const pluginPiEntry = ${JSON.stringify(pluginPiDistPath)};`,\n // Reuse the bun path resolved at install time (REMNIC_OMP_BUN_BIN, PATH,\n // or a common absolute location). Fall back to \"bun\" on PATH if the\n // resolved path no longer exists (e.g. the extension tree was moved), so\n // self-healing still works when bun is reachable only via PATH.\n `const resolvedBunBin = ${JSON.stringify(bunBin)};`,\n 'const bunForRebuild = resolvedBunBin && existsSync(resolvedBunBin) ? resolvedBunBin : \"bun\";',\n \"\",\n \"function bundleIsStale() {\",\n \" if (!existsSync(bundleEntry)) return true;\",\n \" const bundleMtime = statSync(bundleEntry).mtimeMs;\",\n \" if (existsSync(sourceEntry) && bundleMtime < statSync(sourceEntry).mtimeMs) return true;\",\n \" if (pluginPiEntry && existsSync(pluginPiEntry) && bundleMtime < statSync(pluginPiEntry).mtimeMs) return true;\",\n \" return false;\",\n \"}\",\n \"\",\n \"function rebuildBundle() {\",\n \" // Build to a temp dir and swap, mirroring the install-time build, so a\",\n \" // failed self-heal rebuild never corrupts the working bundle.\",\n ' var tmp = join(here, \".dist-bundle.tmp-\" + process.pid + \"-\" + Date.now());',\n \" var result = spawnSync(bunForRebuild, [\",\n ' \"build\",',\n \" sourceEntry,\",\n ' \"--target=bun\",',\n ' \"--outdir=\" + tmp',\n \" ], {\",\n \" cwd: here,\",\n ' stdio: \"inherit\",',\n \" });\",\n \" if (result.status !== 0 || result.error) {\",\n \" try { rmSync(tmp, { recursive: true, force: true }); } catch (e) {}\",\n \" throw new Error(\",\n ' \"Remnic omp extension: bundle is stale or missing and could not be rebuilt. \" +',\n ' \"Install bun (https://bun.sh), then run \" +',\n ' \"`bun build index.ts --target=bun --outdir=dist-bundle` inside \" + here',\n \" );\",\n \" }\",\n \" var backup = null;\",\n \" try {\",\n \" if (existsSync(bundleDir)) {\",\n ' backup = join(here, \".dist-bundle.bak-\" + process.pid + \"-\" + Date.now());',\n \" renameSync(bundleDir, backup);\",\n \" }\",\n \" renameSync(tmp, bundleDir);\",\n \" if (backup) { try { rmSync(backup, { recursive: true, force: true }); } catch (e) {} }\",\n \" } catch (err) {\",\n \" try { rmSync(tmp, { recursive: true, force: true }); } catch (e) {}\",\n \" if (backup && existsSync(backup) && !existsSync(bundleDir)) { try { renameSync(backup, bundleDir); } catch (e) {} }\",\n \" throw new Error(\",\n ' \"Remnic omp extension: failed to finalize rebuilt bundle - \" + (err && err.message ? err.message : err)',\n \" );\",\n \" }\",\n \"}\",\n\n \"\",\n \"if (bundleIsStale()) rebuildBundle();\",\n \"\",\n \"// Cache-bust so a freshly rebuilt bundle is loaded instead of a stale cached copy.\",\n 'const bundle = await import(pathToFileURL(bundleEntry).href + \"?t=\" + Date.now());',\n \"export default bundle.default;\",\n \"\",\n ].join(\"\\n\");\n}\n\n/**\n * Generates the `package.json` that tells omp to load `loader.js` (not\n * auto-discover `index.ts`) and re-bundles after `npm install` via postinstall.\n */\nfunction renderOmpPackageJson(): string {\n // Postinstall re-bundles after `npm install` (e.g. a plugin-pi upgrade moved\n // the dist mtime past the bundle). It delegates to postinstall-bundle.cjs — a\n // Node-only helper — so npm's default cmd.exe shell on Windows runs it just as\n // well as POSIX bash. The helper embeds the resolved bun path with a PATH\n // fallback and swaps the bundle atomically.\n const manifest = {\n name: \"remnic-omp-extension\",\n version: \"0.0.0\",\n private: true,\n type: \"module\",\n omp: { extensions: [\"./loader.js\"] },\n // Legacy key so older omp builds that only read `pi.extensions` also\n // resolve loader.js instead of falling through to index.ts.\n pi: { extensions: [\"./loader.js\"] },\n scripts: { postinstall: \"node postinstall-bundle.cjs\" },\n };\n return `${JSON.stringify(manifest, null, 2)}\\n`;\n}\n\n/**\n * Generates the cross-platform `postinstall-bundle.cjs` helper. Node-only, so\n * npm's default cmd.exe shell on Windows re-bundles after `npm install` just as\n * well as POSIX bash. Embeds the bun path resolved at install time with a PATH\n * fallback and writes the new bundle via a temp-dir swap so a failed rebuild\n * never corrupts the working bundle. The emitted script uses string\n * concatenation (no template literals) so it stays parseable everywhere.\n */\nfunction renderOmpPostinstall(bunBin: string): string {\n // Single template literal: the emitted .cjs uses string concatenation (no\n // template literals of its own), so this body has no backticks and the one\n // ${JSON.stringify(bunBin)} interpolation is unambiguous.\n return `// Auto-generated by Remnic's OmpMemoryExtensionPublisher.\n// Re-bundles the omp extension after npm install (e.g. a plugin-pi upgrade)\n// using the bun path resolved at install time, with a PATH fallback. Node-only\n// so it runs under npm's default cmd.exe shell on Windows as well as POSIX bash.\n\"use strict\";\nvar fs = require(\"node:fs\");\nvar cp = require(\"node:child_process\");\nvar path = require(\"node:path\");\n\nvar RESOLVED_BUN = ${JSON.stringify(bunBin)};\nvar dir = __dirname;\nvar entry = path.join(dir, \"index.ts\");\nvar out = path.join(dir, \"dist-bundle\");\n\nfunction pickBun() {\n var env = process.env.REMNIC_OMP_BUN_BIN;\n if (env && fs.existsSync(env)) return env;\n if (RESOLVED_BUN && fs.existsSync(RESOLVED_BUN)) return RESOLVED_BUN;\n return \"bun\";\n}\n\nfunction rebuild() {\n var bun = pickBun();\n var tmp = path.join(dir, \".dist-bundle.tmp-\" + process.pid + \"-\" + Date.now());\n var r = cp.spawnSync(bun, [\"build\", entry, \"--target=bun\", \"--outdir=\" + tmp], { cwd: dir, stdio: \"inherit\" });\n if (r.error || r.status !== 0) {\n try { fs.rmSync(tmp, { recursive: true, force: true }); } catch (e) {}\n throw new Error(\"Remnic omp extension: postinstall bun build failed (bun=\" + bun + \"). Run bun build index.ts --target=bun --outdir=dist-bundle manually inside \" + dir);\n }\n var backup = null;\n try {\n if (fs.existsSync(out)) {\n backup = path.join(dir, \".dist-bundle.bak-\" + process.pid + \"-\" + Date.now());\n fs.renameSync(out, backup);\n }\n fs.renameSync(tmp, out);\n if (backup) { try { fs.rmSync(backup, { recursive: true, force: true }); } catch (e) {} }\n } catch (err) {\n try { fs.rmSync(tmp, { recursive: true, force: true }); } catch (e) {}\n if (backup && fs.existsSync(backup) && !fs.existsSync(out)) { try { fs.renameSync(backup, out); } catch (e) {} }\n throw err;\n }\n}\n\ntry {\n rebuild();\n} catch (err) {\n console.error(err && err.message ? err.message : err);\n process.exit(1);\n}\n`;\n}\n\n/**\n * True when `candidate` is a regular file that the current process can\n * execute. Used by every `bun`-binary candidate selection site so a stale,\n * non-executable file named `bun` (or `bun.exe`) cannot win over a later\n * working binary — matching `which(1)` and the `spawnSync(\"bun\", [\"--version\"])`\n * version probe, which both skip non-executable files. On Windows\n * `fs.accessSync(X_OK)` verifies read access, which holds for real `.exe`\n * files, so the check is a harmless no-op there.\n */\nfunction isExecutableFile(candidate: string): boolean {\n try {\n const stat = fs.statSync(candidate);\n if (!stat.isFile()) return false;\n fs.accessSync(candidate, fs.constants.X_OK);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Walks `PATH` the way a shell does and returns the first `bun` executable it\n * finds, as a realpath-resolved absolute path (or null when nothing on PATH\n * is an executable `bun`). Used so the install-time PATH probe can embed an\n * absolute bun path in the generated loader/postinstall instead of the bare\n * string `\"bun\"`, which would break self-heal rebuilds under a stripped\n * runtime PATH (GUI/service launches). Mirrors `which(1)`; no dependency.\n */\nexport function resolveBunOnPath(): string | null {\n const pathVar = process.env.PATH ?? process.env.Path ?? process.env.path ?? \"\";\n const separator = process.platform === \"win32\" ? \";\" : \":\";\n const candidateNames =\n process.platform === \"win32\" ? [\"bun.exe\", \"bun\"] : [\"bun\"];\n for (const dir of pathVar.split(separator)) {\n if (!dir) continue;\n for (const name of candidateNames) {\n const candidate = path.isAbsolute(dir)\n ? path.join(dir, name)\n : path.resolve(dir, name);\n if (isExecutableFile(candidate)) {\n return fs.realpathSync(candidate);\n }\n }\n }\n return null;\n}\n\n/**\n * Resolves the `bun` binary for the install-time pre-bundle. Honours\n * `REMNIC_OMP_BUN_BIN` (test/override seam), then PATH, then common locations.\n * Returns null when bun is unavailable so the caller can fail with guidance.\n */\nexport function resolveBunBinary(): string | null {\n const override = process.env.REMNIC_OMP_BUN_BIN;\n if (override !== undefined) {\n return fs.existsSync(override) ? override : null;\n }\n\n const pathProbe = spawnSync(\"bun\", [\"--version\"], { encoding: \"utf-8\" });\n if (!pathProbe.error && pathProbe.status === 0) {\n // Resolve the PATH-found bun to an absolute executable so the embedded\n // loader/postinstall don't depend on omp's runtime PATH — GUI/service\n // launches commonly inherit a stripped PATH, which would make a bare\n // \"bun\" self-heal spawn fail even though install found a working binary.\n // Fall back to \"bun\" only if the PATH walk can't locate it (e.g. a shell\n // function/alias that isn't an actual file on PATH).\n return resolveBunOnPath() ?? \"bun\";\n }\n\n // Mirror omp's path helpers, which resolve the agent home as\n // HOME ?? USERPROFILE ?? os.homedir(). Relying on HOME alone breaks the\n // ~/.bun/bin/bun fallback on Windows installs where HOME is unset.\n const home = process.env.HOME ?? process.env.USERPROFILE ?? os.homedir();\n // The official Bun installer writes ~/.bun/bin/bun on POSIX and\n // ~/.bun/bin/bun.exe on Windows. Select the first candidate that is an\n // executable regular file (not merely one that exists) so a stale,\n // non-executable ~/.bun/bin/bun cannot win over a later working binary\n // (e.g. /usr/local/bin/bun or /opt/homebrew/bin/bun) — same `which(1)`\n // semantics as the PATH walk above.\n const candidates = [\n path.join(home ?? \"\", \".bun\", \"bin\", \"bun\"),\n path.join(home ?? \"\", \".bun\", \"bin\", \"bun.exe\"),\n \"/usr/local/bin/bun\",\n \"/opt/homebrew/bin/bun\",\n ];\n for (const candidate of candidates) {\n if (isExecutableFile(candidate)) return candidate;\n }\n return null;\n}\n\nfunction atomicWriteFile(filePath: string, content: string, mode: number): void {\n rejectSymlinkPath(filePath);\n const tmpPath = `${filePath}.tmp-${process.pid}-${Date.now()}`;\n try {\n fs.writeFileSync(tmpPath, content, { encoding: \"utf-8\", mode });\n fs.renameSync(tmpPath, filePath);\n try {\n fs.chmodSync(filePath, mode);\n } catch {\n // Best effort for platforms that do not support chmod.\n }\n } catch (err) {\n try {\n if (fs.existsSync(tmpPath)) fs.unlinkSync(tmpPath);\n } catch {\n // Best-effort cleanup only.\n }\n throw err;\n }\n}\n\nfunction snapshotFiles(paths: string[]): FileSnapshot[] {\n return paths.map((filePath) => {\n if (!fs.existsSync(filePath)) return { path: filePath, existed: false };\n const stat = fs.lstatSync(filePath);\n if (stat.isSymbolicLink()) {\n throw new Error(`Extension path must not be a symlink: ${filePath}`);\n }\n if (!stat.isFile()) return { path: filePath, existed: false };\n return {\n path: filePath,\n existed: true,\n content: fs.readFileSync(filePath),\n mode: stat.mode & 0o777,\n };\n });\n}\n\nfunction restorePublishSnapshot(extensionRoot: string, rootExisted: boolean, snapshots: FileSnapshot[]): void {\n if (!rootExisted && !canCleanNewExtensionRoot(extensionRoot)) return;\n\n for (const snapshot of snapshots) {\n if (!snapshot.existed) {\n assertSafeExistingPath(snapshot.path);\n fs.rmSync(snapshot.path, { force: true });\n continue;\n }\n fs.mkdirSync(path.dirname(snapshot.path), { recursive: true });\n fs.writeFileSync(snapshot.path, snapshot.content ?? Buffer.alloc(0), { mode: snapshot.mode });\n if (snapshot.mode !== undefined) {\n try {\n fs.chmodSync(snapshot.path, snapshot.mode);\n } catch {\n // Best effort for platforms that do not support chmod.\n }\n }\n }\n\n if (!rootExisted) {\n removeEmptyDirectory(extensionRoot);\n }\n}\n\nfunction snapshotDirs(paths: string[]): DirSnapshot[] {\n return paths.map((dirPath) => {\n let existed = false;\n try {\n const stat = fs.lstatSync(dirPath);\n if (stat.isSymbolicLink()) {\n throw new Error(`Extension path must not be a symlink: ${dirPath}`);\n }\n existed = stat.isDirectory();\n } catch (err) {\n if (err && typeof err === \"object\" && \"code\" in err && err.code === \"ENOENT\") {\n existed = false;\n } else {\n throw err;\n }\n }\n return { path: dirPath, existed };\n });\n}\n\nfunction restoreDirSnapshots(snapshots: DirSnapshot[]): void {\n for (const snapshot of snapshots) {\n if (snapshot.existed) continue;\n try {\n fs.rmSync(snapshot.path, { recursive: true, force: true });\n } catch {\n // best-effort — the loader self-heals at runtime if the dir lingers\n }\n }\n}\n\n\nfunction canCleanNewExtensionRoot(extensionRoot: string): boolean {\n let stat: fs.Stats;\n try {\n stat = fs.lstatSync(extensionRoot);\n } catch (err) {\n if (err && typeof err === \"object\" && \"code\" in err && err.code === \"ENOENT\") return false;\n throw err;\n }\n if (stat.isSymbolicLink()) {\n throw new Error(`Extension path must not be a symlink: ${extensionRoot}`);\n }\n return stat.isDirectory();\n}\n\nfunction removeEmptyDirectory(dirPath: string): void {\n let stat: fs.Stats;\n try {\n stat = fs.lstatSync(dirPath);\n } catch (err) {\n if (err && typeof err === \"object\" && \"code\" in err && err.code === \"ENOENT\") return;\n throw err;\n }\n if (stat.isSymbolicLink()) {\n throw new Error(`Extension path must not be a symlink: ${dirPath}`);\n }\n if (!stat.isDirectory()) return;\n if (fs.readdirSync(dirPath).length > 0) return;\n fs.rmdirSync(dirPath);\n}\n\nfunction removableOwnedExtensionFiles(filePaths: string[]): string[] {\n const removableFiles: string[] = [];\n for (const filePath of filePaths) {\n const stat = statOwnedExtensionPath(filePath);\n if (stat === null) continue;\n if (stat.isSymbolicLink()) {\n throw new Error(`Extension path must not be a symlink: ${filePath}`);\n }\n if (stat.isFile()) removableFiles.push(filePath);\n }\n return removableFiles;\n}\n\nfunction statOwnedExtensionPath(filePath: string): fs.Stats | null {\n let stat: fs.Stats;\n try {\n stat = fs.lstatSync(filePath);\n } catch (err) {\n if (err && typeof err === \"object\" && \"code\" in err && err.code === \"ENOENT\") return null;\n throw err;\n }\n return stat;\n}\n\nfunction mkdirExtensionRoot(extensionRoot: string, agentHome: string): void {\n const extensionsDir = path.join(path.resolve(agentHome), \"extensions\");\n assertSafeExtensionRoot(extensionRoot, agentHome);\n fs.mkdirSync(extensionsDir, { recursive: true });\n rejectSymlinkPath(extensionsDir);\n fs.mkdirSync(extensionRoot, { recursive: true });\n rejectSymlinkPath(extensionRoot);\n}\n\nfunction assertSafeExtensionRoot(extensionRoot: string, agentHome: string): void {\n const resolvedAgentHome = path.resolve(agentHome);\n const expected = path.join(resolvedAgentHome, \"extensions\", \"remnic\");\n if (path.resolve(extensionRoot) !== path.resolve(expected)) {\n throw new Error(`Extension root is outside the configured extensions directory: ${extensionRoot}`);\n }\n const extensionsDir = path.join(resolvedAgentHome, \"extensions\");\n assertPathContained(resolvedAgentHome, extensionsDir);\n assertPathContained(extensionsDir, extensionRoot);\n rejectSymlinkPath(resolvedAgentHome);\n if (fs.existsSync(extensionsDir)) rejectSymlinkPath(extensionsDir);\n if (fs.existsSync(extensionRoot)) rejectSymlinkPath(extensionRoot);\n}\n\nfunction assertSafeExistingPath(filePath: string): void {\n if (fs.existsSync(filePath)) rejectSymlinkPath(filePath);\n}\n\nfunction rejectSymlinkPath(filePath: string): void {\n if (!fs.existsSync(filePath)) return;\n const stat = fs.lstatSync(filePath);\n if (stat.isSymbolicLink()) {\n throw new Error(`Extension path must not be a symlink: ${filePath}`);\n }\n}\n\nfunction assertPathContained(root: string, candidate: string): void {\n const rootResolved = path.resolve(root);\n const candidateResolved = path.resolve(candidate);\n const relative = path.relative(rootResolved, candidateResolved);\n if (relative === \"\" || (!relative.startsWith(\"..\") && !path.isAbsolute(relative))) return;\n throw new Error(`Extension path escapes allowed root: ${candidate}`);\n}\n\nfunction readPriorConfig(configPath: string): Record<string, unknown> {\n if (!fs.existsSync(configPath)) return {};\n try {\n const parsed = JSON.parse(fs.readFileSync(configPath, \"utf8\"));\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n throw new Error(\"expected a JSON object\");\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n throw new Error(`Failed to load existing Remnic Pi config at ${configPath}: ${reason}`);\n }\n}\n\nfunction snapshotTokenEntry(connectorId: string): TokenEntry | null {\n const entry = loadTokenStore().tokens.find((candidate) => candidate.connector === connectorId);\n return cloneTokenEntry(entry ?? null);\n}\n\nfunction cloneTokenEntry(entry: TokenEntry | null): TokenEntry | null {\n return entry ? { ...entry } : null;\n}\n\nfunction restoreTokenEntry(priorEntry: TokenEntry | null, connectorId: string): void {\n const store = loadTokenStore();\n store.tokens = store.tokens.filter((entry) => entry.connector !== connectorId);\n if (priorEntry) store.tokens.push(priorEntry);\n saveTokenStore(store);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/publisher.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport { spawnSync } from \"node:child_process\";\nimport path from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport os from \"node:os\";\n\nimport {\n type MemoryExtensionPublisher,\n type PublishContext,\n type PublishResult,\n type PublisherCapabilities,\n type TokenEntry,\n getConnectorToken,\n loadTokenStore,\n saveTokenStore,\n} from \"@remnic/core\";\n\nimport {\n resolveOmpAgentHome,\n resolveOmpConfigRoot,\n resolveOmpExtensionRoot,\n resolvePiAgentHome,\n resolvePiExtensionRoot,\n} from \"./paths.js\";\n\nconst DEFAULT_DAEMON_PORT = 4318;\nconst BASE_OWNED_FILES = [\"remnic.config.json\", \"index.ts\", \"README.md\"] as const;\nconst EXTENSION_OWNED_TEMP_FILE_SUFFIX = /\\.tmp-\\d+-\\d+$/u;\n\ntype FileSnapshot = {\n path: string;\n existed: boolean;\n content?: Buffer;\n mode?: number;\n};\n\ntype DirSnapshot = {\n path: string;\n existed: boolean;\n};\n\n/**\n * Host-specific parameters for a Pi-family memory extension publisher.\n *\n * The Remnic runtime extension is host-neutral (it only uses Pi's extension\n * hooks, which omp preserves as a superset), so the only things that vary\n * between hosts are *where* the extension is installed, *which* connector\n * token it uses, and *how* it is labelled. Everything else — atomic writes,\n * rollback, symlink guards, config merge — is shared.\n */\nexport interface HostPublisherDescriptor {\n readonly hostId: string;\n readonly connectorId: string;\n readonly displayName: string;\n readonly tokenGenerateHint: string;\n resolveAgentHome(env: NodeJS.ProcessEnv): string;\n resolveExtensionRoot(env: NodeJS.ProcessEnv): string;\n /**\n * Optional: every agent home `unpublish` should sweep for a stale extension,\n * beyond the one resolved from the current env. Hosts with env-sensitive\n * install locations (e.g. omp profiles) provide this so `remnic connectors\n * remove` cleans up even when the remove-time env differs from install time.\n */\n listRemovalAgentHomes?(env: NodeJS.ProcessEnv): string[];\n}\n\nconst PI_HOST: HostPublisherDescriptor = {\n hostId: \"pi\",\n connectorId: \"pi\",\n displayName: \"Pi Coding Agent\",\n tokenGenerateHint: \"remnic token generate pi\",\n resolveAgentHome: resolvePiAgentHome,\n resolveExtensionRoot: resolvePiExtensionRoot,\n};\n\nconst OMP_HOST: HostPublisherDescriptor = {\n hostId: \"omp\",\n connectorId: \"omp\",\n displayName: \"Oh My Pi (omp)\",\n tokenGenerateHint: \"remnic token generate omp\",\n resolveAgentHome: resolveOmpAgentHome,\n resolveExtensionRoot: resolveOmpExtensionRoot,\n listRemovalAgentHomes: ompRemovalAgentHomes,\n};\n\n/**\n * Every omp agent home a stale extension might live under, so `unpublish` cleans\n * up regardless of the profile/env active at remove time: the env-resolved home,\n * the base `<configRoot>/agent`, an explicit `PI_CODING_AGENT_DIR`, and every\n * existing `<configRoot>/profiles/<name>/agent`. Symlinked profile dirs are\n * skipped defensively.\n */\nfunction ompRemovalAgentHomes(env: NodeJS.ProcessEnv): string[] {\n const homes = new Set<string>([resolveOmpAgentHome(env)]);\n const configRoot = resolveOmpConfigRoot(env);\n homes.add(path.join(configRoot, \"agent\"));\n\n const explicit = env.PI_CODING_AGENT_DIR?.trim();\n if (explicit) homes.add(path.resolve(explicit));\n\n const profilesDir = path.join(configRoot, \"profiles\");\n let entries: fs.Dirent[] = [];\n try {\n entries = fs.readdirSync(profilesDir, { withFileTypes: true });\n } catch {\n entries = [];\n }\n for (const entry of entries) {\n if (entry.isDirectory() && !entry.isSymbolicLink()) {\n homes.add(path.join(profilesDir, entry.name, \"agent\"));\n }\n }\n return [...homes];\n}\n\n/**\n * Shared publisher for Pi-family hosts. Concrete hosts (Pi, omp) subclass this\n * with a {@link HostPublisherDescriptor}; the install/rollback machinery is\n * identical across hosts.\n */\nexport class HostMemoryExtensionPublisher implements MemoryExtensionPublisher {\n static readonly capabilities: PublisherCapabilities = {\n // Real publisher: writes host config + wrapper + readme, just no\n // instructions.md/skills/citation/read-path-template artefacts. The\n // explicit flag prevents the parity gate from mis-inferring \"all flags\n // false ⇒ stub\" for this host (#1518).\n isStub: false,\n instructionsMd: false,\n skillsFolder: false,\n citationFormat: false,\n readPathTemplate: false,\n };\n\n protected constructor(private readonly host: HostPublisherDescriptor) {}\n\n /**\n * File basenames this publisher owns inside the extension root. The shared\n * set is config + wrapper + readme; subclasses add host-specific files\n * (e.g. omp's pre-bundle loader + package manifest). Used for snapshot,\n * atomic-write rollback, and unpublish cleanup.\n */\n protected get ownedFileNames(): readonly string[] {\n return BASE_OWNED_FILES;\n }\n\n /**\n * Directory names this publisher owns inside the extension root (build\n * outputs). Recursively removed on unpublish and on publish rollback when\n * newly created.\n */\n protected get ownedDirNames(): readonly string[] {\n return [];\n }\n\n /**\n * Whether the generated wrapper must use a bun-buildable import specifier\n * (relative path) instead of a file:// URL. omp pre-bundles the wrapper with\n * `bun build`, which cannot resolve file:// specifiers; pi loads the wrapper\n * directly via tsx and keeps the file:// URL.\n */\n protected get usesBundledWrapper(): boolean {\n return false;\n }\n\n /**\n * Hook for subclasses to write host-specific files and run install-time\n * build steps after the shared config/wrapper/readme are written. Runs\n * inside the publish try-block: a throw triggers full rollback.\n */\n protected finalizePublish(\n _ctx: PublishContext,\n _extensionRoot: string,\n _paths: { configPath: string; wrapperPath: string; pluginPiDistPath: string },\n ): void {\n // No-op by default; subclasses override.\n }\n\n get hostId(): string {\n return this.host.hostId;\n }\n\n async resolveExtensionRoot(env?: NodeJS.ProcessEnv): Promise<string> {\n return this.host.resolveExtensionRoot(env ?? process.env);\n }\n\n async isHostAvailable(): Promise<boolean> {\n // Pi-family agents auto-discover extensions from their agent extensions\n // directory. The directory can be created before the agent has been\n // launched, so availability should not block first-time installation.\n return true;\n }\n\n async renderInstructions(ctx: PublishContext): Promise<string> {\n const namespace = ctx.config.namespace ?? \"default\";\n const daemonUrl = resolveDaemonUrl(ctx);\n return [\n `# Remnic for ${this.host.displayName}`,\n \"\",\n `Remnic provides memory, retrieval, observation, MCP tools, and long-context compaction coordination for ${this.host.displayName}.`,\n \"\",\n \"## Installed Capabilities\",\n \"\",\n \"- Recall relevant Remnic context in the `context` hook before agent turns.\",\n '- Observe user, assistant, and tool messages with `sourceFormat: \"pi\"`.',\n \"- Coordinate `session_before_compact` with Remnic LCM flush and checkpoint recording.\",\n \"- Register Remnic MCP tools as host tools when daemon authentication is configured.\",\n \"- Persist lightweight dedupe state in custom entries via `appendEntry`.\",\n \"\",\n \"## Runtime\",\n \"\",\n `- Remnic daemon: \\`${daemonUrl}\\``,\n `- Namespace: \\`${namespace}\\``,\n `- Memory directory: \\`${ctx.config.memoryDir}\\``,\n \"\",\n \"The private `remnic.config.json` file stores the daemon URL, namespace, and connector auth token with owner-only permissions.\",\n ].join(\"\\n\");\n }\n\n async publish(ctx: PublishContext): Promise<PublishResult> {\n const extensionRoot = await this.resolveExtensionRoot();\n const agentHome = this.host.resolveAgentHome(process.env);\n assertSafeExtensionRoot(extensionRoot, agentHome);\n const filesWritten: string[] = [];\n const skipped: string[] = [];\n\n ctx.log.info(`Publishing ${this.host.displayName} memory extension to ${extensionRoot}`);\n\n const ownedFilePaths = this.ownedFileNames.map((fileName) => path.join(extensionRoot, fileName));\n const configPath = ownedFilePaths[0];\n const wrapperPath = ownedFilePaths[1];\n const readmePath = ownedFilePaths[2];\n const pluginPiDistPath = resolveExtensionModulePath();\n const rootExisted = fs.existsSync(extensionRoot);\n const fileSnapshots = snapshotFiles(ownedFilePaths);\n const dirSnapshots = snapshotDirs(\n this.ownedDirNames.map((dirName) => path.join(extensionRoot, dirName)),\n );\n const priorTokenEntry =\n ctx.rollbackTokenEntry === undefined\n ? snapshotTokenEntry(this.host.connectorId)\n : cloneTokenEntry(ctx.rollbackTokenEntry);\n\n const token = getConnectorToken(this.host.connectorId);\n if (!token) {\n skipped.push(\n `auth token unavailable; run \\`${this.host.tokenGenerateHint}\\` and reinstall the connector`,\n );\n }\n\n try {\n const priorConfig = readPriorConfig(configPath);\n const config: Record<string, unknown> = {\n recallMode: \"auto\",\n recallTopK: 8,\n recallBudgetChars: 12000,\n recallEnabled: true,\n observeEnabled: true,\n observeSkipExtraction: false,\n compactionEnabled: true,\n mcpToolsEnabled: true,\n statusEnabled: true,\n requestTimeoutMs: 60000,\n startupRequestTimeoutMs: 1000,\n ...priorConfig,\n remnicDaemonUrl: resolveDaemonUrl(ctx),\n };\n if (token) {\n config.authToken = token;\n }\n if (ctx.config.namespace) {\n config.namespace = ctx.config.namespace;\n }\n\n mkdirExtensionRoot(extensionRoot, agentHome);\n\n atomicWriteFile(configPath, `${JSON.stringify(config, null, 2)}\\n`, 0o600);\n filesWritten.push(configPath);\n\n atomicWriteFile(\n wrapperPath,\n renderWrapper(\n pluginPiDistPath,\n configPath,\n this.usesBundledWrapper ? extensionRoot : undefined,\n ),\n 0o644,\n );\n filesWritten.push(wrapperPath);\n\n atomicWriteFile(readmePath, `${await this.renderInstructions(ctx)}\\n`, 0o644);\n filesWritten.push(readmePath);\n\n this.finalizePublish(ctx, extensionRoot, { configPath, wrapperPath, pluginPiDistPath });\n for (let i = BASE_OWNED_FILES.length; i < ownedFilePaths.length; i++) {\n filesWritten.push(ownedFilePaths[i]);\n }\n } catch (err) {\n try {\n // Remove newly created owned dirs (e.g. dist-bundle) BEFORE\n // restorePublishSnapshot's removeEmptyDirectory check, otherwise a\n // first-time publish that created dist-bundle would leave an empty\n // extension root behind on rollback.\n restoreDirSnapshots(dirSnapshots);\n restorePublishSnapshot(extensionRoot, rootExisted, fileSnapshots);\n } catch (restoreErr) {\n ctx.log.warn(\n `${this.host.displayName} extension rollback failed: ${restoreErr instanceof Error ? restoreErr.message : String(restoreErr)}`,\n );\n }\n // A failed omp pre-bundle (bun missing or `bun build` failing) is\n // recoverable: the runtime loader self-heals dist-bundle on first load,\n // and the connector token is already committed by the CLI. Rolling it\n // back here would leave the connector registered with no credential and\n // block a non-`--force` reinstall (AGENTS.md #14 — don't destroy\n // committed state before the new state is confirmed). File/dir rollback\n // above still runs, so a failed first-time publish still cleans its root.\n if (!(err instanceof OmpPreBundleError)) {\n try {\n restoreTokenEntry(priorTokenEntry, this.host.connectorId);\n } catch (tokenErr) {\n ctx.log.warn(\n `${this.host.displayName} connector token rollback failed: ${tokenErr instanceof Error ? tokenErr.message : String(tokenErr)}`,\n );\n }\n }\n throw err;\n }\n\n return {\n hostId: this.host.hostId,\n extensionRoot,\n filesWritten,\n skipped,\n };\n }\n\n async unpublish(): Promise<void> {\n const agentHomes = this.host.listRemovalAgentHomes\n ? this.host.listRemovalAgentHomes(process.env)\n : [this.host.resolveAgentHome(process.env)];\n\n const ownedFileNames = this.ownedFileNames;\n const ownedDirNames = this.ownedDirNames;\n const seen = new Set<string>();\n for (const agentHome of agentHomes) {\n const extensionRoot = path.join(path.resolve(agentHome), \"extensions\", \"remnic\");\n if (seen.has(extensionRoot)) continue;\n seen.add(extensionRoot);\n if (!fs.existsSync(extensionRoot)) continue;\n\n assertSafeExtensionRoot(extensionRoot, agentHome);\n const removableFiles = removableOwnedExtensionFiles(\n extensionOwnedUnpublishPaths(extensionRoot, ownedFileNames),\n );\n for (const filePath of removableFiles) {\n fs.rmSync(filePath, { force: true });\n }\n for (const dirName of ownedDirNames) {\n const dirPath = path.join(extensionRoot, dirName);\n let stat: fs.Stats;\n try {\n stat = fs.lstatSync(dirPath);\n } catch (err) {\n if (err && typeof err === \"object\" && \"code\" in err && err.code === \"ENOENT\") continue;\n throw err;\n }\n if (stat.isSymbolicLink()) {\n throw new Error(`Extension path must not be a symlink: ${dirPath}`);\n }\n if (stat.isDirectory()) {\n fs.rmSync(dirPath, { recursive: true, force: true });\n }\n }\n removeEmptyDirectory(extensionRoot);\n }\n }\n}\n\n/** Publisher for upstream Pi (`~/.pi/agent/extensions/remnic`). */\nexport class PiMemoryExtensionPublisher extends HostMemoryExtensionPublisher {\n constructor() {\n super(PI_HOST);\n }\n}\n\n/**\n * Marks a failure originating from the omp pre-bundle step (bun missing, or the\n * `bun build` itself failing). {@link HostMemoryExtensionPublisher.publish}\n * catches this and rolls back the written files but SKIPS the connector-token\n * rollback: the pre-bundle runs after the install (config + wrapper + token) is\n * already committed, and the runtime loader self-heals `dist-bundle` on first\n * load, so destroying the just-generated token would leave the connector\n * registered with no credential and block a non-`--force` reinstall\n * (AGENTS.md #14 — don't destroy committed state before the new state is\n * confirmed). The message is preserved verbatim so existing `/requires \\`bun\\`/`\n * and `/bun build failed/` assertions still match.\n */\nclass OmpPreBundleError extends Error {}\n\n/** Publisher for Oh My Pi / omp (`~/.omp/agent/extensions/remnic`). */\nexport class OmpMemoryExtensionPublisher extends HostMemoryExtensionPublisher {\n constructor() {\n super(OMP_HOST);\n }\n\n protected get ownedFileNames(): readonly string[] {\n return [...BASE_OWNED_FILES, \"loader.js\", \"package.json\", \"postinstall-bundle.cjs\"];\n }\n\n protected get ownedDirNames(): readonly string[] {\n return [\"dist-bundle\"];\n }\n\n // omp pre-bundles index.ts with `bun build`; the wrapper must use a relative\n // import specifier (bun's bundler cannot resolve file:// URLs).\n protected get usesBundledWrapper(): boolean {\n return true;\n }\n\n protected finalizePublish(\n ctx: PublishContext,\n extensionRoot: string,\n paths: { configPath: string; wrapperPath: string; pluginPiDistPath: string },\n ): void {\n // Resolve once so the install-time build and the generated loader share the\n // same bun path — a loader that hardcodes \"bun\" cannot self-heal when bun\n // is reachable only via REMNIC_OMP_BUN_BIN or a common absolute install\n // path that is not on omp's PATH at runtime.\n const bunBin = resolveBunBinary();\n if (!bunBin) {\n // OmpPreBundleError so publish() keeps the connector token intact (see\n // the class doc); the runtime loader self-heals the bundle once bun is\n // installed.\n throw new OmpPreBundleError(\n \"Remnic omp extension requires `bun` to pre-bundle the extension: omp's embedded \" +\n \"runtime cannot resolve bare npm specifiers from the extension's node_modules. \" +\n \"Install bun from https://bun.sh, then re-run `remnic connectors install omp`.\",\n );\n }\n\n const loaderPath = path.join(extensionRoot, \"loader.js\");\n const packageJsonPath = path.join(extensionRoot, \"package.json\");\n\n const postinstallPath = path.join(extensionRoot, \"postinstall-bundle.cjs\");\n\n atomicWriteFile(loaderPath, renderOmpLoader(paths.pluginPiDistPath, bunBin), 0o644);\n // Cross-platform postinstall helper (Node-only) so npm's default cmd.exe\n // shell on Windows re-bundles after `npm install`; the POSIX one-liner it\n // replaces only ran under bash.\n atomicWriteFile(postinstallPath, renderOmpPostinstall(bunBin), 0o644);\n atomicWriteFile(packageJsonPath, renderOmpPackageJson(), 0o644);\n\n try {\n this.runBundleBuild(ctx, extensionRoot, bunBin);\n } catch (err) {\n // OmpPreBundleError so publish() keeps the connector token intact (see\n // the class doc); the runtime loader self-heals the bundle on next load.\n const message = err instanceof Error ? err.message : String(err);\n throw new OmpPreBundleError(message);\n }\n }\n\n /**\n * Pre-bundles the omp extension with `bun build` so omp's embedded runtime\n * never resolves bare npm specifiers (e.g. @sinclair/typebox) from the\n * extension's node_modules at load time. The bundle is written to a temp\n * directory and swapped into dist-bundle/ on success. The pre-existing\n * dist-bundle is renamed aside (not removed) before the swap, so a failure\n * during the final rename restores the previously working bundle rather than\n * leaving the install with no bundle at all.\n *\n * Override in tests to skip the real bun invocation.\n */\n protected runBundleBuild(ctx: PublishContext, extensionRoot: string, bunBin: string): void {\n const sourceEntry = path.join(extensionRoot, \"index.ts\");\n const tmpOutDir = path.join(extensionRoot, `.dist-bundle.tmp-${process.pid}-${Date.now()}`);\n const finalOutDir = path.join(extensionRoot, \"dist-bundle\");\n\n const result = spawnSync(bunBin, [\"build\", sourceEntry, \"--target=bun\", `--outdir=${tmpOutDir}`], {\n cwd: extensionRoot,\n encoding: \"utf-8\",\n });\n\n if (result.error || result.status !== 0) {\n try {\n fs.rmSync(tmpOutDir, { recursive: true, force: true });\n } catch {\n // best-effort tmp cleanup\n }\n const detail =\n (typeof result.stderr === \"string\" ? result.stderr.trim() : \"\") ||\n (result.error instanceof Error ? result.error.message : \"\") ||\n `bun exited with status ${result.status ?? \"null\"}`;\n throw new Error(\n `Remnic omp extension: bun build failed (${detail}). Resolve the error and re-run ` +\n \"`remnic connectors install omp`, or build manually with \" +\n \"`bun build index.ts --target=bun --outdir=dist-bundle` inside \" +\n `${extensionRoot}.`,\n );\n }\n\n // Swap the freshly built bundle into place without ever leaving the install\n // bundle-less. Rename the existing dist-bundle aside, move the new one in,\n // and only then discard the backup. On any failure mid-swap, restore the\n // backup so the previously working bundle survives (the publish-level\n // rollback only removes newly created dirs — it never restores a removed\n // dist-bundle, so we must not remove it here).\n let backupDir: string | null = null;\n try {\n if (fs.existsSync(finalOutDir)) {\n backupDir = path.join(extensionRoot, `.dist-bundle.bak-${process.pid}-${Date.now()}`);\n fs.renameSync(finalOutDir, backupDir);\n }\n fs.renameSync(tmpOutDir, finalOutDir);\n if (backupDir) {\n try {\n fs.rmSync(backupDir, { recursive: true, force: true });\n } catch {\n // best-effort backup cleanup; leaving it does not break the install\n }\n }\n } catch (err) {\n try {\n if (fs.existsSync(tmpOutDir)) fs.rmSync(tmpOutDir, { recursive: true, force: true });\n } catch {\n // best-effort tmp cleanup\n }\n // Restore the previously working bundle if we moved it aside and the\n // final swap did not land.\n if (backupDir && fs.existsSync(backupDir) && !fs.existsSync(finalOutDir)) {\n try {\n fs.renameSync(backupDir, finalOutDir);\n } catch {\n // best-effort restore; the loader's self-heal rebuilds on next start\n }\n }\n throw new Error(\n `Remnic omp extension: failed to finalize bundle output — ${err instanceof Error ? err.message : String(err)}.`,\n );\n }\n\n ctx.log.info(`Pre-bundled omp extension into ${finalOutDir}`);\n }\n}\n\nfunction extensionOwnedPaths(extensionRoot: string, ownedFileNames: readonly string[]): string[] {\n return ownedFileNames.map((fileName) => path.join(extensionRoot, fileName));\n}\n\nfunction extensionOwnedUnpublishPaths(extensionRoot: string, ownedFileNames: readonly string[]): string[] {\n const ownedBaseNames = new Set(ownedFileNames);\n const ownedPaths = extensionOwnedPaths(extensionRoot, ownedFileNames);\n for (const fileName of fs.readdirSync(extensionRoot)) {\n const match = EXTENSION_OWNED_TEMP_FILE_SUFFIX.exec(fileName);\n if (match && ownedBaseNames.has(fileName.slice(0, match.index))) {\n ownedPaths.push(path.join(extensionRoot, fileName));\n }\n }\n return ownedPaths;\n}\n\nfunction resolveDaemonUrl(ctx: PublishContext): string {\n if (ctx.config.daemonUrl && ctx.config.daemonUrl.trim().length > 0) {\n return trimTrailingSlashes(ctx.config.daemonUrl.trim());\n }\n return `http://127.0.0.1:${ctx.config.daemonPort ?? DEFAULT_DAEMON_PORT}`;\n}\n\nfunction trimTrailingSlashes(value: string): string {\n let end = value.length;\n while (end > 0 && value.charCodeAt(end - 1) === 47) end -= 1;\n return value.slice(0, end);\n}\n\nfunction resolveExtensionModulePath(): string {\n const moduleDir = path.dirname(fileURLToPath(import.meta.url));\n const built = path.join(moduleDir, \"index.js\");\n if (fs.existsSync(built)) return built;\n\n const source = path.join(moduleDir, \"index.ts\");\n if (fs.existsSync(source)) return source;\n\n return built;\n}\n\n/**\n * Resolves the import specifier the omp wrapper uses to reach the\n * `@remnic/plugin-pi` dist entry from the generated `index.ts`. omp pre-bundles\n * that wrapper with `bun build`, whose bundler cannot resolve `file://`\n * specifiers (\"Could not resolve: file://…\" on Bun 1.2–1.3, verified), so the\n * specifier must be a relative path. On Windows, when the extension directory\n * and the plugin-pi install sit on different drives, `path.relative` cannot\n * express a relative path and returns an absolute drive path (e.g. `D:\\…`);\n * prefixing `./` then yields an invalid module specifier that fails `bun build`\n * with a cryptic error. Detect that layout and fail fast with an actionable\n * message instead. (Cross-drive omp installs are unsupported because neither a\n * relative specifier nor a `file://` URL is acceptable to `bun build`.) Drive\n * roots are compared case-insensitively so a same-drive Windows install is not\n * falsely rejected when the agent home and the plugin-pi install report the\n * drive letter in different casing (`C:\\\\` vs `c:\\\\`).\n *\n * Exported so the cross-drive guard can be exercised on non-Windows hosts via\n * `path.win32`.\n */\nexport function resolveOmpWrapperImportSpecifier(\n extensionModulePath: string,\n wrapperDir: string,\n pathApi: typeof path = path,\n): string {\n // Windows drive roots are case-insensitive: `C:\\\\…` (e.g. from the omp agent\n // home) and `c:\\\\…` (e.g. from fileURLToPath(import.meta.url)) are the SAME\n // drive, and path.win32.relative yields a valid relative specifier between\n // them. Compare the parsed roots case-insensitively so a same-drive install\n // isn't falsely rejected as \"different drives\". posix roots (`/`) are\n // unaffected by toLowerCase().\n if (pathApi.parse(wrapperDir).root.toLowerCase() !== pathApi.parse(extensionModulePath).root.toLowerCase()) {\n throw new Error(\n \"Remnic omp extension cannot pre-bundle: the extension directory \" +\n `(${wrapperDir}) and the @remnic/plugin-pi install (${extensionModulePath}) ` +\n \"are on different drives, so no relative import specifier can be generated \" +\n \"for `bun build` (and `bun build` cannot resolve a `file://` specifier). \" +\n \"Move the omp agent home and the Remnic install onto the same drive.\",\n );\n }\n let rel = pathApi.relative(wrapperDir, extensionModulePath);\n rel = rel.split(pathApi.sep).join(\"/\");\n return rel.startsWith(\".\") ? rel : `./${rel}`;\n}\n\nfunction renderWrapper(\n extensionModulePath: string,\n configPath: string,\n wrapperDir?: string,\n): string {\n // omp pre-bundles this entry with `bun build`, whose bundler cannot resolve\n // `file://` specifiers — it exits with \"Could not resolve: file://...\" on\n // Bun 1.2–1.3 (verified). When the wrapper will be bun-built, emit a relative\n // specifier resolved against the wrapper's directory; bun, tsx, and Node ESM\n // all resolve relative specifiers. For tsx-loaded wrappers (pi) the file://\n // URL is retained.\n let importSpecifier: string;\n if (wrapperDir) {\n importSpecifier = resolveOmpWrapperImportSpecifier(extensionModulePath, wrapperDir);\n } else {\n importSpecifier = pathToFileURL(extensionModulePath).href;\n }\n return [\n `import { createRemnicPiExtension } from ${JSON.stringify(importSpecifier)};`,\n \"\",\n `export default createRemnicPiExtension({ configPath: ${JSON.stringify(configPath)} });`,\n \"\",\n ].join(\"\\n\");\n}\n\n/**\n * Generates the self-healing `loader.js` that omp loads via the package\n * manifest's `omp.extensions` entry. It mtime-compares the pre-bundled\n * `dist-bundle/index.js` against `index.ts` and the underlying @remnic/plugin-pi\n * dist, rebuilds via `bun build` when stale (e.g. after an `npm update`), then\n * imports the self-contained bundle so omp's embedded runtime never resolves\n * bare npm specifiers at load time.\n */\nfunction renderOmpLoader(pluginPiDistPath: string, bunBin: string): string {\n return [\n \"// Auto-generated by Remnic's OmpMemoryExtensionPublisher.\",\n \"// omp's embedded runtime cannot resolve bare npm specifiers from this\",\n \"// extension's node_modules, so we pre-bundle with `bun build` and import\",\n \"// the self-contained bundle here. Rebuilt automatically when index.ts or\",\n \"// the underlying @remnic/plugin-pi dist changes.\",\n \"\",\n 'import { existsSync, renameSync, rmSync, statSync } from \"node:fs\";',\n 'import { spawnSync } from \"node:child_process\";',\n 'import { dirname, join } from \"node:path\";',\n 'import { fileURLToPath, pathToFileURL } from \"node:url\";',\n \"\",\n 'const here = dirname(fileURLToPath(import.meta.url));',\n 'const bundleDir = join(here, \"dist-bundle\");',\n 'const bundleEntry = join(bundleDir, \"index.js\");',\n 'const sourceEntry = join(here, \"index.ts\");',\n `const pluginPiEntry = ${JSON.stringify(pluginPiDistPath)};`,\n // Reuse the bun path resolved at install time (REMNIC_OMP_BUN_BIN, PATH,\n // or a common absolute location). Fall back to \"bun\" on PATH if the\n // resolved path no longer exists (e.g. the extension tree was moved), so\n // self-healing still works when bun is reachable only via PATH.\n `const resolvedBunBin = ${JSON.stringify(bunBin)};`,\n 'const bunForRebuild = resolvedBunBin && existsSync(resolvedBunBin) ? resolvedBunBin : \"bun\";',\n \"\",\n \"function bundleIsStale() {\",\n \" if (!existsSync(bundleEntry)) return true;\",\n \" const bundleMtime = statSync(bundleEntry).mtimeMs;\",\n \" if (existsSync(sourceEntry) && bundleMtime < statSync(sourceEntry).mtimeMs) return true;\",\n \" if (pluginPiEntry && existsSync(pluginPiEntry) && bundleMtime < statSync(pluginPiEntry).mtimeMs) return true;\",\n \" return false;\",\n \"}\",\n \"\",\n \"function rebuildBundle() {\",\n \" // Build to a temp dir and swap, mirroring the install-time build, so a\",\n \" // failed self-heal rebuild never corrupts the working bundle.\",\n ' var tmp = join(here, \".dist-bundle.tmp-\" + process.pid + \"-\" + Date.now());',\n \" var result = spawnSync(bunForRebuild, [\",\n ' \"build\",',\n \" sourceEntry,\",\n ' \"--target=bun\",',\n ' \"--outdir=\" + tmp',\n \" ], {\",\n \" cwd: here,\",\n ' stdio: \"inherit\",',\n \" });\",\n \" if (result.status !== 0 || result.error) {\",\n \" try { rmSync(tmp, { recursive: true, force: true }); } catch (e) {}\",\n \" throw new Error(\",\n ' \"Remnic omp extension: bundle is stale or missing and could not be rebuilt. \" +',\n ' \"Install bun (https://bun.sh), then run \" +',\n ' \"`bun build index.ts --target=bun --outdir=dist-bundle` inside \" + here',\n \" );\",\n \" }\",\n \" var backup = null;\",\n \" try {\",\n \" if (existsSync(bundleDir)) {\",\n ' backup = join(here, \".dist-bundle.bak-\" + process.pid + \"-\" + Date.now());',\n \" renameSync(bundleDir, backup);\",\n \" }\",\n \" renameSync(tmp, bundleDir);\",\n \" if (backup) { try { rmSync(backup, { recursive: true, force: true }); } catch (e) {} }\",\n \" } catch (err) {\",\n \" try { rmSync(tmp, { recursive: true, force: true }); } catch (e) {}\",\n \" if (backup && existsSync(backup) && !existsSync(bundleDir)) { try { renameSync(backup, bundleDir); } catch (e) {} }\",\n \" throw new Error(\",\n ' \"Remnic omp extension: failed to finalize rebuilt bundle - \" + (err && err.message ? err.message : err)',\n \" );\",\n \" }\",\n \"}\",\n\n \"\",\n \"if (bundleIsStale()) rebuildBundle();\",\n \"\",\n \"// Cache-bust so a freshly rebuilt bundle is loaded instead of a stale cached copy.\",\n 'const bundle = await import(pathToFileURL(bundleEntry).href + \"?t=\" + Date.now());',\n \"export default bundle.default;\",\n \"\",\n ].join(\"\\n\");\n}\n\n/**\n * Generates the `package.json` that tells omp to load `loader.js` (not\n * auto-discover `index.ts`) and re-bundles after `npm install` via postinstall.\n */\nfunction renderOmpPackageJson(): string {\n // Postinstall re-bundles after `npm install` (e.g. a plugin-pi upgrade moved\n // the dist mtime past the bundle). It delegates to postinstall-bundle.cjs — a\n // Node-only helper — so npm's default cmd.exe shell on Windows runs it just as\n // well as POSIX bash. The helper embeds the resolved bun path with a PATH\n // fallback and swaps the bundle atomically.\n const manifest = {\n name: \"remnic-omp-extension\",\n version: \"0.0.0\",\n private: true,\n type: \"module\",\n omp: { extensions: [\"./loader.js\"] },\n // Legacy key so older omp builds that only read `pi.extensions` also\n // resolve loader.js instead of falling through to index.ts.\n pi: { extensions: [\"./loader.js\"] },\n scripts: { postinstall: \"node postinstall-bundle.cjs\" },\n };\n return `${JSON.stringify(manifest, null, 2)}\\n`;\n}\n\n/**\n * Generates the cross-platform `postinstall-bundle.cjs` helper. Node-only, so\n * npm's default cmd.exe shell on Windows re-bundles after `npm install` just as\n * well as POSIX bash. Embeds the bun path resolved at install time with a PATH\n * fallback and writes the new bundle via a temp-dir swap so a failed rebuild\n * never corrupts the working bundle. The emitted script uses string\n * concatenation (no template literals) so it stays parseable everywhere.\n */\nfunction renderOmpPostinstall(bunBin: string): string {\n // Single template literal: the emitted .cjs uses string concatenation (no\n // template literals of its own), so this body has no backticks and the one\n // ${JSON.stringify(bunBin)} interpolation is unambiguous.\n return `// Auto-generated by Remnic's OmpMemoryExtensionPublisher.\n// Re-bundles the omp extension after npm install (e.g. a plugin-pi upgrade)\n// using the bun path resolved at install time, with a PATH fallback. Node-only\n// so it runs under npm's default cmd.exe shell on Windows as well as POSIX bash.\n\"use strict\";\nvar fs = require(\"node:fs\");\nvar cp = require(\"node:child_process\");\nvar path = require(\"node:path\");\n\nvar RESOLVED_BUN = ${JSON.stringify(bunBin)};\nvar dir = __dirname;\nvar entry = path.join(dir, \"index.ts\");\nvar out = path.join(dir, \"dist-bundle\");\n\nfunction pickBun() {\n var env = process.env.REMNIC_OMP_BUN_BIN;\n if (env && fs.existsSync(env)) return env;\n if (RESOLVED_BUN && fs.existsSync(RESOLVED_BUN)) return RESOLVED_BUN;\n return \"bun\";\n}\n\nfunction rebuild() {\n var bun = pickBun();\n var tmp = path.join(dir, \".dist-bundle.tmp-\" + process.pid + \"-\" + Date.now());\n var r = cp.spawnSync(bun, [\"build\", entry, \"--target=bun\", \"--outdir=\" + tmp], { cwd: dir, stdio: \"inherit\" });\n if (r.error || r.status !== 0) {\n try { fs.rmSync(tmp, { recursive: true, force: true }); } catch (e) {}\n throw new Error(\"Remnic omp extension: postinstall bun build failed (bun=\" + bun + \"). Run bun build index.ts --target=bun --outdir=dist-bundle manually inside \" + dir);\n }\n var backup = null;\n try {\n if (fs.existsSync(out)) {\n backup = path.join(dir, \".dist-bundle.bak-\" + process.pid + \"-\" + Date.now());\n fs.renameSync(out, backup);\n }\n fs.renameSync(tmp, out);\n if (backup) { try { fs.rmSync(backup, { recursive: true, force: true }); } catch (e) {} }\n } catch (err) {\n try { fs.rmSync(tmp, { recursive: true, force: true }); } catch (e) {}\n if (backup && fs.existsSync(backup) && !fs.existsSync(out)) { try { fs.renameSync(backup, out); } catch (e) {} }\n throw err;\n }\n}\n\ntry {\n rebuild();\n} catch (err) {\n console.error(err && err.message ? err.message : err);\n process.exit(1);\n}\n`;\n}\n\n/**\n * True when `candidate` is a regular file that the current process can\n * execute. Used by every `bun`-binary candidate selection site so a stale,\n * non-executable file named `bun` (or `bun.exe`) cannot win over a later\n * working binary — matching `which(1)` and the `spawnSync(\"bun\", [\"--version\"])`\n * version probe, which both skip non-executable files. On Windows\n * `fs.accessSync(X_OK)` verifies read access, which holds for real `.exe`\n * files, so the check is a harmless no-op there.\n */\nfunction isExecutableFile(candidate: string): boolean {\n try {\n const stat = fs.statSync(candidate);\n if (!stat.isFile()) return false;\n fs.accessSync(candidate, fs.constants.X_OK);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Walks `PATH` the way a shell does and returns the first `bun` executable it\n * finds, as a realpath-resolved absolute path (or null when nothing on PATH\n * is an executable `bun`). Used so the install-time PATH probe can embed an\n * absolute bun path in the generated loader/postinstall instead of the bare\n * string `\"bun\"`, which would break self-heal rebuilds under a stripped\n * runtime PATH (GUI/service launches). Mirrors `which(1)`; no dependency.\n */\nexport function resolveBunOnPath(): string | null {\n const pathVar = process.env.PATH ?? process.env.Path ?? process.env.path ?? \"\";\n const separator = process.platform === \"win32\" ? \";\" : \":\";\n const candidateNames =\n process.platform === \"win32\" ? [\"bun.exe\", \"bun\"] : [\"bun\"];\n for (const dir of pathVar.split(separator)) {\n if (!dir) continue;\n for (const name of candidateNames) {\n const candidate = path.isAbsolute(dir)\n ? path.join(dir, name)\n : path.resolve(dir, name);\n if (isExecutableFile(candidate)) {\n return fs.realpathSync(candidate);\n }\n }\n }\n return null;\n}\n\n/**\n * Resolves the `bun` binary for the install-time pre-bundle. Honours\n * `REMNIC_OMP_BUN_BIN` (test/override seam), then PATH, then common locations.\n * Returns null when bun is unavailable so the caller can fail with guidance.\n */\nexport function resolveBunBinary(): string | null {\n const override = process.env.REMNIC_OMP_BUN_BIN;\n if (override !== undefined) {\n return fs.existsSync(override) ? override : null;\n }\n\n const pathProbe = spawnSync(\"bun\", [\"--version\"], { encoding: \"utf-8\" });\n if (!pathProbe.error && pathProbe.status === 0) {\n // Resolve the PATH-found bun to an absolute executable so the embedded\n // loader/postinstall don't depend on omp's runtime PATH — GUI/service\n // launches commonly inherit a stripped PATH, which would make a bare\n // \"bun\" self-heal spawn fail even though install found a working binary.\n // Fall back to \"bun\" only if the PATH walk can't locate it (e.g. a shell\n // function/alias that isn't an actual file on PATH).\n return resolveBunOnPath() ?? \"bun\";\n }\n\n // Mirror omp's path helpers, which resolve the agent home as\n // HOME ?? USERPROFILE ?? os.homedir(). Relying on HOME alone breaks the\n // ~/.bun/bin/bun fallback on Windows installs where HOME is unset.\n const home = process.env.HOME ?? process.env.USERPROFILE ?? os.homedir();\n // The official Bun installer writes ~/.bun/bin/bun on POSIX and\n // ~/.bun/bin/bun.exe on Windows. Select the first candidate that is an\n // executable regular file (not merely one that exists) so a stale,\n // non-executable ~/.bun/bin/bun cannot win over a later working binary\n // (e.g. /usr/local/bin/bun or /opt/homebrew/bin/bun) — same `which(1)`\n // semantics as the PATH walk above.\n const candidates = [\n path.join(home ?? \"\", \".bun\", \"bin\", \"bun\"),\n path.join(home ?? \"\", \".bun\", \"bin\", \"bun.exe\"),\n \"/usr/local/bin/bun\",\n \"/opt/homebrew/bin/bun\",\n ];\n for (const candidate of candidates) {\n if (isExecutableFile(candidate)) return candidate;\n }\n return null;\n}\n\nfunction atomicWriteFile(filePath: string, content: string, mode: number): void {\n rejectSymlinkPath(filePath);\n const tmpPath = `${filePath}.tmp-${process.pid}-${Date.now()}`;\n try {\n fs.writeFileSync(tmpPath, content, { encoding: \"utf-8\", mode });\n fs.renameSync(tmpPath, filePath);\n try {\n fs.chmodSync(filePath, mode);\n } catch {\n // Best effort for platforms that do not support chmod.\n }\n } catch (err) {\n try {\n if (fs.existsSync(tmpPath)) fs.unlinkSync(tmpPath);\n } catch {\n // Best-effort cleanup only.\n }\n throw err;\n }\n}\n\nfunction snapshotFiles(paths: string[]): FileSnapshot[] {\n return paths.map((filePath) => {\n if (!fs.existsSync(filePath)) return { path: filePath, existed: false };\n const stat = fs.lstatSync(filePath);\n if (stat.isSymbolicLink()) {\n throw new Error(`Extension path must not be a symlink: ${filePath}`);\n }\n if (!stat.isFile()) return { path: filePath, existed: false };\n return {\n path: filePath,\n existed: true,\n content: fs.readFileSync(filePath),\n mode: stat.mode & 0o777,\n };\n });\n}\n\nfunction restorePublishSnapshot(extensionRoot: string, rootExisted: boolean, snapshots: FileSnapshot[]): void {\n if (!rootExisted && !canCleanNewExtensionRoot(extensionRoot)) return;\n\n for (const snapshot of snapshots) {\n restoreOwnedFile(snapshot);\n }\n\n if (!rootExisted) {\n removeEmptyDirectory(extensionRoot);\n }\n}\n\n/**\n * Restores a single owned file to its pre-publish state, atomically.\n *\n * Two cases:\n *\n * - The file did NOT exist before publish (publish created it): remove it to\n * undo the publish. {@link assertSafeExistingPath} re-checks it is not a\n * symlink swapped in after the snapshot; `rmSync` removes a symlink itself\n * rather than following it, but refusing surfaces tampering loudly.\n *\n * - The file DID exist before publish: restore its prior content using\n * \"write-new-before-delete-old\" (rules 42/54). We write the prior content to\n * a temp path in the same directory, then {@link fs.renameSync} it into\n * place. The live file is never truncated, so a mid-restore failure (disk\n * full, EACCES, …) leaves the current on-disk content intact rather than\n * half-written — the restore either fully lands or does nothing. The temp\n * path uses the `.tmp-<pid>-<ts>` suffix tracked by\n * {@link EXTENSION_OWNED_TEMP_FILE_SUFFIX}, so any lingering temp is swept\n * by unpublish. The final `renameSync` does NOT follow a symlink even if one\n * was swapped into the snapshot path after the snapshot (TOCTOU\n * defense-in-depth): `rename(2)` replaces the symlink itself, so no write\n * ever reaches an arbitrary target. We still re-check for a symlink right\n * before the rename so the rollback surfaces tampering instead of silently\n * replacing it.\n */\nfunction restoreOwnedFile(snapshot: FileSnapshot): void {\n if (!snapshot.existed) {\n assertSafeExistingPath(snapshot.path);\n fs.rmSync(snapshot.path, { force: true });\n return;\n }\n\n fs.mkdirSync(path.dirname(snapshot.path), { recursive: true });\n const tmpPath = `${snapshot.path}.tmp-${process.pid}-${Date.now()}`;\n try {\n fs.writeFileSync(tmpPath, snapshot.content ?? Buffer.alloc(0), {\n mode: snapshot.mode ?? 0o644,\n });\n if (snapshot.mode !== undefined) {\n try {\n fs.chmodSync(tmpPath, snapshot.mode);\n } catch {\n // Best effort for platforms that do not support chmod.\n }\n }\n rejectSymlinkPath(snapshot.path);\n fs.renameSync(tmpPath, snapshot.path);\n } catch (err) {\n try {\n if (fs.existsSync(tmpPath)) fs.unlinkSync(tmpPath);\n } catch {\n // Best-effort cleanup only.\n }\n throw err;\n }\n}\n\nfunction snapshotDirs(paths: string[]): DirSnapshot[] {\n return paths.map((dirPath) => {\n let existed = false;\n try {\n const stat = fs.lstatSync(dirPath);\n if (stat.isSymbolicLink()) {\n throw new Error(`Extension path must not be a symlink: ${dirPath}`);\n }\n existed = stat.isDirectory();\n } catch (err) {\n if (err && typeof err === \"object\" && \"code\" in err && err.code === \"ENOENT\") {\n existed = false;\n } else {\n throw err;\n }\n }\n return { path: dirPath, existed };\n });\n}\n\nfunction restoreDirSnapshots(snapshots: DirSnapshot[]): void {\n for (const snapshot of snapshots) {\n if (snapshot.existed) continue;\n try {\n fs.rmSync(snapshot.path, { recursive: true, force: true });\n } catch {\n // best-effort — the loader self-heals at runtime if the dir lingers\n }\n }\n}\n\n\nfunction canCleanNewExtensionRoot(extensionRoot: string): boolean {\n let stat: fs.Stats;\n try {\n stat = fs.lstatSync(extensionRoot);\n } catch (err) {\n if (err && typeof err === \"object\" && \"code\" in err && err.code === \"ENOENT\") return false;\n throw err;\n }\n if (stat.isSymbolicLink()) {\n throw new Error(`Extension path must not be a symlink: ${extensionRoot}`);\n }\n return stat.isDirectory();\n}\n\nfunction removeEmptyDirectory(dirPath: string): void {\n let stat: fs.Stats;\n try {\n stat = fs.lstatSync(dirPath);\n } catch (err) {\n if (err && typeof err === \"object\" && \"code\" in err && err.code === \"ENOENT\") return;\n throw err;\n }\n if (stat.isSymbolicLink()) {\n throw new Error(`Extension path must not be a symlink: ${dirPath}`);\n }\n if (!stat.isDirectory()) return;\n if (fs.readdirSync(dirPath).length > 0) return;\n fs.rmdirSync(dirPath);\n}\n\nfunction removableOwnedExtensionFiles(filePaths: string[]): string[] {\n const removableFiles: string[] = [];\n for (const filePath of filePaths) {\n const stat = statOwnedExtensionPath(filePath);\n if (stat === null) continue;\n if (stat.isSymbolicLink()) {\n throw new Error(`Extension path must not be a symlink: ${filePath}`);\n }\n if (stat.isFile()) removableFiles.push(filePath);\n }\n return removableFiles;\n}\n\nfunction statOwnedExtensionPath(filePath: string): fs.Stats | null {\n let stat: fs.Stats;\n try {\n stat = fs.lstatSync(filePath);\n } catch (err) {\n if (err && typeof err === \"object\" && \"code\" in err && err.code === \"ENOENT\") return null;\n throw err;\n }\n return stat;\n}\n\nfunction mkdirExtensionRoot(extensionRoot: string, agentHome: string): void {\n const extensionsDir = path.join(path.resolve(agentHome), \"extensions\");\n assertSafeExtensionRoot(extensionRoot, agentHome);\n fs.mkdirSync(extensionsDir, { recursive: true });\n rejectSymlinkPath(extensionsDir);\n fs.mkdirSync(extensionRoot, { recursive: true });\n rejectSymlinkPath(extensionRoot);\n}\n\nfunction assertSafeExtensionRoot(extensionRoot: string, agentHome: string): void {\n const resolvedAgentHome = path.resolve(agentHome);\n const expected = path.join(resolvedAgentHome, \"extensions\", \"remnic\");\n if (path.resolve(extensionRoot) !== path.resolve(expected)) {\n throw new Error(`Extension root is outside the configured extensions directory: ${extensionRoot}`);\n }\n const extensionsDir = path.join(resolvedAgentHome, \"extensions\");\n assertPathContained(resolvedAgentHome, extensionsDir);\n assertPathContained(extensionsDir, extensionRoot);\n rejectSymlinkPath(resolvedAgentHome);\n if (fs.existsSync(extensionsDir)) rejectSymlinkPath(extensionsDir);\n if (fs.existsSync(extensionRoot)) rejectSymlinkPath(extensionRoot);\n}\n\nfunction assertSafeExistingPath(filePath: string): void {\n if (fs.existsSync(filePath)) rejectSymlinkPath(filePath);\n}\n\nfunction rejectSymlinkPath(filePath: string): void {\n if (!fs.existsSync(filePath)) return;\n const stat = fs.lstatSync(filePath);\n if (stat.isSymbolicLink()) {\n throw new Error(`Extension path must not be a symlink: ${filePath}`);\n }\n}\n\nfunction assertPathContained(root: string, candidate: string): void {\n const rootResolved = path.resolve(root);\n const candidateResolved = path.resolve(candidate);\n const relative = path.relative(rootResolved, candidateResolved);\n if (relative === \"\" || (!relative.startsWith(\"..\") && !path.isAbsolute(relative))) return;\n throw new Error(`Extension path escapes allowed root: ${candidate}`);\n}\n\nfunction readPriorConfig(configPath: string): Record<string, unknown> {\n if (!fs.existsSync(configPath)) return {};\n try {\n const parsed = JSON.parse(fs.readFileSync(configPath, \"utf8\"));\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n throw new Error(\"expected a JSON object\");\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n throw new Error(`Failed to load existing Remnic Pi config at ${configPath}: ${reason}`);\n }\n}\n\nfunction snapshotTokenEntry(connectorId: string): TokenEntry | null {\n const entry = loadTokenStore().tokens.find((candidate) => candidate.connector === connectorId);\n return cloneTokenEntry(entry ?? null);\n}\n\nfunction cloneTokenEntry(entry: TokenEntry | null): TokenEntry | null {\n return entry ? { ...entry } : null;\n}\n\nfunction restoreTokenEntry(priorEntry: TokenEntry | null, connectorId: string): void {\n const store = loadTokenStore();\n store.tokens = store.tokens.filter((entry) => entry.connector !== connectorId);\n if (priorEntry) store.tokens.push(priorEntry);\n saveTokenStore(store);\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remnic/plugin-pi",
|
|
3
|
-
"version": "9.3.
|
|
3
|
+
"version": "9.3.726",
|
|
4
4
|
"description": "Remnic memory extension for Pi Coding Agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@sinclair/typebox": "^0.34.0",
|
|
45
|
-
"@remnic/core": "^9.3.
|
|
45
|
+
"@remnic/core": "^9.3.726"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
48
|
"@earendil-works/pi-coding-agent": "*"
|