patchwork-os 0.2.0-beta.9.canary.92 → 1.1.0-beta.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.bridge.md +15 -16
- package/README.md +46 -458
- package/dist/activationMetrics.js +2 -3
- package/dist/activationMetrics.js.map +1 -1
- package/dist/activityLog.d.ts +1 -0
- package/dist/activityLog.js +8 -3
- package/dist/activityLog.js.map +1 -1
- package/dist/adapters/gemini.js +2 -2
- package/dist/adapters/gemini.js.map +1 -1
- package/dist/adapters/grok.js +6 -1
- package/dist/adapters/grok.js.map +1 -1
- package/dist/adapters/openai.js +4 -0
- package/dist/adapters/openai.js.map +1 -1
- package/dist/approvalHttp.d.ts +9 -0
- package/dist/approvalHttp.js +210 -171
- package/dist/approvalHttp.js.map +1 -1
- package/dist/approvalKpi.d.ts +102 -0
- package/dist/approvalKpi.js +282 -0
- package/dist/approvalKpi.js.map +1 -0
- package/dist/approvalQueue.d.ts +1 -1
- package/dist/approvalQueue.js.map +1 -1
- package/dist/automation.d.ts +45 -4
- package/dist/automation.js +123 -15
- package/dist/automation.js.map +1 -1
- package/dist/bridge.d.ts +10 -0
- package/dist/bridge.js +225 -46
- package/dist/bridge.js.map +1 -1
- package/dist/bridgeLockDiscovery.d.ts +16 -1
- package/dist/bridgeLockDiscovery.js +38 -4
- package/dist/bridgeLockDiscovery.js.map +1 -1
- package/dist/bridgeToolsRules.js +3 -18
- package/dist/bridgeToolsRules.js.map +1 -1
- package/dist/claudeAuthHttp.d.ts +25 -0
- package/dist/claudeAuthHttp.js +219 -0
- package/dist/claudeAuthHttp.js.map +1 -0
- package/dist/claudeDriver.js +4 -1
- package/dist/claudeDriver.js.map +1 -1
- package/dist/claudeOrchestrator.d.ts +15 -0
- package/dist/claudeOrchestrator.js +44 -0
- package/dist/claudeOrchestrator.js.map +1 -1
- package/dist/commands/connect.d.ts +47 -0
- package/dist/commands/connect.js +419 -0
- package/dist/commands/connect.js.map +1 -0
- package/dist/commands/install.js +3 -10
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/launchd.d.ts +7 -0
- package/dist/commands/launchd.js +20 -2
- package/dist/commands/launchd.js.map +1 -1
- package/dist/commands/patchworkInit.d.ts +7 -0
- package/dist/commands/patchworkInit.js +26 -0
- package/dist/commands/patchworkInit.js.map +1 -1
- package/dist/commands/recipe.d.ts +59 -4
- package/dist/commands/recipe.js +206 -5
- package/dist/commands/recipe.js.map +1 -1
- package/dist/commands/recipeInstall.d.ts +9 -0
- package/dist/commands/recipeInstall.js +56 -3
- package/dist/commands/recipeInstall.js.map +1 -1
- package/dist/commands/task.d.ts +25 -0
- package/dist/commands/task.js +61 -41
- package/dist/commands/task.js.map +1 -1
- package/dist/commands/tokenEfficiency.d.ts +4 -0
- package/dist/commands/tokenEfficiency.js +23 -26
- package/dist/commands/tokenEfficiency.js.map +1 -1
- package/dist/commands/tracesExport.d.ts +15 -1
- package/dist/commands/tracesExport.js +39 -5
- package/dist/commands/tracesExport.js.map +1 -1
- package/dist/config.js +16 -0
- package/dist/config.js.map +1 -1
- package/dist/connectorRoutes.js +613 -62
- package/dist/connectorRoutes.js.map +1 -1
- package/dist/connectors/asana.js +9 -3
- package/dist/connectors/asana.js.map +1 -1
- package/dist/connectors/baseConnector.d.ts +16 -0
- package/dist/connectors/baseConnector.js +2 -0
- package/dist/connectors/baseConnector.js.map +1 -1
- package/dist/connectors/caldiy.js +34 -0
- package/dist/connectors/caldiy.js.map +1 -1
- package/dist/connectors/confluence.js +9 -2
- package/dist/connectors/confluence.js.map +1 -1
- package/dist/connectors/connectorActivity.d.ts +24 -0
- package/dist/connectors/connectorActivity.js +32 -0
- package/dist/connectors/connectorActivity.js.map +1 -0
- package/dist/connectors/connectorRedirectUri.js +22 -2
- package/dist/connectors/connectorRedirectUri.js.map +1 -1
- package/dist/connectors/connectorRegistry.d.ts +0 -3
- package/dist/connectors/connectorRegistry.js +8 -8
- package/dist/connectors/connectorRegistry.js.map +1 -1
- package/dist/connectors/datadog.js +8 -1
- package/dist/connectors/datadog.js.map +1 -1
- package/dist/connectors/discord.js +4 -3
- package/dist/connectors/discord.js.map +1 -1
- package/dist/connectors/elasticsearch.js +22 -0
- package/dist/connectors/elasticsearch.js.map +1 -1
- package/dist/connectors/github.d.ts +38 -0
- package/dist/connectors/github.js +91 -5
- package/dist/connectors/github.js.map +1 -1
- package/dist/connectors/gitlab.js +19 -16
- package/dist/connectors/gitlab.js.map +1 -1
- package/dist/connectors/gmail.d.ts +5 -0
- package/dist/connectors/gmail.js +53 -11
- package/dist/connectors/gmail.js.map +1 -1
- package/dist/connectors/googleCalendar.js +5 -4
- package/dist/connectors/googleCalendar.js.map +1 -1
- package/dist/connectors/googleDocs.js +7 -6
- package/dist/connectors/googleDocs.js.map +1 -1
- package/dist/connectors/googleDrive.js +15 -8
- package/dist/connectors/googleDrive.js.map +1 -1
- package/dist/connectors/grafana.js +39 -3
- package/dist/connectors/grafana.js.map +1 -1
- package/dist/connectors/jira.d.ts +1 -1
- package/dist/connectors/jira.js +19 -4
- package/dist/connectors/jira.js.map +1 -1
- package/dist/connectors/linear.js +2 -2
- package/dist/connectors/linear.js.map +1 -1
- package/dist/connectors/mcpClient.d.ts +29 -1
- package/dist/connectors/mcpClient.js +114 -44
- package/dist/connectors/mcpClient.js.map +1 -1
- package/dist/connectors/mcpOAuth.d.ts +1 -0
- package/dist/connectors/mcpOAuth.js +49 -4
- package/dist/connectors/mcpOAuth.js.map +1 -1
- package/dist/connectors/monday.js +5 -4
- package/dist/connectors/monday.js.map +1 -1
- package/dist/connectors/mongodb.js +32 -5
- package/dist/connectors/mongodb.js.map +1 -1
- package/dist/connectors/notion.d.ts +1 -1
- package/dist/connectors/notion.js +22 -6
- package/dist/connectors/notion.js.map +1 -1
- package/dist/connectors/oauthError.d.ts +17 -0
- package/dist/connectors/oauthError.js +50 -0
- package/dist/connectors/oauthError.js.map +1 -0
- package/dist/connectors/postgres.d.ts +7 -0
- package/dist/connectors/postgres.js +32 -2
- package/dist/connectors/postgres.js.map +1 -1
- package/dist/connectors/posthog.js +26 -0
- package/dist/connectors/posthog.js.map +1 -1
- package/dist/connectors/redis.d.ts +1 -0
- package/dist/connectors/redis.js +47 -20
- package/dist/connectors/redis.js.map +1 -1
- package/dist/connectors/salesforce.js +66 -5
- package/dist/connectors/salesforce.js.map +1 -1
- package/dist/connectors/sentry.js +2 -2
- package/dist/connectors/sentry.js.map +1 -1
- package/dist/connectors/slack.js +37 -14
- package/dist/connectors/slack.js.map +1 -1
- package/dist/connectors/snowflake.js +24 -0
- package/dist/connectors/snowflake.js.map +1 -1
- package/dist/connectors/stripe.js +9 -2
- package/dist/connectors/stripe.js.map +1 -1
- package/dist/connectors/supabase.js +34 -0
- package/dist/connectors/supabase.js.map +1 -1
- package/dist/connectors/tokenStorage.d.ts +20 -0
- package/dist/connectors/tokenStorage.js +196 -64
- package/dist/connectors/tokenStorage.js.map +1 -1
- package/dist/connectors/woocommerce.js +34 -0
- package/dist/connectors/woocommerce.js.map +1 -1
- package/dist/copilot/parseIntent.d.ts +58 -0
- package/dist/copilot/parseIntent.js +149 -0
- package/dist/copilot/parseIntent.js.map +1 -0
- package/dist/decisionReplay.d.ts +8 -6
- package/dist/decisionReplay.js +35 -11
- package/dist/decisionReplay.js.map +1 -1
- package/dist/decisionTraceLog.d.ts +9 -0
- package/dist/decisionTraceLog.js +6 -0
- package/dist/decisionTraceLog.js.map +1 -1
- package/dist/drivers/claude/api.js +49 -7
- package/dist/drivers/claude/api.js.map +1 -1
- package/dist/drivers/claude/envSanitizer.d.ts +9 -1
- package/dist/drivers/claude/envSanitizer.js +41 -3
- package/dist/drivers/claude/envSanitizer.js.map +1 -1
- package/dist/drivers/claude/streamParser.d.ts +13 -0
- package/dist/drivers/claude/streamParser.js.map +1 -1
- package/dist/drivers/claude/subprocess.js +131 -16
- package/dist/drivers/claude/subprocess.js.map +1 -1
- package/dist/drivers/claude/subprocessSettings.d.ts +1 -1
- package/dist/drivers/claude/subprocessSettings.js +18 -4
- package/dist/drivers/claude/subprocessSettings.js.map +1 -1
- package/dist/drivers/gemini/index.d.ts +13 -0
- package/dist/drivers/gemini/index.js +211 -67
- package/dist/drivers/gemini/index.js.map +1 -1
- package/dist/drivers/local/index.d.ts +2 -17
- package/dist/drivers/local/index.js +5 -92
- package/dist/drivers/local/index.js.map +1 -1
- package/dist/drivers/openai/index.js +69 -9
- package/dist/drivers/openai/index.js.map +1 -1
- package/dist/drivers/outputCap.d.ts +27 -0
- package/dist/drivers/outputCap.js +50 -0
- package/dist/drivers/outputCap.js.map +1 -0
- package/dist/embeddings/cosine.d.ts +23 -0
- package/dist/embeddings/cosine.js +59 -0
- package/dist/embeddings/cosine.js.map +1 -0
- package/dist/embeddings/index.d.ts +19 -0
- package/dist/embeddings/index.js +35 -0
- package/dist/embeddings/index.js.map +1 -0
- package/dist/embeddings/localEmbeddings.d.ts +29 -0
- package/dist/embeddings/localEmbeddings.js +103 -0
- package/dist/embeddings/localEmbeddings.js.map +1 -0
- package/dist/embeddings/localEndpoints.d.ts +24 -0
- package/dist/embeddings/localEndpoints.js +37 -0
- package/dist/embeddings/localEndpoints.js.map +1 -0
- package/dist/embeddings/types.d.ts +32 -0
- package/dist/embeddings/types.js +12 -0
- package/dist/embeddings/types.js.map +1 -0
- package/dist/featureFlags.d.ts +18 -0
- package/dist/featureFlags.js +32 -0
- package/dist/featureFlags.js.map +1 -1
- package/dist/fp/automationInterpreter.js +118 -39
- package/dist/fp/automationInterpreter.js.map +1 -1
- package/dist/fp/automationProgram.d.ts +10 -0
- package/dist/fp/automationProgram.js.map +1 -1
- package/dist/fp/automationState.d.ts +6 -0
- package/dist/fp/automationState.js +38 -0
- package/dist/fp/automationState.js.map +1 -1
- package/dist/fp/commandDescription.d.ts +6 -0
- package/dist/fp/commandDescription.js +18 -5
- package/dist/fp/commandDescription.js.map +1 -1
- package/dist/fp/interpreterContext.d.ts +16 -5
- package/dist/fp/interpreterContext.js +59 -3
- package/dist/fp/interpreterContext.js.map +1 -1
- package/dist/fp/policyParser.js +18 -8
- package/dist/fp/policyParser.js.map +1 -1
- package/dist/haltPushDispatch.d.ts +4 -0
- package/dist/haltPushDispatch.js +7 -22
- package/dist/haltPushDispatch.js.map +1 -1
- package/dist/inboxRoutes.js +2 -2
- package/dist/inboxRoutes.js.map +1 -1
- package/dist/index.js +672 -135
- package/dist/index.js.map +1 -1
- package/dist/installGuard.js +6 -0
- package/dist/installGuard.js.map +1 -1
- package/dist/localEndpointGuard.d.ts +25 -0
- package/dist/localEndpointGuard.js +101 -0
- package/dist/localEndpointGuard.js.map +1 -0
- package/dist/mcpRoutes.js +1 -1
- package/dist/mcpRoutes.js.map +1 -1
- package/dist/oauth.d.ts +13 -0
- package/dist/oauth.js +53 -9
- package/dist/oauth.js.map +1 -1
- package/dist/orchestrator/childBridgeClient.d.ts +7 -0
- package/dist/orchestrator/childBridgeClient.js +42 -1
- package/dist/orchestrator/childBridgeClient.js.map +1 -1
- package/dist/orchestrator/orchestratorBridge.d.ts +33 -0
- package/dist/orchestrator/orchestratorBridge.js +75 -9
- package/dist/orchestrator/orchestratorBridge.js.map +1 -1
- package/dist/patchworkConfig.d.ts +4 -0
- package/dist/patchworkConfig.js +33 -4
- package/dist/patchworkConfig.js.map +1 -1
- package/dist/probe.js +39 -5
- package/dist/probe.js.map +1 -1
- package/dist/recipeOrchestration.d.ts +59 -0
- package/dist/recipeOrchestration.js +314 -60
- package/dist/recipeOrchestration.js.map +1 -1
- package/dist/recipeRoutes.d.ts +93 -0
- package/dist/recipeRoutes.js +664 -27
- package/dist/recipeRoutes.js.map +1 -1
- package/dist/recipes/RecipeOrchestrator.d.ts +14 -0
- package/dist/recipes/RecipeOrchestrator.js +42 -2
- package/dist/recipes/RecipeOrchestrator.js.map +1 -1
- package/dist/recipes/agentExecutor.d.ts +51 -2
- package/dist/recipes/agentExecutor.js +86 -13
- package/dist/recipes/agentExecutor.js.map +1 -1
- package/dist/recipes/chainedRunner.d.ts +111 -3
- package/dist/recipes/chainedRunner.js +440 -45
- package/dist/recipes/chainedRunner.js.map +1 -1
- package/dist/recipes/compiler.js +42 -29
- package/dist/recipes/compiler.js.map +1 -1
- package/dist/recipes/connectorPreflight.js +31 -0
- package/dist/recipes/connectorPreflight.js.map +1 -1
- package/dist/recipes/dependencyGraph.d.ts +16 -0
- package/dist/recipes/dependencyGraph.js +37 -2
- package/dist/recipes/dependencyGraph.js.map +1 -1
- package/dist/recipes/eventTriggerPrograms.d.ts +36 -0
- package/dist/recipes/eventTriggerPrograms.js +158 -0
- package/dist/recipes/eventTriggerPrograms.js.map +1 -0
- package/dist/recipes/githubInstallSource.d.ts +9 -2
- package/dist/recipes/githubInstallSource.js +36 -6
- package/dist/recipes/githubInstallSource.js.map +1 -1
- package/dist/recipes/haltCategory.d.ts +10 -0
- package/dist/recipes/haltCategory.js +11 -1
- package/dist/recipes/haltCategory.js.map +1 -1
- package/dist/recipes/idempotencyKey.d.ts +30 -3
- package/dist/recipes/idempotencyKey.js +89 -5
- package/dist/recipes/idempotencyKey.js.map +1 -1
- package/dist/recipes/installer.js +12 -35
- package/dist/recipes/installer.js.map +1 -1
- package/dist/recipes/judgeVerdict.d.ts +11 -1
- package/dist/recipes/judgeVerdict.js +47 -7
- package/dist/recipes/judgeVerdict.js.map +1 -1
- package/dist/recipes/names.d.ts +5 -0
- package/dist/recipes/names.js +10 -5
- package/dist/recipes/names.js.map +1 -1
- package/dist/recipes/parser.js +74 -10
- package/dist/recipes/parser.js.map +1 -1
- package/dist/recipes/pricing/costRouter.d.ts +43 -0
- package/dist/recipes/pricing/costRouter.js +44 -0
- package/dist/recipes/pricing/costRouter.js.map +1 -0
- package/dist/recipes/pricing/priceTable.d.ts +76 -0
- package/dist/recipes/pricing/priceTable.js +144 -0
- package/dist/recipes/pricing/priceTable.js.map +1 -0
- package/dist/recipes/replayRun.js +5 -2
- package/dist/recipes/replayRun.js.map +1 -1
- package/dist/recipes/resolveRecipePath.js +15 -1
- package/dist/recipes/resolveRecipePath.js.map +1 -1
- package/dist/recipes/runBudget.d.ts +93 -32
- package/dist/recipes/runBudget.js +213 -49
- package/dist/recipes/runBudget.js.map +1 -1
- package/dist/recipes/runRegistry.d.ts +32 -0
- package/dist/recipes/runRegistry.js +54 -0
- package/dist/recipes/runRegistry.js.map +1 -0
- package/dist/recipes/scheduler.js +6 -2
- package/dist/recipes/scheduler.js.map +1 -1
- package/dist/recipes/schema.d.ts +59 -6
- package/dist/recipes/schemaGenerator.d.ts +9 -0
- package/dist/recipes/schemaGenerator.js +446 -77
- package/dist/recipes/schemaGenerator.js.map +1 -1
- package/dist/recipes/simulation/aggregateRunRisk.d.ts +46 -0
- package/dist/recipes/simulation/aggregateRunRisk.js +120 -0
- package/dist/recipes/simulation/aggregateRunRisk.js.map +1 -0
- package/dist/recipes/simulation/costProjector.d.ts +32 -0
- package/dist/recipes/simulation/costProjector.js +194 -0
- package/dist/recipes/simulation/costProjector.js.map +1 -0
- package/dist/recipes/simulation/sideEffects.d.ts +32 -0
- package/dist/recipes/simulation/sideEffects.js +62 -0
- package/dist/recipes/simulation/sideEffects.js.map +1 -0
- package/dist/recipes/simulation/simulate.d.ts +36 -0
- package/dist/recipes/simulation/simulate.js +264 -0
- package/dist/recipes/simulation/simulate.js.map +1 -0
- package/dist/recipes/simulation/simulateMockedRun.d.ts +52 -0
- package/dist/recipes/simulation/simulateMockedRun.js +84 -0
- package/dist/recipes/simulation/simulateMockedRun.js.map +1 -0
- package/dist/recipes/simulation/synthesizeMockedOutputs.d.ts +31 -0
- package/dist/recipes/simulation/synthesizeMockedOutputs.js +50 -0
- package/dist/recipes/simulation/synthesizeMockedOutputs.js.map +1 -0
- package/dist/recipes/simulation/types.d.ts +198 -0
- package/dist/recipes/simulation/types.js +30 -0
- package/dist/recipes/simulation/types.js.map +1 -0
- package/dist/recipes/stepObservation.d.ts +12 -0
- package/dist/recipes/stepObservation.js +21 -1
- package/dist/recipes/stepObservation.js.map +1 -1
- package/dist/recipes/toolRegistry.js +61 -24
- package/dist/recipes/toolRegistry.js.map +1 -1
- package/dist/recipes/tools/airtable.d.ts +15 -0
- package/dist/recipes/tools/airtable.js +240 -0
- package/dist/recipes/tools/airtable.js.map +1 -0
- package/dist/recipes/tools/caldiy.d.ts +13 -0
- package/dist/recipes/tools/caldiy.js +215 -0
- package/dist/recipes/tools/caldiy.js.map +1 -0
- package/dist/recipes/tools/circleci.d.ts +10 -0
- package/dist/recipes/tools/circleci.js +204 -0
- package/dist/recipes/tools/circleci.js.map +1 -0
- package/dist/recipes/tools/cloudflare.d.ts +13 -0
- package/dist/recipes/tools/cloudflare.js +211 -0
- package/dist/recipes/tools/cloudflare.js.map +1 -0
- package/dist/recipes/tools/confluence.js +11 -10
- package/dist/recipes/tools/confluence.js.map +1 -1
- package/dist/recipes/tools/datadog.js +13 -12
- package/dist/recipes/tools/datadog.js.map +1 -1
- package/dist/recipes/tools/docs.d.ts +18 -0
- package/dist/recipes/tools/docs.js +95 -0
- package/dist/recipes/tools/docs.js.map +1 -0
- package/dist/recipes/tools/elasticsearch.d.ts +11 -0
- package/dist/recipes/tools/elasticsearch.js +157 -0
- package/dist/recipes/tools/elasticsearch.js.map +1 -0
- package/dist/recipes/tools/fanOut.js +4 -4
- package/dist/recipes/tools/fanOut.js.map +1 -1
- package/dist/recipes/tools/figma.d.ts +12 -0
- package/dist/recipes/tools/figma.js +195 -0
- package/dist/recipes/tools/figma.js.map +1 -0
- package/dist/recipes/tools/file.js +43 -22
- package/dist/recipes/tools/file.js.map +1 -1
- package/dist/recipes/tools/github.js +162 -5
- package/dist/recipes/tools/github.js.map +1 -1
- package/dist/recipes/tools/gmail.js +45 -33
- package/dist/recipes/tools/gmail.js.map +1 -1
- package/dist/recipes/tools/grafana.d.ts +11 -0
- package/dist/recipes/tools/grafana.js +216 -0
- package/dist/recipes/tools/grafana.js.map +1 -0
- package/dist/recipes/tools/http.d.ts +1 -1
- package/dist/recipes/tools/http.js +10 -28
- package/dist/recipes/tools/http.js.map +1 -1
- package/dist/recipes/tools/hubspot.js +13 -12
- package/dist/recipes/tools/hubspot.js.map +1 -1
- package/dist/recipes/tools/index.d.ts +28 -0
- package/dist/recipes/tools/index.js +28 -0
- package/dist/recipes/tools/index.js.map +1 -1
- package/dist/recipes/tools/intercom.js +11 -10
- package/dist/recipes/tools/intercom.js.map +1 -1
- package/dist/recipes/tools/jira.js +13 -0
- package/dist/recipes/tools/jira.js.map +1 -1
- package/dist/recipes/tools/meetingNotes.js +2 -2
- package/dist/recipes/tools/monday.d.ts +22 -0
- package/dist/recipes/tools/monday.js +242 -0
- package/dist/recipes/tools/monday.js.map +1 -0
- package/dist/recipes/tools/mongodb.d.ts +19 -0
- package/dist/recipes/tools/mongodb.js +194 -0
- package/dist/recipes/tools/mongodb.js.map +1 -0
- package/dist/recipes/tools/notion.js +11 -10
- package/dist/recipes/tools/notion.js.map +1 -1
- package/dist/recipes/tools/obsidian.d.ts +15 -0
- package/dist/recipes/tools/obsidian.js +173 -0
- package/dist/recipes/tools/obsidian.js.map +1 -0
- package/dist/recipes/tools/outcomes.d.ts +15 -0
- package/dist/recipes/tools/outcomes.js +111 -0
- package/dist/recipes/tools/outcomes.js.map +1 -0
- package/dist/recipes/tools/paystack.d.ts +11 -0
- package/dist/recipes/tools/paystack.js +212 -0
- package/dist/recipes/tools/paystack.js.map +1 -0
- package/dist/recipes/tools/pipedrive.d.ts +16 -0
- package/dist/recipes/tools/pipedrive.js +234 -0
- package/dist/recipes/tools/pipedrive.js.map +1 -0
- package/dist/recipes/tools/postgres.d.ts +15 -0
- package/dist/recipes/tools/postgres.js +186 -0
- package/dist/recipes/tools/postgres.js.map +1 -0
- package/dist/recipes/tools/posthog.d.ts +16 -0
- package/dist/recipes/tools/posthog.js +219 -0
- package/dist/recipes/tools/posthog.js.map +1 -0
- package/dist/recipes/tools/redis.d.ts +9 -0
- package/dist/recipes/tools/redis.js +141 -0
- package/dist/recipes/tools/redis.js.map +1 -0
- package/dist/recipes/tools/resend.d.ts +8 -0
- package/dist/recipes/tools/resend.js +153 -0
- package/dist/recipes/tools/resend.js.map +1 -0
- package/dist/recipes/tools/salesforce.d.ts +16 -0
- package/dist/recipes/tools/salesforce.js +184 -0
- package/dist/recipes/tools/salesforce.js.map +1 -0
- package/dist/recipes/tools/sendgrid.d.ts +9 -0
- package/dist/recipes/tools/sendgrid.js +175 -0
- package/dist/recipes/tools/sendgrid.js.map +1 -0
- package/dist/recipes/tools/shopify.d.ts +16 -0
- package/dist/recipes/tools/shopify.js +266 -0
- package/dist/recipes/tools/shopify.js.map +1 -0
- package/dist/recipes/tools/snowflake.d.ts +16 -0
- package/dist/recipes/tools/snowflake.js +173 -0
- package/dist/recipes/tools/snowflake.js.map +1 -0
- package/dist/recipes/tools/stripe.js +13 -12
- package/dist/recipes/tools/stripe.js.map +1 -1
- package/dist/recipes/tools/supabase.d.ts +9 -0
- package/dist/recipes/tools/supabase.js +133 -0
- package/dist/recipes/tools/supabase.js.map +1 -0
- package/dist/recipes/tools/todoist.d.ts +15 -0
- package/dist/recipes/tools/todoist.js +228 -0
- package/dist/recipes/tools/todoist.js.map +1 -0
- package/dist/recipes/tools/twilio.d.ts +11 -0
- package/dist/recipes/tools/twilio.js +180 -0
- package/dist/recipes/tools/twilio.js.map +1 -0
- package/dist/recipes/tools/vercel.d.ts +9 -0
- package/dist/recipes/tools/vercel.js +146 -0
- package/dist/recipes/tools/vercel.js.map +1 -0
- package/dist/recipes/tools/webflow.d.ts +11 -0
- package/dist/recipes/tools/webflow.js +244 -0
- package/dist/recipes/tools/webflow.js.map +1 -0
- package/dist/recipes/tools/woocommerce.d.ts +18 -0
- package/dist/recipes/tools/woocommerce.js +260 -0
- package/dist/recipes/tools/woocommerce.js.map +1 -0
- package/dist/recipes/tools/wrapConnectorExecute.d.ts +25 -0
- package/dist/recipes/tools/wrapConnectorExecute.js +37 -0
- package/dist/recipes/tools/wrapConnectorExecute.js.map +1 -0
- package/dist/recipes/tools/zendesk.js +11 -10
- package/dist/recipes/tools/zendesk.js.map +1 -1
- package/dist/recipes/triggerVars.d.ts +15 -0
- package/dist/recipes/triggerVars.js +57 -0
- package/dist/recipes/triggerVars.js.map +1 -0
- package/dist/recipes/validation.d.ts +1 -1
- package/dist/recipes/validation.js +536 -12
- package/dist/recipes/validation.js.map +1 -1
- package/dist/recipes/yamlPositions.d.ts +1 -1
- package/dist/recipes/yamlPositions.js +7 -2
- package/dist/recipes/yamlPositions.js.map +1 -1
- package/dist/recipes/yamlRunner.d.ts +216 -4
- package/dist/recipes/yamlRunner.js +1030 -96
- package/dist/recipes/yamlRunner.js.map +1 -1
- package/dist/recipesHttp.d.ts +5 -0
- package/dist/recipesHttp.js +68 -6
- package/dist/recipesHttp.js.map +1 -1
- package/dist/resources.js +14 -1
- package/dist/resources.js.map +1 -1
- package/dist/riskSignals.d.ts +42 -0
- package/dist/riskSignals.js +197 -0
- package/dist/riskSignals.js.map +1 -0
- package/dist/riskTier.d.ts +9 -0
- package/dist/riskTier.js +48 -0
- package/dist/riskTier.js.map +1 -1
- package/dist/runLog.d.ts +53 -0
- package/dist/runLog.js +75 -16
- package/dist/runLog.js.map +1 -1
- package/dist/schemas/recipe.v1.json +360 -16
- package/dist/server.d.ts +60 -0
- package/dist/server.js +192 -31
- package/dist/server.js.map +1 -1
- package/dist/sessionCheckpoint.js +4 -1
- package/dist/sessionCheckpoint.js.map +1 -1
- package/dist/sessionDetail.d.ts +43 -0
- package/dist/sessionDetail.js +59 -0
- package/dist/sessionDetail.js.map +1 -0
- package/dist/settingsEnv.d.ts +47 -0
- package/dist/settingsEnv.js +88 -0
- package/dist/settingsEnv.js.map +1 -0
- package/dist/streamableHttp.js +44 -10
- package/dist/streamableHttp.js.map +1 -1
- package/dist/ta/backtest/abnday.d.ts +53 -0
- package/dist/ta/backtest/abnday.js +123 -0
- package/dist/ta/backtest/abnday.js.map +1 -0
- package/dist/ta/backtest/directional.d.ts +48 -0
- package/dist/ta/backtest/directional.js +103 -0
- package/dist/ta/backtest/directional.js.map +1 -0
- package/dist/ta/backtest/fundx.d.ts +67 -0
- package/dist/ta/backtest/fundx.js +264 -0
- package/dist/ta/backtest/fundx.js.map +1 -0
- package/dist/ta/backtest/fvg.d.ts +62 -0
- package/dist/ta/backtest/fvg.js +236 -0
- package/dist/ta/backtest/fvg.js.map +1 -0
- package/dist/ta/backtest/ichimoku.d.ts +41 -0
- package/dist/ta/backtest/ichimoku.js +146 -0
- package/dist/ta/backtest/ichimoku.js.map +1 -0
- package/dist/ta/backtest/ingest.d.ts +20 -0
- package/dist/ta/backtest/ingest.js +104 -0
- package/dist/ta/backtest/ingest.js.map +1 -0
- package/dist/ta/backtest/msb.d.ts +49 -0
- package/dist/ta/backtest/msb.js +162 -0
- package/dist/ta/backtest/msb.js.map +1 -0
- package/dist/ta/backtest/scoring.d.ts +33 -0
- package/dist/ta/backtest/scoring.js +82 -0
- package/dist/ta/backtest/scoring.js.map +1 -0
- package/dist/ta/backtest/summary.d.ts +20 -0
- package/dist/ta/backtest/summary.js +42 -0
- package/dist/ta/backtest/summary.js.map +1 -0
- package/dist/ta/backtest/tsmom.d.ts +72 -0
- package/dist/ta/backtest/tsmom.js +202 -0
- package/dist/ta/backtest/tsmom.js.map +1 -0
- package/dist/ta/backtest/volsq.d.ts +34 -0
- package/dist/ta/backtest/volsq.js +154 -0
- package/dist/ta/backtest/volsq.js.map +1 -0
- package/dist/ta/backtest/walkForward.d.ts +45 -0
- package/dist/ta/backtest/walkForward.js +136 -0
- package/dist/ta/backtest/walkForward.js.map +1 -0
- package/dist/ta/cycles.d.ts +15 -0
- package/dist/ta/cycles.js +31 -0
- package/dist/ta/cycles.js.map +1 -0
- package/dist/ta/desk/accrualEmitter.d.ts +59 -0
- package/dist/ta/desk/accrualEmitter.js +136 -0
- package/dist/ta/desk/accrualEmitter.js.map +1 -0
- package/dist/ta/desk/cellBacktest.d.ts +179 -0
- package/dist/ta/desk/cellBacktest.js +514 -0
- package/dist/ta/desk/cellBacktest.js.map +1 -0
- package/dist/ta/desk/cells/wpMaRejection.d.ts +49 -0
- package/dist/ta/desk/cells/wpMaRejection.js +141 -0
- package/dist/ta/desk/cells/wpMaRejection.js.map +1 -0
- package/dist/ta/desk/cells/wpVolumeClimax.d.ts +57 -0
- package/dist/ta/desk/cells/wpVolumeClimax.js +193 -0
- package/dist/ta/desk/cells/wpVolumeClimax.js.map +1 -0
- package/dist/ta/desk/collectors.d.ts +18 -0
- package/dist/ta/desk/collectors.js +450 -0
- package/dist/ta/desk/collectors.js.map +1 -0
- package/dist/ta/desk/contract.d.ts +51 -0
- package/dist/ta/desk/contract.js +261 -0
- package/dist/ta/desk/contract.js.map +1 -0
- package/dist/ta/desk/deskLedger.d.ts +36 -0
- package/dist/ta/desk/deskLedger.js +239 -0
- package/dist/ta/desk/deskLedger.js.map +1 -0
- package/dist/ta/desk/liqTape.d.ts +37 -0
- package/dist/ta/desk/liqTape.js +99 -0
- package/dist/ta/desk/liqTape.js.map +1 -0
- package/dist/ta/desk/post.d.ts +20 -0
- package/dist/ta/desk/post.js +65 -0
- package/dist/ta/desk/post.js.map +1 -0
- package/dist/ta/desk/render.d.ts +25 -0
- package/dist/ta/desk/render.js +398 -0
- package/dist/ta/desk/render.js.map +1 -0
- package/dist/ta/desk/surfaces.d.ts +216 -0
- package/dist/ta/desk/surfaces.js +472 -0
- package/dist/ta/desk/surfaces.js.map +1 -0
- package/dist/ta/desk/types.d.ts +174 -0
- package/dist/ta/desk/types.js +104 -0
- package/dist/ta/desk/types.js.map +1 -0
- package/dist/ta/index.d.ts +23 -0
- package/dist/ta/index.js +54 -0
- package/dist/ta/index.js.map +1 -0
- package/dist/ta/ledger.d.ts +44 -0
- package/dist/ta/ledger.js +95 -0
- package/dist/ta/ledger.js.map +1 -0
- package/dist/ta/levels.d.ts +21 -0
- package/dist/ta/levels.js +41 -0
- package/dist/ta/levels.js.map +1 -0
- package/dist/ta/swings.d.ts +30 -0
- package/dist/ta/swings.js +83 -0
- package/dist/ta/swings.js.map +1 -0
- package/dist/ta/types.d.ts +81 -0
- package/dist/ta/types.js +20 -0
- package/dist/ta/types.js.map +1 -0
- package/dist/telemetry.js +20 -9
- package/dist/telemetry.js.map +1 -1
- package/dist/tokenUsageTracker.d.ts +2 -1
- package/dist/tokenUsageTracker.js +38 -20
- package/dist/tokenUsageTracker.js.map +1 -1
- package/dist/tools/contextBundle.js +13 -9
- package/dist/tools/contextBundle.js.map +1 -1
- package/dist/tools/ctxGetTaskContext.js +8 -3
- package/dist/tools/ctxGetTaskContext.js.map +1 -1
- package/dist/tools/ctxQueryTraces.d.ts +18 -1
- package/dist/tools/ctxQueryTraces.js +176 -27
- package/dist/tools/ctxQueryTraces.js.map +1 -1
- package/dist/tools/enrichCommit.js +5 -2
- package/dist/tools/enrichCommit.js.map +1 -1
- package/dist/tools/getDependencyTree.js +2 -2
- package/dist/tools/getDependencyTree.js.map +1 -1
- package/dist/tools/getDiagnostics.js +3 -0
- package/dist/tools/getDiagnostics.js.map +1 -1
- package/dist/tools/getDocumentSymbols.js +5 -1
- package/dist/tools/getDocumentSymbols.js.map +1 -1
- package/dist/tools/getGitHotspots.js +12 -1
- package/dist/tools/getGitHotspots.js.map +1 -1
- package/dist/tools/getGitLog.js +5 -2
- package/dist/tools/getGitLog.js.map +1 -1
- package/dist/tools/getGitStatus.js +6 -1
- package/dist/tools/getGitStatus.js.map +1 -1
- package/dist/tools/getProjectContext.js +3 -1
- package/dist/tools/getProjectContext.js.map +1 -1
- package/dist/tools/getToolCapabilities.js +39 -4
- package/dist/tools/getToolCapabilities.js.map +1 -1
- package/dist/tools/gitWrite.js +12 -3
- package/dist/tools/gitWrite.js.map +1 -1
- package/dist/tools/github/composite.d.ts +1 -1
- package/dist/tools/github/composite.js +7 -7
- package/dist/tools/github/composite.js.map +1 -1
- package/dist/tools/github/pr.d.ts +6 -6
- package/dist/tools/github/pr.js +18 -11
- package/dist/tools/github/pr.js.map +1 -1
- package/dist/tools/httpClient.js +55 -3
- package/dist/tools/httpClient.js.map +1 -1
- package/dist/tools/index.d.ts +2 -1
- package/dist/tools/index.js +19 -12
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/lsp.d.ts +6 -0
- package/dist/tools/lsp.js +81 -12
- package/dist/tools/lsp.js.map +1 -1
- package/dist/tools/previewEdit.js +16 -1
- package/dist/tools/previewEdit.js.map +1 -1
- package/dist/tools/resumeClaudeTask.js +17 -0
- package/dist/tools/resumeClaudeTask.js.map +1 -1
- package/dist/tools/searchAndReplace.js +9 -5
- package/dist/tools/searchAndReplace.js.map +1 -1
- package/dist/tools/searchTools.d.ts +26 -0
- package/dist/tools/searchTools.js +42 -2
- package/dist/tools/searchTools.js.map +1 -1
- package/dist/tools/terminal.js +23 -51
- package/dist/tools/terminal.js.map +1 -1
- package/dist/tools/transaction.d.ts +9 -1
- package/dist/tools/transaction.js +59 -4
- package/dist/tools/transaction.js.map +1 -1
- package/dist/tools/utils.d.ts +16 -0
- package/dist/tools/utils.js +77 -7
- package/dist/tools/utils.js.map +1 -1
- package/dist/transport.d.ts +31 -0
- package/dist/transport.js +117 -14
- package/dist/transport.js.map +1 -1
- package/dist/wireHaltPushDispatch.js +12 -0
- package/dist/wireHaltPushDispatch.js.map +1 -1
- package/dist/workerGateDecisionLog.d.ts +141 -0
- package/dist/workerGateDecisionLog.js +361 -0
- package/dist/workerGateDecisionLog.js.map +1 -0
- package/dist/workers/actionClass.d.ts +38 -0
- package/dist/workers/actionClass.js +155 -0
- package/dist/workers/actionClass.js.map +1 -0
- package/dist/workers/backtest.d.ts +68 -0
- package/dist/workers/backtest.js +113 -0
- package/dist/workers/backtest.js.map +1 -0
- package/dist/workers/contextRisk.d.ts +35 -0
- package/dist/workers/contextRisk.js +20 -0
- package/dist/workers/contextRisk.js.map +1 -0
- package/dist/workers/contextRiskScorer.d.ts +48 -0
- package/dist/workers/contextRiskScorer.js +94 -0
- package/dist/workers/contextRiskScorer.js.map +1 -0
- package/dist/workers/graduation.d.ts +61 -0
- package/dist/workers/graduation.js +112 -0
- package/dist/workers/graduation.js.map +1 -0
- package/dist/workers/outcomeStore.d.ts +98 -0
- package/dist/workers/outcomeStore.js +146 -0
- package/dist/workers/outcomeStore.js.map +1 -0
- package/dist/workers/outcomesCli.d.ts +34 -0
- package/dist/workers/outcomesCli.js +91 -0
- package/dist/workers/outcomesCli.js.map +1 -0
- package/dist/workers/runWorkerShadow.d.ts +90 -0
- package/dist/workers/runWorkerShadow.js +323 -0
- package/dist/workers/runWorkerShadow.js.map +1 -0
- package/dist/workers/shadowGate.d.ts +30 -0
- package/dist/workers/shadowGate.js +58 -0
- package/dist/workers/shadowGate.js.map +1 -0
- package/dist/workers/shadowObserver.d.ts +165 -0
- package/dist/workers/shadowObserver.js +203 -0
- package/dist/workers/shadowObserver.js.map +1 -0
- package/dist/workers/shadowReport.d.ts +16 -0
- package/dist/workers/shadowReport.js +61 -0
- package/dist/workers/shadowReport.js.map +1 -0
- package/dist/workers/shadowRun.d.ts +38 -0
- package/dist/workers/shadowRun.js +44 -0
- package/dist/workers/shadowRun.js.map +1 -0
- package/dist/workers/trustLevel.d.ts +82 -0
- package/dist/workers/trustLevel.js +100 -0
- package/dist/workers/trustLevel.js.map +1 -0
- package/dist/workers/worker.d.ts +49 -0
- package/dist/workers/worker.js +88 -0
- package/dist/workers/worker.js.map +1 -0
- package/dist/workers/workerGate.d.ts +101 -0
- package/dist/workers/workerGate.js +210 -0
- package/dist/workers/workerGate.js.map +1 -0
- package/dist/workers/workerLevelStore.d.ts +33 -0
- package/dist/workers/workerLevelStore.js +87 -0
- package/dist/workers/workerLevelStore.js.map +1 -0
- package/dist/workers/workerLoader.d.ts +7 -0
- package/dist/workers/workerLoader.js +31 -0
- package/dist/workers/workerLoader.js.map +1 -0
- package/dist/writeFileAtomic.js +40 -13
- package/dist/writeFileAtomic.js.map +1 -1
- package/package.json +14 -6
- package/scripts/mcp-stdio-shim.cjs +247 -87
- package/templates/CLAUDE.bridge.md +1 -1
- package/templates/recipes/dependency-bump.yaml +33 -0
- package/templates/recipes/incident-to-pr.yaml +187 -0
- package/templates/recipes/outcome-ingester.yaml +53 -0
- package/templates/recipes/release-notes.yaml +30 -0
- package/templates/recipes/triage-failing-tests-autofile.yaml +152 -0
- package/templates/recipes/triage-failing-tests.yaml +38 -0
- package/templates/workers/dependency-upkeep.worker.yaml +26 -0
- package/templates/workers/release-notes.worker.yaml +17 -0
- package/templates/workers/test-guardian.worker.yaml +24 -0
- package/deploy/README.md +0 -172
- package/deploy/bootstrap-new-vps.sh +0 -364
- package/deploy/bootstrap-vps.sh +0 -192
- package/deploy/claude-ide-bridge.service.template +0 -67
- package/deploy/claude-ide-bridge@.service +0 -31
- package/deploy/deploy-dashboard.sh +0 -198
- package/deploy/deploy-landing.sh +0 -136
- package/deploy/ecosystem.config.js.example +0 -36
- package/deploy/install-vps-service.sh +0 -240
- package/deploy/macos/README.md +0 -153
- package/deploy/macos/com.patchwork.bridge.plist.template +0 -54
- package/deploy/macos/com.patchwork.tunnel.plist.template +0 -76
- package/deploy/macos/install-mac-bridge.sh +0 -244
- package/deploy/macos/uninstall-mac-bridge.sh +0 -22
- package/deploy/nginx-claude-bridge.conf.template +0 -129
package/dist/index.js
CHANGED
|
@@ -13,9 +13,24 @@
|
|
|
13
13
|
const envPath = candidates.find(existsSync);
|
|
14
14
|
if (envPath) {
|
|
15
15
|
for (const line of readFileSync(envPath, "utf-8").split("\n")) {
|
|
16
|
-
const
|
|
16
|
+
const trimmed = line.trim();
|
|
17
|
+
// Skip blank lines and full-line comments outright.
|
|
18
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
19
|
+
continue;
|
|
20
|
+
const m = /^([A-Z_][A-Z0-9_]*)=(.*)$/.exec(trimmed);
|
|
17
21
|
if (m?.[1] && !process.env[m[1]]) {
|
|
18
|
-
|
|
22
|
+
let raw = m[2] ?? "";
|
|
23
|
+
// Strip an inline comment, but only when the value is NOT quoted —
|
|
24
|
+
// a quoted value may legitimately contain '#' (cli-commands-6).
|
|
25
|
+
const isQuoted = (raw.startsWith('"') && raw.endsWith('"') && raw.length >= 2) ||
|
|
26
|
+
(raw.startsWith("'") && raw.endsWith("'") && raw.length >= 2);
|
|
27
|
+
if (!isQuoted) {
|
|
28
|
+
const hashIdx = raw.indexOf("#");
|
|
29
|
+
if (hashIdx !== -1)
|
|
30
|
+
raw = raw.slice(0, hashIdx);
|
|
31
|
+
raw = raw.trim();
|
|
32
|
+
}
|
|
33
|
+
process.env[m[1]] = raw.replace(/^["']|["']$/g, "");
|
|
19
34
|
}
|
|
20
35
|
}
|
|
21
36
|
}
|
|
@@ -38,6 +53,7 @@ import readline from "node:readline";
|
|
|
38
53
|
import { fileURLToPath } from "node:url";
|
|
39
54
|
import { getAnalyticsPref, setAnalyticsPref } from "./analyticsPrefs.js";
|
|
40
55
|
import { Bridge } from "./bridge.js";
|
|
56
|
+
import { findAllLiveBridges, findBridgeLock, } from "./bridgeLockDiscovery.js";
|
|
41
57
|
import { isBridgeToolsFileValid, repairBridgeToolsRulesIfStale, } from "./bridgeToolsRules.js";
|
|
42
58
|
import { findEditor, parseConfig } from "./config.js";
|
|
43
59
|
import { detectWorkspaceSymlinkInstall, PATCHWORK_PACKAGE_NAME, SYMLINK_INSTALL_FIX, } from "./installGuard.js";
|
|
@@ -124,12 +140,18 @@ const KNOWN_SUBCOMMANDS = [
|
|
|
124
140
|
"install",
|
|
125
141
|
"status",
|
|
126
142
|
"shim",
|
|
143
|
+
"quick-task",
|
|
144
|
+
"start-task",
|
|
145
|
+
"continue-handoff",
|
|
146
|
+
"token-efficiency",
|
|
127
147
|
"recipe",
|
|
148
|
+
"connect",
|
|
128
149
|
"traces",
|
|
129
150
|
"suggest",
|
|
130
151
|
"dashboard",
|
|
131
152
|
"launchd",
|
|
132
153
|
"start",
|
|
154
|
+
"orchestrator",
|
|
133
155
|
"kill-switch",
|
|
134
156
|
"panic",
|
|
135
157
|
"halts",
|
|
@@ -137,6 +159,10 @@ const KNOWN_SUBCOMMANDS = [
|
|
|
137
159
|
"analytics",
|
|
138
160
|
"doctor",
|
|
139
161
|
"shadow-scan",
|
|
162
|
+
"workers",
|
|
163
|
+
"approvals",
|
|
164
|
+
"gate",
|
|
165
|
+
"outcomes",
|
|
140
166
|
];
|
|
141
167
|
const __invokedSubcommand = (() => {
|
|
142
168
|
const sub = process.argv[2];
|
|
@@ -194,6 +220,11 @@ if (process.argv[2] === "--help" ||
|
|
|
194
220
|
` recipe run <name> [--vars k=v] Run a recipe by name\n` +
|
|
195
221
|
` recipe install <source> Install from a path or GitHub source\n` +
|
|
196
222
|
` recipe --help Full recipe subcommand index\n\n` +
|
|
223
|
+
`Connectors\n` +
|
|
224
|
+
` connect [list] List connectors + connection status\n` +
|
|
225
|
+
` connect <vendor> OAuth: print authorize URL / PAT: connect\n` +
|
|
226
|
+
` connect test <vendor> Health-probe a connector\n` +
|
|
227
|
+
` connect disconnect <vendor> Revoke a connector\n\n` +
|
|
197
228
|
`Operate\n` +
|
|
198
229
|
` start [--port N] [--workspace <dir>] Start a single bridge (no tmux)\n` +
|
|
199
230
|
` status One-line bridge status (port, uptime)\n` +
|
|
@@ -306,20 +337,7 @@ This is a thin wrapper over \`start-all\`. For advanced flags see:
|
|
|
306
337
|
process.exit(result.status ?? 1);
|
|
307
338
|
}
|
|
308
339
|
function writeRulesFileAtomic(rulesFilePath, content) {
|
|
309
|
-
|
|
310
|
-
writeFileSync(tmpPath, content, { encoding: "utf-8", flag: "wx" });
|
|
311
|
-
try {
|
|
312
|
-
renameSync(tmpPath, rulesFilePath);
|
|
313
|
-
}
|
|
314
|
-
catch (err) {
|
|
315
|
-
try {
|
|
316
|
-
unlinkSync(tmpPath);
|
|
317
|
-
}
|
|
318
|
-
catch {
|
|
319
|
-
/* best-effort cleanup */
|
|
320
|
-
}
|
|
321
|
-
throw err;
|
|
322
|
-
}
|
|
340
|
+
writeFileAtomicSync(rulesFilePath, content, { encoding: "utf-8" });
|
|
323
341
|
}
|
|
324
342
|
/**
|
|
325
343
|
* Handles errors from rules file write operations. EACCES → warning + instructions.
|
|
@@ -335,7 +353,7 @@ function handleRulesWriteError(err, rulesFilePath, indent) {
|
|
|
335
353
|
`${indent} Run with elevated permissions or create the file manually.\n\n`);
|
|
336
354
|
return 0;
|
|
337
355
|
}
|
|
338
|
-
if (code === "ELOOP"
|
|
356
|
+
if (code === "ELOOP") {
|
|
339
357
|
process.stderr.write(`${indent}[error] Bridge rules — suspicious path condition (${code}): ${rulesFilePath}\n\n`);
|
|
340
358
|
return 1;
|
|
341
359
|
}
|
|
@@ -424,7 +442,25 @@ Options:
|
|
|
424
442
|
const backupPath = `${targetPath}.${ts}.bak`;
|
|
425
443
|
try {
|
|
426
444
|
renameSync(targetPath, backupPath);
|
|
427
|
-
|
|
445
|
+
try {
|
|
446
|
+
renameSync(tmpPath, targetPath);
|
|
447
|
+
}
|
|
448
|
+
catch (renameErr) {
|
|
449
|
+
if (renameErr.code === "EEXIST" &&
|
|
450
|
+
process.platform === "win32") {
|
|
451
|
+
// Concurrent writer recreated targetPath between the two renames.
|
|
452
|
+
try {
|
|
453
|
+
unlinkSync(targetPath);
|
|
454
|
+
}
|
|
455
|
+
catch {
|
|
456
|
+
/* best-effort */
|
|
457
|
+
}
|
|
458
|
+
renameSync(tmpPath, targetPath);
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
throw renameErr;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
428
464
|
}
|
|
429
465
|
catch (err) {
|
|
430
466
|
try {
|
|
@@ -443,7 +479,30 @@ Options:
|
|
|
443
479
|
encoding: "utf-8",
|
|
444
480
|
flag: "wx",
|
|
445
481
|
});
|
|
446
|
-
|
|
482
|
+
try {
|
|
483
|
+
renameSync(`${targetPath}.tmp`, targetPath);
|
|
484
|
+
}
|
|
485
|
+
catch (renameErr) {
|
|
486
|
+
if (renameErr.code === "EEXIST" &&
|
|
487
|
+
process.platform === "win32") {
|
|
488
|
+
try {
|
|
489
|
+
unlinkSync(targetPath);
|
|
490
|
+
}
|
|
491
|
+
catch {
|
|
492
|
+
/* best-effort */
|
|
493
|
+
}
|
|
494
|
+
renameSync(`${targetPath}.tmp`, targetPath);
|
|
495
|
+
}
|
|
496
|
+
else {
|
|
497
|
+
try {
|
|
498
|
+
unlinkSync(`${targetPath}.tmp`);
|
|
499
|
+
}
|
|
500
|
+
catch {
|
|
501
|
+
/* best-effort */
|
|
502
|
+
}
|
|
503
|
+
throw renameErr;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
447
506
|
}
|
|
448
507
|
process.stderr.write(`✓ Bridge workflow section written to ${targetPath} (v${PACKAGE_VERSION})\n`);
|
|
449
508
|
// Also write bridge-tools rules file alongside CLAUDE.md
|
|
@@ -493,50 +552,31 @@ Options:
|
|
|
493
552
|
const portIdx = argv.indexOf("--port");
|
|
494
553
|
const portArg = portIdx !== -1 ? argv[portIdx + 1] : undefined;
|
|
495
554
|
const lockDir = path.join(process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), ".claude"), "ide");
|
|
496
|
-
|
|
555
|
+
// Select a *running bridge* lock (isBridge:true + live PID) rather than the
|
|
556
|
+
// most-recently-touched lock in ~/.claude/ide, which can be an IDE-owned
|
|
557
|
+
// lock or a dead bridge.
|
|
558
|
+
let bridgeLock;
|
|
497
559
|
if (portArg) {
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
560
|
+
const port = Number(portArg);
|
|
561
|
+
bridgeLock = findAllLiveBridges().find((b) => b.port === port) ?? null;
|
|
562
|
+
if (!bridgeLock) {
|
|
563
|
+
process.stderr.write(`Error: No running bridge found for port ${portArg} (no lock file for that port, the lock is IDE-owned, or its process is not alive).\n`);
|
|
501
564
|
process.exit(1);
|
|
502
565
|
}
|
|
503
566
|
}
|
|
504
567
|
else {
|
|
505
|
-
|
|
506
|
-
let bestMtime = 0;
|
|
507
|
-
try {
|
|
508
|
-
for (const f of readdirSync(lockDir)) {
|
|
509
|
-
if (!f.endsWith(".lock"))
|
|
510
|
-
continue;
|
|
511
|
-
const full = path.join(lockDir, f);
|
|
512
|
-
const mtime = statSync(full).mtimeMs;
|
|
513
|
-
if (mtime > bestMtime) {
|
|
514
|
-
bestMtime = mtime;
|
|
515
|
-
lockFile = full;
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
catch {
|
|
520
|
-
// lock dir doesn't exist — handled below
|
|
521
|
-
}
|
|
568
|
+
bridgeLock = findBridgeLock();
|
|
522
569
|
}
|
|
523
|
-
if (!
|
|
524
|
-
process.stderr.write(`Error: No bridge lock file found in ${lockDir}\n`);
|
|
570
|
+
if (!bridgeLock) {
|
|
571
|
+
process.stderr.write(`Error: No running bridge lock file found in ${lockDir}\n`);
|
|
525
572
|
process.stderr.write("Make sure the bridge is running first, or pass --port <port>.\n");
|
|
526
573
|
process.exit(1);
|
|
527
574
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
if (!data.authToken) {
|
|
531
|
-
process.stderr.write(`Error: Lock file ${lockFile} has no authToken field\n`);
|
|
532
|
-
process.exit(1);
|
|
533
|
-
}
|
|
534
|
-
process.stdout.write(`${data.authToken}\n`);
|
|
535
|
-
}
|
|
536
|
-
catch {
|
|
537
|
-
process.stderr.write(`Error: Could not read lock file ${lockFile}\n`);
|
|
575
|
+
if (!bridgeLock.authToken) {
|
|
576
|
+
process.stderr.write(`Error: Bridge lock for port ${bridgeLock.port} has no authToken field\n`);
|
|
538
577
|
process.exit(1);
|
|
539
578
|
}
|
|
579
|
+
process.stdout.write(`${bridgeLock.authToken}\n`);
|
|
540
580
|
process.exit(0);
|
|
541
581
|
}
|
|
542
582
|
// Handle notify subcommand — called from CC hooks to fire bridge automation
|
|
@@ -558,63 +598,51 @@ if (process.argv[2] === "notify") {
|
|
|
558
598
|
}
|
|
559
599
|
const notifyRest = notifyArgv.slice(1);
|
|
560
600
|
const namedArgs = {};
|
|
561
|
-
|
|
601
|
+
// Iterate the full list (not length-1) so a trailing bare `--flag` with no
|
|
602
|
+
// following value is still recorded rather than silently dropped
|
|
603
|
+
// (cli-commands-5). A flag whose next token is another `--flag` (or which is
|
|
604
|
+
// last) is treated as an empty-valued flag, matching the existing `?? ""`.
|
|
605
|
+
for (let i = 0; i < notifyRest.length; i++) {
|
|
562
606
|
const arg = notifyRest[i];
|
|
563
607
|
if (arg?.startsWith("--")) {
|
|
564
608
|
const key = arg.slice(2);
|
|
565
|
-
const
|
|
566
|
-
|
|
567
|
-
|
|
609
|
+
const next = notifyRest[i + 1];
|
|
610
|
+
if (next !== undefined && !next.startsWith("--")) {
|
|
611
|
+
namedArgs[key] = next;
|
|
612
|
+
i++;
|
|
613
|
+
}
|
|
614
|
+
else {
|
|
615
|
+
namedArgs[key] = "";
|
|
616
|
+
}
|
|
568
617
|
}
|
|
569
618
|
}
|
|
570
619
|
const notifyLockDir = path.join(process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), ".claude"), "ide");
|
|
571
|
-
|
|
620
|
+
// Select a *running bridge* lock (isBridge:true + live PID) rather than the
|
|
621
|
+
// most-recently-touched lock, which can be an IDE-owned or dead-bridge lock.
|
|
572
622
|
let notifyPort;
|
|
623
|
+
let notifyToken;
|
|
573
624
|
if (namedArgs.port) {
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
if (!
|
|
577
|
-
process.stderr.write(`Error: No
|
|
625
|
+
const port = Number(namedArgs.port);
|
|
626
|
+
const lock = findAllLiveBridges().find((b) => b.port === port);
|
|
627
|
+
if (!lock) {
|
|
628
|
+
process.stderr.write(`Error: No running bridge found for port ${namedArgs.port} (no lock for that port, the lock is IDE-owned, or its process is not alive).\n`);
|
|
578
629
|
process.exit(1);
|
|
579
630
|
}
|
|
631
|
+
notifyPort = lock.port;
|
|
632
|
+
notifyToken = lock.authToken;
|
|
580
633
|
}
|
|
581
634
|
else {
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
continue;
|
|
587
|
-
const full = path.join(notifyLockDir, f);
|
|
588
|
-
const mtime = statSync(full).mtimeMs;
|
|
589
|
-
if (mtime > bestMtime) {
|
|
590
|
-
bestMtime = mtime;
|
|
591
|
-
notifyLockFile = full;
|
|
592
|
-
notifyPort = Number(path.basename(f, ".lock"));
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
catch {
|
|
597
|
-
// lock dir doesn't exist
|
|
635
|
+
const lock = findBridgeLock();
|
|
636
|
+
if (lock) {
|
|
637
|
+
notifyPort = lock.port;
|
|
638
|
+
notifyToken = lock.authToken;
|
|
598
639
|
}
|
|
599
640
|
}
|
|
600
|
-
if (!
|
|
601
|
-
process.stderr.write(`Error: No bridge lock file found in ${notifyLockDir}\n`);
|
|
641
|
+
if (!notifyPort || !notifyToken) {
|
|
642
|
+
process.stderr.write(`Error: No running bridge lock file found in ${notifyLockDir}\n`);
|
|
602
643
|
process.stderr.write("Make sure the bridge is running first (claude-ide-bridge --watch ...).\n");
|
|
603
644
|
process.exit(1);
|
|
604
645
|
}
|
|
605
|
-
let notifyToken;
|
|
606
|
-
try {
|
|
607
|
-
const data = JSON.parse(readFileSync(notifyLockFile, "utf-8"));
|
|
608
|
-
if (!data.authToken) {
|
|
609
|
-
process.stderr.write(`Error: Lock file has no authToken field\n`);
|
|
610
|
-
process.exit(1);
|
|
611
|
-
}
|
|
612
|
-
notifyToken = data.authToken;
|
|
613
|
-
}
|
|
614
|
-
catch {
|
|
615
|
-
process.stderr.write(`Error: Could not read lock file ${notifyLockFile}\n`);
|
|
616
|
-
process.exit(1);
|
|
617
|
-
}
|
|
618
646
|
try {
|
|
619
647
|
const resp = await fetch(`http://127.0.0.1:${notifyPort}/notify`, {
|
|
620
648
|
method: "POST",
|
|
@@ -1171,18 +1199,41 @@ if (process.argv[2] === "recipe" && process.argv[3] === "run") {
|
|
|
1171
1199
|
const { findBridgeLock } = await import("./bridgeLockDiscovery.js");
|
|
1172
1200
|
const lock = localFlag ? null : findBridgeLock();
|
|
1173
1201
|
if (lock && !dryRun && !step && !explicitFile && !attemptId) {
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
}
|
|
1184
|
-
|
|
1185
|
-
|
|
1202
|
+
// 30s per-request deadline — the bridge can pass the findBridgeLock
|
|
1203
|
+
// PID check yet be wedged on HTTP. Without this abort the fetch
|
|
1204
|
+
// blocks forever. Mirrors the AbortController pattern used by every
|
|
1205
|
+
// sibling bridge call (halts, judgments, recipe doctor, kill-switch).
|
|
1206
|
+
const controller = new AbortController();
|
|
1207
|
+
const timer = setTimeout(() => controller.abort(), 30_000);
|
|
1208
|
+
let res;
|
|
1209
|
+
let body;
|
|
1210
|
+
try {
|
|
1211
|
+
res = await fetch(`http://127.0.0.1:${lock.port}/recipes/run`, {
|
|
1212
|
+
method: "POST",
|
|
1213
|
+
headers: {
|
|
1214
|
+
Authorization: `Bearer ${lock.authToken}`,
|
|
1215
|
+
"Content-Type": "application/json",
|
|
1216
|
+
},
|
|
1217
|
+
body: JSON.stringify({
|
|
1218
|
+
name: recipeArg,
|
|
1219
|
+
...(seedVars ? { vars: seedVars } : {}),
|
|
1220
|
+
}),
|
|
1221
|
+
signal: controller.signal,
|
|
1222
|
+
});
|
|
1223
|
+
body = (await res.json());
|
|
1224
|
+
}
|
|
1225
|
+
catch (err) {
|
|
1226
|
+
const aborted = err instanceof Error &&
|
|
1227
|
+
(err.name === "AbortError" || controller.signal.aborted);
|
|
1228
|
+
process.stderr.write(aborted
|
|
1229
|
+
? `Error: bridge on port ${lock.port} did not respond within 30s — it may be wedged. Restart the bridge or re-run with --local.\n`
|
|
1230
|
+
: `Error: failed to reach bridge on port ${lock.port}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
1231
|
+
process.exit(1);
|
|
1232
|
+
return;
|
|
1233
|
+
}
|
|
1234
|
+
finally {
|
|
1235
|
+
clearTimeout(timer);
|
|
1236
|
+
}
|
|
1186
1237
|
if (!body.ok) {
|
|
1187
1238
|
// Fall through to local YAML runner if bridge doesn't know the recipe.
|
|
1188
1239
|
if (!(body.error ?? "").includes("not found")) {
|
|
@@ -1442,6 +1493,8 @@ if (process.argv[2] === "traces" && process.argv[3] === "export") {
|
|
|
1442
1493
|
let output;
|
|
1443
1494
|
let patchworkDir;
|
|
1444
1495
|
let activityDir;
|
|
1496
|
+
let mode;
|
|
1497
|
+
let passphrase;
|
|
1445
1498
|
for (let i = 0; i < args.length; i++) {
|
|
1446
1499
|
const a = args[i];
|
|
1447
1500
|
if (a === "--output" || a === "-o") {
|
|
@@ -1456,10 +1509,30 @@ if (process.argv[2] === "traces" && process.argv[3] === "export") {
|
|
|
1456
1509
|
activityDir = args[i + 1];
|
|
1457
1510
|
i++;
|
|
1458
1511
|
}
|
|
1512
|
+
else if (a === "--mode") {
|
|
1513
|
+
const m = args[i + 1];
|
|
1514
|
+
if (m !== "public" && m !== "keyed") {
|
|
1515
|
+
process.stderr.write(`Error: --mode must be "public" or "keyed" (got: ${m})\n`);
|
|
1516
|
+
process.exit(1);
|
|
1517
|
+
}
|
|
1518
|
+
mode = m;
|
|
1519
|
+
i++;
|
|
1520
|
+
}
|
|
1521
|
+
else if (a === "--passphrase") {
|
|
1522
|
+
passphrase = args[i + 1];
|
|
1523
|
+
i++;
|
|
1524
|
+
}
|
|
1459
1525
|
else if (a === "--help" || a === "-h") {
|
|
1460
|
-
process.stdout.write("patchwork traces export [--output <path>] [--
|
|
1526
|
+
process.stdout.write("patchwork traces export [--output <path>] [--mode public|keyed]\n" +
|
|
1527
|
+
" [--passphrase <phrase>]\n" +
|
|
1528
|
+
" [--patchwork-dir <dir>] [--activity-dir <dir>]\n\n" +
|
|
1461
1529
|
"Bundles ~/.patchwork/{runs,decision_traces,commit_issue_links}.jsonl\n" +
|
|
1462
1530
|
"and ~/.claude/ide/activity-*.jsonl into a single gzipped JSONL file.\n\n" +
|
|
1531
|
+
"Modes:\n" +
|
|
1532
|
+
" public (default) Plain gzip bundle (.jsonl.gz). Anyone with the file\n" +
|
|
1533
|
+
" can read it.\n" +
|
|
1534
|
+
" keyed AES-256-GCM encrypted bundle (.enc). Requires --passphrase;\n" +
|
|
1535
|
+
" auto-detected and decrypted by `traces import --passphrase`.\n\n" +
|
|
1463
1536
|
"Output is a manifest line followed by one envelope per row:\n" +
|
|
1464
1537
|
' {"type":"manifest", ...}\n' +
|
|
1465
1538
|
' {"source":"runs", "entry":{...}}\n' +
|
|
@@ -1469,11 +1542,17 @@ if (process.argv[2] === "traces" && process.argv[3] === "export") {
|
|
|
1469
1542
|
process.exit(0);
|
|
1470
1543
|
}
|
|
1471
1544
|
}
|
|
1545
|
+
if (mode === "keyed" && !passphrase) {
|
|
1546
|
+
process.stderr.write("Error: --mode keyed requires --passphrase <phrase>\n");
|
|
1547
|
+
process.exit(1);
|
|
1548
|
+
}
|
|
1472
1549
|
const { runTracesExport } = await import("./commands/tracesExport.js");
|
|
1473
1550
|
const result = await runTracesExport({
|
|
1474
1551
|
...(output !== undefined && { output }),
|
|
1475
1552
|
...(patchworkDir !== undefined && { patchworkDir }),
|
|
1476
1553
|
...(activityDir !== undefined && { activityDir }),
|
|
1554
|
+
...(mode !== undefined && { mode }),
|
|
1555
|
+
...(passphrase !== undefined && { passphrase }),
|
|
1477
1556
|
});
|
|
1478
1557
|
process.stdout.write(` ✓ Wrote ${result.outputPath}\n`);
|
|
1479
1558
|
process.stdout.write(` ${result.totalCount} rows from ${result.files.length} file${result.files.length === 1 ? "" : "s"} (${result.totalBytes} bytes read)\n`);
|
|
@@ -1569,6 +1648,17 @@ if (process.argv[2] === "traces" && process.argv[3] === "import") {
|
|
|
1569
1648
|
writeFileSync(tmp, plain, { mode: 0o600 });
|
|
1570
1649
|
input = tmp;
|
|
1571
1650
|
process.stderr.write("Decryption succeeded.\n");
|
|
1651
|
+
// Ensure tmp is removed after import regardless of success/failure.
|
|
1652
|
+
// On Windows mode:0o600 is a no-op (NTFS doesn't honour POSIX bits),
|
|
1653
|
+
// so the plaintext would persist in %TEMP% until the next reboot.
|
|
1654
|
+
process.once("exit", () => {
|
|
1655
|
+
try {
|
|
1656
|
+
unlinkSync(tmp);
|
|
1657
|
+
}
|
|
1658
|
+
catch {
|
|
1659
|
+
/* best-effort */
|
|
1660
|
+
}
|
|
1661
|
+
});
|
|
1572
1662
|
}
|
|
1573
1663
|
}
|
|
1574
1664
|
const { runTracesImport } = await import("./commands/tracesImport.js");
|
|
@@ -1722,7 +1812,7 @@ if (process.argv[2] === "kill-switch") {
|
|
|
1722
1812
|
? ` [env-locked: ${j.lockedReason ?? "yes"}]`
|
|
1723
1813
|
: "";
|
|
1724
1814
|
const wsLabel = lock.workspace
|
|
1725
|
-
?
|
|
1815
|
+
? path.join(path.basename(path.dirname(lock.workspace)), path.basename(lock.workspace))
|
|
1726
1816
|
: `pid=${lock.pid}`;
|
|
1727
1817
|
process.stdout.write(` ${engaged} port=${lock.port} ${wsLabel}${lockedSuffix}\n`);
|
|
1728
1818
|
}
|
|
@@ -1738,7 +1828,7 @@ if (process.argv[2] === "kill-switch") {
|
|
|
1738
1828
|
...(reason ? { reason } : {}),
|
|
1739
1829
|
});
|
|
1740
1830
|
const wsLabel = lock.workspace
|
|
1741
|
-
?
|
|
1831
|
+
? path.join(path.basename(path.dirname(lock.workspace)), path.basename(lock.workspace))
|
|
1742
1832
|
: `pid=${lock.pid}`;
|
|
1743
1833
|
if (result.status === 409) {
|
|
1744
1834
|
anyFailed = true;
|
|
@@ -1948,6 +2038,20 @@ if (process.argv[2] === "halts") {
|
|
|
1948
2038
|
}
|
|
1949
2039
|
})();
|
|
1950
2040
|
}
|
|
2041
|
+
// `patchwork connect` — connector front-door. Thin CLI→HTTP shim over the
|
|
2042
|
+
// bridge's `/connections/*` routes (list / OAuth auth / PAT connect / test /
|
|
2043
|
+
// disconnect). This is the command the 23 connector "Run: patchwork connect
|
|
2044
|
+
// <vendor>" error messages point at. Lock discovery reuses the same
|
|
2045
|
+
// isBridge:true + live-PID selector as `recipe run`, `quick-task`, etc.
|
|
2046
|
+
if (process.argv[2] === "connect") {
|
|
2047
|
+
(async () => {
|
|
2048
|
+
const { runConnect } = await import("./commands/connect.js");
|
|
2049
|
+
const { findBridgeLockForTask } = await import("./commands/task.js");
|
|
2050
|
+
await runConnect(process.argv.slice(3), {
|
|
2051
|
+
findBridgeLock: (port) => findBridgeLockForTask(port),
|
|
2052
|
+
});
|
|
2053
|
+
})();
|
|
2054
|
+
}
|
|
1951
2055
|
// `patchwork analytics` — manage the self-hosted telemetry collector config.
|
|
1952
2056
|
// Replaces the brittle "endpoint+secret in launchd plist" pattern with a
|
|
1953
2057
|
// proper config file the bridge reads at startup.
|
|
@@ -2415,6 +2519,134 @@ if (process.argv[2] === "recipe" && process.argv[3] === "preflight") {
|
|
|
2415
2519
|
}
|
|
2416
2520
|
})();
|
|
2417
2521
|
}
|
|
2522
|
+
// `recipe simulate <name|file>` — What-If Preview. Statically simulates a
|
|
2523
|
+
// recipe before it is enabled: projected actions, side-effect taxonomy,
|
|
2524
|
+
// blast-radius risk, tier-only approval projection (honestly flagged as NOT
|
|
2525
|
+
// gated on recipe steps today), low-confidence cost, and undetermined
|
|
2526
|
+
// conditional branches. Executes nothing. See runRecipeSimulate in
|
|
2527
|
+
// commands/recipe.ts.
|
|
2528
|
+
if (process.argv[2] === "recipe" && process.argv[3] === "simulate") {
|
|
2529
|
+
const args = process.argv.slice(4);
|
|
2530
|
+
const usage = "Usage: patchwork recipe simulate <name|file.yaml> [--json] [--step <id>] [--var k=v]\n\n" +
|
|
2531
|
+
"What-If Preview: statically simulate a recipe before enabling it.\n" +
|
|
2532
|
+
"Shows projected actions, side effects, risk, approvals (tier-only),\n" +
|
|
2533
|
+
"cost (low-confidence), and undetermined branches. Executes nothing.\n";
|
|
2534
|
+
let json = false;
|
|
2535
|
+
let step;
|
|
2536
|
+
const vars = {};
|
|
2537
|
+
let target;
|
|
2538
|
+
for (let i = 0; i < args.length; i++) {
|
|
2539
|
+
const arg = args[i];
|
|
2540
|
+
if (arg === undefined)
|
|
2541
|
+
continue;
|
|
2542
|
+
if (arg === "--json") {
|
|
2543
|
+
json = true;
|
|
2544
|
+
continue;
|
|
2545
|
+
}
|
|
2546
|
+
if (arg === "--step" || arg.startsWith("--step=")) {
|
|
2547
|
+
const value = arg === "--step" ? args[++i] : arg.slice("--step=".length);
|
|
2548
|
+
if (!value) {
|
|
2549
|
+
process.stderr.write(`Error: --step requires a value\n${usage}`);
|
|
2550
|
+
process.exit(1);
|
|
2551
|
+
}
|
|
2552
|
+
step = value;
|
|
2553
|
+
continue;
|
|
2554
|
+
}
|
|
2555
|
+
if (arg === "--var" || arg.startsWith("--var=")) {
|
|
2556
|
+
const value = arg === "--var" ? args[++i] : arg.slice("--var=".length);
|
|
2557
|
+
if (!value?.includes("=")) {
|
|
2558
|
+
process.stderr.write(`Error: --var requires k=v\n${usage}`);
|
|
2559
|
+
process.exit(1);
|
|
2560
|
+
}
|
|
2561
|
+
const idx = value.indexOf("=");
|
|
2562
|
+
vars[value.slice(0, idx)] = value.slice(idx + 1);
|
|
2563
|
+
continue;
|
|
2564
|
+
}
|
|
2565
|
+
if (!arg.startsWith("--")) {
|
|
2566
|
+
target = arg;
|
|
2567
|
+
}
|
|
2568
|
+
}
|
|
2569
|
+
if (!target) {
|
|
2570
|
+
process.stderr.write(usage);
|
|
2571
|
+
process.exit(1);
|
|
2572
|
+
}
|
|
2573
|
+
(async () => {
|
|
2574
|
+
try {
|
|
2575
|
+
const { runRecipeSimulate } = await import("./commands/recipe.js");
|
|
2576
|
+
const report = await runRecipeSimulate(target, {
|
|
2577
|
+
...(step ? { step } : {}),
|
|
2578
|
+
...(Object.keys(vars).length ? { vars } : {}),
|
|
2579
|
+
});
|
|
2580
|
+
if (json) {
|
|
2581
|
+
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
2582
|
+
process.exit(0);
|
|
2583
|
+
}
|
|
2584
|
+
const out = process.stdout;
|
|
2585
|
+
const mark = (tier) => tier === "high" ? "●" : tier === "medium" ? "◐" : "○";
|
|
2586
|
+
const fidelityLabel = report.fidelity === "mocked"
|
|
2587
|
+
? `mocked fidelity, ${report.sampleRuns ?? 0} prior run(s)`
|
|
2588
|
+
: `${report.fidelity} fidelity`;
|
|
2589
|
+
out.write(`\n What-If Preview — ${report.recipe} (${report.topology}, ${fidelityLabel})\n`);
|
|
2590
|
+
out.write(` Trigger: ${report.triggerType} · ${report.summary.totalSteps} step(s)\n\n`);
|
|
2591
|
+
out.write(` Risk: ${report.risk.tier.toUpperCase()} (score ${report.risk.score}/100)\n`);
|
|
2592
|
+
const c = report.risk.components;
|
|
2593
|
+
out.write(` high:${c.highSteps} medium:${c.mediumSteps} writes:${c.writeSteps} connector-writes:${c.connectorWriteSteps} http:${c.externalHttpSteps} unresolved:${c.unresolvedSteps}\n\n`);
|
|
2594
|
+
out.write(" Actions:\n");
|
|
2595
|
+
for (const s of report.steps) {
|
|
2596
|
+
const tool = s.tool ?? s.type;
|
|
2597
|
+
const when = s.condition ? ` when: ${s.condition}` : "";
|
|
2598
|
+
out.write(` ${mark(s.effectiveRisk)} ${s.id} ${tool} [${s.sideEffect}]${when}\n`);
|
|
2599
|
+
}
|
|
2600
|
+
out.write("\n");
|
|
2601
|
+
const ns = report.summary.connectorNamespaces;
|
|
2602
|
+
out.write(` Side effects: ${report.summary.writeSteps} write(s) · ${report.summary.connectorSteps} connector call(s)${ns.length ? ` (${ns.join(", ")})` : ""} · ${report.summary.agentSteps} agent step(s)\n`);
|
|
2603
|
+
const gating = report.approvals.projected.filter((a) => a.wouldRequireApproval).length;
|
|
2604
|
+
out.write(` Approvals: ${gating} step(s) would gate IF recipe steps were gated — they are NOT gated today\n`);
|
|
2605
|
+
{
|
|
2606
|
+
const c = report.cost;
|
|
2607
|
+
const toks = typeof c.estInputTokens === "number"
|
|
2608
|
+
? ` ~${c.estInputTokens} in / ${c.estOutputTokens ?? 0} out tokens`
|
|
2609
|
+
: typeof c.estPromptTokens === "number"
|
|
2610
|
+
? ` ~${c.estPromptTokens} input tokens`
|
|
2611
|
+
: "";
|
|
2612
|
+
const usd = typeof c.usd === "number"
|
|
2613
|
+
? ` ≈$${c.usd.toFixed(4)}${typeof c.minUsd === "number" && typeof c.maxUsd === "number"
|
|
2614
|
+
? ` ($${c.minUsd.toFixed(4)}–$${c.maxUsd.toFixed(4)})`
|
|
2615
|
+
: ""}`
|
|
2616
|
+
: "";
|
|
2617
|
+
const conf = c.confidence ? `/${c.confidence}` : "";
|
|
2618
|
+
out.write(` Cost [${c.basis}${conf}]:${toks}${usd}\n`);
|
|
2619
|
+
out.write(` ${c.note}\n`);
|
|
2620
|
+
}
|
|
2621
|
+
if (report.branches.length > 0) {
|
|
2622
|
+
if (report.fidelity === "mocked") {
|
|
2623
|
+
const taken = report.branches.filter((b) => b.outcome === "taken").length;
|
|
2624
|
+
const skipped = report.branches.filter((b) => b.outcome === "skipped").length;
|
|
2625
|
+
const undet = report.branches.filter((b) => b.outcome === "undetermined").length;
|
|
2626
|
+
out.write(` Branches: ${report.branches.length} conditional — ${taken} taken, ${skipped} skipped, ${undet} undetermined\n`);
|
|
2627
|
+
for (const b of report.branches) {
|
|
2628
|
+
out.write(` ${b.outcome.padEnd(12)} ${b.stepId} ${b.condition}\n`);
|
|
2629
|
+
}
|
|
2630
|
+
}
|
|
2631
|
+
else {
|
|
2632
|
+
out.write(` Branches: ${report.branches.length} conditional — undetermined (resolve in a later sandbox phase)\n`);
|
|
2633
|
+
}
|
|
2634
|
+
}
|
|
2635
|
+
if (report.lint.errors.length > 0 || report.lint.warnings.length > 0) {
|
|
2636
|
+
out.write(` Lint: ${report.lint.errors.length} error(s), ${report.lint.warnings.length} warning(s)\n`);
|
|
2637
|
+
}
|
|
2638
|
+
out.write("\n Notes:\n");
|
|
2639
|
+
for (const n of report.notes)
|
|
2640
|
+
out.write(` • ${n}\n`);
|
|
2641
|
+
out.write("\n");
|
|
2642
|
+
process.exit(0);
|
|
2643
|
+
}
|
|
2644
|
+
catch (err) {
|
|
2645
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
2646
|
+
process.exit(1);
|
|
2647
|
+
}
|
|
2648
|
+
})();
|
|
2649
|
+
}
|
|
2418
2650
|
// `recipe doctor <name|file>` — one-screen "why is this recipe unhealthy
|
|
2419
2651
|
// + how do I fix it" diagnosis. Composes the static preflight check with
|
|
2420
2652
|
// the recipe-scoped runtime halt summary from a live bridge (fail-soft:
|
|
@@ -2473,7 +2705,42 @@ if (process.argv[2] === "recipe" && process.argv[3] === "doctor") {
|
|
|
2473
2705
|
}
|
|
2474
2706
|
return null;
|
|
2475
2707
|
};
|
|
2476
|
-
|
|
2708
|
+
// Connector auth fetcher: walk live bridges, GET /connections to read
|
|
2709
|
+
// each connector's status. Returns null when no bridge is reachable so
|
|
2710
|
+
// doctor degrades to static-only (lists required connectors, no auth).
|
|
2711
|
+
const fetchConnections = localOnly
|
|
2712
|
+
? undefined
|
|
2713
|
+
: async () => {
|
|
2714
|
+
const { findAllLiveBridges } = await import("./bridgeLockDiscovery.js");
|
|
2715
|
+
const liveLocks = findAllLiveBridges();
|
|
2716
|
+
if (liveLocks.length === 0)
|
|
2717
|
+
return null;
|
|
2718
|
+
for (const lock of liveLocks) {
|
|
2719
|
+
const controller = new AbortController();
|
|
2720
|
+
const timer = setTimeout(() => controller.abort(), 10_000);
|
|
2721
|
+
try {
|
|
2722
|
+
const res = await fetch(`http://127.0.0.1:${lock.port}/connections`, {
|
|
2723
|
+
headers: { Authorization: `Bearer ${lock.authToken}` },
|
|
2724
|
+
signal: controller.signal,
|
|
2725
|
+
});
|
|
2726
|
+
if (res.ok) {
|
|
2727
|
+
const body = (await res.json());
|
|
2728
|
+
return body.connectors ?? [];
|
|
2729
|
+
}
|
|
2730
|
+
}
|
|
2731
|
+
catch {
|
|
2732
|
+
/* unreachable lock — try next */
|
|
2733
|
+
}
|
|
2734
|
+
finally {
|
|
2735
|
+
clearTimeout(timer);
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
return null;
|
|
2739
|
+
};
|
|
2740
|
+
const result = await runRecipeDoctor(ref, {
|
|
2741
|
+
fetchHalts,
|
|
2742
|
+
fetchConnections,
|
|
2743
|
+
});
|
|
2477
2744
|
if (wantJson) {
|
|
2478
2745
|
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
2479
2746
|
}
|
|
@@ -2943,9 +3210,19 @@ Steps performed:
|
|
|
2943
3210
|
// claude -p spawns the stdio command via Node's child_process, which
|
|
2944
3211
|
// can't resolve a bare `.cmd` shim on Windows. Record the `.cmd` form
|
|
2945
3212
|
// on win32 so the bridge binary is findable by the spawned process.
|
|
3213
|
+
//
|
|
3214
|
+
// This global entry stays even though the project also gets a
|
|
3215
|
+
// `patchwork` MCP server (project .mcp.json / project config) covering
|
|
3216
|
+
// this same workspace — VS Code/Windsurf/Cursor launches inject
|
|
3217
|
+
// --mcp-config, which overrides any project .mcp.json entirely, so
|
|
3218
|
+
// this ~/.claude.json entry is the ONLY way bridge tools are wired
|
|
3219
|
+
// under an IDE launch. Pin --workspace so that when multiple bridges
|
|
3220
|
+
// are running (this one plus others elsewhere), the workspace-aware
|
|
3221
|
+
// lock discovery in mcp-stdio-shim.cjs picks THIS project's lock
|
|
3222
|
+
// instead of falling back to cwd-based guessing or the wrong bridge.
|
|
2946
3223
|
mcpServers["claude-ide-bridge"] = {
|
|
2947
3224
|
command: ensureCmdShim("claude-ide-bridge"),
|
|
2948
|
-
args: ["shim"],
|
|
3225
|
+
args: ["shim", "--workspace", workspace],
|
|
2949
3226
|
type: "stdio",
|
|
2950
3227
|
};
|
|
2951
3228
|
claudeJson.mcpServers = mcpServers;
|
|
@@ -2956,10 +3233,21 @@ Steps performed:
|
|
|
2956
3233
|
}
|
|
2957
3234
|
}
|
|
2958
3235
|
catch {
|
|
2959
|
-
process.stderr.write(` [warn] Could not update ${claudeJsonAbs} — add manually:\n { "mcpServers": { "claude-ide-bridge": { "command": "claude-ide-bridge", "args": ["shim"] } } }\n\n`);
|
|
3236
|
+
process.stderr.write(` [warn] Could not update ${claudeJsonAbs} — add manually:\n { "mcpServers": { "claude-ide-bridge": { "command": "claude-ide-bridge", "args": ["shim", "--workspace", "${workspace}"] } } }\n\n`);
|
|
2960
3237
|
}
|
|
2961
3238
|
// Step 3b: Wire CC hooks in ~/.claude/settings.json
|
|
2962
|
-
const
|
|
3239
|
+
const ccConfigDir = process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), ".claude");
|
|
3240
|
+
const ccSettingsPath = path.join(ccConfigDir, "settings.json");
|
|
3241
|
+
// On a truly fresh machine ~/.claude/ may not exist yet. Every settings.json
|
|
3242
|
+
// writer below (CC hooks, the IDE-skip env step, the PreToolUse hook) does an
|
|
3243
|
+
// atomic write that needs the parent dir to exist — without this, all three
|
|
3244
|
+
// silently fail with ENOENT and the user's onboarding state is never persisted.
|
|
3245
|
+
try {
|
|
3246
|
+
mkdirSync(ccConfigDir, { recursive: true });
|
|
3247
|
+
}
|
|
3248
|
+
catch {
|
|
3249
|
+
// best-effort — the individual writers below surface their own warnings
|
|
3250
|
+
}
|
|
2963
3251
|
const CC_HOOK_NOTIFY_CMDS = {
|
|
2964
3252
|
PreCompact: "claude-ide-bridge notify PreCompact",
|
|
2965
3253
|
PostCompact: "claude-ide-bridge notify PostCompact",
|
|
@@ -3021,6 +3309,33 @@ Steps performed:
|
|
|
3021
3309
|
process.stderr.write(` [warn] Could not update ${ccSettingsPath} — add CC hook entries manually.\n` +
|
|
3022
3310
|
` See CLAUDE.md Automation Policy section for the settings.json snippet.\n\n`);
|
|
3023
3311
|
}
|
|
3312
|
+
// Step 3c: Persist CLAUDE_CODE_IDE_SKIP_VALID_CHECK into the settings.json
|
|
3313
|
+
// `env` block so `claude --ide` connects without the user prefixing the var
|
|
3314
|
+
// on every launch. The bridge writes its own ~/.claude/ide/<port>.lock, but
|
|
3315
|
+
// Claude Code's `--ide` "valid check" is an in-IDE-terminal detection that a
|
|
3316
|
+
// standalone bridge can't satisfy by lock-file shape alone — so we set the
|
|
3317
|
+
// skip flag where Patchwork already manages CC config (NOT the shell rc).
|
|
3318
|
+
try {
|
|
3319
|
+
const { registerSkipIdeValidCheckEnv } = await import("./settingsEnv.js");
|
|
3320
|
+
const envResult = registerSkipIdeValidCheckEnv(ccSettingsPath);
|
|
3321
|
+
if (envResult.action === "added") {
|
|
3322
|
+
process.stderr.write(` ✓ IDE connect — set CLAUDE_CODE_IDE_SKIP_VALID_CHECK=true in ${ccSettingsPath}\n You can now run a plain \`claude --ide\` — no env-var prefix needed.\n\n`);
|
|
3323
|
+
}
|
|
3324
|
+
else if (envResult.action === "already-present") {
|
|
3325
|
+
process.stderr.write(` ✓ IDE connect — CLAUDE_CODE_IDE_SKIP_VALID_CHECK already set in ${ccSettingsPath}\n\n`);
|
|
3326
|
+
}
|
|
3327
|
+
else if (envResult.action === "preserved-user-value") {
|
|
3328
|
+
process.stderr.write(` [info] IDE connect — left your CLAUDE_CODE_IDE_SKIP_VALID_CHECK="${envResult.existingValue}" untouched.\n` +
|
|
3329
|
+
` If \`claude --ide\` won't connect, set it to "true" in ${ccSettingsPath}.\n\n`);
|
|
3330
|
+
}
|
|
3331
|
+
else {
|
|
3332
|
+
process.stderr.write(` [warn] IDE connect — could not set CLAUDE_CODE_IDE_SKIP_VALID_CHECK (${envResult.error}).\n` +
|
|
3333
|
+
` Add { "env": { "CLAUDE_CODE_IDE_SKIP_VALID_CHECK": "true" } } to ${ccSettingsPath} manually.\n\n`);
|
|
3334
|
+
}
|
|
3335
|
+
}
|
|
3336
|
+
catch (err) {
|
|
3337
|
+
process.stderr.write(` [warn] IDE connect — ${err instanceof Error ? err.message : String(err)}\n\n`);
|
|
3338
|
+
}
|
|
3024
3339
|
// Patchwork: register PreToolUse approval hook so the dashboard can
|
|
3025
3340
|
// approve/reject CC tool calls in real time.
|
|
3026
3341
|
try {
|
|
@@ -3077,8 +3392,14 @@ Steps performed:
|
|
|
3077
3392
|
"Next steps:\n" +
|
|
3078
3393
|
` 1. Start the bridge: claude-ide-bridge --watch (runs in this workspace)\n` +
|
|
3079
3394
|
" 2. Restart your IDE once so it picks up the new extension + MCP config.\n" +
|
|
3080
|
-
" 3.
|
|
3081
|
-
"
|
|
3395
|
+
" 3. Connect Claude Code: claude --ide (the IDE-detection skip flag is\n" +
|
|
3396
|
+
" already set in ~/.claude/settings.json — no env-var prefix needed).\n" +
|
|
3397
|
+
" 4. Type /mcp — the claude-ide-bridge server should show as connected,\n" +
|
|
3398
|
+
" then /ide to see live workspace state (open editors, diagnostics, git).\n" +
|
|
3399
|
+
" 5. Run your first recipe (no connectors needed — local git + a brief):\n" +
|
|
3400
|
+
" patchwork recipe run daily-status --local\n" +
|
|
3401
|
+
" Writes a morning brief to ~/.patchwork/inbox/. Try --dry-run first to\n" +
|
|
3402
|
+
" see the plan without spending tokens.\n\n");
|
|
3082
3403
|
if (inWorktree) {
|
|
3083
3404
|
process.stdout.write("⚠ This workspace is a linked git worktree. If this is a Cowork session,\n" +
|
|
3084
3405
|
" bridge MCP tools are unreachable from inside Cowork itself — run\n" +
|
|
@@ -3126,11 +3447,19 @@ Steps performed:
|
|
|
3126
3447
|
? fallbackDocs
|
|
3127
3448
|
: null;
|
|
3128
3449
|
if (target) {
|
|
3129
|
-
// Use execFile with argv
|
|
3130
|
-
//
|
|
3131
|
-
//
|
|
3132
|
-
|
|
3133
|
-
|
|
3450
|
+
// Use execFile with argv — exec(`code "${target}"`) was shell-evaluated
|
|
3451
|
+
// and could be injected via `--workspace '"; ...'`. Audit 2026-05-17.
|
|
3452
|
+
// Windows: code is a .cmd shim; shell:true lets cmd.exe resolve it
|
|
3453
|
+
// without needing ensureCmdShim (args-as-array prevents injection).
|
|
3454
|
+
// Wrap in try-catch: in Node 22 on Windows, spawn errors can throw
|
|
3455
|
+
// synchronously before the callback is registered.
|
|
3456
|
+
try {
|
|
3457
|
+
const { execFile } = await import("node:child_process");
|
|
3458
|
+
execFile("code", [target], { timeout: 3000, shell: process.platform === "win32" }, () => { });
|
|
3459
|
+
}
|
|
3460
|
+
catch {
|
|
3461
|
+
// best-effort — VS Code may not be installed
|
|
3462
|
+
}
|
|
3134
3463
|
}
|
|
3135
3464
|
}
|
|
3136
3465
|
// Analytics opt-in prompt — only ask once; skip if preference already set
|
|
@@ -3298,12 +3627,25 @@ Options:
|
|
|
3298
3627
|
process.exit(0);
|
|
3299
3628
|
}
|
|
3300
3629
|
const portIdx = argv.indexOf("--port");
|
|
3301
|
-
|
|
3630
|
+
// --port wins over PATCHWORK_BRIDGE_PORT wins over workspace-aware discovery.
|
|
3631
|
+
const portArg = portIdx !== -1 ? argv[portIdx + 1] : process.env.PATCHWORK_BRIDGE_PORT;
|
|
3302
3632
|
const jsonFlag = argv.includes("--json");
|
|
3303
3633
|
const lockDir = path.join(process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), ".claude"), "ide");
|
|
3304
3634
|
let lockFile;
|
|
3305
3635
|
let lockPort;
|
|
3306
3636
|
if (portArg) {
|
|
3637
|
+
// Validate before path.join — path.join does not block traversal, so an
|
|
3638
|
+
// unsanitized `--port ../../../etc/passwd` would read an arbitrary
|
|
3639
|
+
// `*.lock` file outside lockDir and leak its JSON contents
|
|
3640
|
+
// (cli-commands-1).
|
|
3641
|
+
const portNum = Number(portArg);
|
|
3642
|
+
if (!/^\d{1,5}$/.test(portArg) ||
|
|
3643
|
+
!Number.isInteger(portNum) ||
|
|
3644
|
+
portNum < 1 ||
|
|
3645
|
+
portNum > 65535) {
|
|
3646
|
+
process.stderr.write("Error: --port must be a valid port number\n");
|
|
3647
|
+
process.exit(1);
|
|
3648
|
+
}
|
|
3307
3649
|
lockFile = path.join(lockDir, `${portArg}.lock`);
|
|
3308
3650
|
lockPort = portArg;
|
|
3309
3651
|
if (!existsSync(lockFile)) {
|
|
@@ -3312,22 +3654,17 @@ Options:
|
|
|
3312
3654
|
}
|
|
3313
3655
|
}
|
|
3314
3656
|
else {
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
}
|
|
3327
|
-
}
|
|
3328
|
-
}
|
|
3329
|
-
catch {
|
|
3330
|
-
// lock dir doesn't exist
|
|
3657
|
+
// Workspace-aware, isBridge-filtered, live-PID-filtered discovery — the
|
|
3658
|
+
// same helper the MCP shim and task runner use (#1052/#1054 fixed the
|
|
3659
|
+
// shim's copy of this bug; this was a separate, unfixed copy that instead
|
|
3660
|
+
// picked the newest-mtime `.lock` file in the directory with no isBridge
|
|
3661
|
+
// check, so an unrelated editor's IDE-owned lock — or a stale bridge from
|
|
3662
|
+
// a different workspace — could shadow the real bridge's status).
|
|
3663
|
+
const { findBridgeLock } = await import("./bridgeLockDiscovery.js");
|
|
3664
|
+
const found = findBridgeLock({ lockDir });
|
|
3665
|
+
if (found) {
|
|
3666
|
+
lockFile = path.join(lockDir, `${found.port}.lock`);
|
|
3667
|
+
lockPort = String(found.port);
|
|
3331
3668
|
}
|
|
3332
3669
|
}
|
|
3333
3670
|
if (!lockFile) {
|
|
@@ -3510,6 +3847,206 @@ if (process.argv[2] === "shadow-scan") {
|
|
|
3510
3847
|
}
|
|
3511
3848
|
})();
|
|
3512
3849
|
}
|
|
3850
|
+
// Handle workers subcommand — read-only worker trust-dial (shadow). Replays the
|
|
3851
|
+
// recipe run log + gate decision log through the (worker × action-class) ramp
|
|
3852
|
+
// and prints the dial + a "ramp would vs gate did" comparison. Changes nothing.
|
|
3853
|
+
if (process.argv[2] === "workers") {
|
|
3854
|
+
const args = process.argv.slice(3);
|
|
3855
|
+
const sub = args[0];
|
|
3856
|
+
if ((sub !== "shadow" && sub !== "backtest") ||
|
|
3857
|
+
args.includes("--help") ||
|
|
3858
|
+
args.includes("-h")) {
|
|
3859
|
+
process.stdout.write("Usage: patchwork workers <shadow|backtest> [--workers-dir <path>]\n\n" +
|
|
3860
|
+
" shadow Read-only worker trust dial: replays ~/.patchwork/runs.jsonl\n" +
|
|
3861
|
+
" + the gate decision log through the (worker × action-class)\n" +
|
|
3862
|
+
" ramp. Computes what the ramp WOULD decide vs what the gate DID.\n" +
|
|
3863
|
+
" backtest Replays each worker's history and reports DIVERGENCE: where the\n" +
|
|
3864
|
+
" ramp would have auto-run a bad action (false-allow, the risk) or\n" +
|
|
3865
|
+
" gated a good one (false-gate, the cost). Calibration, not a\n" +
|
|
3866
|
+
" success rate. Neither command changes a live decision.\n\n" +
|
|
3867
|
+
" --workers-dir <path> Where *.worker.yaml live (default ~/.patchwork/workers)\n");
|
|
3868
|
+
process.exit(0);
|
|
3869
|
+
}
|
|
3870
|
+
(async () => {
|
|
3871
|
+
try {
|
|
3872
|
+
const dirIdx = args.indexOf("--workers-dir");
|
|
3873
|
+
const workersDir = dirIdx !== -1 ? args[dirIdx + 1] : undefined;
|
|
3874
|
+
const { runWorkerShadowReport, runWorkerBacktest } = await import("./workers/runWorkerShadow.js");
|
|
3875
|
+
const opts = workersDir ? { workersDir } : {};
|
|
3876
|
+
process.stdout.write(sub === "backtest"
|
|
3877
|
+
? runWorkerBacktest(opts)
|
|
3878
|
+
: runWorkerShadowReport(opts));
|
|
3879
|
+
}
|
|
3880
|
+
catch (err) {
|
|
3881
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
3882
|
+
process.exit(1);
|
|
3883
|
+
}
|
|
3884
|
+
})();
|
|
3885
|
+
}
|
|
3886
|
+
// Handle approvals subcommand — considered-approval KPI. Reads the local
|
|
3887
|
+
// approval-decision log directly (like `workers shadow`); no bridge round-trip.
|
|
3888
|
+
// The lens that tells you whether the trust climbing the worker dial was EARNED
|
|
3889
|
+
// or rubber-stamped: reject rate, latency-to-decision, abandoned, channel split.
|
|
3890
|
+
if (process.argv[2] === "approvals") {
|
|
3891
|
+
const args = process.argv.slice(3);
|
|
3892
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
3893
|
+
process.stdout.write("Usage: patchwork approvals [--window 1h|24h|overnight|7d|any] [--json]\n\n" +
|
|
3894
|
+
"Considered-approval KPI from the local approval-decision log:\n" +
|
|
3895
|
+
" reject rate · latency-to-decision · abandoned · dashboard-vs-phone.\n\n" +
|
|
3896
|
+
" --window <name> lookback (default: any). overnight = since 6pm yesterday.\n" +
|
|
3897
|
+
" --json emit raw JSON (for scripting)\n");
|
|
3898
|
+
process.exit(0);
|
|
3899
|
+
}
|
|
3900
|
+
(async () => {
|
|
3901
|
+
try {
|
|
3902
|
+
const wIdx = args.findIndex((a) => a === "--window" || a === "-w");
|
|
3903
|
+
const win = wIdx >= 0 && wIdx + 1 < args.length
|
|
3904
|
+
? args[wIdx + 1]
|
|
3905
|
+
: "any";
|
|
3906
|
+
const fixed = {
|
|
3907
|
+
"1h": 3_600_000,
|
|
3908
|
+
"24h": 86_400_000,
|
|
3909
|
+
"7d": 604_800_000,
|
|
3910
|
+
any: null,
|
|
3911
|
+
};
|
|
3912
|
+
let sinceMs;
|
|
3913
|
+
if (win === "overnight") {
|
|
3914
|
+
const d = new Date();
|
|
3915
|
+
d.setHours(18, 0, 0, 0);
|
|
3916
|
+
if (d.getTime() > Date.now())
|
|
3917
|
+
d.setDate(d.getDate() - 1);
|
|
3918
|
+
sinceMs = d.getTime();
|
|
3919
|
+
}
|
|
3920
|
+
else if (Object.hasOwn(fixed, win)) {
|
|
3921
|
+
const dur = fixed[win];
|
|
3922
|
+
sinceMs = dur == null ? undefined : Date.now() - dur;
|
|
3923
|
+
}
|
|
3924
|
+
else {
|
|
3925
|
+
process.stderr.write(`Unknown --window value: "${win}"\n`);
|
|
3926
|
+
process.exit(1);
|
|
3927
|
+
}
|
|
3928
|
+
const { readConsideredDecisions, computeConsideredApprovalKpi, formatConsideredApprovalKpi, } = await import("./approvalKpi.js");
|
|
3929
|
+
const decisions = readConsideredDecisions(sinceMs !== undefined ? { sinceMs } : {});
|
|
3930
|
+
const kpi = computeConsideredApprovalKpi(decisions);
|
|
3931
|
+
process.stdout.write(args.includes("--json")
|
|
3932
|
+
? `${JSON.stringify(kpi, null, 2)}\n`
|
|
3933
|
+
: `${formatConsideredApprovalKpi(kpi, { windowLabel: win })}\n`);
|
|
3934
|
+
process.exit(0);
|
|
3935
|
+
}
|
|
3936
|
+
catch (err) {
|
|
3937
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
3938
|
+
process.exit(1);
|
|
3939
|
+
}
|
|
3940
|
+
})();
|
|
3941
|
+
}
|
|
3942
|
+
// Handle gate subcommand — "why did the worker allow/gate THIS action" over
|
|
3943
|
+
// the local Decision Record (worker_gate_decisions.jsonl). Reads the log
|
|
3944
|
+
// directly (like `workers shadow` / `approvals`); no bridge round-trip needed
|
|
3945
|
+
// since the record is a local file, not live state.
|
|
3946
|
+
if (process.argv[2] === "gate") {
|
|
3947
|
+
const args = process.argv.slice(3);
|
|
3948
|
+
const sub = args[0];
|
|
3949
|
+
if (sub !== "explain" ||
|
|
3950
|
+
args.includes("--help") ||
|
|
3951
|
+
args.includes("-h") ||
|
|
3952
|
+
args.length < 3) {
|
|
3953
|
+
process.stdout.write("Usage: patchwork gate explain <workerId> <classKey> [--limit N] [--diff] [--json]\n\n" +
|
|
3954
|
+
"Explain the worker-autonomy gate's most recent decision(s) for a given\n" +
|
|
3955
|
+
"worker × action-class, from the local Decision Record\n" +
|
|
3956
|
+
"(~/.patchwork/worker_gate_decisions.jsonl) — no bridge required.\n\n" +
|
|
3957
|
+
' <classKey> e.g. "issue:compensable:high" (domain:reversibility:blastTier)\n' +
|
|
3958
|
+
" --limit N show the N most recent decisions (default 1, or 2 with --diff)\n" +
|
|
3959
|
+
" --diff compare the 2 most recent decisions and show what changed\n" +
|
|
3960
|
+
" --json emit raw JSON (for scripting)\n");
|
|
3961
|
+
process.exit(sub === "explain" ? 1 : 0);
|
|
3962
|
+
}
|
|
3963
|
+
(async () => {
|
|
3964
|
+
try {
|
|
3965
|
+
// Length checked above (args.length < 3 short-circuits to usage), so
|
|
3966
|
+
// args[1]/[2] are present here — noUncheckedIndexedAccess just can't see
|
|
3967
|
+
// across the `if` block above.
|
|
3968
|
+
const workerId = args[1];
|
|
3969
|
+
const classKey = args[2];
|
|
3970
|
+
const wantDiff = args.includes("--diff");
|
|
3971
|
+
const limitIdx = args.indexOf("--limit");
|
|
3972
|
+
const explicitLimit = limitIdx >= 0 && limitIdx + 1 < args.length
|
|
3973
|
+
? Number.parseInt(args[limitIdx + 1], 10)
|
|
3974
|
+
: undefined;
|
|
3975
|
+
const limit = explicitLimit ?? (wantDiff ? 2 : 1);
|
|
3976
|
+
if (!Number.isFinite(limit) || limit < 1) {
|
|
3977
|
+
process.stderr.write("Error: --limit must be a positive integer\n");
|
|
3978
|
+
process.exit(1);
|
|
3979
|
+
}
|
|
3980
|
+
const { WorkerGateDecisionLog, formatGateDecisionHistory, formatGateDecisionDiff, } = await import("./workerGateDecisionLog.js");
|
|
3981
|
+
const patchworkDir = process.env.PATCHWORK_HOME ?? path.join(os.homedir(), ".patchwork");
|
|
3982
|
+
const log = new WorkerGateDecisionLog({ dir: patchworkDir });
|
|
3983
|
+
const decisions = log.query({ workerId, classKey, limit });
|
|
3984
|
+
if (args.includes("--json")) {
|
|
3985
|
+
process.stdout.write(`${JSON.stringify(decisions, null, 2)}\n`);
|
|
3986
|
+
process.exit(0);
|
|
3987
|
+
}
|
|
3988
|
+
if (decisions.length === 0) {
|
|
3989
|
+
process.stdout.write(`No gate decisions found for worker "${workerId}" on class "${classKey}".\n` +
|
|
3990
|
+
"Either the worker hasn't acted on this class yet, or worker.autonomy is off.\n");
|
|
3991
|
+
process.exit(0);
|
|
3992
|
+
}
|
|
3993
|
+
if (wantDiff) {
|
|
3994
|
+
// query() sorts most-recent-first: newer is [0], older is [1].
|
|
3995
|
+
const [newer, older] = decisions;
|
|
3996
|
+
if (!newer || !older) {
|
|
3997
|
+
process.stdout.write(`Only one decision found for worker "${workerId}" on class "${classKey}" — nothing to diff against yet.\n`);
|
|
3998
|
+
process.exit(0);
|
|
3999
|
+
}
|
|
4000
|
+
process.stdout.write(`${formatGateDecisionDiff(newer, older)}\n`);
|
|
4001
|
+
process.exit(0);
|
|
4002
|
+
}
|
|
4003
|
+
process.stdout.write(`${formatGateDecisionHistory(decisions)}\n`);
|
|
4004
|
+
process.exit(0);
|
|
4005
|
+
}
|
|
4006
|
+
catch (err) {
|
|
4007
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
4008
|
+
process.exit(1);
|
|
4009
|
+
}
|
|
4010
|
+
})();
|
|
4011
|
+
}
|
|
4012
|
+
// Handle outcomes subcommand — operator positive-act confirmation of worker
|
|
4013
|
+
// filings (confirm/reject/list). Writes to ~/.patchwork/outcome-log.jsonl, the
|
|
4014
|
+
// same store the trust ramp reads; it is NOT a recipe step, so a worker cannot
|
|
4015
|
+
// self-confirm its own filings. No bridge required.
|
|
4016
|
+
if (process.argv[2] === "outcomes") {
|
|
4017
|
+
const args = process.argv.slice(3);
|
|
4018
|
+
(async () => {
|
|
4019
|
+
try {
|
|
4020
|
+
// `outcomes pending` — the confirm queue (join over runs + dispositions).
|
|
4021
|
+
// Handled here (not in the pure runOutcomesCli) because it needs the run
|
|
4022
|
+
// log + worker manifests, which live behind the shadow readers.
|
|
4023
|
+
if (args[0] === "pending") {
|
|
4024
|
+
const { computePendingConfirmations, formatPendingConfirmations } = await import("./workers/runWorkerShadow.js");
|
|
4025
|
+
const pending = computePendingConfirmations();
|
|
4026
|
+
if (args.includes("--json")) {
|
|
4027
|
+
process.stdout.write(`${JSON.stringify(pending, null, 2)}\n`);
|
|
4028
|
+
}
|
|
4029
|
+
else {
|
|
4030
|
+
process.stdout.write(formatPendingConfirmations(pending));
|
|
4031
|
+
}
|
|
4032
|
+
process.exit(0);
|
|
4033
|
+
}
|
|
4034
|
+
const { runOutcomesCli } = await import("./workers/outcomesCli.js");
|
|
4035
|
+
const { OutcomeStore, resolveOutcomeLogDir } = await import("./workers/outcomeStore.js");
|
|
4036
|
+
const store = new OutcomeStore(resolveOutcomeLogDir());
|
|
4037
|
+
const res = runOutcomesCli(args, { store, now: Date.now() });
|
|
4038
|
+
if (res.stdout)
|
|
4039
|
+
process.stdout.write(res.stdout);
|
|
4040
|
+
if (res.stderr)
|
|
4041
|
+
process.stderr.write(res.stderr);
|
|
4042
|
+
process.exit(res.exitCode);
|
|
4043
|
+
}
|
|
4044
|
+
catch (err) {
|
|
4045
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
4046
|
+
process.exit(1);
|
|
4047
|
+
}
|
|
4048
|
+
})();
|
|
4049
|
+
}
|
|
3513
4050
|
// Handle launchd subcommand — install/uninstall macOS LaunchAgent for auto-start
|
|
3514
4051
|
if (process.argv[2] === "launchd") {
|
|
3515
4052
|
const sub = process.argv[3];
|