@intutic/cli 1.10.0 → 1.10.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 +1 -0
- package/dist/cli.js +868 -30
- package/dist/cli.js.map +1 -1
- package/dist/commands/attenuate.d.ts +39 -0
- package/dist/commands/attenuate.d.ts.map +1 -0
- package/dist/commands/attenuate.js +98 -0
- package/dist/commands/attenuate.js.map +1 -0
- package/dist/commands/budget.d.ts +80 -0
- package/dist/commands/budget.d.ts.map +1 -1
- package/dist/commands/budget.js +119 -3
- package/dist/commands/budget.js.map +1 -1
- package/dist/commands/connect.d.ts +10 -0
- package/dist/commands/connect.d.ts.map +1 -1
- package/dist/commands/connect.js +358 -48
- package/dist/commands/connect.js.map +1 -1
- package/dist/commands/credentials.d.ts +31 -0
- package/dist/commands/credentials.d.ts.map +1 -0
- package/dist/commands/credentials.js +134 -0
- package/dist/commands/credentials.js.map +1 -0
- package/dist/commands/decision.d.ts +37 -0
- package/dist/commands/decision.d.ts.map +1 -0
- package/dist/commands/decision.js +56 -0
- package/dist/commands/decision.js.map +1 -0
- package/dist/commands/doctor.d.ts +38 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +228 -18
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/enforce.d.ts +33 -0
- package/dist/commands/enforce.d.ts.map +1 -0
- package/dist/commands/enforce.js +150 -0
- package/dist/commands/enforce.js.map +1 -0
- package/dist/commands/enterprise.d.ts +29 -0
- package/dist/commands/enterprise.d.ts.map +1 -0
- package/dist/commands/enterprise.js +141 -0
- package/dist/commands/enterprise.js.map +1 -0
- package/dist/commands/env.d.ts +7 -0
- package/dist/commands/env.d.ts.map +1 -0
- package/dist/commands/env.js +55 -0
- package/dist/commands/env.js.map +1 -0
- package/dist/commands/exec.d.ts +38 -2
- package/dist/commands/exec.d.ts.map +1 -1
- package/dist/commands/exec.js +247 -10
- package/dist/commands/exec.js.map +1 -1
- package/dist/commands/findings.d.ts +84 -0
- package/dist/commands/findings.d.ts.map +1 -0
- package/dist/commands/findings.js +329 -0
- package/dist/commands/findings.js.map +1 -0
- package/dist/commands/gateway.d.ts +62 -0
- package/dist/commands/gateway.d.ts.map +1 -0
- package/dist/commands/gateway.js +298 -0
- package/dist/commands/gateway.js.map +1 -0
- package/dist/commands/guardrails.d.ts +81 -0
- package/dist/commands/guardrails.d.ts.map +1 -0
- package/dist/commands/guardrails.js +735 -0
- package/dist/commands/guardrails.js.map +1 -0
- package/dist/commands/init.d.ts +8 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +14 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/install-daemon.d.ts +70 -3
- package/dist/commands/install-daemon.d.ts.map +1 -1
- package/dist/commands/install-daemon.js +482 -47
- package/dist/commands/install-daemon.js.map +1 -1
- package/dist/commands/integrity.d.ts +205 -0
- package/dist/commands/integrity.d.ts.map +1 -0
- package/dist/commands/integrity.js +620 -0
- package/dist/commands/integrity.js.map +1 -0
- package/dist/commands/judge.d.ts +31 -0
- package/dist/commands/judge.d.ts.map +1 -0
- package/dist/commands/judge.js +118 -0
- package/dist/commands/judge.js.map +1 -0
- package/dist/commands/org.d.ts +31 -0
- package/dist/commands/org.d.ts.map +1 -0
- package/dist/commands/org.js +159 -0
- package/dist/commands/org.js.map +1 -0
- package/dist/commands/policy.d.ts +138 -4
- package/dist/commands/policy.d.ts.map +1 -1
- package/dist/commands/policy.js +538 -97
- package/dist/commands/policy.js.map +1 -1
- package/dist/commands/predict-cost.d.ts +45 -0
- package/dist/commands/predict-cost.d.ts.map +1 -0
- package/dist/commands/predict-cost.js +123 -0
- package/dist/commands/predict-cost.js.map +1 -0
- package/dist/commands/rollback.d.ts +5 -0
- package/dist/commands/rollback.d.ts.map +1 -0
- package/dist/commands/rollback.js +142 -0
- package/dist/commands/rollback.js.map +1 -0
- package/dist/commands/routing.d.ts +62 -0
- package/dist/commands/routing.d.ts.map +1 -0
- package/dist/commands/routing.js +121 -0
- package/dist/commands/routing.js.map +1 -0
- package/dist/commands/setup.d.ts +62 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +238 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/skill.d.ts +268 -1
- package/dist/commands/skill.d.ts.map +1 -1
- package/dist/commands/skill.js +744 -39
- package/dist/commands/skill.js.map +1 -1
- package/dist/commands/sops.d.ts +95 -0
- package/dist/commands/sops.d.ts.map +1 -1
- package/dist/commands/sops.js +334 -34
- package/dist/commands/sops.js.map +1 -1
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +1 -18
- package/dist/commands/start.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +44 -1
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/team.d.ts +38 -0
- package/dist/commands/team.d.ts.map +1 -0
- package/dist/commands/team.js +140 -0
- package/dist/commands/team.js.map +1 -0
- package/dist/commands/traces.d.ts +8 -1
- package/dist/commands/traces.d.ts.map +1 -1
- package/dist/commands/traces.js +114 -15
- package/dist/commands/traces.js.map +1 -1
- package/dist/commands/whoami.d.ts.map +1 -1
- package/dist/commands/whoami.js +2 -1
- package/dist/commands/whoami.js.map +1 -1
- package/dist/config/keychain.d.ts +10 -2
- package/dist/config/keychain.d.ts.map +1 -1
- package/dist/config/keychain.js +83 -14
- package/dist/config/keychain.js.map +1 -1
- package/dist/config/paths.d.ts +27 -0
- package/dist/config/paths.d.ts.map +1 -1
- package/dist/config/paths.js +37 -6
- package/dist/config/paths.js.map +1 -1
- package/dist/config/store.d.ts.map +1 -1
- package/dist/config/store.js +19 -3
- package/dist/config/store.js.map +1 -1
- package/dist/harness/ag2.d.ts +22 -0
- package/dist/harness/ag2.d.ts.map +1 -0
- package/dist/harness/ag2.js +34 -0
- package/dist/harness/ag2.js.map +1 -0
- package/dist/harness/agentcore.d.ts +55 -0
- package/dist/harness/agentcore.d.ts.map +1 -0
- package/dist/harness/agentcore.js +119 -0
- package/dist/harness/agentcore.js.map +1 -0
- package/dist/harness/agenticOrchestrator.d.ts +52 -0
- package/dist/harness/agenticOrchestrator.d.ts.map +1 -0
- package/dist/harness/agenticOrchestrator.js +115 -0
- package/dist/harness/agenticOrchestrator.js.map +1 -0
- package/dist/harness/aiSdkHarness.d.ts +30 -0
- package/dist/harness/aiSdkHarness.d.ts.map +1 -0
- package/dist/harness/aiSdkHarness.js +51 -0
- package/dist/harness/aiSdkHarness.js.map +1 -0
- package/dist/harness/aiSdkWorkflow.d.ts +26 -0
- package/dist/harness/aiSdkWorkflow.d.ts.map +1 -0
- package/dist/harness/aiSdkWorkflow.js +39 -0
- package/dist/harness/aiSdkWorkflow.js.map +1 -0
- package/dist/harness/autogen.d.ts +24 -0
- package/dist/harness/autogen.d.ts.map +1 -0
- package/dist/harness/autogen.js +36 -0
- package/dist/harness/autogen.js.map +1 -0
- package/dist/harness/base.js.map +1 -1
- package/dist/harness/continue.d.ts.map +1 -1
- package/dist/harness/continue.js +2 -1
- package/dist/harness/continue.js.map +1 -1
- package/dist/harness/crewai.d.ts +15 -0
- package/dist/harness/crewai.d.ts.map +1 -0
- package/dist/harness/crewai.js +26 -0
- package/dist/harness/crewai.js.map +1 -0
- package/dist/harness/detector.d.ts +1 -1
- package/dist/harness/detector.d.ts.map +1 -1
- package/dist/harness/detector.js +47 -1
- package/dist/harness/detector.js.map +1 -1
- package/dist/harness/dsh.d.ts +19 -0
- package/dist/harness/dsh.d.ts.map +1 -0
- package/dist/harness/dsh.js +93 -0
- package/dist/harness/dsh.js.map +1 -0
- package/dist/harness/eve.d.ts +40 -0
- package/dist/harness/eve.d.ts.map +1 -0
- package/dist/harness/eve.js +67 -0
- package/dist/harness/eve.js.map +1 -0
- package/dist/harness/googleAdk.d.ts +18 -0
- package/dist/harness/googleAdk.d.ts.map +1 -0
- package/dist/harness/googleAdk.js +29 -0
- package/dist/harness/googleAdk.js.map +1 -0
- package/dist/harness/goose.d.ts.map +1 -1
- package/dist/harness/goose.js +5 -1
- package/dist/harness/goose.js.map +1 -1
- package/dist/harness/grok.d.ts +16 -0
- package/dist/harness/grok.d.ts.map +1 -0
- package/dist/harness/grok.js +78 -0
- package/dist/harness/grok.js.map +1 -0
- package/dist/harness/jsSdkGatedAdapter.d.ts +102 -0
- package/dist/harness/jsSdkGatedAdapter.d.ts.map +1 -0
- package/dist/harness/jsSdkGatedAdapter.js +139 -0
- package/dist/harness/jsSdkGatedAdapter.js.map +1 -0
- package/dist/harness/langchain.d.ts +22 -0
- package/dist/harness/langchain.d.ts.map +1 -0
- package/dist/harness/langchain.js +33 -0
- package/dist/harness/langchain.js.map +1 -0
- package/dist/harness/langgraph.d.ts +22 -0
- package/dist/harness/langgraph.d.ts.map +1 -0
- package/dist/harness/langgraph.js +80 -0
- package/dist/harness/langgraph.js.map +1 -0
- package/dist/harness/mastra.d.ts +21 -0
- package/dist/harness/mastra.d.ts.map +1 -0
- package/dist/harness/mastra.js +33 -0
- package/dist/harness/mastra.js.map +1 -0
- package/dist/harness/muse.d.ts +22 -0
- package/dist/harness/muse.d.ts.map +1 -0
- package/dist/harness/muse.js +92 -0
- package/dist/harness/muse.js.map +1 -0
- package/dist/harness/n8n.d.ts.map +1 -1
- package/dist/harness/n8n.js +36 -11
- package/dist/harness/n8n.js.map +1 -1
- package/dist/harness/openaiAgents.d.ts +40 -0
- package/dist/harness/openaiAgents.d.ts.map +1 -0
- package/dist/harness/openaiAgents.js +107 -0
- package/dist/harness/openaiAgents.js.map +1 -0
- package/dist/harness/opencode.d.ts +24 -0
- package/dist/harness/opencode.d.ts.map +1 -0
- package/dist/harness/opencode.js +97 -0
- package/dist/harness/opencode.js.map +1 -0
- package/dist/harness/pydanticAi.d.ts +19 -0
- package/dist/harness/pydanticAi.d.ts.map +1 -0
- package/dist/harness/pydanticAi.js +30 -0
- package/dist/harness/pydanticAi.js.map +1 -0
- package/dist/harness/sdkGatedAdapter.d.ts +55 -0
- package/dist/harness/sdkGatedAdapter.d.ts.map +1 -0
- package/dist/harness/sdkGatedAdapter.js +85 -0
- package/dist/harness/sdkGatedAdapter.js.map +1 -0
- package/dist/harness/smolagents.d.ts +22 -0
- package/dist/harness/smolagents.d.ts.map +1 -0
- package/dist/harness/smolagents.js +33 -0
- package/dist/harness/smolagents.js.map +1 -0
- package/dist/harness/strands.d.ts +22 -0
- package/dist/harness/strands.d.ts.map +1 -0
- package/dist/harness/strands.js +35 -0
- package/dist/harness/strands.js.map +1 -0
- package/dist/harness/trueforge.d.ts +25 -0
- package/dist/harness/trueforge.d.ts.map +1 -0
- package/dist/harness/trueforge.js +40 -0
- package/dist/harness/trueforge.js.map +1 -0
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js +70 -0
- package/dist/harness/types.js.map +1 -1
- package/dist/harness/vercelAiSdk.d.ts +31 -0
- package/dist/harness/vercelAiSdk.d.ts.map +1 -0
- package/dist/harness/vercelAiSdk.js +44 -0
- package/dist/harness/vercelAiSdk.js.map +1 -0
- package/dist/harness/xirp.d.ts +43 -0
- package/dist/harness/xirp.d.ts.map +1 -0
- package/dist/harness/xirp.js +95 -0
- package/dist/harness/xirp.js.map +1 -0
- package/dist/lib/api.d.ts +68 -0
- package/dist/lib/api.d.ts.map +1 -1
- package/dist/lib/api.js +108 -67
- package/dist/lib/api.js.map +1 -1
- package/dist/lib/authMessages.d.ts +19 -0
- package/dist/lib/authMessages.d.ts.map +1 -0
- package/dist/lib/authMessages.js +20 -0
- package/dist/lib/authMessages.js.map +1 -0
- package/dist/lib/binaryChecksum.d.ts +12 -0
- package/dist/lib/binaryChecksum.d.ts.map +1 -0
- package/dist/lib/binaryChecksum.js +45 -0
- package/dist/lib/binaryChecksum.js.map +1 -0
- package/dist/lib/binaryOnPath.d.ts +24 -0
- package/dist/lib/binaryOnPath.d.ts.map +1 -0
- package/dist/lib/binaryOnPath.js +31 -0
- package/dist/lib/binaryOnPath.js.map +1 -0
- package/dist/lib/caTrust.d.ts +69 -0
- package/dist/lib/caTrust.d.ts.map +1 -0
- package/dist/lib/caTrust.js +169 -0
- package/dist/lib/caTrust.js.map +1 -0
- package/dist/lib/ciscoScanner.d.ts +178 -0
- package/dist/lib/ciscoScanner.d.ts.map +1 -0
- package/dist/lib/ciscoScanner.js +292 -0
- package/dist/lib/ciscoScanner.js.map +1 -0
- package/dist/lib/decisionsLogRefresh.d.ts +28 -0
- package/dist/lib/decisionsLogRefresh.d.ts.map +1 -0
- package/dist/lib/decisionsLogRefresh.js +54 -0
- package/dist/lib/decisionsLogRefresh.js.map +1 -0
- package/dist/lib/deviceReport.d.ts +26 -0
- package/dist/lib/deviceReport.d.ts.map +1 -0
- package/dist/lib/deviceReport.js +55 -0
- package/dist/lib/deviceReport.js.map +1 -0
- package/dist/lib/elevation.d.ts +58 -0
- package/dist/lib/elevation.d.ts.map +1 -0
- package/dist/lib/elevation.js +83 -0
- package/dist/lib/elevation.js.map +1 -0
- package/dist/lib/enforcementState.d.ts +69 -0
- package/dist/lib/enforcementState.d.ts.map +1 -0
- package/dist/lib/enforcementState.js +113 -0
- package/dist/lib/enforcementState.js.map +1 -0
- package/dist/lib/ensureValkey.d.ts.map +1 -1
- package/dist/lib/ensureValkey.js +2 -2
- package/dist/lib/ensureValkey.js.map +1 -1
- package/dist/lib/envInjector.d.ts.map +1 -1
- package/dist/lib/envInjector.js +22 -4
- package/dist/lib/envInjector.js.map +1 -1
- package/dist/lib/gitHooks.d.ts +7 -2
- package/dist/lib/gitHooks.d.ts.map +1 -1
- package/dist/lib/gitHooks.js +180 -13
- package/dist/lib/gitHooks.js.map +1 -1
- package/dist/lib/gracefulShutdown.d.ts +37 -0
- package/dist/lib/gracefulShutdown.d.ts.map +1 -0
- package/dist/lib/gracefulShutdown.js +57 -0
- package/dist/lib/gracefulShutdown.js.map +1 -0
- package/dist/lib/graphIdentity.d.ts +80 -0
- package/dist/lib/graphIdentity.d.ts.map +1 -0
- package/dist/lib/graphIdentity.js +120 -0
- package/dist/lib/graphIdentity.js.map +1 -0
- package/dist/lib/localTraces.d.ts +61 -0
- package/dist/lib/localTraces.d.ts.map +1 -0
- package/dist/lib/localTraces.js +173 -0
- package/dist/lib/localTraces.js.map +1 -0
- package/dist/lib/mdmManifest.d.ts +73 -0
- package/dist/lib/mdmManifest.d.ts.map +1 -0
- package/dist/lib/mdmManifest.js +190 -0
- package/dist/lib/mdmManifest.js.map +1 -0
- package/dist/lib/onboarding.d.ts.map +1 -1
- package/dist/lib/onboarding.js +60 -1
- package/dist/lib/onboarding.js.map +1 -1
- package/dist/lib/otelPropagation.d.ts +26 -0
- package/dist/lib/otelPropagation.d.ts.map +1 -0
- package/dist/lib/otelPropagation.js +40 -0
- package/dist/lib/otelPropagation.js.map +1 -0
- package/dist/lib/policySnapshot.d.ts +92 -0
- package/dist/lib/policySnapshot.d.ts.map +1 -0
- package/dist/lib/policySnapshot.js +173 -0
- package/dist/lib/policySnapshot.js.map +1 -0
- package/dist/lib/process.d.ts.map +1 -1
- package/dist/lib/process.js +60 -0
- package/dist/lib/process.js.map +1 -1
- package/dist/lib/providerProbe.d.ts +28 -0
- package/dist/lib/providerProbe.d.ts.map +1 -0
- package/dist/lib/providerProbe.js +57 -0
- package/dist/lib/providerProbe.js.map +1 -0
- package/dist/lib/proxyBinary.d.ts +19 -0
- package/dist/lib/proxyBinary.d.ts.map +1 -0
- package/dist/lib/proxyBinary.js +30 -0
- package/dist/lib/proxyBinary.js.map +1 -0
- package/dist/lib/sandbox/firecracker.d.ts +89 -0
- package/dist/lib/sandbox/firecracker.d.ts.map +1 -0
- package/dist/lib/sandbox/firecracker.js +135 -0
- package/dist/lib/sandbox/firecracker.js.map +1 -0
- package/dist/lib/sandbox/index.d.ts +34 -0
- package/dist/lib/sandbox/index.d.ts.map +1 -0
- package/dist/lib/sandbox/index.js +54 -0
- package/dist/lib/sandbox/index.js.map +1 -0
- package/dist/lib/sandbox/oci.d.ts +38 -0
- package/dist/lib/sandbox/oci.d.ts.map +1 -0
- package/dist/lib/sandbox/oci.js +108 -0
- package/dist/lib/sandbox/oci.js.map +1 -0
- package/dist/lib/sandbox/types.d.ts +48 -0
- package/dist/lib/sandbox/types.d.ts.map +1 -0
- package/dist/lib/sandbox/types.js +12 -0
- package/dist/lib/sandbox/types.js.map +1 -0
- package/dist/lib/sopFrontMatter.d.ts +50 -0
- package/dist/lib/sopFrontMatter.d.ts.map +1 -0
- package/dist/lib/sopFrontMatter.js +129 -0
- package/dist/lib/sopFrontMatter.js.map +1 -0
- package/package.json +11 -6
package/dist/cli.js
CHANGED
|
@@ -32,6 +32,26 @@ program
|
|
|
32
32
|
const { runInit } = await import('./commands/init.js');
|
|
33
33
|
await runInit(opts);
|
|
34
34
|
});
|
|
35
|
+
// ── Cohort setup wizard (LLD #70) ────────────────────────────────────────
|
|
36
|
+
program
|
|
37
|
+
.command('setup')
|
|
38
|
+
.description('Guided setup: detect harnesses, configure a provider credential, verify it, and (optionally) a judge model')
|
|
39
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
40
|
+
.action(async (opts) => {
|
|
41
|
+
const { runSetup } = await import('./commands/setup.js');
|
|
42
|
+
await runSetup(opts);
|
|
43
|
+
});
|
|
44
|
+
const judgeCmd = program
|
|
45
|
+
.command('judge')
|
|
46
|
+
.description('Configure the LLM-as-judge path');
|
|
47
|
+
judgeCmd
|
|
48
|
+
.command('configure')
|
|
49
|
+
.description('Generate local artifacts (litellm_config.yaml, env, Helm values) for an on-prem judge — never a remote API call')
|
|
50
|
+
.option('--out <path>', 'Where to write litellm_config.yaml', './litellm_config.yaml')
|
|
51
|
+
.action(async (opts) => {
|
|
52
|
+
const { runJudgeConfigure } = await import('./commands/judge.js');
|
|
53
|
+
await runJudgeConfigure(opts);
|
|
54
|
+
});
|
|
35
55
|
program
|
|
36
56
|
.command('login')
|
|
37
57
|
.description('Authenticate with the Intutic control plane')
|
|
@@ -62,14 +82,45 @@ program
|
|
|
62
82
|
const { runDoctor } = await import('./commands/doctor.js');
|
|
63
83
|
await runDoctor();
|
|
64
84
|
});
|
|
85
|
+
program
|
|
86
|
+
.command('rollback')
|
|
87
|
+
.description('List or restore pre-images captured when a guard flagged a file-writing call')
|
|
88
|
+
.option('--list', 'List captured pre-images (the default with no --id)')
|
|
89
|
+
.option('--id <id>', 'Restore the named pre-image')
|
|
90
|
+
.action(async (opts) => {
|
|
91
|
+
const { runRollback } = await import('./commands/rollback.js');
|
|
92
|
+
await runRollback(opts);
|
|
93
|
+
});
|
|
65
94
|
program
|
|
66
95
|
.command('budget')
|
|
67
96
|
.description('Check remaining daily/monthly budget and list active loops')
|
|
68
97
|
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
98
|
+
.option('--watch', 'Continuously print machine-local and workspace spend, one line per tick')
|
|
99
|
+
.option('--interval <seconds>', 'Tick interval in seconds for --watch', String(5))
|
|
69
100
|
.action(async (opts) => {
|
|
101
|
+
if (opts.watch) {
|
|
102
|
+
const { runBudgetWatch } = await import('./commands/budget.js');
|
|
103
|
+
await runBudgetWatch(opts);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
70
106
|
const { runBudget } = await import('./commands/budget.js');
|
|
71
107
|
await runBudget(opts);
|
|
72
108
|
});
|
|
109
|
+
program
|
|
110
|
+
.command('predict-cost')
|
|
111
|
+
.description('Pre-flight cost estimate for a prompt/task before it runs (POST /api/v1/predict-cost). ' +
|
|
112
|
+
'Estimates output tokens from the workspace baseline and prices with the same rate table ' +
|
|
113
|
+
'every other cost figure in the control plane uses.')
|
|
114
|
+
.requiredOption('--model <model>', 'Model to estimate against (e.g. claude-sonnet-4-5)')
|
|
115
|
+
.option('--task-type <type>', 'Task type used to pick the baseline bucket', 'coding')
|
|
116
|
+
.option('--tokens <n>', 'Input token count (mutually exclusive with --file)')
|
|
117
|
+
.option('--file <path>', 'File whose contents size the input (mutually exclusive with --tokens)')
|
|
118
|
+
.option('--json', 'Output as JSON instead of a report')
|
|
119
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
120
|
+
.action(async (opts) => {
|
|
121
|
+
const { runPredictCost } = await import('./commands/predict-cost.js');
|
|
122
|
+
await runPredictCost(opts);
|
|
123
|
+
});
|
|
73
124
|
const sopsCmd = program
|
|
74
125
|
.command('sops')
|
|
75
126
|
.description('Manage local and global SOP rules');
|
|
@@ -77,10 +128,44 @@ sopsCmd
|
|
|
77
128
|
.command('push <name>')
|
|
78
129
|
.description('Push a local offline SOP folder to the central workspace')
|
|
79
130
|
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
131
|
+
.option('--org', "Push as an org-wide floor (LLD #65 follow-up) instead of a workspace SOP -- applies to every workspace under the caller's org, in addition to that workspace's own SOPs")
|
|
80
132
|
.action(async (name, opts) => {
|
|
81
133
|
const { runSopsPush } = await import('./commands/sops.js');
|
|
82
134
|
await runSopsPush(name, opts);
|
|
83
135
|
});
|
|
136
|
+
sopsCmd
|
|
137
|
+
.command('pull')
|
|
138
|
+
.description('Pull every workspace SOP from the control plane into .intutic/sops/<slug>.md')
|
|
139
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
140
|
+
.option('--force', 'Overwrite locally-modified files instead of refusing them')
|
|
141
|
+
.action(async (opts) => {
|
|
142
|
+
const { runSopsPull } = await import('./commands/sops.js');
|
|
143
|
+
await runSopsPull(opts);
|
|
144
|
+
});
|
|
145
|
+
sopsCmd
|
|
146
|
+
.command('status')
|
|
147
|
+
.description('Show drift between .intutic/sops/*.md and the control plane, read-only')
|
|
148
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
149
|
+
.action(async (opts) => {
|
|
150
|
+
const { runSopsStatus } = await import('./commands/sops.js');
|
|
151
|
+
await runSopsStatus(opts);
|
|
152
|
+
});
|
|
153
|
+
sopsCmd
|
|
154
|
+
.command('org-list')
|
|
155
|
+
.description("List org-wide SOP floors for the caller's own org (LLD #65 follow-up)")
|
|
156
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
157
|
+
.action(async (opts) => {
|
|
158
|
+
const { runSopsOrgList } = await import('./commands/sops.js');
|
|
159
|
+
await runSopsOrgList(opts);
|
|
160
|
+
});
|
|
161
|
+
sopsCmd
|
|
162
|
+
.command('org-rm <orgSopId>')
|
|
163
|
+
.description('Remove (soft-delete) an org-wide SOP floor')
|
|
164
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
165
|
+
.action(async (orgSopId, opts) => {
|
|
166
|
+
const { runSopsOrgRm } = await import('./commands/sops.js');
|
|
167
|
+
await runSopsOrgRm(orgSopId, opts);
|
|
168
|
+
});
|
|
84
169
|
const policyCmd = program
|
|
85
170
|
.command('policy')
|
|
86
171
|
.description('Manage compliance and safety policies');
|
|
@@ -109,6 +194,17 @@ policyCmd
|
|
|
109
194
|
const { runPolicyRollback } = await import('./commands/policy.js');
|
|
110
195
|
await runPolicyRollback(policyId, opts);
|
|
111
196
|
});
|
|
197
|
+
policyCmd
|
|
198
|
+
.command('snapshot')
|
|
199
|
+
.description('Compile workspace policy to ~/.intutic/hooks/policy-snapshot.rules — the file every gate reads.\n' +
|
|
200
|
+
'\n' +
|
|
201
|
+
' Does only that. \'intutic connect\' writes it too, but as one step of\n' +
|
|
202
|
+
' starting the full sync daemon; this arms the gates without one.')
|
|
203
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
204
|
+
.action(async (opts) => {
|
|
205
|
+
const { runPolicySnapshot } = await import('./commands/policy.js');
|
|
206
|
+
await runPolicySnapshot(opts);
|
|
207
|
+
});
|
|
112
208
|
policyCmd
|
|
113
209
|
.command('export')
|
|
114
210
|
.description('Export compliance policies to stdout')
|
|
@@ -130,9 +226,12 @@ policyCmd
|
|
|
130
226
|
policyCmd
|
|
131
227
|
.command('compile')
|
|
132
228
|
.description('Compile an AssemblyScript rule to WASM (wraps asc)')
|
|
133
|
-
.option('--src <path>', 'Rule source entry file
|
|
134
|
-
.option('--out <path>', 'Output .wasm path
|
|
229
|
+
.option('--src <path>', 'Rule source entry file (default: assembly/index.ts)')
|
|
230
|
+
.option('--out <path>', 'Output .wasm path (default: build/rule.wasm, or build/<candidate>.wasm with --candidate)')
|
|
135
231
|
.option('--debug', 'Include debug info and source maps')
|
|
232
|
+
.option('--candidate <id>', 'Compile a rule candidate from its source of record on the control plane (fetched and hash-verified; not with --src)')
|
|
233
|
+
.option('--upload', 'With --candidate: upload the bundle to the candidate with its source hash and print the gate results')
|
|
234
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
136
235
|
.action(async (opts) => {
|
|
137
236
|
const { runPolicyCompile } = await import('./commands/policy.js');
|
|
138
237
|
await runPolicyCompile(opts);
|
|
@@ -154,6 +253,217 @@ policyCmd
|
|
|
154
253
|
const { runPolicyListLocal } = await import('./commands/policy.js');
|
|
155
254
|
await runPolicyListLocal();
|
|
156
255
|
});
|
|
256
|
+
policyCmd
|
|
257
|
+
.command('replay <ruleId>')
|
|
258
|
+
.description("Replay a rule (installed or not) against this workspace's own recent traffic and report " +
|
|
259
|
+
'what it would have done, without touching enforcement.')
|
|
260
|
+
.option('--limit <n>', 'Max sampled contexts to replay against (default 500, capped at 5000)')
|
|
261
|
+
.option('--since <duration>', 'Only contexts at or after this time — relative ("7d", "24h") or ISO 8601')
|
|
262
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
263
|
+
.action(async (ruleId, opts) => {
|
|
264
|
+
const { runPolicyReplay } = await import('./commands/policy.js');
|
|
265
|
+
await runPolicyReplay(ruleId, opts);
|
|
266
|
+
});
|
|
267
|
+
// Policy Clause Ledger (LLD #71). `intutic policy` is the WASM loop and
|
|
268
|
+
// `intutic sops` is files on disk; this namespace is the cited guardrails
|
|
269
|
+
// extracted from policy documents and their shadow -> promote lifecycle.
|
|
270
|
+
const guardrailsCmd = program
|
|
271
|
+
.command('guardrails')
|
|
272
|
+
.description('Policy documents, cited clauses and the guardrails compiled from them');
|
|
273
|
+
const guardrailsSourcesCmd = guardrailsCmd
|
|
274
|
+
.command('sources')
|
|
275
|
+
.description('Policy sources (Notion, Confluence, GitHub, Google Docs) feeding the ledger');
|
|
276
|
+
guardrailsSourcesCmd
|
|
277
|
+
.command('list')
|
|
278
|
+
.description('List configured policy sources and their last sync')
|
|
279
|
+
.option('--json', 'Output as JSON')
|
|
280
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
281
|
+
.action(async (opts) => {
|
|
282
|
+
const { runGuardrailsSourcesList } = await import('./commands/guardrails.js');
|
|
283
|
+
await runGuardrailsSourcesList(opts);
|
|
284
|
+
});
|
|
285
|
+
guardrailsSourcesCmd
|
|
286
|
+
.command('add <provider>')
|
|
287
|
+
.description('Connect a policy source; the credential is read from --token or --token-file, never echoed')
|
|
288
|
+
.requiredOption('--name <name>', 'Display name for the source')
|
|
289
|
+
.option('--token <token>', 'Provider credential (integration token or API key)')
|
|
290
|
+
.option('--token-file <path>', 'Read the credential from a file (a Google service-account key JSON)')
|
|
291
|
+
.option('--config <json>', 'Provider-specific settings as a JSON object (gdrive: {"folder_id": "…"})')
|
|
292
|
+
.option('--json', 'Output as JSON')
|
|
293
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
294
|
+
.action(async (provider, opts) => {
|
|
295
|
+
const { runGuardrailsSourcesAdd } = await import('./commands/guardrails.js');
|
|
296
|
+
await runGuardrailsSourcesAdd(provider, opts);
|
|
297
|
+
});
|
|
298
|
+
guardrailsSourcesCmd
|
|
299
|
+
.command('sync <connectorId>')
|
|
300
|
+
.description('Pull the source now instead of waiting for the next cron pass')
|
|
301
|
+
.option('--json', 'Output as JSON')
|
|
302
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
303
|
+
.action(async (connectorId, opts) => {
|
|
304
|
+
const { runGuardrailsSourcesSync } = await import('./commands/guardrails.js');
|
|
305
|
+
await runGuardrailsSourcesSync(connectorId, opts);
|
|
306
|
+
});
|
|
307
|
+
const guardrailsDocsCmd = guardrailsCmd
|
|
308
|
+
.command('docs')
|
|
309
|
+
.description('Ingested policy documents and their citable passages');
|
|
310
|
+
guardrailsDocsCmd
|
|
311
|
+
.command('list')
|
|
312
|
+
.description('List ingested policy documents')
|
|
313
|
+
.option('--json', 'Output as JSON')
|
|
314
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
315
|
+
.action(async (opts) => {
|
|
316
|
+
const { runGuardrailsDocsList } = await import('./commands/guardrails.js');
|
|
317
|
+
await runGuardrailsDocsList(opts);
|
|
318
|
+
});
|
|
319
|
+
guardrailsDocsCmd
|
|
320
|
+
.command('show <docId>')
|
|
321
|
+
.description('Show a document, its passages and the clauses extracted from them')
|
|
322
|
+
.option('--json', 'Output as JSON')
|
|
323
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
324
|
+
.action(async (docId, opts) => {
|
|
325
|
+
const { runGuardrailsDocsShow } = await import('./commands/guardrails.js');
|
|
326
|
+
await runGuardrailsDocsShow(docId, opts);
|
|
327
|
+
});
|
|
328
|
+
guardrailsDocsCmd
|
|
329
|
+
.command('extract <docId>')
|
|
330
|
+
.description('Propose cited guardrails from a document (every proposal is re-validated deterministically)')
|
|
331
|
+
.option('--no-llm', 'Only lift existing SOP front matter; do not call the extraction model')
|
|
332
|
+
.option('--json', 'Output as JSON')
|
|
333
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
334
|
+
.action(async (docId, opts) => {
|
|
335
|
+
const { runGuardrailsDocsExtract } = await import('./commands/guardrails.js');
|
|
336
|
+
await runGuardrailsDocsExtract(docId, { ...opts, noLlm: opts.llm === false });
|
|
337
|
+
});
|
|
338
|
+
guardrailsCmd
|
|
339
|
+
.command('search <token>')
|
|
340
|
+
.description('Which passages and guardrails mention a tool or action token (e.g. Bash, terraform); with --text, which passages use these words')
|
|
341
|
+
.option('--text', 'Full-text search over the words of every live passage instead of an exact token')
|
|
342
|
+
.option('--json', 'Output as JSON')
|
|
343
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
344
|
+
.action(async (token, opts) => {
|
|
345
|
+
const { runGuardrailsSearch } = await import('./commands/guardrails.js');
|
|
346
|
+
await runGuardrailsSearch(token, opts);
|
|
347
|
+
});
|
|
348
|
+
guardrailsCmd
|
|
349
|
+
.command('list')
|
|
350
|
+
.description('List guardrails with their status and shadow evidence')
|
|
351
|
+
.option('--status <status>', 'PROPOSED | SHADOW | ENFORCING | REJECTED | RETIRED')
|
|
352
|
+
.option('--target <target>', 'hook_rule | sop_front_matter | wasm_rule')
|
|
353
|
+
.option('--doc <docId>', 'Only guardrails cited from this document')
|
|
354
|
+
.option('--limit <n>', 'Max rows (default 50, capped at 200)')
|
|
355
|
+
.option('--json', 'Output as JSON')
|
|
356
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
357
|
+
.action(async (opts) => {
|
|
358
|
+
const { runGuardrailsList } = await import('./commands/guardrails.js');
|
|
359
|
+
await runGuardrailsList(opts);
|
|
360
|
+
});
|
|
361
|
+
guardrailsCmd
|
|
362
|
+
.command('show <guardrailId>')
|
|
363
|
+
.description('Show a guardrail: cited passage, rendered artifact, validation checks and readiness')
|
|
364
|
+
.option('--json', 'Output as JSON')
|
|
365
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
366
|
+
.action(async (guardrailId, opts) => {
|
|
367
|
+
const { runGuardrailsShow } = await import('./commands/guardrails.js');
|
|
368
|
+
await runGuardrailsShow(guardrailId, opts);
|
|
369
|
+
});
|
|
370
|
+
guardrailsCmd
|
|
371
|
+
.command('approve-shadow <guardrailId>')
|
|
372
|
+
.description('Ship a proposed guardrail in shadow: it reports, never blocks')
|
|
373
|
+
.option('--json', 'Output as JSON')
|
|
374
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
375
|
+
.action(async (guardrailId, opts) => {
|
|
376
|
+
const { runGuardrailsApproveShadow } = await import('./commands/guardrails.js');
|
|
377
|
+
await runGuardrailsApproveShadow(guardrailId, opts);
|
|
378
|
+
});
|
|
379
|
+
guardrailsCmd
|
|
380
|
+
.command('promote <guardrailId>')
|
|
381
|
+
.description('Promote a shadow guardrail to enforcing once the server says it is ready')
|
|
382
|
+
.option('--acknowledge-no-traffic', 'Promote a rule that no observed traffic exercised')
|
|
383
|
+
.option('--json', 'Output as JSON')
|
|
384
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
385
|
+
.action(async (guardrailId, opts) => {
|
|
386
|
+
const { runGuardrailsPromote } = await import('./commands/guardrails.js');
|
|
387
|
+
await runGuardrailsPromote(guardrailId, opts);
|
|
388
|
+
});
|
|
389
|
+
guardrailsCmd
|
|
390
|
+
.command('reject <guardrailId>')
|
|
391
|
+
.description('Reject a guardrail; the reason is recorded on its authority chain')
|
|
392
|
+
.requiredOption('--reason <reason>', 'Why it is rejected')
|
|
393
|
+
.option('--json', 'Output as JSON')
|
|
394
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
395
|
+
.action(async (guardrailId, opts) => {
|
|
396
|
+
const { runGuardrailsReject } = await import('./commands/guardrails.js');
|
|
397
|
+
await runGuardrailsReject(guardrailId, opts);
|
|
398
|
+
});
|
|
399
|
+
guardrailsCmd
|
|
400
|
+
.command('retire <guardrailId>')
|
|
401
|
+
.description('Retire a shadow or enforcing guardrail; it stops being projected')
|
|
402
|
+
.option('--json', 'Output as JSON')
|
|
403
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
404
|
+
.action(async (guardrailId, opts) => {
|
|
405
|
+
const { runGuardrailsRetire } = await import('./commands/guardrails.js');
|
|
406
|
+
await runGuardrailsRetire(guardrailId, opts);
|
|
407
|
+
});
|
|
408
|
+
guardrailsCmd
|
|
409
|
+
.command('reconfirm <guardrailId>')
|
|
410
|
+
.description('Confirm a guardrail whose cited passage changed upstream still holds')
|
|
411
|
+
.option('--json', 'Output as JSON')
|
|
412
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
413
|
+
.action(async (guardrailId, opts) => {
|
|
414
|
+
const { runGuardrailsReconfirm } = await import('./commands/guardrails.js');
|
|
415
|
+
await runGuardrailsReconfirm(guardrailId, opts);
|
|
416
|
+
});
|
|
417
|
+
guardrailsCmd
|
|
418
|
+
.command('replay <guardrailId>')
|
|
419
|
+
.description('Run a guardrail over captured calls and report how many it would have fired on')
|
|
420
|
+
.option('--json', 'Output as JSON')
|
|
421
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
422
|
+
.action(async (guardrailId, opts) => {
|
|
423
|
+
const { runGuardrailsReplay } = await import('./commands/guardrails.js');
|
|
424
|
+
await runGuardrailsReplay(guardrailId, opts);
|
|
425
|
+
});
|
|
426
|
+
guardrailsCmd
|
|
427
|
+
.command('conflicts')
|
|
428
|
+
.description('List guardrails that contradict each other, with both quotes')
|
|
429
|
+
.option('--json', 'Output as JSON')
|
|
430
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
431
|
+
.action(async (opts) => {
|
|
432
|
+
const { runGuardrailsConflicts } = await import('./commands/guardrails.js');
|
|
433
|
+
await runGuardrailsConflicts(opts);
|
|
434
|
+
});
|
|
435
|
+
guardrailsCmd
|
|
436
|
+
.command('impact')
|
|
437
|
+
.description('What a change to a document or passage reaches: passages within five computed edges, the clauses citing them, their guardrails')
|
|
438
|
+
.option('--doc <docId>', 'Start from every live passage of this document')
|
|
439
|
+
.option('--passage <passageId>', 'Start from one passage')
|
|
440
|
+
.option('--json', 'Output as JSON')
|
|
441
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
442
|
+
.action(async (opts) => {
|
|
443
|
+
const { runGuardrailsImpact } = await import('./commands/guardrails.js');
|
|
444
|
+
await runGuardrailsImpact(opts);
|
|
445
|
+
});
|
|
446
|
+
guardrailsCmd
|
|
447
|
+
.command('duplicates')
|
|
448
|
+
.description('Overlapping passage pairs (with their Jaccard arithmetic) and the same rule cited from more than one passage')
|
|
449
|
+
.option('--min-jaccard <n>', 'Only pairs at or above this Jaccard (never below the recorded 0.70)')
|
|
450
|
+
.option('--json', 'Output as JSON')
|
|
451
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
452
|
+
.action(async (opts) => {
|
|
453
|
+
const { runGuardrailsDuplicates } = await import('./commands/guardrails.js');
|
|
454
|
+
await runGuardrailsDuplicates(opts);
|
|
455
|
+
});
|
|
456
|
+
guardrailsCmd
|
|
457
|
+
.command('pull')
|
|
458
|
+
.description('Write the SHADOW and ENFORCING front-matter guardrails to .intutic/sops/guardrail-<id>.md, for a proxy that reads SOPs from disk')
|
|
459
|
+
.option('--force', 'Overwrite a locally-modified guardrail file instead of refusing it')
|
|
460
|
+
.option('--prune', 'Remove guardrail files that are no longer served (unmodified ones only)')
|
|
461
|
+
.option('--json', 'Output as JSON')
|
|
462
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
463
|
+
.action(async (opts) => {
|
|
464
|
+
const { runGuardrailsPull } = await import('./commands/guardrails.js');
|
|
465
|
+
await runGuardrailsPull(opts);
|
|
466
|
+
});
|
|
157
467
|
program
|
|
158
468
|
.command('whoami')
|
|
159
469
|
.description('Show current authenticated identity')
|
|
@@ -172,6 +482,83 @@ program
|
|
|
172
482
|
const { runStart } = await import('./commands/start.js');
|
|
173
483
|
await runStart(opts);
|
|
174
484
|
});
|
|
485
|
+
// L2 mandatory egress firewall (LLD #63 §5). Makes the governing proxy
|
|
486
|
+
// non-optional: default-deny host egress to everything except the proxy, DNS,
|
|
487
|
+
// and declared infra. apply/remove need root; generate/status do not.
|
|
488
|
+
const enforceCmd = program
|
|
489
|
+
.command('enforce')
|
|
490
|
+
.description('Manage the mandatory default-deny egress firewall (forces all traffic through the proxy)');
|
|
491
|
+
for (const [action, desc] of [
|
|
492
|
+
['apply', 'Apply the default-deny egress firewall (root). All egress except the proxy, DNS and --allow infra is dropped.'],
|
|
493
|
+
['remove', 'Remove the Intutic egress firewall (root).'],
|
|
494
|
+
['status', 'Report whether the egress firewall is currently applied.'],
|
|
495
|
+
['generate', 'Print the platform ruleset without applying it (no privilege).'],
|
|
496
|
+
]) {
|
|
497
|
+
enforceCmd
|
|
498
|
+
.command(action)
|
|
499
|
+
.description(desc)
|
|
500
|
+
.option('--port <port>', "The proxy's listener port to permit", '4000')
|
|
501
|
+
.option('--uid <uid>', 'uid the proxy runs as, exempted from the deny (defaults to current user)')
|
|
502
|
+
.option('--allow <cidrs>', 'Comma-separated extra destination CIDRs to permit (control plane, registries, …)')
|
|
503
|
+
.option('--no-dns', 'Also deny outbound DNS (only if a local resolver serves the host)')
|
|
504
|
+
.option('--platform <os>', 'Target ruleset platform: linux | macos | windows (defaults to current OS)')
|
|
505
|
+
.action(async (opts) => {
|
|
506
|
+
const { runEnforce } = await import('./commands/enforce.js');
|
|
507
|
+
await runEnforce(action, opts);
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
enforceCmd
|
|
511
|
+
.command('report')
|
|
512
|
+
.description('Report locally recorded enforcement state (firewall/CA-trust/system-hooks) to the control plane. ' +
|
|
513
|
+
'No privilege needed — for when apply/remove ran elevated and could not reach stored credentials.')
|
|
514
|
+
.action(async () => {
|
|
515
|
+
const { runEnforce } = await import('./commands/enforce.js');
|
|
516
|
+
await runEnforce('report', {});
|
|
517
|
+
});
|
|
518
|
+
// CA-trust rollout, Cursor system hooks, and Jamf/Intune MDM manifest
|
|
519
|
+
// generation for a managed fleet. Deliberately named `enterprise install`,
|
|
520
|
+
// not the old hyphenated `enterprise-install` — that name collides with
|
|
521
|
+
// tools/scripts/intutic-enterprise-install.sh, an unrelated air-gapped
|
|
522
|
+
// docker-compose installer. Does not touch the host firewall; see `enforce`.
|
|
523
|
+
const enterpriseCmd = program
|
|
524
|
+
.command('enterprise')
|
|
525
|
+
.description('Enterprise fleet rollout: CA trust, Cursor system hooks, MDM manifests');
|
|
526
|
+
enterpriseCmd
|
|
527
|
+
.command('install')
|
|
528
|
+
.description('Generate MDM manifests, and (if privileged) install system CA trust + Cursor system hooks')
|
|
529
|
+
.option('--proxy-url <url>', 'Proxy base URL embedded in generated hooks (default: $INTUTIC_PROXY_URL or http://localhost:4000)')
|
|
530
|
+
.option('--generate-mdm-only', 'Only write the MDM manifests; skip CA trust and system hooks (no privilege needed)')
|
|
531
|
+
.option('--mdm-output-dir <dir>', 'Directory to write generated MDM manifests to', './intutic-mdm')
|
|
532
|
+
.option('--skip-ca', 'Skip system-wide CA trust installation')
|
|
533
|
+
.option('--skip-hooks', 'Skip system-level Cursor hooks installation')
|
|
534
|
+
.option('--dev', 'Use local control plane (http://localhost:3001) when resolving workspace context')
|
|
535
|
+
.option('--cli-binary-path <path>', 'Absolute path to the intutic CLI binary on TARGET machines, embedded in the firewall-deployment manifests (default: /usr/local/bin/intutic — override if your fleet installs elsewhere)')
|
|
536
|
+
.action(async (opts) => {
|
|
537
|
+
const { runEnterpriseInstall } = await import('./commands/enterprise.js');
|
|
538
|
+
await runEnterpriseInstall(opts);
|
|
539
|
+
});
|
|
540
|
+
const envCmd = program
|
|
541
|
+
.command('env')
|
|
542
|
+
.description('Persist or clear the proxy base-URL environment variables');
|
|
543
|
+
envCmd
|
|
544
|
+
.command('persist')
|
|
545
|
+
.description('Write ANTHROPIC_BASE_URL / OPENAI_BASE_URL at the OS level so they survive a restart.\n' +
|
|
546
|
+
' macOS: launchctl setenv Linux: ~/.bashrc Windows: setx\n' +
|
|
547
|
+
'\n' +
|
|
548
|
+
' Opt-in on purpose: on macOS this reaches every GUI app you launch afterwards.\n' +
|
|
549
|
+
' For a single command instead, use \'intutic exec\'.')
|
|
550
|
+
.option('--proxy-url <url>', 'Proxy base URL to point the variables at', 'http://localhost:4000')
|
|
551
|
+
.action(async (opts) => {
|
|
552
|
+
const { runEnvPersist } = await import('./commands/env.js');
|
|
553
|
+
await runEnvPersist({ proxyUrl: opts.proxyUrl });
|
|
554
|
+
});
|
|
555
|
+
envCmd
|
|
556
|
+
.command('clear')
|
|
557
|
+
.description('Remove the variables written by \'intutic env persist\'')
|
|
558
|
+
.action(async () => {
|
|
559
|
+
const { runEnvClear } = await import('./commands/env.js');
|
|
560
|
+
await runEnvClear();
|
|
561
|
+
});
|
|
175
562
|
program
|
|
176
563
|
.command('connect')
|
|
177
564
|
.description('Start sync daemon — bidirectional config sync with control plane (requires an account)')
|
|
@@ -194,13 +581,49 @@ program
|
|
|
194
581
|
const { runSyncContext } = await import('./commands/syncContext.js');
|
|
195
582
|
await runSyncContext(opts);
|
|
196
583
|
});
|
|
584
|
+
program
|
|
585
|
+
.command('decisions-log-refresh')
|
|
586
|
+
.description('One-shot refresh of the governed decisions log (.intutic/DECISIONS.md + the claude-code ' +
|
|
587
|
+
'harness config section) — no-ops if decisionsLogEnabled is off. Invoked by the optional ' +
|
|
588
|
+
'post-merge hook (see gitHooks.ts); safe to run manually.')
|
|
589
|
+
.action(async () => {
|
|
590
|
+
const { refreshDecisionsLog } = await import('./lib/decisionsLogRefresh.js');
|
|
591
|
+
const result = await refreshDecisionsLog(process.cwd());
|
|
592
|
+
if (!result.refreshed && result.reason) {
|
|
593
|
+
const { log } = await import('./lib/logger.js');
|
|
594
|
+
log.dim(`Decisions log not refreshed: ${result.reason}`);
|
|
595
|
+
}
|
|
596
|
+
});
|
|
197
597
|
program
|
|
198
598
|
.command('exec')
|
|
199
599
|
.description('Execute a command wrapped with Intutic proxy environment variables')
|
|
200
600
|
.argument('[command...]', 'Command and arguments to execute (e.g. -- claude)')
|
|
201
|
-
|
|
601
|
+
// Run the agent inside an isolated sandbox whose ONLY egress is the proxy
|
|
602
|
+
// (LLD #63 §6): cap-drop, no-new-privileges, read-only rootfs, resource caps,
|
|
603
|
+
// and a default-deny egress firewall the agent cannot undo.
|
|
604
|
+
.option('--sandbox [kind]', 'Run the agent in an isolated sandbox (kind: oci | firecracker; default oci)')
|
|
605
|
+
.option('--sandbox-image <image>', 'Sandbox image (must contain the agent + nftables + capsh)', 'intutic/sandbox:latest')
|
|
606
|
+
.option('--sandbox-memory <size>', 'Sandbox memory cap (e.g. 2g)', '2g')
|
|
607
|
+
.option('--sandbox-cpus <n>', 'Sandbox CPU cap', '2')
|
|
608
|
+
.option('--sandbox-pids <n>', 'Sandbox max process count', '512')
|
|
609
|
+
.option('--sandbox-allow <cidrs>', 'Comma-separated extra destination CIDRs the sandbox may reach')
|
|
610
|
+
.action(async (commandAndArgs, opts) => {
|
|
202
611
|
const { runExec } = await import('./commands/exec.js');
|
|
203
|
-
|
|
612
|
+
if (opts.sandbox) {
|
|
613
|
+
await runExec(commandAndArgs, {
|
|
614
|
+
kind: typeof opts.sandbox === 'string' ? opts.sandbox : 'oci',
|
|
615
|
+
image: opts.sandboxImage,
|
|
616
|
+
memory: opts.sandboxMemory,
|
|
617
|
+
cpus: opts.sandboxCpus,
|
|
618
|
+
pidsLimit: Number.parseInt(opts.sandboxPids, 10),
|
|
619
|
+
allow: opts.sandboxAllow
|
|
620
|
+
? String(opts.sandboxAllow).split(',').map((s) => s.trim()).filter(Boolean)
|
|
621
|
+
: [],
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
else {
|
|
625
|
+
await runExec(commandAndArgs);
|
|
626
|
+
}
|
|
204
627
|
});
|
|
205
628
|
const traces = program
|
|
206
629
|
.command('traces')
|
|
@@ -211,7 +634,8 @@ traces
|
|
|
211
634
|
.description('List execution traces for the workspace')
|
|
212
635
|
.option('--limit <n>', 'Number of traces to show (default: 20, max: 100)')
|
|
213
636
|
.option('--since <duration>', 'Time window, e.g. "24h", "7d", "30m" (default: "24h")')
|
|
214
|
-
.option('--action <type>', 'Filter by enforcement action (BYPASS|ENHANCE|HIJACK|KILL)')
|
|
637
|
+
.option('--action <type>', 'Filter by enforcement action (BYPASS|ENHANCE|HIJACK|KILL) — connected mode only')
|
|
638
|
+
.option('--verdict <type>', 'Filter by verdict (allowed|killed|upstream_error|reasked|hijacked) — local mode only')
|
|
215
639
|
.option('--model <name>', 'Filter by model name')
|
|
216
640
|
.option('--json', 'Output as JSON instead of table')
|
|
217
641
|
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
@@ -227,10 +651,115 @@ traces
|
|
|
227
651
|
const { runTracesInspect } = await import('./commands/traces.js');
|
|
228
652
|
await runTracesInspect(traceId, opts);
|
|
229
653
|
});
|
|
654
|
+
// ── Findings (detector_findings adjudication) ───────────────────────────────
|
|
655
|
+
const findings = program
|
|
656
|
+
.command('findings')
|
|
657
|
+
.description('Query and adjudicate detector findings — the TP/FP labeling surface');
|
|
658
|
+
findings
|
|
659
|
+
.command('list')
|
|
660
|
+
.description('List detector findings for the workspace')
|
|
661
|
+
.option('--unadjudicated', "Only show findings nobody has ruled on yet (the route's own default is to show all)")
|
|
662
|
+
.option('--detector <id>', 'Filter by detector_id (e.g. response_injection:override-instructions)')
|
|
663
|
+
.option('--limit <n>', 'Max rows to return (default: 100, max: 500)')
|
|
664
|
+
.option('--json', 'Output as JSON instead of table')
|
|
665
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
666
|
+
.action(async (opts) => {
|
|
667
|
+
const { runFindingsList } = await import('./commands/findings.js');
|
|
668
|
+
await runFindingsList(opts);
|
|
669
|
+
});
|
|
670
|
+
findings
|
|
671
|
+
.command('adjudicate <findingId>')
|
|
672
|
+
.description('Record a human ruling (true/false positive) on one finding')
|
|
673
|
+
.option('--true-positive', 'Mark this finding a true positive')
|
|
674
|
+
.option('--false-positive', 'Mark this finding a false positive')
|
|
675
|
+
.option('--note <text>', 'Optional note recorded with the ruling')
|
|
676
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
677
|
+
.action(async (findingId, opts) => {
|
|
678
|
+
const { runFindingsAdjudicate } = await import('./commands/findings.js');
|
|
679
|
+
await runFindingsAdjudicate(findingId, opts);
|
|
680
|
+
});
|
|
681
|
+
findings
|
|
682
|
+
.command('stats')
|
|
683
|
+
.description('Per-detector false-positive rate, computed over adjudicated findings only')
|
|
684
|
+
.option('--json', 'Output as JSON instead of table')
|
|
685
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
686
|
+
.action(async (opts) => {
|
|
687
|
+
const { runFindingsStats } = await import('./commands/findings.js');
|
|
688
|
+
await runFindingsStats(opts);
|
|
689
|
+
});
|
|
690
|
+
findings
|
|
691
|
+
.command('echo-report')
|
|
692
|
+
.description('Response-injection echo measurement report — per-pattern false-positive rate')
|
|
693
|
+
.option('--since <date>', 'ISO date — window start (route default: trailing 7 days)')
|
|
694
|
+
.option('--until <date>', 'ISO date — window end (route default: now)')
|
|
695
|
+
.option('--json', 'Output as JSON instead of table')
|
|
696
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
697
|
+
.action(async (opts) => {
|
|
698
|
+
const { runFindingsEchoReport } = await import('./commands/findings.js');
|
|
699
|
+
await runFindingsEchoReport(opts);
|
|
700
|
+
});
|
|
701
|
+
// ── Trace integrity ────────────────────────────────────────────────────────
|
|
702
|
+
const integrity = program
|
|
703
|
+
.command('integrity')
|
|
704
|
+
.description('Verify sealed trace roots — list, re-derive, walk the root chain, and walk the config snapshot chain');
|
|
705
|
+
integrity
|
|
706
|
+
.command('roots')
|
|
707
|
+
.description('List sealed Merkle roots for the workspace, newest first')
|
|
708
|
+
.option('--loop-run <id>', 'Only roots sealed for this loop run')
|
|
709
|
+
.option('--json', 'Output as JSON instead of table')
|
|
710
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
711
|
+
.action(async (opts) => {
|
|
712
|
+
const { runIntegrityRoots } = await import('./commands/integrity.js');
|
|
713
|
+
await runIntegrityRoots(opts);
|
|
714
|
+
});
|
|
715
|
+
integrity
|
|
716
|
+
.command('verify <root_id>')
|
|
717
|
+
.description('Re-derive a root from the live traces and check its signature (exit 1 on mismatch)')
|
|
718
|
+
.option('--json', 'Output as JSON instead of a report')
|
|
719
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
720
|
+
.action(async (rootId, opts) => {
|
|
721
|
+
const { runIntegrityVerify } = await import('./commands/integrity.js');
|
|
722
|
+
await runIntegrityVerify(rootId, opts);
|
|
723
|
+
});
|
|
724
|
+
integrity
|
|
725
|
+
.command('chain')
|
|
726
|
+
.description('Walk the previous_root chain and report deleted roots (exit 1 on a break)')
|
|
727
|
+
.option('--json', 'Output as JSON instead of a report')
|
|
728
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
729
|
+
.action(async (opts) => {
|
|
730
|
+
const { runIntegrityChain } = await import('./commands/integrity.js');
|
|
731
|
+
await runIntegrityChain(opts);
|
|
732
|
+
});
|
|
733
|
+
integrity
|
|
734
|
+
.command('config-chain')
|
|
735
|
+
.description('Walk the harness config snapshot chain and re-hash each stored body ' +
|
|
736
|
+
'(exit 1 on a break or a content mismatch)')
|
|
737
|
+
.option('--json', 'Output as JSON instead of a report')
|
|
738
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
739
|
+
.action(async (opts) => {
|
|
740
|
+
const { runIntegrityConfigChain } = await import('./commands/integrity.js');
|
|
741
|
+
await runIntegrityConfigChain(opts);
|
|
742
|
+
});
|
|
743
|
+
// ── Routing reports ─────────────────────────────────────────────────────────
|
|
744
|
+
const routing = program
|
|
745
|
+
.command('routing')
|
|
746
|
+
.description('Read-only reports for the routing subsystem');
|
|
747
|
+
routing
|
|
748
|
+
.command('adoption-report')
|
|
749
|
+
.description('Mirror-test adoption report for one candidate model — win/loss/tie, fault-rate delta, ' +
|
|
750
|
+
'cost delta, latency delta (Phase 7b: GET /api/v1/routing/mirror-adoption-report). ' +
|
|
751
|
+
'A reported signal for human review, not an automatic gate.')
|
|
752
|
+
.requiredOption('--candidate-model <model>', 'The mirror-tested candidate model to report on')
|
|
753
|
+
.option('--json', 'Output as JSON instead of a report')
|
|
754
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
755
|
+
.action(async (opts) => {
|
|
756
|
+
const { runRoutingAdoptionReport } = await import('./commands/routing.js');
|
|
757
|
+
await runRoutingAdoptionReport(opts);
|
|
758
|
+
});
|
|
230
759
|
// ── Daemon persistence (WS-5 — Q3 Layer 4) ─────────────────────────────────
|
|
231
760
|
const daemon = program
|
|
232
761
|
.command('daemon')
|
|
233
|
-
.description('Manage the Intutic sync-daemon system
|
|
762
|
+
.description('Manage the Intutic sync-daemon, MCP daemon and standalone proxy system services (LaunchAgent / systemd)');
|
|
234
763
|
daemon
|
|
235
764
|
.command('install')
|
|
236
765
|
.alias('install-daemon') // top-level alias for discoverability
|
|
@@ -238,17 +767,52 @@ daemon
|
|
|
238
767
|
' macOS: ~/Library/LaunchAgents/ai.intutic.sync-daemon.plist (KeepAlive: true)\n' +
|
|
239
768
|
' Linux: ~/.config/systemd/user/intutic-sync-daemon.service (Restart=always)\n' +
|
|
240
769
|
'\n' +
|
|
770
|
+
' --proxy installs the standalone intutic-proxy binary instead (no workspace or key needed):\n' +
|
|
771
|
+
' macOS: ~/Library/LaunchAgents/ai.intutic.proxy.plist\n' +
|
|
772
|
+
' Linux: ~/.config/systemd/user/intutic-proxy.service\n' +
|
|
773
|
+
'\n' +
|
|
241
774
|
' NOTE: To stop the daemon you MUST use \'intutic daemon uninstall\' or \'launchctl unload\'.\n' +
|
|
242
775
|
' \'intutic disconnect\' alone will NOT stop a daemon-installed service. (TD-154)')
|
|
243
|
-
.
|
|
244
|
-
.
|
|
776
|
+
.option('--workspace-id <id>', 'Workspace ID (e.g. wk_xxxx) — required unless --proxy')
|
|
777
|
+
.option('--api-key <key>', 'Workspace API key (e.g. vk_xxxx) — required unless --proxy')
|
|
245
778
|
.option('--control-plane-url <url>', 'Control plane URL', 'https://api.intutic.ai')
|
|
246
|
-
.option('--binary-path <path>', 'Path to intutic CLI binary (defaults to current process)')
|
|
779
|
+
.option('--binary-path <path>', 'Path to intutic CLI binary (defaults to current process); with --proxy, absolute path to intutic-proxy')
|
|
247
780
|
.option('--dry-run', 'Print what would be done without writing files')
|
|
248
781
|
.option('--system', 'Install as a system-level service (LaunchDaemon on macOS, systemd system unit on Linux)')
|
|
249
|
-
.
|
|
250
|
-
|
|
251
|
-
|
|
782
|
+
.option('--mcp', 'Install the MCP proxy daemon instead of the sync-daemon')
|
|
783
|
+
.option('--proxy', 'Install the standalone intutic-proxy binary as a service (TD-465)')
|
|
784
|
+
.option('--port <port>', 'With --proxy: proxy listen port', '4000')
|
|
785
|
+
.option('--valkey-url <url>', 'With --proxy: Valkey URL to attach to; omit to run standalone (INTUTIC_STANDALONE=1)')
|
|
786
|
+
.option('--upstream-url <url>', 'With --proxy: upstream LLM provider base URL')
|
|
787
|
+
.action(async (opts, cmd) => {
|
|
788
|
+
if (opts.proxy) {
|
|
789
|
+
// The standalone proxy has no control plane, so no workspace or key.
|
|
790
|
+
// Its unit carries the same environment `intutic start` sets, fixed at
|
|
791
|
+
// install time rather than probed at launch (TD-465).
|
|
792
|
+
const { installProxyService } = await import('./commands/install-daemon.js');
|
|
793
|
+
await installProxyService({
|
|
794
|
+
port: opts.port,
|
|
795
|
+
valkeyUrl: opts.valkeyUrl,
|
|
796
|
+
upstreamUrl: opts.upstreamUrl,
|
|
797
|
+
binaryPath: opts.binaryPath,
|
|
798
|
+
dryRun: opts.dryRun,
|
|
799
|
+
system: opts.system,
|
|
800
|
+
});
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
// Required for the two daemons; commander's `requiredOption` cannot express
|
|
804
|
+
// "unless --proxy", so the check lives here with the same wording.
|
|
805
|
+
if (!opts.workspaceId)
|
|
806
|
+
cmd.error("error: required option '--workspace-id <id>' not specified");
|
|
807
|
+
if (!opts.apiKey)
|
|
808
|
+
cmd.error("error: required option '--api-key <key>' not specified");
|
|
809
|
+
// `installMcpDaemon` and `buildMcpPlist` were written, tested and exported,
|
|
810
|
+
// and then nothing called them: every route into install-daemon.ts landed
|
|
811
|
+
// on `installDaemon`, so the MCP proxy daemon could not be installed by any
|
|
812
|
+
// command the CLI offered (TD-153). This flag is that missing route.
|
|
813
|
+
const { installDaemon, installMcpDaemon } = await import('./commands/install-daemon.js');
|
|
814
|
+
const install = opts.mcp ? installMcpDaemon : installDaemon;
|
|
815
|
+
await install({
|
|
252
816
|
workspaceId: opts.workspaceId,
|
|
253
817
|
apiKey: opts.apiKey,
|
|
254
818
|
controlPlaneUrl: opts.controlPlaneUrl,
|
|
@@ -263,30 +827,48 @@ daemon
|
|
|
263
827
|
.description('Remove the sync-daemon system service and stop it permanently.')
|
|
264
828
|
.option('--dry-run', 'Print what would be done without writing files')
|
|
265
829
|
.option('--system', 'Uninstall the system-level service')
|
|
830
|
+
.option('--mcp', 'Uninstall the MCP proxy daemon instead of the sync-daemon')
|
|
831
|
+
.option('--proxy', 'Uninstall the standalone intutic-proxy service instead of the sync-daemon')
|
|
266
832
|
.action(async (opts) => {
|
|
267
|
-
const { uninstallDaemon } = await import('./commands/install-daemon.js');
|
|
268
|
-
|
|
833
|
+
const { uninstallDaemon, uninstallMcpDaemon, uninstallProxyService } = await import('./commands/install-daemon.js');
|
|
834
|
+
const uninstall = opts.proxy ? uninstallProxyService : opts.mcp ? uninstallMcpDaemon : uninstallDaemon;
|
|
835
|
+
await uninstall({ dryRun: opts.dryRun, system: opts.system });
|
|
269
836
|
});
|
|
270
837
|
daemon
|
|
271
838
|
.command('status')
|
|
272
|
-
.description('Show sync-daemon system service status.')
|
|
273
|
-
.
|
|
274
|
-
|
|
275
|
-
|
|
839
|
+
.description('Show sync-daemon system service status (or the standalone proxy service with --proxy).')
|
|
840
|
+
.option('--proxy', 'Show the standalone intutic-proxy service instead of the sync-daemon')
|
|
841
|
+
.option('--mcp', 'Show the MCP proxy daemon instead of the sync-daemon')
|
|
842
|
+
.action(async (opts) => {
|
|
843
|
+
// `mcpDaemonStatus` was exported and never called by any command until the
|
|
844
|
+
// reachability test widened to the status/stop/start verbs (TD-153's
|
|
845
|
+
// shape, one more time).
|
|
846
|
+
const { daemonStatus, proxyServiceStatus, mcpDaemonStatus } = await import('./commands/install-daemon.js');
|
|
847
|
+
await (opts.proxy ? proxyServiceStatus() : opts.mcp ? mcpDaemonStatus() : daemonStatus());
|
|
276
848
|
});
|
|
277
849
|
daemon
|
|
278
850
|
.command('stop')
|
|
279
|
-
.description('Stop and unload the sync-daemon system service.')
|
|
280
|
-
.
|
|
281
|
-
|
|
282
|
-
|
|
851
|
+
.description('Stop and unload the sync-daemon system service (or the standalone proxy service with --proxy).')
|
|
852
|
+
.option('--proxy', 'Stop the standalone intutic-proxy service instead of the sync-daemon')
|
|
853
|
+
.option('--mcp', 'Stop the MCP proxy daemon instead of the sync-daemon')
|
|
854
|
+
.action(async (opts) => {
|
|
855
|
+
// `mcpDaemonStop` was exported and never called by any command until the
|
|
856
|
+
// reachability test widened to the status/stop/start verbs (TD-153's
|
|
857
|
+
// shape, one more time).
|
|
858
|
+
const { daemonStop, proxyServiceStop, mcpDaemonStop } = await import('./commands/install-daemon.js');
|
|
859
|
+
await (opts.proxy ? proxyServiceStop() : opts.mcp ? mcpDaemonStop() : daemonStop());
|
|
283
860
|
});
|
|
284
861
|
daemon
|
|
285
862
|
.command('start')
|
|
286
|
-
.description('Start and load the sync-daemon system service.')
|
|
287
|
-
.
|
|
288
|
-
|
|
289
|
-
|
|
863
|
+
.description('Start and load the sync-daemon system service (or the standalone proxy service with --proxy).')
|
|
864
|
+
.option('--proxy', 'Start the standalone intutic-proxy service instead of the sync-daemon')
|
|
865
|
+
.option('--mcp', 'Start the MCP proxy daemon instead of the sync-daemon')
|
|
866
|
+
.action(async (opts) => {
|
|
867
|
+
// `mcpDaemonStart` was exported and never called by any command until the
|
|
868
|
+
// reachability test widened to the status/stop/start verbs (TD-153's
|
|
869
|
+
// shape, one more time).
|
|
870
|
+
const { daemonStart, proxyServiceStart, mcpDaemonStart } = await import('./commands/install-daemon.js');
|
|
871
|
+
await (opts.proxy ? proxyServiceStart() : opts.mcp ? mcpDaemonStart() : daemonStart());
|
|
290
872
|
});
|
|
291
873
|
// Top-level shortcuts (for discoverability)
|
|
292
874
|
program
|
|
@@ -314,9 +896,12 @@ program
|
|
|
314
896
|
.description('Shortcut for \'intutic daemon uninstall\'')
|
|
315
897
|
.option('--dry-run', 'Print what would be done without writing files')
|
|
316
898
|
.option('--system', 'Uninstall the system-level service')
|
|
899
|
+
.option('--mcp', 'Uninstall the MCP proxy daemon instead of the sync-daemon')
|
|
900
|
+
.option('--proxy', 'Uninstall the standalone intutic-proxy service instead of the sync-daemon')
|
|
317
901
|
.action(async (opts) => {
|
|
318
|
-
const { uninstallDaemon } = await import('./commands/install-daemon.js');
|
|
319
|
-
|
|
902
|
+
const { uninstallDaemon, uninstallMcpDaemon, uninstallProxyService } = await import('./commands/install-daemon.js');
|
|
903
|
+
const uninstall = opts.proxy ? uninstallProxyService : opts.mcp ? uninstallMcpDaemon : uninstallDaemon;
|
|
904
|
+
await uninstall({ dryRun: opts.dryRun, system: opts.system });
|
|
320
905
|
});
|
|
321
906
|
// ── Skill commands ─────────────────────────────────────────────────────────
|
|
322
907
|
const skillCmd = program
|
|
@@ -332,9 +917,19 @@ skillCmd
|
|
|
332
917
|
skillCmd
|
|
333
918
|
.command('audit')
|
|
334
919
|
.description('Audit local rules/skills for security leakage or unsafe command patterns')
|
|
335
|
-
.
|
|
920
|
+
.option('--sarif', 'Output findings as a SARIF 2.1.0 document (for GitHub Code Scanning / CI interop) instead of the human-readable report')
|
|
921
|
+
.option('--engine <engine>', "Scanning engine: 'native' (default, always runs) or 'cisco' to also run the opt-in Cisco " +
|
|
922
|
+
"skill-scanner integration (requires the 'skill-scanner' binary on PATH — pipx install cisco-ai-skill-scanner)", 'native')
|
|
923
|
+
.action(async (opts) => {
|
|
336
924
|
const { runSkillAudit } = await import('./commands/skill.js');
|
|
337
|
-
await runSkillAudit();
|
|
925
|
+
await runSkillAudit({ sarif: opts.sarif, engine: opts.engine });
|
|
926
|
+
});
|
|
927
|
+
skillCmd
|
|
928
|
+
.command('scan-staged')
|
|
929
|
+
.description('Warn-only content scan of staged skill-surface additions (.agents/skills, .claude/skills) — used by the pre-commit hook, never blocks the commit')
|
|
930
|
+
.action(async () => {
|
|
931
|
+
const { runSkillScanStaged } = await import('./commands/skill.js');
|
|
932
|
+
await runSkillScanStaged();
|
|
338
933
|
});
|
|
339
934
|
// ── Loop commands ──────────────────────────────────────────────────────────
|
|
340
935
|
const loopCmd = program
|
|
@@ -368,6 +963,17 @@ loopCmd
|
|
|
368
963
|
const { runLoopKill } = await import('./commands/skill.js');
|
|
369
964
|
await runLoopKill(loopRunId, opts);
|
|
370
965
|
});
|
|
966
|
+
loopCmd
|
|
967
|
+
.command('review <loopRunId>')
|
|
968
|
+
.description('Approve or reject a loop run held for human review')
|
|
969
|
+
.option('--approve', 'Release the hold; the run resumes')
|
|
970
|
+
.option('--reject', 'Refuse the hold; the run is killed')
|
|
971
|
+
.option('--note <note>', 'Why, recorded against the run')
|
|
972
|
+
.option('--dev', 'Target the local control plane')
|
|
973
|
+
.action(async (loopRunId, opts) => {
|
|
974
|
+
const { runLoopReview } = await import('./commands/skill.js');
|
|
975
|
+
await runLoopReview(loopRunId, opts);
|
|
976
|
+
});
|
|
371
977
|
loopCmd
|
|
372
978
|
.command('list')
|
|
373
979
|
.description('List loop runs and cost accounting details for the workspace')
|
|
@@ -389,5 +995,237 @@ loopCmd
|
|
|
389
995
|
const { runLoopExec } = await import('./commands/skill.js');
|
|
390
996
|
await runLoopExec(commandAndArgs, opts);
|
|
391
997
|
});
|
|
998
|
+
// ── Decision commands ────────────────────────────────────────────────────
|
|
999
|
+
//
|
|
1000
|
+
// The real front door for a review_before hold. See commands/decision.ts's
|
|
1001
|
+
// module doc for why `intutic loop review` was never the right command for
|
|
1002
|
+
// this — it addresses a different id space (Loop Runs, not decisions).
|
|
1003
|
+
const decisionCmd = program
|
|
1004
|
+
.command('decision')
|
|
1005
|
+
.description('Approve or reject a decision (e.g. a review_before hold) held for human review');
|
|
1006
|
+
decisionCmd
|
|
1007
|
+
.command('approve <holdId>')
|
|
1008
|
+
.description('Approve a held decision; may also unblock the exact retried call, if the workspace opted in')
|
|
1009
|
+
.option('--reason <reason>', 'Why, recorded against the decision')
|
|
1010
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
1011
|
+
.action(async (holdId, opts) => {
|
|
1012
|
+
const { runDecisionApprove } = await import('./commands/decision.js');
|
|
1013
|
+
await runDecisionApprove(holdId, opts);
|
|
1014
|
+
});
|
|
1015
|
+
decisionCmd
|
|
1016
|
+
.command('reject <holdId>')
|
|
1017
|
+
.description('Reject a held decision')
|
|
1018
|
+
.option('--reason <reason>', 'Why, recorded against the decision')
|
|
1019
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
1020
|
+
.action(async (holdId, opts) => {
|
|
1021
|
+
const { runDecisionReject } = await import('./commands/decision.js');
|
|
1022
|
+
await runDecisionReject(holdId, opts);
|
|
1023
|
+
});
|
|
1024
|
+
// ── Gateway commands (LLD #66) ───────────────────────────────────────────
|
|
1025
|
+
//
|
|
1026
|
+
// Self-hosted gateway registration/management. Previously reachable only by
|
|
1027
|
+
// hand-written curl against services/control-plane/src/routes/gateways.ts —
|
|
1028
|
+
// see commands/gateway.ts's module doc.
|
|
1029
|
+
const gatewayCmd = program
|
|
1030
|
+
.command('gateway')
|
|
1031
|
+
.description('Manage self-hosted gateway registrations (Docker / Kubernetes / bare-metal)');
|
|
1032
|
+
gatewayCmd
|
|
1033
|
+
.command('register')
|
|
1034
|
+
.description('Register a new self-hosted gateway and print its one-time gwk_ token')
|
|
1035
|
+
.requiredOption('--name <name>', 'Display name for this gateway')
|
|
1036
|
+
.requiredOption('--target <docker|kubernetes|bare_metal>', 'Deployment target')
|
|
1037
|
+
.option('--json', 'Output as JSON')
|
|
1038
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
1039
|
+
.action(async (opts) => {
|
|
1040
|
+
const { runGatewayRegister } = await import('./commands/gateway.js');
|
|
1041
|
+
await runGatewayRegister(opts);
|
|
1042
|
+
});
|
|
1043
|
+
gatewayCmd
|
|
1044
|
+
.command('list')
|
|
1045
|
+
.description("List the org's registered gateways")
|
|
1046
|
+
.option('--json', 'Output as JSON')
|
|
1047
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
1048
|
+
.action(async (opts) => {
|
|
1049
|
+
const { runGatewayList } = await import('./commands/gateway.js');
|
|
1050
|
+
await runGatewayList(opts);
|
|
1051
|
+
});
|
|
1052
|
+
gatewayCmd
|
|
1053
|
+
.command('status <gateway_id>')
|
|
1054
|
+
.description('Live heartbeat-derived status for one gateway')
|
|
1055
|
+
.option('--json', 'Output as JSON')
|
|
1056
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
1057
|
+
.action(async (gatewayId, opts) => {
|
|
1058
|
+
const { runGatewayStatus } = await import('./commands/gateway.js');
|
|
1059
|
+
await runGatewayStatus(gatewayId, opts);
|
|
1060
|
+
});
|
|
1061
|
+
gatewayCmd
|
|
1062
|
+
.command('rotate <gateway_id>')
|
|
1063
|
+
.description('Issue a new gwk_ token; the old one keeps working during the rotation grace period')
|
|
1064
|
+
.option('--json', 'Output as JSON')
|
|
1065
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
1066
|
+
.action(async (gatewayId, opts) => {
|
|
1067
|
+
const { runGatewayRotate } = await import('./commands/gateway.js');
|
|
1068
|
+
await runGatewayRotate(gatewayId, opts);
|
|
1069
|
+
});
|
|
1070
|
+
gatewayCmd
|
|
1071
|
+
.command('revoke <gateway_id>')
|
|
1072
|
+
.description('Revoke a gateway immediately (kills any active rotation grace period too)')
|
|
1073
|
+
.option('--reason <text>', 'Recorded in the audit log')
|
|
1074
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
1075
|
+
.action(async (gatewayId, opts) => {
|
|
1076
|
+
const { runGatewayRevoke } = await import('./commands/gateway.js');
|
|
1077
|
+
await runGatewayRevoke(gatewayId, opts);
|
|
1078
|
+
});
|
|
1079
|
+
const gatewayConfigCmd = gatewayCmd
|
|
1080
|
+
.command('config')
|
|
1081
|
+
.description('Manage a gateway\'s remote config (requireVk, requireProvisionedKey)');
|
|
1082
|
+
gatewayConfigCmd
|
|
1083
|
+
.command('set <gateway_id>')
|
|
1084
|
+
.description('Update one or both config flags on a gateway')
|
|
1085
|
+
.option('--require-vk <true|false>', 'Refuse non-vk_ bearer tokens at this gateway')
|
|
1086
|
+
.option('--require-provisioned-key <true|false>', 'Refuse workspaces with no provisioned upstream key')
|
|
1087
|
+
.option('--json', 'Output as JSON')
|
|
1088
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
1089
|
+
.action(async (gatewayId, opts) => {
|
|
1090
|
+
const { runGatewayConfigSet } = await import('./commands/gateway.js');
|
|
1091
|
+
await runGatewayConfigSet(gatewayId, opts);
|
|
1092
|
+
});
|
|
1093
|
+
gatewayCmd
|
|
1094
|
+
.command('assign')
|
|
1095
|
+
.description('Assign (or clear) the gateway this workspace or org defaults to')
|
|
1096
|
+
.option('--gateway <gateway_id>', 'Gateway to assign')
|
|
1097
|
+
.option('--clear', 'Clear the current assignment instead of setting one')
|
|
1098
|
+
.option('--org <org_id>', 'Set the ORG default instead of this workspace\'s own override')
|
|
1099
|
+
.option('--json', 'Output as JSON')
|
|
1100
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
1101
|
+
.action(async (opts) => {
|
|
1102
|
+
const { runGatewayAssign } = await import('./commands/gateway.js');
|
|
1103
|
+
await runGatewayAssign(opts);
|
|
1104
|
+
});
|
|
1105
|
+
gatewayCmd
|
|
1106
|
+
.command('resolve')
|
|
1107
|
+
.description('Show which gateway this workspace currently resolves to (own override, org default, or shared)')
|
|
1108
|
+
.option('--json', 'Output as JSON')
|
|
1109
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
1110
|
+
.action(async (opts) => {
|
|
1111
|
+
const { runGatewayResolve } = await import('./commands/gateway.js');
|
|
1112
|
+
await runGatewayResolve(opts);
|
|
1113
|
+
});
|
|
1114
|
+
// ── Provider credentials (LLD #64 §4, LLD #67) ───────────────────────────
|
|
1115
|
+
//
|
|
1116
|
+
// Provision a workspace's own upstream provider keys — the BYO-key wizard's
|
|
1117
|
+
// API, previously dashboard-only. See commands/credentials.ts's module doc.
|
|
1118
|
+
const credentialsCmd = program
|
|
1119
|
+
.command('credentials')
|
|
1120
|
+
.description('Manage this workspace\'s own upstream provider API keys (BYO-key)');
|
|
1121
|
+
credentialsCmd
|
|
1122
|
+
.command('list')
|
|
1123
|
+
.description('Provisioning status for every registry provider')
|
|
1124
|
+
.option('--json', 'Output as JSON')
|
|
1125
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
1126
|
+
.action(async (opts) => {
|
|
1127
|
+
const { runCredentialsList } = await import('./commands/credentials.js');
|
|
1128
|
+
await runCredentialsList(opts);
|
|
1129
|
+
});
|
|
1130
|
+
credentialsCmd
|
|
1131
|
+
.command('set <provider>')
|
|
1132
|
+
.description('Provision or rotate a provider credential (e.g. --field apiKey=sk-ant-...)')
|
|
1133
|
+
.option('--field <key=value>', 'A credential field; repeat for multi-field providers', (v, prev) => [...prev, v], [])
|
|
1134
|
+
.option('--json', 'Output as JSON')
|
|
1135
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
1136
|
+
.action(async (provider, opts) => {
|
|
1137
|
+
const { runCredentialsSet } = await import('./commands/credentials.js');
|
|
1138
|
+
await runCredentialsSet(provider, opts);
|
|
1139
|
+
});
|
|
1140
|
+
credentialsCmd
|
|
1141
|
+
.command('unset <provider>')
|
|
1142
|
+
.description('Remove a provisioned provider credential')
|
|
1143
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
1144
|
+
.action(async (provider, opts) => {
|
|
1145
|
+
const { runCredentialsUnset } = await import('./commands/credentials.js');
|
|
1146
|
+
await runCredentialsUnset(provider, opts);
|
|
1147
|
+
});
|
|
1148
|
+
// ── DCT Token Attenuation (LLD #19 §2.1, HLD §5.6 Patent Family A) ───────
|
|
1149
|
+
//
|
|
1150
|
+
// "CLI mints, dashboard audits": this is the mint/inspect half. See
|
|
1151
|
+
// commands/attenuate.ts's module doc.
|
|
1152
|
+
const attenuateCmd = program
|
|
1153
|
+
.command('attenuate')
|
|
1154
|
+
.description('Attenuate an API key to a narrower child key (capability subset + optional TTL)')
|
|
1155
|
+
.option('--parent-key <keyId>', 'Parent API key ID to attenuate')
|
|
1156
|
+
.option('--caps <caps>', 'Comma-separated capability subset to grant the child key')
|
|
1157
|
+
.option('--ttl <seconds>', 'Child key TTL in seconds (server default 14400, max 86400)')
|
|
1158
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
1159
|
+
.action(async (opts) => {
|
|
1160
|
+
const { runAttenuate } = await import('./commands/attenuate.js');
|
|
1161
|
+
await runAttenuate(opts);
|
|
1162
|
+
});
|
|
1163
|
+
attenuateCmd
|
|
1164
|
+
.command('chain <chainId>')
|
|
1165
|
+
.description('Resolve the full delegation lineage for an attenuation chain')
|
|
1166
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
1167
|
+
.action(async (chainId, opts) => {
|
|
1168
|
+
const { runAttenuateChain } = await import('./commands/attenuate.js');
|
|
1169
|
+
await runAttenuateChain(chainId, opts);
|
|
1170
|
+
});
|
|
1171
|
+
// ── Org signup + team management (LLD #65) ───────────────────────────────
|
|
1172
|
+
const orgCmd = program
|
|
1173
|
+
.command('org')
|
|
1174
|
+
.description('Create and manage orgs');
|
|
1175
|
+
orgCmd
|
|
1176
|
+
.command('create')
|
|
1177
|
+
.description('Create a real org (paid tier, 30-day trial) with a default team and workspace. ' +
|
|
1178
|
+
'Requires `intutic login` first and DNS domain-ownership verification.')
|
|
1179
|
+
.option('--org-name <orgName>', 'Organization name (prompted if omitted)')
|
|
1180
|
+
.option('--domain <domain>', 'Domain to verify ownership of (prompted if omitted)')
|
|
1181
|
+
.option('--region <region>', "Gateway cell region (e.g. 'us', 'eu'); server-validated, defaults to the deployment's home region")
|
|
1182
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
1183
|
+
.action(async (opts) => {
|
|
1184
|
+
const { runOrgCreate } = await import('./commands/org.js');
|
|
1185
|
+
await runOrgCreate(opts);
|
|
1186
|
+
});
|
|
1187
|
+
const teamCmd = program
|
|
1188
|
+
.command('team')
|
|
1189
|
+
.description('Manage teams and workspaces under an org');
|
|
1190
|
+
teamCmd
|
|
1191
|
+
.command('list')
|
|
1192
|
+
.description("List an org's teams")
|
|
1193
|
+
.requiredOption('--org <org_id>', 'Org ID')
|
|
1194
|
+
.option('--json', 'Output as JSON')
|
|
1195
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
1196
|
+
.action(async (opts) => {
|
|
1197
|
+
const { runTeamList } = await import('./commands/team.js');
|
|
1198
|
+
await runTeamList(opts);
|
|
1199
|
+
});
|
|
1200
|
+
teamCmd
|
|
1201
|
+
.command('create')
|
|
1202
|
+
.description('Create a new team under an org')
|
|
1203
|
+
.requiredOption('--org <org_id>', 'Org ID')
|
|
1204
|
+
.requiredOption('--name <name>', 'Team name')
|
|
1205
|
+
.option('--json', 'Output as JSON')
|
|
1206
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
1207
|
+
.action(async (opts) => {
|
|
1208
|
+
const { runTeamCreate } = await import('./commands/team.js');
|
|
1209
|
+
await runTeamCreate(opts);
|
|
1210
|
+
});
|
|
1211
|
+
teamCmd
|
|
1212
|
+
.command('workspaces <team_id>')
|
|
1213
|
+
.description('List the workspaces under a team')
|
|
1214
|
+
.option('--json', 'Output as JSON')
|
|
1215
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
1216
|
+
.action(async (teamId, opts) => {
|
|
1217
|
+
const { runTeamWorkspaces } = await import('./commands/team.js');
|
|
1218
|
+
await runTeamWorkspaces(teamId, opts);
|
|
1219
|
+
});
|
|
1220
|
+
teamCmd
|
|
1221
|
+
.command('create-workspace <team_id>')
|
|
1222
|
+
.description('Create a new workspace under a team; you become its OWNER')
|
|
1223
|
+
.requiredOption('--name <name>', 'Workspace name')
|
|
1224
|
+
.option('--json', 'Output as JSON')
|
|
1225
|
+
.option('--dev', 'Use local control plane (http://localhost:3001)')
|
|
1226
|
+
.action(async (teamId, opts) => {
|
|
1227
|
+
const { runTeamCreateWorkspace } = await import('./commands/team.js');
|
|
1228
|
+
await runTeamCreateWorkspace(teamId, opts);
|
|
1229
|
+
});
|
|
392
1230
|
program.parse();
|
|
393
1231
|
//# sourceMappingURL=cli.js.map
|