@xyagent/cli 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +860 -0
- package/bin/agentlink +8585 -0
- package/bin/agentlink-agent +7 -0
- package/bin/agentlink-mcp-stdio +173 -0
- package/docs/LOGGING.md +159 -0
- package/package.json +38 -0
- package/src/core.mjs +320 -0
- package/src/tunnel.mjs +291 -0
- package/src/tunnel_proxy.mjs +913 -0
- package/src/tunnel_service.mjs +699 -0
- package/src/tunnel_state.mjs +301 -0
- package/src/unified_producer.mjs +259 -0
- package/src-ext/README.md +75 -0
- package/src-ext/bin.mjs +128 -0
- package/src-ext/commands/agent.mjs +66 -0
- package/src-ext/commands/help.mjs +31 -0
- package/src-ext/commands/pair.mjs +455 -0
- package/src-ext/commands/reload.mjs +78 -0
- package/src-ext/commands/service.mjs +64 -0
- package/src-ext/core/accountResolver.mjs +137 -0
- package/src-ext/core/activeRuns.mjs +76 -0
- package/src-ext/core/agentlinkToolsBootstrap.mjs +75 -0
- package/src-ext/core/agentlinkToolsRegistry.mjs +323 -0
- package/src-ext/core/agentlinkToolsServer.mjs +109 -0
- package/src-ext/core/approvalGateway.mjs +132 -0
- package/src-ext/core/attachAuditLog.mjs +79 -0
- package/src-ext/core/attachDenylist.mjs +128 -0
- package/src-ext/core/autoTunnelDetector.mjs +98 -0
- package/src-ext/core/autoTunnelGateway.mjs +393 -0
- package/src-ext/core/autoTunnelWiring.mjs +184 -0
- package/src-ext/core/boundedMap.mjs +98 -0
- package/src-ext/core/bridgeAttachAutoScanner.mjs +237 -0
- package/src-ext/core/bridgeHooks.mjs +168 -0
- package/src-ext/core/bridgeSelfUninstall.mjs +133 -0
- package/src-ext/core/claudeTrust.mjs +74 -0
- package/src-ext/core/codexTrust.mjs +77 -0
- package/src-ext/core/cosUploadClient.mjs +326 -0
- package/src-ext/core/createWorkspace.mjs +170 -0
- package/src-ext/core/daemonRestart.mjs +49 -0
- package/src-ext/core/defaultWorkspace.mjs +65 -0
- package/src-ext/core/ensureGitRepo.mjs +57 -0
- package/src-ext/core/hermesProfile.mjs +75 -0
- package/src-ext/core/interruptGateway.mjs +119 -0
- package/src-ext/core/interruptRunListener.mjs +106 -0
- package/src-ext/core/mcpRuntimeFanout.mjs +236 -0
- package/src-ext/core/mcpStdioClient.mjs +316 -0
- package/src-ext/core/openclawGatewayClient.mjs +230 -0
- package/src-ext/core/pairCodeClient.mjs +331 -0
- package/src-ext/core/pairInventory.mjs +59 -0
- package/src-ext/core/pathExpand.mjs +34 -0
- package/src-ext/core/preprocessAttachments.mjs +113 -0
- package/src-ext/core/relayWorker.mjs +284 -0
- package/src-ext/core/relayWorkerHttp.mjs +58 -0
- package/src-ext/core/scanWorkspaces.mjs +842 -0
- package/src-ext/core/sessionSlot.mjs +70 -0
- package/src-ext/core/stateMachine.mjs +85 -0
- package/src-ext/core/toolsServerLifecycle.mjs +169 -0
- package/src-ext/core/unifiedDispatchHandler.mjs +439 -0
- package/src-ext/core/usageReporter.mjs +207 -0
- package/src-ext/openclaw-plugin/envelope-builder.cjs +619 -0
- package/src-ext/openclaw-plugin/index.cjs +1919 -0
- package/src-ext/openclaw-plugin/openclaw.plugin.json +20 -0
- package/src-ext/openclaw-plugin/package.json +12 -0
- package/src-ext/openclaw-plugin/relay-defaults.json +3 -0
- package/src-ext/openclaw-plugin/todoTranslatorUtils.cjs +101 -0
- package/src-ext/runtime/_shared/cliExec.mjs +138 -0
- package/src-ext/runtime/_shared/cosTextRefs.mjs +120 -0
- package/src-ext/runtime/_shared/fetchAndBase64Encode.mjs +52 -0
- package/src-ext/runtime/_shared/relayObjectToBlock.mjs +307 -0
- package/src-ext/runtime/_shared/sessionPicker.mjs +418 -0
- package/src-ext/runtime/_shared/slashCommandRouter.mjs +1346 -0
- package/src-ext/runtime/_shared/todoTranslatorUtils.mjs +94 -0
- package/src-ext/runtime/claude/askUserQuestionTranslator.mjs +241 -0
- package/src-ext/runtime/claude/customCommandsCollector.mjs +132 -0
- package/src-ext/runtime/claude/handleRequest.mjs +1418 -0
- package/src-ext/runtime/claude/index.mjs +160 -0
- package/src-ext/runtime/claude/internalCommandNames.mjs +58 -0
- package/src-ext/runtime/claude/launcher.mjs +313 -0
- package/src-ext/runtime/claude/mcpConfigAdapter.mjs +174 -0
- package/src-ext/runtime/claude/mcpConfigWriter.mjs +57 -0
- package/src-ext/runtime/claude/modelContextWindow.mjs +30 -0
- package/src-ext/runtime/claude/modelScanner.mjs +257 -0
- package/src-ext/runtime/claude/permissionInterruptBlock.mjs +86 -0
- package/src-ext/runtime/claude/permissionPromptServer.mjs +170 -0
- package/src-ext/runtime/claude/permissionPromptServerWorker.mjs +214 -0
- package/src-ext/runtime/claude/preflight.mjs +62 -0
- package/src-ext/runtime/claude/stdoutParser.mjs +365 -0
- package/src-ext/runtime/claude/todoTranslator.mjs +66 -0
- package/src-ext/runtime/claude/usageProbe.mjs +576 -0
- package/src-ext/runtime/claude/usageProbeStrip.mjs +14 -0
- package/src-ext/runtime/codex/codexCustomCommandsCollector.mjs +87 -0
- package/src-ext/runtime/codex/elicitationHandler.mjs +91 -0
- package/src-ext/runtime/codex/eventMapper.mjs +154 -0
- package/src-ext/runtime/codex/handleRequest.mjs +1152 -0
- package/src-ext/runtime/codex/index.mjs +142 -0
- package/src-ext/runtime/codex/mcpConfigAdapter.mjs +155 -0
- package/src-ext/runtime/codex/modelScanner.mjs +305 -0
- package/src-ext/runtime/codex/planNotificationHandler.mjs +61 -0
- package/src-ext/runtime/codex/preflight.mjs +67 -0
- package/src-ext/runtime/hermes/commandBackend.mjs +117 -0
- package/src-ext/runtime/hermes/envSetup.mjs +229 -0
- package/src-ext/runtime/hermes/gatewayManager.mjs +625 -0
- package/src-ext/runtime/hermes/handleRequest.mjs +1257 -0
- package/src-ext/runtime/hermes/httpBackend.mjs +219 -0
- package/src-ext/runtime/hermes/index.mjs +196 -0
- package/src-ext/runtime/hermes/mcpConfigAdapter.mjs +189 -0
- package/src-ext/runtime/hermes/preflight.mjs +359 -0
- package/src-ext/runtime/hermes/responsesBackend.mjs +276 -0
- package/src-ext/runtime/hermes/sessionStore.mjs +78 -0
- package/src-ext/runtime/hermes/todoTranslator.mjs +90 -0
- package/src-ext/runtime/hermes/workspaceRpc.mjs +124 -0
- package/src-ext/runtime/openclaw/buildOpenclawDaemonInput.mjs +143 -0
- package/src-ext/runtime/openclaw/mcpConfigAdapter.mjs +91 -0
- package/src-ext/runtime/openclaw/openclawConfig.mjs +145 -0
- package/src-ext/runtime/picoclaw/constants.mjs +39 -0
- package/src-ext/runtime/picoclaw/handleRequest.mjs +289 -0
- package/src-ext/runtime/picoclaw/index.mjs +314 -0
- package/src-ext/runtime/picoclaw/pairFlow.mjs +224 -0
- package/src-ext/runtime/picoclaw/state.mjs +78 -0
- package/src-ext/runtime/picoclaw/todoTranslator.mjs +67 -0
- package/src-ext/runtime/picoclaw/translator.mjs +272 -0
- package/src-ext/runtime/picoclaw/wsClient.mjs +129 -0
- package/src-ext/service/serviceManager.mjs +521 -0
- package/src-shared/README.md +44 -0
- package/src-shared/chat_push_normalizer.mjs +113 -0
- package/src-shared/correlation.mjs +25 -0
- package/src-shared/envelope_builder.mjs +612 -0
- package/src-shared/logger.mjs +167 -0
- package/src-shared/relay-defaults.json +3 -0
- package/src-shared/relay_revoke.mjs +46 -0
- package/src-shared/vendor/README.md +60 -0
- package/src-shared/vendor/smol-toml/LICENSE +24 -0
- package/src-shared/vendor/smol-toml/README.md +236 -0
- package/src-shared/vendor/smol-toml/VENDORED.md +37 -0
- package/src-shared/vendor/smol-toml/dist/date.d.ts +41 -0
- package/src-shared/vendor/smol-toml/dist/date.js +127 -0
- package/src-shared/vendor/smol-toml/dist/error.d.ts +38 -0
- package/src-shared/vendor/smol-toml/dist/error.js +63 -0
- package/src-shared/vendor/smol-toml/dist/extract.d.ts +30 -0
- package/src-shared/vendor/smol-toml/dist/extract.js +109 -0
- package/src-shared/vendor/smol-toml/dist/index.cjs +900 -0
- package/src-shared/vendor/smol-toml/dist/index.d.ts +43 -0
- package/src-shared/vendor/smol-toml/dist/index.js +33 -0
- package/src-shared/vendor/smol-toml/dist/parse.d.ts +37 -0
- package/src-shared/vendor/smol-toml/dist/parse.js +148 -0
- package/src-shared/vendor/smol-toml/dist/primitive.d.ts +31 -0
- package/src-shared/vendor/smol-toml/dist/primitive.js +178 -0
- package/src-shared/vendor/smol-toml/dist/stringify.d.ts +31 -0
- package/src-shared/vendor/smol-toml/dist/stringify.js +163 -0
- package/src-shared/vendor/smol-toml/dist/struct.d.ts +32 -0
- package/src-shared/vendor/smol-toml/dist/struct.js +194 -0
- package/src-shared/vendor/smol-toml/dist/util.d.ts +42 -0
- package/src-shared/vendor/smol-toml/dist/util.js +100 -0
- package/src-shared/vendor/smol-toml/package.json +54 -0
- package/src-shared/vendor/yaml/LICENSE +13 -0
- package/src-shared/vendor/yaml/README.md +155 -0
- package/src-shared/vendor/yaml/VENDORED.md +29 -0
- package/src-shared/vendor/yaml/bin.mjs +11 -0
- package/src-shared/vendor/yaml/browser/dist/compose/compose-collection.js +88 -0
- package/src-shared/vendor/yaml/browser/dist/compose/compose-doc.js +42 -0
- package/src-shared/vendor/yaml/browser/dist/compose/compose-node.js +92 -0
- package/src-shared/vendor/yaml/browser/dist/compose/compose-scalar.js +80 -0
- package/src-shared/vendor/yaml/browser/dist/compose/composer.js +217 -0
- package/src-shared/vendor/yaml/browser/dist/compose/resolve-block-map.js +113 -0
- package/src-shared/vendor/yaml/browser/dist/compose/resolve-block-scalar.js +198 -0
- package/src-shared/vendor/yaml/browser/dist/compose/resolve-block-seq.js +47 -0
- package/src-shared/vendor/yaml/browser/dist/compose/resolve-end.js +37 -0
- package/src-shared/vendor/yaml/browser/dist/compose/resolve-flow-collection.js +203 -0
- package/src-shared/vendor/yaml/browser/dist/compose/resolve-flow-scalar.js +223 -0
- package/src-shared/vendor/yaml/browser/dist/compose/resolve-props.js +148 -0
- package/src-shared/vendor/yaml/browser/dist/compose/util-contains-newline.js +34 -0
- package/src-shared/vendor/yaml/browser/dist/compose/util-empty-scalar-position.js +27 -0
- package/src-shared/vendor/yaml/browser/dist/compose/util-flow-indent-check.js +15 -0
- package/src-shared/vendor/yaml/browser/dist/compose/util-map-includes.js +17 -0
- package/src-shared/vendor/yaml/browser/dist/doc/Document.js +334 -0
- package/src-shared/vendor/yaml/browser/dist/doc/anchors.js +72 -0
- package/src-shared/vendor/yaml/browser/dist/doc/applyReviver.js +55 -0
- package/src-shared/vendor/yaml/browser/dist/doc/createNode.js +89 -0
- package/src-shared/vendor/yaml/browser/dist/doc/directives.js +176 -0
- package/src-shared/vendor/yaml/browser/dist/errors.js +57 -0
- package/src-shared/vendor/yaml/browser/dist/index.js +17 -0
- package/src-shared/vendor/yaml/browser/dist/log.js +14 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/Alias.js +101 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/Collection.js +147 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/Node.js +38 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/Pair.js +36 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/Scalar.js +24 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/YAMLMap.js +144 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/YAMLSeq.js +113 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/addPairToJSMap.js +104 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/identity.js +36 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/toJS.js +37 -0
- package/src-shared/vendor/yaml/browser/dist/parse/cst-scalar.js +214 -0
- package/src-shared/vendor/yaml/browser/dist/parse/cst-stringify.js +61 -0
- package/src-shared/vendor/yaml/browser/dist/parse/cst-visit.js +97 -0
- package/src-shared/vendor/yaml/browser/dist/parse/cst.js +98 -0
- package/src-shared/vendor/yaml/browser/dist/parse/lexer.js +717 -0
- package/src-shared/vendor/yaml/browser/dist/parse/line-counter.js +39 -0
- package/src-shared/vendor/yaml/browser/dist/parse/parser.js +954 -0
- package/src-shared/vendor/yaml/browser/dist/public-api.js +99 -0
- package/src-shared/vendor/yaml/browser/dist/schema/Schema.js +38 -0
- package/src-shared/vendor/yaml/browser/dist/schema/common/map.js +17 -0
- package/src-shared/vendor/yaml/browser/dist/schema/common/null.js +15 -0
- package/src-shared/vendor/yaml/browser/dist/schema/common/seq.js +17 -0
- package/src-shared/vendor/yaml/browser/dist/schema/common/string.js +14 -0
- package/src-shared/vendor/yaml/browser/dist/schema/core/bool.js +19 -0
- package/src-shared/vendor/yaml/browser/dist/schema/core/float.js +43 -0
- package/src-shared/vendor/yaml/browser/dist/schema/core/int.js +38 -0
- package/src-shared/vendor/yaml/browser/dist/schema/core/schema.js +23 -0
- package/src-shared/vendor/yaml/browser/dist/schema/json/schema.js +62 -0
- package/src-shared/vendor/yaml/browser/dist/schema/tags.js +83 -0
- package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/binary.js +66 -0
- package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/bool.js +26 -0
- package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/float.js +46 -0
- package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/int.js +71 -0
- package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/omap.js +74 -0
- package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/pairs.js +78 -0
- package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/schema.js +37 -0
- package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/set.js +93 -0
- package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/timestamp.js +101 -0
- package/src-shared/vendor/yaml/browser/dist/stringify/foldFlowLines.js +146 -0
- package/src-shared/vendor/yaml/browser/dist/stringify/stringify.js +124 -0
- package/src-shared/vendor/yaml/browser/dist/stringify/stringifyCollection.js +143 -0
- package/src-shared/vendor/yaml/browser/dist/stringify/stringifyComment.js +20 -0
- package/src-shared/vendor/yaml/browser/dist/stringify/stringifyDocument.js +85 -0
- package/src-shared/vendor/yaml/browser/dist/stringify/stringifyNumber.js +24 -0
- package/src-shared/vendor/yaml/browser/dist/stringify/stringifyPair.js +150 -0
- package/src-shared/vendor/yaml/browser/dist/stringify/stringifyString.js +328 -0
- package/src-shared/vendor/yaml/browser/dist/util.js +11 -0
- package/src-shared/vendor/yaml/browser/dist/visit.js +233 -0
- package/src-shared/vendor/yaml/browser/index.js +5 -0
- package/src-shared/vendor/yaml/browser/package.json +3 -0
- package/src-shared/vendor/yaml/dist/cli.d.ts +8 -0
- package/src-shared/vendor/yaml/dist/cli.mjs +199 -0
- package/src-shared/vendor/yaml/dist/compose/compose-collection.d.ts +11 -0
- package/src-shared/vendor/yaml/dist/compose/compose-collection.js +90 -0
- package/src-shared/vendor/yaml/dist/compose/compose-doc.d.ts +7 -0
- package/src-shared/vendor/yaml/dist/compose/compose-doc.js +44 -0
- package/src-shared/vendor/yaml/dist/compose/compose-node.d.ts +28 -0
- package/src-shared/vendor/yaml/dist/compose/compose-node.js +95 -0
- package/src-shared/vendor/yaml/dist/compose/compose-scalar.d.ts +5 -0
- package/src-shared/vendor/yaml/dist/compose/compose-scalar.js +82 -0
- package/src-shared/vendor/yaml/dist/compose/composer.d.ts +62 -0
- package/src-shared/vendor/yaml/dist/compose/composer.js +221 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-block-map.d.ts +6 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-block-map.js +115 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-block-scalar.d.ts +11 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-block-scalar.js +200 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-block-seq.d.ts +6 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-block-seq.js +49 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-end.d.ts +6 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-end.js +39 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-flow-collection.d.ts +7 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-flow-collection.js +205 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-flow-scalar.d.ts +10 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-flow-scalar.js +225 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-props.d.ts +23 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-props.js +150 -0
- package/src-shared/vendor/yaml/dist/compose/util-contains-newline.d.ts +2 -0
- package/src-shared/vendor/yaml/dist/compose/util-contains-newline.js +36 -0
- package/src-shared/vendor/yaml/dist/compose/util-empty-scalar-position.d.ts +2 -0
- package/src-shared/vendor/yaml/dist/compose/util-empty-scalar-position.js +29 -0
- package/src-shared/vendor/yaml/dist/compose/util-flow-indent-check.d.ts +3 -0
- package/src-shared/vendor/yaml/dist/compose/util-flow-indent-check.js +17 -0
- package/src-shared/vendor/yaml/dist/compose/util-map-includes.d.ts +4 -0
- package/src-shared/vendor/yaml/dist/compose/util-map-includes.js +19 -0
- package/src-shared/vendor/yaml/dist/doc/Document.d.ts +141 -0
- package/src-shared/vendor/yaml/dist/doc/Document.js +336 -0
- package/src-shared/vendor/yaml/dist/doc/anchors.d.ts +24 -0
- package/src-shared/vendor/yaml/dist/doc/anchors.js +77 -0
- package/src-shared/vendor/yaml/dist/doc/applyReviver.d.ts +9 -0
- package/src-shared/vendor/yaml/dist/doc/applyReviver.js +57 -0
- package/src-shared/vendor/yaml/dist/doc/createNode.d.ts +17 -0
- package/src-shared/vendor/yaml/dist/doc/createNode.js +91 -0
- package/src-shared/vendor/yaml/dist/doc/directives.d.ts +49 -0
- package/src-shared/vendor/yaml/dist/doc/directives.js +178 -0
- package/src-shared/vendor/yaml/dist/errors.d.ts +21 -0
- package/src-shared/vendor/yaml/dist/errors.js +62 -0
- package/src-shared/vendor/yaml/dist/index.d.ts +22 -0
- package/src-shared/vendor/yaml/dist/index.js +50 -0
- package/src-shared/vendor/yaml/dist/log.d.ts +3 -0
- package/src-shared/vendor/yaml/dist/log.js +17 -0
- package/src-shared/vendor/yaml/dist/nodes/Alias.d.ts +28 -0
- package/src-shared/vendor/yaml/dist/nodes/Alias.js +103 -0
- package/src-shared/vendor/yaml/dist/nodes/Collection.d.ts +73 -0
- package/src-shared/vendor/yaml/dist/nodes/Collection.js +151 -0
- package/src-shared/vendor/yaml/dist/nodes/Node.d.ts +47 -0
- package/src-shared/vendor/yaml/dist/nodes/Node.js +40 -0
- package/src-shared/vendor/yaml/dist/nodes/Pair.d.ts +21 -0
- package/src-shared/vendor/yaml/dist/nodes/Pair.js +39 -0
- package/src-shared/vendor/yaml/dist/nodes/Scalar.d.ts +42 -0
- package/src-shared/vendor/yaml/dist/nodes/Scalar.js +27 -0
- package/src-shared/vendor/yaml/dist/nodes/YAMLMap.d.ts +53 -0
- package/src-shared/vendor/yaml/dist/nodes/YAMLMap.js +147 -0
- package/src-shared/vendor/yaml/dist/nodes/YAMLSeq.d.ts +60 -0
- package/src-shared/vendor/yaml/dist/nodes/YAMLSeq.js +115 -0
- package/src-shared/vendor/yaml/dist/nodes/addPairToJSMap.d.ts +4 -0
- package/src-shared/vendor/yaml/dist/nodes/addPairToJSMap.js +106 -0
- package/src-shared/vendor/yaml/dist/nodes/identity.d.ts +23 -0
- package/src-shared/vendor/yaml/dist/nodes/identity.js +53 -0
- package/src-shared/vendor/yaml/dist/nodes/toJS.d.ts +27 -0
- package/src-shared/vendor/yaml/dist/nodes/toJS.js +39 -0
- package/src-shared/vendor/yaml/dist/options.d.ts +338 -0
- package/src-shared/vendor/yaml/dist/parse/cst-scalar.d.ts +64 -0
- package/src-shared/vendor/yaml/dist/parse/cst-scalar.js +218 -0
- package/src-shared/vendor/yaml/dist/parse/cst-stringify.d.ts +8 -0
- package/src-shared/vendor/yaml/dist/parse/cst-stringify.js +63 -0
- package/src-shared/vendor/yaml/dist/parse/cst-visit.d.ts +39 -0
- package/src-shared/vendor/yaml/dist/parse/cst-visit.js +99 -0
- package/src-shared/vendor/yaml/dist/parse/cst.d.ts +108 -0
- package/src-shared/vendor/yaml/dist/parse/cst.js +112 -0
- package/src-shared/vendor/yaml/dist/parse/lexer.d.ts +87 -0
- package/src-shared/vendor/yaml/dist/parse/lexer.js +719 -0
- package/src-shared/vendor/yaml/dist/parse/line-counter.d.ts +22 -0
- package/src-shared/vendor/yaml/dist/parse/line-counter.js +41 -0
- package/src-shared/vendor/yaml/dist/parse/parser.d.ts +84 -0
- package/src-shared/vendor/yaml/dist/parse/parser.js +958 -0
- package/src-shared/vendor/yaml/dist/public-api.d.ts +43 -0
- package/src-shared/vendor/yaml/dist/public-api.js +104 -0
- package/src-shared/vendor/yaml/dist/schema/Schema.d.ts +18 -0
- package/src-shared/vendor/yaml/dist/schema/Schema.js +40 -0
- package/src-shared/vendor/yaml/dist/schema/common/map.d.ts +2 -0
- package/src-shared/vendor/yaml/dist/schema/common/map.js +19 -0
- package/src-shared/vendor/yaml/dist/schema/common/null.d.ts +4 -0
- package/src-shared/vendor/yaml/dist/schema/common/null.js +17 -0
- package/src-shared/vendor/yaml/dist/schema/common/seq.d.ts +2 -0
- package/src-shared/vendor/yaml/dist/schema/common/seq.js +19 -0
- package/src-shared/vendor/yaml/dist/schema/common/string.d.ts +2 -0
- package/src-shared/vendor/yaml/dist/schema/common/string.js +16 -0
- package/src-shared/vendor/yaml/dist/schema/core/bool.d.ts +4 -0
- package/src-shared/vendor/yaml/dist/schema/core/bool.js +21 -0
- package/src-shared/vendor/yaml/dist/schema/core/float.d.ts +4 -0
- package/src-shared/vendor/yaml/dist/schema/core/float.js +47 -0
- package/src-shared/vendor/yaml/dist/schema/core/int.d.ts +4 -0
- package/src-shared/vendor/yaml/dist/schema/core/int.js +42 -0
- package/src-shared/vendor/yaml/dist/schema/core/schema.d.ts +1 -0
- package/src-shared/vendor/yaml/dist/schema/core/schema.js +25 -0
- package/src-shared/vendor/yaml/dist/schema/json/schema.d.ts +2 -0
- package/src-shared/vendor/yaml/dist/schema/json/schema.js +64 -0
- package/src-shared/vendor/yaml/dist/schema/json-schema.d.ts +69 -0
- package/src-shared/vendor/yaml/dist/schema/tags.d.ts +40 -0
- package/src-shared/vendor/yaml/dist/schema/tags.js +86 -0
- package/src-shared/vendor/yaml/dist/schema/types.d.ts +90 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/binary.d.ts +2 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/binary.js +68 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/bool.d.ts +7 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/bool.js +29 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/float.d.ts +4 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/float.js +50 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/int.d.ts +5 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/int.js +76 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/omap.d.ts +28 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/omap.js +77 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/pairs.d.ts +10 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/pairs.js +82 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/schema.d.ts +1 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/schema.js +39 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/set.d.ts +28 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/set.js +96 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/timestamp.d.ts +6 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/timestamp.js +105 -0
- package/src-shared/vendor/yaml/dist/stringify/foldFlowLines.d.ts +34 -0
- package/src-shared/vendor/yaml/dist/stringify/foldFlowLines.js +151 -0
- package/src-shared/vendor/yaml/dist/stringify/stringify.d.ts +21 -0
- package/src-shared/vendor/yaml/dist/stringify/stringify.js +127 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyCollection.d.ts +17 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyCollection.js +145 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyComment.d.ts +10 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyComment.js +24 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyDocument.d.ts +4 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyDocument.js +87 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyNumber.d.ts +2 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyNumber.js +26 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyPair.d.ts +3 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyPair.js +152 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyString.d.ts +9 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyString.js +330 -0
- package/src-shared/vendor/yaml/dist/test-events.d.ts +4 -0
- package/src-shared/vendor/yaml/dist/test-events.js +134 -0
- package/src-shared/vendor/yaml/dist/util.d.ts +12 -0
- package/src-shared/vendor/yaml/dist/util.js +28 -0
- package/src-shared/vendor/yaml/dist/visit.d.ts +102 -0
- package/src-shared/vendor/yaml/dist/visit.js +236 -0
- package/src-shared/vendor/yaml/package.json +96 -0
- package/src-shared/vendor/yaml/util.js +2 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// Codex 桥接入口 — long-poll worker 模式。
|
|
2
|
+
//
|
|
3
|
+
// 与 claude bridge 思路相同:每条 user message 拉到后,起一次 codex mcp 子进程
|
|
4
|
+
// 跑完 turn 即退出,以避免长生命周期 SSE 订阅。
|
|
5
|
+
//
|
|
6
|
+
// 关键差异:codex 走 MCP stdio,需要先 initialize → tools/call codex(prompt) →
|
|
7
|
+
// 监听 notifications/codex/event → mcp 子进程会保持运行直到 codex 自己结束 turn,
|
|
8
|
+
// 我们在 task_complete 通知到达后主动 close 子进程。
|
|
9
|
+
//
|
|
10
|
+
// 转译规则:codex notification 的 params.msg 直接序列化成 native event,
|
|
11
|
+
// msg.type 已经对齐服务端 codex adapter 的 EventType 枚举(session_configured /
|
|
12
|
+
// agent_message_delta / task_started / ...);因此 native event 形如
|
|
13
|
+
// { event: "chunk", data: JSON.stringify(msg) }
|
|
14
|
+
// 服务端 codex adapter 直接吃这条 chunk 的 data 字段(stream-json line)。
|
|
15
|
+
|
|
16
|
+
import process from "node:process";
|
|
17
|
+
import path from "node:path";
|
|
18
|
+
|
|
19
|
+
import { createSessionSlot, SESSION_STATES } from "../../core/sessionSlot.mjs";
|
|
20
|
+
import { createRelayWorker } from "../../core/relayWorker.mjs";
|
|
21
|
+
import { bootstrapAgentlinkTools } from "../../core/agentlinkToolsBootstrap.mjs";
|
|
22
|
+
import { installAutoTunnel } from "../../core/autoTunnelWiring.mjs";
|
|
23
|
+
import { resolveSession } from "../../core/pairCodeClient.mjs";
|
|
24
|
+
import { createUsageReporter } from "../../core/usageReporter.mjs";
|
|
25
|
+
import { runWorkerWithRevokeHandling } from "../../core/bridgeSelfUninstall.mjs";
|
|
26
|
+
import { codexPreflight } from "./preflight.mjs";
|
|
27
|
+
import { createCodexHandleRequest } from "./handleRequest.mjs";
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
export async function runCodexBridge({ options, positional, log }) {
|
|
31
|
+
const cwd = path.resolve(options.cwd || process.cwd());
|
|
32
|
+
|
|
33
|
+
// 1. preflight
|
|
34
|
+
const pre = codexPreflight({ log });
|
|
35
|
+
if (!pre.ok) {
|
|
36
|
+
process.stderr.write(`codex preflight failed: ${pre.error}\n`);
|
|
37
|
+
if (pre.hint) process.stderr.write(`${pre.hint}\n`);
|
|
38
|
+
process.exit(65);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 2. resolve session
|
|
42
|
+
let gwId, relayBase, bridgeToken;
|
|
43
|
+
try {
|
|
44
|
+
const r = await resolveSession({ runtime: "codex", options, log });
|
|
45
|
+
gwId = r.gwId;
|
|
46
|
+
relayBase = r.relayUrl;
|
|
47
|
+
bridgeToken = r.bridgeToken;
|
|
48
|
+
log.info("bridge.preflight.pass", "codex runtime entry reached", {
|
|
49
|
+
cwd, relay: relayBase, gw_id: gwId, source: r.source, has_bridge_token: !!bridgeToken,
|
|
50
|
+
});
|
|
51
|
+
} catch (err) {
|
|
52
|
+
process.stderr.write(`${err?.message || err}\n`);
|
|
53
|
+
process.exit(65);
|
|
54
|
+
}
|
|
55
|
+
if (!bridgeToken) {
|
|
56
|
+
process.stderr.write("缺少 bridge_token,请重新运行 agentlink pair <code> -r codex\n");
|
|
57
|
+
process.exit(65);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const sessLog = log.child({ gw: gwId });
|
|
61
|
+
const worker = createRelayWorker({
|
|
62
|
+
relayUrl: relayBase, gwId, bridgeToken, runtime: "codex", log: sessLog,
|
|
63
|
+
});
|
|
64
|
+
installAutoTunnel({ relayUrl: relayBase, log: sessLog });
|
|
65
|
+
|
|
66
|
+
let lastRequestId = null;
|
|
67
|
+
const usage = createUsageReporter({
|
|
68
|
+
runtime: "codex", log: sessLog,
|
|
69
|
+
publishEvent: (event) => {
|
|
70
|
+
if (!lastRequestId) return Promise.resolve(false);
|
|
71
|
+
return worker.publishEvent(lastRequestId, event);
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
usage.setContextWindow(272_000);
|
|
75
|
+
|
|
76
|
+
const slot = createSessionSlot({ log: sessLog, name: "codex" });
|
|
77
|
+
slot.transition(SESSION_STATES.READY, "worker-ready");
|
|
78
|
+
|
|
79
|
+
// agentlinkToolsServer + discovery 文件(见 claude/index.mjs 同段注释)
|
|
80
|
+
let toolsLifecycle = null;
|
|
81
|
+
try {
|
|
82
|
+
toolsLifecycle = await bootstrapAgentlinkTools({
|
|
83
|
+
runtime: "codex", gwId, relayUrl: relayBase, bridgeToken,
|
|
84
|
+
worker, log: sessLog, version: pre?.version || "",
|
|
85
|
+
getLastRequestId: () => lastRequestId,
|
|
86
|
+
});
|
|
87
|
+
} catch (err) {
|
|
88
|
+
sessLog.warn("agentlink_tools.bootstrap.failed",
|
|
89
|
+
"agentlink tools server failed to start; continuing without tools", { err: err?.message });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const shuttingDownRef = { current: false };
|
|
93
|
+
let shuttingDown = false;
|
|
94
|
+
async function shutdown(reason) {
|
|
95
|
+
if (shuttingDown) return;
|
|
96
|
+
shuttingDown = true;
|
|
97
|
+
shuttingDownRef.current = true;
|
|
98
|
+
sessLog.info("bridge.shutdown", "shutting down", { reason });
|
|
99
|
+
worker.stop(reason);
|
|
100
|
+
try { usage.dispose(); } catch {}
|
|
101
|
+
if (toolsLifecycle?.stop) {
|
|
102
|
+
try { await toolsLifecycle.stop(); } catch {}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
process.once("SIGINT", () => { void shutdown("SIGINT").then(() => process.exit(0)); });
|
|
106
|
+
process.once("SIGTERM", () => { void shutdown("SIGTERM").then(() => process.exit(0)); });
|
|
107
|
+
|
|
108
|
+
// ---- handler:每条 in-flight 请求一次完整 turn ----
|
|
109
|
+
// D-C2.2.3: 替换内联 handleRequest,改为 createCodexHandleRequest 工厂函数
|
|
110
|
+
const _handleRequestImpl = createCodexHandleRequest({
|
|
111
|
+
pre,
|
|
112
|
+
cwd,
|
|
113
|
+
log: sessLog,
|
|
114
|
+
usage,
|
|
115
|
+
shuttingDownRef,
|
|
116
|
+
bridgeCtx: {
|
|
117
|
+
version: pre?.version || "",
|
|
118
|
+
gwId,
|
|
119
|
+
model: null, // codex 模型由 ~/.codex/config.toml 决定, bridge 没主动读
|
|
120
|
+
},
|
|
121
|
+
dispatchHandlerOptions: {
|
|
122
|
+
relayUrl: relayBase,
|
|
123
|
+
bridgeToken,
|
|
124
|
+
},
|
|
125
|
+
scanner: toolsLifecycle?.scanner ?? null,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
async function handleRequest(request, ctx) {
|
|
129
|
+
lastRequestId = ctx.requestId;
|
|
130
|
+
return _handleRequestImpl(request, ctx);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
sessLog.info("bridge.loop.entered", "codex worker entering long-poll loop", {
|
|
134
|
+
gw_id: gwId, cwd, version: pre.version || "", mcp_subcommand: pre.mcpSubcommand,
|
|
135
|
+
});
|
|
136
|
+
// 双向解绑:收到 App 端 deleteContact 触发的 HTTP 404 时自动清状态 + 卸 plist。
|
|
137
|
+
await runWorkerWithRevokeHandling({
|
|
138
|
+
worker, handler: handleRequest, runtime: "codex", log: sessLog,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// helpers 已移入 handleRequest.mjs
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// mcpConfigAdapter.mjs — Codex 的 MCP server 配置注入器。
|
|
2
|
+
//
|
|
3
|
+
// Codex 读 ~/.codex/config.toml,具体:
|
|
4
|
+
//
|
|
5
|
+
// [mcp_servers.<name>]
|
|
6
|
+
// command = "node"
|
|
7
|
+
// args = [...]
|
|
8
|
+
//
|
|
9
|
+
// 跟 Claude 不同:Codex 配置文件用 TOML,需要文档级 round-trip
|
|
10
|
+
// (vendored smol-toml)。smol-toml 不保留注释,但只会把我们改的 section
|
|
11
|
+
// 重写,用户其它 section/keys 通过 parse->merge->stringify 路径保留。
|
|
12
|
+
//
|
|
13
|
+
// 操作要求(spec.md Codex MCP server registration):
|
|
14
|
+
// - 通过 vendored smol-toml round-trip
|
|
15
|
+
// - non-destructive(其它 mcp_servers / 其它 sections / 其它 keys 都保留)
|
|
16
|
+
// - idempotent(args 数组 + command 与 desired 完全相等就不重写)
|
|
17
|
+
// - versioned
|
|
18
|
+
|
|
19
|
+
import path from "node:path";
|
|
20
|
+
import os from "node:os";
|
|
21
|
+
import fs from "node:fs/promises";
|
|
22
|
+
|
|
23
|
+
import { fileURLToPath } from "node:url";
|
|
24
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
25
|
+
const TOML = await import(
|
|
26
|
+
path.join(__dirname, "../../../src-shared/vendor/smol-toml/dist/index.js")
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
export const fileExtension = "toml";
|
|
30
|
+
|
|
31
|
+
const SERVER_NAME = "agentlink";
|
|
32
|
+
|
|
33
|
+
function configPath() {
|
|
34
|
+
return path.join(os.homedir(), ".codex", "config.toml");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function read() {
|
|
38
|
+
return await fs.readFile(configPath(), "utf8");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function register({ args, log } = {}) {
|
|
42
|
+
const cfgPath = configPath();
|
|
43
|
+
const desired = {
|
|
44
|
+
command: "node",
|
|
45
|
+
args: args.slice(),
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
let raw = "";
|
|
49
|
+
try {
|
|
50
|
+
raw = await fs.readFile(cfgPath, "utf8");
|
|
51
|
+
} catch (err) {
|
|
52
|
+
if (err.code !== "ENOENT") throw err;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let cfg = {};
|
|
56
|
+
if (raw.trim().length > 0) {
|
|
57
|
+
try {
|
|
58
|
+
cfg = TOML.parse(raw);
|
|
59
|
+
} catch (err) {
|
|
60
|
+
// 解析失败别盲目覆盖 —— 抛出去让 caller backup + log + 决定怎么办。
|
|
61
|
+
const e = new Error(`codex config.toml parse failed: ${err.message}`);
|
|
62
|
+
e.kind = "parse_failed";
|
|
63
|
+
throw e;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (!cfg.mcp_servers || typeof cfg.mcp_servers !== "object") {
|
|
67
|
+
cfg.mcp_servers = {};
|
|
68
|
+
}
|
|
69
|
+
const current = cfg.mcp_servers[SERVER_NAME];
|
|
70
|
+
if (tomlEqual(current, desired)) {
|
|
71
|
+
return { changed: false, configPath: cfgPath, mode: "noop_idempotent" };
|
|
72
|
+
}
|
|
73
|
+
cfg.mcp_servers[SERVER_NAME] = desired;
|
|
74
|
+
|
|
75
|
+
// 确保 ~/.codex/ 存在(用户可能从来没装过 codex)
|
|
76
|
+
await fs.mkdir(path.dirname(cfgPath), { recursive: true, mode: 0o700 });
|
|
77
|
+
await fs.writeFile(cfgPath, TOML.stringify(cfg), { mode: 0o600 });
|
|
78
|
+
log?.info?.("mcp.fanout.codex.write", "wrote agentlink mcp entry", {
|
|
79
|
+
configPath: cfgPath,
|
|
80
|
+
});
|
|
81
|
+
return { changed: true, configPath: cfgPath };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export async function deregister({ log } = {}) {
|
|
85
|
+
const cfgPath = configPath();
|
|
86
|
+
let raw;
|
|
87
|
+
try {
|
|
88
|
+
raw = await fs.readFile(cfgPath, "utf8");
|
|
89
|
+
} catch (err) {
|
|
90
|
+
if (err.code === "ENOENT") {
|
|
91
|
+
return { changed: false, configPath: cfgPath, mode: "noop_no_config" };
|
|
92
|
+
}
|
|
93
|
+
throw err;
|
|
94
|
+
}
|
|
95
|
+
let cfg;
|
|
96
|
+
try {
|
|
97
|
+
cfg = TOML.parse(raw);
|
|
98
|
+
} catch {
|
|
99
|
+
return { changed: false, configPath: cfgPath, mode: "noop_parse_failed" };
|
|
100
|
+
}
|
|
101
|
+
if (!cfg?.mcp_servers || !(SERVER_NAME in cfg.mcp_servers)) {
|
|
102
|
+
return { changed: false, configPath: cfgPath, mode: "noop_not_registered" };
|
|
103
|
+
}
|
|
104
|
+
delete cfg.mcp_servers[SERVER_NAME];
|
|
105
|
+
await fs.writeFile(cfgPath, TOML.stringify(cfg), { mode: 0o600 });
|
|
106
|
+
log?.info?.("mcp.fanout.codex.delete", "removed agentlink mcp entry", {
|
|
107
|
+
configPath: cfgPath,
|
|
108
|
+
});
|
|
109
|
+
return { changed: true, configPath: cfgPath };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export async function status() {
|
|
113
|
+
const cfgPath = configPath();
|
|
114
|
+
let raw;
|
|
115
|
+
try {
|
|
116
|
+
raw = await fs.readFile(cfgPath, "utf8");
|
|
117
|
+
} catch (err) {
|
|
118
|
+
if (err.code === "ENOENT") {
|
|
119
|
+
return { installed: true, registered: false, configPath: cfgPath, reason: "no_config_file" };
|
|
120
|
+
}
|
|
121
|
+
return { installed: false, registered: false, error: err.message };
|
|
122
|
+
}
|
|
123
|
+
let cfg;
|
|
124
|
+
try { cfg = TOML.parse(raw); }
|
|
125
|
+
catch { return { installed: true, registered: false, configPath: cfgPath, reason: "parse_failed" }; }
|
|
126
|
+
const entry = cfg?.mcp_servers?.[SERVER_NAME];
|
|
127
|
+
if (!entry) {
|
|
128
|
+
return { installed: true, registered: false, configPath: cfgPath };
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
installed: true,
|
|
132
|
+
registered: true,
|
|
133
|
+
configPath: cfgPath,
|
|
134
|
+
version: extractAgentlinkVersion(entry.args),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function tomlEqual(a, b) {
|
|
139
|
+
if (a === b) return true;
|
|
140
|
+
if (!a || !b) return false;
|
|
141
|
+
if (a.command !== b.command) return false;
|
|
142
|
+
if (!Array.isArray(a.args) || !Array.isArray(b.args)) return false;
|
|
143
|
+
if (a.args.length !== b.args.length) return false;
|
|
144
|
+
for (let i = 0; i < a.args.length; i++) {
|
|
145
|
+
if (a.args[i] !== b.args[i]) return false;
|
|
146
|
+
}
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function extractAgentlinkVersion(args) {
|
|
151
|
+
if (!Array.isArray(args)) return null;
|
|
152
|
+
const idx = args.indexOf("--agentlink-version");
|
|
153
|
+
if (idx >= 0 && idx + 1 < args.length) return args[idx + 1];
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
// Codex 模型列表扫描器。
|
|
2
|
+
//
|
|
3
|
+
// 背景:codex CLI 二进制(native, Rust)里嵌了一份结构化 JSON 清单,每项形如:
|
|
4
|
+
// {
|
|
5
|
+
// "slug": "gpt-5.4",
|
|
6
|
+
// "description": "Latest frontier agentic coding model.",
|
|
7
|
+
// ...
|
|
8
|
+
// }
|
|
9
|
+
// 这就是交互式 TUI `/model` picker 的数据源。
|
|
10
|
+
//
|
|
11
|
+
// 策略:
|
|
12
|
+
// 1. 扫描 codex native binary,提取所有 slug + description 配对
|
|
13
|
+
// 2. 读 ~/.codex/config.toml 的顶层 `model` 字段,标记"当前默认"
|
|
14
|
+
// 3. 完全动态 —— 跟着 codex CLI 升级自动带来新清单
|
|
15
|
+
//
|
|
16
|
+
// 扫不到(codex 未装 / 二进制定位失败 / 读取错误)→ 返回空列表。
|
|
17
|
+
//
|
|
18
|
+
// 跨平台:
|
|
19
|
+
// - 二进制路径按 platform+arch 计算 npm 包 target-triple,定位 vendor 下的 native binary
|
|
20
|
+
// - 若找不到 vendor 结构(自定义 build 等),回退到 which 能找到的任何二进制
|
|
21
|
+
// - strings 扫描跨 PE/ELF/Mach-O(ASCII 字符串明文)
|
|
22
|
+
|
|
23
|
+
import { promises as fs, existsSync } from "node:fs";
|
|
24
|
+
import os from "node:os";
|
|
25
|
+
import path from "node:path";
|
|
26
|
+
import process from "node:process";
|
|
27
|
+
import { spawnSync } from "node:child_process";
|
|
28
|
+
|
|
29
|
+
const cache = new Map(); // key -> { at, result }
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 返回 { models: [ { id, label, hint }, ... ], default: "<slug>" | null }。
|
|
33
|
+
* default 取自 `~/.codex/config.toml` 的顶层 model 字段。
|
|
34
|
+
*/
|
|
35
|
+
export async function listCodexModels() {
|
|
36
|
+
const bin = await resolveCodexBinary();
|
|
37
|
+
const cfgPath = path.join(os.homedir(), ".codex", "config.toml");
|
|
38
|
+
const binStat = bin ? await safeStat(bin) : null;
|
|
39
|
+
const cfgStat = await safeStat(cfgPath);
|
|
40
|
+
const cacheKey = `${bin || ""}:${binStat?.mtimeMs ?? 0}:${cfgStat?.mtimeMs ?? 0}`;
|
|
41
|
+
const hit = cache.get(cacheKey);
|
|
42
|
+
if (hit) return hit.result;
|
|
43
|
+
|
|
44
|
+
// 1. 扫二进制拿全量 slug + description
|
|
45
|
+
let scanned = [];
|
|
46
|
+
if (bin) {
|
|
47
|
+
try {
|
|
48
|
+
scanned = await scanCodexBinaryForModels(bin);
|
|
49
|
+
} catch {
|
|
50
|
+
scanned = [];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 2. 读 config.toml 的当前默认 model(可选,仅用于标记)
|
|
55
|
+
let defaultModel = null;
|
|
56
|
+
if (cfgStat) {
|
|
57
|
+
const raw = await readTextSafe(cfgPath);
|
|
58
|
+
if (raw) defaultModel = parseDefaultModel(raw);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const models = composeModels(scanned, defaultModel);
|
|
62
|
+
const result = { models, default: defaultModel };
|
|
63
|
+
cache.set(cacheKey, { at: Date.now(), result });
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ── Compose ───────────────────────────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
function composeModels(scanned, defaultModel) {
|
|
70
|
+
// 保留扫描顺序(codex 二进制内 JSON 顺序 = picker 展示顺序)。若 defaultModel
|
|
71
|
+
// 匹配某条,提到最前。每条返回 raw description + isDefault 标志,
|
|
72
|
+
// 由 App 端根据 locale 决定最终展示文案(国际化在 UI 层做)。
|
|
73
|
+
const out = [];
|
|
74
|
+
let defaultItem = null;
|
|
75
|
+
for (const m of scanned) {
|
|
76
|
+
const base = {
|
|
77
|
+
id: m.slug,
|
|
78
|
+
label: prettyLabel(m.slug),
|
|
79
|
+
hint: m.description || "",
|
|
80
|
+
};
|
|
81
|
+
if (defaultModel && m.slug === defaultModel) {
|
|
82
|
+
defaultItem = { ...base, isDefault: true };
|
|
83
|
+
} else {
|
|
84
|
+
out.push(base);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (defaultItem) return [defaultItem, ...out];
|
|
88
|
+
|
|
89
|
+
// defaultModel 不在 scanned 里(用户手配了 codex 不认识的 id)—— 仍展示
|
|
90
|
+
if (defaultModel && !scanned.some((m) => m.slug === defaultModel)) {
|
|
91
|
+
return [
|
|
92
|
+
{ id: defaultModel, label: prettyLabel(defaultModel), hint: "", isDefault: true },
|
|
93
|
+
...out,
|
|
94
|
+
];
|
|
95
|
+
}
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function prettyLabel(id) {
|
|
100
|
+
// gpt-5.4 → "GPT-5.4";gpt-5.2-codex → "GPT-5.2 Codex";o3 → "o3"
|
|
101
|
+
if (/^o\d/i.test(id)) return id;
|
|
102
|
+
if (id.startsWith("gpt-")) {
|
|
103
|
+
const rest = id.slice(4);
|
|
104
|
+
return "GPT-" + rest
|
|
105
|
+
.split("-")
|
|
106
|
+
.map((seg, i) => i === 0 ? seg : (seg ? seg[0].toUpperCase() + seg.slice(1) : seg))
|
|
107
|
+
.join(" ");
|
|
108
|
+
}
|
|
109
|
+
return id;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ── Binary scanning ───────────────────────────────────────────────────────
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* 扫 codex 二进制,提取所有 `"slug": "...", "description": "..."` 配对。
|
|
116
|
+
* JSON blob 跨行,slug 和 description 之间可能夹着别的字段 —— 我们只靠
|
|
117
|
+
* "紧邻出现"假设(实测 codex 内这两者在同一 object 内相邻)。
|
|
118
|
+
*/
|
|
119
|
+
async function scanCodexBinaryForModels(binPath) {
|
|
120
|
+
const CHUNK = 1 << 20;
|
|
121
|
+
// codex 二进制里每个 model 的 JSON object 大约 800-1200 字节(含 reasoning
|
|
122
|
+
// levels / visibility / shell_type 等字段),OVERLAP 要覆盖一整个 object
|
|
123
|
+
const OVERLAP = 4096;
|
|
124
|
+
const fd = await fs.open(binPath, "r");
|
|
125
|
+
try {
|
|
126
|
+
const buf = Buffer.alloc(CHUNK);
|
|
127
|
+
// 匹配同一 model object 内的 slug + description + visibility 三元组。
|
|
128
|
+
// - id 限定在 [A-Za-z0-9._-],防止匹配到非模型字符串
|
|
129
|
+
// - 中间段用 `(?:(?!"slug":)[\s\S]){0,N}?` 非贪婪 + 禁止跨对象(另一个 slug)
|
|
130
|
+
// - visibility 是 codex 的 picker 过滤依据:list / hide
|
|
131
|
+
const RE = /"slug":\s*"([A-Za-z0-9._-]{1,64})"(?:(?!"slug":)[\s\S]){0,2000}?"description":\s*"([^"\\]{0,500})"(?:(?!"slug":)[\s\S]){0,2000}?"visibility":\s*"([a-z]+)"/g;
|
|
132
|
+
const seen = new Map(); // slug -> { description, visibility }
|
|
133
|
+
let position = 0;
|
|
134
|
+
let carry = "";
|
|
135
|
+
while (true) {
|
|
136
|
+
const { bytesRead } = await fd.read(buf, 0, CHUNK, position);
|
|
137
|
+
if (bytesRead === 0) break;
|
|
138
|
+
const hay = carry + buf.slice(0, bytesRead).toString("latin1");
|
|
139
|
+
RE.lastIndex = 0;
|
|
140
|
+
for (const m of hay.matchAll(RE)) {
|
|
141
|
+
const slug = m[1];
|
|
142
|
+
if (seen.has(slug)) continue;
|
|
143
|
+
seen.set(slug, { description: m[2], visibility: m[3] });
|
|
144
|
+
}
|
|
145
|
+
carry = hay.slice(-OVERLAP);
|
|
146
|
+
position += bytesRead;
|
|
147
|
+
}
|
|
148
|
+
// 只保留 picker 展示的(visibility=list),对齐 codex TUI 的 /model 菜单
|
|
149
|
+
const out = [];
|
|
150
|
+
for (const [slug, meta] of seen) {
|
|
151
|
+
if (!looksLikeModelId(slug)) continue;
|
|
152
|
+
if (meta.visibility !== "list") continue;
|
|
153
|
+
out.push({ slug, description: meta.description });
|
|
154
|
+
}
|
|
155
|
+
return out;
|
|
156
|
+
} finally {
|
|
157
|
+
await fd.close();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function looksLikeModelId(slug) {
|
|
162
|
+
if (slug.length < 2) return false;
|
|
163
|
+
// 典型 id: gpt-5.4 / gpt-5-codex / o3 / o4-mini / claude-* 等 —— 都含小写和数字
|
|
164
|
+
if (!/[a-z]/.test(slug)) return false;
|
|
165
|
+
if (!/\d/.test(slug)) return false;
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ── Default-model extraction from config.toml ─────────────────────────────
|
|
170
|
+
|
|
171
|
+
function parseDefaultModel(text) {
|
|
172
|
+
// 只抓顶层 `model = "..."`(第一个出现且在 section header 之前的)
|
|
173
|
+
const lines = text.split(/\r?\n/);
|
|
174
|
+
for (const raw of lines) {
|
|
175
|
+
const line = raw.replace(/^\s+|\s+$/g, "");
|
|
176
|
+
if (!line || line.startsWith("#")) continue;
|
|
177
|
+
if (line.startsWith("[")) return null; // 遇到 section header 就停
|
|
178
|
+
const m = line.match(/^model\s*=\s*["']([^"']+)["']/);
|
|
179
|
+
if (m) return m[1];
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ── Binary resolution ─────────────────────────────────────────────────────
|
|
185
|
+
//
|
|
186
|
+
// 布局(@openai/codex npm 包):
|
|
187
|
+
// <codex.js 所在 bin>/codex.js (node script entry)
|
|
188
|
+
// 上溯至 @openai/codex/package.json
|
|
189
|
+
// → <pkg root>/node_modules/@openai/codex-<triple>/vendor/<triple>/codex/<codex|codex.exe>
|
|
190
|
+
//
|
|
191
|
+
// 若 codex 是自定义 build / 通过其他方式安装,这里走不通,就回退到 which 到的文件。
|
|
192
|
+
|
|
193
|
+
async function resolveCodexBinary() {
|
|
194
|
+
const scriptPath = findCodexInPath();
|
|
195
|
+
if (!scriptPath) return null;
|
|
196
|
+
const real = await safeRealpath(scriptPath);
|
|
197
|
+
if (!real) return null;
|
|
198
|
+
|
|
199
|
+
// 若 realpath 本身就是 native binary(big,非 .js)—— 直接用
|
|
200
|
+
const st = await safeStat(real);
|
|
201
|
+
if (st && st.isFile() && st.size > 1024 * 1024 && !real.endsWith(".js")) {
|
|
202
|
+
return real;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// 否则尝试从 @openai/codex 包布局里找 vendor native
|
|
206
|
+
const vendor = await locateVendorNative(real);
|
|
207
|
+
if (vendor) return vendor;
|
|
208
|
+
|
|
209
|
+
// 兜底:返回脚本路径(扫描会失败,但不报错)
|
|
210
|
+
return real;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function findCodexInPath() {
|
|
214
|
+
const pathDirs = (process.env.PATH || "").split(process.platform === "win32" ? ";" : ":");
|
|
215
|
+
const candidates = process.platform === "win32"
|
|
216
|
+
? ["codex.exe", "codex.cmd", "codex.bat", "codex"]
|
|
217
|
+
: ["codex"];
|
|
218
|
+
for (const dir of pathDirs) {
|
|
219
|
+
if (!dir) continue;
|
|
220
|
+
for (const name of candidates) {
|
|
221
|
+
const p = path.join(dir, name);
|
|
222
|
+
try {
|
|
223
|
+
if (fsExistsSync(p)) return p;
|
|
224
|
+
} catch {}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// 兜底:which / where
|
|
228
|
+
const which = process.platform === "win32" ? "where" : "which";
|
|
229
|
+
const r = spawnSync(which, ["codex"], { encoding: "utf8" });
|
|
230
|
+
if (r.status === 0) {
|
|
231
|
+
const first = (r.stdout || "").split(/\r?\n/).map((s) => s.trim()).find(Boolean);
|
|
232
|
+
if (first && fsExistsSync(first)) return first;
|
|
233
|
+
}
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
async function locateVendorNative(codexJsRealPath) {
|
|
238
|
+
// 上溯找到 @openai/codex 包根(含 package.json)
|
|
239
|
+
let dir = path.dirname(codexJsRealPath);
|
|
240
|
+
let pkgRoot = null;
|
|
241
|
+
for (let i = 0; i < 6; i++) {
|
|
242
|
+
if (fsExistsSync(path.join(dir, "package.json"))) {
|
|
243
|
+
try {
|
|
244
|
+
const pkg = JSON.parse(await fs.readFile(path.join(dir, "package.json"), "utf8"));
|
|
245
|
+
if (pkg.name === "@openai/codex") { pkgRoot = dir; break; }
|
|
246
|
+
} catch {}
|
|
247
|
+
}
|
|
248
|
+
const parent = path.dirname(dir);
|
|
249
|
+
if (parent === dir) break;
|
|
250
|
+
dir = parent;
|
|
251
|
+
}
|
|
252
|
+
if (!pkgRoot) return null;
|
|
253
|
+
|
|
254
|
+
const plat = resolvePlatformMapping();
|
|
255
|
+
if (!plat) return null;
|
|
256
|
+
const binName = process.platform === "win32" ? "codex.exe" : "codex";
|
|
257
|
+
// npm package 名用 node-platform 后缀(如 codex-darwin-arm64),
|
|
258
|
+
// 但 vendor 子目录用 target-triple(如 aarch64-apple-darwin)—— 不一样
|
|
259
|
+
const vendorBin = path.join(
|
|
260
|
+
pkgRoot, "node_modules", "@openai", `codex-${plat.pkgSuffix}`,
|
|
261
|
+
"vendor", plat.triple, "codex", binName,
|
|
262
|
+
);
|
|
263
|
+
if (!fsExistsSync(vendorBin)) return null;
|
|
264
|
+
const st = await safeStat(vendorBin);
|
|
265
|
+
if (!st || !st.isFile() || st.size < 1024 * 1024) return null;
|
|
266
|
+
return vendorBin;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* 返回 { triple, pkgSuffix }。对齐 @openai/codex/bin/codex.js 里的
|
|
271
|
+
* PLATFORM_PACKAGE_BY_TARGET:key 是 target-triple(vendor 目录名),
|
|
272
|
+
* value 是 @openai/codex-<suffix> 的 npm 包名后缀。
|
|
273
|
+
*/
|
|
274
|
+
function resolvePlatformMapping() {
|
|
275
|
+
const { platform, arch } = process;
|
|
276
|
+
if (platform === "linux" || platform === "android") {
|
|
277
|
+
if (arch === "x64") return { triple: "x86_64-unknown-linux-musl", pkgSuffix: "linux-x64" };
|
|
278
|
+
if (arch === "arm64") return { triple: "aarch64-unknown-linux-musl", pkgSuffix: "linux-arm64" };
|
|
279
|
+
} else if (platform === "darwin") {
|
|
280
|
+
if (arch === "x64") return { triple: "x86_64-apple-darwin", pkgSuffix: "darwin-x64" };
|
|
281
|
+
if (arch === "arm64") return { triple: "aarch64-apple-darwin", pkgSuffix: "darwin-arm64" };
|
|
282
|
+
} else if (platform === "win32") {
|
|
283
|
+
if (arch === "x64") return { triple: "x86_64-pc-windows-msvc", pkgSuffix: "win32-x64" };
|
|
284
|
+
if (arch === "arm64") return { triple: "aarch64-pc-windows-msvc", pkgSuffix: "win32-arm64" };
|
|
285
|
+
}
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// ── fs helpers ─────────────────────────────────────────────────────────────
|
|
290
|
+
|
|
291
|
+
function fsExistsSync(p) {
|
|
292
|
+
try { return existsSync(p); } catch { return false; }
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
async function safeStat(p) {
|
|
296
|
+
try { return await fs.stat(p); } catch { return null; }
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
async function safeRealpath(p) {
|
|
300
|
+
try { return await fs.realpath(p); } catch { return null; }
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async function readTextSafe(p) {
|
|
304
|
+
try { return await fs.readFile(p, "utf8"); } catch { return null; }
|
|
305
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* planNotificationHandler.mjs — Codex turn/plan/updated → todo_list block translator
|
|
3
|
+
*
|
|
4
|
+
* 按 translator-spec.md D2 决策实现:
|
|
5
|
+
* - 首次 handlePlanUpdated:发 content_block.started
|
|
6
|
+
* - 后续调用:发 content_block.delta(复用 block.id)
|
|
7
|
+
* - complete():发 content_block.completed
|
|
8
|
+
* - 未收过通知时 complete() 是 no-op
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
buildBlockStarted,
|
|
13
|
+
buildBlockCompleted,
|
|
14
|
+
buildBlockDelta,
|
|
15
|
+
newBlockId,
|
|
16
|
+
} from "../../../src-shared/envelope_builder.mjs";
|
|
17
|
+
import { buildTodoItems } from "../_shared/todoTranslatorUtils.mjs";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 创建 Codex plan notification handler。
|
|
21
|
+
*
|
|
22
|
+
* @param {{ runId: string, onEnvelopes: (envs: object[]) => void }} opts
|
|
23
|
+
* @returns {{ handlePlanUpdated(payload: object): void, complete(): void }}
|
|
24
|
+
*/
|
|
25
|
+
export function createCodexPlanHandler({ runId, onEnvelopes }) {
|
|
26
|
+
let blockId = null;
|
|
27
|
+
let lastItems = null;
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
/**
|
|
31
|
+
* 处理 turn/plan/updated 通知 payload。
|
|
32
|
+
* 第一次:发 started;后续:发 delta。
|
|
33
|
+
*
|
|
34
|
+
* @param {{ turnId: string|null, explanation?: string|null, plan: Array<{step: string, status: string}> }} payload
|
|
35
|
+
*/
|
|
36
|
+
handlePlanUpdated(payload) {
|
|
37
|
+
const rawPlan = Array.isArray(payload?.plan) ? payload.plan : [];
|
|
38
|
+
const items = buildTodoItems(rawPlan);
|
|
39
|
+
lastItems = items;
|
|
40
|
+
|
|
41
|
+
if (blockId === null) {
|
|
42
|
+
// 首次调用:生成 block.id,发 started
|
|
43
|
+
blockId = newBlockId();
|
|
44
|
+
const block = { id: blockId, type: "todo_list", items };
|
|
45
|
+
onEnvelopes([buildBlockStarted(runId, block)]);
|
|
46
|
+
} else {
|
|
47
|
+
// 后续调用:发 delta(复用 block.id)
|
|
48
|
+
onEnvelopes([buildBlockDelta(runId, blockId, { todo_items_replace: items })]);
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 发出 content_block.completed(若从未收到过 handlePlanUpdated 则 no-op)。
|
|
54
|
+
*/
|
|
55
|
+
complete() {
|
|
56
|
+
if (blockId === null) return; // no-op
|
|
57
|
+
const block = { id: blockId, type: "todo_list", items: lastItems ?? [] };
|
|
58
|
+
onEnvelopes([buildBlockCompleted(runId, block)]);
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|