@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,105 @@
|
|
|
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
|
+
import * as fs from 'node:fs/promises';
|
|
25
|
+
import * as path from 'node:path';
|
|
26
|
+
import * as os from 'node:os';
|
|
27
|
+
import { parse as parseToml } from 'smol-toml';
|
|
28
|
+
import { createLogger } from '@intutic/logger';
|
|
29
|
+
import { keepOriginal } from '../disconnect/originals.js';
|
|
30
|
+
const log = createLogger('sync-codex-config');
|
|
31
|
+
/** First line of the file the previous adapter wrote over the user's config. */
|
|
32
|
+
const LEGACY_HEADER = '# Intutic proxy config (auto-generated)';
|
|
33
|
+
const KEY = 'openai_base_url';
|
|
34
|
+
/** Comment written above the key the first time it is inserted. */
|
|
35
|
+
const KEY_COMMENT = "# Set by Intutic: routes Codex's built-in OpenAI provider through the Intutic proxy.";
|
|
36
|
+
function parses(toml) {
|
|
37
|
+
try {
|
|
38
|
+
return parseToml(toml);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Set the top-level `openai_base_url` in a Codex config.toml's text.
|
|
46
|
+
*
|
|
47
|
+
* @returns the new file content, or `null` when `raw` is not valid TOML (the
|
|
48
|
+
* caller must leave the file alone).
|
|
49
|
+
*/
|
|
50
|
+
export function setCodexOpenaiBaseUrl(raw, baseUrl) {
|
|
51
|
+
const source = raw.startsWith(LEGACY_HEADER) ? '' : raw;
|
|
52
|
+
if (parses(source) === null)
|
|
53
|
+
return null;
|
|
54
|
+
const line = `${KEY} = ${JSON.stringify(baseUrl)}`;
|
|
55
|
+
const lines = source.split('\n');
|
|
56
|
+
// Top-level keys must come before the first table header; a key of the same
|
|
57
|
+
// name inside a table is a different key and is not touched.
|
|
58
|
+
const firstTable = lines.findIndex((l) => /^\s*\[/.test(l));
|
|
59
|
+
const topLevelEnd = firstTable === -1 ? lines.length : firstTable;
|
|
60
|
+
const existing = lines.slice(0, topLevelEnd).findIndex((l) => new RegExp(`^\\s*${KEY}\\s*=`).test(l));
|
|
61
|
+
let next;
|
|
62
|
+
if (existing !== -1) {
|
|
63
|
+
lines[existing] = line;
|
|
64
|
+
next = lines.join('\n');
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
next = `${KEY_COMMENT}\n${line}\n` + (source.trim() === '' ? '' : `\n${source}`);
|
|
68
|
+
}
|
|
69
|
+
const check = parses(next);
|
|
70
|
+
if (check === null || check[KEY] !== baseUrl)
|
|
71
|
+
return null;
|
|
72
|
+
return next;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Merge `openai_base_url` into the Codex user config at `configPath`.
|
|
76
|
+
*
|
|
77
|
+
* @returns `true` when the file now carries the URL, `false` when it was left
|
|
78
|
+
* untouched because it is not valid TOML (a warning is logged).
|
|
79
|
+
*/
|
|
80
|
+
export async function mergeCodexConfig(configPath, baseUrl) {
|
|
81
|
+
let raw = '';
|
|
82
|
+
try {
|
|
83
|
+
raw = await fs.readFile(configPath, 'utf-8');
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
if (err.code !== 'ENOENT')
|
|
87
|
+
throw err;
|
|
88
|
+
}
|
|
89
|
+
const next = setCodexOpenaiBaseUrl(raw, baseUrl);
|
|
90
|
+
if (next === null) {
|
|
91
|
+
log.warn({ action: 'codex_config_merge_skipped', path: configPath }, `${configPath} is not valid TOML — left untouched; fix it and the next sync will set ${KEY}`);
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
if (next === raw)
|
|
95
|
+
return true;
|
|
96
|
+
// A user-level file: its record lives under the home directory.
|
|
97
|
+
await keepOriginal(configPath, os.homedir());
|
|
98
|
+
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
|
99
|
+
const tmp = configPath + '.intutic-tmp';
|
|
100
|
+
await fs.writeFile(tmp, next, 'utf-8');
|
|
101
|
+
await fs.rename(tmp, configPath);
|
|
102
|
+
log.info({ action: 'codex_config_merged', path: configPath }, `Set ${KEY} in Codex config`);
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=codexConfigMerger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codexConfigMerger.js","sourceRoot":"","sources":["../../src/harness/codexConfigMerger.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
* codexHooks.ts — Codex CLI PreToolUse hook injection.
|
|
3
3
|
*
|
|
4
4
|
* Codex CLI executes PreToolUse hooks registered in `hooks.json`:
|
|
5
|
-
* - User-level: ~/.codex/hooks.json
|
|
6
|
-
* - Project-level: <workspaceRoot>/.codex/hooks.json
|
|
5
|
+
* - User-level: $CODEX_HOME/hooks.json (~/.codex/hooks.json by default)
|
|
6
|
+
* - Project-level: <workspaceRoot>/.codex/hooks.json — Codex loads this one
|
|
7
|
+
* only once the user has trusted the project's `.codex/` layer, so the
|
|
8
|
+
* user-level registration is the one that always applies.
|
|
7
9
|
*
|
|
8
10
|
* Each PreToolUse entry carries a `matcher` regex over tool names; the hook
|
|
9
11
|
* command receives JSON on stdin — {tool_name, tool_use_id, tool_input}, with
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codexHooks.d.ts","sourceRoot":"","sources":["../../src/harness/codexHooks.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"codexHooks.d.ts","sourceRoot":"","sources":["../../src/harness/codexHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAsLH;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CACnC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAK,GACf,OAAO,CAAC,IAAI,CAAC,CA2Bf"}
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
* codexHooks.ts — Codex CLI PreToolUse hook injection.
|
|
3
3
|
*
|
|
4
4
|
* Codex CLI executes PreToolUse hooks registered in `hooks.json`:
|
|
5
|
-
* - User-level: ~/.codex/hooks.json
|
|
6
|
-
* - Project-level: <workspaceRoot>/.codex/hooks.json
|
|
5
|
+
* - User-level: $CODEX_HOME/hooks.json (~/.codex/hooks.json by default)
|
|
6
|
+
* - Project-level: <workspaceRoot>/.codex/hooks.json — Codex loads this one
|
|
7
|
+
* only once the user has trusted the project's `.codex/` layer, so the
|
|
8
|
+
* user-level registration is the one that always applies.
|
|
7
9
|
*
|
|
8
10
|
* Each PreToolUse entry carries a `matcher` regex over tool names; the hook
|
|
9
11
|
* command receives JSON on stdin — {tool_name, tool_use_id, tool_input}, with
|
|
@@ -26,8 +28,10 @@ import * as fs from 'node:fs/promises';
|
|
|
26
28
|
import * as path from 'node:path';
|
|
27
29
|
import * as os from 'node:os';
|
|
28
30
|
import { createLogger } from '@intutic/logger';
|
|
31
|
+
import { keepOriginal } from '../disconnect/originals.js';
|
|
29
32
|
import { newIso } from '@intutic/id';
|
|
30
33
|
import { emitJsGate, emitJsFailClosedPrelude } from './gateBody.js';
|
|
34
|
+
import { readJsonObjectForMerge } from './jsonMergeTarget.js';
|
|
31
35
|
const log = createLogger('sync-codex-hooks');
|
|
32
36
|
/**
|
|
33
37
|
* The pre-tool gate script — receives Codex's PreToolUse JSON on stdin,
|
|
@@ -139,13 +143,9 @@ process.stdin.on('end', () => {
|
|
|
139
143
|
* uses — so repeated syncs are idempotent and never stack duplicate entries.
|
|
140
144
|
*/
|
|
141
145
|
async function mergeCodexHooksJson(configPath, hookScriptPath) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
catch {
|
|
147
|
-
// File doesn't exist or is malformed — start fresh
|
|
148
|
-
}
|
|
146
|
+
const existing = await readJsonObjectForMerge(configPath);
|
|
147
|
+
if (existing === null)
|
|
148
|
+
return;
|
|
149
149
|
const existingHooks = existing.hooks ?? {};
|
|
150
150
|
const existingPreToolUse = Array.isArray(existingHooks.PreToolUse)
|
|
151
151
|
? existingHooks.PreToolUse
|
|
@@ -215,11 +215,14 @@ export async function writeCodexHooks(workspaceRoot, proxyUrl, workspaceId = '')
|
|
|
215
215
|
await fs.chmod(hookScriptPath, 0o755);
|
|
216
216
|
// 1. Project-level: <root>/.codex/hooks.json
|
|
217
217
|
const projectCodexDir = path.join(workspaceRoot, '.codex');
|
|
218
|
+
await keepOriginal(path.join(projectCodexDir, 'hooks.json'), workspaceRoot);
|
|
218
219
|
await fs.mkdir(projectCodexDir, { recursive: true });
|
|
219
220
|
await mergeCodexHooksJson(path.join(projectCodexDir, 'hooks.json'), hookScriptPath);
|
|
220
221
|
log.info({ action: 'codex_hooks_written', level: 'project', path: projectCodexDir }, 'Codex project-level hooks written');
|
|
221
|
-
// 2. User-level:
|
|
222
|
-
|
|
222
|
+
// 2. User-level: $CODEX_HOME/hooks.json — Codex keeps its user config in
|
|
223
|
+
// CODEX_HOME when set, ~/.codex otherwise.
|
|
224
|
+
const userCodexDir = process.env.CODEX_HOME || path.join(os.homedir(), '.codex');
|
|
225
|
+
await keepOriginal(path.join(userCodexDir, 'hooks.json'), workspaceRoot);
|
|
223
226
|
await fs.mkdir(userCodexDir, { recursive: true });
|
|
224
227
|
await mergeCodexHooksJson(path.join(userCodexDir, 'hooks.json'), hookScriptPath);
|
|
225
228
|
log.info({ action: 'codex_hooks_written', level: 'user', path: userCodexDir }, 'Codex user-level hooks written');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codexHooks.js","sourceRoot":"","sources":["../../src/harness/codexHooks.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"codexHooks.js","sourceRoot":"","sources":["../../src/harness/codexHooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* continueConfigMerger.ts — point Continue's OpenAI and Anthropic models at
|
|
3
|
+
* the proxy.
|
|
4
|
+
*
|
|
5
|
+
* Continue reads its models from `~/.continue/config.yaml`. This sets
|
|
6
|
+
* `apiBase` on each model whose `provider` is `openai` or `anthropic` — the two
|
|
7
|
+
* wire protocols the proxy serves — and leaves every other model, and every
|
|
8
|
+
* other key, alone. Pointing an Ollama or Gemini model at the proxy would break
|
|
9
|
+
* it, so those are not touched.
|
|
10
|
+
*
|
|
11
|
+
* Continue joins its request path onto `apiBase` with URL resolution
|
|
12
|
+
* (`chat/completions` for OpenAI, `messages` for Anthropic), so the value is
|
|
13
|
+
* the proxy's `/v1/` with the trailing slash: without it the last path segment
|
|
14
|
+
* would be replaced instead of extended.
|
|
15
|
+
*
|
|
16
|
+
* The file is edited through the `yaml` library's document model, so comments,
|
|
17
|
+
* ordering and formatting survive. A file that does not parse, or has no
|
|
18
|
+
* `models` list, is left alone: inventing a model entry would give Continue a
|
|
19
|
+
* model with no provider or name, which it rejects.
|
|
20
|
+
*
|
|
21
|
+
* @module
|
|
22
|
+
*/
|
|
23
|
+
/** Result of merging the proxy URL into Continue's config.yaml text. */
|
|
24
|
+
export interface ContinueMergeResult {
|
|
25
|
+
/** New file content, or `null` when the file must be left untouched. */
|
|
26
|
+
content: string | null;
|
|
27
|
+
/** Number of models now pointing at the proxy. */
|
|
28
|
+
routed: number;
|
|
29
|
+
/** Why the file was left untouched, when `content` is `null`. */
|
|
30
|
+
skipped?: 'unparseable' | 'no-models';
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Set `apiBase` on every OpenAI/Anthropic model in a Continue config.yaml.
|
|
34
|
+
*
|
|
35
|
+
* @param raw - Current file content.
|
|
36
|
+
* @param apiBase - The proxy's OpenAI-style base URL (host + `/v1`).
|
|
37
|
+
*/
|
|
38
|
+
export declare function setContinueApiBase(raw: string, apiBase: string): ContinueMergeResult;
|
|
39
|
+
/**
|
|
40
|
+
* Merge the proxy URL into Continue's config.yaml at `configPath`.
|
|
41
|
+
*
|
|
42
|
+
* @returns the number of models routed through the proxy; 0 when the file was
|
|
43
|
+
* missing, unparseable or had no OpenAI/Anthropic model (logged).
|
|
44
|
+
*/
|
|
45
|
+
export declare function mergeContinueConfig(configPath: string, apiBase: string): Promise<number>;
|
|
46
|
+
//# sourceMappingURL=continueConfigMerger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"continueConfigMerger.d.ts","sourceRoot":"","sources":["../../src/harness/continueConfigMerger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAcH,wEAAwE;AACxE,MAAM,WAAW,mBAAmB;IAClC,wEAAwE;IACxE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAA;IACd,iEAAiE;IACjE,OAAO,CAAC,EAAE,aAAa,GAAG,WAAW,CAAA;CACtC;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,mBAAmB,CAkBpF;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA6B9F"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* continueConfigMerger.ts — point Continue's OpenAI and Anthropic models at
|
|
3
|
+
* the proxy.
|
|
4
|
+
*
|
|
5
|
+
* Continue reads its models from `~/.continue/config.yaml`. This sets
|
|
6
|
+
* `apiBase` on each model whose `provider` is `openai` or `anthropic` — the two
|
|
7
|
+
* wire protocols the proxy serves — and leaves every other model, and every
|
|
8
|
+
* other key, alone. Pointing an Ollama or Gemini model at the proxy would break
|
|
9
|
+
* it, so those are not touched.
|
|
10
|
+
*
|
|
11
|
+
* Continue joins its request path onto `apiBase` with URL resolution
|
|
12
|
+
* (`chat/completions` for OpenAI, `messages` for Anthropic), so the value is
|
|
13
|
+
* the proxy's `/v1/` with the trailing slash: without it the last path segment
|
|
14
|
+
* would be replaced instead of extended.
|
|
15
|
+
*
|
|
16
|
+
* The file is edited through the `yaml` library's document model, so comments,
|
|
17
|
+
* ordering and formatting survive. A file that does not parse, or has no
|
|
18
|
+
* `models` list, is left alone: inventing a model entry would give Continue a
|
|
19
|
+
* model with no provider or name, which it rejects.
|
|
20
|
+
*
|
|
21
|
+
* @module
|
|
22
|
+
*/
|
|
23
|
+
import * as fs from 'node:fs/promises';
|
|
24
|
+
import * as path from 'node:path';
|
|
25
|
+
import * as os from 'node:os';
|
|
26
|
+
import { parseDocument, isMap, isSeq } from 'yaml';
|
|
27
|
+
import { createLogger } from '@intutic/logger';
|
|
28
|
+
import { keepOriginal } from '../disconnect/originals.js';
|
|
29
|
+
const log = createLogger('sync-continue-config');
|
|
30
|
+
/** Providers whose wire protocol the proxy serves. */
|
|
31
|
+
const PROXIED_PROVIDERS = new Set(['openai', 'anthropic']);
|
|
32
|
+
/**
|
|
33
|
+
* Set `apiBase` on every OpenAI/Anthropic model in a Continue config.yaml.
|
|
34
|
+
*
|
|
35
|
+
* @param raw - Current file content.
|
|
36
|
+
* @param apiBase - The proxy's OpenAI-style base URL (host + `/v1`).
|
|
37
|
+
*/
|
|
38
|
+
export function setContinueApiBase(raw, apiBase) {
|
|
39
|
+
const value = apiBase.endsWith('/') ? apiBase : `${apiBase}/`;
|
|
40
|
+
const doc = parseDocument(raw);
|
|
41
|
+
if (doc.errors.length > 0)
|
|
42
|
+
return { content: null, routed: 0, skipped: 'unparseable' };
|
|
43
|
+
const models = isMap(doc.contents) ? doc.contents.get('models', true) : undefined;
|
|
44
|
+
if (!isSeq(models))
|
|
45
|
+
return { content: null, routed: 0, skipped: 'no-models' };
|
|
46
|
+
let routed = 0;
|
|
47
|
+
for (const model of models.items) {
|
|
48
|
+
if (!isMap(model))
|
|
49
|
+
continue;
|
|
50
|
+
const provider = model.get('provider');
|
|
51
|
+
if (typeof provider !== 'string' || !PROXIED_PROVIDERS.has(provider))
|
|
52
|
+
continue;
|
|
53
|
+
model.set('apiBase', value);
|
|
54
|
+
routed++;
|
|
55
|
+
}
|
|
56
|
+
if (routed === 0)
|
|
57
|
+
return { content: null, routed: 0, skipped: 'no-models' };
|
|
58
|
+
return { content: doc.toString(), routed };
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Merge the proxy URL into Continue's config.yaml at `configPath`.
|
|
62
|
+
*
|
|
63
|
+
* @returns the number of models routed through the proxy; 0 when the file was
|
|
64
|
+
* missing, unparseable or had no OpenAI/Anthropic model (logged).
|
|
65
|
+
*/
|
|
66
|
+
export async function mergeContinueConfig(configPath, apiBase) {
|
|
67
|
+
let raw;
|
|
68
|
+
try {
|
|
69
|
+
raw = await fs.readFile(configPath, 'utf-8');
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
if (err.code === 'ENOENT')
|
|
73
|
+
return 0;
|
|
74
|
+
throw err;
|
|
75
|
+
}
|
|
76
|
+
const result = setContinueApiBase(raw, apiBase);
|
|
77
|
+
if (result.content === null) {
|
|
78
|
+
log.warn({ action: 'continue_config_merge_skipped', path: configPath, reason: result.skipped }, result.skipped === 'unparseable'
|
|
79
|
+
? `${configPath} is not valid YAML — left untouched`
|
|
80
|
+
: `${configPath} has no openai or anthropic model to route — left untouched`);
|
|
81
|
+
return 0;
|
|
82
|
+
}
|
|
83
|
+
if (result.content !== raw) {
|
|
84
|
+
// A user-level file: its record lives under the home directory.
|
|
85
|
+
await keepOriginal(configPath, os.homedir());
|
|
86
|
+
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
|
87
|
+
const tmp = configPath + '.intutic-tmp';
|
|
88
|
+
await fs.writeFile(tmp, result.content, 'utf-8');
|
|
89
|
+
await fs.rename(tmp, configPath);
|
|
90
|
+
}
|
|
91
|
+
log.info({ action: 'continue_config_merged', path: configPath, routed: result.routed }, 'Continue models routed through the Intutic proxy');
|
|
92
|
+
return result.routed;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=continueConfigMerger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"continueConfigMerger.js","sourceRoot":"","sources":["../../src/harness/continueConfigMerger.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* continueHooks.ts — Continue
|
|
2
|
+
* continueHooks.ts — Continue CLI PreToolUse gate.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
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.
|
|
5
7
|
*
|
|
6
|
-
*
|
|
7
|
-
* `~/.continue/config.json` is set to `proxyUrl`, so LLM egress traverses
|
|
8
|
-
* the Intutic proxy.
|
|
9
|
-
*
|
|
10
|
-
* 2. **PreToolUse gate** (Continue **CLI `cn` only** — the IDE extension has
|
|
8
|
+
* **PreToolUse gate** (Continue **CLI `cn` only** — the IDE extension has
|
|
11
9
|
* no hook system): the CLI executes PreToolUse hooks registered in
|
|
12
10
|
* `~/.continue/settings.json` (user) and `<repo>/.continue/settings.json`
|
|
13
11
|
* (project), with a Claude-Code-compatible stdin contract —
|
|
@@ -40,9 +38,8 @@
|
|
|
40
38
|
* get no gate at all. Double registration is harmless: both gates evaluate
|
|
41
39
|
* the same rules, and an allow/allow or block/block pair is idempotent.
|
|
42
40
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* standard JSON with a managed-file header comment.
|
|
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).
|
|
46
43
|
*
|
|
47
44
|
* LLD #14 — Phase 3 cross-harness defence (Gap 3, WS-B)
|
|
48
45
|
* HLD §3.14 — Three-Tier Defense Cascade (Tier 1 Native Gating)
|
|
@@ -50,24 +47,17 @@
|
|
|
50
47
|
* @module
|
|
51
48
|
*/
|
|
52
49
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* 2. Sets `apiBase = proxyUrl` on each model that doesn't already use it.
|
|
58
|
-
* 3. Adds a `customInstructions` governance notice.
|
|
59
|
-
* 4. Writes the merged result back atomically.
|
|
60
|
-
* 5. Writes `.intutic/env/continue.env` with the proxy URL.
|
|
61
|
-
* 6. Writes the gate script `.intutic/hooks/continue-check.js` and registers
|
|
62
|
-
* it in `~/.continue/settings.json` and `<root>/.continue/settings.json`
|
|
63
|
-
* (Continue CLI PreToolUse — see the module header for the
|
|
64
|
-
* `.claude/settings.json` overlap).
|
|
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).
|
|
65
54
|
*
|
|
66
|
-
* Safe to call repeatedly — uses atomic rename (`.intutic-tmp` → final path)
|
|
55
|
+
* Safe to call repeatedly — uses atomic rename (`.intutic-tmp` → final path)
|
|
56
|
+
* and command-keyed de-duplication in both settings files.
|
|
67
57
|
*
|
|
68
58
|
* @param workspaceRoot - Absolute workspace root path.
|
|
69
|
-
* @param proxyUrl - Intutic proxy URL
|
|
70
|
-
* @param workspaceId - Workspace ID
|
|
59
|
+
* @param proxyUrl - Intutic proxy URL written into the gate header.
|
|
60
|
+
* @param workspaceId - Workspace ID embedded in every hook event payload.
|
|
71
61
|
*/
|
|
72
62
|
export declare function writeContinueHooks(workspaceRoot: string, proxyUrl: string, workspaceId?: string): Promise<void>;
|
|
73
63
|
//# sourceMappingURL=continueHooks.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"continueHooks.d.ts","sourceRoot":"","sources":["../../src/harness/continueHooks.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"continueHooks.d.ts","sourceRoot":"","sources":["../../src/harness/continueHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAkLH;;;;;;;;;;;;GAYG;AACH,wBAAsB,kBAAkB,CACtC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAK,GACf,OAAO,CAAC,IAAI,CAAC,CA+Bf"}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* continueHooks.ts — Continue
|
|
2
|
+
* continueHooks.ts — Continue CLI PreToolUse gate.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
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.
|
|
5
7
|
*
|
|
6
|
-
*
|
|
7
|
-
* `~/.continue/config.json` is set to `proxyUrl`, so LLM egress traverses
|
|
8
|
-
* the Intutic proxy.
|
|
9
|
-
*
|
|
10
|
-
* 2. **PreToolUse gate** (Continue **CLI `cn` only** — the IDE extension has
|
|
8
|
+
* **PreToolUse gate** (Continue **CLI `cn` only** — the IDE extension has
|
|
11
9
|
* no hook system): the CLI executes PreToolUse hooks registered in
|
|
12
10
|
* `~/.continue/settings.json` (user) and `<repo>/.continue/settings.json`
|
|
13
11
|
* (project), with a Claude-Code-compatible stdin contract —
|
|
@@ -40,9 +38,8 @@
|
|
|
40
38
|
* get no gate at all. Double registration is harmless: both gates evaluate
|
|
41
39
|
* the same rules, and an allow/allow or block/block pair is idempotent.
|
|
42
40
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* standard JSON with a managed-file header comment.
|
|
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).
|
|
46
43
|
*
|
|
47
44
|
* LLD #14 — Phase 3 cross-harness defence (Gap 3, WS-B)
|
|
48
45
|
* HLD §3.14 — Three-Tier Defense Cascade (Tier 1 Native Gating)
|
|
@@ -53,28 +50,11 @@ import * as fs from 'node:fs/promises';
|
|
|
53
50
|
import * as path from 'node:path';
|
|
54
51
|
import * as os from 'node:os';
|
|
55
52
|
import { createLogger } from '@intutic/logger';
|
|
53
|
+
import { keepOriginal } from '../disconnect/originals.js';
|
|
56
54
|
import { newIso } from '@intutic/id';
|
|
57
55
|
import { emitJsGate, emitJsFailClosedPrelude } from './gateBody.js';
|
|
56
|
+
import { readJsonObjectForMerge } from './jsonMergeTarget.js';
|
|
58
57
|
const log = createLogger('sync-continue-hooks');
|
|
59
|
-
/** Default path to the Continue.dev global config file. */
|
|
60
|
-
const CONTINUE_CONFIG_PATH = path.join(os.homedir(), '.continue', 'config.json');
|
|
61
|
-
/** Governance notice injected into customInstructions. */
|
|
62
|
-
const GOVERNANCE_NOTICE = [
|
|
63
|
-
'This workspace is governed by Intutic SOP policies.',
|
|
64
|
-
'All LLM API requests are routed through the Intutic governance proxy.',
|
|
65
|
-
'Do not remove or override the apiBase field in this configuration.',
|
|
66
|
-
].join(' ');
|
|
67
|
-
// ─── JSONC comment stripper ───────────────────────────────────────────────────
|
|
68
|
-
/**
|
|
69
|
-
* Strip single-line `// ...` comments from a JSONC string so it can be
|
|
70
|
-
* parsed by JSON.parse. Block comments (`/* ... *\/`) are not stripped
|
|
71
|
-
* because they are uncommon in Continue config files.
|
|
72
|
-
*/
|
|
73
|
-
function stripJsoncComments(raw) {
|
|
74
|
-
// Remove // comments that are not inside strings.
|
|
75
|
-
// Simple approach: replace // up to end-of-line (acceptable for Continue config).
|
|
76
|
-
return raw.replace(/\/\/[^\n]*/g, '');
|
|
77
|
-
}
|
|
78
58
|
// ─── PreToolUse gate (Continue CLI `cn`) ──────────────────────────────────────
|
|
79
59
|
/**
|
|
80
60
|
* The pre-tool gate script — receives the Continue CLI's Claude-Code-compatible
|
|
@@ -191,13 +171,9 @@ process.stdin.on('end', () => {
|
|
|
191
171
|
* repeated syncs never stack duplicate entries.
|
|
192
172
|
*/
|
|
193
173
|
async function mergeContinueSettings(settingsPath, hookScriptPath) {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
}
|
|
198
|
-
catch {
|
|
199
|
-
// File doesn't exist or is malformed — start fresh
|
|
200
|
-
}
|
|
174
|
+
const existing = await readJsonObjectForMerge(settingsPath);
|
|
175
|
+
if (existing === null)
|
|
176
|
+
return;
|
|
201
177
|
const existingHooks = existing.hooks ?? {};
|
|
202
178
|
const existingPreToolUse = Array.isArray(existingHooks.PreToolUse)
|
|
203
179
|
? existingHooks.PreToolUse
|
|
@@ -234,91 +210,19 @@ async function mergeContinueSettings(settingsPath, hookScriptPath) {
|
|
|
234
210
|
}
|
|
235
211
|
// ─── Main export ──────────────────────────────────────────────────────────────
|
|
236
212
|
/**
|
|
237
|
-
*
|
|
238
|
-
*
|
|
213
|
+
* Write the Continue CLI gate script `.intutic/hooks/continue-check.js` and
|
|
214
|
+
* register it in `~/.continue/settings.json` and
|
|
215
|
+
* `<root>/.continue/settings.json` (see the module header for the
|
|
216
|
+
* `.claude/settings.json` overlap).
|
|
239
217
|
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
* 3. Adds a `customInstructions` governance notice.
|
|
243
|
-
* 4. Writes the merged result back atomically.
|
|
244
|
-
* 5. Writes `.intutic/env/continue.env` with the proxy URL.
|
|
245
|
-
* 6. Writes the gate script `.intutic/hooks/continue-check.js` and registers
|
|
246
|
-
* it in `~/.continue/settings.json` and `<root>/.continue/settings.json`
|
|
247
|
-
* (Continue CLI PreToolUse — see the module header for the
|
|
248
|
-
* `.claude/settings.json` overlap).
|
|
249
|
-
*
|
|
250
|
-
* Safe to call repeatedly — uses atomic rename (`.intutic-tmp` → final path).
|
|
218
|
+
* Safe to call repeatedly — uses atomic rename (`.intutic-tmp` → final path)
|
|
219
|
+
* and command-keyed de-duplication in both settings files.
|
|
251
220
|
*
|
|
252
221
|
* @param workspaceRoot - Absolute workspace root path.
|
|
253
|
-
* @param proxyUrl - Intutic proxy URL
|
|
254
|
-
* @param workspaceId - Workspace ID
|
|
222
|
+
* @param proxyUrl - Intutic proxy URL written into the gate header.
|
|
223
|
+
* @param workspaceId - Workspace ID embedded in every hook event payload.
|
|
255
224
|
*/
|
|
256
225
|
export async function writeContinueHooks(workspaceRoot, proxyUrl, workspaceId = '') {
|
|
257
|
-
// ── 1. Ensure directories ──────────────────────────────────────────────────
|
|
258
|
-
const continueDir = path.join(os.homedir(), '.continue');
|
|
259
|
-
const envDir = path.join(workspaceRoot, '.intutic', 'env');
|
|
260
|
-
await Promise.all([
|
|
261
|
-
fs.mkdir(continueDir, { recursive: true }),
|
|
262
|
-
fs.mkdir(envDir, { recursive: true }),
|
|
263
|
-
]);
|
|
264
|
-
// ── 2. Read and parse ~/.continue/config.json ─────────────────────────────
|
|
265
|
-
let configObj = {};
|
|
266
|
-
try {
|
|
267
|
-
const raw = await fs.readFile(CONTINUE_CONFIG_PATH, 'utf-8');
|
|
268
|
-
const stripped = stripJsoncComments(raw);
|
|
269
|
-
configObj = JSON.parse(stripped);
|
|
270
|
-
}
|
|
271
|
-
catch {
|
|
272
|
-
// File doesn't exist or is malformed — start with empty config
|
|
273
|
-
log.warn({ action: 'continue_config_read_failed', path: CONTINUE_CONFIG_PATH }, 'Could not read ~/.continue/config.json — creating fresh');
|
|
274
|
-
}
|
|
275
|
-
// ── 3. Update model apiBase fields ────────────────────────────────────────
|
|
276
|
-
const models = Array.isArray(configObj.models) ? configObj.models : [];
|
|
277
|
-
let modifiedCount = 0;
|
|
278
|
-
const updatedModels = models.map((model) => {
|
|
279
|
-
if (typeof model !== 'object' || model === null)
|
|
280
|
-
return model;
|
|
281
|
-
const m = model;
|
|
282
|
-
if (m.apiBase === proxyUrl)
|
|
283
|
-
return m; // Already set — no-op
|
|
284
|
-
modifiedCount++;
|
|
285
|
-
return { ...m, apiBase: proxyUrl };
|
|
286
|
-
});
|
|
287
|
-
// ── 4. Build merged config ────────────────────────────────────────────────
|
|
288
|
-
const mergedConfig = {
|
|
289
|
-
...configObj,
|
|
290
|
-
// Re-attach updated models array (or keep original if empty)
|
|
291
|
-
...(models.length > 0 ? { models: updatedModels } : {}),
|
|
292
|
-
// Add/overwrite customInstructions with governance notice
|
|
293
|
-
customInstructions: GOVERNANCE_NOTICE,
|
|
294
|
-
// Metadata fields
|
|
295
|
-
_intutic_managed: true,
|
|
296
|
-
_intutic_last_sync: newIso(),
|
|
297
|
-
_intutic_workspace_id: workspaceId,
|
|
298
|
-
};
|
|
299
|
-
// ── 5. Write back atomically ──────────────────────────────────────────────
|
|
300
|
-
// Prepend a managed-file warning as a comment (will be lost on next read —
|
|
301
|
-
// that's acceptable; we re-add it on each sync).
|
|
302
|
-
const managedHeader = `// AUTO-MANAGED by Intutic sync-daemon. Last sync: ${newIso()}\n// Do not edit apiBase fields — they are governance proxy routes.\n`;
|
|
303
|
-
const serialized = managedHeader + JSON.stringify(mergedConfig, null, 2) + '\n';
|
|
304
|
-
const tmpConfig = CONTINUE_CONFIG_PATH + '.intutic-tmp';
|
|
305
|
-
await fs.writeFile(tmpConfig, serialized, 'utf-8');
|
|
306
|
-
await fs.rename(tmpConfig, CONTINUE_CONFIG_PATH);
|
|
307
|
-
log.info({ action: 'continue_config_written', path: CONTINUE_CONFIG_PATH, modifiedModels: modifiedCount }, 'Continue.dev config patched with Intutic proxy apiBase');
|
|
308
|
-
// ── 6. Write .intutic/env/continue.env ────────────────────────────────────
|
|
309
|
-
const envContent = [
|
|
310
|
-
`# Intutic Continue.dev governance env — auto-generated ${newIso()}`,
|
|
311
|
-
`# This file is managed by the Intutic sync-daemon. DO NOT EDIT.`,
|
|
312
|
-
`CONTINUE_PROXY_URL=${proxyUrl}`,
|
|
313
|
-
workspaceId ? `INTUTIC_WORKSPACE_ID=${workspaceId}` : '',
|
|
314
|
-
`# All Continue.dev model requests are routed through CONTINUE_PROXY_URL.`,
|
|
315
|
-
].filter((l) => l !== '').join('\n') + '\n';
|
|
316
|
-
const envFilePath = path.join(envDir, 'continue.env');
|
|
317
|
-
const tmpEnv = envFilePath + '.intutic-tmp';
|
|
318
|
-
await fs.writeFile(tmpEnv, envContent, 'utf-8');
|
|
319
|
-
await fs.rename(tmpEnv, envFilePath);
|
|
320
|
-
log.info({ action: 'continue_env_written', path: envFilePath }, 'Continue.dev env snippet written');
|
|
321
|
-
// ── 7. PreToolUse gate for the Continue CLI (cn) ──────────────────────────
|
|
322
226
|
const hookScriptDir = path.join(workspaceRoot, '.intutic', 'hooks');
|
|
323
227
|
await fs.mkdir(hookScriptDir, { recursive: true });
|
|
324
228
|
await fs.mkdir(path.join(workspaceRoot, '.intutic', 'events'), { recursive: true });
|
|
@@ -331,12 +235,14 @@ export async function writeContinueHooks(workspaceRoot, proxyUrl, workspaceId =
|
|
|
331
235
|
await fs.chmod(hookScriptPath, 0o755);
|
|
332
236
|
// Project-level: <root>/.continue/settings.json
|
|
333
237
|
const projectContinueDir = path.join(workspaceRoot, '.continue');
|
|
238
|
+
await keepOriginal(path.join(projectContinueDir, 'settings.json'), workspaceRoot);
|
|
334
239
|
await fs.mkdir(projectContinueDir, { recursive: true });
|
|
335
240
|
await mergeContinueSettings(path.join(projectContinueDir, 'settings.json'), hookScriptPath);
|
|
336
|
-
// User-level: ~/.continue/settings.json. homedir() read at call time
|
|
337
|
-
//
|
|
338
|
-
//
|
|
241
|
+
// User-level: ~/.continue/settings.json. homedir() read at call time so
|
|
242
|
+
// tests that move HOME before invoking are honoured — see the gooseHooks
|
|
243
|
+
// note in generatedShellIntegrity.test.ts.
|
|
339
244
|
const userContinueDir = path.join(os.homedir(), '.continue');
|
|
245
|
+
await keepOriginal(path.join(userContinueDir, 'settings.json'), workspaceRoot);
|
|
340
246
|
await fs.mkdir(userContinueDir, { recursive: true });
|
|
341
247
|
await mergeContinueSettings(path.join(userContinueDir, 'settings.json'), hookScriptPath);
|
|
342
248
|
log.info({ action: 'continue_gate_written', path: hookScriptPath }, 'Continue CLI PreToolUse gate written and registered (project + user settings.json)');
|