@intutic/cli 2.1.0 → 2.3.0
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 +8 -8
- package/dist/cli.d.ts +0 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +492 -21
- package/dist/cli.js.map +1 -1
- package/dist/commands/apiCommand.d.ts +40 -0
- package/dist/commands/apiCommand.d.ts.map +1 -0
- package/dist/commands/apiCommand.js +88 -0
- package/dist/commands/apiCommand.js.map +1 -0
- package/dist/commands/budget.d.ts +4 -0
- package/dist/commands/budget.d.ts.map +1 -1
- package/dist/commands/budget.js +8 -0
- package/dist/commands/budget.js.map +1 -1
- package/dist/commands/compliance.d.ts +122 -0
- package/dist/commands/compliance.d.ts.map +1 -0
- package/dist/commands/compliance.js +278 -0
- package/dist/commands/compliance.js.map +1 -0
- package/dist/commands/connect.d.ts +26 -1
- package/dist/commands/connect.d.ts.map +1 -1
- package/dist/commands/connect.js +222 -86
- package/dist/commands/connect.js.map +1 -1
- package/dist/commands/credentials.d.ts +1 -1
- package/dist/commands/credentials.js +1 -1
- package/dist/commands/decision.d.ts +4 -4
- package/dist/commands/decision.js +4 -4
- package/dist/commands/disconnect.d.ts +29 -0
- package/dist/commands/disconnect.d.ts.map +1 -0
- package/dist/commands/disconnect.js +266 -0
- package/dist/commands/disconnect.js.map +1 -0
- package/dist/commands/doctor.d.ts +0 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +0 -1
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/enforce.d.ts +1 -1
- package/dist/commands/enforce.js +1 -1
- package/dist/commands/enterprise.d.ts +3 -2
- package/dist/commands/enterprise.d.ts.map +1 -1
- package/dist/commands/enterprise.js +11 -3
- package/dist/commands/enterprise.js.map +1 -1
- package/dist/commands/env.d.ts +1 -1
- package/dist/commands/env.d.ts.map +1 -1
- package/dist/commands/env.js +3 -8
- package/dist/commands/env.js.map +1 -1
- package/dist/commands/exec.d.ts +2 -3
- package/dist/commands/exec.d.ts.map +1 -1
- package/dist/commands/exec.js +4 -5
- package/dist/commands/exec.js.map +1 -1
- package/dist/commands/gateLiveness.d.ts +15 -0
- package/dist/commands/gateLiveness.d.ts.map +1 -0
- package/dist/commands/gateLiveness.js +34 -0
- package/dist/commands/gateLiveness.js.map +1 -0
- package/dist/commands/gateway.d.ts +16 -4
- package/dist/commands/gateway.d.ts.map +1 -1
- package/dist/commands/gateway.js +47 -6
- package/dist/commands/gateway.js.map +1 -1
- package/dist/commands/github.d.ts +20 -0
- package/dist/commands/github.d.ts.map +1 -0
- package/dist/commands/github.js +41 -0
- package/dist/commands/github.js.map +1 -0
- package/dist/commands/guardrails.d.ts +28 -2
- package/dist/commands/guardrails.d.ts.map +1 -1
- package/dist/commands/guardrails.js +233 -24
- package/dist/commands/guardrails.js.map +1 -1
- package/dist/commands/init.d.ts +1 -2
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +3 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/install-daemon.d.ts +1 -1
- package/dist/commands/install-daemon.js +2 -2
- package/dist/commands/integrity.d.ts +10 -2
- package/dist/commands/integrity.d.ts.map +1 -1
- package/dist/commands/integrity.js +10 -3
- package/dist/commands/integrity.js.map +1 -1
- package/dist/commands/inventory.d.ts +60 -0
- package/dist/commands/inventory.d.ts.map +1 -0
- package/dist/commands/inventory.js +141 -0
- package/dist/commands/inventory.js.map +1 -0
- package/dist/commands/judge.d.ts +1 -1
- package/dist/commands/judge.js +2 -2
- package/dist/commands/judge.js.map +1 -1
- package/dist/commands/login.d.ts +0 -1
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +0 -1
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/logout.d.ts +0 -1
- package/dist/commands/logout.d.ts.map +1 -1
- package/dist/commands/logout.js +0 -1
- package/dist/commands/logout.js.map +1 -1
- package/dist/commands/mcp.d.ts +31 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +65 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/notifications.d.ts +54 -0
- package/dist/commands/notifications.d.ts.map +1 -0
- package/dist/commands/notifications.js +151 -0
- package/dist/commands/notifications.js.map +1 -0
- package/dist/commands/org.d.ts +3 -3
- package/dist/commands/org.js +3 -3
- package/dist/commands/policy.d.ts +6 -1
- package/dist/commands/policy.d.ts.map +1 -1
- package/dist/commands/policy.js +77 -37
- package/dist/commands/policy.js.map +1 -1
- package/dist/commands/predict-cost.d.ts +1 -2
- package/dist/commands/predict-cost.d.ts.map +1 -1
- package/dist/commands/predict-cost.js +1 -4
- package/dist/commands/predict-cost.js.map +1 -1
- package/dist/commands/rollback.d.ts.map +1 -1
- package/dist/commands/rollback.js +3 -1
- package/dist/commands/rollback.js.map +1 -1
- package/dist/commands/routing.d.ts +1 -1
- package/dist/commands/routing.js +1 -1
- package/dist/commands/rules.d.ts +74 -0
- package/dist/commands/rules.d.ts.map +1 -0
- package/dist/commands/rules.js +283 -0
- package/dist/commands/rules.js.map +1 -0
- package/dist/commands/settings.d.ts +43 -0
- package/dist/commands/settings.d.ts.map +1 -0
- package/dist/commands/settings.js +108 -0
- package/dist/commands/settings.js.map +1 -0
- package/dist/commands/setup.d.ts +1 -1
- package/dist/commands/setup.js +1 -1
- package/dist/commands/siem.d.ts +53 -0
- package/dist/commands/siem.d.ts.map +1 -0
- package/dist/commands/siem.js +132 -0
- package/dist/commands/siem.js.map +1 -0
- package/dist/commands/skill.d.ts +5 -5
- package/dist/commands/skill.js +7 -7
- package/dist/commands/skill.js.map +1 -1
- package/dist/commands/status.d.ts +0 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +2 -3
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/syncContext.d.ts +0 -1
- package/dist/commands/syncContext.d.ts.map +1 -1
- package/dist/commands/syncContext.js +0 -1
- package/dist/commands/syncContext.js.map +1 -1
- package/dist/commands/team.d.ts +2 -2
- package/dist/commands/team.js +2 -2
- package/dist/commands/traces.d.ts +0 -3
- package/dist/commands/traces.d.ts.map +1 -1
- package/dist/commands/traces.js +0 -3
- package/dist/commands/traces.js.map +1 -1
- package/dist/commands/usage.d.ts +41 -0
- package/dist/commands/usage.d.ts.map +1 -0
- package/dist/commands/usage.js +129 -0
- package/dist/commands/usage.js.map +1 -0
- package/dist/commands/whoami.d.ts +0 -1
- package/dist/commands/whoami.d.ts.map +1 -1
- package/dist/commands/whoami.js +0 -1
- package/dist/commands/whoami.js.map +1 -1
- package/dist/config/keychain.d.ts +0 -1
- package/dist/config/keychain.d.ts.map +1 -1
- package/dist/config/keychain.js +0 -1
- package/dist/config/keychain.js.map +1 -1
- package/dist/config/paths.d.ts +0 -1
- package/dist/config/paths.d.ts.map +1 -1
- package/dist/config/paths.js +0 -1
- package/dist/config/paths.js.map +1 -1
- package/dist/config/store.d.ts +0 -1
- package/dist/config/store.d.ts.map +1 -1
- package/dist/config/store.js +0 -1
- package/dist/config/store.js.map +1 -1
- package/dist/harness/ag2.d.ts +1 -1
- package/dist/harness/ag2.js +1 -1
- package/dist/harness/agentFramework.d.ts +1 -1
- package/dist/harness/agentFramework.js +1 -1
- package/dist/harness/agenticOrchestrator.d.ts +4 -6
- package/dist/harness/agenticOrchestrator.d.ts.map +1 -1
- package/dist/harness/agenticOrchestrator.js +5 -7
- package/dist/harness/agenticOrchestrator.js.map +1 -1
- package/dist/harness/agentsMd.d.ts +24 -0
- package/dist/harness/agentsMd.d.ts.map +1 -0
- package/dist/harness/agentsMd.js +28 -0
- package/dist/harness/agentsMd.js.map +1 -0
- package/dist/harness/aider.d.ts +3 -1
- package/dist/harness/aider.d.ts.map +1 -1
- package/dist/harness/aider.js +9 -7
- package/dist/harness/aider.js.map +1 -1
- package/dist/harness/antigravity.d.ts +23 -5
- package/dist/harness/antigravity.d.ts.map +1 -1
- package/dist/harness/antigravity.js +45 -54
- package/dist/harness/antigravity.js.map +1 -1
- package/dist/harness/autogen.d.ts +1 -1
- package/dist/harness/autogen.js +2 -2
- package/dist/harness/autogen.js.map +1 -1
- package/dist/harness/claudeCode.d.ts +22 -1
- package/dist/harness/claudeCode.d.ts.map +1 -1
- package/dist/harness/claudeCode.js +47 -3
- package/dist/harness/claudeCode.js.map +1 -1
- package/dist/harness/cline.d.ts.map +1 -1
- package/dist/harness/cline.js +16 -24
- package/dist/harness/cline.js.map +1 -1
- package/dist/harness/codex.d.ts +6 -1
- package/dist/harness/codex.d.ts.map +1 -1
- package/dist/harness/codex.js +26 -28
- package/dist/harness/codex.js.map +1 -1
- package/dist/harness/continue.d.ts +10 -4
- package/dist/harness/continue.d.ts.map +1 -1
- package/dist/harness/continue.js +22 -22
- package/dist/harness/continue.js.map +1 -1
- package/dist/harness/cursor.d.ts +5 -2
- package/dist/harness/cursor.d.ts.map +1 -1
- package/dist/harness/cursor.js +19 -27
- package/dist/harness/cursor.js.map +1 -1
- package/dist/harness/detector.d.ts +0 -1
- package/dist/harness/detector.d.ts.map +1 -1
- package/dist/harness/detector.js +0 -1
- package/dist/harness/detector.js.map +1 -1
- package/dist/harness/dsh.d.ts +5 -3
- package/dist/harness/dsh.d.ts.map +1 -1
- package/dist/harness/dsh.js +17 -29
- package/dist/harness/dsh.js.map +1 -1
- package/dist/harness/eve.d.ts +3 -4
- package/dist/harness/eve.d.ts.map +1 -1
- package/dist/harness/eve.js +3 -4
- package/dist/harness/eve.js.map +1 -1
- package/dist/harness/githubCopilot.d.ts +4 -1
- package/dist/harness/githubCopilot.d.ts.map +1 -1
- package/dist/harness/githubCopilot.js +13 -24
- package/dist/harness/githubCopilot.js.map +1 -1
- package/dist/harness/goose.d.ts +7 -3
- package/dist/harness/goose.d.ts.map +1 -1
- package/dist/harness/goose.js +18 -21
- package/dist/harness/goose.js.map +1 -1
- package/dist/harness/grok.d.ts +2 -2
- package/dist/harness/grok.d.ts.map +1 -1
- package/dist/harness/grok.js +15 -28
- package/dist/harness/grok.js.map +1 -1
- package/dist/harness/hermes.d.ts +6 -0
- package/dist/harness/hermes.d.ts.map +1 -1
- package/dist/harness/hermes.js +17 -16
- package/dist/harness/hermes.js.map +1 -1
- package/dist/harness/jsSdkGatedAdapter.d.ts.map +1 -1
- package/dist/harness/jsSdkGatedAdapter.js +4 -6
- package/dist/harness/jsSdkGatedAdapter.js.map +1 -1
- package/dist/harness/langgraph.d.ts.map +1 -1
- package/dist/harness/langgraph.js +4 -6
- package/dist/harness/langgraph.js.map +1 -1
- package/dist/harness/muse.d.ts +3 -7
- package/dist/harness/muse.d.ts.map +1 -1
- package/dist/harness/muse.js +17 -39
- package/dist/harness/muse.js.map +1 -1
- package/dist/harness/n8n.d.ts +13 -7
- package/dist/harness/n8n.d.ts.map +1 -1
- package/dist/harness/n8n.js +21 -127
- package/dist/harness/n8n.js.map +1 -1
- package/dist/harness/openWebUI.d.ts.map +1 -1
- package/dist/harness/openWebUI.js +5 -4
- package/dist/harness/openWebUI.js.map +1 -1
- package/dist/harness/openaiAgents.d.ts +1 -1
- package/dist/harness/openaiAgents.d.ts.map +1 -1
- package/dist/harness/openaiAgents.js +6 -3
- package/dist/harness/openaiAgents.js.map +1 -1
- package/dist/harness/openclaw.d.ts +8 -0
- package/dist/harness/openclaw.d.ts.map +1 -1
- package/dist/harness/openclaw.js +27 -12
- package/dist/harness/openclaw.js.map +1 -1
- package/dist/harness/opencode.d.ts +4 -5
- package/dist/harness/opencode.d.ts.map +1 -1
- package/dist/harness/opencode.js +17 -34
- package/dist/harness/opencode.js.map +1 -1
- package/dist/harness/openhands.d.ts +9 -3
- package/dist/harness/openhands.d.ts.map +1 -1
- package/dist/harness/openhands.js +43 -25
- package/dist/harness/openhands.js.map +1 -1
- package/dist/harness/pi.d.ts +7 -2
- package/dist/harness/pi.d.ts.map +1 -1
- package/dist/harness/pi.js +19 -18
- package/dist/harness/pi.js.map +1 -1
- package/dist/harness/rooCode.d.ts +12 -5
- package/dist/harness/rooCode.d.ts.map +1 -1
- package/dist/harness/rooCode.js +25 -43
- package/dist/harness/rooCode.js.map +1 -1
- package/dist/harness/rulesFiles.d.ts +53 -0
- package/dist/harness/rulesFiles.d.ts.map +1 -0
- package/dist/harness/rulesFiles.js +106 -0
- package/dist/harness/rulesFiles.js.map +1 -0
- package/dist/harness/sdkGatedAdapter.d.ts.map +1 -1
- package/dist/harness/sdkGatedAdapter.js +4 -6
- package/dist/harness/sdkGatedAdapter.js.map +1 -1
- package/dist/harness/smolagents.d.ts +2 -2
- package/dist/harness/smolagents.js +3 -3
- package/dist/harness/smolagents.js.map +1 -1
- package/dist/harness/types.d.ts +20 -6
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js +1 -95
- package/dist/harness/types.js.map +1 -1
- package/dist/harness/windsurf.d.ts +7 -2
- package/dist/harness/windsurf.d.ts.map +1 -1
- package/dist/harness/windsurf.js +22 -29
- package/dist/harness/windsurf.js.map +1 -1
- package/dist/harness/xirp.d.ts +6 -9
- package/dist/harness/xirp.d.ts.map +1 -1
- package/dist/harness/xirp.js +7 -10
- package/dist/harness/xirp.js.map +1 -1
- package/dist/lib/api.d.ts +27 -1
- package/dist/lib/api.d.ts.map +1 -1
- package/dist/lib/api.js +55 -18
- package/dist/lib/api.js.map +1 -1
- package/dist/lib/ciscoScanner.d.ts +1 -1
- package/dist/lib/ciscoScanner.js +1 -1
- package/dist/lib/decisionsLogRefresh.d.ts.map +1 -1
- package/dist/lib/decisionsLogRefresh.js +6 -7
- package/dist/lib/decisionsLogRefresh.js.map +1 -1
- package/dist/lib/deviceReport.d.ts +15 -0
- package/dist/lib/deviceReport.d.ts.map +1 -1
- package/dist/lib/deviceReport.js +30 -0
- package/dist/lib/deviceReport.js.map +1 -1
- package/dist/lib/envInjector.d.ts +16 -6
- package/dist/lib/envInjector.d.ts.map +1 -1
- package/dist/lib/envInjector.js +99 -88
- package/dist/lib/envInjector.js.map +1 -1
- package/dist/lib/gitHooks.d.ts +0 -1
- package/dist/lib/gitHooks.d.ts.map +1 -1
- package/dist/lib/gitHooks.js +8 -9
- package/dist/lib/gitHooks.js.map +1 -1
- package/dist/lib/gracefulShutdown.d.ts +1 -1
- package/dist/lib/gracefulShutdown.js +1 -1
- package/dist/lib/hash.d.ts +0 -1
- package/dist/lib/hash.d.ts.map +1 -1
- package/dist/lib/hash.js +0 -1
- package/dist/lib/hash.js.map +1 -1
- package/dist/lib/inventory.d.ts +47 -0
- package/dist/lib/inventory.d.ts.map +1 -0
- package/dist/lib/inventory.js +106 -0
- package/dist/lib/inventory.js.map +1 -0
- package/dist/lib/logger.d.ts +0 -1
- package/dist/lib/logger.d.ts.map +1 -1
- package/dist/lib/logger.js +0 -1
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/mdmManifest.d.ts +30 -2
- package/dist/lib/mdmManifest.d.ts.map +1 -1
- package/dist/lib/mdmManifest.js +50 -2
- package/dist/lib/mdmManifest.js.map +1 -1
- package/dist/lib/onboarding.d.ts +0 -1
- package/dist/lib/onboarding.d.ts.map +1 -1
- package/dist/lib/onboarding.js +2 -3
- package/dist/lib/onboarding.js.map +1 -1
- package/dist/lib/otelPropagation.d.ts +1 -1
- package/dist/lib/otelPropagation.js +1 -1
- package/dist/lib/policySnapshot.d.ts +3 -1
- package/dist/lib/policySnapshot.d.ts.map +1 -1
- package/dist/lib/policySnapshot.js +15 -5
- package/dist/lib/policySnapshot.js.map +1 -1
- package/dist/lib/process.d.ts +5 -1
- package/dist/lib/process.d.ts.map +1 -1
- package/dist/lib/process.js +13 -4
- package/dist/lib/process.js.map +1 -1
- package/dist/lib/providerProbe.d.ts +2 -2
- package/dist/lib/providerProbe.js +2 -2
- package/dist/lib/sandbox/firecracker.d.ts +3 -3
- package/dist/lib/sandbox/firecracker.js +3 -3
- package/dist/lib/sandbox/index.d.ts +2 -2
- package/dist/lib/sandbox/index.js +2 -2
- package/dist/lib/sandbox/oci.d.ts +1 -1
- package/dist/lib/sandbox/oci.js +1 -1
- package/dist/lib/sandbox/types.d.ts +1 -1
- package/dist/lib/sandbox/types.js +1 -1
- package/dist/lib/sopFrontMatter.d.ts +1 -1
- package/dist/lib/sopFrontMatter.js +1 -1
- package/package.json +6 -5
- package/resources/sandbox/Dockerfile +1 -1
- package/resources/sandbox/entrypoint.sh +4 -4
- package/resources/sandbox/firecracker-launch.sh +1 -1
- package/dist/harness/base.d.ts +0 -20
- package/dist/harness/base.d.ts.map +0 -1
- package/dist/harness/base.js +0 -73
- package/dist/harness/base.js.map +0 -1
package/dist/commands/connect.js
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* a real-time filesystem watcher for configuration drift detection,
|
|
6
6
|
* and a 30-second HTTP polling loop as a secondary fallback.
|
|
7
7
|
*
|
|
8
|
-
* LLD #14 — connect.ts
|
|
9
8
|
* HLD §3.14 — Real-Time State Mirroring
|
|
10
9
|
*
|
|
11
10
|
* @module
|
|
@@ -25,10 +24,12 @@ import { getAdapter } from '../harness/detector.js';
|
|
|
25
24
|
import { printOnboardingGuide } from '../lib/onboarding.js';
|
|
26
25
|
import { writeEnforcementState } from '../lib/enforcementState.js';
|
|
27
26
|
import { reportDeviceState } from '../lib/deviceReport.js';
|
|
27
|
+
import { reportMachineInventory, shouldReportInventoryThisIteration } from '../lib/inventory.js';
|
|
28
28
|
import { parseChecksums, verifyChecksum } from '../lib/binaryChecksum.js';
|
|
29
29
|
import { newIso } from '@intutic/id';
|
|
30
|
+
import { HarnessType, rulesFileOf } from '@intutic/shared-types';
|
|
30
31
|
import pc from 'picocolors';
|
|
31
|
-
import { SyncWsClient, startWatcher, updatePreToolUseHooks, injectMcpServer, guardSettingsFile, warnIfDshCoverageGap, writeRuntimeEnv, refreshPolicySnapshot, refreshGateCaches, localHoldTokensFor, runComplianceProbes, drainHookEvents, drainReviewRequests, REVIEW_REQUESTS_LOG, syncOfflineTraces, TrajectoryMonitor,
|
|
32
|
+
import { SyncWsClient, startWatcher, updatePreToolUseHooks, injectMcpServer, noteProxyUrl, guardPolicySnapshot, guardSettingsFile, isGuardedPath, warnIfDshCoverageGap, writeRuntimeEnv, refreshPolicySnapshot, refreshGateCaches, localHoldTokensFor, runComplianceProbes, drainHookEvents, drainReviewRequests, REVIEW_REQUESTS_LOG, syncOfflineTraces, TrajectoryMonitor, fetchLocalProxyInstanceId, endAllOpenSessions, applySkillOptEdits, reportHarnessAgents, captureAndUpload, shouldCaptureThisIteration, refreshDecisionsDigest, retireClaudeMdDigest, claudeCodeReadsAgentsMd, writeBundledSkills, clearImmutable, setImmutable, } from '@intutic/sync-daemon';
|
|
32
33
|
import { watch } from 'chokidar';
|
|
33
34
|
// Named, not default: under `module: Node16` TypeScript resolves ioredis's
|
|
34
35
|
// default export to the module namespace rather than the class, so `new
|
|
@@ -61,20 +62,57 @@ function isPortInUse(port) {
|
|
|
61
62
|
});
|
|
62
63
|
}
|
|
63
64
|
/**
|
|
64
|
-
*
|
|
65
|
+
* Applies a synced configuration to each configured harness.
|
|
65
66
|
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
67
|
+
* The gate is installed whatever rule sets the workspace has: it enforces the
|
|
68
|
+
* built-in protections, the destructive-command tier, group rules and holds,
|
|
69
|
+
* none of which need a rule set. Gating it on rule sets left a workspace with
|
|
70
|
+
* none, or with none targeting a harness, with that harness ungoverned. The
|
|
71
|
+
* rules file is written only when a rule set targets the harness, or on a
|
|
72
|
+
* forced sync.
|
|
73
|
+
*
|
|
74
|
+
* Harnesses that read the same file (Codex, Grok Build, OpenCode, Muse Code
|
|
75
|
+
* and others all read `AGENTS.md`) each get every rule set aimed at any of
|
|
76
|
+
* them that is configured here: one section, the same for every writer, so
|
|
77
|
+
* the last writer no longer replaces the others' rules.
|
|
78
|
+
*
|
|
79
|
+
* Claude Code reads its own `.claude/rules/` file at every launch, and also
|
|
80
|
+
* the workspace's `AGENTS.md` when there is no `CLAUDE.md` on the path (or
|
|
81
|
+
* the user chose `claude-md-and-agents-md`; see claudeAgentsMd.ts). When it
|
|
82
|
+
* does, a rule set aimed at both Claude Code and an `AGENTS.md` reader
|
|
83
|
+
* reaches it through `AGENTS.md`, so its own file leaves that rule set out
|
|
84
|
+
* rather than load it twice.
|
|
85
|
+
*
|
|
86
|
+
* @returns how many rule sets were written into rules files.
|
|
68
87
|
*/
|
|
69
|
-
function
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
88
|
+
export async function writeHarnessConfigs(harnesses, workspaceRoot, sops, proxyUrl, force) {
|
|
89
|
+
let written = 0;
|
|
90
|
+
const writtenFiles = new Set();
|
|
91
|
+
const agentsMdReaders = harnesses.filter((h) => rulesFileOf(h) === 'AGENTS.md');
|
|
92
|
+
const claudeFromAgentsMd = harnesses.includes(HarnessType.CLAUDE_CODE) && agentsMdReaders.length > 0 && (await claudeCodeReadsAgentsMd(workspaceRoot));
|
|
93
|
+
for (const harnessType of harnesses) {
|
|
94
|
+
const adapter = getAdapter(harnessType);
|
|
95
|
+
if (!adapter)
|
|
96
|
+
continue;
|
|
97
|
+
await adapter.installGate?.(workspaceRoot, proxyUrl);
|
|
98
|
+
const file = rulesFileOf(harnessType);
|
|
99
|
+
const readers = file === null ? [harnessType] : harnesses.filter((h) => rulesFileOf(h) === file);
|
|
100
|
+
const aimed = sops.filter((sop) => sop.harnessTargets.some((t) => readers.includes(t)));
|
|
101
|
+
const viaAgentsMd = harnessType === HarnessType.CLAUDE_CODE && claudeFromAgentsMd;
|
|
102
|
+
const targetSops = viaAgentsMd ? aimed.filter((sop) => !sop.harnessTargets.some((t) => agentsMdReaders.includes(t))) : aimed;
|
|
103
|
+
// Claude Code's own file is rewritten even when every rule set aimed at
|
|
104
|
+
// it now comes through AGENTS.md, so it stops carrying them.
|
|
105
|
+
if (aimed.length === 0 && !force)
|
|
106
|
+
continue;
|
|
107
|
+
if (!(await adapter.writeConfig(workspaceRoot, targetSops, proxyUrl)))
|
|
108
|
+
continue;
|
|
109
|
+
// A shared file counts once, however many of its readers wrote it.
|
|
110
|
+
if (file === null || !writtenFiles.has(file))
|
|
111
|
+
written += targetSops.length;
|
|
112
|
+
if (file !== null)
|
|
113
|
+
writtenFiles.add(file);
|
|
114
|
+
}
|
|
115
|
+
return written;
|
|
78
116
|
}
|
|
79
117
|
/**
|
|
80
118
|
* Asset names MUST match publish.yml's build-rust-proxy matrix
|
|
@@ -262,7 +300,7 @@ export async function runConnect(opts) {
|
|
|
262
300
|
// 2. AbortController for clean shutdown
|
|
263
301
|
let proxyProc = null;
|
|
264
302
|
const ac = new AbortController();
|
|
265
|
-
//
|
|
303
|
+
// This handler used to abort the loop and hope. The process stayed
|
|
266
304
|
// alive for hours. `createShutdownHandler` adds the exit deadline and the
|
|
267
305
|
// second-signal exit; `terminateChild` escalates to SIGKILL.
|
|
268
306
|
const shutdown = createShutdownHandler({
|
|
@@ -453,24 +491,57 @@ export async function runConnect(opts) {
|
|
|
453
491
|
// when the config version moved: policy changes without the config version
|
|
454
492
|
// changing, and a guardrail promoted, a hold approved or an egress mode
|
|
455
493
|
// flipped mid-session used to reach a connected machine only on restart —
|
|
456
|
-
// this function was called once, at startup
|
|
457
|
-
// daemon's own loop (`syncLoop.ts`'s Steps 0b–0d). None of these throw.
|
|
494
|
+
// this function was called once, at startup. None of these throw.
|
|
458
495
|
//
|
|
459
496
|
// The snapshot also carries this workspace's `review_before:` tokens as hold
|
|
460
497
|
// rules (synced SOPs, settings and local `.intutic/sops`), so every gate —
|
|
461
|
-
// not only the Claude Code hook — holds on them
|
|
462
|
-
|
|
498
|
+
// not only the Claude Code hook — holds on them.
|
|
499
|
+
//
|
|
500
|
+
// First, the snapshot's self-heal: a snapshot changed while the daemon was
|
|
501
|
+
// stopped is put back to the last verified copy, and reported, before the
|
|
502
|
+
// refresh replaces it. With `checkSnapshot` false the caller is that check.
|
|
503
|
+
async function refreshGateCachesForConnect(checkSnapshot = true) {
|
|
504
|
+
if (checkSnapshot)
|
|
505
|
+
await guardPolicySnapshot(safeConfig.workspaceRoot, async () => { });
|
|
463
506
|
const localHoldTokens = await localHoldTokensFor(safeConfig.workspaceRoot, lastCachedConfig?.sops ?? [], lastCachedConfig?.settings, safeConfig.harnesses);
|
|
464
507
|
const landed = await refreshGateCaches({ controlPlaneUrl, apiKey: safeCreds.apiKey, workspaceId: safeCreds.workspaceId, localHoldTokens });
|
|
465
508
|
if (!landed.snapshot)
|
|
466
509
|
log.dim('Policy snapshot refresh failed (will retry next sync); built-in protections are unaffected.');
|
|
467
510
|
}
|
|
511
|
+
// The rules files the adapters write, absolute. `configFileName` is
|
|
512
|
+
// relative to the workspace for most harnesses and absolute for the few
|
|
513
|
+
// that keep their config in the home directory.
|
|
514
|
+
const rulesFilePaths = () => safeConfig.harnesses
|
|
515
|
+
.map((h) => getAdapter(h)?.configFileName)
|
|
516
|
+
.filter((f) => Boolean(f))
|
|
517
|
+
.map((f) => node_path.resolve(safeConfig.workspaceRoot, f));
|
|
518
|
+
// Whether Claude Code read the workspace's AGENTS.md at the last sync. A
|
|
519
|
+
// CLAUDE.md added or removed (or the user's Project instructions setting
|
|
520
|
+
// changed) moves rule sets between AGENTS.md and Claude Code's own file, so
|
|
521
|
+
// a change rewrites the rules files as a new config would.
|
|
522
|
+
let lastClaudeFromAgentsMd;
|
|
468
523
|
// 3. Define configuration applier function
|
|
469
524
|
async function applySyncConfig(syncConfig, force = false) {
|
|
470
525
|
let sopsWritten = 0;
|
|
471
526
|
lastCachedConfig = syncConfig;
|
|
472
527
|
await refreshGateCachesForConnect();
|
|
473
|
-
|
|
528
|
+
const claudeFromAgentsMd = safeConfig.harnesses.includes(HarnessType.CLAUDE_CODE)
|
|
529
|
+
? await claudeCodeReadsAgentsMd(safeConfig.workspaceRoot)
|
|
530
|
+
: false;
|
|
531
|
+
const claudeDeliveryMoved = lastClaudeFromAgentsMd !== undefined && claudeFromAgentsMd !== lastClaudeFromAgentsMd;
|
|
532
|
+
lastClaudeFromAgentsMd = claudeFromAgentsMd;
|
|
533
|
+
// Write-protect (`bypassEnforcementTier: 'immutable'`, macOS): the rules
|
|
534
|
+
// files carry the user-immutable flag between cycles, so it comes off
|
|
535
|
+
// before anything below writes them and goes back on afterwards. It also
|
|
536
|
+
// comes off whenever the config moved, so a workspace that switched away
|
|
537
|
+
// from write-protect is not left with files nothing can rewrite.
|
|
538
|
+
const writeProtect = syncConfig.settings?.bypassEnforcementTier === 'immutable';
|
|
539
|
+
const configMoved = syncConfig.configVersion > localConfigVersion || force || claudeDeliveryMoved;
|
|
540
|
+
if (writeProtect || configMoved) {
|
|
541
|
+
for (const file of rulesFilePaths())
|
|
542
|
+
await clearImmutable(file);
|
|
543
|
+
}
|
|
544
|
+
if (configMoved) {
|
|
474
545
|
log.info(`Applying configuration v${syncConfig.configVersion}...`);
|
|
475
546
|
// Load and compile local SOP entries
|
|
476
547
|
const localSopEntries = [];
|
|
@@ -524,19 +595,11 @@ export async function runConnect(opts) {
|
|
|
524
595
|
}
|
|
525
596
|
}
|
|
526
597
|
const combinedSops = [...syncConfig.sops, ...localSopEntries];
|
|
527
|
-
// a. Write configs for all active harnesses
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
const targetSops = combinedSops.filter((sop) => sop.harnessTargets.includes(harnessType));
|
|
533
|
-
if (targetSops.length === 0 && !force)
|
|
534
|
-
continue;
|
|
535
|
-
const written = await adapter.writeConfig(safeConfig.workspaceRoot, targetSops, syncConfig.proxyUrl);
|
|
536
|
-
if (written) {
|
|
537
|
-
sopsWritten += targetSops.length;
|
|
538
|
-
}
|
|
539
|
-
}
|
|
598
|
+
// a. Write configs for all active harnesses. The proxy URL is recorded
|
|
599
|
+
// first: `intutic disconnect` recognises the base-URL settings it is
|
|
600
|
+
// written into by it.
|
|
601
|
+
await noteProxyUrl(syncConfig.proxyUrl);
|
|
602
|
+
sopsWritten += await writeHarnessConfigs(safeConfig.harnesses, safeConfig.workspaceRoot, combinedSops, syncConfig.proxyUrl, force);
|
|
540
603
|
// b. Invalidate/update Claude Code hooks and settings
|
|
541
604
|
if (safeConfig.harnesses.includes('claude-code')) {
|
|
542
605
|
try {
|
|
@@ -546,19 +609,54 @@ export async function runConnect(opts) {
|
|
|
546
609
|
log.warn(`Failed to update Claude Code hooks: ${err instanceof Error ? err.message : String(err)}`);
|
|
547
610
|
}
|
|
548
611
|
}
|
|
549
|
-
// c. Inject + proxy-wrap MCP servers across all supported harnesses
|
|
550
|
-
try {
|
|
551
|
-
await injectMcpServer(safeConfig.workspaceRoot, safeCreds.workspaceId);
|
|
552
|
-
}
|
|
553
|
-
catch (err) {
|
|
554
|
-
log.warn(`Failed to inject MCP server configs: ${err instanceof Error ? err.message : String(err)}`);
|
|
555
|
-
}
|
|
556
612
|
localConfigVersion = syncConfig.configVersion;
|
|
557
613
|
// Mirror workspace settings locally so spawn-time policies (like
|
|
558
614
|
// allowLocalMemoryVaults) apply on the next connect without a fetch.
|
|
559
615
|
safeConfig.settings = syncConfig.settings;
|
|
560
616
|
saveConfig({ ...safeConfig, configVersion: localConfigVersion, settings: syncConfig.settings });
|
|
561
617
|
}
|
|
618
|
+
// c. Inject + proxy-wrap MCP servers across all supported harnesses, on
|
|
619
|
+
// every cycle rather than only when the config moved: a server a user adds
|
|
620
|
+
// to a harness config after `connect` started is wrapped on the next cycle.
|
|
621
|
+
// The writes are write-if-changed, so an unchanged config costs no write.
|
|
622
|
+
try {
|
|
623
|
+
await injectMcpServer(safeConfig.workspaceRoot, safeCreds.workspaceId, { skip: safeConfig.disconnectedHarnesses });
|
|
624
|
+
}
|
|
625
|
+
catch (err) {
|
|
626
|
+
log.warn(`Failed to inject MCP server configs: ${err instanceof Error ? err.message : String(err)}`);
|
|
627
|
+
}
|
|
628
|
+
// d. SkillOpt edits the control plane queued for this workspace, acked
|
|
629
|
+
// back so a suggestion reaches `applied` only once it is on disk. All of
|
|
630
|
+
// them again when the rules files were just rewritten, which drops every
|
|
631
|
+
// overlay. Before the integrity hashes below, so the drift watcher does
|
|
632
|
+
// not take the edit for tampering.
|
|
633
|
+
await applySkillOptEdits({
|
|
634
|
+
workspaceRoot: safeConfig.workspaceRoot,
|
|
635
|
+
controlPlaneUrl,
|
|
636
|
+
apiKey: safeCreds.apiKey,
|
|
637
|
+
appliedEdits: syncConfig.appliedEdits,
|
|
638
|
+
bypassEnforcementTier: syncConfig.settings?.bypassEnforcementTier,
|
|
639
|
+
reapplyAll: configMoved,
|
|
640
|
+
});
|
|
641
|
+
// e. The governed decisions log, opt-in (`decisionsLogEnabled`, off by
|
|
642
|
+
// default), into each harness's instructions file. Also before the hashes:
|
|
643
|
+
// it writes into shared files such as AGENTS.md, whose rules section they
|
|
644
|
+
// cover. The section earlier versions put in CLAUDE.md comes out whether
|
|
645
|
+
// or not the log is on.
|
|
646
|
+
await retireClaudeMdDigest(safeConfig.workspaceRoot);
|
|
647
|
+
if (syncConfig.settings?.decisionsLogEnabled) {
|
|
648
|
+
await refreshDecisionsDigest({
|
|
649
|
+
controlPlaneUrl,
|
|
650
|
+
apiKey: safeCreds.apiKey,
|
|
651
|
+
workspaceId: safeCreds.workspaceId,
|
|
652
|
+
workspaceRoot: safeConfig.workspaceRoot,
|
|
653
|
+
harnesses: safeConfig.harnesses,
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
if (writeProtect) {
|
|
657
|
+
for (const file of rulesFilePaths())
|
|
658
|
+
await setImmutable(file);
|
|
659
|
+
}
|
|
562
660
|
// c. Compute file hashes + update integrity store
|
|
563
661
|
const fileHashes = [];
|
|
564
662
|
const canonicalHashes = {};
|
|
@@ -695,7 +793,7 @@ export async function runConnect(opts) {
|
|
|
695
793
|
proxyStatus = 'unhealthy';
|
|
696
794
|
if (ac.signal.aborted) {
|
|
697
795
|
// Shutting down: the proxy is gone because we stopped it. Re-spawning
|
|
698
|
-
// it here would leave an orphan holding the port
|
|
796
|
+
// it here would leave an orphan holding the port.
|
|
699
797
|
proxyStatus = 'stopped';
|
|
700
798
|
}
|
|
701
799
|
else if (proxyProc) {
|
|
@@ -762,7 +860,7 @@ export async function runConnect(opts) {
|
|
|
762
860
|
catch (err) {
|
|
763
861
|
log.warn(`Failed to send daemon heartbeat: ${err instanceof Error ? err.message : String(err)}`);
|
|
764
862
|
}
|
|
765
|
-
// g. Enforcement device visibility (post-strip gap #2,
|
|
863
|
+
// g. Enforcement device visibility (post-strip gap #2, egress
|
|
766
864
|
// hardening) — the "interval" half of "continuous compliance": this
|
|
767
865
|
// loop already runs unprivileged, as the real user, on a timer, and
|
|
768
866
|
// already just computed sslTrustStatus above for its own DR-healing
|
|
@@ -797,7 +895,7 @@ export async function runConnect(opts) {
|
|
|
797
895
|
mcpProxyFailBehavior: syncConfig.settings?.mcpProxyFailBehavior,
|
|
798
896
|
mcpProxyMode: syncConfig.settings?.mcpProxyMode,
|
|
799
897
|
bypassEnforcementTier: syncConfig.settings?.bypassEnforcementTier,
|
|
800
|
-
// Shared by the MCP proxies for their session window
|
|
898
|
+
// Shared by the MCP proxies for their session window — only when it is running.
|
|
801
899
|
valkeyUrl: valkeyResult.running ? valkeyUrl : undefined,
|
|
802
900
|
});
|
|
803
901
|
}
|
|
@@ -828,7 +926,7 @@ export async function runConnect(opts) {
|
|
|
828
926
|
log.warn(`Compliance probes failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
|
|
829
927
|
}
|
|
830
928
|
};
|
|
831
|
-
// Drains BOTH logs
|
|
929
|
+
// Drains BOTH logs.
|
|
832
930
|
//
|
|
833
931
|
// This drained hook events only. `intutic connect` runs the daemon in-process,
|
|
834
932
|
// so under the CLI runtime a `review_before` hold still blocked the tool
|
|
@@ -899,7 +997,7 @@ export async function runConnect(opts) {
|
|
|
899
997
|
controlPlaneUrl,
|
|
900
998
|
apiKey: safeCreds.apiKey,
|
|
901
999
|
workspaceId: safeCreds.workspaceId,
|
|
902
|
-
// Shared by the MCP proxies for their session window
|
|
1000
|
+
// Shared by the MCP proxies for their session window — only when it is running.
|
|
903
1001
|
valkeyUrl: valkeyResult.running ? valkeyUrl : undefined,
|
|
904
1002
|
});
|
|
905
1003
|
}
|
|
@@ -923,6 +1021,14 @@ export async function runConnect(opts) {
|
|
|
923
1021
|
'will not apply until the next successful sync. Built-in protections are unaffected.');
|
|
924
1022
|
}
|
|
925
1023
|
await refreshGateCachesForConnect();
|
|
1024
|
+
// The bundled rule-author agent skill, written only when absent so a local
|
|
1025
|
+
// edit is never overwritten.
|
|
1026
|
+
try {
|
|
1027
|
+
await writeBundledSkills(safeConfig.workspaceRoot);
|
|
1028
|
+
}
|
|
1029
|
+
catch (err) {
|
|
1030
|
+
log.dim(`Could not write the bundled agent skill: ${err instanceof Error ? err.message : String(err)}`);
|
|
1031
|
+
}
|
|
926
1032
|
// Sync offline traces back to PostgreSQL on startup
|
|
927
1033
|
try {
|
|
928
1034
|
await syncOfflineTraces(controlPlaneUrl, safeCreds.apiKey);
|
|
@@ -1016,7 +1122,7 @@ export async function runConnect(opts) {
|
|
|
1016
1122
|
const drainSafetyTimer = setInterval(runDrain, 60_000);
|
|
1017
1123
|
// Run initial compliance check on startup
|
|
1018
1124
|
await runProbes();
|
|
1019
|
-
// dsh coverage-gap visibility
|
|
1125
|
+
// dsh coverage-gap visibility: once at startup, not every poll
|
|
1020
1126
|
// tick — the gap only changes state on the user's first `dsh --profile
|
|
1021
1127
|
// <name>` run, which the filesystem watcher below reacts to immediately
|
|
1022
1128
|
// via settingsGuard.ts's isDshProfilesRoot `addDir` handling. Only worth
|
|
@@ -1062,20 +1168,17 @@ export async function runConnect(opts) {
|
|
|
1062
1168
|
}
|
|
1063
1169
|
// B. Tamper detection for every governed config, not just Claude Code's.
|
|
1064
1170
|
//
|
|
1065
|
-
//
|
|
1066
|
-
//
|
|
1067
|
-
//
|
|
1068
|
-
//
|
|
1069
|
-
//
|
|
1070
|
-
|
|
1071
|
-
// Windsurf/VS Code/Gemini settings file passed the basename test only to fall
|
|
1072
|
-
// through and log `settings_intact`.
|
|
1073
|
-
//
|
|
1074
|
-
// Route anything on the governed list to the guard and let it decide.
|
|
1075
|
-
if (isGovernedConfigPath(changedPath, filename)) {
|
|
1171
|
+
// Routed on the guard's own watch list. A filter on file names here, first
|
|
1172
|
+
// `settings.json` and later `settings.json`/`hooks.json`/`intutic-governance`,
|
|
1173
|
+
// kept the guard from ever seeing the paths it restores under other names:
|
|
1174
|
+
// a gate script, Cline's PreToolUse, OpenClaw's openclaw.json, Muse's
|
|
1175
|
+
// managed-hooks file and every dsh profile patch.
|
|
1176
|
+
if (isGuardedPath(changedPath, safeConfig.workspaceRoot)) {
|
|
1076
1177
|
try {
|
|
1077
1178
|
const sops = lastCachedConfig?.sops ?? [];
|
|
1078
|
-
|
|
1179
|
+
// The proxy URL too: the restored gate scripts carry it, and an empty
|
|
1180
|
+
// one would be written into them.
|
|
1181
|
+
const tampered = await guardSettingsFile(changedPath, safeConfig.workspaceRoot, sops, lastCachedConfig?.proxyUrl ?? '', undefined, new Set(safeConfig.disconnectedHarnesses ?? []), () => refreshGateCachesForConnect(false));
|
|
1079
1182
|
if (tampered) {
|
|
1080
1183
|
log.warn(`[Security] Governance settings tamper detected and restored: ${changedPath}`);
|
|
1081
1184
|
wsClient.send({
|
|
@@ -1092,8 +1195,9 @@ export async function runConnect(opts) {
|
|
|
1092
1195
|
}
|
|
1093
1196
|
return;
|
|
1094
1197
|
}
|
|
1095
|
-
// B. Handle governed harness file drift detection
|
|
1096
|
-
|
|
1198
|
+
// B. Handle governed harness file drift detection. By path, not
|
|
1199
|
+
// basename: rules files sit in directories (`.cursor/rules/…`).
|
|
1200
|
+
const matchingHarness = safeConfig.harnesses.find((h) => getAdapter(h)?.configFileName === relativePath.split(node_path.sep).join('/'));
|
|
1097
1201
|
if (!matchingHarness)
|
|
1098
1202
|
return;
|
|
1099
1203
|
const adapter = getAdapter(matchingHarness);
|
|
@@ -1105,6 +1209,20 @@ export async function runConnect(opts) {
|
|
|
1105
1209
|
return;
|
|
1106
1210
|
const canonical = integrity.files[adapter.configFileName] ?? '';
|
|
1107
1211
|
if (currentHash !== canonical) {
|
|
1212
|
+
// Record only (`bypassEnforcementTier: 'alert-only'`): the edit stays,
|
|
1213
|
+
// and the drift report below is the record.
|
|
1214
|
+
const tier = (lastCachedConfig?.settings ?? safeConfig.settings)?.bypassEnforcementTier;
|
|
1215
|
+
if (tier === 'alert-only') {
|
|
1216
|
+
log.warn(`Governed config file "${filename}" was edited by hand; recording the drift and leaving the edit in place.`);
|
|
1217
|
+
wsClient.send({
|
|
1218
|
+
type: 'drift_report',
|
|
1219
|
+
harnessType: adapter.type,
|
|
1220
|
+
filePath: adapter.configFileName,
|
|
1221
|
+
localHash: currentHash || '',
|
|
1222
|
+
canonicalHash: canonical,
|
|
1223
|
+
});
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1108
1226
|
log.warn(`Governed config file "${filename}" modification detected! Reverting to approved baseline...`);
|
|
1109
1227
|
// Backup drifted version first
|
|
1110
1228
|
try {
|
|
@@ -1138,6 +1256,7 @@ export async function runConnect(opts) {
|
|
|
1138
1256
|
}
|
|
1139
1257
|
});
|
|
1140
1258
|
// 6. Secondary fallback HTTP poll loop
|
|
1259
|
+
let pollIteration = 0;
|
|
1141
1260
|
while (!ac.signal.aborted) {
|
|
1142
1261
|
try {
|
|
1143
1262
|
const syncConfig = await client.fetchConfig(safeCreds.workspaceId);
|
|
@@ -1154,40 +1273,56 @@ export async function runConnect(opts) {
|
|
|
1154
1273
|
log.dim(`Offline trace sync failed (will retry next poll): ${err instanceof Error ? err.message : String(err)}`);
|
|
1155
1274
|
}
|
|
1156
1275
|
// Register agents + report one session per harness (the reporter
|
|
1157
|
-
// dedupes per run and per proxy process)
|
|
1158
|
-
//
|
|
1159
|
-
//
|
|
1160
|
-
//
|
|
1161
|
-
//
|
|
1276
|
+
// dedupes per run and per proxy process), and turn this cycle's skill
|
|
1277
|
+
// scan findings into `skill_flagged` events. The local proxy's instance
|
|
1278
|
+
// id is read once per iteration: with it the harness's git/task context
|
|
1279
|
+
// lands on the proxy's own session row, the one its traces are filed
|
|
1280
|
+
// under.
|
|
1162
1281
|
const proxyInstanceId = safeConfig.harnesses.length > 0 ? await fetchLocalProxyInstanceId() : null;
|
|
1163
|
-
|
|
1282
|
+
const { governanceInputs, failures } = await reportHarnessAgents({
|
|
1283
|
+
controlPlaneUrl,
|
|
1284
|
+
apiKey: safeCreds.apiKey,
|
|
1285
|
+
workspaceId: safeCreds.workspaceId,
|
|
1286
|
+
workspaceRoot: safeConfig.workspaceRoot,
|
|
1287
|
+
harnesses: safeConfig.harnesses,
|
|
1288
|
+
allowLocalVaults: syncConfig.settings?.allowLocalMemoryVaults,
|
|
1289
|
+
proxyInstanceId,
|
|
1290
|
+
});
|
|
1291
|
+
for (const { harness, error } of failures) {
|
|
1292
|
+
log.dim(`Agent report/session for harness '${harness}' failed: ${error}`);
|
|
1293
|
+
}
|
|
1294
|
+
// Every few minutes, the machine's AI inventory for the org-wide view:
|
|
1295
|
+
// every harness the detection rules find, connected or not, with its
|
|
1296
|
+
// gate state, and the MCP servers and skill bundles on the machine.
|
|
1297
|
+
if (shouldReportInventoryThisIteration(pollIteration)) {
|
|
1298
|
+
const inventory = await reportMachineInventory({
|
|
1299
|
+
controlPlaneUrl,
|
|
1300
|
+
apiKey: safeCreds.apiKey,
|
|
1301
|
+
workspaceRoot: safeConfig.workspaceRoot,
|
|
1302
|
+
configured: safeConfig.harnesses,
|
|
1303
|
+
disconnected: safeConfig.disconnectedHarnesses,
|
|
1304
|
+
cliVersion: cliPkgVersion,
|
|
1305
|
+
});
|
|
1306
|
+
if (!inventory.reported)
|
|
1307
|
+
log.dim(`AI inventory report not sent (will retry): ${inventory.reason}`);
|
|
1308
|
+
}
|
|
1309
|
+
// Every Nth poll, capture the rules files that changed for the config
|
|
1310
|
+
// history. Content goes only when this poll's settings have
|
|
1311
|
+
// `configBodyUpload` on; otherwise path, hash, size and time.
|
|
1312
|
+
if (shouldCaptureThisIteration(pollIteration)) {
|
|
1164
1313
|
try {
|
|
1165
|
-
|
|
1166
|
-
workspaceRoot: process.cwd(),
|
|
1167
|
-
harnessType,
|
|
1168
|
-
configSynced: true,
|
|
1169
|
-
dlpEnabled: true,
|
|
1170
|
-
policyEnforced: true,
|
|
1171
|
-
allowLocalVaults: syncConfig.settings?.allowLocalMemoryVaults,
|
|
1172
|
-
});
|
|
1173
|
-
await reportAgent(controlPlaneUrl, safeCreds.apiKey, safeCreds.workspaceId, report);
|
|
1174
|
-
await startHarnessSession({
|
|
1314
|
+
await captureAndUpload({
|
|
1175
1315
|
controlPlaneUrl,
|
|
1176
1316
|
apiKey: safeCreds.apiKey,
|
|
1177
1317
|
workspaceId: safeCreds.workspaceId,
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1318
|
+
workspaceRoot: safeConfig.workspaceRoot,
|
|
1319
|
+
harnesses: safeConfig.harnesses,
|
|
1320
|
+
includeContent: syncConfig.settings?.configBodyUpload === true,
|
|
1321
|
+
governanceInputs,
|
|
1181
1322
|
});
|
|
1182
1323
|
}
|
|
1183
1324
|
catch (err) {
|
|
1184
|
-
|
|
1185
|
-
// must not stop the others or abort the poll iteration. Silence was
|
|
1186
|
-
// not deliberate — fetchConfig already succeeded above, so a failure
|
|
1187
|
-
// here means the control plane accepted the config read and rejected
|
|
1188
|
-
// the agent report, which is exactly the "dashboard graph is empty"
|
|
1189
|
-
// symptom this loop was added to fix. Report it so it is diagnosable.
|
|
1190
|
-
log.dim(`Agent report/session for harness '${harnessType}' failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1325
|
+
log.dim(`Config capture failed (will retry): ${err instanceof Error ? err.message : String(err)}`);
|
|
1191
1326
|
}
|
|
1192
1327
|
}
|
|
1193
1328
|
// Run compliance probes on each iteration
|
|
@@ -1197,6 +1332,7 @@ export async function runConnect(opts) {
|
|
|
1197
1332
|
log.error(`Sync iteration failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1198
1333
|
log.dim(`Retrying in ${pollInterval / 1000}s...`);
|
|
1199
1334
|
}
|
|
1335
|
+
pollIteration++;
|
|
1200
1336
|
// Sleep until next interval (AbortSignal-aware)
|
|
1201
1337
|
await new Promise((resolve) => {
|
|
1202
1338
|
const timer = setTimeout(resolve, pollInterval);
|