@intutic/sync-daemon 2.0.0 → 2.2.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/dist/agentReporter.d.ts +5 -12
- package/dist/agentReporter.d.ts.map +1 -1
- package/dist/agentReporter.js +25 -4
- package/dist/agentReporter.js.map +1 -1
- package/dist/configReader.d.ts +44 -22
- package/dist/configReader.d.ts.map +1 -1
- package/dist/configReader.js +70 -44
- package/dist/configReader.js.map +1 -1
- package/dist/configWriter.d.ts +29 -41
- package/dist/configWriter.d.ts.map +1 -1
- package/dist/configWriter.js +28 -625
- package/dist/configWriter.js.map +1 -1
- package/dist/disconnect/harnesses.d.ts +31 -0
- package/dist/disconnect/harnesses.d.ts.map +1 -0
- package/dist/disconnect/harnesses.js +728 -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 +82 -0
- package/dist/disconnect/index.js.map +1 -0
- package/dist/disconnect/mcp.d.ts +60 -0
- package/dist/disconnect/mcp.d.ts.map +1 -0
- package/dist/disconnect/mcp.js +283 -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 +98 -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 +42 -5
- package/dist/harness/aiderConfigMerger.d.ts.map +1 -1
- package/dist/harness/aiderConfigMerger.js +113 -86
- package/dist/harness/aiderConfigMerger.js.map +1 -1
- package/dist/harness/antigravityHooks.d.ts +13 -8
- package/dist/harness/antigravityHooks.d.ts.map +1 -1
- package/dist/harness/antigravityHooks.js +53 -39
- package/dist/harness/antigravityHooks.js.map +1 -1
- package/dist/harness/claudeCodeHooks.d.ts +1 -2
- package/dist/harness/claudeCodeHooks.d.ts.map +1 -1
- package/dist/harness/claudeCodeHooks.js +61 -34
- package/dist/harness/claudeCodeHooks.js.map +1 -1
- package/dist/harness/claudeDesktopHooks.d.ts +3 -0
- package/dist/harness/claudeDesktopHooks.d.ts.map +1 -1
- package/dist/harness/claudeDesktopHooks.js +3 -1
- package/dist/harness/claudeDesktopHooks.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 +39 -9
- package/dist/harness/clineHooks.d.ts.map +1 -1
- package/dist/harness/clineHooks.js +142 -60
- package/dist/harness/clineHooks.js.map +1 -1
- package/dist/harness/codexConfigMerger.d.ts +38 -0
- package/dist/harness/codexConfigMerger.d.ts.map +1 -0
- package/dist/harness/codexConfigMerger.js +105 -0
- package/dist/harness/codexConfigMerger.js.map +1 -0
- package/dist/harness/codexHooks.d.ts +4 -2
- package/dist/harness/codexHooks.d.ts.map +1 -1
- package/dist/harness/codexHooks.js +14 -11
- package/dist/harness/codexHooks.js.map +1 -1
- package/dist/harness/continueConfigMerger.d.ts +46 -0
- package/dist/harness/continueConfigMerger.d.ts.map +1 -0
- package/dist/harness/continueConfigMerger.js +94 -0
- package/dist/harness/continueConfigMerger.js.map +1 -0
- package/dist/harness/continueHooks.d.ts +15 -25
- package/dist/harness/continueHooks.d.ts.map +1 -1
- package/dist/harness/continueHooks.js +25 -119
- package/dist/harness/continueHooks.js.map +1 -1
- package/dist/harness/cursorHooks.d.ts +33 -16
- package/dist/harness/cursorHooks.d.ts.map +1 -1
- package/dist/harness/cursorHooks.js +69 -25
- package/dist/harness/cursorHooks.js.map +1 -1
- package/dist/harness/dshHooks.d.ts +59 -52
- package/dist/harness/dshHooks.d.ts.map +1 -1
- package/dist/harness/dshHooks.js +152 -147
- package/dist/harness/dshHooks.js.map +1 -1
- package/dist/harness/gateBody.d.ts +11 -2
- package/dist/harness/gateBody.d.ts.map +1 -1
- package/dist/harness/gateBody.js +33 -6
- package/dist/harness/gateBody.js.map +1 -1
- package/dist/harness/gateKind.d.ts.map +1 -1
- package/dist/harness/gateKind.js +1 -0
- package/dist/harness/gateKind.js.map +1 -1
- package/dist/harness/githubCopilotHooks.d.ts.map +1 -1
- package/dist/harness/githubCopilotHooks.js +5 -10
- package/dist/harness/githubCopilotHooks.js.map +1 -1
- package/dist/harness/gooseHooks.d.ts +15 -0
- package/dist/harness/gooseHooks.d.ts.map +1 -1
- package/dist/harness/gooseHooks.js +55 -43
- package/dist/harness/gooseHooks.js.map +1 -1
- package/dist/harness/grokHooks.d.ts.map +1 -1
- package/dist/harness/grokHooks.js +14 -12
- package/dist/harness/grokHooks.js.map +1 -1
- package/dist/harness/hermesHooks.d.ts +9 -0
- package/dist/harness/hermesHooks.d.ts.map +1 -1
- package/dist/harness/hermesHooks.js +39 -33
- 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/jsonMergeTarget.d.ts +23 -0
- package/dist/harness/jsonMergeTarget.d.ts.map +1 -0
- package/dist/harness/jsonMergeTarget.js +50 -0
- package/dist/harness/jsonMergeTarget.js.map +1 -0
- package/dist/harness/mcpAutoWrite.d.ts +64 -20
- package/dist/harness/mcpAutoWrite.d.ts.map +1 -1
- package/dist/harness/mcpAutoWrite.js +429 -85
- package/dist/harness/mcpAutoWrite.js.map +1 -1
- package/dist/harness/museHooks.d.ts.map +1 -1
- package/dist/harness/museHooks.js +6 -2
- package/dist/harness/museHooks.js.map +1 -1
- package/dist/harness/n8nHooks.d.ts.map +1 -1
- package/dist/harness/n8nHooks.js +6 -7
- package/dist/harness/n8nHooks.js.map +1 -1
- package/dist/harness/openCodeHooks.d.ts +37 -6
- package/dist/harness/openCodeHooks.d.ts.map +1 -1
- package/dist/harness/openCodeHooks.js +103 -13
- package/dist/harness/openCodeHooks.js.map +1 -1
- package/dist/harness/openWebuiHooks.d.ts.map +1 -1
- package/dist/harness/openWebuiHooks.js +10 -5
- package/dist/harness/openWebuiHooks.js.map +1 -1
- package/dist/harness/openclawHooks.d.ts +5 -0
- package/dist/harness/openclawHooks.d.ts.map +1 -1
- package/dist/harness/openclawHooks.js +3 -1
- package/dist/harness/openclawHooks.js.map +1 -1
- package/dist/harness/openhandsHooks.d.ts +26 -1
- package/dist/harness/openhandsHooks.d.ts.map +1 -1
- package/dist/harness/openhandsHooks.js +160 -40
- package/dist/harness/openhandsHooks.js.map +1 -1
- package/dist/harness/piHooks.d.ts +17 -0
- package/dist/harness/piHooks.d.ts.map +1 -1
- package/dist/harness/piHooks.js +31 -29
- package/dist/harness/piHooks.js.map +1 -1
- package/dist/harness/protectedPaths.d.ts +64 -6
- package/dist/harness/protectedPaths.d.ts.map +1 -1
- package/dist/harness/protectedPaths.js +161 -11
- package/dist/harness/protectedPaths.js.map +1 -1
- package/dist/harness/rooCodeHooks.d.ts.map +1 -1
- package/dist/harness/rooCodeHooks.js +5 -10
- package/dist/harness/rooCodeHooks.js.map +1 -1
- package/dist/harness/windsurfHooks.d.ts +21 -3
- package/dist/harness/windsurfHooks.d.ts.map +1 -1
- package/dist/harness/windsurfHooks.js +96 -44
- package/dist/harness/windsurfHooks.js.map +1 -1
- package/dist/harness/windsurfJetBrainsProxy.d.ts +10 -7
- package/dist/harness/windsurfJetBrainsProxy.d.ts.map +1 -1
- package/dist/harness/windsurfJetBrainsProxy.js +22 -7
- package/dist/harness/windsurfJetBrainsProxy.js.map +1 -1
- package/dist/index.d.ts +12 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -10
- package/dist/index.js.map +1 -1
- package/dist/lib/decisionsDigest.d.ts +1 -1
- package/dist/lib/decisionsDigest.js +1 -1
- package/dist/lib/policySnapshot.d.ts +37 -0
- package/dist/lib/policySnapshot.d.ts.map +1 -1
- package/dist/lib/policySnapshot.js +170 -8
- package/dist/lib/policySnapshot.js.map +1 -1
- package/dist/sessionReporter.d.ts +2 -2
- package/dist/sessionReporter.js +2 -2
- package/dist/syncCycle.d.ts +142 -0
- package/dist/syncCycle.d.ts.map +1 -0
- package/dist/syncCycle.js +315 -0
- package/dist/syncCycle.js.map +1 -0
- package/dist/trajectoryMonitor.d.ts +1 -1
- package/dist/trajectoryMonitor.d.ts.map +1 -1
- package/dist/trajectoryMonitor.js +1 -1
- package/dist/trajectoryMonitor.js.map +1 -1
- package/dist/watcher/settingsGuard.d.ts +4 -1
- package/dist/watcher/settingsGuard.d.ts.map +1 -1
- package/dist/watcher/settingsGuard.js +42 -18
- package/dist/watcher/settingsGuard.js.map +1 -1
- package/package.json +6 -6
- 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
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* claudeProjectApproval.ts — which of a repo's `.mcp.json` servers Claude Code
|
|
3
|
+
* would start, decided the way Claude Code decides it.
|
|
4
|
+
*
|
|
5
|
+
* The sync daemon governs project-scope servers through wrapped local-scope
|
|
6
|
+
* copies (mcpAutoWrite.ts's `shadowProjectServers`), and a local-scope server
|
|
7
|
+
* starts without Claude Code's project-server approval. So a copy may exist
|
|
8
|
+
* only for a server Claude Code would itself start; anything looser would let
|
|
9
|
+
* a cloned repository approve its own server and have us wrap and start it.
|
|
10
|
+
*
|
|
11
|
+
* Claude Code's rules (https://code.claude.com/docs/en/mcp, "Project server
|
|
12
|
+
* approvals and workspace trust"):
|
|
13
|
+
*
|
|
14
|
+
* - Approvals (`enableAllProjectMcpServers`, `enabledMcpjsonServers`) from
|
|
15
|
+
* the user's `~/.claude/settings.json`, managed settings and the user's own
|
|
16
|
+
* record in `~/.claude.json` always apply.
|
|
17
|
+
* - Approvals from the project's `.claude/settings.json` and
|
|
18
|
+
* `.claude/settings.local.json` apply only once the folder is trusted.
|
|
19
|
+
* Trust is `projects[<repository root>].hasTrustDialogAccepted` in
|
|
20
|
+
* `~/.claude.json` — keyed on the git root, or the main checkout's root for
|
|
21
|
+
* a worktree, or the folder itself outside a repository. Anything we cannot
|
|
22
|
+
* confirm counts as untrusted.
|
|
23
|
+
* - `disabledMcpjsonServers` in any of those files rejects the server,
|
|
24
|
+
* trusted folder or not.
|
|
25
|
+
*
|
|
26
|
+
* Managed policy can also arrive through MDM (a macOS configuration profile)
|
|
27
|
+
* or the Windows registry, neither of which this module reads. Where one may
|
|
28
|
+
* be present, no project server is approved here: missing an organization's
|
|
29
|
+
* disable is the one mistake this check must not make.
|
|
30
|
+
*
|
|
31
|
+
* @module
|
|
32
|
+
*/
|
|
33
|
+
import * as node_fs from 'node:fs/promises';
|
|
34
|
+
import * as node_path from 'node:path';
|
|
35
|
+
import * as node_os from 'node:os';
|
|
36
|
+
import { existsSync, realpathSync } from 'node:fs';
|
|
37
|
+
import { execFileSync } from 'node:child_process';
|
|
38
|
+
function defaultManagedDir(platform) {
|
|
39
|
+
if (platform === 'darwin')
|
|
40
|
+
return '/Library/Application Support/ClaudeCode';
|
|
41
|
+
if (platform === 'win32')
|
|
42
|
+
return 'C:\\Program Files\\ClaudeCode';
|
|
43
|
+
return '/etc/claude-code';
|
|
44
|
+
}
|
|
45
|
+
function defaultMdmProfiles(platform) {
|
|
46
|
+
if (platform !== 'darwin')
|
|
47
|
+
return [];
|
|
48
|
+
const user = (() => {
|
|
49
|
+
try {
|
|
50
|
+
return node_os.userInfo().username;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return '';
|
|
54
|
+
}
|
|
55
|
+
})();
|
|
56
|
+
return [
|
|
57
|
+
'/Library/Managed Preferences/com.anthropic.claudecode.plist',
|
|
58
|
+
...(user ? [`/Library/Managed Preferences/${user}/com.anthropic.claudecode.plist`] : []),
|
|
59
|
+
];
|
|
60
|
+
}
|
|
61
|
+
async function readObject(file) {
|
|
62
|
+
try {
|
|
63
|
+
const parsed = JSON.parse(await node_fs.readFile(file, 'utf-8'));
|
|
64
|
+
return parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : null;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/** The managed-settings documents: `managed-settings.json` and the `managed-settings.d/` drop-ins. */
|
|
71
|
+
async function readManaged(dir) {
|
|
72
|
+
const out = [];
|
|
73
|
+
const main = await readObject(node_path.join(dir, 'managed-settings.json'));
|
|
74
|
+
if (main)
|
|
75
|
+
out.push(main);
|
|
76
|
+
try {
|
|
77
|
+
const dropIns = (await node_fs.readdir(node_path.join(dir, 'managed-settings.d'))).filter((f) => f.endsWith('.json')).sort();
|
|
78
|
+
for (const f of dropIns) {
|
|
79
|
+
const doc = await readObject(node_path.join(dir, 'managed-settings.d', f));
|
|
80
|
+
if (doc)
|
|
81
|
+
out.push(doc);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// No drop-in directory.
|
|
86
|
+
}
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* The path(s) Claude Code may key this folder's trust on: the repository root
|
|
91
|
+
* (the main checkout's root for a worktree), or the folder itself outside a
|
|
92
|
+
* repository — each as given and as its real path, since `projects` keys are
|
|
93
|
+
* whatever path Claude Code was started with.
|
|
94
|
+
*/
|
|
95
|
+
function trustKeys(workspaceRoot) {
|
|
96
|
+
const keys = new Set([workspaceRoot]);
|
|
97
|
+
try {
|
|
98
|
+
const common = execFileSync('git', ['-C', workspaceRoot, 'rev-parse', '--path-format=absolute', '--git-common-dir'], {
|
|
99
|
+
encoding: 'utf-8',
|
|
100
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
101
|
+
timeout: 2000,
|
|
102
|
+
}).trim();
|
|
103
|
+
if (common)
|
|
104
|
+
keys.add(node_path.dirname(common));
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
// Not a repository, or no git: the folder itself is the key.
|
|
108
|
+
}
|
|
109
|
+
for (const k of [...keys]) {
|
|
110
|
+
try {
|
|
111
|
+
keys.add(realpathSync(k));
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// A key that does not resolve stays as given.
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return [...keys];
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Decides which project servers Claude Code would start for `workspaceRoot`,
|
|
121
|
+
* given the parsed `~/.claude.json` (`claudeState`).
|
|
122
|
+
*/
|
|
123
|
+
export async function projectServerApproval(workspaceRoot, claudeState, env = {}) {
|
|
124
|
+
const platform = env.platform ?? process.platform;
|
|
125
|
+
const mdmProfiles = env.mdmProfiles ?? defaultMdmProfiles(platform);
|
|
126
|
+
if (platform === 'win32' || mdmProfiles.some((p) => existsSync(p))) {
|
|
127
|
+
return {
|
|
128
|
+
approved: () => false,
|
|
129
|
+
blockedReason: 'Claude Code managed policy may be deployed through MDM or the registry, which Intutic cannot read, so no project server is copied',
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
const projectEntry = (key) => {
|
|
133
|
+
const entry = claudeState.projects?.[key];
|
|
134
|
+
return entry !== null && typeof entry === 'object' && !Array.isArray(entry) ? entry : {};
|
|
135
|
+
};
|
|
136
|
+
const keys = trustKeys(workspaceRoot);
|
|
137
|
+
const trusted = keys.some((k) => projectEntry(k).hasTrustDialogAccepted === true);
|
|
138
|
+
const userSources = [
|
|
139
|
+
projectEntry(workspaceRoot),
|
|
140
|
+
...(await readManaged(env.managedDir ?? defaultManagedDir(platform))),
|
|
141
|
+
];
|
|
142
|
+
const userSettings = await readObject(node_path.join(node_os.homedir(), '.claude', 'settings.json'));
|
|
143
|
+
if (userSettings)
|
|
144
|
+
userSources.push(userSettings);
|
|
145
|
+
const projectSources = [];
|
|
146
|
+
for (const file of ['settings.json', 'settings.local.json']) {
|
|
147
|
+
const doc = await readObject(node_path.join(workspaceRoot, '.claude', file));
|
|
148
|
+
if (doc)
|
|
149
|
+
projectSources.push(doc);
|
|
150
|
+
}
|
|
151
|
+
const names = (v) => (Array.isArray(v) ? v.filter((n) => typeof n === 'string') : []);
|
|
152
|
+
const approving = trusted ? [...userSources, ...projectSources] : userSources;
|
|
153
|
+
const all = approving.some((src) => src.enableAllProjectMcpServers === true);
|
|
154
|
+
const enabled = new Set(approving.flatMap((src) => names(src.enabledMcpjsonServers)));
|
|
155
|
+
// A disable counts from every file, the committed ones included.
|
|
156
|
+
const disabled = new Set([...userSources, ...projectSources].flatMap((src) => names(src.disabledMcpjsonServers)));
|
|
157
|
+
return { approved: (name) => !disabled.has(name) && (all || enabled.has(name)) };
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=claudeProjectApproval.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claudeProjectApproval.js","sourceRoot":"","sources":["../../src/harness/claudeProjectApproval.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* clineHooks.ts — Cline PreToolUse governance hook
|
|
2
|
+
* clineHooks.ts — Cline PreToolUse governance hook.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Cline runs file hooks: an executable named exactly `PreToolUse` (no
|
|
5
|
+
* extension on macOS/Linux) in a hooks directory, fed the pending tool call as
|
|
6
|
+
* JSON on stdin. It refuses the call when the hook prints `{"cancel": true}`
|
|
7
|
+
* on stdout; the exit code does not decide anything. Confirmed against
|
|
8
|
+
* Cline's source (apps/vscode/src/core/hooks, sdk/packages/core/src/hooks):
|
|
7
9
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
+
* - The VS Code extension reads `<workspace>/.clinerules/hooks/PreToolUse` and
|
|
11
|
+
* runs it only when "Enable Hooks" is on in Cline's feature settings and the
|
|
12
|
+
* file is executable. Its payload is
|
|
13
|
+
* `{hookName, preToolUse: {toolName, parameters}}`, each parameter value
|
|
14
|
+
* JSON-encoded as a string.
|
|
15
|
+
* - The Cline CLI/SDK searches the same `.clinerules/hooks` directory (plus
|
|
16
|
+
* `.cline/hooks` and two user-level directories). Its payload is
|
|
17
|
+
* `{hookName, tool_call: {id, name, input}}`.
|
|
18
|
+
*
|
|
19
|
+
* So one file, `.clinerules/hooks/PreToolUse`, covers both, and the gate
|
|
20
|
+
* accepts both payload shapes. There is no `hooks.json` and no matcher: the
|
|
21
|
+
* hook runs for every tool call, MCP calls (`use_mcp_tool`) included, and the
|
|
22
|
+
* gate decides from the arguments.
|
|
23
|
+
*
|
|
24
|
+
* `.clinerules` must therefore be a directory. Earlier versions wrote it as a
|
|
25
|
+
* flat rules file (and registered the gate in a `.cline/hooks/hooks.json`
|
|
26
|
+
* Cline never reads); `ensureClinerulesDirectory` converts the flat file this
|
|
27
|
+
* product wrote, and leaves a flat `.clinerules` the user wrote alone — in
|
|
28
|
+
* that case no gate can be installed and a warning says why.
|
|
10
29
|
*
|
|
11
30
|
* LLD #14 — Phase 3 cross-harness defence
|
|
12
31
|
* HLD §3.14 — Three-Tier Defense Cascade
|
|
@@ -14,10 +33,21 @@
|
|
|
14
33
|
* @module
|
|
15
34
|
*/
|
|
16
35
|
/**
|
|
17
|
-
*
|
|
36
|
+
* Make `<workspace>/.clinerules` a directory, converting the flat rules file
|
|
37
|
+
* earlier versions of this product wrote there.
|
|
38
|
+
*
|
|
39
|
+
* @returns `true` when `.clinerules` is (now) a directory; `false` when it is a
|
|
40
|
+
* file the user wrote, which is left alone (a warning is logged).
|
|
41
|
+
*/
|
|
42
|
+
export declare function ensureClinerulesDirectory(workspaceRoot: string): Promise<boolean>;
|
|
43
|
+
/**
|
|
44
|
+
* Write the Intutic gate to `<workspace>/.clinerules/hooks/PreToolUse`.
|
|
18
45
|
*
|
|
19
46
|
* @param workspaceRoot - Absolute workspace root path.
|
|
20
|
-
* @param proxyUrl - Intutic proxy URL for inclusion in
|
|
47
|
+
* @param proxyUrl - Intutic proxy URL for inclusion in the gate header.
|
|
48
|
+
* @param workspaceId - Workspace ID embedded in every hook event payload.
|
|
49
|
+
* @returns the gate path, or `null` when it could not be installed without
|
|
50
|
+
* overwriting a file the user owns (a warning is logged).
|
|
21
51
|
*/
|
|
22
|
-
export declare function writeClineHooks(workspaceRoot: string, proxyUrl: string, workspaceId?: string): Promise<
|
|
52
|
+
export declare function writeClineHooks(workspaceRoot: string, proxyUrl: string, workspaceId?: string): Promise<string | null>;
|
|
23
53
|
//# sourceMappingURL=clineHooks.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clineHooks.d.ts","sourceRoot":"","sources":["../../src/harness/clineHooks.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"clineHooks.d.ts","sourceRoot":"","sources":["../../src/harness/clineHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAiBH;;;;;;GAMG;AACH,wBAAsB,yBAAyB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAuBvF;AAkBD;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CACnC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAK,GACf,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAoKxB"}
|
|
@@ -1,12 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* clineHooks.ts — Cline PreToolUse governance hook
|
|
2
|
+
* clineHooks.ts — Cline PreToolUse governance hook.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Cline runs file hooks: an executable named exactly `PreToolUse` (no
|
|
5
|
+
* extension on macOS/Linux) in a hooks directory, fed the pending tool call as
|
|
6
|
+
* JSON on stdin. It refuses the call when the hook prints `{"cancel": true}`
|
|
7
|
+
* on stdout; the exit code does not decide anything. Confirmed against
|
|
8
|
+
* Cline's source (apps/vscode/src/core/hooks, sdk/packages/core/src/hooks):
|
|
7
9
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
+
* - The VS Code extension reads `<workspace>/.clinerules/hooks/PreToolUse` and
|
|
11
|
+
* runs it only when "Enable Hooks" is on in Cline's feature settings and the
|
|
12
|
+
* file is executable. Its payload is
|
|
13
|
+
* `{hookName, preToolUse: {toolName, parameters}}`, each parameter value
|
|
14
|
+
* JSON-encoded as a string.
|
|
15
|
+
* - The Cline CLI/SDK searches the same `.clinerules/hooks` directory (plus
|
|
16
|
+
* `.cline/hooks` and two user-level directories). Its payload is
|
|
17
|
+
* `{hookName, tool_call: {id, name, input}}`.
|
|
18
|
+
*
|
|
19
|
+
* So one file, `.clinerules/hooks/PreToolUse`, covers both, and the gate
|
|
20
|
+
* accepts both payload shapes. There is no `hooks.json` and no matcher: the
|
|
21
|
+
* hook runs for every tool call, MCP calls (`use_mcp_tool`) included, and the
|
|
22
|
+
* gate decides from the arguments.
|
|
23
|
+
*
|
|
24
|
+
* `.clinerules` must therefore be a directory. Earlier versions wrote it as a
|
|
25
|
+
* flat rules file (and registered the gate in a `.cline/hooks/hooks.json`
|
|
26
|
+
* Cline never reads); `ensureClinerulesDirectory` converts the flat file this
|
|
27
|
+
* product wrote, and leaves a flat `.clinerules` the user wrote alone — in
|
|
28
|
+
* that case no gate can be installed and a warning says why.
|
|
10
29
|
*
|
|
11
30
|
* LLD #14 — Phase 3 cross-harness defence
|
|
12
31
|
* HLD §3.14 — Three-Tier Defense Cascade
|
|
@@ -16,52 +35,91 @@
|
|
|
16
35
|
import * as fs from 'node:fs/promises';
|
|
17
36
|
import * as path from 'node:path';
|
|
18
37
|
import { createLogger } from '@intutic/logger';
|
|
38
|
+
import { keepOriginal, noteWritten } from '../disconnect/originals.js';
|
|
19
39
|
import { newIso } from '@intutic/id';
|
|
20
40
|
import { emitJsGate, emitJsFailClosedPrelude, REVIEW_REQUESTS_BASENAME } from './gateBody.js';
|
|
21
41
|
const log = createLogger('sync-cline-hooks');
|
|
22
|
-
/**
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
'str_replace_based_edit_tool',
|
|
27
|
-
'replace_in_file',
|
|
28
|
-
'create_file',
|
|
29
|
-
'delete_file',
|
|
30
|
-
'rename_file',
|
|
31
|
-
'read_file', // watch for exfiltration patterns
|
|
32
|
-
];
|
|
42
|
+
/** First line of every rules file this product writes. */
|
|
43
|
+
const RULES_HEADER = '# Intutic Governance Rules (auto-generated)';
|
|
44
|
+
/** Marker line in the generated hook, used to recognise our own file. */
|
|
45
|
+
const GATE_MARKER = 'Intutic Cline PreToolUse governance gate.';
|
|
33
46
|
/**
|
|
34
|
-
*
|
|
47
|
+
* Make `<workspace>/.clinerules` a directory, converting the flat rules file
|
|
48
|
+
* earlier versions of this product wrote there.
|
|
49
|
+
*
|
|
50
|
+
* @returns `true` when `.clinerules` is (now) a directory; `false` when it is a
|
|
51
|
+
* file the user wrote, which is left alone (a warning is logged).
|
|
52
|
+
*/
|
|
53
|
+
export async function ensureClinerulesDirectory(workspaceRoot) {
|
|
54
|
+
const rulesPath = path.join(workspaceRoot, '.clinerules');
|
|
55
|
+
let stat;
|
|
56
|
+
try {
|
|
57
|
+
stat = await fs.stat(rulesPath);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
await fs.mkdir(rulesPath, { recursive: true });
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
if (stat.isDirectory())
|
|
64
|
+
return true;
|
|
65
|
+
const content = await fs.readFile(rulesPath, 'utf-8');
|
|
66
|
+
if (!content.startsWith(RULES_HEADER)) {
|
|
67
|
+
log.warn({ action: 'cline_rules_file_kept', path: rulesPath }, '.clinerules is a rules file you wrote, so Intutic cannot create .clinerules/hooks/ — move its content into ' +
|
|
68
|
+
'.clinerules/<name>.md to enable the Intutic gate');
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
await fs.unlink(rulesPath);
|
|
72
|
+
await fs.mkdir(rulesPath, { recursive: true });
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
/** Remove the `.cline/hooks` registration earlier versions wrote — Cline never
|
|
76
|
+
* read it. Only files carrying this product's names or marker are removed. */
|
|
77
|
+
async function removeLegacyRegistration(workspaceRoot) {
|
|
78
|
+
const legacyDir = path.join(workspaceRoot, '.cline', 'hooks');
|
|
79
|
+
await fs.rm(path.join(legacyDir, 'intutic-check.js'), { force: true });
|
|
80
|
+
const legacyJson = path.join(legacyDir, 'hooks.json');
|
|
81
|
+
try {
|
|
82
|
+
const parsed = JSON.parse(await fs.readFile(legacyJson, 'utf-8'));
|
|
83
|
+
if (typeof parsed._comment === 'string' && parsed._comment.startsWith('Intutic governance hooks')) {
|
|
84
|
+
await fs.unlink(legacyJson);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// Absent, or not ours to judge — left alone.
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Write the Intutic gate to `<workspace>/.clinerules/hooks/PreToolUse`.
|
|
35
93
|
*
|
|
36
94
|
* @param workspaceRoot - Absolute workspace root path.
|
|
37
|
-
* @param proxyUrl - Intutic proxy URL for inclusion in
|
|
95
|
+
* @param proxyUrl - Intutic proxy URL for inclusion in the gate header.
|
|
96
|
+
* @param workspaceId - Workspace ID embedded in every hook event payload.
|
|
97
|
+
* @returns the gate path, or `null` when it could not be installed without
|
|
98
|
+
* overwriting a file the user owns (a warning is logged).
|
|
38
99
|
*/
|
|
39
100
|
export async function writeClineHooks(workspaceRoot, proxyUrl, workspaceId = '') {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
101
|
+
await removeLegacyRegistration(workspaceRoot);
|
|
102
|
+
if (!(await ensureClinerulesDirectory(workspaceRoot)))
|
|
103
|
+
return null;
|
|
104
|
+
const hooksDir = path.join(workspaceRoot, '.clinerules', 'hooks');
|
|
105
|
+
const checkScriptPath = path.join(hooksDir, 'PreToolUse');
|
|
106
|
+
await keepOriginal(checkScriptPath, workspaceRoot);
|
|
43
107
|
await fs.mkdir(hooksDir, { recursive: true });
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
})),
|
|
55
|
-
};
|
|
56
|
-
const tmpJson = hooksJsonPath + '.intutic-tmp';
|
|
57
|
-
await fs.writeFile(tmpJson, JSON.stringify(hooksConfig, null, 2) + '\n', 'utf-8');
|
|
58
|
-
await fs.rename(tmpJson, hooksJsonPath);
|
|
108
|
+
try {
|
|
109
|
+
const existing = await fs.readFile(checkScriptPath, 'utf-8');
|
|
110
|
+
if (!existing.includes(GATE_MARKER)) {
|
|
111
|
+
log.warn({ action: 'cline_hook_kept', path: checkScriptPath }, 'A PreToolUse hook you wrote already exists — left untouched, so the Intutic gate is not installed for Cline');
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// No hook yet.
|
|
117
|
+
}
|
|
59
118
|
// ── pre-tool-check script ────────────────────────────────────────────
|
|
60
|
-
// Cline passes tool_name + tool_input as JSON on stdin.
|
|
61
119
|
// Return {"cancel": false} to allow, {"cancel": true, "errorMessage": "..."} to block.
|
|
62
120
|
const checkScript = `#!/usr/bin/env node
|
|
63
121
|
/**
|
|
64
|
-
*
|
|
122
|
+
* ${GATE_MARKER}
|
|
65
123
|
* Auto-generated by intutic sync-daemon. DO NOT EDIT.
|
|
66
124
|
* Proxy: ${proxyUrl}
|
|
67
125
|
* Generated: ${newIso()}
|
|
@@ -100,7 +158,7 @@ function logEvent(verdict, toolName, reason) {
|
|
|
100
158
|
// on any machine where nothing else had created ~/.intutic/events the append
|
|
101
159
|
// threw ENOENT and was discarded — every Cline audit line, including blocks,
|
|
102
160
|
// silently dropped. The other writers create this directory from the
|
|
103
|
-
// TypeScript side; Cline's writer only creates
|
|
161
|
+
// TypeScript side; Cline's writer only creates its hooks directory, so its log
|
|
104
162
|
// existed only by luck.
|
|
105
163
|
try {
|
|
106
164
|
const _dir = path.dirname(HOOK_EVENTS_LOG);
|
|
@@ -127,30 +185,50 @@ process.stdin.on('data', (c) => { raw += c; });
|
|
|
127
185
|
process.stdin.on('end', () => {
|
|
128
186
|
try {
|
|
129
187
|
const ctx = JSON.parse(raw);
|
|
130
|
-
_intuticSessionId = ctx.
|
|
188
|
+
_intuticSessionId = ctx.taskId || ctx.task_id || ctx.session_id || ctx.sessionId || ctx.conversation_id || ctx.conversationId || '';
|
|
131
189
|
// An envelope carrying none of the tool fields extracts to empty strings,
|
|
132
190
|
// which match no rule — an allow. Refused instead, and the refusal CANCELS
|
|
133
191
|
// (this harness ignores exit codes); see intuticGuardEnvelope.
|
|
134
|
-
intuticGuardEnvelope(ctx, ['tool_name', 'toolName', 'tool_input', 'toolInput'], logEvent);
|
|
135
|
-
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
const
|
|
143
|
-
|
|
192
|
+
intuticGuardEnvelope(ctx, ['tool_call', 'preToolUse', 'tool_name', 'toolName', 'tool_input', 'toolInput'], logEvent);
|
|
193
|
+
|
|
194
|
+
// Cline sends one of two shapes (see clineHooks.ts): the CLI/SDK's
|
|
195
|
+
// tool_call {name, input}, or the VS Code extension's preToolUse
|
|
196
|
+
// {toolName, parameters} with every parameter value JSON-encoded as a
|
|
197
|
+
// string. tool_name/tool_input is the generic shape the shared gate tests
|
|
198
|
+
// drive. Case is preserved for the gate: a BLOCK: SOP compiles to a
|
|
199
|
+
// tool-name pattern the operator wrote as they see it.
|
|
200
|
+
const dejson = (v) => {
|
|
201
|
+
if (typeof v !== 'string') return v;
|
|
202
|
+
try { const p = JSON.parse(v); return (p !== null && typeof p === 'object') ? p : v; } catch { return v; }
|
|
203
|
+
};
|
|
204
|
+
let rawToolName = '';
|
|
205
|
+
let input = {};
|
|
206
|
+
if (ctx.tool_call && typeof ctx.tool_call === 'object') {
|
|
207
|
+
rawToolName = ctx.tool_call.name || '';
|
|
208
|
+
input = ctx.tool_call.input;
|
|
209
|
+
} else if (ctx.preToolUse && typeof ctx.preToolUse === 'object') {
|
|
210
|
+
rawToolName = ctx.preToolUse.toolName || '';
|
|
211
|
+
const params = ctx.preToolUse.parameters || {};
|
|
212
|
+
input = {};
|
|
213
|
+
for (const k of Object.keys(params)) input[k] = dejson(params[k]);
|
|
214
|
+
} else {
|
|
215
|
+
rawToolName = ctx.tool_name || ctx.toolName || '';
|
|
216
|
+
input = ctx.tool_input || ctx.toolInput || {};
|
|
217
|
+
}
|
|
218
|
+
if (input === null || typeof input !== 'object') input = { input: input };
|
|
219
|
+
const tool = rawToolName.toLowerCase();
|
|
144
220
|
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
221
|
+
// The CLI's file and shell tools take lists (read_files {files}, run_commands
|
|
222
|
+
// {commands}); the extension's take single values. Both are read.
|
|
223
|
+
const firstFile = Array.isArray(input.files) && input.files.length > 0
|
|
224
|
+
? (typeof input.files[0] === 'string' ? input.files[0] : (input.files[0] && input.files[0].path) || '')
|
|
225
|
+
: '';
|
|
226
|
+
const commands = Array.isArray(input.commands)
|
|
227
|
+
? input.commands.map((c) => (typeof c === 'string' ? c : (c && c.command) || '')).join('\\n')
|
|
228
|
+
: '';
|
|
151
229
|
const targetPath = input.path || input.file_path || input.filePath ||
|
|
152
|
-
input.target || input.source || input.notebook_path || '';
|
|
153
|
-
const command = input.command || input.cmd || input.script || '';
|
|
230
|
+
input.target || input.source || input.notebook_path || firstFile || '';
|
|
231
|
+
const command = input.command || input.cmd || input.script || commands || '';
|
|
154
232
|
intuticGate(rawToolName, targetPath, command, logEvent, _intuticWsId, input);
|
|
155
233
|
|
|
156
234
|
// Allow all other tool calls
|
|
@@ -171,7 +249,11 @@ process.stdin.on('end', () => {
|
|
|
171
249
|
const tmpScript = checkScriptPath + '.intutic-tmp';
|
|
172
250
|
await fs.writeFile(tmpScript, checkScript, 'utf-8');
|
|
173
251
|
await fs.rename(tmpScript, checkScriptPath);
|
|
252
|
+
// Executable is what enables the hook: Cline creates new hook files 0644,
|
|
253
|
+
// i.e. toggled off.
|
|
174
254
|
await fs.chmod(checkScriptPath, 0o755);
|
|
175
|
-
|
|
255
|
+
await noteWritten(checkScriptPath, workspaceRoot, checkScript);
|
|
256
|
+
log.info({ action: 'cline_hooks_written', path: checkScriptPath }, 'Cline PreToolUse governance hook written');
|
|
257
|
+
return checkScriptPath;
|
|
176
258
|
}
|
|
177
259
|
//# sourceMappingURL=clineHooks.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clineHooks.js","sourceRoot":"","sources":["../../src/harness/clineHooks.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"clineHooks.js","sourceRoot":"","sources":["../../src/harness/clineHooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* codexConfigMerger.ts — point Codex's built-in OpenAI provider at the proxy.
|
|
3
|
+
*
|
|
4
|
+
* Codex reads its user config from `$CODEX_HOME/config.toml` (`~/.codex` by
|
|
5
|
+
* default). The one key this writer owns is the top-level `openai_base_url`,
|
|
6
|
+
* Codex's documented override for the built-in `openai` provider's base URL.
|
|
7
|
+
* It is user-level only, which is why this writes the user file and not a
|
|
8
|
+
* project `.codex/config.toml`. A `[model_providers.openai]` table cannot do
|
|
9
|
+
* this job: Codex reserves the built-in provider ids (`openai`, `ollama`,
|
|
10
|
+
* `lmstudio`) and does not let `model_providers` override them.
|
|
11
|
+
*
|
|
12
|
+
* Everything else in the file belongs to the user — MCP servers, profiles,
|
|
13
|
+
* model choice, comments — and is kept byte for byte: the key is replaced or
|
|
14
|
+
* inserted as a single line rather than by re-serialising the document, which
|
|
15
|
+
* would drop every comment. The result is parsed before it is written, and an
|
|
16
|
+
* existing file that does not parse is left alone.
|
|
17
|
+
*
|
|
18
|
+
* A file whose first line is the old adapter's header was written whole by
|
|
19
|
+
* that adapter (it replaced the user's config with two provider tables), so it
|
|
20
|
+
* holds nothing of the user's and is regenerated.
|
|
21
|
+
*
|
|
22
|
+
* @module
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* Set the top-level `openai_base_url` in a Codex config.toml's text.
|
|
26
|
+
*
|
|
27
|
+
* @returns the new file content, or `null` when `raw` is not valid TOML (the
|
|
28
|
+
* caller must leave the file alone).
|
|
29
|
+
*/
|
|
30
|
+
export declare function setCodexOpenaiBaseUrl(raw: string, baseUrl: string): string | null;
|
|
31
|
+
/**
|
|
32
|
+
* Merge `openai_base_url` into the Codex user config at `configPath`.
|
|
33
|
+
*
|
|
34
|
+
* @returns `true` when the file now carries the URL, `false` when it was left
|
|
35
|
+
* untouched because it is not valid TOML (a warning is logged).
|
|
36
|
+
*/
|
|
37
|
+
export declare function mergeCodexConfig(configPath: string, baseUrl: string): Promise<boolean>;
|
|
38
|
+
//# sourceMappingURL=codexConfigMerger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codexConfigMerger.d.ts","sourceRoot":"","sources":["../../src/harness/codexConfigMerger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AA2BH;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAuBjF;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CA0B5F"}
|