@ai-agent-forge/agent-forge 0.88.8 → 0.88.10

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.
Files changed (50) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/bundled-plugins/agent-loop-policy/agent-forge.json +1 -1
  3. package/bundled-plugins/agent-loop-policy/plugin.json +1 -1
  4. package/bundled-plugins/alignment/agent-forge.json +1 -1
  5. package/bundled-plugins/alignment/plugin.json +1 -1
  6. package/bundled-plugins/browser-playwright/agent-forge.json +1 -1
  7. package/bundled-plugins/browser-playwright/plugin.json +1 -1
  8. package/bundled-plugins/coding-tools/agent-forge.json +1 -1
  9. package/bundled-plugins/coding-tools/plugin.json +1 -1
  10. package/bundled-plugins/compaction-policy/agent-forge.json +1 -1
  11. package/bundled-plugins/compaction-policy/plugin.json +1 -1
  12. package/bundled-plugins/overflow-policy/agent-forge.json +1 -1
  13. package/bundled-plugins/overflow-policy/plugin.json +1 -1
  14. package/bundled-plugins/provider-failover/agent-forge.json +1 -1
  15. package/bundled-plugins/provider-failover/plugin.json +1 -1
  16. package/bundled-plugins/retry-policy/agent-forge.json +1 -1
  17. package/bundled-plugins/retry-policy/plugin.json +1 -1
  18. package/dist/bundle/.build-manifest.json +11 -11
  19. package/dist/bundle/chunks/{chunk-ZS5JEI76.js → chunk-4BD6FSZA.js} +1 -1
  20. package/dist/bundle/chunks/{chunk-ISLHFRER.js → chunk-F73USSXE.js} +1 -1
  21. package/dist/bundle/chunks/{chunk-WJSKB3FC.js → chunk-HXGAAJQQ.js} +1 -1
  22. package/dist/bundle/chunks/{chunk-L2SXIPI5.js → chunk-ZZCUNSGO.js} +4 -4
  23. package/dist/bundle/chunks/image-resize-worker.js +1 -1
  24. package/dist/bundle/chunks/{virtual-modules-2QGILFV7.js → virtual-modules-UQRUJLPM.js} +1 -1
  25. package/dist/bundle/cli.js +1 -1
  26. package/dist/bundle/client.js +1 -1
  27. package/dist/bundle/index.js +1 -1
  28. package/dist/bundle/rpc-entry.js +1 -1
  29. package/dist/core/i18n/locales/zh-cn/cli.d.ts.map +1 -1
  30. package/dist/core/i18n/locales/zh-cn/cli.js +2 -1
  31. package/dist/core/i18n/locales/zh-cn/cli.js.map +1 -1
  32. package/dist/plugins/ecosystem/catalog.d.ts +1 -1
  33. package/dist/plugins/ecosystem/catalog.d.ts.map +1 -1
  34. package/dist/plugins/ecosystem/catalog.js +10 -4
  35. package/dist/plugins/ecosystem/catalog.js.map +1 -1
  36. package/dist/plugins/ecosystem/node-disk-registry-source.d.ts.map +1 -1
  37. package/dist/plugins/ecosystem/node-disk-registry-source.js +12 -0
  38. package/dist/plugins/ecosystem/node-disk-registry-source.js.map +1 -1
  39. package/dist/plugins/ecosystem/node-plugin-bootstrap.d.ts +6 -1
  40. package/dist/plugins/ecosystem/node-plugin-bootstrap.d.ts.map +1 -1
  41. package/dist/plugins/ecosystem/node-plugin-bootstrap.js +11 -3
  42. package/dist/plugins/ecosystem/node-plugin-bootstrap.js.map +1 -1
  43. package/dist/plugins/ecosystem/plugin-bootstrap.d.ts +10 -0
  44. package/dist/plugins/ecosystem/plugin-bootstrap.d.ts.map +1 -1
  45. package/dist/plugins/ecosystem/plugin-bootstrap.js +66 -23
  46. package/dist/plugins/ecosystem/plugin-bootstrap.js.map +1 -1
  47. package/examples/extensions/with-deps/package-lock.json +2 -2
  48. package/examples/extensions/with-deps/package.json +1 -1
  49. package/examples/extensions/with-deps/plugin.json +1 -1
  50. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"node-disk-registry-source.js","sourceRoot":"","sources":["../../../src/plugins/ecosystem/node-disk-registry-source.ts"],"names":[],"mappings":"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","sourcesContent":["import { open } from \"node:fs/promises\";\nimport { isAbsolute, join } from \"node:path\";\nimport { t } from \"../../core/i18n/index.ts\";\nimport { withinInstallStoreRoot } from \"./install-store-paths.ts\";\nimport type { PluginRegistryManifestEnvelopeV1, PluginRegistrySourceV1 } from \"./registry.ts\";\nimport type { PluginManifestV1 } from \"./types.ts\";\n\nconst NODE_DISK_REGISTRY_SOURCE_VERSION_V1 = 1 as const;\n/** Maximum index.v1.json bytes accepted by this source boundary. */\nexport const MAX_NODE_DISK_REGISTRY_INDEX_BYTES_V1 = 1024 * 1024;\n/** Maximum per-installation agent-forge.json bytes accepted by this source. */\nexport const MAX_NODE_DISK_REGISTRY_MANIFEST_BYTES_V1 = 64 * 1024;\n/** Maximum index records projected by one list() call; matches the registry manifest cap. */\nexport const MAX_NODE_DISK_REGISTRY_RECORDS_V1 = 256;\n\nconst INDEX_FILE_V1 = \"index.v1.json\";\nconst ECOSYSTEM_MANIFEST_FILE_V1 = \"agent-forge.json\";\nconst CAPABILITY_MANIFEST_FILE_V1 = \"plugin.json\";\nconst NODE_PACKAGE_FORMAT_VERSION_V1 = 1;\n\nexport type NodeDiskRegistrySourceErrorCodeV1 =\n\t| \"cancelled\"\n\t| \"invalid_configuration\"\n\t| \"invalid_index\"\n\t| \"invalid_manifest\"\n\t| \"identity_mismatch\"\n\t| \"too_many_records\"\n\t| \"io_failed\";\n\n/**\n * Deliberately does not retain filesystem exceptions or file contents: callers\n * must not turn registry diagnostics into a path or package disclosure.\n */\nexport class NodeDiskRegistrySourceErrorV1 extends Error {\n\treadonly code: NodeDiskRegistrySourceErrorCodeV1;\n\n\tconstructor(code: NodeDiskRegistrySourceErrorCodeV1, message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"NodeDiskRegistrySourceErrorV1\";\n\t\tthis.code = code;\n\t}\n}\n\nexport interface NodeDiskRegistrySourceOptionsV1 {\n\t/** Absolute host-owned default install store directory. It is never returned in a DTO. */\n\treadonly rootDirectory: string;\n}\n\nexport interface NodeDiskRegistrySourceResolveInputV1 {\n\treadonly pluginId: string;\n\treadonly pluginVersion: string;\n\treadonly signal?: AbortSignal;\n}\n\nexport interface NodeDiskRegistrySourceV1 extends PluginRegistrySourceV1 {\n\treadonly version: typeof NODE_DISK_REGISTRY_SOURCE_VERSION_V1;\n\t/**\n\t * Internal host channel (D-075 S2): absolute `plugin.json` path of the\n\t * installation directory matching `{pluginId, pluginVersion}` in the store\n\t * index, for the sdk's installed-plugin code loading. Registry snapshots\n\t * still carry no paths; undefined means no matching record or the manifest\n\t * file is absent from the installation directory.\n\t */\n\treadonly resolveCapabilityManifestPath?: (\n\t\tinput: NodeDiskRegistrySourceResolveInputV1,\n\t) => Promise<string | undefined>;\n}\n\ninterface DiskIndexRecordV1 {\n\treadonly installationRef: string;\n\treadonly directory: string;\n\treadonly pluginId: string;\n\treadonly pluginVersion: string;\n\treadonly manifestDigest: string;\n\treadonly scope: \"global\" | \"project\";\n\treadonly scopeRef: string;\n\treadonly state: \"installed\" | \"active\" | \"retained\";\n}\n\nconst INDEX_KEYS_V1 = [\n\t\"installationRef\",\n\t\"directory\",\n\t\"pluginId\",\n\t\"pluginVersion\",\n\t\"manifestDigest\",\n\t\"scope\",\n\t\"scopeRef\",\n\t\"state\",\n] as const;\n\nfunction opaqueField(value: unknown, max: number): value is string {\n\treturn (\n\t\ttypeof value === \"string\" &&\n\t\tvalue.length > 0 &&\n\t\tvalue.length <= max &&\n\t\t!/[\\u0000-\\u001f\\u007f]/u.test(value) &&\n\t\t!/\\s/u.test(value) &&\n\t\t!/^(?:file:|[a-z]:[\\\\/]|[\\\\/]{1,2}|\\\\\\\\)/iu.test(value)\n\t);\n}\n\nfunction boundedField(value: unknown, max: number): value is string {\n\treturn typeof value === \"string\" && value.length > 0 && value.length <= max && !/[\\u0000-\\u001f\\u007f]/u.test(value);\n}\n\nfunction validateRecord(root: string, value: unknown, index: number): DiskIndexRecordV1 {\n\tif (value === null || typeof value !== \"object\" || Array.isArray(value))\n\t\tthrow new NodeDiskRegistrySourceErrorV1(\"invalid_index\", t(\"Index record {index} is not an object\", { index }));\n\tconst record = value as Record<string, unknown>;\n\tconst keys = INDEX_KEYS_V1;\n\tif (Object.keys(record).length !== keys.length || keys.some((key) => !(key in record)))\n\t\tthrow new NodeDiskRegistrySourceErrorV1(\n\t\t\t\"invalid_index\",\n\t\t\tt(\"Index record {index} has an invalid schema\", { index }),\n\t\t);\n\tif (\n\t\t!opaqueField(record.installationRef, 2048) ||\n\t\t!boundedField(record.directory, 2048) ||\n\t\t!boundedField(record.pluginId, 256) ||\n\t\t!boundedField(record.pluginVersion, 256) ||\n\t\t!boundedField(record.manifestDigest, 512) ||\n\t\t!opaqueField(record.scopeRef, 2048) ||\n\t\t(record.scope !== \"global\" && record.scope !== \"project\") ||\n\t\t(record.state !== \"installed\" && record.state !== \"active\" && record.state !== \"retained\") ||\n\t\t!withinInstallStoreRoot(root, record.directory)\n\t)\n\t\tthrow new NodeDiskRegistrySourceErrorV1(\"invalid_index\", t(\"Index record {index} is invalid\", { index }));\n\treturn {\n\t\tinstallationRef: record.installationRef,\n\t\tdirectory: record.directory,\n\t\tpluginId: record.pluginId,\n\t\tpluginVersion: record.pluginVersion,\n\t\tmanifestDigest: record.manifestDigest,\n\t\tscope: record.scope,\n\t\tscopeRef: record.scopeRef,\n\t\tstate: record.state,\n\t};\n}\n\nfunction parseIndex(root: string, raw: string): readonly DiskIndexRecordV1[] {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch {\n\t\tthrow new NodeDiskRegistrySourceErrorV1(\"invalid_index\", t(\"Install store index JSON is invalid\"));\n\t}\n\tif (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed))\n\t\tthrow new NodeDiskRegistrySourceErrorV1(\"invalid_index\", t(\"Install store index is not an object\"));\n\tconst value = parsed as Record<string, unknown>;\n\tif (\n\t\tObject.keys(value).length !== 2 ||\n\t\tvalue.version !== NODE_DISK_REGISTRY_SOURCE_VERSION_V1 ||\n\t\t!Array.isArray(value.records)\n\t)\n\t\tthrow new NodeDiskRegistrySourceErrorV1(\"invalid_index\", t(\"Install store index schema is unsupported\"));\n\tif (value.records.length > MAX_NODE_DISK_REGISTRY_RECORDS_V1)\n\t\tthrow new NodeDiskRegistrySourceErrorV1(\"too_many_records\", t(\"Install store index exceeds the record limit\"));\n\treturn value.records.map((record, index) => validateRecord(root, record, index));\n}\n\n/** Read a bounded UTF-8 file; the absolute path never appears in a thrown message. */\nasync function readBoundedFile(path: string, maxBytes: number, label: string): Promise<string> {\n\tconst handle = await open(path, \"r\");\n\ttry {\n\t\tconst stat = await handle.stat();\n\t\tif (!stat.isFile() || stat.size > maxBytes)\n\t\t\tthrow new NodeDiskRegistrySourceErrorV1(\n\t\t\t\tlabel === \"index\" ? \"invalid_index\" : \"invalid_manifest\",\n\t\t\t\tt(\"{label} exceeds the size limit\", { label }),\n\t\t\t);\n\t\treturn await handle.readFile(\"utf8\");\n\t} finally {\n\t\tawait handle.close().catch(() => undefined);\n\t}\n}\n\nfunction parseManifest(record: DiskIndexRecordV1, raw: string): PluginManifestV1 {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch {\n\t\tthrow new NodeDiskRegistrySourceErrorV1(\"invalid_manifest\", t(\"Installed plugin manifest JSON is invalid\"));\n\t}\n\tif (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed))\n\t\tthrow new NodeDiskRegistrySourceErrorV1(\"invalid_manifest\", t(\"Installed plugin manifest is not an object\"));\n\tconst value = { ...(parsed as Record<string, unknown>) };\n\tif (value.packageFormatVersion !== undefined && value.packageFormatVersion !== NODE_PACKAGE_FORMAT_VERSION_V1)\n\t\tthrow new NodeDiskRegistrySourceErrorV1(\"invalid_manifest\", t(\"Installed plugin package format is unsupported\"));\n\t// The package envelope version is an install-host field; it is not part of\n\t// PluginManifestV1 and the stable manifest digest excludes it.\n\tdelete value.packageFormatVersion;\n\tif (value.id !== record.pluginId || value.pluginVersion !== record.pluginVersion)\n\t\tthrow new NodeDiskRegistrySourceErrorV1(\n\t\t\t\"identity_mismatch\",\n\t\t\tt(\"Installed plugin manifest identity does not match index record for {identity}\", {\n\t\t\t\tidentity: `${record.pluginId}@${record.pluginVersion}`,\n\t\t\t}),\n\t\t);\n\treturn value as unknown as PluginManifestV1;\n}\n\n/** Read the install store index records; undefined when the store is empty. */\nasync function readIndexRecords(\n\trootDirectory: string,\n\tsignal: AbortSignal | undefined,\n): Promise<readonly DiskIndexRecordV1[] | undefined> {\n\tif (signal?.aborted)\n\t\tthrow new NodeDiskRegistrySourceErrorV1(\"cancelled\", t(\"Disk registry source read was cancelled\"));\n\tlet rawIndex: string;\n\ttry {\n\t\trawIndex = await readBoundedFile(\n\t\t\tjoin(rootDirectory, INDEX_FILE_V1),\n\t\t\tMAX_NODE_DISK_REGISTRY_INDEX_BYTES_V1,\n\t\t\t\"index\",\n\t\t);\n\t} catch (error) {\n\t\tif (error instanceof NodeDiskRegistrySourceErrorV1) throw error;\n\t\tif (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") return undefined;\n\t\tthrow new NodeDiskRegistrySourceErrorV1(\"io_failed\", t(\"Install store index could not be read\"));\n\t}\n\treturn parseIndex(rootDirectory, rawIndex);\n}\n\n/**\n * Read-only registry source over the default install store. It projects the\n * persistent index and each installation instance's ecosystem manifest as\n * bounded envelopes for the registry's own digest verification. It never\n * writes the index or installation directories, and installation directories\n * stay inside the host-owned store.\n */\nexport function createNodeDiskPluginRegistrySourceV1(\n\toptions: NodeDiskRegistrySourceOptionsV1,\n): NodeDiskRegistrySourceV1 {\n\tconst rootDirectory = options.rootDirectory;\n\tif (typeof rootDirectory !== \"string\" || !isAbsolute(rootDirectory) || rootDirectory.trim() === \"\")\n\t\tthrow new NodeDiskRegistrySourceErrorV1(\n\t\t\t\"invalid_configuration\",\n\t\t\tt(\"Install store root directory must be absolute\"),\n\t\t);\n\treturn {\n\t\tversion: NODE_DISK_REGISTRY_SOURCE_VERSION_V1,\n\t\tlist: async ({ signal } = {}) => {\n\t\t\tconst records = await readIndexRecords(rootDirectory, signal);\n\t\t\tif (records === undefined) return Object.freeze([]);\n\t\t\tconst envelopes: PluginRegistryManifestEnvelopeV1[] = [];\n\t\t\tfor (const record of records) {\n\t\t\t\tif (signal?.aborted)\n\t\t\t\t\tthrow new NodeDiskRegistrySourceErrorV1(\"cancelled\", t(\"Disk registry source read was cancelled\"));\n\t\t\t\tlet rawManifest: string;\n\t\t\t\ttry {\n\t\t\t\t\trawManifest = await readBoundedFile(\n\t\t\t\t\t\tjoin(record.directory, ECOSYSTEM_MANIFEST_FILE_V1),\n\t\t\t\t\t\tMAX_NODE_DISK_REGISTRY_MANIFEST_BYTES_V1,\n\t\t\t\t\t\t\"manifest\",\n\t\t\t\t\t);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (error instanceof NodeDiskRegistrySourceErrorV1) throw error;\n\t\t\t\t\tthrow new NodeDiskRegistrySourceErrorV1(\"io_failed\", t(\"Installed plugin manifest could not be read\"));\n\t\t\t\t}\n\t\t\t\tconst manifest = parseManifest(record, rawManifest);\n\t\t\t\tenvelopes.push({\n\t\t\t\t\tmanifest,\n\t\t\t\t\tsource: record.installationRef,\n\t\t\t\t\tmanifestDigest: record.manifestDigest,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn Object.freeze(envelopes);\n\t\t},\n\t\tresolveCapabilityManifestPath: async ({ pluginId, pluginVersion, signal }) => {\n\t\t\tconst records = await readIndexRecords(rootDirectory, signal);\n\t\t\tif (records === undefined) return undefined;\n\t\t\tconst record = records.find(\n\t\t\t\t(candidate) => candidate.pluginId === pluginId && candidate.pluginVersion === pluginVersion,\n\t\t\t);\n\t\t\tif (record === undefined) return undefined;\n\t\t\tconst manifestPath = join(record.directory, CAPABILITY_MANIFEST_FILE_V1);\n\t\t\ttry {\n\t\t\t\tconst handle = await open(manifestPath, \"r\");\n\t\t\t\tawait handle.close().catch(() => undefined);\n\t\t\t} catch {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\treturn manifestPath;\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"node-disk-registry-source.js","sourceRoot":"","sources":["../../../src/plugins/ecosystem/node-disk-registry-source.ts"],"names":[],"mappings":"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","sourcesContent":["import { open } from \"node:fs/promises\";\nimport { isAbsolute, join } from \"node:path\";\nimport { t } from \"../../core/i18n/index.ts\";\nimport { withinInstallStoreRoot } from \"./install-store-paths.ts\";\nimport type { PluginRegistryManifestEnvelopeV1, PluginRegistrySourceV1 } from \"./registry.ts\";\nimport type { PluginManifestV1 } from \"./types.ts\";\n\nconst NODE_DISK_REGISTRY_SOURCE_VERSION_V1 = 1 as const;\n/** Maximum index.v1.json bytes accepted by this source boundary. */\nexport const MAX_NODE_DISK_REGISTRY_INDEX_BYTES_V1 = 1024 * 1024;\n/** Maximum per-installation agent-forge.json bytes accepted by this source. */\nexport const MAX_NODE_DISK_REGISTRY_MANIFEST_BYTES_V1 = 64 * 1024;\n/** Maximum index records projected by one list() call; matches the registry manifest cap. */\nexport const MAX_NODE_DISK_REGISTRY_RECORDS_V1 = 256;\n\nconst INDEX_FILE_V1 = \"index.v1.json\";\nconst ECOSYSTEM_MANIFEST_FILE_V1 = \"agent-forge.json\";\nconst CAPABILITY_MANIFEST_FILE_V1 = \"plugin.json\";\nconst NODE_PACKAGE_FORMAT_VERSION_V1 = 1;\n\nexport type NodeDiskRegistrySourceErrorCodeV1 =\n\t| \"cancelled\"\n\t| \"invalid_configuration\"\n\t| \"invalid_index\"\n\t| \"invalid_manifest\"\n\t| \"identity_mismatch\"\n\t| \"too_many_records\"\n\t| \"io_failed\";\n\n/**\n * Deliberately does not retain filesystem exceptions or file contents: callers\n * must not turn registry diagnostics into a path or package disclosure.\n */\nexport class NodeDiskRegistrySourceErrorV1 extends Error {\n\treadonly code: NodeDiskRegistrySourceErrorCodeV1;\n\n\tconstructor(code: NodeDiskRegistrySourceErrorCodeV1, message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"NodeDiskRegistrySourceErrorV1\";\n\t\tthis.code = code;\n\t}\n}\n\nexport interface NodeDiskRegistrySourceOptionsV1 {\n\t/** Absolute host-owned default install store directory. It is never returned in a DTO. */\n\treadonly rootDirectory: string;\n}\n\nexport interface NodeDiskRegistrySourceResolveInputV1 {\n\treadonly pluginId: string;\n\treadonly pluginVersion: string;\n\treadonly signal?: AbortSignal;\n}\n\nexport interface NodeDiskRegistrySourceV1 extends PluginRegistrySourceV1 {\n\treadonly version: typeof NODE_DISK_REGISTRY_SOURCE_VERSION_V1;\n\t/**\n\t * Internal host channel (D-075 S2): absolute `plugin.json` path of the\n\t * installation directory matching `{pluginId, pluginVersion}` in the store\n\t * index, for the sdk's installed-plugin code loading. Registry snapshots\n\t * still carry no paths; undefined means no matching record or the manifest\n\t * file is absent from the installation directory.\n\t */\n\treadonly resolveCapabilityManifestPath?: (\n\t\tinput: NodeDiskRegistrySourceResolveInputV1,\n\t) => Promise<string | undefined>;\n}\n\ninterface DiskIndexRecordV1 {\n\treadonly installationRef: string;\n\treadonly directory: string;\n\treadonly pluginId: string;\n\treadonly pluginVersion: string;\n\treadonly manifestDigest: string;\n\treadonly scope: \"global\" | \"project\";\n\treadonly scopeRef: string;\n\treadonly state: \"installed\" | \"active\" | \"retained\";\n}\n\nconst INDEX_KEYS_V1 = [\n\t\"installationRef\",\n\t\"directory\",\n\t\"pluginId\",\n\t\"pluginVersion\",\n\t\"manifestDigest\",\n\t\"scope\",\n\t\"scopeRef\",\n\t\"state\",\n] as const;\n\nfunction opaqueField(value: unknown, max: number): value is string {\n\treturn (\n\t\ttypeof value === \"string\" &&\n\t\tvalue.length > 0 &&\n\t\tvalue.length <= max &&\n\t\t!/[\\u0000-\\u001f\\u007f]/u.test(value) &&\n\t\t!/\\s/u.test(value) &&\n\t\t!/^(?:file:|[a-z]:[\\\\/]|[\\\\/]{1,2}|\\\\\\\\)/iu.test(value)\n\t);\n}\n\nfunction boundedField(value: unknown, max: number): value is string {\n\treturn typeof value === \"string\" && value.length > 0 && value.length <= max && !/[\\u0000-\\u001f\\u007f]/u.test(value);\n}\n\nfunction validateRecord(root: string, value: unknown, index: number): DiskIndexRecordV1 {\n\tif (value === null || typeof value !== \"object\" || Array.isArray(value))\n\t\tthrow new NodeDiskRegistrySourceErrorV1(\"invalid_index\", t(\"Index record {index} is not an object\", { index }));\n\tconst record = value as Record<string, unknown>;\n\tconst keys = INDEX_KEYS_V1;\n\tif (Object.keys(record).length !== keys.length || keys.some((key) => !(key in record)))\n\t\tthrow new NodeDiskRegistrySourceErrorV1(\n\t\t\t\"invalid_index\",\n\t\t\tt(\"Index record {index} has an invalid schema\", { index }),\n\t\t);\n\tif (\n\t\t!opaqueField(record.installationRef, 2048) ||\n\t\t!boundedField(record.directory, 2048) ||\n\t\t!boundedField(record.pluginId, 256) ||\n\t\t!boundedField(record.pluginVersion, 256) ||\n\t\t!boundedField(record.manifestDigest, 512) ||\n\t\t!opaqueField(record.scopeRef, 2048) ||\n\t\t(record.scope !== \"global\" && record.scope !== \"project\") ||\n\t\t(record.state !== \"installed\" && record.state !== \"active\" && record.state !== \"retained\") ||\n\t\t!withinInstallStoreRoot(root, record.directory)\n\t)\n\t\tthrow new NodeDiskRegistrySourceErrorV1(\"invalid_index\", t(\"Index record {index} is invalid\", { index }));\n\treturn {\n\t\tinstallationRef: record.installationRef,\n\t\tdirectory: record.directory,\n\t\tpluginId: record.pluginId,\n\t\tpluginVersion: record.pluginVersion,\n\t\tmanifestDigest: record.manifestDigest,\n\t\tscope: record.scope,\n\t\tscopeRef: record.scopeRef,\n\t\tstate: record.state,\n\t};\n}\n\nfunction parseIndex(root: string, raw: string): readonly DiskIndexRecordV1[] {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch {\n\t\tthrow new NodeDiskRegistrySourceErrorV1(\"invalid_index\", t(\"Install store index JSON is invalid\"));\n\t}\n\tif (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed))\n\t\tthrow new NodeDiskRegistrySourceErrorV1(\"invalid_index\", t(\"Install store index is not an object\"));\n\tconst value = parsed as Record<string, unknown>;\n\tif (\n\t\tObject.keys(value).length !== 2 ||\n\t\tvalue.version !== NODE_DISK_REGISTRY_SOURCE_VERSION_V1 ||\n\t\t!Array.isArray(value.records)\n\t)\n\t\tthrow new NodeDiskRegistrySourceErrorV1(\"invalid_index\", t(\"Install store index schema is unsupported\"));\n\tif (value.records.length > MAX_NODE_DISK_REGISTRY_RECORDS_V1)\n\t\tthrow new NodeDiskRegistrySourceErrorV1(\"too_many_records\", t(\"Install store index exceeds the record limit\"));\n\treturn value.records.map((record, index) => validateRecord(root, record, index));\n}\n\n/** Read a bounded UTF-8 file; the absolute path never appears in a thrown message. */\nasync function readBoundedFile(path: string, maxBytes: number, label: string): Promise<string> {\n\tconst handle = await open(path, \"r\");\n\ttry {\n\t\tconst stat = await handle.stat();\n\t\tif (!stat.isFile() || stat.size > maxBytes)\n\t\t\tthrow new NodeDiskRegistrySourceErrorV1(\n\t\t\t\tlabel === \"index\" ? \"invalid_index\" : \"invalid_manifest\",\n\t\t\t\tt(\"{label} exceeds the size limit\", { label }),\n\t\t\t);\n\t\treturn await handle.readFile(\"utf8\");\n\t} finally {\n\t\tawait handle.close().catch(() => undefined);\n\t}\n}\n\nfunction parseManifest(record: DiskIndexRecordV1, raw: string): PluginManifestV1 {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch {\n\t\tthrow new NodeDiskRegistrySourceErrorV1(\"invalid_manifest\", t(\"Installed plugin manifest JSON is invalid\"));\n\t}\n\tif (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed))\n\t\tthrow new NodeDiskRegistrySourceErrorV1(\"invalid_manifest\", t(\"Installed plugin manifest is not an object\"));\n\tconst value = { ...(parsed as Record<string, unknown>) };\n\tif (value.packageFormatVersion !== undefined && value.packageFormatVersion !== NODE_PACKAGE_FORMAT_VERSION_V1)\n\t\tthrow new NodeDiskRegistrySourceErrorV1(\"invalid_manifest\", t(\"Installed plugin package format is unsupported\"));\n\t// The package envelope version is an install-host field; it is not part of\n\t// PluginManifestV1 and the stable manifest digest excludes it.\n\tdelete value.packageFormatVersion;\n\tif (value.id !== record.pluginId || value.pluginVersion !== record.pluginVersion)\n\t\tthrow new NodeDiskRegistrySourceErrorV1(\n\t\t\t\"identity_mismatch\",\n\t\t\tt(\"Installed plugin manifest identity does not match index record for {identity}\", {\n\t\t\t\tidentity: `${record.pluginId}@${record.pluginVersion}`,\n\t\t\t}),\n\t\t);\n\treturn value as unknown as PluginManifestV1;\n}\n\n/** Read the install store index records; undefined when the store is empty. */\nasync function readIndexRecords(\n\trootDirectory: string,\n\tsignal: AbortSignal | undefined,\n): Promise<readonly DiskIndexRecordV1[] | undefined> {\n\tif (signal?.aborted)\n\t\tthrow new NodeDiskRegistrySourceErrorV1(\"cancelled\", t(\"Disk registry source read was cancelled\"));\n\tlet rawIndex: string;\n\ttry {\n\t\trawIndex = await readBoundedFile(\n\t\t\tjoin(rootDirectory, INDEX_FILE_V1),\n\t\t\tMAX_NODE_DISK_REGISTRY_INDEX_BYTES_V1,\n\t\t\t\"index\",\n\t\t);\n\t} catch (error) {\n\t\tif (error instanceof NodeDiskRegistrySourceErrorV1) throw error;\n\t\tif (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") return undefined;\n\t\tthrow new NodeDiskRegistrySourceErrorV1(\"io_failed\", t(\"Install store index could not be read\"));\n\t}\n\treturn parseIndex(rootDirectory, rawIndex);\n}\n\n/**\n * Read-only registry source over the default install store. It projects the\n * persistent index and each installation instance's ecosystem manifest as\n * bounded envelopes for the registry's own digest verification. It never\n * writes the index or installation directories, and installation directories\n * stay inside the host-owned store.\n */\nexport function createNodeDiskPluginRegistrySourceV1(\n\toptions: NodeDiskRegistrySourceOptionsV1,\n): NodeDiskRegistrySourceV1 {\n\tconst rootDirectory = options.rootDirectory;\n\tif (typeof rootDirectory !== \"string\" || !isAbsolute(rootDirectory) || rootDirectory.trim() === \"\")\n\t\tthrow new NodeDiskRegistrySourceErrorV1(\n\t\t\t\"invalid_configuration\",\n\t\t\tt(\"Install store root directory must be absolute\"),\n\t\t);\n\treturn {\n\t\tversion: NODE_DISK_REGISTRY_SOURCE_VERSION_V1,\n\t\tlist: async ({ signal } = {}) => {\n\t\t\tconst records = await readIndexRecords(rootDirectory, signal);\n\t\t\tif (records === undefined) return Object.freeze([]);\n\t\t\tconst envelopes: PluginRegistryManifestEnvelopeV1[] = [];\n\t\t\t// index 按 installationRef 键控,重装/对齐重试会为同一 pluginId@version\n\t\t\t// 追加多条记录(2026-10-08 实证同身份 4 条、摘要零冲突)。同一身份内容\n\t\t\t// 一致(同一 tarball),按身份只投影一次;摘要冲突才是存储不一致。\n\t\t\tconst projectedDigests = new Map<string, string>();\n\t\t\tfor (const record of records) {\n\t\t\t\tif (signal?.aborted)\n\t\t\t\t\tthrow new NodeDiskRegistrySourceErrorV1(\"cancelled\", t(\"Disk registry source read was cancelled\"));\n\t\t\t\tconst identity = `${record.pluginId}@${record.pluginVersion}`;\n\t\t\t\tconst projectedDigest = projectedDigests.get(identity);\n\t\t\t\tif (projectedDigest !== undefined) {\n\t\t\t\t\tif (projectedDigest !== record.manifestDigest)\n\t\t\t\t\t\tthrow new NodeDiskRegistrySourceErrorV1(\n\t\t\t\t\t\t\t\"invalid_index\",\n\t\t\t\t\t\t\tt(\"Install store index contains conflicting manifests for {identity}\", { identity }),\n\t\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tlet rawManifest: string;\n\t\t\t\ttry {\n\t\t\t\t\trawManifest = await readBoundedFile(\n\t\t\t\t\t\tjoin(record.directory, ECOSYSTEM_MANIFEST_FILE_V1),\n\t\t\t\t\t\tMAX_NODE_DISK_REGISTRY_MANIFEST_BYTES_V1,\n\t\t\t\t\t\t\"manifest\",\n\t\t\t\t\t);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (error instanceof NodeDiskRegistrySourceErrorV1) throw error;\n\t\t\t\t\tthrow new NodeDiskRegistrySourceErrorV1(\"io_failed\", t(\"Installed plugin manifest could not be read\"));\n\t\t\t\t}\n\t\t\t\tconst manifest = parseManifest(record, rawManifest);\n\t\t\t\tenvelopes.push({\n\t\t\t\t\tmanifest,\n\t\t\t\t\tsource: record.installationRef,\n\t\t\t\t\tmanifestDigest: record.manifestDigest,\n\t\t\t\t});\n\t\t\t\tprojectedDigests.set(identity, record.manifestDigest);\n\t\t\t}\n\t\t\treturn Object.freeze(envelopes);\n\t\t},\n\t\tresolveCapabilityManifestPath: async ({ pluginId, pluginVersion, signal }) => {\n\t\t\tconst records = await readIndexRecords(rootDirectory, signal);\n\t\t\tif (records === undefined) return undefined;\n\t\t\tconst record = records.find(\n\t\t\t\t(candidate) => candidate.pluginId === pluginId && candidate.pluginVersion === pluginVersion,\n\t\t\t);\n\t\t\tif (record === undefined) return undefined;\n\t\t\tconst manifestPath = join(record.directory, CAPABILITY_MANIFEST_FILE_V1);\n\t\t\ttry {\n\t\t\t\tconst handle = await open(manifestPath, \"r\");\n\t\t\t\tawait handle.close().catch(() => undefined);\n\t\t\t} catch {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\treturn manifestPath;\n\t\t},\n\t};\n}\n"]}
@@ -33,8 +33,13 @@ export interface NodePluginBootstrapOptionsV1 {
33
33
  readonly signal?: AbortSignal;
34
34
  /** Full dependency override for deterministic tests; the env switch still applies. */
35
35
  readonly deps?: PluginBootstrapDepsV1;
36
- /** Overrides the first-run progress line (stderr) for tests and hosts. */
36
+ /** Overrides the first-run/upgrade progress line (stderr) for tests and hosts. */
37
37
  readonly onPendingInstalls?: (count: number) => void;
38
+ /** Overrides the per-entry "installing…" start line (stderr) for tests and hosts. */
39
+ readonly onInstallStart?: (entry: {
40
+ readonly npmName: string;
41
+ readonly pluginId: string;
42
+ }) => void;
38
43
  /**
39
44
  * Overrides the per-plugin progress renderer (stderr) for tests and hosts.
40
45
  * The default prints one line per installed/aligned/failed entry and stays
@@ -1 +1 @@
1
- {"version":3,"file":"node-plugin-bootstrap.d.ts","sourceRoot":"","sources":["../../../src/plugins/ecosystem/node-plugin-bootstrap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,iCAAiC,CAAC;AASrF,OAAO,EAGN,KAAK,sBAAsB,EAC3B,MAAM,gCAAgC,CAAC;AAExC,OAAO,EACN,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAE5B,MAAM,uBAAuB,CAAC;AAG/B,6EAA6E;AAC7E,eAAO,MAAM,mCAAmC,iCAAiC,CAAC;AAKlF,MAAM,WAAW,4BAA4B;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,+CAA+C;IAC/C,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,6BAA6B,EAAE,CAAC;IAC7D,2EAA2E;IAC3E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,sBAAsB,CAAC;IACzC,iDAAiD;IACjD,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IACzC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,sFAAsF;IACtF,QAAQ,CAAC,IAAI,CAAC,EAAE,qBAAqB,CAAC;IACtC,0EAA0E;IAC1E,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,uBAAuB,KAAK,IAAI,CAAC;CAC9D;AAED,MAAM,MAAM,2BAA2B,GAAG,uBAAuB,GAAG;IACnE;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;CAC1C,CAAC;AAmEF;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAC7C,OAAO,EAAE,4BAA4B,GACnC,OAAO,CAAC,2BAA2B,CAAC,CAsTtC","sourcesContent":["/**\n * Node wiring for the startup plugin bootstrap (design\n * `插件独立分发与启动装配设计.md` §4.4, §0.7).\n *\n * `AGENT_FORGE_PLUGIN_BOOTSTRAP=off` skips everything, including the local\n * comparison. The comparison dependency reads the global and project\n * plugin-state.json stores only — read-only, and it never constructs an HTTP\n * client, so an assembled environment starts offline (G3). Network objects\n * (npm acquisition host, install host) are built lazily inside the install\n * action and closed when the bootstrap finishes. Installs go through the\n * npm acquisition path exclusively and persist the `npm:<host>` source label\n * in the global plugin state (same prepare/install/activate/upsert\n * transaction shape as the CLI install flow, implemented as a library\n * function without going through the CLI layer).\n */\n\nimport { join } from \"node:path\";\nimport { CONFIG_DIR_NAME } from \"../../config.ts\";\nimport { t } from \"../../core/i18n/index.ts\";\nimport { isOfflineModeEnabled } from \"../../core/package-process-runner.ts\";\nimport type { PluginBootstrapFactoryEntryV1 } from \"./bootstrap-factory-manifest.ts\";\nimport { BOOTSTRAP_FACTORY_PLUGIN_MANIFEST_V1 } from \"./bootstrap-factory-manifest.ts\";\nimport {\n\tDEFAULT_PLUGIN_STATE_FILE_V1,\n\tdefaultPluginHostFactsV1,\n\tdefaultPluginInstallStoreDirectoryV1,\n\tprojectPluginScopeRefV1,\n} from \"./default-active-bindings.ts\";\nimport { createPluginInstallerV1, DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1, type PluginInstallerV1 } from \"./installer.ts\";\nimport {\n\tcreateNodePackageInstallHostV1,\n\ttype NodePackageInstallHostV1,\n\ttype NodeZipPackageLimitsV1,\n} from \"./node-package-install-host.ts\";\nimport { createNodePluginAcquisitionHostV1, type NodePluginAcquisitionHostV1 } from \"./node-plugin-acquisition-host.ts\";\nimport {\n\ttype PluginBootstrapActionV1,\n\ttype PluginBootstrapDepsV1,\n\ttype PluginBootstrapResultV1,\n\trunPluginBootstrapV1,\n} from \"./plugin-bootstrap.ts\";\nimport { createPluginStateStoreV1, type PluginStateRecordV1, type PluginStateStoreV1 } from \"./state.ts\";\n\n/** Environment variable disabling the startup bootstrap entirely (`off`). */\nexport const AGENT_FORGE_PLUGIN_BOOTSTRAP_ENV_V1 = \"AGENT_FORGE_PLUGIN_BOOTSTRAP\";\n\n/** The only recognized disabling value; every other value (and absence) keeps bootstrap on. */\nconst PLUGIN_BOOTSTRAP_DISABLED_VALUE_V1 = \"off\";\n\nexport interface NodePluginBootstrapOptionsV1 {\n\treadonly agentDir: string;\n\treadonly cwd: string;\n\t/** Defaults to the frozen factory manifest. */\n\treadonly manifest?: readonly PluginBootstrapFactoryEntryV1[];\n\t/** Bare http(s) npm registry origin; forwarded to the acquisition host. */\n\treadonly npmRegistry?: string;\n\t/** npm registry bearer token; forwarded to the acquisition host. */\n\treadonly npmToken?: string;\n\treadonly limits?: NodeZipPackageLimitsV1;\n\t/** Deterministic fetch replacement for tests. */\n\treadonly fetch?: typeof globalThis.fetch;\n\treadonly signal?: AbortSignal;\n\t/** Full dependency override for deterministic tests; the env switch still applies. */\n\treadonly deps?: PluginBootstrapDepsV1;\n\t/** Overrides the first-run progress line (stderr) for tests and hosts. */\n\treadonly onPendingInstalls?: (count: number) => void;\n\t/**\n\t * Overrides the per-plugin progress renderer (stderr) for tests and hosts.\n\t * The default prints one line per installed/aligned/failed entry and stays\n\t * silent for satisfied entries.\n\t */\n\treadonly onAction?: (action: PluginBootstrapActionV1) => void;\n}\n\nexport type NodePluginBootstrapResultV1 = PluginBootstrapResultV1 & {\n\t/**\n\t * Present when a posture switch short-circuited the run before any local\n\t * comparison: the wiring-level env off switch, or the offline posture\n\t * (设计 §4.4 offline 姿态门, 2026-10-03 — offline installs cannot succeed\n\t * and would only fail slowly, so the run is skipped immediately).\n\t */\n\treadonly skipped?: \"disabled\" | \"offline\";\n};\n\n/** Install-scoped boundaries; created lazily by the first install action. */\ninterface InstallSessionV1 {\n\treadonly acquisition: NodePluginAcquisitionHostV1;\n\treadonly installHost: NodePackageInstallHostV1;\n\treadonly installer: PluginInstallerV1;\n\treadonly stateStore: PluginStateStoreV1;\n}\n\nfunction readActiveVersions(\n\tstore: PluginStateStoreV1,\n\tscope: \"global\" | \"project\",\n\tscopeRef: string,\n\tsignal: AbortSignal | undefined,\n): Promise<ReadonlyMap<string, string>> {\n\treturn (async () => {\n\t\tconst result = await store.read({ ...(signal === undefined ? {} : { signal }) });\n\t\tif (result.status !== \"ok\" && result.status !== \"missing\")\n\t\t\tthrow new Error(\n\t\t\t\tt(\"plugin state read failed ({detail}) for the {scope} scope\", {\n\t\t\t\t\tdetail: result.diagnostics[0]?.code ?? result.status,\n\t\t\t\t\tscope,\n\t\t\t\t}),\n\t\t\t);\n\t\tconst versions = new Map<string, string>();\n\t\tfor (const record of result.snapshot.records) {\n\t\t\tif (record.state.scope !== scope || record.state.scopeRef !== scopeRef) continue;\n\t\t\t// Enabled is deliberately not filtered: a disabled plugin still owns\n\t\t\t// its active installation record, and bootstrap never touches it (G5).\n\t\t\tif (record.state.lifecycle !== \"active\") continue;\n\t\t\tversions.set(record.pluginId, record.pluginVersion);\n\t\t}\n\t\treturn versions;\n\t})();\n}\n\n/**\n * Previous active installation record for one plugin id in the bootstrap's\n * global installation scope, or undefined when the plugin has no active\n * global installation. Mirrors the CLI update transaction's previous lookup\n * (cli.ts `findCurrentStateRecord`): same plugin id, same scope/scopeRef,\n * lifecycle active — bootstrap installs exclusively into this scope, so only\n * a record here can have had its runtime disposed by host.activate.\n */\nasync function readPreviousActiveRecord(\n\tstore: PluginStateStoreV1,\n\tpluginId: string,\n\tsignal: AbortSignal | undefined,\n): Promise<PluginStateRecordV1 | undefined> {\n\tconst result = await store.read({ ...(signal === undefined ? {} : { signal }) });\n\tif (result.status !== \"ok\" && result.status !== \"missing\")\n\t\tthrow new Error(\n\t\t\tt(\"plugin state read failed ({detail}) for the {scope} scope\", {\n\t\t\t\tdetail: result.diagnostics[0]?.code ?? result.status,\n\t\t\t\tscope: \"global\",\n\t\t\t}),\n\t\t);\n\treturn result.snapshot.records.find(\n\t\t(record) =>\n\t\t\trecord.pluginId === pluginId &&\n\t\t\trecord.state.scope === \"global\" &&\n\t\t\trecord.state.scopeRef === DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1 &&\n\t\t\trecord.state.lifecycle === \"active\",\n\t);\n}\n\n/**\n * Run the startup bootstrap for one agent directory. The function resolves\n * with a structured result and never throws for per-plugin failures; only a\n * wiring-level construction error propagates to the caller (main.ts guards\n * startup with its own catch).\n */\nexport async function runNodePluginBootstrapV1(\n\toptions: NodePluginBootstrapOptionsV1,\n): Promise<NodePluginBootstrapResultV1> {\n\tif (process.env[AGENT_FORGE_PLUGIN_BOOTSTRAP_ENV_V1]?.trim() === PLUGIN_BOOTSTRAP_DISABLED_VALUE_V1) {\n\t\treturn Object.freeze({\n\t\t\tversion: 1,\n\t\t\tstatus: \"succeeded\",\n\t\t\tactions: Object.freeze([]),\n\t\t\tdiagnostics: Object.freeze([]),\n\t\t\tdurationMs: 0,\n\t\t\tskipped: \"disabled\",\n\t\t});\n\t}\n\tif (isOfflineModeEnabled()) {\n\t\treturn Object.freeze({\n\t\t\tversion: 1,\n\t\t\tstatus: \"succeeded\",\n\t\t\tactions: Object.freeze([]),\n\t\t\tdiagnostics: Object.freeze([\n\t\t\t\t{\n\t\t\t\t\tcode: \"bootstrap_skipped_offline\",\n\t\t\t\t\tmessage: t(\n\t\t\t\t\t\t\"Offline posture (AGENT_FORGE_OFFLINE) skips the startup plugin bootstrap; run `agent-forge plugin install <package>` explicitly to install plugins.\",\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t] as const),\n\t\t\tdurationMs: 0,\n\t\t\tskipped: \"offline\",\n\t\t});\n\t}\n\tif (options.deps !== undefined) {\n\t\treturn await runPluginBootstrapV1({\n\t\t\tmanifest: options.manifest ?? BOOTSTRAP_FACTORY_PLUGIN_MANIFEST_V1,\n\t\t\tdeps: options.deps,\n\t\t\t...(options.onPendingInstalls === undefined ? {} : { onPendingInstalls: options.onPendingInstalls }),\n\t\t\t...(options.onAction === undefined ? {} : { onAction: options.onAction }),\n\t\t});\n\t}\n\tlet session: InstallSessionV1 | undefined;\n\tconst ensureSession = async (): Promise<InstallSessionV1> => {\n\t\tif (session !== undefined) return session;\n\t\tconst acquisition = createNodePluginAcquisitionHostV1({\n\t\t\t...(options.npmRegistry === undefined ? {} : { npmRegistry: options.npmRegistry }),\n\t\t\t...(options.npmToken === undefined ? {} : { npmToken: options.npmToken }),\n\t\t\t...(options.fetch === undefined ? {} : { fetch: options.fetch }),\n\t\t\t...(options.limits === undefined ? {} : { limits: options.limits }),\n\t\t});\n\t\tconst installHost = await createNodePackageInstallHostV1({\n\t\t\topenArtifact: (input) => acquisition.openArtifact(input),\n\t\t\tinstallationDirectory: defaultPluginInstallStoreDirectoryV1(options.agentDir),\n\t\t\tpersistence: \"persistent\",\n\t\t\tsupportedScopes: [\"global\", \"project\"],\n\t\t\t...(options.limits === undefined ? {} : { ...options.limits }),\n\t\t});\n\t\tsession = {\n\t\t\tacquisition,\n\t\t\tinstallHost,\n\t\t\tinstaller: createPluginInstallerV1({ host: installHost }),\n\t\t\tstateStore: createPluginStateStoreV1({ filePath: join(options.agentDir, DEFAULT_PLUGIN_STATE_FILE_V1) }),\n\t\t};\n\t\treturn session;\n\t};\n\tconst deps: PluginBootstrapDepsV1 = {\n\t\tlistActive: async ({ signal }) => {\n\t\t\tconst globalVersions = readActiveVersions(\n\t\t\t\tcreatePluginStateStoreV1({ filePath: join(options.agentDir, DEFAULT_PLUGIN_STATE_FILE_V1) }),\n\t\t\t\t\"global\",\n\t\t\t\tDEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tconst projectVersions = readActiveVersions(\n\t\t\t\tcreatePluginStateStoreV1({ filePath: join(options.cwd, CONFIG_DIR_NAME, DEFAULT_PLUGIN_STATE_FILE_V1) }),\n\t\t\t\t\"project\",\n\t\t\t\tprojectPluginScopeRefV1(options.cwd),\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tconst [globalResult, projectResult] = await Promise.all([globalVersions, projectVersions]);\n\t\t\t// Project precedence mirrors active-binding resolution: a project\n\t\t\t// installation shadows the same plugin id from the global scope.\n\t\t\treturn new Map([...globalResult, ...projectResult]);\n\t\t},\n\t\tinstallFromNpm: async ({ npmName, range, signal }) => {\n\t\t\t// Download retry (设计 §4.4 下载重试, 2026-10-04): transient registry\n\t\t\t// failures retried with backoff at this wiring layer so the decision\n\t\t\t// loop stays pure. Abort propagates immediately between attempts.\n\t\t\tlet lastError: unknown;\n\t\t\tfor (let attempt = 1; attempt <= 3; attempt++) {\n\t\t\t\tif (signal?.aborted === true) throw lastError ?? new Error(\"install aborted\");\n\t\t\t\ttry {\n\t\t\t\t\treturn await installFromNpmOnce({ npmName, range, signal });\n\t\t\t\t} catch (error) {\n\t\t\t\t\tlastError = error;\n\t\t\t\t\t// Abort during the attempt surfaces as the attempt's own error;\n\t\t\t\t\t// the loop-top check and the backoff listener cover the pauses.\n\t\t\t\t\tif (attempt === 3) throw error;\n\t\t\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\t\t\tconst timer = setTimeout(resolve, 400 * 2 ** (attempt - 1));\n\t\t\t\t\t\tsignal?.addEventListener(\n\t\t\t\t\t\t\t\"abort\",\n\t\t\t\t\t\t\t() => {\n\t\t\t\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\t\t\t\treject(lastError);\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{ once: true },\n\t\t\t\t\t\t);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow lastError;\n\t\t},\n\t\t...(options.signal === undefined ? {} : { signal: options.signal }),\n\t};\n\tconst installFromNpmOnce = async (input: {\n\t\treadonly npmName: string;\n\t\treadonly range?: string;\n\t\treadonly signal?: AbortSignal;\n\t}): Promise<{ readonly pluginVersion: string; readonly source: string }> => {\n\t\tconst { npmName, range, signal } = input;\n\t\t{\n\t\t\tconst active = await ensureSession();\n\t\t\t// npm refs only (§0.7): scoped or plain names with the shared minimal\n\t\t\t// range subset; no github/gitee/url/local ref can be expressed here.\n\t\t\tconst ref = range === undefined ? npmName : `${npmName}@${range}`;\n\t\t\tconst acquired = await active.acquisition.resolvePackage({ ref, ...(signal === undefined ? {} : { signal }) });\n\t\t\tlet released = false;\n\t\t\tconst release = async (): Promise<void> => {\n\t\t\t\tif (released) return;\n\t\t\t\treleased = true;\n\t\t\t\tawait acquired.release();\n\t\t\t};\n\t\t\ttry {\n\t\t\t\tconst verified = active.installer.prepare({\n\t\t\t\t\tmanifest: acquired.manifest,\n\t\t\t\t\tsource: acquired.source,\n\t\t\t\t\thost: defaultPluginHostFactsV1(),\n\t\t\t\t});\n\t\t\t\tif (verified.status !== \"verified\")\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\tt(\"installer prepare rejected {pluginId}: {reason}\", {\n\t\t\t\t\t\t\tpluginId: acquired.manifest.id,\n\t\t\t\t\t\t\treason: verified.reason,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\tconst installed = await active.installer.install({\n\t\t\t\t\trecord: verified.record,\n\t\t\t\t\tscope: \"global\",\n\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\tartifact: acquired.artifact,\n\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t});\n\t\t\t\tif (installed.status !== \"succeeded\")\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\tt(\"plugin install failed ({code}) for {pluginId}\", {\n\t\t\t\t\t\t\tcode: installed.error.code,\n\t\t\t\t\t\t\tpluginId: acquired.manifest.id,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\tconst activated = await active.installer.activate({\n\t\t\t\t\tinstallation: installed.installation,\n\t\t\t\t\trecord: verified.record,\n\t\t\t\t\tscope: \"global\",\n\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t});\n\t\t\t\tif (activated.status !== \"succeeded\") {\n\t\t\t\t\t// CLI update-transaction alignment (cli.ts activate-failure\n\t\t\t\t\t// path): host.activate disposed the previous runtime for this\n\t\t\t\t\t// installation key before the new candidate failed to commit,\n\t\t\t\t\t// so restore the previous active installation through the\n\t\t\t\t\t// installer rollback. Rollback runs before the failed\n\t\t\t\t\t// candidate is removed because host.rollback requires the\n\t\t\t\t\t// failed record to still exist.\n\t\t\t\t\tlet restoreFailure: string | undefined;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst previous = await readPreviousActiveRecord(active.stateStore, acquired.manifest.id, signal);\n\t\t\t\t\t\tif (previous !== undefined) {\n\t\t\t\t\t\t\tconst rolledBack = await active.installer.rollback({\n\t\t\t\t\t\t\t\tfailed: installed.installation,\n\t\t\t\t\t\t\t\tprevious: {\n\t\t\t\t\t\t\t\t\tversion: 1,\n\t\t\t\t\t\t\t\t\tpluginId: previous.pluginId,\n\t\t\t\t\t\t\t\t\tpluginVersion: previous.pluginVersion,\n\t\t\t\t\t\t\t\t\tmanifestDigest: previous.manifestDigest,\n\t\t\t\t\t\t\t\t\tscope: previous.state.scope,\n\t\t\t\t\t\t\t\t\tscopeRef: previous.state.scopeRef,\n\t\t\t\t\t\t\t\t\tinstallationRef: previous.installationRef,\n\t\t\t\t\t\t\t\t\tstate: \"active\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tscope: \"global\",\n\t\t\t\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tif (rolledBack.status !== \"succeeded\") restoreFailure = rolledBack.error.code;\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\trestoreFailure = error instanceof Error ? error.message : String(error);\n\t\t\t\t\t}\n\t\t\t\t\tawait active.installer\n\t\t\t\t\t\t.remove({\n\t\t\t\t\t\t\tinstallation: installed.installation,\n\t\t\t\t\t\t\tscope: \"global\",\n\t\t\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t\tconst activationMessage = t(\"plugin activation failed ({code}) for {pluginId}\", {\n\t\t\t\t\t\tcode: activated.error.code,\n\t\t\t\t\t\tpluginId: acquired.manifest.id,\n\t\t\t\t\t});\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\trestoreFailure === undefined\n\t\t\t\t\t\t\t? activationMessage\n\t\t\t\t\t\t\t: `${activationMessage}; previous version restore failed: ${restoreFailure}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst persisted = await active.stateStore.upsert({\n\t\t\t\t\tinstallation: activated.installation,\n\t\t\t\t\tscope: \"global\",\n\t\t\t\t\tenabled: true,\n\t\t\t\t\tsource: acquired.source,\n\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t});\n\t\t\t\tif (persisted.status !== \"ok\") {\n\t\t\t\t\tawait active.installer\n\t\t\t\t\t\t.remove({\n\t\t\t\t\t\t\tinstallation: activated.installation,\n\t\t\t\t\t\t\tscope: \"global\",\n\t\t\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\tt(\"plugin state persist failed ({status}) for {pluginId}\", {\n\t\t\t\t\t\t\tstatus: persisted.status,\n\t\t\t\t\t\t\tpluginId: acquired.manifest.id,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\t// 出厂对齐语义(duplicate_active 修复,2026-10-08):stateStore 的 upsert\n\t\t\t\t// 按 scope:scopeRef:pluginId@version 去重,多版本并存是 store 的合法形态;\n\t\t\t\t// 但 bootstrap 把该插件在 global scope 对齐到工厂 range——升级成功后同插件\n\t\t\t\t// 其他版本的 active binding 必须退役,否则 active-bindings 解析遇\n\t\t\t\t// duplicate_active 拒选(真实升级 0.88.5→0.88.6 实证)。只退役 state 记录、\n\t\t\t\t// 不删安装目录;退役失败降级保留残留(等同修复前状态,下次启动重试)。\n\t\t\t\tconst allRecords = await active.stateStore.read({ ...(signal === undefined ? {} : { signal }) });\n\t\t\t\tif (allRecords.status === \"ok\") {\n\t\t\t\t\tfor (const record of allRecords.snapshot.records) {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\trecord.pluginId === activated.installation.pluginId &&\n\t\t\t\t\t\t\trecord.pluginVersion !== activated.installation.pluginVersion &&\n\t\t\t\t\t\t\trecord.state.scope === \"global\" &&\n\t\t\t\t\t\t\trecord.state.scopeRef === DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1 &&\n\t\t\t\t\t\t\trecord.state.lifecycle === \"active\"\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tawait active.stateStore\n\t\t\t\t\t\t\t\t.remove({\n\t\t\t\t\t\t\t\t\tpluginId: record.pluginId,\n\t\t\t\t\t\t\t\t\tpluginVersion: record.pluginVersion,\n\t\t\t\t\t\t\t\t\tscope: \"global\",\n\t\t\t\t\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn { pluginVersion: activated.installation.pluginVersion, source: acquired.source };\n\t\t\t} finally {\n\t\t\t\tawait release();\n\t\t\t}\n\t\t}\n\t};\n\ttry {\n\t\treturn await runPluginBootstrapV1({\n\t\t\tmanifest: options.manifest ?? BOOTSTRAP_FACTORY_PLUGIN_MANIFEST_V1,\n\t\t\tdeps,\n\t\t\t// First-run progress line (设计 §4.4, 2026-10-03): a fresh install is\n\t\t\t// otherwise silent for the whole network install window.\n\t\t\tonPendingInstalls:\n\t\t\t\toptions.onPendingInstalls ??\n\t\t\t\t((count: number) => {\n\t\t\t\t\tconsole.error(t(\"First run: installing {count} factory plugins…\", { count }));\n\t\t\t\t}),\n\t\t\t// Per-plugin progress (设计 §4.4 逐插件进度, 2026-10-04): one stderr\n\t\t\t// line per installed/aligned/failed entry; satisfied entries stay\n\t\t\t// silent so an all-green startup prints nothing extra.\n\t\t\tonAction:\n\t\t\t\toptions.onAction ??\n\t\t\t\t((action: PluginBootstrapActionV1) => {\n\t\t\t\t\tif (action.status === \"installed\") {\n\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\tt(\"{pluginId}: installed {version}\", {\n\t\t\t\t\t\t\t\tpluginId: action.pluginId,\n\t\t\t\t\t\t\t\tversion: action.installedVersion ?? \"\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t} else if (action.status === \"failed\") {\n\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\tt(\n\t\t\t\t\t\t\t\t\"{pluginId}: install failed ({code}) — startup continues; the next startup retries automatically\",\n\t\t\t\t\t\t\t\t{ pluginId: action.pluginId, code: action.errorCode ?? \"install_failed\" },\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t});\n\t} finally {\n\t\tconst active = session;\n\t\tsession = undefined;\n\t\tif (active !== undefined) {\n\t\t\tawait active.installHost.close().catch(() => undefined);\n\t\t\tawait active.acquisition.close().catch(() => undefined);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"node-plugin-bootstrap.d.ts","sourceRoot":"","sources":["../../../src/plugins/ecosystem/node-plugin-bootstrap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,iCAAiC,CAAC;AASrF,OAAO,EAGN,KAAK,sBAAsB,EAC3B,MAAM,gCAAgC,CAAC;AAExC,OAAO,EACN,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAE5B,MAAM,uBAAuB,CAAC;AAG/B,6EAA6E;AAC7E,eAAO,MAAM,mCAAmC,iCAAiC,CAAC;AAKlF,MAAM,WAAW,4BAA4B;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,+CAA+C;IAC/C,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,6BAA6B,EAAE,CAAC;IAC7D,2EAA2E;IAC3E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,sBAAsB,CAAC;IACzC,iDAAiD;IACjD,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IACzC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,sFAAsF;IACtF,QAAQ,CAAC,IAAI,CAAC,EAAE,qBAAqB,CAAC;IACtC,kFAAkF;IAClF,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,uFAAqF;IACrF,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACnG;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,uBAAuB,KAAK,IAAI,CAAC;CAC9D;AAED,MAAM,MAAM,2BAA2B,GAAG,uBAAuB,GAAG;IACnE;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;CAC1C,CAAC;AAmEF;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAC7C,OAAO,EAAE,4BAA4B,GACnC,OAAO,CAAC,2BAA2B,CAAC,CA+TtC","sourcesContent":["/**\n * Node wiring for the startup plugin bootstrap (design\n * `插件独立分发与启动装配设计.md` §4.4, §0.7).\n *\n * `AGENT_FORGE_PLUGIN_BOOTSTRAP=off` skips everything, including the local\n * comparison. The comparison dependency reads the global and project\n * plugin-state.json stores only — read-only, and it never constructs an HTTP\n * client, so an assembled environment starts offline (G3). Network objects\n * (npm acquisition host, install host) are built lazily inside the install\n * action and closed when the bootstrap finishes. Installs go through the\n * npm acquisition path exclusively and persist the `npm:<host>` source label\n * in the global plugin state (same prepare/install/activate/upsert\n * transaction shape as the CLI install flow, implemented as a library\n * function without going through the CLI layer).\n */\n\nimport { join } from \"node:path\";\nimport { CONFIG_DIR_NAME } from \"../../config.ts\";\nimport { t } from \"../../core/i18n/index.ts\";\nimport { isOfflineModeEnabled } from \"../../core/package-process-runner.ts\";\nimport type { PluginBootstrapFactoryEntryV1 } from \"./bootstrap-factory-manifest.ts\";\nimport { BOOTSTRAP_FACTORY_PLUGIN_MANIFEST_V1 } from \"./bootstrap-factory-manifest.ts\";\nimport {\n\tDEFAULT_PLUGIN_STATE_FILE_V1,\n\tdefaultPluginHostFactsV1,\n\tdefaultPluginInstallStoreDirectoryV1,\n\tprojectPluginScopeRefV1,\n} from \"./default-active-bindings.ts\";\nimport { createPluginInstallerV1, DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1, type PluginInstallerV1 } from \"./installer.ts\";\nimport {\n\tcreateNodePackageInstallHostV1,\n\ttype NodePackageInstallHostV1,\n\ttype NodeZipPackageLimitsV1,\n} from \"./node-package-install-host.ts\";\nimport { createNodePluginAcquisitionHostV1, type NodePluginAcquisitionHostV1 } from \"./node-plugin-acquisition-host.ts\";\nimport {\n\ttype PluginBootstrapActionV1,\n\ttype PluginBootstrapDepsV1,\n\ttype PluginBootstrapResultV1,\n\trunPluginBootstrapV1,\n} from \"./plugin-bootstrap.ts\";\nimport { createPluginStateStoreV1, type PluginStateRecordV1, type PluginStateStoreV1 } from \"./state.ts\";\n\n/** Environment variable disabling the startup bootstrap entirely (`off`). */\nexport const AGENT_FORGE_PLUGIN_BOOTSTRAP_ENV_V1 = \"AGENT_FORGE_PLUGIN_BOOTSTRAP\";\n\n/** The only recognized disabling value; every other value (and absence) keeps bootstrap on. */\nconst PLUGIN_BOOTSTRAP_DISABLED_VALUE_V1 = \"off\";\n\nexport interface NodePluginBootstrapOptionsV1 {\n\treadonly agentDir: string;\n\treadonly cwd: string;\n\t/** Defaults to the frozen factory manifest. */\n\treadonly manifest?: readonly PluginBootstrapFactoryEntryV1[];\n\t/** Bare http(s) npm registry origin; forwarded to the acquisition host. */\n\treadonly npmRegistry?: string;\n\t/** npm registry bearer token; forwarded to the acquisition host. */\n\treadonly npmToken?: string;\n\treadonly limits?: NodeZipPackageLimitsV1;\n\t/** Deterministic fetch replacement for tests. */\n\treadonly fetch?: typeof globalThis.fetch;\n\treadonly signal?: AbortSignal;\n\t/** Full dependency override for deterministic tests; the env switch still applies. */\n\treadonly deps?: PluginBootstrapDepsV1;\n\t/** Overrides the first-run/upgrade progress line (stderr) for tests and hosts. */\n\treadonly onPendingInstalls?: (count: number) => void;\n\t/** Overrides the per-entry \"installing…\" start line (stderr) for tests and hosts. */\n\treadonly onInstallStart?: (entry: { readonly npmName: string; readonly pluginId: string }) => void;\n\t/**\n\t * Overrides the per-plugin progress renderer (stderr) for tests and hosts.\n\t * The default prints one line per installed/aligned/failed entry and stays\n\t * silent for satisfied entries.\n\t */\n\treadonly onAction?: (action: PluginBootstrapActionV1) => void;\n}\n\nexport type NodePluginBootstrapResultV1 = PluginBootstrapResultV1 & {\n\t/**\n\t * Present when a posture switch short-circuited the run before any local\n\t * comparison: the wiring-level env off switch, or the offline posture\n\t * (设计 §4.4 offline 姿态门, 2026-10-03 — offline installs cannot succeed\n\t * and would only fail slowly, so the run is skipped immediately).\n\t */\n\treadonly skipped?: \"disabled\" | \"offline\";\n};\n\n/** Install-scoped boundaries; created lazily by the first install action. */\ninterface InstallSessionV1 {\n\treadonly acquisition: NodePluginAcquisitionHostV1;\n\treadonly installHost: NodePackageInstallHostV1;\n\treadonly installer: PluginInstallerV1;\n\treadonly stateStore: PluginStateStoreV1;\n}\n\nfunction readActiveVersions(\n\tstore: PluginStateStoreV1,\n\tscope: \"global\" | \"project\",\n\tscopeRef: string,\n\tsignal: AbortSignal | undefined,\n): Promise<ReadonlyMap<string, string>> {\n\treturn (async () => {\n\t\tconst result = await store.read({ ...(signal === undefined ? {} : { signal }) });\n\t\tif (result.status !== \"ok\" && result.status !== \"missing\")\n\t\t\tthrow new Error(\n\t\t\t\tt(\"plugin state read failed ({detail}) for the {scope} scope\", {\n\t\t\t\t\tdetail: result.diagnostics[0]?.code ?? result.status,\n\t\t\t\t\tscope,\n\t\t\t\t}),\n\t\t\t);\n\t\tconst versions = new Map<string, string>();\n\t\tfor (const record of result.snapshot.records) {\n\t\t\tif (record.state.scope !== scope || record.state.scopeRef !== scopeRef) continue;\n\t\t\t// Enabled is deliberately not filtered: a disabled plugin still owns\n\t\t\t// its active installation record, and bootstrap never touches it (G5).\n\t\t\tif (record.state.lifecycle !== \"active\") continue;\n\t\t\tversions.set(record.pluginId, record.pluginVersion);\n\t\t}\n\t\treturn versions;\n\t})();\n}\n\n/**\n * Previous active installation record for one plugin id in the bootstrap's\n * global installation scope, or undefined when the plugin has no active\n * global installation. Mirrors the CLI update transaction's previous lookup\n * (cli.ts `findCurrentStateRecord`): same plugin id, same scope/scopeRef,\n * lifecycle active — bootstrap installs exclusively into this scope, so only\n * a record here can have had its runtime disposed by host.activate.\n */\nasync function readPreviousActiveRecord(\n\tstore: PluginStateStoreV1,\n\tpluginId: string,\n\tsignal: AbortSignal | undefined,\n): Promise<PluginStateRecordV1 | undefined> {\n\tconst result = await store.read({ ...(signal === undefined ? {} : { signal }) });\n\tif (result.status !== \"ok\" && result.status !== \"missing\")\n\t\tthrow new Error(\n\t\t\tt(\"plugin state read failed ({detail}) for the {scope} scope\", {\n\t\t\t\tdetail: result.diagnostics[0]?.code ?? result.status,\n\t\t\t\tscope: \"global\",\n\t\t\t}),\n\t\t);\n\treturn result.snapshot.records.find(\n\t\t(record) =>\n\t\t\trecord.pluginId === pluginId &&\n\t\t\trecord.state.scope === \"global\" &&\n\t\t\trecord.state.scopeRef === DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1 &&\n\t\t\trecord.state.lifecycle === \"active\",\n\t);\n}\n\n/**\n * Run the startup bootstrap for one agent directory. The function resolves\n * with a structured result and never throws for per-plugin failures; only a\n * wiring-level construction error propagates to the caller (main.ts guards\n * startup with its own catch).\n */\nexport async function runNodePluginBootstrapV1(\n\toptions: NodePluginBootstrapOptionsV1,\n): Promise<NodePluginBootstrapResultV1> {\n\tif (process.env[AGENT_FORGE_PLUGIN_BOOTSTRAP_ENV_V1]?.trim() === PLUGIN_BOOTSTRAP_DISABLED_VALUE_V1) {\n\t\treturn Object.freeze({\n\t\t\tversion: 1,\n\t\t\tstatus: \"succeeded\",\n\t\t\tactions: Object.freeze([]),\n\t\t\tdiagnostics: Object.freeze([]),\n\t\t\tdurationMs: 0,\n\t\t\tskipped: \"disabled\",\n\t\t});\n\t}\n\tif (isOfflineModeEnabled()) {\n\t\treturn Object.freeze({\n\t\t\tversion: 1,\n\t\t\tstatus: \"succeeded\",\n\t\t\tactions: Object.freeze([]),\n\t\t\tdiagnostics: Object.freeze([\n\t\t\t\t{\n\t\t\t\t\tcode: \"bootstrap_skipped_offline\",\n\t\t\t\t\tmessage: t(\n\t\t\t\t\t\t\"Offline posture (AGENT_FORGE_OFFLINE) skips the startup plugin bootstrap; run `agent-forge plugin install <package>` explicitly to install plugins.\",\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t] as const),\n\t\t\tdurationMs: 0,\n\t\t\tskipped: \"offline\",\n\t\t});\n\t}\n\tif (options.deps !== undefined) {\n\t\treturn await runPluginBootstrapV1({\n\t\t\tmanifest: options.manifest ?? BOOTSTRAP_FACTORY_PLUGIN_MANIFEST_V1,\n\t\t\tdeps: options.deps,\n\t\t\t...(options.onPendingInstalls === undefined ? {} : { onPendingInstalls: options.onPendingInstalls }),\n\t\t\t...(options.onAction === undefined ? {} : { onAction: options.onAction }),\n\t\t});\n\t}\n\tlet session: InstallSessionV1 | undefined;\n\tconst ensureSession = async (): Promise<InstallSessionV1> => {\n\t\tif (session !== undefined) return session;\n\t\tconst acquisition = createNodePluginAcquisitionHostV1({\n\t\t\t...(options.npmRegistry === undefined ? {} : { npmRegistry: options.npmRegistry }),\n\t\t\t...(options.npmToken === undefined ? {} : { npmToken: options.npmToken }),\n\t\t\t...(options.fetch === undefined ? {} : { fetch: options.fetch }),\n\t\t\t...(options.limits === undefined ? {} : { limits: options.limits }),\n\t\t});\n\t\tconst installHost = await createNodePackageInstallHostV1({\n\t\t\topenArtifact: (input) => acquisition.openArtifact(input),\n\t\t\tinstallationDirectory: defaultPluginInstallStoreDirectoryV1(options.agentDir),\n\t\t\tpersistence: \"persistent\",\n\t\t\tsupportedScopes: [\"global\", \"project\"],\n\t\t\t...(options.limits === undefined ? {} : { ...options.limits }),\n\t\t});\n\t\tsession = {\n\t\t\tacquisition,\n\t\t\tinstallHost,\n\t\t\tinstaller: createPluginInstallerV1({ host: installHost }),\n\t\t\tstateStore: createPluginStateStoreV1({ filePath: join(options.agentDir, DEFAULT_PLUGIN_STATE_FILE_V1) }),\n\t\t};\n\t\treturn session;\n\t};\n\tconst deps: PluginBootstrapDepsV1 = {\n\t\tlistActive: async ({ signal }) => {\n\t\t\tconst globalVersions = readActiveVersions(\n\t\t\t\tcreatePluginStateStoreV1({ filePath: join(options.agentDir, DEFAULT_PLUGIN_STATE_FILE_V1) }),\n\t\t\t\t\"global\",\n\t\t\t\tDEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tconst projectVersions = readActiveVersions(\n\t\t\t\tcreatePluginStateStoreV1({ filePath: join(options.cwd, CONFIG_DIR_NAME, DEFAULT_PLUGIN_STATE_FILE_V1) }),\n\t\t\t\t\"project\",\n\t\t\t\tprojectPluginScopeRefV1(options.cwd),\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tconst [globalResult, projectResult] = await Promise.all([globalVersions, projectVersions]);\n\t\t\t// Project precedence mirrors active-binding resolution: a project\n\t\t\t// installation shadows the same plugin id from the global scope.\n\t\t\treturn new Map([...globalResult, ...projectResult]);\n\t\t},\n\t\tinstallFromNpm: async ({ npmName, range, signal }) => {\n\t\t\t// Download retry (设计 §4.4 下载重试, 2026-10-04): transient registry\n\t\t\t// failures retried with backoff at this wiring layer so the decision\n\t\t\t// loop stays pure. Abort propagates immediately between attempts.\n\t\t\tlet lastError: unknown;\n\t\t\tfor (let attempt = 1; attempt <= 3; attempt++) {\n\t\t\t\tif (signal?.aborted === true) throw lastError ?? new Error(\"install aborted\");\n\t\t\t\ttry {\n\t\t\t\t\treturn await installFromNpmOnce({ npmName, range, signal });\n\t\t\t\t} catch (error) {\n\t\t\t\t\tlastError = error;\n\t\t\t\t\t// Abort during the attempt surfaces as the attempt's own error;\n\t\t\t\t\t// the loop-top check and the backoff listener cover the pauses.\n\t\t\t\t\tif (attempt === 3) throw error;\n\t\t\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\t\t\tconst timer = setTimeout(resolve, 400 * 2 ** (attempt - 1));\n\t\t\t\t\t\tsignal?.addEventListener(\n\t\t\t\t\t\t\t\"abort\",\n\t\t\t\t\t\t\t() => {\n\t\t\t\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\t\t\t\treject(lastError);\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{ once: true },\n\t\t\t\t\t\t);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow lastError;\n\t\t},\n\t\t...(options.signal === undefined ? {} : { signal: options.signal }),\n\t};\n\tconst installFromNpmOnce = async (input: {\n\t\treadonly npmName: string;\n\t\treadonly range?: string;\n\t\treadonly signal?: AbortSignal;\n\t}): Promise<{ readonly pluginVersion: string; readonly source: string }> => {\n\t\tconst { npmName, range, signal } = input;\n\t\t{\n\t\t\tconst active = await ensureSession();\n\t\t\t// npm refs only (§0.7): scoped or plain names with the shared minimal\n\t\t\t// range subset; no github/gitee/url/local ref can be expressed here.\n\t\t\tconst ref = range === undefined ? npmName : `${npmName}@${range}`;\n\t\t\tconst acquired = await active.acquisition.resolvePackage({ ref, ...(signal === undefined ? {} : { signal }) });\n\t\t\tlet released = false;\n\t\t\tconst release = async (): Promise<void> => {\n\t\t\t\tif (released) return;\n\t\t\t\treleased = true;\n\t\t\t\tawait acquired.release();\n\t\t\t};\n\t\t\ttry {\n\t\t\t\tconst verified = active.installer.prepare({\n\t\t\t\t\tmanifest: acquired.manifest,\n\t\t\t\t\tsource: acquired.source,\n\t\t\t\t\thost: defaultPluginHostFactsV1(),\n\t\t\t\t});\n\t\t\t\tif (verified.status !== \"verified\")\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\tt(\"installer prepare rejected {pluginId}: {reason}\", {\n\t\t\t\t\t\t\tpluginId: acquired.manifest.id,\n\t\t\t\t\t\t\treason: verified.reason,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\tconst installed = await active.installer.install({\n\t\t\t\t\trecord: verified.record,\n\t\t\t\t\tscope: \"global\",\n\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\tartifact: acquired.artifact,\n\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t});\n\t\t\t\tif (installed.status !== \"succeeded\")\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\tt(\"plugin install failed ({code}) for {pluginId}\", {\n\t\t\t\t\t\t\tcode: installed.error.code,\n\t\t\t\t\t\t\tpluginId: acquired.manifest.id,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\tconst activated = await active.installer.activate({\n\t\t\t\t\tinstallation: installed.installation,\n\t\t\t\t\trecord: verified.record,\n\t\t\t\t\tscope: \"global\",\n\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t});\n\t\t\t\tif (activated.status !== \"succeeded\") {\n\t\t\t\t\t// CLI update-transaction alignment (cli.ts activate-failure\n\t\t\t\t\t// path): host.activate disposed the previous runtime for this\n\t\t\t\t\t// installation key before the new candidate failed to commit,\n\t\t\t\t\t// so restore the previous active installation through the\n\t\t\t\t\t// installer rollback. Rollback runs before the failed\n\t\t\t\t\t// candidate is removed because host.rollback requires the\n\t\t\t\t\t// failed record to still exist.\n\t\t\t\t\tlet restoreFailure: string | undefined;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst previous = await readPreviousActiveRecord(active.stateStore, acquired.manifest.id, signal);\n\t\t\t\t\t\tif (previous !== undefined) {\n\t\t\t\t\t\t\tconst rolledBack = await active.installer.rollback({\n\t\t\t\t\t\t\t\tfailed: installed.installation,\n\t\t\t\t\t\t\t\tprevious: {\n\t\t\t\t\t\t\t\t\tversion: 1,\n\t\t\t\t\t\t\t\t\tpluginId: previous.pluginId,\n\t\t\t\t\t\t\t\t\tpluginVersion: previous.pluginVersion,\n\t\t\t\t\t\t\t\t\tmanifestDigest: previous.manifestDigest,\n\t\t\t\t\t\t\t\t\tscope: previous.state.scope,\n\t\t\t\t\t\t\t\t\tscopeRef: previous.state.scopeRef,\n\t\t\t\t\t\t\t\t\tinstallationRef: previous.installationRef,\n\t\t\t\t\t\t\t\t\tstate: \"active\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tscope: \"global\",\n\t\t\t\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tif (rolledBack.status !== \"succeeded\") restoreFailure = rolledBack.error.code;\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\trestoreFailure = error instanceof Error ? error.message : String(error);\n\t\t\t\t\t}\n\t\t\t\t\tawait active.installer\n\t\t\t\t\t\t.remove({\n\t\t\t\t\t\t\tinstallation: installed.installation,\n\t\t\t\t\t\t\tscope: \"global\",\n\t\t\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t\tconst activationMessage = t(\"plugin activation failed ({code}) for {pluginId}\", {\n\t\t\t\t\t\tcode: activated.error.code,\n\t\t\t\t\t\tpluginId: acquired.manifest.id,\n\t\t\t\t\t});\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\trestoreFailure === undefined\n\t\t\t\t\t\t\t? activationMessage\n\t\t\t\t\t\t\t: `${activationMessage}; previous version restore failed: ${restoreFailure}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst persisted = await active.stateStore.upsert({\n\t\t\t\t\tinstallation: activated.installation,\n\t\t\t\t\tscope: \"global\",\n\t\t\t\t\tenabled: true,\n\t\t\t\t\tsource: acquired.source,\n\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t});\n\t\t\t\tif (persisted.status !== \"ok\") {\n\t\t\t\t\tawait active.installer\n\t\t\t\t\t\t.remove({\n\t\t\t\t\t\t\tinstallation: activated.installation,\n\t\t\t\t\t\t\tscope: \"global\",\n\t\t\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\tt(\"plugin state persist failed ({status}) for {pluginId}\", {\n\t\t\t\t\t\t\tstatus: persisted.status,\n\t\t\t\t\t\t\tpluginId: acquired.manifest.id,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\t// 出厂对齐语义(duplicate_active 修复,2026-10-08):stateStore 的 upsert\n\t\t\t\t// 按 scope:scopeRef:pluginId@version 去重,多版本并存是 store 的合法形态;\n\t\t\t\t// 但 bootstrap 把该插件在 global scope 对齐到工厂 range——升级成功后同插件\n\t\t\t\t// 其他版本的 active binding 必须退役,否则 active-bindings 解析遇\n\t\t\t\t// duplicate_active 拒选(真实升级 0.88.5→0.88.6 实证)。只退役 state 记录、\n\t\t\t\t// 不删安装目录;退役失败降级保留残留(等同修复前状态,下次启动重试)。\n\t\t\t\tconst allRecords = await active.stateStore.read({ ...(signal === undefined ? {} : { signal }) });\n\t\t\t\tif (allRecords.status === \"ok\") {\n\t\t\t\t\tfor (const record of allRecords.snapshot.records) {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\trecord.pluginId === activated.installation.pluginId &&\n\t\t\t\t\t\t\trecord.pluginVersion !== activated.installation.pluginVersion &&\n\t\t\t\t\t\t\trecord.state.scope === \"global\" &&\n\t\t\t\t\t\t\trecord.state.scopeRef === DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1 &&\n\t\t\t\t\t\t\trecord.state.lifecycle === \"active\"\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tawait active.stateStore\n\t\t\t\t\t\t\t\t.remove({\n\t\t\t\t\t\t\t\t\tpluginId: record.pluginId,\n\t\t\t\t\t\t\t\t\tpluginVersion: record.pluginVersion,\n\t\t\t\t\t\t\t\t\tscope: \"global\",\n\t\t\t\t\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn { pluginVersion: activated.installation.pluginVersion, source: acquired.source };\n\t\t\t} finally {\n\t\t\t\tawait release();\n\t\t\t}\n\t\t}\n\t};\n\ttry {\n\t\treturn await runPluginBootstrapV1({\n\t\t\tmanifest: options.manifest ?? BOOTSTRAP_FACTORY_PLUGIN_MANIFEST_V1,\n\t\t\tdeps,\n\t\t\t// First-run/upgrade progress line (设计 §4.4, 2026-10-03; 升级对齐也计数\n\t\t\t// 2026-10-08): the npm alignment window is otherwise silent — a\n\t\t\t// 0.88.7→0.88.8 upgrade replayed at 38s with zero feedback.\n\t\t\tonPendingInstalls:\n\t\t\t\toptions.onPendingInstalls ??\n\t\t\t\t((count: number) => {\n\t\t\t\t\tconsole.error(t(\"Aligning {count} factory plugin(s) from npm…\", { count }));\n\t\t\t\t}),\n\t\t\t// Per-entry start line (2026-10-08 走查): npm metadata + tarball take\n\t\t\t// seconds per plugin; the waiting user sees which plugin is being\n\t\t\t// fetched before its completion line lands.\n\t\t\tonInstallStart:\n\t\t\t\toptions.onInstallStart ??\n\t\t\t\t((entry) => {\n\t\t\t\t\tconsole.error(t(\"{pluginId}: installing…\", { pluginId: entry.pluginId }));\n\t\t\t\t}),\n\t\t\t// Per-plugin progress (设计 §4.4 逐插件进度, 2026-10-04): one stderr\n\t\t\t// line per installed/aligned/failed entry; satisfied entries stay\n\t\t\t// silent so an all-green startup prints nothing extra.\n\t\t\tonAction:\n\t\t\t\toptions.onAction ??\n\t\t\t\t((action: PluginBootstrapActionV1) => {\n\t\t\t\t\tif (action.status === \"installed\") {\n\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\tt(\"{pluginId}: installed {version}\", {\n\t\t\t\t\t\t\t\tpluginId: action.pluginId,\n\t\t\t\t\t\t\t\tversion: action.installedVersion ?? \"\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t} else if (action.status === \"failed\") {\n\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\tt(\n\t\t\t\t\t\t\t\t\"{pluginId}: install failed ({code}) — startup continues; the next startup retries automatically\",\n\t\t\t\t\t\t\t\t{ pluginId: action.pluginId, code: action.errorCode ?? \"install_failed\" },\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t});\n\t} finally {\n\t\tconst active = session;\n\t\tsession = undefined;\n\t\tif (active !== undefined) {\n\t\t\tawait active.installHost.close().catch(() => undefined);\n\t\t\tawait active.acquisition.close().catch(() => undefined);\n\t\t}\n\t}\n}\n"]}
@@ -325,11 +325,19 @@ export async function runNodePluginBootstrapV1(options) {
325
325
  return await runPluginBootstrapV1({
326
326
  manifest: options.manifest ?? BOOTSTRAP_FACTORY_PLUGIN_MANIFEST_V1,
327
327
  deps,
328
- // First-run progress line (设计 §4.4, 2026-10-03): a fresh install is
329
- // otherwise silent for the whole network install window.
328
+ // First-run/upgrade progress line (设计 §4.4, 2026-10-03; 升级对齐也计数
329
+ // 2026-10-08): the npm alignment window is otherwise silent — a
330
+ // 0.88.7→0.88.8 upgrade replayed at 38s with zero feedback.
330
331
  onPendingInstalls: options.onPendingInstalls ??
331
332
  ((count) => {
332
- console.error(t("First run: installing {count} factory plugins…", { count }));
333
+ console.error(t("Aligning {count} factory plugin(s) from npm…", { count }));
334
+ }),
335
+ // Per-entry start line (2026-10-08 走查): npm metadata + tarball take
336
+ // seconds per plugin; the waiting user sees which plugin is being
337
+ // fetched before its completion line lands.
338
+ onInstallStart: options.onInstallStart ??
339
+ ((entry) => {
340
+ console.error(t("{pluginId}: installing…", { pluginId: entry.pluginId }));
333
341
  }),
334
342
  // Per-plugin progress (设计 §4.4 逐插件进度, 2026-10-04): one stderr
335
343
  // line per installed/aligned/failed entry; satisfied entries stay
@@ -1 +1 @@
1
- {"version":3,"file":"node-plugin-bootstrap.js","sourceRoot":"","sources":["../../../src/plugins/ecosystem/node-plugin-bootstrap.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Node wiring for the startup plugin bootstrap (design\n * `插件独立分发与启动装配设计.md` §4.4, §0.7).\n *\n * `AGENT_FORGE_PLUGIN_BOOTSTRAP=off` skips everything, including the local\n * comparison. The comparison dependency reads the global and project\n * plugin-state.json stores only — read-only, and it never constructs an HTTP\n * client, so an assembled environment starts offline (G3). Network objects\n * (npm acquisition host, install host) are built lazily inside the install\n * action and closed when the bootstrap finishes. Installs go through the\n * npm acquisition path exclusively and persist the `npm:<host>` source label\n * in the global plugin state (same prepare/install/activate/upsert\n * transaction shape as the CLI install flow, implemented as a library\n * function without going through the CLI layer).\n */\n\nimport { join } from \"node:path\";\nimport { CONFIG_DIR_NAME } from \"../../config.ts\";\nimport { t } from \"../../core/i18n/index.ts\";\nimport { isOfflineModeEnabled } from \"../../core/package-process-runner.ts\";\nimport type { PluginBootstrapFactoryEntryV1 } from \"./bootstrap-factory-manifest.ts\";\nimport { BOOTSTRAP_FACTORY_PLUGIN_MANIFEST_V1 } from \"./bootstrap-factory-manifest.ts\";\nimport {\n\tDEFAULT_PLUGIN_STATE_FILE_V1,\n\tdefaultPluginHostFactsV1,\n\tdefaultPluginInstallStoreDirectoryV1,\n\tprojectPluginScopeRefV1,\n} from \"./default-active-bindings.ts\";\nimport { createPluginInstallerV1, DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1, type PluginInstallerV1 } from \"./installer.ts\";\nimport {\n\tcreateNodePackageInstallHostV1,\n\ttype NodePackageInstallHostV1,\n\ttype NodeZipPackageLimitsV1,\n} from \"./node-package-install-host.ts\";\nimport { createNodePluginAcquisitionHostV1, type NodePluginAcquisitionHostV1 } from \"./node-plugin-acquisition-host.ts\";\nimport {\n\ttype PluginBootstrapActionV1,\n\ttype PluginBootstrapDepsV1,\n\ttype PluginBootstrapResultV1,\n\trunPluginBootstrapV1,\n} from \"./plugin-bootstrap.ts\";\nimport { createPluginStateStoreV1, type PluginStateRecordV1, type PluginStateStoreV1 } from \"./state.ts\";\n\n/** Environment variable disabling the startup bootstrap entirely (`off`). */\nexport const AGENT_FORGE_PLUGIN_BOOTSTRAP_ENV_V1 = \"AGENT_FORGE_PLUGIN_BOOTSTRAP\";\n\n/** The only recognized disabling value; every other value (and absence) keeps bootstrap on. */\nconst PLUGIN_BOOTSTRAP_DISABLED_VALUE_V1 = \"off\";\n\nexport interface NodePluginBootstrapOptionsV1 {\n\treadonly agentDir: string;\n\treadonly cwd: string;\n\t/** Defaults to the frozen factory manifest. */\n\treadonly manifest?: readonly PluginBootstrapFactoryEntryV1[];\n\t/** Bare http(s) npm registry origin; forwarded to the acquisition host. */\n\treadonly npmRegistry?: string;\n\t/** npm registry bearer token; forwarded to the acquisition host. */\n\treadonly npmToken?: string;\n\treadonly limits?: NodeZipPackageLimitsV1;\n\t/** Deterministic fetch replacement for tests. */\n\treadonly fetch?: typeof globalThis.fetch;\n\treadonly signal?: AbortSignal;\n\t/** Full dependency override for deterministic tests; the env switch still applies. */\n\treadonly deps?: PluginBootstrapDepsV1;\n\t/** Overrides the first-run progress line (stderr) for tests and hosts. */\n\treadonly onPendingInstalls?: (count: number) => void;\n\t/**\n\t * Overrides the per-plugin progress renderer (stderr) for tests and hosts.\n\t * The default prints one line per installed/aligned/failed entry and stays\n\t * silent for satisfied entries.\n\t */\n\treadonly onAction?: (action: PluginBootstrapActionV1) => void;\n}\n\nexport type NodePluginBootstrapResultV1 = PluginBootstrapResultV1 & {\n\t/**\n\t * Present when a posture switch short-circuited the run before any local\n\t * comparison: the wiring-level env off switch, or the offline posture\n\t * (设计 §4.4 offline 姿态门, 2026-10-03 — offline installs cannot succeed\n\t * and would only fail slowly, so the run is skipped immediately).\n\t */\n\treadonly skipped?: \"disabled\" | \"offline\";\n};\n\n/** Install-scoped boundaries; created lazily by the first install action. */\ninterface InstallSessionV1 {\n\treadonly acquisition: NodePluginAcquisitionHostV1;\n\treadonly installHost: NodePackageInstallHostV1;\n\treadonly installer: PluginInstallerV1;\n\treadonly stateStore: PluginStateStoreV1;\n}\n\nfunction readActiveVersions(\n\tstore: PluginStateStoreV1,\n\tscope: \"global\" | \"project\",\n\tscopeRef: string,\n\tsignal: AbortSignal | undefined,\n): Promise<ReadonlyMap<string, string>> {\n\treturn (async () => {\n\t\tconst result = await store.read({ ...(signal === undefined ? {} : { signal }) });\n\t\tif (result.status !== \"ok\" && result.status !== \"missing\")\n\t\t\tthrow new Error(\n\t\t\t\tt(\"plugin state read failed ({detail}) for the {scope} scope\", {\n\t\t\t\t\tdetail: result.diagnostics[0]?.code ?? result.status,\n\t\t\t\t\tscope,\n\t\t\t\t}),\n\t\t\t);\n\t\tconst versions = new Map<string, string>();\n\t\tfor (const record of result.snapshot.records) {\n\t\t\tif (record.state.scope !== scope || record.state.scopeRef !== scopeRef) continue;\n\t\t\t// Enabled is deliberately not filtered: a disabled plugin still owns\n\t\t\t// its active installation record, and bootstrap never touches it (G5).\n\t\t\tif (record.state.lifecycle !== \"active\") continue;\n\t\t\tversions.set(record.pluginId, record.pluginVersion);\n\t\t}\n\t\treturn versions;\n\t})();\n}\n\n/**\n * Previous active installation record for one plugin id in the bootstrap's\n * global installation scope, or undefined when the plugin has no active\n * global installation. Mirrors the CLI update transaction's previous lookup\n * (cli.ts `findCurrentStateRecord`): same plugin id, same scope/scopeRef,\n * lifecycle active — bootstrap installs exclusively into this scope, so only\n * a record here can have had its runtime disposed by host.activate.\n */\nasync function readPreviousActiveRecord(\n\tstore: PluginStateStoreV1,\n\tpluginId: string,\n\tsignal: AbortSignal | undefined,\n): Promise<PluginStateRecordV1 | undefined> {\n\tconst result = await store.read({ ...(signal === undefined ? {} : { signal }) });\n\tif (result.status !== \"ok\" && result.status !== \"missing\")\n\t\tthrow new Error(\n\t\t\tt(\"plugin state read failed ({detail}) for the {scope} scope\", {\n\t\t\t\tdetail: result.diagnostics[0]?.code ?? result.status,\n\t\t\t\tscope: \"global\",\n\t\t\t}),\n\t\t);\n\treturn result.snapshot.records.find(\n\t\t(record) =>\n\t\t\trecord.pluginId === pluginId &&\n\t\t\trecord.state.scope === \"global\" &&\n\t\t\trecord.state.scopeRef === DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1 &&\n\t\t\trecord.state.lifecycle === \"active\",\n\t);\n}\n\n/**\n * Run the startup bootstrap for one agent directory. The function resolves\n * with a structured result and never throws for per-plugin failures; only a\n * wiring-level construction error propagates to the caller (main.ts guards\n * startup with its own catch).\n */\nexport async function runNodePluginBootstrapV1(\n\toptions: NodePluginBootstrapOptionsV1,\n): Promise<NodePluginBootstrapResultV1> {\n\tif (process.env[AGENT_FORGE_PLUGIN_BOOTSTRAP_ENV_V1]?.trim() === PLUGIN_BOOTSTRAP_DISABLED_VALUE_V1) {\n\t\treturn Object.freeze({\n\t\t\tversion: 1,\n\t\t\tstatus: \"succeeded\",\n\t\t\tactions: Object.freeze([]),\n\t\t\tdiagnostics: Object.freeze([]),\n\t\t\tdurationMs: 0,\n\t\t\tskipped: \"disabled\",\n\t\t});\n\t}\n\tif (isOfflineModeEnabled()) {\n\t\treturn Object.freeze({\n\t\t\tversion: 1,\n\t\t\tstatus: \"succeeded\",\n\t\t\tactions: Object.freeze([]),\n\t\t\tdiagnostics: Object.freeze([\n\t\t\t\t{\n\t\t\t\t\tcode: \"bootstrap_skipped_offline\",\n\t\t\t\t\tmessage: t(\n\t\t\t\t\t\t\"Offline posture (AGENT_FORGE_OFFLINE) skips the startup plugin bootstrap; run `agent-forge plugin install <package>` explicitly to install plugins.\",\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t] as const),\n\t\t\tdurationMs: 0,\n\t\t\tskipped: \"offline\",\n\t\t});\n\t}\n\tif (options.deps !== undefined) {\n\t\treturn await runPluginBootstrapV1({\n\t\t\tmanifest: options.manifest ?? BOOTSTRAP_FACTORY_PLUGIN_MANIFEST_V1,\n\t\t\tdeps: options.deps,\n\t\t\t...(options.onPendingInstalls === undefined ? {} : { onPendingInstalls: options.onPendingInstalls }),\n\t\t\t...(options.onAction === undefined ? {} : { onAction: options.onAction }),\n\t\t});\n\t}\n\tlet session: InstallSessionV1 | undefined;\n\tconst ensureSession = async (): Promise<InstallSessionV1> => {\n\t\tif (session !== undefined) return session;\n\t\tconst acquisition = createNodePluginAcquisitionHostV1({\n\t\t\t...(options.npmRegistry === undefined ? {} : { npmRegistry: options.npmRegistry }),\n\t\t\t...(options.npmToken === undefined ? {} : { npmToken: options.npmToken }),\n\t\t\t...(options.fetch === undefined ? {} : { fetch: options.fetch }),\n\t\t\t...(options.limits === undefined ? {} : { limits: options.limits }),\n\t\t});\n\t\tconst installHost = await createNodePackageInstallHostV1({\n\t\t\topenArtifact: (input) => acquisition.openArtifact(input),\n\t\t\tinstallationDirectory: defaultPluginInstallStoreDirectoryV1(options.agentDir),\n\t\t\tpersistence: \"persistent\",\n\t\t\tsupportedScopes: [\"global\", \"project\"],\n\t\t\t...(options.limits === undefined ? {} : { ...options.limits }),\n\t\t});\n\t\tsession = {\n\t\t\tacquisition,\n\t\t\tinstallHost,\n\t\t\tinstaller: createPluginInstallerV1({ host: installHost }),\n\t\t\tstateStore: createPluginStateStoreV1({ filePath: join(options.agentDir, DEFAULT_PLUGIN_STATE_FILE_V1) }),\n\t\t};\n\t\treturn session;\n\t};\n\tconst deps: PluginBootstrapDepsV1 = {\n\t\tlistActive: async ({ signal }) => {\n\t\t\tconst globalVersions = readActiveVersions(\n\t\t\t\tcreatePluginStateStoreV1({ filePath: join(options.agentDir, DEFAULT_PLUGIN_STATE_FILE_V1) }),\n\t\t\t\t\"global\",\n\t\t\t\tDEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tconst projectVersions = readActiveVersions(\n\t\t\t\tcreatePluginStateStoreV1({ filePath: join(options.cwd, CONFIG_DIR_NAME, DEFAULT_PLUGIN_STATE_FILE_V1) }),\n\t\t\t\t\"project\",\n\t\t\t\tprojectPluginScopeRefV1(options.cwd),\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tconst [globalResult, projectResult] = await Promise.all([globalVersions, projectVersions]);\n\t\t\t// Project precedence mirrors active-binding resolution: a project\n\t\t\t// installation shadows the same plugin id from the global scope.\n\t\t\treturn new Map([...globalResult, ...projectResult]);\n\t\t},\n\t\tinstallFromNpm: async ({ npmName, range, signal }) => {\n\t\t\t// Download retry (设计 §4.4 下载重试, 2026-10-04): transient registry\n\t\t\t// failures retried with backoff at this wiring layer so the decision\n\t\t\t// loop stays pure. Abort propagates immediately between attempts.\n\t\t\tlet lastError: unknown;\n\t\t\tfor (let attempt = 1; attempt <= 3; attempt++) {\n\t\t\t\tif (signal?.aborted === true) throw lastError ?? new Error(\"install aborted\");\n\t\t\t\ttry {\n\t\t\t\t\treturn await installFromNpmOnce({ npmName, range, signal });\n\t\t\t\t} catch (error) {\n\t\t\t\t\tlastError = error;\n\t\t\t\t\t// Abort during the attempt surfaces as the attempt's own error;\n\t\t\t\t\t// the loop-top check and the backoff listener cover the pauses.\n\t\t\t\t\tif (attempt === 3) throw error;\n\t\t\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\t\t\tconst timer = setTimeout(resolve, 400 * 2 ** (attempt - 1));\n\t\t\t\t\t\tsignal?.addEventListener(\n\t\t\t\t\t\t\t\"abort\",\n\t\t\t\t\t\t\t() => {\n\t\t\t\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\t\t\t\treject(lastError);\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{ once: true },\n\t\t\t\t\t\t);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow lastError;\n\t\t},\n\t\t...(options.signal === undefined ? {} : { signal: options.signal }),\n\t};\n\tconst installFromNpmOnce = async (input: {\n\t\treadonly npmName: string;\n\t\treadonly range?: string;\n\t\treadonly signal?: AbortSignal;\n\t}): Promise<{ readonly pluginVersion: string; readonly source: string }> => {\n\t\tconst { npmName, range, signal } = input;\n\t\t{\n\t\t\tconst active = await ensureSession();\n\t\t\t// npm refs only (§0.7): scoped or plain names with the shared minimal\n\t\t\t// range subset; no github/gitee/url/local ref can be expressed here.\n\t\t\tconst ref = range === undefined ? npmName : `${npmName}@${range}`;\n\t\t\tconst acquired = await active.acquisition.resolvePackage({ ref, ...(signal === undefined ? {} : { signal }) });\n\t\t\tlet released = false;\n\t\t\tconst release = async (): Promise<void> => {\n\t\t\t\tif (released) return;\n\t\t\t\treleased = true;\n\t\t\t\tawait acquired.release();\n\t\t\t};\n\t\t\ttry {\n\t\t\t\tconst verified = active.installer.prepare({\n\t\t\t\t\tmanifest: acquired.manifest,\n\t\t\t\t\tsource: acquired.source,\n\t\t\t\t\thost: defaultPluginHostFactsV1(),\n\t\t\t\t});\n\t\t\t\tif (verified.status !== \"verified\")\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\tt(\"installer prepare rejected {pluginId}: {reason}\", {\n\t\t\t\t\t\t\tpluginId: acquired.manifest.id,\n\t\t\t\t\t\t\treason: verified.reason,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\tconst installed = await active.installer.install({\n\t\t\t\t\trecord: verified.record,\n\t\t\t\t\tscope: \"global\",\n\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\tartifact: acquired.artifact,\n\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t});\n\t\t\t\tif (installed.status !== \"succeeded\")\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\tt(\"plugin install failed ({code}) for {pluginId}\", {\n\t\t\t\t\t\t\tcode: installed.error.code,\n\t\t\t\t\t\t\tpluginId: acquired.manifest.id,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\tconst activated = await active.installer.activate({\n\t\t\t\t\tinstallation: installed.installation,\n\t\t\t\t\trecord: verified.record,\n\t\t\t\t\tscope: \"global\",\n\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t});\n\t\t\t\tif (activated.status !== \"succeeded\") {\n\t\t\t\t\t// CLI update-transaction alignment (cli.ts activate-failure\n\t\t\t\t\t// path): host.activate disposed the previous runtime for this\n\t\t\t\t\t// installation key before the new candidate failed to commit,\n\t\t\t\t\t// so restore the previous active installation through the\n\t\t\t\t\t// installer rollback. Rollback runs before the failed\n\t\t\t\t\t// candidate is removed because host.rollback requires the\n\t\t\t\t\t// failed record to still exist.\n\t\t\t\t\tlet restoreFailure: string | undefined;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst previous = await readPreviousActiveRecord(active.stateStore, acquired.manifest.id, signal);\n\t\t\t\t\t\tif (previous !== undefined) {\n\t\t\t\t\t\t\tconst rolledBack = await active.installer.rollback({\n\t\t\t\t\t\t\t\tfailed: installed.installation,\n\t\t\t\t\t\t\t\tprevious: {\n\t\t\t\t\t\t\t\t\tversion: 1,\n\t\t\t\t\t\t\t\t\tpluginId: previous.pluginId,\n\t\t\t\t\t\t\t\t\tpluginVersion: previous.pluginVersion,\n\t\t\t\t\t\t\t\t\tmanifestDigest: previous.manifestDigest,\n\t\t\t\t\t\t\t\t\tscope: previous.state.scope,\n\t\t\t\t\t\t\t\t\tscopeRef: previous.state.scopeRef,\n\t\t\t\t\t\t\t\t\tinstallationRef: previous.installationRef,\n\t\t\t\t\t\t\t\t\tstate: \"active\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tscope: \"global\",\n\t\t\t\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tif (rolledBack.status !== \"succeeded\") restoreFailure = rolledBack.error.code;\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\trestoreFailure = error instanceof Error ? error.message : String(error);\n\t\t\t\t\t}\n\t\t\t\t\tawait active.installer\n\t\t\t\t\t\t.remove({\n\t\t\t\t\t\t\tinstallation: installed.installation,\n\t\t\t\t\t\t\tscope: \"global\",\n\t\t\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t\tconst activationMessage = t(\"plugin activation failed ({code}) for {pluginId}\", {\n\t\t\t\t\t\tcode: activated.error.code,\n\t\t\t\t\t\tpluginId: acquired.manifest.id,\n\t\t\t\t\t});\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\trestoreFailure === undefined\n\t\t\t\t\t\t\t? activationMessage\n\t\t\t\t\t\t\t: `${activationMessage}; previous version restore failed: ${restoreFailure}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst persisted = await active.stateStore.upsert({\n\t\t\t\t\tinstallation: activated.installation,\n\t\t\t\t\tscope: \"global\",\n\t\t\t\t\tenabled: true,\n\t\t\t\t\tsource: acquired.source,\n\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t});\n\t\t\t\tif (persisted.status !== \"ok\") {\n\t\t\t\t\tawait active.installer\n\t\t\t\t\t\t.remove({\n\t\t\t\t\t\t\tinstallation: activated.installation,\n\t\t\t\t\t\t\tscope: \"global\",\n\t\t\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\tt(\"plugin state persist failed ({status}) for {pluginId}\", {\n\t\t\t\t\t\t\tstatus: persisted.status,\n\t\t\t\t\t\t\tpluginId: acquired.manifest.id,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\t// 出厂对齐语义(duplicate_active 修复,2026-10-08):stateStore 的 upsert\n\t\t\t\t// 按 scope:scopeRef:pluginId@version 去重,多版本并存是 store 的合法形态;\n\t\t\t\t// 但 bootstrap 把该插件在 global scope 对齐到工厂 range——升级成功后同插件\n\t\t\t\t// 其他版本的 active binding 必须退役,否则 active-bindings 解析遇\n\t\t\t\t// duplicate_active 拒选(真实升级 0.88.5→0.88.6 实证)。只退役 state 记录、\n\t\t\t\t// 不删安装目录;退役失败降级保留残留(等同修复前状态,下次启动重试)。\n\t\t\t\tconst allRecords = await active.stateStore.read({ ...(signal === undefined ? {} : { signal }) });\n\t\t\t\tif (allRecords.status === \"ok\") {\n\t\t\t\t\tfor (const record of allRecords.snapshot.records) {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\trecord.pluginId === activated.installation.pluginId &&\n\t\t\t\t\t\t\trecord.pluginVersion !== activated.installation.pluginVersion &&\n\t\t\t\t\t\t\trecord.state.scope === \"global\" &&\n\t\t\t\t\t\t\trecord.state.scopeRef === DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1 &&\n\t\t\t\t\t\t\trecord.state.lifecycle === \"active\"\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tawait active.stateStore\n\t\t\t\t\t\t\t\t.remove({\n\t\t\t\t\t\t\t\t\tpluginId: record.pluginId,\n\t\t\t\t\t\t\t\t\tpluginVersion: record.pluginVersion,\n\t\t\t\t\t\t\t\t\tscope: \"global\",\n\t\t\t\t\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn { pluginVersion: activated.installation.pluginVersion, source: acquired.source };\n\t\t\t} finally {\n\t\t\t\tawait release();\n\t\t\t}\n\t\t}\n\t};\n\ttry {\n\t\treturn await runPluginBootstrapV1({\n\t\t\tmanifest: options.manifest ?? BOOTSTRAP_FACTORY_PLUGIN_MANIFEST_V1,\n\t\t\tdeps,\n\t\t\t// First-run progress line (设计 §4.4, 2026-10-03): a fresh install is\n\t\t\t// otherwise silent for the whole network install window.\n\t\t\tonPendingInstalls:\n\t\t\t\toptions.onPendingInstalls ??\n\t\t\t\t((count: number) => {\n\t\t\t\t\tconsole.error(t(\"First run: installing {count} factory plugins…\", { count }));\n\t\t\t\t}),\n\t\t\t// Per-plugin progress (设计 §4.4 逐插件进度, 2026-10-04): one stderr\n\t\t\t// line per installed/aligned/failed entry; satisfied entries stay\n\t\t\t// silent so an all-green startup prints nothing extra.\n\t\t\tonAction:\n\t\t\t\toptions.onAction ??\n\t\t\t\t((action: PluginBootstrapActionV1) => {\n\t\t\t\t\tif (action.status === \"installed\") {\n\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\tt(\"{pluginId}: installed {version}\", {\n\t\t\t\t\t\t\t\tpluginId: action.pluginId,\n\t\t\t\t\t\t\t\tversion: action.installedVersion ?? \"\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t} else if (action.status === \"failed\") {\n\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\tt(\n\t\t\t\t\t\t\t\t\"{pluginId}: install failed ({code}) — startup continues; the next startup retries automatically\",\n\t\t\t\t\t\t\t\t{ pluginId: action.pluginId, code: action.errorCode ?? \"install_failed\" },\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t});\n\t} finally {\n\t\tconst active = session;\n\t\tsession = undefined;\n\t\tif (active !== undefined) {\n\t\t\tawait active.installHost.close().catch(() => undefined);\n\t\t\tawait active.acquisition.close().catch(() => undefined);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"node-plugin-bootstrap.js","sourceRoot":"","sources":["../../../src/plugins/ecosystem/node-plugin-bootstrap.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Node wiring for the startup plugin bootstrap (design\n * `插件独立分发与启动装配设计.md` §4.4, §0.7).\n *\n * `AGENT_FORGE_PLUGIN_BOOTSTRAP=off` skips everything, including the local\n * comparison. The comparison dependency reads the global and project\n * plugin-state.json stores only — read-only, and it never constructs an HTTP\n * client, so an assembled environment starts offline (G3). Network objects\n * (npm acquisition host, install host) are built lazily inside the install\n * action and closed when the bootstrap finishes. Installs go through the\n * npm acquisition path exclusively and persist the `npm:<host>` source label\n * in the global plugin state (same prepare/install/activate/upsert\n * transaction shape as the CLI install flow, implemented as a library\n * function without going through the CLI layer).\n */\n\nimport { join } from \"node:path\";\nimport { CONFIG_DIR_NAME } from \"../../config.ts\";\nimport { t } from \"../../core/i18n/index.ts\";\nimport { isOfflineModeEnabled } from \"../../core/package-process-runner.ts\";\nimport type { PluginBootstrapFactoryEntryV1 } from \"./bootstrap-factory-manifest.ts\";\nimport { BOOTSTRAP_FACTORY_PLUGIN_MANIFEST_V1 } from \"./bootstrap-factory-manifest.ts\";\nimport {\n\tDEFAULT_PLUGIN_STATE_FILE_V1,\n\tdefaultPluginHostFactsV1,\n\tdefaultPluginInstallStoreDirectoryV1,\n\tprojectPluginScopeRefV1,\n} from \"./default-active-bindings.ts\";\nimport { createPluginInstallerV1, DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1, type PluginInstallerV1 } from \"./installer.ts\";\nimport {\n\tcreateNodePackageInstallHostV1,\n\ttype NodePackageInstallHostV1,\n\ttype NodeZipPackageLimitsV1,\n} from \"./node-package-install-host.ts\";\nimport { createNodePluginAcquisitionHostV1, type NodePluginAcquisitionHostV1 } from \"./node-plugin-acquisition-host.ts\";\nimport {\n\ttype PluginBootstrapActionV1,\n\ttype PluginBootstrapDepsV1,\n\ttype PluginBootstrapResultV1,\n\trunPluginBootstrapV1,\n} from \"./plugin-bootstrap.ts\";\nimport { createPluginStateStoreV1, type PluginStateRecordV1, type PluginStateStoreV1 } from \"./state.ts\";\n\n/** Environment variable disabling the startup bootstrap entirely (`off`). */\nexport const AGENT_FORGE_PLUGIN_BOOTSTRAP_ENV_V1 = \"AGENT_FORGE_PLUGIN_BOOTSTRAP\";\n\n/** The only recognized disabling value; every other value (and absence) keeps bootstrap on. */\nconst PLUGIN_BOOTSTRAP_DISABLED_VALUE_V1 = \"off\";\n\nexport interface NodePluginBootstrapOptionsV1 {\n\treadonly agentDir: string;\n\treadonly cwd: string;\n\t/** Defaults to the frozen factory manifest. */\n\treadonly manifest?: readonly PluginBootstrapFactoryEntryV1[];\n\t/** Bare http(s) npm registry origin; forwarded to the acquisition host. */\n\treadonly npmRegistry?: string;\n\t/** npm registry bearer token; forwarded to the acquisition host. */\n\treadonly npmToken?: string;\n\treadonly limits?: NodeZipPackageLimitsV1;\n\t/** Deterministic fetch replacement for tests. */\n\treadonly fetch?: typeof globalThis.fetch;\n\treadonly signal?: AbortSignal;\n\t/** Full dependency override for deterministic tests; the env switch still applies. */\n\treadonly deps?: PluginBootstrapDepsV1;\n\t/** Overrides the first-run/upgrade progress line (stderr) for tests and hosts. */\n\treadonly onPendingInstalls?: (count: number) => void;\n\t/** Overrides the per-entry \"installing…\" start line (stderr) for tests and hosts. */\n\treadonly onInstallStart?: (entry: { readonly npmName: string; readonly pluginId: string }) => void;\n\t/**\n\t * Overrides the per-plugin progress renderer (stderr) for tests and hosts.\n\t * The default prints one line per installed/aligned/failed entry and stays\n\t * silent for satisfied entries.\n\t */\n\treadonly onAction?: (action: PluginBootstrapActionV1) => void;\n}\n\nexport type NodePluginBootstrapResultV1 = PluginBootstrapResultV1 & {\n\t/**\n\t * Present when a posture switch short-circuited the run before any local\n\t * comparison: the wiring-level env off switch, or the offline posture\n\t * (设计 §4.4 offline 姿态门, 2026-10-03 — offline installs cannot succeed\n\t * and would only fail slowly, so the run is skipped immediately).\n\t */\n\treadonly skipped?: \"disabled\" | \"offline\";\n};\n\n/** Install-scoped boundaries; created lazily by the first install action. */\ninterface InstallSessionV1 {\n\treadonly acquisition: NodePluginAcquisitionHostV1;\n\treadonly installHost: NodePackageInstallHostV1;\n\treadonly installer: PluginInstallerV1;\n\treadonly stateStore: PluginStateStoreV1;\n}\n\nfunction readActiveVersions(\n\tstore: PluginStateStoreV1,\n\tscope: \"global\" | \"project\",\n\tscopeRef: string,\n\tsignal: AbortSignal | undefined,\n): Promise<ReadonlyMap<string, string>> {\n\treturn (async () => {\n\t\tconst result = await store.read({ ...(signal === undefined ? {} : { signal }) });\n\t\tif (result.status !== \"ok\" && result.status !== \"missing\")\n\t\t\tthrow new Error(\n\t\t\t\tt(\"plugin state read failed ({detail}) for the {scope} scope\", {\n\t\t\t\t\tdetail: result.diagnostics[0]?.code ?? result.status,\n\t\t\t\t\tscope,\n\t\t\t\t}),\n\t\t\t);\n\t\tconst versions = new Map<string, string>();\n\t\tfor (const record of result.snapshot.records) {\n\t\t\tif (record.state.scope !== scope || record.state.scopeRef !== scopeRef) continue;\n\t\t\t// Enabled is deliberately not filtered: a disabled plugin still owns\n\t\t\t// its active installation record, and bootstrap never touches it (G5).\n\t\t\tif (record.state.lifecycle !== \"active\") continue;\n\t\t\tversions.set(record.pluginId, record.pluginVersion);\n\t\t}\n\t\treturn versions;\n\t})();\n}\n\n/**\n * Previous active installation record for one plugin id in the bootstrap's\n * global installation scope, or undefined when the plugin has no active\n * global installation. Mirrors the CLI update transaction's previous lookup\n * (cli.ts `findCurrentStateRecord`): same plugin id, same scope/scopeRef,\n * lifecycle active — bootstrap installs exclusively into this scope, so only\n * a record here can have had its runtime disposed by host.activate.\n */\nasync function readPreviousActiveRecord(\n\tstore: PluginStateStoreV1,\n\tpluginId: string,\n\tsignal: AbortSignal | undefined,\n): Promise<PluginStateRecordV1 | undefined> {\n\tconst result = await store.read({ ...(signal === undefined ? {} : { signal }) });\n\tif (result.status !== \"ok\" && result.status !== \"missing\")\n\t\tthrow new Error(\n\t\t\tt(\"plugin state read failed ({detail}) for the {scope} scope\", {\n\t\t\t\tdetail: result.diagnostics[0]?.code ?? result.status,\n\t\t\t\tscope: \"global\",\n\t\t\t}),\n\t\t);\n\treturn result.snapshot.records.find(\n\t\t(record) =>\n\t\t\trecord.pluginId === pluginId &&\n\t\t\trecord.state.scope === \"global\" &&\n\t\t\trecord.state.scopeRef === DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1 &&\n\t\t\trecord.state.lifecycle === \"active\",\n\t);\n}\n\n/**\n * Run the startup bootstrap for one agent directory. The function resolves\n * with a structured result and never throws for per-plugin failures; only a\n * wiring-level construction error propagates to the caller (main.ts guards\n * startup with its own catch).\n */\nexport async function runNodePluginBootstrapV1(\n\toptions: NodePluginBootstrapOptionsV1,\n): Promise<NodePluginBootstrapResultV1> {\n\tif (process.env[AGENT_FORGE_PLUGIN_BOOTSTRAP_ENV_V1]?.trim() === PLUGIN_BOOTSTRAP_DISABLED_VALUE_V1) {\n\t\treturn Object.freeze({\n\t\t\tversion: 1,\n\t\t\tstatus: \"succeeded\",\n\t\t\tactions: Object.freeze([]),\n\t\t\tdiagnostics: Object.freeze([]),\n\t\t\tdurationMs: 0,\n\t\t\tskipped: \"disabled\",\n\t\t});\n\t}\n\tif (isOfflineModeEnabled()) {\n\t\treturn Object.freeze({\n\t\t\tversion: 1,\n\t\t\tstatus: \"succeeded\",\n\t\t\tactions: Object.freeze([]),\n\t\t\tdiagnostics: Object.freeze([\n\t\t\t\t{\n\t\t\t\t\tcode: \"bootstrap_skipped_offline\",\n\t\t\t\t\tmessage: t(\n\t\t\t\t\t\t\"Offline posture (AGENT_FORGE_OFFLINE) skips the startup plugin bootstrap; run `agent-forge plugin install <package>` explicitly to install plugins.\",\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t] as const),\n\t\t\tdurationMs: 0,\n\t\t\tskipped: \"offline\",\n\t\t});\n\t}\n\tif (options.deps !== undefined) {\n\t\treturn await runPluginBootstrapV1({\n\t\t\tmanifest: options.manifest ?? BOOTSTRAP_FACTORY_PLUGIN_MANIFEST_V1,\n\t\t\tdeps: options.deps,\n\t\t\t...(options.onPendingInstalls === undefined ? {} : { onPendingInstalls: options.onPendingInstalls }),\n\t\t\t...(options.onAction === undefined ? {} : { onAction: options.onAction }),\n\t\t});\n\t}\n\tlet session: InstallSessionV1 | undefined;\n\tconst ensureSession = async (): Promise<InstallSessionV1> => {\n\t\tif (session !== undefined) return session;\n\t\tconst acquisition = createNodePluginAcquisitionHostV1({\n\t\t\t...(options.npmRegistry === undefined ? {} : { npmRegistry: options.npmRegistry }),\n\t\t\t...(options.npmToken === undefined ? {} : { npmToken: options.npmToken }),\n\t\t\t...(options.fetch === undefined ? {} : { fetch: options.fetch }),\n\t\t\t...(options.limits === undefined ? {} : { limits: options.limits }),\n\t\t});\n\t\tconst installHost = await createNodePackageInstallHostV1({\n\t\t\topenArtifact: (input) => acquisition.openArtifact(input),\n\t\t\tinstallationDirectory: defaultPluginInstallStoreDirectoryV1(options.agentDir),\n\t\t\tpersistence: \"persistent\",\n\t\t\tsupportedScopes: [\"global\", \"project\"],\n\t\t\t...(options.limits === undefined ? {} : { ...options.limits }),\n\t\t});\n\t\tsession = {\n\t\t\tacquisition,\n\t\t\tinstallHost,\n\t\t\tinstaller: createPluginInstallerV1({ host: installHost }),\n\t\t\tstateStore: createPluginStateStoreV1({ filePath: join(options.agentDir, DEFAULT_PLUGIN_STATE_FILE_V1) }),\n\t\t};\n\t\treturn session;\n\t};\n\tconst deps: PluginBootstrapDepsV1 = {\n\t\tlistActive: async ({ signal }) => {\n\t\t\tconst globalVersions = readActiveVersions(\n\t\t\t\tcreatePluginStateStoreV1({ filePath: join(options.agentDir, DEFAULT_PLUGIN_STATE_FILE_V1) }),\n\t\t\t\t\"global\",\n\t\t\t\tDEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tconst projectVersions = readActiveVersions(\n\t\t\t\tcreatePluginStateStoreV1({ filePath: join(options.cwd, CONFIG_DIR_NAME, DEFAULT_PLUGIN_STATE_FILE_V1) }),\n\t\t\t\t\"project\",\n\t\t\t\tprojectPluginScopeRefV1(options.cwd),\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tconst [globalResult, projectResult] = await Promise.all([globalVersions, projectVersions]);\n\t\t\t// Project precedence mirrors active-binding resolution: a project\n\t\t\t// installation shadows the same plugin id from the global scope.\n\t\t\treturn new Map([...globalResult, ...projectResult]);\n\t\t},\n\t\tinstallFromNpm: async ({ npmName, range, signal }) => {\n\t\t\t// Download retry (设计 §4.4 下载重试, 2026-10-04): transient registry\n\t\t\t// failures retried with backoff at this wiring layer so the decision\n\t\t\t// loop stays pure. Abort propagates immediately between attempts.\n\t\t\tlet lastError: unknown;\n\t\t\tfor (let attempt = 1; attempt <= 3; attempt++) {\n\t\t\t\tif (signal?.aborted === true) throw lastError ?? new Error(\"install aborted\");\n\t\t\t\ttry {\n\t\t\t\t\treturn await installFromNpmOnce({ npmName, range, signal });\n\t\t\t\t} catch (error) {\n\t\t\t\t\tlastError = error;\n\t\t\t\t\t// Abort during the attempt surfaces as the attempt's own error;\n\t\t\t\t\t// the loop-top check and the backoff listener cover the pauses.\n\t\t\t\t\tif (attempt === 3) throw error;\n\t\t\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\t\t\tconst timer = setTimeout(resolve, 400 * 2 ** (attempt - 1));\n\t\t\t\t\t\tsignal?.addEventListener(\n\t\t\t\t\t\t\t\"abort\",\n\t\t\t\t\t\t\t() => {\n\t\t\t\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\t\t\t\treject(lastError);\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{ once: true },\n\t\t\t\t\t\t);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow lastError;\n\t\t},\n\t\t...(options.signal === undefined ? {} : { signal: options.signal }),\n\t};\n\tconst installFromNpmOnce = async (input: {\n\t\treadonly npmName: string;\n\t\treadonly range?: string;\n\t\treadonly signal?: AbortSignal;\n\t}): Promise<{ readonly pluginVersion: string; readonly source: string }> => {\n\t\tconst { npmName, range, signal } = input;\n\t\t{\n\t\t\tconst active = await ensureSession();\n\t\t\t// npm refs only (§0.7): scoped or plain names with the shared minimal\n\t\t\t// range subset; no github/gitee/url/local ref can be expressed here.\n\t\t\tconst ref = range === undefined ? npmName : `${npmName}@${range}`;\n\t\t\tconst acquired = await active.acquisition.resolvePackage({ ref, ...(signal === undefined ? {} : { signal }) });\n\t\t\tlet released = false;\n\t\t\tconst release = async (): Promise<void> => {\n\t\t\t\tif (released) return;\n\t\t\t\treleased = true;\n\t\t\t\tawait acquired.release();\n\t\t\t};\n\t\t\ttry {\n\t\t\t\tconst verified = active.installer.prepare({\n\t\t\t\t\tmanifest: acquired.manifest,\n\t\t\t\t\tsource: acquired.source,\n\t\t\t\t\thost: defaultPluginHostFactsV1(),\n\t\t\t\t});\n\t\t\t\tif (verified.status !== \"verified\")\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\tt(\"installer prepare rejected {pluginId}: {reason}\", {\n\t\t\t\t\t\t\tpluginId: acquired.manifest.id,\n\t\t\t\t\t\t\treason: verified.reason,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\tconst installed = await active.installer.install({\n\t\t\t\t\trecord: verified.record,\n\t\t\t\t\tscope: \"global\",\n\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\tartifact: acquired.artifact,\n\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t});\n\t\t\t\tif (installed.status !== \"succeeded\")\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\tt(\"plugin install failed ({code}) for {pluginId}\", {\n\t\t\t\t\t\t\tcode: installed.error.code,\n\t\t\t\t\t\t\tpluginId: acquired.manifest.id,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\tconst activated = await active.installer.activate({\n\t\t\t\t\tinstallation: installed.installation,\n\t\t\t\t\trecord: verified.record,\n\t\t\t\t\tscope: \"global\",\n\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t});\n\t\t\t\tif (activated.status !== \"succeeded\") {\n\t\t\t\t\t// CLI update-transaction alignment (cli.ts activate-failure\n\t\t\t\t\t// path): host.activate disposed the previous runtime for this\n\t\t\t\t\t// installation key before the new candidate failed to commit,\n\t\t\t\t\t// so restore the previous active installation through the\n\t\t\t\t\t// installer rollback. Rollback runs before the failed\n\t\t\t\t\t// candidate is removed because host.rollback requires the\n\t\t\t\t\t// failed record to still exist.\n\t\t\t\t\tlet restoreFailure: string | undefined;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst previous = await readPreviousActiveRecord(active.stateStore, acquired.manifest.id, signal);\n\t\t\t\t\t\tif (previous !== undefined) {\n\t\t\t\t\t\t\tconst rolledBack = await active.installer.rollback({\n\t\t\t\t\t\t\t\tfailed: installed.installation,\n\t\t\t\t\t\t\t\tprevious: {\n\t\t\t\t\t\t\t\t\tversion: 1,\n\t\t\t\t\t\t\t\t\tpluginId: previous.pluginId,\n\t\t\t\t\t\t\t\t\tpluginVersion: previous.pluginVersion,\n\t\t\t\t\t\t\t\t\tmanifestDigest: previous.manifestDigest,\n\t\t\t\t\t\t\t\t\tscope: previous.state.scope,\n\t\t\t\t\t\t\t\t\tscopeRef: previous.state.scopeRef,\n\t\t\t\t\t\t\t\t\tinstallationRef: previous.installationRef,\n\t\t\t\t\t\t\t\t\tstate: \"active\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tscope: \"global\",\n\t\t\t\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tif (rolledBack.status !== \"succeeded\") restoreFailure = rolledBack.error.code;\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\trestoreFailure = error instanceof Error ? error.message : String(error);\n\t\t\t\t\t}\n\t\t\t\t\tawait active.installer\n\t\t\t\t\t\t.remove({\n\t\t\t\t\t\t\tinstallation: installed.installation,\n\t\t\t\t\t\t\tscope: \"global\",\n\t\t\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t\tconst activationMessage = t(\"plugin activation failed ({code}) for {pluginId}\", {\n\t\t\t\t\t\tcode: activated.error.code,\n\t\t\t\t\t\tpluginId: acquired.manifest.id,\n\t\t\t\t\t});\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\trestoreFailure === undefined\n\t\t\t\t\t\t\t? activationMessage\n\t\t\t\t\t\t\t: `${activationMessage}; previous version restore failed: ${restoreFailure}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst persisted = await active.stateStore.upsert({\n\t\t\t\t\tinstallation: activated.installation,\n\t\t\t\t\tscope: \"global\",\n\t\t\t\t\tenabled: true,\n\t\t\t\t\tsource: acquired.source,\n\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t});\n\t\t\t\tif (persisted.status !== \"ok\") {\n\t\t\t\t\tawait active.installer\n\t\t\t\t\t\t.remove({\n\t\t\t\t\t\t\tinstallation: activated.installation,\n\t\t\t\t\t\t\tscope: \"global\",\n\t\t\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\tt(\"plugin state persist failed ({status}) for {pluginId}\", {\n\t\t\t\t\t\t\tstatus: persisted.status,\n\t\t\t\t\t\t\tpluginId: acquired.manifest.id,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\t// 出厂对齐语义(duplicate_active 修复,2026-10-08):stateStore 的 upsert\n\t\t\t\t// 按 scope:scopeRef:pluginId@version 去重,多版本并存是 store 的合法形态;\n\t\t\t\t// 但 bootstrap 把该插件在 global scope 对齐到工厂 range——升级成功后同插件\n\t\t\t\t// 其他版本的 active binding 必须退役,否则 active-bindings 解析遇\n\t\t\t\t// duplicate_active 拒选(真实升级 0.88.5→0.88.6 实证)。只退役 state 记录、\n\t\t\t\t// 不删安装目录;退役失败降级保留残留(等同修复前状态,下次启动重试)。\n\t\t\t\tconst allRecords = await active.stateStore.read({ ...(signal === undefined ? {} : { signal }) });\n\t\t\t\tif (allRecords.status === \"ok\") {\n\t\t\t\t\tfor (const record of allRecords.snapshot.records) {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\trecord.pluginId === activated.installation.pluginId &&\n\t\t\t\t\t\t\trecord.pluginVersion !== activated.installation.pluginVersion &&\n\t\t\t\t\t\t\trecord.state.scope === \"global\" &&\n\t\t\t\t\t\t\trecord.state.scopeRef === DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1 &&\n\t\t\t\t\t\t\trecord.state.lifecycle === \"active\"\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tawait active.stateStore\n\t\t\t\t\t\t\t\t.remove({\n\t\t\t\t\t\t\t\t\tpluginId: record.pluginId,\n\t\t\t\t\t\t\t\t\tpluginVersion: record.pluginVersion,\n\t\t\t\t\t\t\t\t\tscope: \"global\",\n\t\t\t\t\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn { pluginVersion: activated.installation.pluginVersion, source: acquired.source };\n\t\t\t} finally {\n\t\t\t\tawait release();\n\t\t\t}\n\t\t}\n\t};\n\ttry {\n\t\treturn await runPluginBootstrapV1({\n\t\t\tmanifest: options.manifest ?? BOOTSTRAP_FACTORY_PLUGIN_MANIFEST_V1,\n\t\t\tdeps,\n\t\t\t// First-run/upgrade progress line (设计 §4.4, 2026-10-03; 升级对齐也计数\n\t\t\t// 2026-10-08): the npm alignment window is otherwise silent — a\n\t\t\t// 0.88.7→0.88.8 upgrade replayed at 38s with zero feedback.\n\t\t\tonPendingInstalls:\n\t\t\t\toptions.onPendingInstalls ??\n\t\t\t\t((count: number) => {\n\t\t\t\t\tconsole.error(t(\"Aligning {count} factory plugin(s) from npm…\", { count }));\n\t\t\t\t}),\n\t\t\t// Per-entry start line (2026-10-08 走查): npm metadata + tarball take\n\t\t\t// seconds per plugin; the waiting user sees which plugin is being\n\t\t\t// fetched before its completion line lands.\n\t\t\tonInstallStart:\n\t\t\t\toptions.onInstallStart ??\n\t\t\t\t((entry) => {\n\t\t\t\t\tconsole.error(t(\"{pluginId}: installing…\", { pluginId: entry.pluginId }));\n\t\t\t\t}),\n\t\t\t// Per-plugin progress (设计 §4.4 逐插件进度, 2026-10-04): one stderr\n\t\t\t// line per installed/aligned/failed entry; satisfied entries stay\n\t\t\t// silent so an all-green startup prints nothing extra.\n\t\t\tonAction:\n\t\t\t\toptions.onAction ??\n\t\t\t\t((action: PluginBootstrapActionV1) => {\n\t\t\t\t\tif (action.status === \"installed\") {\n\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\tt(\"{pluginId}: installed {version}\", {\n\t\t\t\t\t\t\t\tpluginId: action.pluginId,\n\t\t\t\t\t\t\t\tversion: action.installedVersion ?? \"\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t} else if (action.status === \"failed\") {\n\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\tt(\n\t\t\t\t\t\t\t\t\"{pluginId}: install failed ({code}) — startup continues; the next startup retries automatically\",\n\t\t\t\t\t\t\t\t{ pluginId: action.pluginId, code: action.errorCode ?? \"install_failed\" },\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t});\n\t} finally {\n\t\tconst active = session;\n\t\tsession = undefined;\n\t\tif (active !== undefined) {\n\t\t\tawait active.installHost.close().catch(() => undefined);\n\t\t\tawait active.acquisition.close().catch(() => undefined);\n\t\t}\n\t}\n}\n"]}
@@ -96,6 +96,16 @@ export declare function runPluginBootstrapV1(input: {
96
96
  * itself stays free of console I/O.
97
97
  */
98
98
  readonly onPendingInstalls?: (count: number) => void;
99
+ /**
100
+ * Fired when an install/align actually starts (2026-10-08 启动走查): npm
101
+ * metadata + tarball take seconds per plugin, so the waiting user needs a
102
+ * per-entry start line, not only the completion lines. Upgrade alignments
103
+ * fire this too.
104
+ */
105
+ readonly onInstallStart?: (entry: {
106
+ readonly npmName: string;
107
+ readonly pluginId: string;
108
+ }) => void;
99
109
  /**
100
110
  * Fired after every per-entry action (设计 §4.4 逐插件进度, 2026-10-04):
101
111
  * the host renders one line per install/align/failure so the waiting user
@@ -1 +1 @@
1
- {"version":3,"file":"plugin-bootstrap.d.ts","sourceRoot":"","sources":["../../../src/plugins/ecosystem/plugin-bootstrap.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Startup plugin bootstrap orchestrator (design\n * `插件独立分发与启动装配设计.md` §4.4, §0.7). Pure injectable decision loop\n * over the frozen factory manifest: every entry is compared against the\n * locally resolved active installations; missing plugins and factory entries\n * whose active version no longer satisfies the factory range are handed to\n * the npm install dependency — bootstrap never acquires through any other\n * source.\n *\n * Coverage rules (G5, with the 2026-10-04 install-and-use exception): a\n * satisfied range is skipped silently (zero acquisition calls, G3); a factory\n * entry whose range is unsatisfied is automatically realigned by installing\n * the factory range — factory plugins are version-locked to the running\n * framework, so an unsatisfied range is a broken compatibility state, not a\n * user preference to protect. User-installed third-party plugins are not in\n * the factory manifest and stay untouched. Failure semantics (G4): one\n * plugin's install failure is recorded and the loop continues; the run itself\n * never throws, and a failed entry self-heals on the next startup because it\n * stays unsatisfied. All effects live behind the injected dependencies, so\n * the loop is fully deterministic under mocks.\n */\n\nimport type { PluginBootstrapFactoryEntryV1 } from \"./bootstrap-factory-manifest.ts\";\nimport { selectNpmPackageVersionV1 } from \"./node-npm-registry-source.ts\";\nimport { PluginAcquisitionErrorV1 } from \"./plugin-acquisition.ts\";\n\nexport const PLUGIN_BOOTSTRAP_VERSION_V1 = 1 as const;\n\n/** Read-only local lookup of active installation versions (never networked). */\nexport type PluginBootstrapListActiveV1 = (input: {\n\treadonly signal?: AbortSignal;\n}) => Promise<ReadonlyMap<string, string>>;\n\n/** npm-only install action; resolves with the installed version and source label. */\nexport type PluginBootstrapInstallFromNpmV1 = (input: {\n\treadonly npmName: string;\n\treadonly range?: string;\n\treadonly signal?: AbortSignal;\n}) => Promise<{ readonly pluginVersion: string; readonly source: string }>;\n\nexport interface PluginBootstrapDepsV1 {\n\treadonly listActive: PluginBootstrapListActiveV1;\n\treadonly installFromNpm: PluginBootstrapInstallFromNpmV1;\n\t/** Deterministic clock for the observed duration; defaults to Date.now. */\n\treadonly now?: () => number;\n\t/** Cooperative cancellation; checked before each manifest entry. */\n\treadonly signal?: AbortSignal;\n}\n\nexport type PluginBootstrapActionStatusV1 = \"skipped\" | \"installed\" | \"failed\";\n\nexport type PluginBootstrapActionReasonV1 =\n\t| \"satisfies_range\"\n\t| \"installed\"\n\t| \"range_aligned\"\n\t| \"install_failed\"\n\t| \"align_failed\";\n\n/** One per-entry outcome; the version fields carry what each status observed. */\nexport interface PluginBootstrapActionV1 {\n\treadonly pluginId: string;\n\treadonly status: PluginBootstrapActionStatusV1;\n\treadonly reason: PluginBootstrapActionReasonV1;\n\treadonly npmName: string;\n\treadonly range?: string;\n\t/** Active version that decided a skipped outcome. */\n\treadonly activeVersion?: string;\n\t/** Version reported by the install action. */\n\treadonly installedVersion?: string;\n\t/** npm source label reported by the install action. */\n\treadonly source?: string;\n\t/** Acquisition or install failure code for failed actions. */\n\treadonly errorCode?: string;\n}\n\nexport type PluginBootstrapDiagnosticCodeV1 =\n\t| \"install_failed\"\n\t| \"list_active_failed\"\n\t| \"cancelled\"\n\t| \"bootstrap_failed\"\n\t| \"bootstrap_skipped_offline\";\n\nexport interface PluginBootstrapDiagnosticV1 {\n\treadonly code: PluginBootstrapDiagnosticCodeV1;\n\treadonly message: string;\n\treadonly pluginId?: string;\n}\n\nexport type PluginBootstrapStatusV1 = \"succeeded\" | \"failed\";\n\nexport interface PluginBootstrapResultV1 {\n\treadonly version: typeof PLUGIN_BOOTSTRAP_VERSION_V1;\n\t/** Aggregated outcome: failed (any failed entry) > succeeded. */\n\treadonly status: PluginBootstrapStatusV1;\n\treadonly actions: readonly PluginBootstrapActionV1[];\n\treadonly diagnostics: readonly PluginBootstrapDiagnosticV1[];\n\treadonly durationMs: number;\n}\n\n/**\n * A version satisfies a factory range under the same selection semantics npm\n * resolution uses: it is the version that would be picked if it were the only\n * candidate. Keeps local comparison and registry resolution consistent.\n */\nfunction satisfiesRangeV1(version: string, range: string | undefined): boolean {\n\treturn selectNpmPackageVersionV1([version], range) === version;\n}\n\n/** Executable repair command for diagnostics (G4 remediation hint). */\nexport function pluginBootstrapInstallCommandV1(entry: { readonly npmName: string; readonly range?: string }): string {\n\treturn `agent-forge plugin install ${entry.npmName}@${entry.range ?? \"latest\"}`;\n}\n\nfunction errorMessage(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n\nfunction acquireErrorCode(error: unknown): string {\n\treturn error instanceof PluginAcquisitionErrorV1 ? error.code : \"install_failed\";\n}\n\nfunction actionOf(\n\tentry: PluginBootstrapFactoryEntryV1,\n\tstatus: PluginBootstrapActionStatusV1,\n\treason: PluginBootstrapActionReasonV1,\n): PluginBootstrapActionV1 {\n\treturn Object.freeze({\n\t\tpluginId: entry.pluginId,\n\t\tstatus,\n\t\treason,\n\t\tnpmName: entry.npmName,\n\t\t...(entry.range === undefined ? {} : { range: entry.range }),\n\t});\n}\n\n/**\n * Run the bootstrap decision loop. An empty manifest short-circuits before\n * any dependency is touched, so the factory-empty S1-c2 state performs zero\n * reads on real startups.\n */\nexport async function runPluginBootstrapV1(input: {\n\treadonly manifest: readonly PluginBootstrapFactoryEntryV1[];\n\treadonly deps: PluginBootstrapDepsV1;\n\t/**\n\t * Fired once after the local active-state read when at least one factory\n\t * entry has no active installation — the host's first-run progress line\n\t * hook (设计 §4.4 首装进度行, 2026-10-03): a fresh install is otherwise\n\t * silent for the whole network install window. Injected effect, the loop\n\t * itself stays free of console I/O.\n\t */\n\treadonly onPendingInstalls?: (count: number) => void;\n\t/**\n\t * Fired after every per-entry action (设计 §4.4 逐插件进度, 2026-10-04):\n\t * the host renders one line per install/align/failure so the waiting user\n\t * sees live state; skipped entries stay silent at the host's discretion.\n\t * Injected effect, the loop itself stays free of console I/O.\n\t */\n\treadonly onAction?: (action: PluginBootstrapActionV1) => void;\n}): Promise<PluginBootstrapResultV1> {\n\tconst now = input.deps.now ?? Date.now;\n\tconst startedAt = now();\n\tconst finish = (\n\t\tstatus: PluginBootstrapStatusV1,\n\t\tactions: readonly PluginBootstrapActionV1[],\n\t\tdiagnostics: readonly PluginBootstrapDiagnosticV1[],\n\t): PluginBootstrapResultV1 =>\n\t\tObject.freeze({\n\t\t\tversion: PLUGIN_BOOTSTRAP_VERSION_V1,\n\t\t\tstatus,\n\t\t\tactions: Object.freeze([...actions]),\n\t\t\tdiagnostics: Object.freeze([...diagnostics]),\n\t\t\tdurationMs: Math.max(0, now() - startedAt),\n\t\t});\n\tif (input.manifest.length === 0) return finish(\"succeeded\", [], []);\n\tconst signal = input.deps.signal;\n\tlet active: ReadonlyMap<string, string>;\n\ttry {\n\t\tactive = await input.deps.listActive({ ...(signal === undefined ? {} : { signal }) });\n\t} catch (error) {\n\t\treturn finish(\n\t\t\t\"failed\",\n\t\t\t[],\n\t\t\t[\n\t\t\t\t{\n\t\t\t\t\tcode: \"list_active_failed\",\n\t\t\t\t\tmessage: `Active plugin state could not be read; bootstrap installed nothing: ${errorMessage(error)}`,\n\t\t\t\t},\n\t\t\t],\n\t\t);\n\t}\n\tconst actions: PluginBootstrapActionV1[] = [];\n\tconst diagnostics: PluginBootstrapDiagnosticV1[] = [];\n\tconst record = (action: PluginBootstrapActionV1): void => {\n\t\tactions.push(action);\n\t\tinput.onAction?.(action);\n\t};\n\tif (input.onPendingInstalls !== undefined) {\n\t\tconst pending = input.manifest.filter((entry) => !active.has(entry.pluginId)).length;\n\t\tif (pending > 0) input.onPendingInstalls(pending);\n\t}\n\tfor (const entry of input.manifest) {\n\t\tif (signal?.aborted === true) {\n\t\t\tdiagnostics.push({\n\t\t\t\tcode: \"cancelled\",\n\t\t\t\tmessage: \"Plugin bootstrap was cancelled before all factory entries were processed\",\n\t\t\t});\n\t\t\tbreak;\n\t\t}\n\t\tconst activeVersion = active.get(entry.pluginId);\n\t\tif (activeVersion !== undefined && satisfiesRangeV1(activeVersion, entry.range)) {\n\t\t\trecord(Object.freeze({ ...actionOf(entry, \"skipped\", \"satisfies_range\"), activeVersion }));\n\t\t\tcontinue;\n\t\t}\n\t\ttry {\n\t\t\tconst installed = await input.deps.installFromNpm({\n\t\t\t\tnpmName: entry.npmName,\n\t\t\t\t...(entry.range === undefined ? {} : { range: entry.range }),\n\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t});\n\t\t\trecord(\n\t\t\t\tObject.freeze({\n\t\t\t\t\t...actionOf(entry, \"installed\", activeVersion === undefined ? \"installed\" : \"range_aligned\"),\n\t\t\t\t\tinstalledVersion: installed.pluginVersion,\n\t\t\t\t\tsource: installed.source,\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tconst errorCode = acquireErrorCode(error);\n\t\t\tconst aligning = activeVersion !== undefined;\n\t\t\trecord(\n\t\t\t\tObject.freeze({ ...actionOf(entry, \"failed\", aligning ? \"align_failed\" : \"install_failed\"), errorCode }),\n\t\t\t);\n\t\t\tdiagnostics.push({\n\t\t\t\tcode: \"install_failed\",\n\t\t\t\tpluginId: entry.pluginId,\n\t\t\t\tmessage: aligning\n\t\t\t\t\t? `Bootstrap could not align ${entry.pluginId} (active ${activeVersion}) to the factory range ` +\n\t\t\t\t\t\t`${entry.range ?? \"latest\"} from npm (${errorCode}: ${errorMessage(error)}). ` +\n\t\t\t\t\t\t`Startup continues; the factory range stays unsatisfied, so the next startup retries automatically. ` +\n\t\t\t\t\t\t`Run \\`${pluginBootstrapInstallCommandV1(entry)}\\` to align it now.`\n\t\t\t\t\t: `Bootstrap could not install ${entry.pluginId} from npm (${errorCode}: ${errorMessage(error)}). ` +\n\t\t\t\t\t\t`Startup continues without it; its capabilities stay unavailable until installed, ` +\n\t\t\t\t\t\t`and the next startup retries automatically. ` +\n\t\t\t\t\t\t`Run \\`${pluginBootstrapInstallCommandV1(entry)}\\` to install it manually.`,\n\t\t\t});\n\t\t}\n\t}\n\tconst status: PluginBootstrapStatusV1 = actions.some((action) => action.status === \"failed\")\n\t\t? \"failed\"\n\t\t: \"succeeded\";\n\treturn finish(status, actions, diagnostics);\n}\n"]}
1
+ {"version":3,"file":"plugin-bootstrap.d.ts","sourceRoot":"","sources":["../../../src/plugins/ecosystem/plugin-bootstrap.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Startup plugin bootstrap orchestrator (design\n * `插件独立分发与启动装配设计.md` §4.4, §0.7). Pure injectable decision loop\n * over the frozen factory manifest: every entry is compared against the\n * locally resolved active installations; missing plugins and factory entries\n * whose active version no longer satisfies the factory range are handed to\n * the npm install dependency — bootstrap never acquires through any other\n * source.\n *\n * Coverage rules (G5, with the 2026-10-04 install-and-use exception): a\n * satisfied range is skipped silently (zero acquisition calls, G3); a factory\n * entry whose range is unsatisfied is automatically realigned by installing\n * the factory range — factory plugins are version-locked to the running\n * framework, so an unsatisfied range is a broken compatibility state, not a\n * user preference to protect. User-installed third-party plugins are not in\n * the factory manifest and stay untouched. Failure semantics (G4): one\n * plugin's install failure is recorded and the loop continues; the run itself\n * never throws, and a failed entry self-heals on the next startup because it\n * stays unsatisfied. All effects live behind the injected dependencies, so\n * the loop is fully deterministic under mocks.\n */\n\nimport type { PluginBootstrapFactoryEntryV1 } from \"./bootstrap-factory-manifest.ts\";\nimport { selectNpmPackageVersionV1 } from \"./node-npm-registry-source.ts\";\nimport { PluginAcquisitionErrorV1 } from \"./plugin-acquisition.ts\";\n\nexport const PLUGIN_BOOTSTRAP_VERSION_V1 = 1 as const;\n\n/** Read-only local lookup of active installation versions (never networked). */\nexport type PluginBootstrapListActiveV1 = (input: {\n\treadonly signal?: AbortSignal;\n}) => Promise<ReadonlyMap<string, string>>;\n\n/** npm-only install action; resolves with the installed version and source label. */\nexport type PluginBootstrapInstallFromNpmV1 = (input: {\n\treadonly npmName: string;\n\treadonly range?: string;\n\treadonly signal?: AbortSignal;\n}) => Promise<{ readonly pluginVersion: string; readonly source: string }>;\n\nexport interface PluginBootstrapDepsV1 {\n\treadonly listActive: PluginBootstrapListActiveV1;\n\treadonly installFromNpm: PluginBootstrapInstallFromNpmV1;\n\t/** Deterministic clock for the observed duration; defaults to Date.now. */\n\treadonly now?: () => number;\n\t/** Cooperative cancellation; checked before each manifest entry. */\n\treadonly signal?: AbortSignal;\n}\n\nexport type PluginBootstrapActionStatusV1 = \"skipped\" | \"installed\" | \"failed\";\n\nexport type PluginBootstrapActionReasonV1 =\n\t| \"satisfies_range\"\n\t| \"installed\"\n\t| \"range_aligned\"\n\t| \"install_failed\"\n\t| \"align_failed\";\n\n/** One per-entry outcome; the version fields carry what each status observed. */\nexport interface PluginBootstrapActionV1 {\n\treadonly pluginId: string;\n\treadonly status: PluginBootstrapActionStatusV1;\n\treadonly reason: PluginBootstrapActionReasonV1;\n\treadonly npmName: string;\n\treadonly range?: string;\n\t/** Active version that decided a skipped outcome. */\n\treadonly activeVersion?: string;\n\t/** Version reported by the install action. */\n\treadonly installedVersion?: string;\n\t/** npm source label reported by the install action. */\n\treadonly source?: string;\n\t/** Acquisition or install failure code for failed actions. */\n\treadonly errorCode?: string;\n}\n\nexport type PluginBootstrapDiagnosticCodeV1 =\n\t| \"install_failed\"\n\t| \"list_active_failed\"\n\t| \"cancelled\"\n\t| \"bootstrap_failed\"\n\t| \"bootstrap_skipped_offline\";\n\nexport interface PluginBootstrapDiagnosticV1 {\n\treadonly code: PluginBootstrapDiagnosticCodeV1;\n\treadonly message: string;\n\treadonly pluginId?: string;\n}\n\nexport type PluginBootstrapStatusV1 = \"succeeded\" | \"failed\";\n\nexport interface PluginBootstrapResultV1 {\n\treadonly version: typeof PLUGIN_BOOTSTRAP_VERSION_V1;\n\t/** Aggregated outcome: failed (any failed entry) > succeeded. */\n\treadonly status: PluginBootstrapStatusV1;\n\treadonly actions: readonly PluginBootstrapActionV1[];\n\treadonly diagnostics: readonly PluginBootstrapDiagnosticV1[];\n\treadonly durationMs: number;\n}\n\n/**\n * A version satisfies a factory range under the same selection semantics npm\n * resolution uses: it is the version that would be picked if it were the only\n * candidate. Keeps local comparison and registry resolution consistent.\n */\nfunction satisfiesRangeV1(version: string, range: string | undefined): boolean {\n\treturn selectNpmPackageVersionV1([version], range) === version;\n}\n\n/** Executable repair command for diagnostics (G4 remediation hint). */\nexport function pluginBootstrapInstallCommandV1(entry: { readonly npmName: string; readonly range?: string }): string {\n\treturn `agent-forge plugin install ${entry.npmName}@${entry.range ?? \"latest\"}`;\n}\n\nfunction errorMessage(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n\nfunction acquireErrorCode(error: unknown): string {\n\treturn error instanceof PluginAcquisitionErrorV1 ? error.code : \"install_failed\";\n}\n\nfunction actionOf(\n\tentry: PluginBootstrapFactoryEntryV1,\n\tstatus: PluginBootstrapActionStatusV1,\n\treason: PluginBootstrapActionReasonV1,\n): PluginBootstrapActionV1 {\n\treturn Object.freeze({\n\t\tpluginId: entry.pluginId,\n\t\tstatus,\n\t\treason,\n\t\tnpmName: entry.npmName,\n\t\t...(entry.range === undefined ? {} : { range: entry.range }),\n\t});\n}\n\n/**\n * Run the bootstrap decision loop. An empty manifest short-circuits before\n * any dependency is touched, so the factory-empty S1-c2 state performs zero\n * reads on real startups.\n */\nexport async function runPluginBootstrapV1(input: {\n\treadonly manifest: readonly PluginBootstrapFactoryEntryV1[];\n\treadonly deps: PluginBootstrapDepsV1;\n\t/**\n\t * Fired once after the local active-state read when at least one factory\n\t * entry has no active installation — the host's first-run progress line\n\t * hook (设计 §4.4 首装进度行, 2026-10-03): a fresh install is otherwise\n\t * silent for the whole network install window. Injected effect, the loop\n\t * itself stays free of console I/O.\n\t */\n\treadonly onPendingInstalls?: (count: number) => void;\n\t/**\n\t * Fired when an install/align actually starts (2026-10-08 启动走查): npm\n\t * metadata + tarball take seconds per plugin, so the waiting user needs a\n\t * per-entry start line, not only the completion lines. Upgrade alignments\n\t * fire this too.\n\t */\n\treadonly onInstallStart?: (entry: { readonly npmName: string; readonly pluginId: string }) => void;\n\t/**\n\t * Fired after every per-entry action (设计 §4.4 逐插件进度, 2026-10-04):\n\t * the host renders one line per install/align/failure so the waiting user\n\t * sees live state; skipped entries stay silent at the host's discretion.\n\t * Injected effect, the loop itself stays free of console I/O.\n\t */\n\treadonly onAction?: (action: PluginBootstrapActionV1) => void;\n}): Promise<PluginBootstrapResultV1> {\n\tconst now = input.deps.now ?? Date.now;\n\tconst startedAt = now();\n\tconst finish = (\n\t\tstatus: PluginBootstrapStatusV1,\n\t\tactions: readonly PluginBootstrapActionV1[],\n\t\tdiagnostics: readonly PluginBootstrapDiagnosticV1[],\n\t): PluginBootstrapResultV1 =>\n\t\tObject.freeze({\n\t\t\tversion: PLUGIN_BOOTSTRAP_VERSION_V1,\n\t\t\tstatus,\n\t\t\tactions: Object.freeze([...actions]),\n\t\t\tdiagnostics: Object.freeze([...diagnostics]),\n\t\t\tdurationMs: Math.max(0, now() - startedAt),\n\t\t});\n\tif (input.manifest.length === 0) return finish(\"succeeded\", [], []);\n\tconst signal = input.deps.signal;\n\tlet active: ReadonlyMap<string, string>;\n\ttry {\n\t\tactive = await input.deps.listActive({ ...(signal === undefined ? {} : { signal }) });\n\t} catch (error) {\n\t\treturn finish(\n\t\t\t\"failed\",\n\t\t\t[],\n\t\t\t[\n\t\t\t\t{\n\t\t\t\t\tcode: \"list_active_failed\",\n\t\t\t\t\tmessage: `Active plugin state could not be read; bootstrap installed nothing: ${errorMessage(error)}`,\n\t\t\t\t},\n\t\t\t],\n\t\t);\n\t}\n\tconst actions: PluginBootstrapActionV1[] = [];\n\tconst diagnostics: PluginBootstrapDiagnosticV1[] = [];\n\t// Per-entry results slot by manifest index: parallel workers complete out of\n\t// order, but actions/diagnostics and their callbacks keep a stable order —\n\t// onAction stays live (fires the moment an entry settles) while the arrays\n\t// the result carries always read in manifest order.\n\tconst slots: Array<{ actions: PluginBootstrapActionV1[]; diagnostics: PluginBootstrapDiagnosticV1[] }> =\n\t\tinput.manifest.map(() => ({ actions: [], diagnostics: [] }));\n\tconst record = (index: number, action: PluginBootstrapActionV1): void => {\n\t\tslots[index]!.actions.push(action);\n\t\tinput.onAction?.(action);\n\t};\n\tconst addDiagnostic = (index: number, diagnostic: PluginBootstrapDiagnosticV1): void => {\n\t\tslots[index]!.diagnostics.push(diagnostic);\n\t};\n\tif (input.onPendingInstalls !== undefined) {\n\t\t// Upgrades count too (2026-10-08 启动走查): an active version outside the\n\t\t// factory range goes through the same npm install window as a missing\n\t\t// one — counting only missing entries left upgrade startups silent for\n\t\t// the whole alignment (real replay: 9 plugins, 38s, zero feedback).\n\t\tconst pending = input.manifest.filter((entry) => {\n\t\t\tconst activeVersion = active.get(entry.pluginId);\n\t\t\treturn activeVersion === undefined || !satisfiesRangeV1(activeVersion, entry.range);\n\t\t}).length;\n\t\tif (pending > 0) input.onPendingInstalls(pending);\n\t}\n\tconst processEntry = async (index: number, entry: PluginBootstrapFactoryEntryV1): Promise<void> => {\n\t\tconst signal = input.deps.signal;\n\t\tconst activeVersion = active.get(entry.pluginId);\n\t\tif (activeVersion !== undefined && satisfiesRangeV1(activeVersion, entry.range)) {\n\t\t\trecord(index, Object.freeze({ ...actionOf(entry, \"skipped\", \"satisfies_range\"), activeVersion }));\n\t\t\treturn;\n\t\t}\n\t\tinput.onInstallStart?.({ npmName: entry.npmName, pluginId: entry.pluginId });\n\t\ttry {\n\t\t\tconst installed = await input.deps.installFromNpm({\n\t\t\t\tnpmName: entry.npmName,\n\t\t\t\t...(entry.range === undefined ? {} : { range: entry.range }),\n\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t});\n\t\t\trecord(\n\t\t\t\tindex,\n\t\t\t\tObject.freeze({\n\t\t\t\t\t...actionOf(entry, \"installed\", activeVersion === undefined ? \"installed\" : \"range_aligned\"),\n\t\t\t\t\tinstalledVersion: installed.pluginVersion,\n\t\t\t\t\tsource: installed.source,\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tconst errorCode = acquireErrorCode(error);\n\t\t\tconst aligning = activeVersion !== undefined;\n\t\t\trecord(\n\t\t\t\tindex,\n\t\t\t\tObject.freeze({ ...actionOf(entry, \"failed\", aligning ? \"align_failed\" : \"install_failed\"), errorCode }),\n\t\t\t);\n\t\t\taddDiagnostic(\n\t\t\t\tindex,\n\t\t\t\taligning\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tcode: \"install_failed\",\n\t\t\t\t\t\t\tpluginId: entry.pluginId,\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t`Bootstrap could not align ${entry.pluginId} (active ${activeVersion}) to the factory range ` +\n\t\t\t\t\t\t\t\t`${entry.range ?? \"latest\"} from npm (${errorCode}: ${errorMessage(error)}). ` +\n\t\t\t\t\t\t\t\t`Startup continues; the factory range stays unsatisfied, so the next startup retries automatically. ` +\n\t\t\t\t\t\t\t\t`Run \\`${pluginBootstrapInstallCommandV1(entry)}\\` to align it now.`,\n\t\t\t\t\t\t}\n\t\t\t\t\t: {\n\t\t\t\t\t\t\tcode: \"install_failed\",\n\t\t\t\t\t\t\tpluginId: entry.pluginId,\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t`Bootstrap could not install ${entry.pluginId} from npm (${errorCode}: ${errorMessage(error)}). ` +\n\t\t\t\t\t\t\t\t`Startup continues without it; its capabilities stay unavailable until installed, ` +\n\t\t\t\t\t\t\t\t`and the next startup retries automatically. ` +\n\t\t\t\t\t\t\t\t`Run \\`${pluginBootstrapInstallCommandV1(entry)}\\` to install it manually.`,\n\t\t\t\t\t\t},\n\t\t\t);\n\t\t}\n\t};\n\t// Bounded parallelism (2026-10-08 启动走查): serial npm alignment held a\n\t// 0.88.7→0.88.8 upgrade startup hostage for ~38s (9 plugins × metadata +\n\t// tarball, each silent). Four workers cut the wall time to roughly the\n\t// slowest four installs while the npm acquisition host and the per-plugin\n\t// activation chains stay per-key serialized underneath.\n\tconst WORKERS = 4;\n\tlet cursor = 0;\n\tlet cancelNoted = false;\n\tconst worker = async (): Promise<void> => {\n\t\twhile (cursor < input.manifest.length) {\n\t\t\tif (input.deps.signal?.aborted === true) {\n\t\t\t\tif (!cancelNoted) {\n\t\t\t\t\tcancelNoted = true;\n\t\t\t\t\tdiagnostics.push({\n\t\t\t\t\t\tcode: \"cancelled\",\n\t\t\t\t\t\tmessage: \"Plugin bootstrap was cancelled before all factory entries were processed\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst index = cursor++;\n\t\t\tawait processEntry(index, input.manifest[index]!);\n\t\t}\n\t};\n\tawait Promise.all(Array.from({ length: Math.min(WORKERS, input.manifest.length) }, worker));\n\tfor (const slot of slots) {\n\t\tactions.push(...slot.actions);\n\t\tdiagnostics.push(...slot.diagnostics);\n\t}\n\tconst status: PluginBootstrapStatusV1 = actions.some((action) => action.status === \"failed\")\n\t\t? \"failed\"\n\t\t: \"succeeded\";\n\treturn finish(status, actions, diagnostics);\n}\n"]}