@intutic/sync-daemon 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 +1 -1
- package/dist/agentReporter.d.ts +24 -11
- package/dist/agentReporter.d.ts.map +1 -1
- package/dist/agentReporter.js +20 -13
- package/dist/agentReporter.js.map +1 -1
- package/dist/configReader.d.ts +45 -25
- package/dist/configReader.d.ts.map +1 -1
- package/dist/configReader.js +70 -46
- package/dist/configReader.js.map +1 -1
- package/dist/configWriter.d.ts +32 -45
- package/dist/configWriter.d.ts.map +1 -1
- package/dist/configWriter.js +52 -686
- package/dist/configWriter.js.map +1 -1
- package/dist/disconnect/harnesses.d.ts +29 -0
- package/dist/disconnect/harnesses.d.ts.map +1 -0
- package/dist/disconnect/harnesses.js +805 -0
- package/dist/disconnect/harnesses.js.map +1 -0
- package/dist/disconnect/index.d.ts +25 -0
- package/dist/disconnect/index.d.ts.map +1 -0
- package/dist/disconnect/index.js +92 -0
- package/dist/disconnect/index.js.map +1 -0
- package/dist/disconnect/mcp.d.ts +81 -0
- package/dist/disconnect/mcp.d.ts.map +1 -0
- package/dist/disconnect/mcp.js +354 -0
- package/dist/disconnect/mcp.js.map +1 -0
- package/dist/disconnect/originals.d.ts +88 -0
- package/dist/disconnect/originals.d.ts.map +1 -0
- package/dist/disconnect/originals.js +257 -0
- package/dist/disconnect/originals.js.map +1 -0
- package/dist/disconnect/plan.d.ts +175 -0
- package/dist/disconnect/plan.d.ts.map +1 -0
- package/dist/disconnect/plan.js +580 -0
- package/dist/disconnect/plan.js.map +1 -0
- package/dist/disconnect/recognise.d.ts +43 -0
- package/dist/disconnect/recognise.d.ts.map +1 -0
- package/dist/disconnect/recognise.js +108 -0
- package/dist/disconnect/recognise.js.map +1 -0
- package/dist/disconnect/tomlLines.d.ts +39 -0
- package/dist/disconnect/tomlLines.d.ts.map +1 -0
- package/dist/disconnect/tomlLines.js +105 -0
- package/dist/disconnect/tomlLines.js.map +1 -0
- package/dist/harness/aiderConfigMerger.d.ts +29 -8
- package/dist/harness/aiderConfigMerger.d.ts.map +1 -1
- package/dist/harness/aiderConfigMerger.js +63 -12
- package/dist/harness/aiderConfigMerger.js.map +1 -1
- package/dist/harness/antigravityCliHooks.d.ts +61 -0
- package/dist/harness/antigravityCliHooks.d.ts.map +1 -0
- package/dist/harness/antigravityCliHooks.js +207 -0
- package/dist/harness/antigravityCliHooks.js.map +1 -0
- package/dist/harness/antigravityHooks.d.ts +12 -2
- package/dist/harness/antigravityHooks.d.ts.map +1 -1
- package/dist/harness/antigravityHooks.js +38 -16
- package/dist/harness/antigravityHooks.js.map +1 -1
- package/dist/harness/antigravityProducts.d.ts +40 -0
- package/dist/harness/antigravityProducts.d.ts.map +1 -0
- package/dist/harness/antigravityProducts.js +109 -0
- package/dist/harness/antigravityProducts.js.map +1 -0
- package/dist/harness/claudeAgentsMd.d.ts +29 -0
- package/dist/harness/claudeAgentsMd.d.ts.map +1 -0
- package/dist/harness/claudeAgentsMd.js +80 -0
- package/dist/harness/claudeAgentsMd.js.map +1 -0
- package/dist/harness/claudeCodeHooks.d.ts +6 -6
- package/dist/harness/claudeCodeHooks.d.ts.map +1 -1
- package/dist/harness/claudeCodeHooks.js +115 -28
- package/dist/harness/claudeCodeHooks.js.map +1 -1
- package/dist/harness/claudeProjectApproval.d.ts +53 -0
- package/dist/harness/claudeProjectApproval.d.ts.map +1 -0
- package/dist/harness/claudeProjectApproval.js +159 -0
- package/dist/harness/claudeProjectApproval.js.map +1 -0
- package/dist/harness/clineHooks.d.ts +0 -1
- package/dist/harness/clineHooks.d.ts.map +1 -1
- package/dist/harness/clineHooks.js +13 -4
- package/dist/harness/clineHooks.js.map +1 -1
- package/dist/harness/codexConfigMerger.d.ts.map +1 -1
- package/dist/harness/codexConfigMerger.js +4 -0
- package/dist/harness/codexConfigMerger.js.map +1 -1
- package/dist/harness/codexHooks.d.ts +2 -0
- package/dist/harness/codexHooks.d.ts.map +1 -1
- package/dist/harness/codexHooks.js +15 -4
- package/dist/harness/codexHooks.js.map +1 -1
- package/dist/harness/continueConfigMerger.d.ts.map +1 -1
- package/dist/harness/continueConfigMerger.js +4 -0
- package/dist/harness/continueConfigMerger.js.map +1 -1
- package/dist/harness/cursorHooks.d.ts +5 -2
- package/dist/harness/cursorHooks.d.ts.map +1 -1
- package/dist/harness/cursorHooks.js +12 -3
- package/dist/harness/cursorHooks.js.map +1 -1
- package/dist/harness/dshHooks.d.ts +5 -5
- package/dist/harness/dshHooks.d.ts.map +1 -1
- package/dist/harness/dshHooks.js +12 -5
- package/dist/harness/dshHooks.js.map +1 -1
- package/dist/harness/gateArtifacts.d.ts +35 -0
- package/dist/harness/gateArtifacts.d.ts.map +1 -0
- package/dist/harness/gateArtifacts.js +88 -0
- package/dist/harness/gateArtifacts.js.map +1 -0
- package/dist/harness/gateBody.d.ts +195 -69
- package/dist/harness/gateBody.d.ts.map +1 -1
- package/dist/harness/gateBody.js +847 -182
- package/dist/harness/gateBody.js.map +1 -1
- package/dist/harness/gateSightings.d.ts +47 -0
- package/dist/harness/gateSightings.d.ts.map +1 -0
- package/dist/harness/gateSightings.js +159 -0
- package/dist/harness/gateSightings.js.map +1 -0
- package/dist/harness/githubCopilotHooks.d.ts.map +1 -1
- package/dist/harness/githubCopilotHooks.js +11 -10
- package/dist/harness/githubCopilotHooks.js.map +1 -1
- package/dist/harness/gooseHardener.d.ts +0 -1
- package/dist/harness/gooseHardener.d.ts.map +1 -1
- package/dist/harness/gooseHardener.js +1 -1
- package/dist/harness/gooseHardener.js.map +1 -1
- package/dist/harness/gooseHooks.d.ts +29 -1
- package/dist/harness/gooseHooks.d.ts.map +1 -1
- package/dist/harness/gooseHooks.js +107 -69
- package/dist/harness/gooseHooks.js.map +1 -1
- package/dist/harness/grokHooks.d.ts +2 -2
- package/dist/harness/grokHooks.d.ts.map +1 -1
- package/dist/harness/grokHooks.js +20 -18
- package/dist/harness/grokHooks.js.map +1 -1
- package/dist/harness/hermesHooks.d.ts +18 -8
- package/dist/harness/hermesHooks.d.ts.map +1 -1
- package/dist/harness/hermesHooks.js +55 -19
- package/dist/harness/hermesHooks.js.map +1 -1
- package/dist/harness/holdRedaction.d.ts +4 -1
- package/dist/harness/holdRedaction.d.ts.map +1 -1
- package/dist/harness/holdRedaction.js +32 -8
- package/dist/harness/holdRedaction.js.map +1 -1
- package/dist/harness/jetbrainsXmlConfig.d.ts +20 -0
- package/dist/harness/jetbrainsXmlConfig.d.ts.map +1 -1
- package/dist/harness/jetbrainsXmlConfig.js +39 -18
- package/dist/harness/jetbrainsXmlConfig.js.map +1 -1
- package/dist/harness/mcpAutoWrite.d.ts +62 -25
- package/dist/harness/mcpAutoWrite.d.ts.map +1 -1
- package/dist/harness/mcpAutoWrite.js +406 -95
- package/dist/harness/mcpAutoWrite.js.map +1 -1
- package/dist/harness/museHooks.d.ts +3 -3
- package/dist/harness/museHooks.d.ts.map +1 -1
- package/dist/harness/museHooks.js +15 -7
- package/dist/harness/museHooks.js.map +1 -1
- package/dist/harness/n8nHooks.d.ts +0 -1
- package/dist/harness/n8nHooks.d.ts.map +1 -1
- package/dist/harness/n8nHooks.js +4 -1
- package/dist/harness/n8nHooks.js.map +1 -1
- package/dist/harness/openCodeHooks.d.ts +2 -2
- package/dist/harness/openCodeHooks.d.ts.map +1 -1
- package/dist/harness/openCodeHooks.js +9 -8
- package/dist/harness/openCodeHooks.js.map +1 -1
- package/dist/harness/openWebuiHooks.d.ts +0 -1
- package/dist/harness/openWebuiHooks.d.ts.map +1 -1
- package/dist/harness/openWebuiHooks.js +11 -3
- package/dist/harness/openWebuiHooks.js.map +1 -1
- package/dist/harness/openclawHooks.d.ts +75 -18
- package/dist/harness/openclawHooks.d.ts.map +1 -1
- package/dist/harness/openclawHooks.js +214 -160
- package/dist/harness/openclawHooks.js.map +1 -1
- package/dist/harness/openhandsHooks.d.ts +37 -11
- package/dist/harness/openhandsHooks.d.ts.map +1 -1
- package/dist/harness/openhandsHooks.js +148 -97
- package/dist/harness/openhandsHooks.js.map +1 -1
- package/dist/harness/piHooks.d.ts +52 -17
- package/dist/harness/piHooks.d.ts.map +1 -1
- package/dist/harness/piHooks.js +177 -163
- package/dist/harness/piHooks.js.map +1 -1
- package/dist/harness/protectedPaths.d.ts +107 -25
- package/dist/harness/protectedPaths.d.ts.map +1 -1
- package/dist/harness/protectedPaths.js +342 -48
- package/dist/harness/protectedPaths.js.map +1 -1
- package/dist/harness/rulesSection.d.ts +62 -0
- package/dist/harness/rulesSection.d.ts.map +1 -0
- package/dist/harness/rulesSection.js +145 -0
- package/dist/harness/rulesSection.js.map +1 -0
- package/dist/harness/windsurfHooks.d.ts +18 -3
- package/dist/harness/windsurfHooks.d.ts.map +1 -1
- package/dist/harness/windsurfHooks.js +41 -8
- package/dist/harness/windsurfHooks.js.map +1 -1
- package/dist/harness/windsurfJetBrainsProxy.d.ts +1 -1
- package/dist/harness/windsurfJetBrainsProxy.d.ts.map +1 -1
- package/dist/harness/windsurfJetBrainsProxy.js +5 -1
- package/dist/harness/windsurfJetBrainsProxy.js.map +1 -1
- package/dist/index.d.ts +21 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -19
- package/dist/index.js.map +1 -1
- package/dist/inventory.d.ts +53 -0
- package/dist/inventory.d.ts.map +1 -0
- package/dist/inventory.js +133 -0
- package/dist/inventory.js.map +1 -0
- package/dist/lib/complianceProbes.d.ts +2 -1
- package/dist/lib/complianceProbes.d.ts.map +1 -1
- package/dist/lib/complianceProbes.js +1 -1
- package/dist/lib/complianceProbes.js.map +1 -1
- package/dist/lib/decisionsDigest.d.ts +35 -17
- package/dist/lib/decisionsDigest.d.ts.map +1 -1
- package/dist/lib/decisionsDigest.js +106 -47
- package/dist/lib/decisionsDigest.js.map +1 -1
- package/dist/lib/egressPolicy.d.ts +2 -2
- package/dist/lib/egressPolicy.js +2 -2
- package/dist/lib/mcpRegistryPy.d.ts +11 -0
- package/dist/lib/mcpRegistryPy.d.ts.map +1 -0
- package/dist/lib/mcpRegistryPy.js +173 -0
- package/dist/lib/mcpRegistryPy.js.map +1 -0
- package/dist/lib/phrasesPy.d.ts +11 -0
- package/dist/lib/phrasesPy.d.ts.map +1 -0
- package/dist/lib/phrasesPy.js +351 -0
- package/dist/lib/phrasesPy.js.map +1 -0
- package/dist/lib/policySnapshot.d.ts +63 -9
- package/dist/lib/policySnapshot.d.ts.map +1 -1
- package/dist/lib/policySnapshot.js +269 -45
- package/dist/lib/policySnapshot.js.map +1 -1
- package/dist/lib/processPoller.d.ts.map +1 -1
- package/dist/lib/processPoller.js +9 -2
- package/dist/lib/processPoller.js.map +1 -1
- package/dist/lib/regexLinearity.d.ts +43 -0
- package/dist/lib/regexLinearity.d.ts.map +1 -0
- package/dist/lib/regexLinearity.js +275 -0
- package/dist/lib/regexLinearity.js.map +1 -0
- package/dist/lib/runtimeEnv.d.ts +1 -3
- package/dist/lib/runtimeEnv.d.ts.map +1 -1
- package/dist/lib/runtimeEnv.js +0 -2
- package/dist/lib/runtimeEnv.js.map +1 -1
- package/dist/lib/sequencePy.d.ts +10 -0
- package/dist/lib/sequencePy.d.ts.map +1 -0
- package/dist/lib/sequencePy.js +102 -0
- package/dist/lib/sequencePy.js.map +1 -0
- package/dist/sessionReporter.d.ts +18 -12
- package/dist/sessionReporter.d.ts.map +1 -1
- package/dist/sessionReporter.js +37 -24
- package/dist/sessionReporter.js.map +1 -1
- package/dist/skillWriter.d.ts +23 -5
- package/dist/skillWriter.d.ts.map +1 -1
- package/dist/skillWriter.js +159 -16
- package/dist/skillWriter.js.map +1 -1
- package/dist/syncCycle.d.ts +142 -0
- package/dist/syncCycle.d.ts.map +1 -0
- package/dist/syncCycle.js +327 -0
- package/dist/syncCycle.js.map +1 -0
- package/dist/trajectoryMonitor.d.ts +2 -4
- package/dist/trajectoryMonitor.d.ts.map +1 -1
- package/dist/trajectoryMonitor.js +2 -5
- package/dist/trajectoryMonitor.js.map +1 -1
- package/dist/watcher/driftWatcher.d.ts +0 -1
- package/dist/watcher/driftWatcher.d.ts.map +1 -1
- package/dist/watcher/driftWatcher.js +1 -2
- package/dist/watcher/driftWatcher.js.map +1 -1
- package/dist/watcher/settingsGuard.d.ts +36 -7
- package/dist/watcher/settingsGuard.d.ts.map +1 -1
- package/dist/watcher/settingsGuard.js +397 -60
- package/dist/watcher/settingsGuard.js.map +1 -1
- package/dist/watcher/vscodeHookSettings.d.ts +30 -0
- package/dist/watcher/vscodeHookSettings.d.ts.map +1 -0
- package/dist/watcher/vscodeHookSettings.js +170 -0
- package/dist/watcher/vscodeHookSettings.js.map +1 -0
- package/dist/wsClient.d.ts +0 -1
- package/dist/wsClient.d.ts.map +1 -1
- package/dist/wsClient.js +0 -1
- package/dist/wsClient.js.map +1 -1
- package/package.json +5 -5
- package/dist/harness/claudeDesktopHooks.d.ts +0 -30
- package/dist/harness/claudeDesktopHooks.d.ts.map +0 -1
- package/dist/harness/claudeDesktopHooks.js +0 -254
- package/dist/harness/claudeDesktopHooks.js.map +0 -1
- package/dist/harness/continueHooks.d.ts +0 -63
- package/dist/harness/continueHooks.d.ts.map +0 -1
- package/dist/harness/continueHooks.js +0 -247
- package/dist/harness/continueHooks.js.map +0 -1
- package/dist/harness/gateKind.d.ts +0 -164
- package/dist/harness/gateKind.d.ts.map +0 -1
- package/dist/harness/gateKind.js +0 -144
- package/dist/harness/gateKind.js.map +0 -1
- package/dist/harness/rooCodeHooks.d.ts +0 -31
- package/dist/harness/rooCodeHooks.d.ts.map +0 -1
- package/dist/harness/rooCodeHooks.js +0 -286
- package/dist/harness/rooCodeHooks.js.map +0 -1
- package/dist/hashReporter.d.ts +0 -33
- package/dist/hashReporter.d.ts.map +0 -1
- package/dist/hashReporter.js +0 -69
- package/dist/hashReporter.js.map +0 -1
- package/dist/integrityStore.d.ts +0 -35
- package/dist/integrityStore.d.ts.map +0 -1
- package/dist/integrityStore.js +0 -71
- package/dist/integrityStore.js.map +0 -1
- package/dist/lib/gitWorktrees.d.ts +0 -140
- package/dist/lib/gitWorktrees.d.ts.map +0 -1
- package/dist/lib/gitWorktrees.js +0 -191
- package/dist/lib/gitWorktrees.js.map +0 -1
- package/dist/syncLoop.d.ts +0 -172
- package/dist/syncLoop.d.ts.map +0 -1
- package/dist/syncLoop.js +0 -878
- package/dist/syncLoop.js.map +0 -1
|
@@ -1,254 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* claudeDesktopHooks.ts — Claude Desktop PreToolUse hook generator.
|
|
3
|
-
*
|
|
4
|
-
* Claude Desktop shares the same hook format as Claude Code (PreToolUse,
|
|
5
|
-
* exit 2 on stdin JSON) but reads its config from an OS-specific location:
|
|
6
|
-
* macOS : ~/Library/Application Support/Claude/claude_desktop_config.json
|
|
7
|
-
* Linux : ~/.config/Claude/claude_desktop_config.json
|
|
8
|
-
*
|
|
9
|
-
* Hook events are written via dual-path logEvent:
|
|
10
|
-
* Path B (reliable) — synchronous append to .intutic/events/claude-desktop-hook-events.jsonl
|
|
11
|
-
* Path A (telemetry) — fire-and-forget https.request to the control plane
|
|
12
|
-
*
|
|
13
|
-
* LLD #14 — Phase 3 cross-harness defence (Gap 3, WS-B)
|
|
14
|
-
* HLD §3.14 — Three-Tier Defense Cascade (Tier 1 Native Gating)
|
|
15
|
-
*
|
|
16
|
-
* @module
|
|
17
|
-
*/
|
|
18
|
-
import * as fs from 'node:fs/promises';
|
|
19
|
-
import * as path from 'node:path';
|
|
20
|
-
import * as os from 'node:os';
|
|
21
|
-
import { createLogger } from '@intutic/logger';
|
|
22
|
-
import { newIso } from '@intutic/id';
|
|
23
|
-
import { emitJsGate, emitJsFailClosedPrelude } from './gateBody.js';
|
|
24
|
-
const log = createLogger('sync-claude-desktop-hooks');
|
|
25
|
-
/** Governance-sensitive paths that the hook gate protects (mirrors claudeCodeHooks). */
|
|
26
|
-
/** Resolve the OS-specific claude_desktop_config.json path. */
|
|
27
|
-
function resolveClaudeDesktopConfigPath() {
|
|
28
|
-
switch (process.platform) {
|
|
29
|
-
case 'darwin':
|
|
30
|
-
return path.join(os.homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
|
|
31
|
-
case 'linux':
|
|
32
|
-
return path.join(os.homedir(), '.config', 'Claude', 'claude_desktop_config.json');
|
|
33
|
-
default:
|
|
34
|
-
// Fallback to macOS path — Windows is out of scope for now
|
|
35
|
-
return path.join(os.homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
// ─── Node.js hook script template ─────────────────────────────────────────────
|
|
39
|
-
function buildClaudeDesktopCheckScript(proxyUrl, workspaceRoot, workspaceId) {
|
|
40
|
-
const hookEventsLog = path.join(workspaceRoot, '.intutic', 'events', 'claude-desktop-hook-events.jsonl');
|
|
41
|
-
const runtimeEnvPath = path.join(os.homedir(), '.intutic', 'env', 'runtime.env');
|
|
42
|
-
return `/**
|
|
43
|
-
* Intutic Claude Desktop PreToolUse execution gate.
|
|
44
|
-
* Auto-generated by intutic sync-daemon. DO NOT EDIT.
|
|
45
|
-
*
|
|
46
|
-
* Dual-path logEvent:
|
|
47
|
-
* Path B — synchronous file append to hook-events.jsonl
|
|
48
|
-
* Path A — fire-and-forget https.request to the control plane
|
|
49
|
-
*
|
|
50
|
-
* Proxy: ${proxyUrl}
|
|
51
|
-
* Generated: ${newIso()}
|
|
52
|
-
* Workspace: ${workspaceId}
|
|
53
|
-
*/
|
|
54
|
-
'use strict';
|
|
55
|
-
${emitJsFailClosedPrelude({ harness: 'claude-desktop', contract: 'exit2' })}
|
|
56
|
-
const path = require('path');
|
|
57
|
-
const os = require('os');
|
|
58
|
-
const fs = require('fs');
|
|
59
|
-
const https = require('https');
|
|
60
|
-
const crypto = require('crypto');
|
|
61
|
-
const url = require('url');
|
|
62
|
-
|
|
63
|
-
// ── Runtime env loader (Path A credentials) ──────────────────────────────────
|
|
64
|
-
const RUNTIME_ENV_PATH = ${JSON.stringify(runtimeEnvPath)};
|
|
65
|
-
|
|
66
|
-
function loadRuntimeEnv() {
|
|
67
|
-
const env = {
|
|
68
|
-
INTUTIC_HOST: 'https://api.intutic.ai',
|
|
69
|
-
INTUTIC_API_KEY: '',
|
|
70
|
-
INTUTIC_WORKSPACE_ID: ${JSON.stringify(workspaceId)},
|
|
71
|
-
};
|
|
72
|
-
try {
|
|
73
|
-
const raw = fs.readFileSync(RUNTIME_ENV_PATH, 'utf-8');
|
|
74
|
-
for (const line of raw.split('\\n')) {
|
|
75
|
-
const stripped = line.trim();
|
|
76
|
-
if (!stripped || stripped.startsWith('#') || !stripped.includes('=')) continue;
|
|
77
|
-
const eq = stripped.indexOf('=');
|
|
78
|
-
const k = stripped.slice(0, eq).trim();
|
|
79
|
-
const v = stripped.slice(eq + 1).trim();
|
|
80
|
-
env[k] = v;
|
|
81
|
-
}
|
|
82
|
-
} catch { /* file may not exist yet */ }
|
|
83
|
-
return env;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// ── Governance-sensitive paths ────────────────────────────────────────────────
|
|
87
|
-
${emitJsGate({ harness: "claude-desktop", contract: 'exit2' })}
|
|
88
|
-
|
|
89
|
-
// ── Dual-path logEvent ────────────────────────────────────────────────────────
|
|
90
|
-
const HOOK_EVENTS_LOG = ${JSON.stringify(hookEventsLog)};
|
|
91
|
-
|
|
92
|
-
let _intuticSessionId = '';
|
|
93
|
-
function logEvent(env, verdict, toolName, reason) {
|
|
94
|
-
const ts = new Date().toISOString();
|
|
95
|
-
const incidentId = crypto.createHash('sha1')
|
|
96
|
-
.update(ts + toolName + env.INTUTIC_WORKSPACE_ID)
|
|
97
|
-
.digest('hex').slice(0, 16);
|
|
98
|
-
|
|
99
|
-
const entry = {
|
|
100
|
-
// Passed through, not collapsed to two values: the advisory tier emits
|
|
101
|
-
// 'tool_flagged', and a ternary here silently recorded it as an allow.
|
|
102
|
-
event: verdict,
|
|
103
|
-
toolName,
|
|
104
|
-
reason: reason || '',
|
|
105
|
-
workspaceId: env.INTUTIC_WORKSPACE_ID,
|
|
106
|
-
harnessType: 'claude-desktop',
|
|
107
|
-
timestamp: ts,
|
|
108
|
-
incidentId,
|
|
109
|
-
...(_intuticSessionId ? { sessionId: _intuticSessionId } : {}),
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
// Path B — reliable file append
|
|
113
|
-
try {
|
|
114
|
-
const dir = path.dirname(HOOK_EVENTS_LOG);
|
|
115
|
-
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
116
|
-
fs.appendFileSync(HOOK_EVENTS_LOG, JSON.stringify(entry) + '\\n', { flag: 'a' });
|
|
117
|
-
} catch { /* never crash the hook */ }
|
|
118
|
-
|
|
119
|
-
// Path A — fire-and-forget https.request
|
|
120
|
-
if (env.INTUTIC_API_KEY) {
|
|
121
|
-
try {
|
|
122
|
-
const body = Buffer.from(JSON.stringify({ events: [entry] }));
|
|
123
|
-
const parsedUrl = url.parse(env.INTUTIC_HOST + '/api/v1/hook-events');
|
|
124
|
-
const req = https.request({
|
|
125
|
-
hostname: parsedUrl.hostname,
|
|
126
|
-
port: parsedUrl.port || 443,
|
|
127
|
-
path: parsedUrl.path,
|
|
128
|
-
method: 'POST',
|
|
129
|
-
headers: {
|
|
130
|
-
'Content-Type': 'application/json',
|
|
131
|
-
'Content-Length': body.length,
|
|
132
|
-
'Authorization': 'Bearer ' + env.INTUTIC_API_KEY,
|
|
133
|
-
},
|
|
134
|
-
});
|
|
135
|
-
req.on('error', () => {}); // fire-and-forget
|
|
136
|
-
req.write(body);
|
|
137
|
-
req.end();
|
|
138
|
-
} catch { /* never crash the hook */ }
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// ── Main ──────────────────────────────────────────────────────────────────────
|
|
143
|
-
const runtimeEnv = loadRuntimeEnv();
|
|
144
|
-
let inputData = '';
|
|
145
|
-
process.stdin.on('data', (chunk) => { inputData += chunk; });
|
|
146
|
-
process.stdin.on('end', () => {
|
|
147
|
-
try {
|
|
148
|
-
const ctx = JSON.parse(inputData);
|
|
149
|
-
_intuticSessionId = ctx.session_id || ctx.sessionId || ctx.conversation_id || ctx.conversationId || ctx.task_id || ctx.taskId || '';
|
|
150
|
-
// An envelope carrying none of the tool fields extracts to empty strings,
|
|
151
|
-
// which match no rule — an allow. Refused instead; see the guard itself.
|
|
152
|
-
intuticGuardEnvelope(ctx, ['tool_name', 'toolName', 'tool_input', 'toolInput'],
|
|
153
|
-
(v, t, r) => logEvent(runtimeEnv, v, t, r));
|
|
154
|
-
const toolName = (ctx.tool_name || ctx.toolName || '').toLowerCase()
|
|
155
|
-
// Case preserved for the gate. A BLOCK: SOP compiles to a tool-name
|
|
156
|
-
// pattern the operator wrote as they see it (Bash, Write) and this
|
|
157
|
-
// harness lowercases the tool for its own matching. Handing the gate the
|
|
158
|
-
// lowercased form means every SOP tool rule silently matches nothing here
|
|
159
|
-
// while appearing active everywhere else.
|
|
160
|
-
const rawToolName = ctx.tool_name || ctx.toolName || '';
|
|
161
|
-
const toolInput = ctx.tool_input || ctx.toolInput || {};
|
|
162
|
-
const toolInputStr = JSON.stringify(toolInput);
|
|
163
|
-
|
|
164
|
-
// Both guards used to be gated on the tool name — paths only for
|
|
165
|
-
// edit/write/multiedit, commands only for a tool literally called "bash".
|
|
166
|
-
// A harness that names its shell tool anything else (run_command, terminal,
|
|
167
|
-
// execute) walked past both. The shared gate keys on the *arguments*, so a
|
|
168
|
-
// tool name nobody anticipated is no longer a bypass.
|
|
169
|
-
const targetPath = toolInput.path || toolInput.file_path || toolInput.filePath ||
|
|
170
|
-
toolInput.new_path || toolInput.target || toolInput.notebook_path || '';
|
|
171
|
-
const command = toolInput.command || toolInput.cmd || toolInput.script || '';
|
|
172
|
-
intuticGate(rawToolName, targetPath, command, (v, t, r) => logEvent(runtimeEnv, v, t, r), runtimeEnv.INTUTIC_WORKSPACE_ID, toolInput);
|
|
173
|
-
|
|
174
|
-
logEvent(runtimeEnv, 'tool_allowed', toolName, '');
|
|
175
|
-
process.exit(0);
|
|
176
|
-
} catch (err) {
|
|
177
|
-
console.error('[Intutic Guardrail] Hook error (blocking for safety):', err);
|
|
178
|
-
logEvent(runtimeEnv, 'tool_blocked', 'unknown', String(err));
|
|
179
|
-
process.exit(2);
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
`.trim();
|
|
183
|
-
}
|
|
184
|
-
// ─── Main export ──────────────────────────────────────────────────────────────
|
|
185
|
-
/**
|
|
186
|
-
* Write the Claude Desktop governance hook script and merge hook configuration
|
|
187
|
-
* into the OS-specific claude_desktop_config.json.
|
|
188
|
-
*
|
|
189
|
-
* Safe to call repeatedly — uses atomic rename (`.intutic-tmp` → final path).
|
|
190
|
-
* Does NOT touch the `mcpServers` section of the config.
|
|
191
|
-
*
|
|
192
|
-
* @param workspaceRoot - Absolute workspace root path.
|
|
193
|
-
* @param proxyUrl - Intutic proxy URL written into the hook script header.
|
|
194
|
-
* @param workspaceId - Workspace ID embedded in every hook event payload.
|
|
195
|
-
*/
|
|
196
|
-
export async function writeClaudeDesktopHooks(workspaceRoot, proxyUrl, workspaceId = '') {
|
|
197
|
-
// ── 1. Ensure directories ──────────────────────────────────────────────────
|
|
198
|
-
const hookScriptDir = path.join(workspaceRoot, '.intutic', 'hooks');
|
|
199
|
-
const hookEventsDir = path.join(workspaceRoot, '.intutic', 'events');
|
|
200
|
-
const desktopConfigPath = resolveClaudeDesktopConfigPath();
|
|
201
|
-
const desktopConfigDir = path.dirname(desktopConfigPath);
|
|
202
|
-
await Promise.all([
|
|
203
|
-
fs.mkdir(hookScriptDir, { recursive: true }),
|
|
204
|
-
fs.mkdir(hookEventsDir, { recursive: true }),
|
|
205
|
-
fs.mkdir(desktopConfigDir, { recursive: true }),
|
|
206
|
-
]);
|
|
207
|
-
// ── 2. Write hook script (atomic) ─────────────────────────────────────────
|
|
208
|
-
const hookScriptPath = path.join(hookScriptDir, 'claude-desktop-check.js');
|
|
209
|
-
const tmpScript = hookScriptPath + '.intutic-tmp';
|
|
210
|
-
await fs.writeFile(tmpScript, buildClaudeDesktopCheckScript(proxyUrl, workspaceRoot, workspaceId), 'utf-8');
|
|
211
|
-
await fs.rename(tmpScript, hookScriptPath);
|
|
212
|
-
log.info({ action: 'claude_desktop_hook_script_written', path: hookScriptPath }, 'Claude Desktop hook script written');
|
|
213
|
-
// ── 3. Merge into claude_desktop_config.json (safe merge, preserve mcpServers) ──
|
|
214
|
-
let existingConfig = {};
|
|
215
|
-
try {
|
|
216
|
-
const raw = await fs.readFile(desktopConfigPath, 'utf-8');
|
|
217
|
-
existingConfig = JSON.parse(raw);
|
|
218
|
-
}
|
|
219
|
-
catch {
|
|
220
|
-
// File doesn't exist yet — start fresh
|
|
221
|
-
}
|
|
222
|
-
const existingHooks = existingConfig.hooks ?? {};
|
|
223
|
-
const existingPreToolUse = existingHooks.PreToolUse ?? [];
|
|
224
|
-
// De-duplicate: remove any existing Intutic entry for Bash then re-add
|
|
225
|
-
// M3: 'mcp__.*' routes Claude Desktop's mcp__<server>__<tool> calls into
|
|
226
|
-
// this same gate script — see the identical note in claudeCodeHooks.ts,
|
|
227
|
-
// which shares this harness's PreToolUse contract exactly.
|
|
228
|
-
const HOOK_MATCHERS = ['Bash', 'Edit', 'Write', 'MultiEdit', 'mcp__.*'];
|
|
229
|
-
const intuticCmd = `node ${hookScriptPath}`;
|
|
230
|
-
const filteredPTU = existingPreToolUse.filter((entry) => {
|
|
231
|
-
if (typeof entry !== 'object' || entry === null)
|
|
232
|
-
return true;
|
|
233
|
-
const e = entry;
|
|
234
|
-
// Remove entries where all inner hooks use the intutic command
|
|
235
|
-
const innerHooks = e.hooks ?? [];
|
|
236
|
-
return !innerHooks.every((h) => typeof h === 'object' && h !== null && h.command === intuticCmd);
|
|
237
|
-
});
|
|
238
|
-
const newPreToolUseEntries = HOOK_MATCHERS.map((matcher) => ({
|
|
239
|
-
matcher,
|
|
240
|
-
hooks: [{ type: 'command', command: intuticCmd }],
|
|
241
|
-
}));
|
|
242
|
-
const mergedConfig = {
|
|
243
|
-
...existingConfig,
|
|
244
|
-
hooks: {
|
|
245
|
-
...existingHooks,
|
|
246
|
-
PreToolUse: [...filteredPTU, ...newPreToolUseEntries],
|
|
247
|
-
},
|
|
248
|
-
};
|
|
249
|
-
const tmpConfig = desktopConfigPath + '.intutic-tmp';
|
|
250
|
-
await fs.writeFile(tmpConfig, JSON.stringify(mergedConfig, null, 2) + '\n', 'utf-8');
|
|
251
|
-
await fs.rename(tmpConfig, desktopConfigPath);
|
|
252
|
-
log.info({ action: 'claude_desktop_config_written', path: desktopConfigPath }, 'Merged Intutic hooks into claude_desktop_config.json');
|
|
253
|
-
}
|
|
254
|
-
//# sourceMappingURL=claudeDesktopHooks.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"claudeDesktopHooks.js","sourceRoot":"","sources":["../../src/harness/claudeDesktopHooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* continueHooks.ts — Continue CLI PreToolUse gate.
|
|
3
|
-
*
|
|
4
|
-
* Proxy routing for Continue (the `apiBase` of each OpenAI/Anthropic model in
|
|
5
|
-
* `~/.continue/config.yaml`) is written by the CLI's Continue adapter
|
|
6
|
-
* (tools/cli/src/harness/continue.ts). This module writes only the gate.
|
|
7
|
-
*
|
|
8
|
-
* **PreToolUse gate** (Continue **CLI `cn` only** — the IDE extension has
|
|
9
|
-
* no hook system): the CLI executes PreToolUse hooks registered in
|
|
10
|
-
* `~/.continue/settings.json` (user) and `<repo>/.continue/settings.json`
|
|
11
|
-
* (project), with a Claude-Code-compatible stdin contract —
|
|
12
|
-
* {tool_name, tool_input, tool_use_id} — refused via exit code 2 (or a
|
|
13
|
-
* stdout `{"permissionDecision":"deny"}`; exit 2 is used here, the contract
|
|
14
|
-
* every other exit-2 harness shares).
|
|
15
|
-
*
|
|
16
|
-
* **LIVE-VERIFIED LIMITS (cn 1.5.47, 2026-08-10), do not oversell this
|
|
17
|
-
* gate:**
|
|
18
|
-
* - **Headless `-p`/`--print` mode does not execute PreToolUse hooks at
|
|
19
|
-
* all.** Verified against a real install with a bare marker hook: the
|
|
20
|
-
* terminal tool ran, the marker never fired, with the hook registered in
|
|
21
|
-
* every path the bundle's own search list names. The hooks engine (and
|
|
22
|
-
* this exact search list, including `.claude/settings.json`) is present
|
|
23
|
-
* in the shipped bundle — headless mode simply bypasses it. Interactive
|
|
24
|
-
* TUI sessions are the mode this registration targets; a headless `cn
|
|
25
|
-
* -p` run is governed by the proxy layer only.
|
|
26
|
-
* - **cn's hook engine fails OPEN on hook errors by its own design** —
|
|
27
|
-
* the bundled dispatcher catches a failed hook event and returns
|
|
28
|
-
* `{blocked: false}`. Our gate's own crash posture (fail-closed exit 2)
|
|
29
|
-
* cannot override the harness swallowing the failure; that is cn's
|
|
30
|
-
* contract, recorded here so the security matrix can say it honestly.
|
|
31
|
-
*
|
|
32
|
-
* **Overlap, stated so nobody "fixes" it:** the Continue CLI ALSO reads
|
|
33
|
-
* `.claude/settings.json`, so on a machine where claude-code is governed,
|
|
34
|
-
* `cn` may already run the claude-code gate by accident. This writer makes
|
|
35
|
-
* that deliberate by registering in Continue's own settings files — the
|
|
36
|
-
* dedicated registration is still correct because a user who runs continue
|
|
37
|
-
* but not claude-code has no `.claude/settings.json` and would otherwise
|
|
38
|
-
* get no gate at all. Double registration is harmless: both gates evaluate
|
|
39
|
-
* the same rules, and an allow/allow or block/block pair is idempotent.
|
|
40
|
-
*
|
|
41
|
-
* The settings files are merged, never replaced: a file that is not a plain
|
|
42
|
-
* JSON object is left alone and reported (see jsonMergeTarget.ts).
|
|
43
|
-
*
|
|
44
|
-
* LLD #14 — Phase 3 cross-harness defence (Gap 3, WS-B)
|
|
45
|
-
* HLD §3.14 — Three-Tier Defense Cascade (Tier 1 Native Gating)
|
|
46
|
-
*
|
|
47
|
-
* @module
|
|
48
|
-
*/
|
|
49
|
-
/**
|
|
50
|
-
* Write the Continue CLI gate script `.intutic/hooks/continue-check.js` and
|
|
51
|
-
* register it in `~/.continue/settings.json` and
|
|
52
|
-
* `<root>/.continue/settings.json` (see the module header for the
|
|
53
|
-
* `.claude/settings.json` overlap).
|
|
54
|
-
*
|
|
55
|
-
* Safe to call repeatedly — uses atomic rename (`.intutic-tmp` → final path)
|
|
56
|
-
* and command-keyed de-duplication in both settings files.
|
|
57
|
-
*
|
|
58
|
-
* @param workspaceRoot - Absolute workspace root path.
|
|
59
|
-
* @param proxyUrl - Intutic proxy URL written into the gate header.
|
|
60
|
-
* @param workspaceId - Workspace ID embedded in every hook event payload.
|
|
61
|
-
*/
|
|
62
|
-
export declare function writeContinueHooks(workspaceRoot: string, proxyUrl: string, workspaceId?: string): Promise<void>;
|
|
63
|
-
//# sourceMappingURL=continueHooks.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"continueHooks.d.ts","sourceRoot":"","sources":["../../src/harness/continueHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAiLH;;;;;;;;;;;;GAYG;AACH,wBAAsB,kBAAkB,CACtC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAK,GACf,OAAO,CAAC,IAAI,CAAC,CA6Bf"}
|
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* continueHooks.ts — Continue CLI PreToolUse gate.
|
|
3
|
-
*
|
|
4
|
-
* Proxy routing for Continue (the `apiBase` of each OpenAI/Anthropic model in
|
|
5
|
-
* `~/.continue/config.yaml`) is written by the CLI's Continue adapter
|
|
6
|
-
* (tools/cli/src/harness/continue.ts). This module writes only the gate.
|
|
7
|
-
*
|
|
8
|
-
* **PreToolUse gate** (Continue **CLI `cn` only** — the IDE extension has
|
|
9
|
-
* no hook system): the CLI executes PreToolUse hooks registered in
|
|
10
|
-
* `~/.continue/settings.json` (user) and `<repo>/.continue/settings.json`
|
|
11
|
-
* (project), with a Claude-Code-compatible stdin contract —
|
|
12
|
-
* {tool_name, tool_input, tool_use_id} — refused via exit code 2 (or a
|
|
13
|
-
* stdout `{"permissionDecision":"deny"}`; exit 2 is used here, the contract
|
|
14
|
-
* every other exit-2 harness shares).
|
|
15
|
-
*
|
|
16
|
-
* **LIVE-VERIFIED LIMITS (cn 1.5.47, 2026-08-10), do not oversell this
|
|
17
|
-
* gate:**
|
|
18
|
-
* - **Headless `-p`/`--print` mode does not execute PreToolUse hooks at
|
|
19
|
-
* all.** Verified against a real install with a bare marker hook: the
|
|
20
|
-
* terminal tool ran, the marker never fired, with the hook registered in
|
|
21
|
-
* every path the bundle's own search list names. The hooks engine (and
|
|
22
|
-
* this exact search list, including `.claude/settings.json`) is present
|
|
23
|
-
* in the shipped bundle — headless mode simply bypasses it. Interactive
|
|
24
|
-
* TUI sessions are the mode this registration targets; a headless `cn
|
|
25
|
-
* -p` run is governed by the proxy layer only.
|
|
26
|
-
* - **cn's hook engine fails OPEN on hook errors by its own design** —
|
|
27
|
-
* the bundled dispatcher catches a failed hook event and returns
|
|
28
|
-
* `{blocked: false}`. Our gate's own crash posture (fail-closed exit 2)
|
|
29
|
-
* cannot override the harness swallowing the failure; that is cn's
|
|
30
|
-
* contract, recorded here so the security matrix can say it honestly.
|
|
31
|
-
*
|
|
32
|
-
* **Overlap, stated so nobody "fixes" it:** the Continue CLI ALSO reads
|
|
33
|
-
* `.claude/settings.json`, so on a machine where claude-code is governed,
|
|
34
|
-
* `cn` may already run the claude-code gate by accident. This writer makes
|
|
35
|
-
* that deliberate by registering in Continue's own settings files — the
|
|
36
|
-
* dedicated registration is still correct because a user who runs continue
|
|
37
|
-
* but not claude-code has no `.claude/settings.json` and would otherwise
|
|
38
|
-
* get no gate at all. Double registration is harmless: both gates evaluate
|
|
39
|
-
* the same rules, and an allow/allow or block/block pair is idempotent.
|
|
40
|
-
*
|
|
41
|
-
* The settings files are merged, never replaced: a file that is not a plain
|
|
42
|
-
* JSON object is left alone and reported (see jsonMergeTarget.ts).
|
|
43
|
-
*
|
|
44
|
-
* LLD #14 — Phase 3 cross-harness defence (Gap 3, WS-B)
|
|
45
|
-
* HLD §3.14 — Three-Tier Defense Cascade (Tier 1 Native Gating)
|
|
46
|
-
*
|
|
47
|
-
* @module
|
|
48
|
-
*/
|
|
49
|
-
import * as fs from 'node:fs/promises';
|
|
50
|
-
import * as path from 'node:path';
|
|
51
|
-
import * as os from 'node:os';
|
|
52
|
-
import { createLogger } from '@intutic/logger';
|
|
53
|
-
import { newIso } from '@intutic/id';
|
|
54
|
-
import { emitJsGate, emitJsFailClosedPrelude } from './gateBody.js';
|
|
55
|
-
import { readJsonObjectForMerge } from './jsonMergeTarget.js';
|
|
56
|
-
const log = createLogger('sync-continue-hooks');
|
|
57
|
-
// ─── PreToolUse gate (Continue CLI `cn`) ──────────────────────────────────────
|
|
58
|
-
/**
|
|
59
|
-
* The pre-tool gate script — receives the Continue CLI's Claude-Code-compatible
|
|
60
|
-
* PreToolUse JSON on stdin, exits 0 to allow or 2 to block.
|
|
61
|
-
*/
|
|
62
|
-
function buildHookScript(proxyUrl, workspaceRoot, workspaceId) {
|
|
63
|
-
const hookEventsLog = path.join(workspaceRoot, '.intutic', 'events', 'hook-events.jsonl');
|
|
64
|
-
return `#!/usr/bin/env node
|
|
65
|
-
/**
|
|
66
|
-
* Intutic Continue CLI governance gate.
|
|
67
|
-
* Auto-generated by intutic sync-daemon. DO NOT EDIT.
|
|
68
|
-
*
|
|
69
|
-
* Applies to the Continue CLI (cn) only — the IDE extension has no hook
|
|
70
|
-
* system. Note the CLI also reads .claude/settings.json, so a machine governed
|
|
71
|
-
* for claude-code may run that gate too; both evaluate the same rules.
|
|
72
|
-
*
|
|
73
|
-
* Proxy: ${proxyUrl}
|
|
74
|
-
* Generated: ${newIso()}
|
|
75
|
-
*/
|
|
76
|
-
${emitJsFailClosedPrelude({ harness: 'continue', contract: 'exit2' })}
|
|
77
|
-
const path = require('path');
|
|
78
|
-
const os = require('os');
|
|
79
|
-
const fs = require('fs');
|
|
80
|
-
const crypto = require('crypto');
|
|
81
|
-
const https = require('https');
|
|
82
|
-
|
|
83
|
-
// Runtime credentials
|
|
84
|
-
const _runtimeEnvPath = path.join(os.homedir(), '.intutic', 'env', 'runtime.env');
|
|
85
|
-
let _intuticHost = 'https://api.intutic.ai', _intuticKey = '', _intuticWsId = ${JSON.stringify(workspaceId)};
|
|
86
|
-
try {
|
|
87
|
-
fs.readFileSync(_runtimeEnvPath, 'utf-8').split('\\n').forEach(line => {
|
|
88
|
-
const eq = line.indexOf('='); if (eq < 0) return;
|
|
89
|
-
const k = line.slice(0, eq).trim(), v = line.slice(eq + 1).trim();
|
|
90
|
-
if (k === 'INTUTIC_HOST' && v) _intuticHost = v;
|
|
91
|
-
if (k === 'INTUTIC_API_KEY' && v) _intuticKey = v;
|
|
92
|
-
if (k === 'INTUTIC_WORKSPACE_ID' && v) _intuticWsId = v;
|
|
93
|
-
});
|
|
94
|
-
} catch {}
|
|
95
|
-
|
|
96
|
-
${emitJsGate({ harness: 'continue', contract: 'exit2' })}
|
|
97
|
-
|
|
98
|
-
let _intuticSessionId = '';
|
|
99
|
-
function logEvent(verdict, toolName, reason) {
|
|
100
|
-
try {
|
|
101
|
-
const ts = new Date().toISOString();
|
|
102
|
-
const incidentId = crypto.createHash('sha1').update(ts + toolName + _intuticWsId).digest('hex').slice(0, 16);
|
|
103
|
-
const entry = JSON.stringify({
|
|
104
|
-
// Passed through, not collapsed to two values: the advisory tier emits
|
|
105
|
-
// 'tool_flagged', and a ternary here silently recorded it as an allow.
|
|
106
|
-
event: verdict,
|
|
107
|
-
toolName, reason: reason || '',
|
|
108
|
-
workspaceId: _intuticWsId,
|
|
109
|
-
harnessType: 'continue',
|
|
110
|
-
timestamp: ts,
|
|
111
|
-
incidentId,
|
|
112
|
-
...(_intuticSessionId ? { sessionId: _intuticSessionId } : {}),
|
|
113
|
-
}) + '\\n';
|
|
114
|
-
fs.appendFileSync(${JSON.stringify(hookEventsLog)}, entry, { flag: 'a' });
|
|
115
|
-
if (_intuticKey) {
|
|
116
|
-
try {
|
|
117
|
-
const body = JSON.stringify({ events: [JSON.parse(entry)] });
|
|
118
|
-
const urlObj = new URL('/api/v1/hook-events', _intuticHost);
|
|
119
|
-
const mod = urlObj.protocol === 'https:' ? https : require('http');
|
|
120
|
-
const req = mod.request({ hostname: urlObj.hostname, port: urlObj.port || 443, path: urlObj.pathname, method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body), 'Authorization': 'Bearer ' + _intuticKey } });
|
|
121
|
-
req.on('error', () => { /* fire-and-forget */ });
|
|
122
|
-
req.write(body); req.end();
|
|
123
|
-
} catch { /* never crash the hook */ }
|
|
124
|
-
}
|
|
125
|
-
} catch { /* never crash the hook */ }
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
let raw = '';
|
|
129
|
-
process.stdin.on('data', (c) => { raw += c; });
|
|
130
|
-
process.stdin.on('end', () => {
|
|
131
|
-
try {
|
|
132
|
-
const ctx = JSON.parse(raw);
|
|
133
|
-
_intuticSessionId = ctx.session_id || ctx.sessionId || '';
|
|
134
|
-
// Verified during the v5 audit: this writer did NOT have the envelope
|
|
135
|
-
// guard its Claude-Code-compatible contract implied it inherited — a
|
|
136
|
-
// payload with neither a tool name nor a tool_input defaulted to toolName
|
|
137
|
-
// 'tool' with empty fields, matched no rule, and was allowed. Refused now.
|
|
138
|
-
intuticGuardEnvelope(ctx, ['tool_name', 'toolName', 'tool_input', 'toolInput'], logEvent);
|
|
139
|
-
const input = ctx.tool_input || ctx.toolInput || {};
|
|
140
|
-
const toolName = ctx.tool_name || ctx.toolName || 'tool';
|
|
141
|
-
|
|
142
|
-
// Every spelling any harness uses, matching the bash extractor. A field
|
|
143
|
-
// name this list misses is a path guard that silently does not run — and
|
|
144
|
-
// the caller controls which spelling it sends.
|
|
145
|
-
const targetPath = input.path || input.file_path || input.filePath || input.file ||
|
|
146
|
-
input.target || input.notebook_path || '';
|
|
147
|
-
const command = input.command || input.cmd || input.script || '';
|
|
148
|
-
|
|
149
|
-
intuticGate(toolName, targetPath, command, logEvent, _intuticWsId, input);
|
|
150
|
-
|
|
151
|
-
// Allow
|
|
152
|
-
logEvent('tool_allowed', toolName, '');
|
|
153
|
-
process.exit(0);
|
|
154
|
-
} catch (err) {
|
|
155
|
-
// Fail CLOSED
|
|
156
|
-
const errMsg = String(err);
|
|
157
|
-
process.stderr.write('[Intutic Governance] Hook error (fail-closed): ' + errMsg + '\\n');
|
|
158
|
-
logEvent('tool_blocked', 'unknown', errMsg);
|
|
159
|
-
process.exit(2);
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
`;
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* Merge the Intutic PreToolUse entry into a Continue settings.json, preserving
|
|
166
|
-
* everything the user registered themselves.
|
|
167
|
-
*
|
|
168
|
-
* De-duplication is by command (any entry whose hooks all invoke this gate
|
|
169
|
-
* script is ours and is replaced) — the claudeDesktopHooks convention — so
|
|
170
|
-
* repeated syncs never stack duplicate entries.
|
|
171
|
-
*/
|
|
172
|
-
async function mergeContinueSettings(settingsPath, hookScriptPath) {
|
|
173
|
-
const existing = await readJsonObjectForMerge(settingsPath);
|
|
174
|
-
if (existing === null)
|
|
175
|
-
return;
|
|
176
|
-
const existingHooks = existing.hooks ?? {};
|
|
177
|
-
const existingPreToolUse = Array.isArray(existingHooks.PreToolUse)
|
|
178
|
-
? existingHooks.PreToolUse
|
|
179
|
-
: [];
|
|
180
|
-
const intuticCmd = `node "${hookScriptPath}"`;
|
|
181
|
-
const filtered = existingPreToolUse.filter((entry) => {
|
|
182
|
-
if (typeof entry !== 'object' || entry === null)
|
|
183
|
-
return true;
|
|
184
|
-
const inner = entry.hooks ?? [];
|
|
185
|
-
return !(inner.length > 0 &&
|
|
186
|
-
inner.every((h) => typeof h === 'object' && h !== null && h.command === intuticCmd));
|
|
187
|
-
});
|
|
188
|
-
const merged = {
|
|
189
|
-
...existing,
|
|
190
|
-
_intutic_comment: 'Intutic governance PreToolUse hook (Continue CLI) — auto-managed entries only; your own hooks are preserved.',
|
|
191
|
-
_intutic_last_sync: newIso(),
|
|
192
|
-
hooks: {
|
|
193
|
-
...existingHooks,
|
|
194
|
-
PreToolUse: [
|
|
195
|
-
...filtered,
|
|
196
|
-
{
|
|
197
|
-
// One catch-all matcher rather than a tool list: the gate itself
|
|
198
|
-
// decides from the arguments, so a tool name nobody anticipated is
|
|
199
|
-
// still evaluated rather than silently unmatched.
|
|
200
|
-
matcher: '.*',
|
|
201
|
-
hooks: [{ type: 'command', command: intuticCmd }],
|
|
202
|
-
},
|
|
203
|
-
],
|
|
204
|
-
},
|
|
205
|
-
};
|
|
206
|
-
const tmp = settingsPath + '.intutic-tmp';
|
|
207
|
-
await fs.writeFile(tmp, JSON.stringify(merged, null, 2) + '\n', 'utf-8');
|
|
208
|
-
await fs.rename(tmp, settingsPath);
|
|
209
|
-
}
|
|
210
|
-
// ─── Main export ──────────────────────────────────────────────────────────────
|
|
211
|
-
/**
|
|
212
|
-
* Write the Continue CLI gate script `.intutic/hooks/continue-check.js` and
|
|
213
|
-
* register it in `~/.continue/settings.json` and
|
|
214
|
-
* `<root>/.continue/settings.json` (see the module header for the
|
|
215
|
-
* `.claude/settings.json` overlap).
|
|
216
|
-
*
|
|
217
|
-
* Safe to call repeatedly — uses atomic rename (`.intutic-tmp` → final path)
|
|
218
|
-
* and command-keyed de-duplication in both settings files.
|
|
219
|
-
*
|
|
220
|
-
* @param workspaceRoot - Absolute workspace root path.
|
|
221
|
-
* @param proxyUrl - Intutic proxy URL written into the gate header.
|
|
222
|
-
* @param workspaceId - Workspace ID embedded in every hook event payload.
|
|
223
|
-
*/
|
|
224
|
-
export async function writeContinueHooks(workspaceRoot, proxyUrl, workspaceId = '') {
|
|
225
|
-
const hookScriptDir = path.join(workspaceRoot, '.intutic', 'hooks');
|
|
226
|
-
await fs.mkdir(hookScriptDir, { recursive: true });
|
|
227
|
-
await fs.mkdir(path.join(workspaceRoot, '.intutic', 'events'), { recursive: true });
|
|
228
|
-
// Named after its writer — see the note in claudeCodeHooks.ts about two
|
|
229
|
-
// writers overwriting one `pre-tool-check.js`.
|
|
230
|
-
const hookScriptPath = path.join(hookScriptDir, 'continue-check.js');
|
|
231
|
-
const tmpScript = hookScriptPath + '.intutic-tmp';
|
|
232
|
-
await fs.writeFile(tmpScript, buildHookScript(proxyUrl, workspaceRoot, workspaceId), 'utf-8');
|
|
233
|
-
await fs.rename(tmpScript, hookScriptPath);
|
|
234
|
-
await fs.chmod(hookScriptPath, 0o755);
|
|
235
|
-
// Project-level: <root>/.continue/settings.json
|
|
236
|
-
const projectContinueDir = path.join(workspaceRoot, '.continue');
|
|
237
|
-
await fs.mkdir(projectContinueDir, { recursive: true });
|
|
238
|
-
await mergeContinueSettings(path.join(projectContinueDir, 'settings.json'), hookScriptPath);
|
|
239
|
-
// User-level: ~/.continue/settings.json. homedir() read at call time so
|
|
240
|
-
// tests that move HOME before invoking are honoured — see the gooseHooks
|
|
241
|
-
// note in generatedShellIntegrity.test.ts.
|
|
242
|
-
const userContinueDir = path.join(os.homedir(), '.continue');
|
|
243
|
-
await fs.mkdir(userContinueDir, { recursive: true });
|
|
244
|
-
await mergeContinueSettings(path.join(userContinueDir, 'settings.json'), hookScriptPath);
|
|
245
|
-
log.info({ action: 'continue_gate_written', path: hookScriptPath }, 'Continue CLI PreToolUse gate written and registered (project + user settings.json)');
|
|
246
|
-
}
|
|
247
|
-
//# sourceMappingURL=continueHooks.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"continueHooks.js","sourceRoot":"","sources":["../../src/harness/continueHooks.ts"],"names":[],"mappings":"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"}
|