@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,576 @@
|
|
|
1
|
+
// Claude Code `/usage` TUI 抓取探针。
|
|
2
|
+
//
|
|
3
|
+
// 背景:
|
|
4
|
+
// Claude Code CLI 的 5h/weekly 额度只能通过交互式 TUI 的 `/usage` 查看,
|
|
5
|
+
// -p / stream-json 模式下不可用。要拿到额度数据,只能起 PTY 真实运行
|
|
6
|
+
// claude,输入 `/usage`、从渲染出的 ANSI 字符流里 scrape。
|
|
7
|
+
//
|
|
8
|
+
// 设计:
|
|
9
|
+
// - 必选依赖 node-pty —— 装不上就 fail loudly(package.json 移出 optional)。
|
|
10
|
+
// - 独立 short-lived claude 进程(不复用主会话),干净退出。
|
|
11
|
+
// - 抓取结果归一化到 usageReporter 的 rate_limits 形状:
|
|
12
|
+
// { primary:{used_percent, window_minutes:300, resets_at}, secondary:{...}, plan_type }
|
|
13
|
+
// - 不依赖具体提示符检测 —— 固定延时后直接发 /usage,再轮询 buffer 里是否
|
|
14
|
+
// 出现"% used" + "Resets in …"双重特征,稳定 500ms 后结束。
|
|
15
|
+
// - 宽容解析:5h/weekly/Current session 三大类标签都尝试命中,percent 接受
|
|
16
|
+
// "30%"、"30.5% used"、"Used: 30%"、"30/100";resets 接受多种常见自然语言。
|
|
17
|
+
|
|
18
|
+
import { stripAnsi } from "./usageProbeStrip.mjs";
|
|
19
|
+
import { execFileSync, spawn as cpSpawn } from "node:child_process";
|
|
20
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
21
|
+
import os from "node:os";
|
|
22
|
+
import path from "node:path";
|
|
23
|
+
|
|
24
|
+
const PROBE_HARD_TIMEOUT_MS = 15_000;
|
|
25
|
+
const STABLE_DWELL_MS = 1500; // 识别到双特征后额外等待多久再 parse
|
|
26
|
+
// —— v2.1 TUI 第一遍渲染可能含坏字符(Resets→Reses),
|
|
27
|
+
// 第二遍才完整;等久一点确保两遍都进 buffer
|
|
28
|
+
const POLL_INTERVAL_MS = 200;
|
|
29
|
+
|
|
30
|
+
let nodePtyDisabledReason = null;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 运行一次 `/usage` 探针。
|
|
34
|
+
*
|
|
35
|
+
* @param {Object} opts
|
|
36
|
+
* @param {string} opts.cwd
|
|
37
|
+
* @param {import("../../../src-shared/logger.mjs").Logger} opts.log
|
|
38
|
+
* @param {AbortSignal} [opts.signal]
|
|
39
|
+
* @returns {Promise<null | {
|
|
40
|
+
* primary: null|{used_percent:number, window_minutes:number, resets_at:number},
|
|
41
|
+
* secondary: null|{used_percent:number, window_minutes:number, resets_at:number},
|
|
42
|
+
* plan_type: string|null,
|
|
43
|
+
* }>}
|
|
44
|
+
*/
|
|
45
|
+
export async function probeClaudeUsage({ cwd, log, signal }) {
|
|
46
|
+
const pLog = log.child({ comp: "usage-probe", runtime: "claude" });
|
|
47
|
+
|
|
48
|
+
const claudeBin = resolveClaudeBin();
|
|
49
|
+
const env = buildProbeEnv();
|
|
50
|
+
// Probe 子进程的 cwd:**必须**用 claude 已信任过的目录,否则被"Trust this folder?"
|
|
51
|
+
// 安全弹窗拦住,根本进不了 TUI。
|
|
52
|
+
// 优先级:传入 cwd 已信任 > ~/.claude.json 里任意已信任项 > HOME(兜底)
|
|
53
|
+
// 三端通用:claude-code 在 macOS/Linux/Windows 都用同一个 ~/.claude.json。
|
|
54
|
+
let probeCwd = pickExistingTrustedCwd(cwd) || pickAnyTrustedCwd() || os.homedir();
|
|
55
|
+
pLog.info("probe.cwd", "resolved probe cwd", { probeCwd, passedCwd: cwd });
|
|
56
|
+
|
|
57
|
+
// macOS service 环境里 node-pty 的失败路径可能泄漏 PTY FD;优先用系统
|
|
58
|
+
// script(1),保留 node-pty 作为 script 不可用时的跨平台后备。
|
|
59
|
+
const preferScript = process.platform === "darwin";
|
|
60
|
+
let term = null;
|
|
61
|
+
let via = null;
|
|
62
|
+
if (preferScript) {
|
|
63
|
+
term = createScriptPtyTerm({ claudeBin, cwd: probeCwd, env, pLog });
|
|
64
|
+
if (term) via = "script(1)";
|
|
65
|
+
}
|
|
66
|
+
if (!term) {
|
|
67
|
+
term = await createNodePtyTerm({ claudeBin, cwd: probeCwd, env, pLog });
|
|
68
|
+
if (term) via = "node-pty";
|
|
69
|
+
}
|
|
70
|
+
if (!term && !preferScript) {
|
|
71
|
+
term = createScriptPtyTerm({ claudeBin, cwd: probeCwd, env, pLog });
|
|
72
|
+
if (term) via = "script(1)";
|
|
73
|
+
}
|
|
74
|
+
if (!term) {
|
|
75
|
+
pLog.error("probe.pty.unavailable", "no PTY mechanism available", { claudeBin });
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
pLog.info("probe.pty.via", "probe PTY backend", { via });
|
|
79
|
+
|
|
80
|
+
return new Promise((resolve) => {
|
|
81
|
+
let buffer = "";
|
|
82
|
+
let settled = false;
|
|
83
|
+
let sentUsage = false;
|
|
84
|
+
let stableTimer = null;
|
|
85
|
+
let hardTimer = null;
|
|
86
|
+
let pollTimer = null;
|
|
87
|
+
let bootstrapTimer = null;
|
|
88
|
+
const cleanups = [];
|
|
89
|
+
|
|
90
|
+
const done = (result, reason) => {
|
|
91
|
+
if (settled) return;
|
|
92
|
+
settled = true;
|
|
93
|
+
if (stableTimer) clearTimeout(stableTimer);
|
|
94
|
+
if (hardTimer) clearTimeout(hardTimer);
|
|
95
|
+
if (pollTimer) clearInterval(pollTimer);
|
|
96
|
+
if (bootstrapTimer) clearInterval(bootstrapTimer);
|
|
97
|
+
for (const cleanup of cleanups.splice(0)) {
|
|
98
|
+
try { cleanup(); } catch {}
|
|
99
|
+
}
|
|
100
|
+
try { term.write("\x03"); } catch {} // Ctrl-C 让 TUI 主动退出
|
|
101
|
+
setTimeout(() => { try { term.kill(); } catch {} }, 100);
|
|
102
|
+
const got = result || parseUsageOutput(stripAnsi(buffer));
|
|
103
|
+
pLog.info("probe.finished", "probe finished", {
|
|
104
|
+
reason,
|
|
105
|
+
via,
|
|
106
|
+
buffer_len: buffer.length,
|
|
107
|
+
got_primary: !!got?.primary,
|
|
108
|
+
got_secondary: !!got?.secondary,
|
|
109
|
+
got_plan: !!got?.plan_type,
|
|
110
|
+
});
|
|
111
|
+
if (!got || (!got.primary && !got.secondary && !got.plan_type)) {
|
|
112
|
+
// 抬到 info 级别,便于在 service 日志里看到(debug 默认不输出)
|
|
113
|
+
pLog.info("probe.parse.empty", "parse found nothing — buffer tail for diagnosis", {
|
|
114
|
+
buffer_len: buffer.length,
|
|
115
|
+
sample_head: stripAnsi(buffer).slice(0, 800),
|
|
116
|
+
sample_tail: stripAnsi(buffer).slice(-1500),
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
resolve(got);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const onAbort = () => done(null, "aborted");
|
|
123
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
124
|
+
if (signal) cleanups.push(() => signal.removeEventListener("abort", onAbort));
|
|
125
|
+
if (signal?.aborted) {
|
|
126
|
+
done(null, "aborted");
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
let trustDismissed = false;
|
|
131
|
+
const dataSub = term.onData((data) => {
|
|
132
|
+
buffer += data;
|
|
133
|
+
if (buffer.length > 262144) buffer = buffer.slice(-131072);
|
|
134
|
+
});
|
|
135
|
+
if (dataSub && typeof dataSub.dispose === "function") {
|
|
136
|
+
cleanups.push(() => dataSub.dispose());
|
|
137
|
+
}
|
|
138
|
+
const exitSub = term.onExit(({ exitCode }) => done(null, `child-exit-${exitCode}`));
|
|
139
|
+
if (exitSub && typeof exitSub.dispose === "function") {
|
|
140
|
+
cleanups.push(() => exitSub.dispose());
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const sendUsage = () => {
|
|
144
|
+
if (settled || sentUsage) return;
|
|
145
|
+
try {
|
|
146
|
+
term.write("/usage\r");
|
|
147
|
+
sentUsage = true;
|
|
148
|
+
pLog.info("probe.usage.sent", "/usage written");
|
|
149
|
+
} catch (err) {
|
|
150
|
+
pLog.warn("probe.usage.write.failed", "write /usage failed", {
|
|
151
|
+
err: err?.message,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// 启动流程:
|
|
157
|
+
// t=0 spawn claude
|
|
158
|
+
// 每 200ms 轮询 stripped buffer:
|
|
159
|
+
// - 见到 trust prompt → 发 Enter(选默认 #1)→ 继续等 2s → sendUsage
|
|
160
|
+
// - 无 prompt 但 3s 已过 → 认定已信任 → sendUsage
|
|
161
|
+
// 之后进入 hasUsageMarkers 正常轮询 → 稳定后 parse
|
|
162
|
+
const spawnAt = Date.now();
|
|
163
|
+
const TRUST_WAIT_MAX_MS = 3000; // 3s 内没 prompt 就假设不需要信任
|
|
164
|
+
const TRUST_POST_DELAY_MS = 2000; // 信任接受后再等主 TUI 渲染
|
|
165
|
+
bootstrapTimer = setInterval(() => {
|
|
166
|
+
if (settled || sentUsage) { clearInterval(bootstrapTimer); return; }
|
|
167
|
+
const stripped = stripAnsi(buffer);
|
|
168
|
+
// 三端通用的 trust prompt 关键短语 —— script(1) 剥空格后都用 \s* 宽松
|
|
169
|
+
const sawTrust = /trust\s*this\s*folder|i\s*trust\s*this\s*folder|yes,\s*i\s*trust/i.test(stripped);
|
|
170
|
+
if (sawTrust && !trustDismissed) {
|
|
171
|
+
trustDismissed = true;
|
|
172
|
+
try { term.write("\r"); } catch {} // Enter 选择默认 #1
|
|
173
|
+
pLog.info("probe.trust.dismissed", "trust prompt auto-accepted (Enter)");
|
|
174
|
+
setTimeout(sendUsage, TRUST_POST_DELAY_MS);
|
|
175
|
+
clearInterval(bootstrapTimer);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
// 超过阈值仍无 trust prompt → 认定不需要信任,直接发 /usage
|
|
179
|
+
if (Date.now() - spawnAt >= TRUST_WAIT_MAX_MS) {
|
|
180
|
+
pLog.info("probe.trust.skipped", "no trust prompt detected within window");
|
|
181
|
+
sendUsage();
|
|
182
|
+
clearInterval(bootstrapTimer);
|
|
183
|
+
}
|
|
184
|
+
}, 200);
|
|
185
|
+
|
|
186
|
+
// 轮询:/usage 发出后,只要 buffer 出现"% used"+"Resets"双特征就稳定 500ms 后收网
|
|
187
|
+
pollTimer = setInterval(() => {
|
|
188
|
+
if (!sentUsage || settled) return;
|
|
189
|
+
const stripped = stripAnsi(buffer);
|
|
190
|
+
if (hasUsageMarkers(stripped) && !stableTimer) {
|
|
191
|
+
stableTimer = setTimeout(() => {
|
|
192
|
+
done(parseUsageOutput(stripAnsi(buffer)), "markers-stable");
|
|
193
|
+
}, STABLE_DWELL_MS);
|
|
194
|
+
}
|
|
195
|
+
}, POLL_INTERVAL_MS);
|
|
196
|
+
|
|
197
|
+
// 硬兜底:15s 无论如何解析并返回
|
|
198
|
+
hardTimer = setTimeout(() => {
|
|
199
|
+
done(parseUsageOutput(stripAnsi(buffer)), "hard-timeout");
|
|
200
|
+
}, PROBE_HARD_TIMEOUT_MS);
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ── PTY 抽象层 ────────────────────────────────────────────────────────────
|
|
205
|
+
//
|
|
206
|
+
// 两套实现,对外同接口:write / onData / onExit / kill。
|
|
207
|
+
// 1. node-pty(首选,原生 PTY,fork+posix_spawnp)
|
|
208
|
+
// 2. script(1) fallback(macOS/Linux 自带,绕过 node-pty 的 posix_spawnp
|
|
209
|
+
// 受限场景;用 `script -q /dev/null <cmd>` 创建 PTY,主进程走管道与它通信)
|
|
210
|
+
//
|
|
211
|
+
// 抽象好处:主流程不用关心哪种后端,跑不通一种就换一种。
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* 构造 probe 子进程的 env:
|
|
215
|
+
* - 从 process.env 起步
|
|
216
|
+
* - 剥掉 Claude Desktop / 托管环境注入的强制 provider 变量
|
|
217
|
+
* (否则 probe 被绑定到 host OAuth,不再读用户磁盘上的订阅凭证)
|
|
218
|
+
* - 强制 TERM/FORCE_COLOR,并关掉更新检查
|
|
219
|
+
*/
|
|
220
|
+
function buildProbeEnv() {
|
|
221
|
+
const base = { ...process.env };
|
|
222
|
+
for (const k of [
|
|
223
|
+
"CLAUDE_CODE_OAUTH_TOKEN",
|
|
224
|
+
"CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST",
|
|
225
|
+
"CLAUDE_CODE_ENTRYPOINT",
|
|
226
|
+
"CLAUDE_AGENT_SDK_VERSION",
|
|
227
|
+
"ANTHROPIC_API_KEY",
|
|
228
|
+
"ANTHROPIC_AUTH_TOKEN",
|
|
229
|
+
"ANTHROPIC_BASE_URL",
|
|
230
|
+
"ANTHROPIC_CUSTOM_HEADERS",
|
|
231
|
+
]) {
|
|
232
|
+
delete base[k];
|
|
233
|
+
}
|
|
234
|
+
return {
|
|
235
|
+
...base,
|
|
236
|
+
CLAUDE_CODE_NO_UPDATE_CHECK: "1",
|
|
237
|
+
TERM: "xterm-256color",
|
|
238
|
+
FORCE_COLOR: "1",
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* 读 ~/.claude.json 的 projects,找出用户已接受信任对话框的路径集合。
|
|
244
|
+
* 三端通用:claude-code 在 macOS / Linux / Windows 都把这个配置存在 $HOME 下。
|
|
245
|
+
*/
|
|
246
|
+
function readTrustedProjects() {
|
|
247
|
+
try {
|
|
248
|
+
const file = path.join(os.homedir(), ".claude.json");
|
|
249
|
+
const raw = readFileSync(file, "utf8");
|
|
250
|
+
const obj = JSON.parse(raw);
|
|
251
|
+
const projects = obj?.projects;
|
|
252
|
+
if (!projects || typeof projects !== "object") return [];
|
|
253
|
+
return Object.entries(projects)
|
|
254
|
+
.filter(([, meta]) => meta && meta.hasTrustDialogAccepted === true)
|
|
255
|
+
.map(([p]) => p);
|
|
256
|
+
} catch {
|
|
257
|
+
return [];
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** 传入 cwd 若正好在已信任列表里且目录仍存在 → 返回它;否则 null。 */
|
|
262
|
+
function pickExistingTrustedCwd(cwd) {
|
|
263
|
+
if (!cwd || cwd === "/" || cwd === "\\") return null;
|
|
264
|
+
const trusted = readTrustedProjects();
|
|
265
|
+
if (trusted.includes(cwd) && existsSync(cwd)) return cwd;
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/** 从已信任列表里挑**仍然存在**的首个路径,作为 probe 安全 cwd 兜底。 */
|
|
270
|
+
function pickAnyTrustedCwd() {
|
|
271
|
+
const trusted = readTrustedProjects();
|
|
272
|
+
for (const p of trusted) {
|
|
273
|
+
if (existsSync(p)) return p;
|
|
274
|
+
}
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function resolveClaudeBin() {
|
|
279
|
+
// node-pty posix_spawnp 对 PATH 搜索有时失败,先解析绝对路径。跨平台:
|
|
280
|
+
// - Unix (macOS/Linux): `which claude` 或 POSIX `command -v`
|
|
281
|
+
// - Windows: `where claude`(可能返回多行 .exe/.cmd,取首行)
|
|
282
|
+
const isWin = process.platform === "win32";
|
|
283
|
+
const attempts = isWin
|
|
284
|
+
? [["where", "claude"]]
|
|
285
|
+
: [["which", "claude"], ["/bin/sh", "-c", "command -v claude"]];
|
|
286
|
+
for (const args of attempts) {
|
|
287
|
+
try {
|
|
288
|
+
const out = execFileSync(args[0], args.slice(1), {
|
|
289
|
+
encoding: "utf8", timeout: 2000, windowsHide: true,
|
|
290
|
+
}).trim();
|
|
291
|
+
if (out) return out.split(/\r?\n/)[0];
|
|
292
|
+
} catch {}
|
|
293
|
+
}
|
|
294
|
+
return isWin ? "claude.cmd" : "claude";
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async function createNodePtyTerm({ claudeBin, cwd, env, pLog }) {
|
|
298
|
+
if (nodePtyDisabledReason) {
|
|
299
|
+
pLog.info("probe.pty.skipped", "node-pty disabled for this agent process after earlier failure", {
|
|
300
|
+
reason: nodePtyDisabledReason,
|
|
301
|
+
});
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
let pty;
|
|
305
|
+
try {
|
|
306
|
+
pty = await import("node-pty");
|
|
307
|
+
} catch (err) {
|
|
308
|
+
const reason = `load failed: ${err?.message || String(err)}`;
|
|
309
|
+
nodePtyDisabledReason = reason;
|
|
310
|
+
pLog.warn("probe.pty.load.failed", "node-pty failed to load, disabling node-pty backend for this process", { err: reason });
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
// 跨平台:macOS/Linux 用 xterm 兼容 pty;Windows 走 ConPTY(node-pty 内置)
|
|
314
|
+
const isWin = process.platform === "win32";
|
|
315
|
+
try {
|
|
316
|
+
const term = pty.spawn(claudeBin, [], {
|
|
317
|
+
name: isWin ? "windows-terminal" : "xterm-256color",
|
|
318
|
+
cols: 120, rows: 40, cwd, env,
|
|
319
|
+
// Windows ConPTY 默认即启;node-pty 用 useConpty=true 是默认
|
|
320
|
+
});
|
|
321
|
+
return {
|
|
322
|
+
write: (s) => term.write(s),
|
|
323
|
+
kill: () => { try { term.kill(); } catch {} },
|
|
324
|
+
onData: (cb) => term.onData(cb),
|
|
325
|
+
onExit: (cb) => term.onExit(cb),
|
|
326
|
+
};
|
|
327
|
+
} catch (err) {
|
|
328
|
+
const reason = `spawn failed: ${err?.message || String(err)}`;
|
|
329
|
+
nodePtyDisabledReason = reason;
|
|
330
|
+
pLog.warn("probe.pty.spawn.failed", "node-pty spawn failed, disabling node-pty backend for this process", {
|
|
331
|
+
err: reason, bin: claudeBin,
|
|
332
|
+
});
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Unix(macOS/Linux)fallback:用系统自带的 `script(1)` 包一层 PTY。
|
|
339
|
+
* Windows 没有 `script(1)` —— 直接返回 null,让 node-pty(ConPTY)成为唯一路径。
|
|
340
|
+
* 如果 Windows 上 node-pty 也不可用,probe 就无法工作 —— 这是底层限制,无兜底。
|
|
341
|
+
*/
|
|
342
|
+
function createScriptPtyTerm({ claudeBin, cwd, env, pLog }) {
|
|
343
|
+
if (process.platform === "win32") {
|
|
344
|
+
pLog.warn("probe.script.unsupported", "script(1) fallback unavailable on Windows — relying on node-pty");
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
// script(1) 的路径和参数在 macOS 与 Linux 上不同:
|
|
348
|
+
// macOS: /usr/bin/script -q /dev/null <cmd> [args...]
|
|
349
|
+
// Linux: /usr/bin/script -q -c "<cmd args>" /dev/null
|
|
350
|
+
const scriptPath = "/usr/bin/script";
|
|
351
|
+
if (!existsSync(scriptPath)) {
|
|
352
|
+
pLog.warn("probe.script.missing", "script(1) not found at /usr/bin/script");
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
const isMac = process.platform === "darwin";
|
|
356
|
+
const args = isMac
|
|
357
|
+
? ["-q", "/dev/null", claudeBin]
|
|
358
|
+
: ["-q", "-c", claudeBin, "/dev/null"];
|
|
359
|
+
let child;
|
|
360
|
+
try {
|
|
361
|
+
child = cpSpawn(scriptPath, args, {
|
|
362
|
+
cwd, env, stdio: ["pipe", "pipe", "pipe"],
|
|
363
|
+
});
|
|
364
|
+
} catch (err) {
|
|
365
|
+
pLog.warn("probe.script.spawn.failed", "script(1) spawn failed", { err: err?.message });
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
const dataCbs = [];
|
|
369
|
+
const exitCbs = [];
|
|
370
|
+
child.stdout.on("data", (d) => { const s = d.toString(); dataCbs.forEach((cb) => cb(s)); });
|
|
371
|
+
child.stderr.on("data", (d) => { const s = d.toString(); dataCbs.forEach((cb) => cb(s)); });
|
|
372
|
+
child.on("exit", (code) => { exitCbs.forEach((cb) => cb({ exitCode: code ?? -1 })); });
|
|
373
|
+
return {
|
|
374
|
+
write: (s) => { try { child.stdin.write(s); } catch {} },
|
|
375
|
+
kill: () => {
|
|
376
|
+
try { child.kill("SIGINT"); } catch {}
|
|
377
|
+
setTimeout(() => { try { child.kill("SIGKILL"); } catch {} }, 500);
|
|
378
|
+
},
|
|
379
|
+
onData: (cb) => {
|
|
380
|
+
dataCbs.push(cb);
|
|
381
|
+
return { dispose: () => removeCallback(dataCbs, cb) };
|
|
382
|
+
},
|
|
383
|
+
onExit: (cb) => {
|
|
384
|
+
exitCbs.push(cb);
|
|
385
|
+
return { dispose: () => removeCallback(exitCbs, cb) };
|
|
386
|
+
},
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function removeCallback(callbacks, cb) {
|
|
391
|
+
const idx = callbacks.indexOf(cb);
|
|
392
|
+
if (idx >= 0) callbacks.splice(idx, 1);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function hasUsageMarkers(text) {
|
|
396
|
+
// 至少一对 % + Reset/Reses 关键字 视为 /usage 已渲染。
|
|
397
|
+
// 放宽条件原因:bridge service 环境下 script(1) 流速慢,原来要求 2 次的阈值常常
|
|
398
|
+
// 等不到;就算只有 1 对,parse 也能拿到部分数据(比双空更好)。
|
|
399
|
+
const hasPct = /\d{1,3}(?:\.\d+)?\s*%/.test(text);
|
|
400
|
+
const hasReset = /res[ea]ts?\b/i.test(text); // 吃 "Resets" / "Reses"(第一遍坏字符)
|
|
401
|
+
return hasPct && hasReset;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* 解析 `/usage` 的明文(已 strip ANSI)。
|
|
406
|
+
* 宽容策略:命中哪段算哪段;空返回 null。
|
|
407
|
+
*/
|
|
408
|
+
export function parseUsageOutput(text) {
|
|
409
|
+
if (typeof text !== "string" || !text) return null;
|
|
410
|
+
const now = Math.floor(Date.now() / 1000);
|
|
411
|
+
|
|
412
|
+
// 宽松标签匹配:涵盖多版本 TUI 文案
|
|
413
|
+
// v2.1 常见:
|
|
414
|
+
// "Current session" / "Current 5h session"
|
|
415
|
+
// "Weekly usage" / "Current week"
|
|
416
|
+
// "Opus weekly" / "All models usage"
|
|
417
|
+
// 标签用 \s* —— v2.1 TUI 第一遍渲染带空格、script(1) 抓的第二遍压成无空格,都要能吃
|
|
418
|
+
const primary = extractWindow(
|
|
419
|
+
text,
|
|
420
|
+
/(current\s*session|5[- ]?hour|5h(?:our)?\s*(?:limit|session|window)?|current\s*5h)/gi,
|
|
421
|
+
300, now,
|
|
422
|
+
);
|
|
423
|
+
const secondary = extractWindow(
|
|
424
|
+
text,
|
|
425
|
+
/(current\s*week|weekly(?:\s+limit|\s+usage|\s+session)?|7[- ]?day\s*(?:limit|usage)?|this\s*week|week\s*limit)/gi,
|
|
426
|
+
10080, now,
|
|
427
|
+
);
|
|
428
|
+
|
|
429
|
+
// plan: "Plan: Pro" / "Your plan: Max 5x" / "Subscription: Pro"
|
|
430
|
+
let planType = null;
|
|
431
|
+
const planPatterns = [
|
|
432
|
+
/(?:^|\n|│|\|)\s*(?:your\s+)?plan\s*[::]\s*([A-Za-z0-9_+.\- ]{1,40}?)(?=\s*(?:$|\r|\n|│|\|))/im,
|
|
433
|
+
/subscription\s*[::]\s*([A-Za-z0-9_+.\- ]{1,40}?)(?=\s*(?:$|\r|\n|│|\|))/im,
|
|
434
|
+
];
|
|
435
|
+
for (const re of planPatterns) {
|
|
436
|
+
const m = re.exec(text);
|
|
437
|
+
if (m) {
|
|
438
|
+
planType = m[1].trim().toLowerCase().replace(/\s+/g, "_");
|
|
439
|
+
if (planType) break;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
if (!primary && !secondary && !planType) return null;
|
|
444
|
+
return { primary, secondary, plan_type: planType };
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// 各 window 的 label 统称 —— 用于切片时"下一个 label 出现处"截断,
|
|
448
|
+
// 避免 primary 的 slice 吃到 secondary 的 reset 时间。
|
|
449
|
+
const NEXT_LABEL_RE = /current\s*(?:session|week|5h)|5[- ]?hour|weekly|this\s*week|current\s*5h|week\s*limit/i;
|
|
450
|
+
|
|
451
|
+
function extractWindow(text, labelRe, windowMinutes, nowSec) {
|
|
452
|
+
// labelRe 必须带 /g —— 我们会枚举所有 label 匹配,挑出第一个"能同时拿到 pct + reset"
|
|
453
|
+
// 的切片;如果都拿不到 reset,退回第一个能拿 pct 的。
|
|
454
|
+
// 原因:v2.1 TUI 第一遍渲染可能缺字符("Resets" 被切成 "Reses"),但第二遍完整。
|
|
455
|
+
let fallbackPct = null;
|
|
456
|
+
let m;
|
|
457
|
+
labelRe.lastIndex = 0;
|
|
458
|
+
while ((m = labelRe.exec(text))) {
|
|
459
|
+
const afterLabel = text.slice(m.index + m[0].length);
|
|
460
|
+
const nextLabelMatch = NEXT_LABEL_RE.exec(afterLabel);
|
|
461
|
+
const sliceEnd = m.index + m[0].length + (nextLabelMatch ? nextLabelMatch.index : 400);
|
|
462
|
+
const slice = text.slice(m.index, sliceEnd);
|
|
463
|
+
const pct = matchPercent(slice);
|
|
464
|
+
if (pct == null) continue;
|
|
465
|
+
const resetsAt = matchResetsAt(slice, nowSec);
|
|
466
|
+
if (resetsAt) {
|
|
467
|
+
return { used_percent: pct, window_minutes: windowMinutes, resets_at: resetsAt };
|
|
468
|
+
}
|
|
469
|
+
if (fallbackPct == null) fallbackPct = pct;
|
|
470
|
+
}
|
|
471
|
+
if (fallbackPct != null) {
|
|
472
|
+
return { used_percent: fallbackPct, window_minutes: windowMinutes, resets_at: 0 };
|
|
473
|
+
}
|
|
474
|
+
return null;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function matchPercent(slice) {
|
|
478
|
+
// 优先 "used: 30%" / "30% used" / "30.5%"
|
|
479
|
+
const m = slice.match(/(?:used\s*[::]?\s*)?(\d{1,3}(?:\.\d+)?)\s*%/i);
|
|
480
|
+
if (m) {
|
|
481
|
+
const v = Number(m[1]);
|
|
482
|
+
if (Number.isFinite(v)) return clamp(v, 0, 100);
|
|
483
|
+
}
|
|
484
|
+
// 回退 "30 / 100" 样式(个别版本)
|
|
485
|
+
const frac = slice.match(/(\d+(?:\.\d+)?)\s*\/\s*(\d+(?:\.\d+)?)/);
|
|
486
|
+
if (frac) {
|
|
487
|
+
const a = Number(frac[1]), b = Number(frac[2]);
|
|
488
|
+
if (Number.isFinite(a) && Number.isFinite(b) && b > 0) return clamp((a / b) * 100, 0, 100);
|
|
489
|
+
}
|
|
490
|
+
return null;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function matchResetsAt(slice, nowSec) {
|
|
494
|
+
// 1. v2.1+ TUI 新格式 —— 两种写法都吃:
|
|
495
|
+
// "Resets 2:59pm (Asia/Shanghai)" 带空格
|
|
496
|
+
// "ResetsApr24at11am(Asia/Shanghai)" script(1) 剥离后的无空格版
|
|
497
|
+
// "Resets 14:59" / "Resets3pm" 24h 或无 tz
|
|
498
|
+
// 时区:直接用本机当前 TZ 做转换,忽略 TUI 里带的 (TZ) 标注 —— 一般跟 claude 设置一致
|
|
499
|
+
// 关键字吃 "Resets" / "Reses"(第一遍坏字符) / "Reset" 三种写法
|
|
500
|
+
const mNew = slice.match(
|
|
501
|
+
/(?:resets?|reses)\s*(?:([A-Za-z]{3})\s*(\d{1,2})\s*at\s*)?(\d{1,2})(?::(\d{2}))?\s*(am|pm)?\s*(?:\([^)]+\))?/i
|
|
502
|
+
);
|
|
503
|
+
if (mNew) {
|
|
504
|
+
const [, monStr, dayStr, hourStr, minStr, ampm] = mNew;
|
|
505
|
+
let hour = Number(hourStr);
|
|
506
|
+
const minute = minStr ? Number(minStr) : 0;
|
|
507
|
+
if (ampm) {
|
|
508
|
+
const isPm = /pm/i.test(ampm);
|
|
509
|
+
if (isPm && hour < 12) hour += 12;
|
|
510
|
+
if (!isPm && hour === 12) hour = 0;
|
|
511
|
+
}
|
|
512
|
+
const epoch = resolveLocalEpoch({
|
|
513
|
+
month: monStr ? parseShortMonth(monStr) : null,
|
|
514
|
+
day: dayStr ? Number(dayStr) : null,
|
|
515
|
+
hour, minute, nowSec,
|
|
516
|
+
});
|
|
517
|
+
if (epoch && epoch > nowSec) return epoch;
|
|
518
|
+
}
|
|
519
|
+
// 2. 旧格式:"Resets in 2h 14m" / "resets in 45m" / "resets in 4d 8h"
|
|
520
|
+
const rel = slice.match(/reset(?:s)?\s+in\s+([0-9dhms\s]+)(?=[^0-9dhms]|$)/i);
|
|
521
|
+
if (rel) {
|
|
522
|
+
let sec = 0;
|
|
523
|
+
for (const p of rel[1].matchAll(/(\d+)\s*([dhms])/gi)) {
|
|
524
|
+
const n = Number(p[1]);
|
|
525
|
+
if (!Number.isFinite(n)) continue;
|
|
526
|
+
const u = p[2].toLowerCase();
|
|
527
|
+
if (u === "d") sec += n * 86400;
|
|
528
|
+
else if (u === "h") sec += n * 3600;
|
|
529
|
+
else if (u === "m") sec += n * 60;
|
|
530
|
+
else if (u === "s") sec += n;
|
|
531
|
+
}
|
|
532
|
+
if (sec > 0) return nowSec + sec;
|
|
533
|
+
}
|
|
534
|
+
// 3. "Resets on 2026-04-25" —— 允许 ISO 日期
|
|
535
|
+
const abs = slice.match(/reset(?:s)?\s+(?:on|at)\s+(\d{4}-\d{2}-\d{2})/i);
|
|
536
|
+
if (abs) {
|
|
537
|
+
const t = Date.parse(abs[1] + "T00:00:00Z");
|
|
538
|
+
if (!Number.isNaN(t)) return Math.floor(t / 1000);
|
|
539
|
+
}
|
|
540
|
+
return null;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
const MONTH_MAP = {
|
|
544
|
+
jan: 0, feb: 1, mar: 2, apr: 3, may: 4, jun: 5,
|
|
545
|
+
jul: 6, aug: 7, sep: 8, oct: 9, nov: 10, dec: 11,
|
|
546
|
+
};
|
|
547
|
+
function parseShortMonth(s) {
|
|
548
|
+
const k = String(s).toLowerCase().slice(0, 3);
|
|
549
|
+
return MONTH_MAP[k] ?? null;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* 把「年月日时分」按**本机当前时区**转换成 unix seconds。
|
|
554
|
+
* 缺省 month/day 时用"今天";若结果已过当前时间则推到"明天"(或明年)。
|
|
555
|
+
*/
|
|
556
|
+
function resolveLocalEpoch({ month, day, hour, minute, nowSec }) {
|
|
557
|
+
const now = new Date(nowSec * 1000);
|
|
558
|
+
const year = now.getFullYear();
|
|
559
|
+
const m = month != null ? month : now.getMonth();
|
|
560
|
+
const d = day != null ? day : now.getDate();
|
|
561
|
+
let target = new Date(year, m, d, hour, minute, 0, 0);
|
|
562
|
+
// 没给日期且结果已过 → 推到次日
|
|
563
|
+
if (month == null && target.getTime() <= now.getTime()) {
|
|
564
|
+
target = new Date(year, m, d + 1, hour, minute, 0, 0);
|
|
565
|
+
}
|
|
566
|
+
// 给了月日但结果在过去 → 跨年
|
|
567
|
+
if (month != null && target.getTime() <= now.getTime()) {
|
|
568
|
+
target = new Date(year + 1, m, d, hour, minute, 0, 0);
|
|
569
|
+
}
|
|
570
|
+
const sec = Math.floor(target.getTime() / 1000);
|
|
571
|
+
return sec > nowSec ? sec : null;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
function clamp(v, lo, hi) {
|
|
575
|
+
return Math.max(lo, Math.min(hi, v));
|
|
576
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// 极简 ANSI escape 剥离 —— 避免额外依赖。
|
|
2
|
+
// 覆盖:CSI 序列(\x1b[...)、OSC 序列(\x1b]...\x07/\x1b\\)、常见 C0/C1 控制字符。
|
|
3
|
+
|
|
4
|
+
const CSI_RE = /\x1B\[[0-?]*[ -/]*[@-~]/g;
|
|
5
|
+
const OSC_RE = /\x1B\][^\x07\x1B]*(?:\x07|\x1B\\)/g;
|
|
6
|
+
const SINGLE_ESC_RE = /\x1B[@-_]/g;
|
|
7
|
+
|
|
8
|
+
export function stripAnsi(input) {
|
|
9
|
+
if (typeof input !== "string") return "";
|
|
10
|
+
return input
|
|
11
|
+
.replace(OSC_RE, "")
|
|
12
|
+
.replace(CSI_RE, "")
|
|
13
|
+
.replace(SINGLE_ESC_RE, "");
|
|
14
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* codexCustomCommandsCollector.mjs — codex 自定义 prompt 命令收集器
|
|
3
|
+
*
|
|
4
|
+
* collectCodexCustomCommands({_promptsDir?})
|
|
5
|
+
* → Promise<Array<{command, description, argumentHint, kind}>>
|
|
6
|
+
*
|
|
7
|
+
* 扫 ~/.codex/prompts/*.md,解析 YAML frontmatter,返回命令列表。
|
|
8
|
+
* 解析规则:
|
|
9
|
+
* - name 字段 → command(加 / 前缀);无 name 则取文件名去 .md
|
|
10
|
+
* - description 字段 → 描述;无则空串
|
|
11
|
+
* - argument-hint / argumentHint 字段 → argumentHint;
|
|
12
|
+
* 若无该字段但正文含 $ARGUMENTS 或 $1-$9 → "[arguments]"
|
|
13
|
+
* - kind 固定为 "custom"
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { readdirSync, readFileSync, existsSync } from "node:fs";
|
|
17
|
+
import { homedir } from "node:os";
|
|
18
|
+
import { join, basename } from "node:path";
|
|
19
|
+
|
|
20
|
+
// 检测占位符 $ARGUMENTS / $1-$9
|
|
21
|
+
const PLACEHOLDER_RE = /\$ARGUMENTS|\$[1-9]/;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 简单 frontmatter 解析(单行 key: value 格式)。
|
|
25
|
+
* 返回 {fields, body} —— fields 是键值对,body 是 --- 之后的正文。
|
|
26
|
+
*/
|
|
27
|
+
function parseFrontmatter(content) {
|
|
28
|
+
if (typeof content !== "string") return { fields: {}, body: content };
|
|
29
|
+
const m = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/.exec(content);
|
|
30
|
+
if (!m) return { fields: {}, body: content };
|
|
31
|
+
const fields = {};
|
|
32
|
+
for (const line of m[1].split("\n")) {
|
|
33
|
+
const idx = line.indexOf(":");
|
|
34
|
+
if (idx < 0) continue;
|
|
35
|
+
const key = line.slice(0, idx).trim();
|
|
36
|
+
const val = line.slice(idx + 1).trim();
|
|
37
|
+
if (key) fields[key] = val;
|
|
38
|
+
}
|
|
39
|
+
return { fields, body: m[2] };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param {{
|
|
44
|
+
* _promptsDir?: string, // 测试注入;默认 ~/.codex/prompts/
|
|
45
|
+
* }} opts
|
|
46
|
+
* @returns {Promise<Array<{command, description, argumentHint, kind}>>}
|
|
47
|
+
*/
|
|
48
|
+
export async function collectCodexCustomCommands({ _promptsDir } = {}) {
|
|
49
|
+
const promptsDir = _promptsDir ?? join(homedir(), ".codex", "prompts");
|
|
50
|
+
|
|
51
|
+
if (!existsSync(promptsDir)) return [];
|
|
52
|
+
|
|
53
|
+
let files;
|
|
54
|
+
try {
|
|
55
|
+
files = readdirSync(promptsDir).filter((f) => f.endsWith(".md"));
|
|
56
|
+
} catch {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const result = [];
|
|
61
|
+
for (const file of files) {
|
|
62
|
+
const filePath = join(promptsDir, file);
|
|
63
|
+
let content = "";
|
|
64
|
+
try {
|
|
65
|
+
content = readFileSync(filePath, "utf8");
|
|
66
|
+
} catch {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const { fields, body } = parseFrontmatter(content);
|
|
71
|
+
const name = fields["name"] || basename(file, ".md");
|
|
72
|
+
const description = fields["description"] ?? "";
|
|
73
|
+
const rawHint = fields["argument-hint"] ?? fields["argumentHint"] ?? null;
|
|
74
|
+
const argumentHint = rawHint
|
|
75
|
+
? String(rawHint)
|
|
76
|
+
: PLACEHOLDER_RE.test(body) ? "[arguments]" : null;
|
|
77
|
+
|
|
78
|
+
result.push({
|
|
79
|
+
command: `/${name}`,
|
|
80
|
+
description: String(description),
|
|
81
|
+
argumentHint,
|
|
82
|
+
kind: "custom",
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return result;
|
|
87
|
+
}
|