@intutic/sync-daemon 1.10.0 → 1.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/agentReporter.d.ts +205 -0
- package/dist/agentReporter.d.ts.map +1 -0
- package/dist/agentReporter.js +394 -0
- package/dist/agentReporter.js.map +1 -0
- package/dist/configReader.d.ts +39 -2
- package/dist/configReader.d.ts.map +1 -1
- package/dist/configReader.js +56 -2
- package/dist/configReader.js.map +1 -1
- package/dist/configWriter.d.ts +51 -1
- package/dist/configWriter.d.ts.map +1 -1
- package/dist/configWriter.js +490 -63
- package/dist/configWriter.js.map +1 -1
- package/dist/harness/antigravityHooks.d.ts.map +1 -1
- package/dist/harness/antigravityHooks.js +33 -55
- package/dist/harness/antigravityHooks.js.map +1 -1
- package/dist/harness/claudeCodeHooks.d.ts +47 -13
- package/dist/harness/claudeCodeHooks.d.ts.map +1 -1
- package/dist/harness/claudeCodeHooks.js +316 -54
- package/dist/harness/claudeCodeHooks.js.map +1 -1
- package/dist/harness/claudeDesktopHooks.d.ts.map +1 -1
- package/dist/harness/claudeDesktopHooks.js +35 -48
- package/dist/harness/claudeDesktopHooks.js.map +1 -1
- package/dist/harness/clineHooks.d.ts.map +1 -1
- package/dist/harness/clineHooks.js +42 -34
- package/dist/harness/clineHooks.js.map +1 -1
- package/dist/harness/codexHooks.d.ts +36 -0
- package/dist/harness/codexHooks.d.ts.map +1 -0
- package/dist/harness/codexHooks.js +227 -0
- package/dist/harness/codexHooks.js.map +1 -0
- package/dist/harness/continueHooks.d.ts +45 -5
- package/dist/harness/continueHooks.d.ts.map +1 -1
- package/dist/harness/continueHooks.js +225 -5
- package/dist/harness/continueHooks.js.map +1 -1
- package/dist/harness/cursorHooks.d.ts +31 -0
- package/dist/harness/cursorHooks.d.ts.map +1 -1
- package/dist/harness/cursorHooks.js +78 -49
- package/dist/harness/cursorHooks.js.map +1 -1
- package/dist/harness/dshHooks.d.ts +198 -0
- package/dist/harness/dshHooks.d.ts.map +1 -0
- package/dist/harness/dshHooks.js +569 -0
- package/dist/harness/dshHooks.js.map +1 -0
- package/dist/harness/gateBody.d.ts +422 -0
- package/dist/harness/gateBody.d.ts.map +1 -0
- package/dist/harness/gateBody.js +1816 -0
- package/dist/harness/gateBody.js.map +1 -0
- package/dist/harness/gateKind.d.ts +164 -0
- package/dist/harness/gateKind.d.ts.map +1 -0
- package/dist/harness/gateKind.js +143 -0
- package/dist/harness/gateKind.js.map +1 -0
- package/dist/harness/githubCopilotHooks.d.ts +54 -0
- package/dist/harness/githubCopilotHooks.d.ts.map +1 -0
- package/dist/harness/githubCopilotHooks.js +218 -0
- package/dist/harness/githubCopilotHooks.js.map +1 -0
- package/dist/harness/gooseHooks.d.ts.map +1 -1
- package/dist/harness/gooseHooks.js +67 -37
- package/dist/harness/gooseHooks.js.map +1 -1
- package/dist/harness/grokHooks.d.ts +77 -0
- package/dist/harness/grokHooks.d.ts.map +1 -0
- package/dist/harness/grokHooks.js +350 -0
- package/dist/harness/grokHooks.js.map +1 -0
- package/dist/harness/hermesHooks.d.ts.map +1 -1
- package/dist/harness/hermesHooks.js +26 -55
- package/dist/harness/hermesHooks.js.map +1 -1
- package/dist/harness/holdRedaction.d.ts +69 -0
- package/dist/harness/holdRedaction.d.ts.map +1 -0
- package/dist/harness/holdRedaction.js +211 -0
- package/dist/harness/holdRedaction.js.map +1 -0
- package/dist/harness/jetbrainsXmlConfig.d.ts +51 -0
- package/dist/harness/jetbrainsXmlConfig.d.ts.map +1 -0
- package/dist/harness/jetbrainsXmlConfig.js +151 -0
- package/dist/harness/jetbrainsXmlConfig.js.map +1 -0
- package/dist/harness/mcpAutoWrite.d.ts +100 -3
- package/dist/harness/mcpAutoWrite.d.ts.map +1 -1
- package/dist/harness/mcpAutoWrite.js +712 -90
- package/dist/harness/mcpAutoWrite.js.map +1 -1
- package/dist/harness/museHooks.d.ts +53 -0
- package/dist/harness/museHooks.d.ts.map +1 -0
- package/dist/harness/museHooks.js +273 -0
- package/dist/harness/museHooks.js.map +1 -0
- package/dist/harness/n8nHooks.d.ts +25 -9
- package/dist/harness/n8nHooks.d.ts.map +1 -1
- package/dist/harness/n8nHooks.js +174 -9
- package/dist/harness/n8nHooks.js.map +1 -1
- package/dist/harness/openCodeHooks.d.ts +93 -0
- package/dist/harness/openCodeHooks.d.ts.map +1 -0
- package/dist/harness/openCodeHooks.js +248 -0
- package/dist/harness/openCodeHooks.js.map +1 -0
- package/dist/harness/openWebuiHooks.d.ts.map +1 -1
- package/dist/harness/openWebuiHooks.js +56 -11
- package/dist/harness/openWebuiHooks.js.map +1 -1
- package/dist/harness/openclawHooks.d.ts.map +1 -1
- package/dist/harness/openclawHooks.js +30 -54
- package/dist/harness/openclawHooks.js.map +1 -1
- package/dist/harness/openhandsHooks.d.ts.map +1 -1
- package/dist/harness/openhandsHooks.js +35 -41
- package/dist/harness/openhandsHooks.js.map +1 -1
- package/dist/harness/piHooks.d.ts.map +1 -1
- package/dist/harness/piHooks.js +31 -58
- package/dist/harness/piHooks.js.map +1 -1
- package/dist/harness/protectedPaths.d.ts +353 -0
- package/dist/harness/protectedPaths.d.ts.map +1 -0
- package/dist/harness/protectedPaths.js +805 -0
- package/dist/harness/protectedPaths.js.map +1 -0
- package/dist/harness/rooCodeHooks.d.ts.map +1 -1
- package/dist/harness/rooCodeHooks.js +34 -48
- package/dist/harness/rooCodeHooks.js.map +1 -1
- package/dist/harness/windsurfHooks.d.ts +72 -0
- package/dist/harness/windsurfHooks.d.ts.map +1 -1
- package/dist/harness/windsurfHooks.js +172 -58
- package/dist/harness/windsurfHooks.js.map +1 -1
- package/dist/harness/windsurfJetBrainsProxy.d.ts +78 -0
- package/dist/harness/windsurfJetBrainsProxy.d.ts.map +1 -0
- package/dist/harness/windsurfJetBrainsProxy.js +159 -0
- package/dist/harness/windsurfJetBrainsProxy.js.map +1 -0
- package/dist/hashReporter.js +1 -1
- package/dist/hashReporter.js.map +1 -1
- package/dist/index.d.ts +12 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -6
- package/dist/index.js.map +1 -1
- package/dist/integrityStore.d.ts +0 -8
- package/dist/integrityStore.d.ts.map +1 -1
- package/dist/integrityStore.js +6 -29
- package/dist/integrityStore.js.map +1 -1
- package/dist/lib/approvedBypasses.d.ts +76 -0
- package/dist/lib/approvedBypasses.d.ts.map +1 -0
- package/dist/lib/approvedBypasses.js +140 -0
- package/dist/lib/approvedBypasses.js.map +1 -0
- package/dist/lib/complianceProbes.d.ts.map +1 -1
- package/dist/lib/complianceProbes.js +5 -0
- package/dist/lib/complianceProbes.js.map +1 -1
- package/dist/lib/decisionsDigest.d.ts +99 -0
- package/dist/lib/decisionsDigest.d.ts.map +1 -0
- package/dist/lib/decisionsDigest.js +199 -0
- package/dist/lib/decisionsDigest.js.map +1 -0
- package/dist/lib/egressPolicy.d.ts +69 -0
- package/dist/lib/egressPolicy.d.ts.map +1 -0
- package/dist/lib/egressPolicy.js +126 -0
- package/dist/lib/egressPolicy.js.map +1 -0
- package/dist/lib/gitWorktrees.d.ts +140 -0
- package/dist/lib/gitWorktrees.d.ts.map +1 -0
- package/dist/lib/gitWorktrees.js +191 -0
- package/dist/lib/gitWorktrees.js.map +1 -0
- package/dist/lib/policySnapshot.d.ts +224 -0
- package/dist/lib/policySnapshot.d.ts.map +1 -0
- package/dist/lib/policySnapshot.js +637 -0
- package/dist/lib/policySnapshot.js.map +1 -0
- package/dist/lib/processPoller.d.ts +44 -0
- package/dist/lib/processPoller.d.ts.map +1 -1
- package/dist/lib/processPoller.js +176 -1
- package/dist/lib/processPoller.js.map +1 -1
- package/dist/lib/runtimeEnv.d.ts +8 -0
- package/dist/lib/runtimeEnv.d.ts.map +1 -1
- package/dist/lib/runtimeEnv.js +3 -0
- package/dist/lib/runtimeEnv.js.map +1 -1
- package/dist/localTrajectoryHeuristics.d.ts +51 -0
- package/dist/localTrajectoryHeuristics.d.ts.map +1 -0
- package/dist/localTrajectoryHeuristics.js +90 -0
- package/dist/localTrajectoryHeuristics.js.map +1 -0
- package/dist/sessionReporter.d.ts +70 -0
- package/dist/sessionReporter.d.ts.map +1 -0
- package/dist/sessionReporter.js +140 -0
- package/dist/sessionReporter.js.map +1 -0
- package/dist/skillWriter.d.ts +1 -1
- package/dist/skillWriter.d.ts.map +1 -1
- package/dist/skillWriter.js +5 -2
- package/dist/skillWriter.js.map +1 -1
- package/dist/syncLoop.d.ts +105 -3
- package/dist/syncLoop.d.ts.map +1 -1
- package/dist/syncLoop.js +397 -65
- package/dist/syncLoop.js.map +1 -1
- package/dist/trajectoryMonitor.d.ts +2 -0
- package/dist/trajectoryMonitor.d.ts.map +1 -1
- package/dist/trajectoryMonitor.js +57 -0
- package/dist/trajectoryMonitor.js.map +1 -1
- package/dist/watcher/driftWatcher.d.ts.map +1 -1
- package/dist/watcher/driftWatcher.js +12 -3
- package/dist/watcher/driftWatcher.js.map +1 -1
- package/dist/watcher/settingsGuard.d.ts +23 -0
- package/dist/watcher/settingsGuard.d.ts.map +1 -1
- package/dist/watcher/settingsGuard.js +202 -17
- package/dist/watcher/settingsGuard.js.map +1 -1
- package/package.json +37 -6
- package/dist/brainIndexer.d.ts +0 -51
- package/dist/brainIndexer.d.ts.map +0 -1
- package/dist/brainIndexer.js +0 -148
- package/dist/brainIndexer.js.map +0 -1
- package/dist/harness/cursorHooksJson.d.ts +0 -29
- package/dist/harness/cursorHooksJson.d.ts.map +0 -1
- package/dist/harness/cursorHooksJson.js +0 -130
- package/dist/harness/cursorHooksJson.js.map +0 -1
- package/dist/harness/gooseHooksWriter.d.ts +0 -27
- package/dist/harness/gooseHooksWriter.d.ts.map +0 -1
- package/dist/harness/gooseHooksWriter.js +0 -147
- package/dist/harness/gooseHooksWriter.js.map +0 -1
- package/dist/harness/openhandsHooksWriter.d.ts +0 -23
- package/dist/harness/openhandsHooksWriter.d.ts.map +0 -1
- package/dist/harness/openhandsHooksWriter.js +0 -146
- package/dist/harness/openhandsHooksWriter.js.map +0 -1
package/dist/harness/n8nHooks.js
CHANGED
|
@@ -1,11 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* n8nHooks.ts — n8n governance workflow
|
|
2
|
+
* n8nHooks.ts — n8n governance: workflow-level blocking gate + event workflow.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Two artifacts, honest about what each is:
|
|
5
|
+
*
|
|
6
|
+
* 1. **The blocking gate** (`~/.intutic/hooks/n8n-governance-hook.js`) — an
|
|
7
|
+
* n8n *external hook* module. n8n's only pre-execution mechanism is
|
|
8
|
+
* deployment-level: the operator sets
|
|
9
|
+
* `EXTERNAL_HOOK_FILES=/path/to/n8n-governance-hook.js` in the n8n
|
|
10
|
+
* process's environment, and the module's `workflow.preExecute` function
|
|
11
|
+
* then runs before every workflow execution, receiving the full Workflow
|
|
12
|
+
* object (all nodes and their parameters). **Throwing aborts the
|
|
13
|
+
* execution** — this is genuinely blocking, but per WORKFLOW, not per tool
|
|
14
|
+
* call, and installation is manual and deployment-side: this daemon cannot
|
|
15
|
+
* set another process's environment (the same reason open-webui's filter
|
|
16
|
+
* is installed by hand — see openWebuiHooks.ts). `INSTALL.md` next to the
|
|
17
|
+
* workflow JSON explains the env var.
|
|
18
|
+
*
|
|
19
|
+
* 2. **The event-forwarding workflow** (`~/.intutic/n8n/governance-workflow.json`)
|
|
20
|
+
* — an importable n8n workflow (webhook → IF → HTTP Request) that forwards
|
|
21
|
+
* tool_blocked events to the control plane. Telemetry, not enforcement.
|
|
9
22
|
*
|
|
10
23
|
* LLD #14 — Phase 3 cross-harness defence (Gap 3, WS-B)
|
|
11
24
|
* HLD §3.14 — Three-Tier Defense Cascade (Tier 1 Native Gating)
|
|
@@ -17,6 +30,7 @@ import * as path from 'node:path';
|
|
|
17
30
|
import * as os from 'node:os';
|
|
18
31
|
import { createLogger } from '@intutic/logger';
|
|
19
32
|
import { newIso } from '@intutic/id';
|
|
33
|
+
import { emitN8nWorkflowGate } from './gateBody.js';
|
|
20
34
|
const log = createLogger('sync-n8n-hooks');
|
|
21
35
|
/** Default n8n governance workflow directory. */
|
|
22
36
|
const N8N_DIR = path.join(os.homedir(), '.intutic', 'n8n');
|
|
@@ -159,12 +173,145 @@ function buildN8nWorkflow(proxyUrl, workspaceId) {
|
|
|
159
173
|
},
|
|
160
174
|
};
|
|
161
175
|
}
|
|
176
|
+
// ─── External-hook gate builder ───────────────────────────────────────────────
|
|
177
|
+
/**
|
|
178
|
+
* The blocking gate — an n8n external-hook module whose `workflow.preExecute`
|
|
179
|
+
* throws to abort an execution. See the module header for the
|
|
180
|
+
* `EXTERNAL_HOOK_FILES` contract and why installation is manual.
|
|
181
|
+
*/
|
|
182
|
+
function buildN8nGovernanceHook(proxyUrl, workspaceId) {
|
|
183
|
+
return `/**
|
|
184
|
+
* Intutic n8n governance gate (external hook module).
|
|
185
|
+
* Auto-generated by intutic sync-daemon. DO NOT EDIT.
|
|
186
|
+
*
|
|
187
|
+
* MANUAL INSTALL (deployment-side): set, in the n8n process's environment,
|
|
188
|
+
* EXTERNAL_HOOK_FILES=/path/to/this/file
|
|
189
|
+
* and restart n8n. The daemon cannot set another process's environment — see
|
|
190
|
+
* INSTALL.md next to ~/.intutic/n8n/governance-workflow.json.
|
|
191
|
+
*
|
|
192
|
+
* workflow.preExecute runs before every execution with the full Workflow
|
|
193
|
+
* object; a throw here ABORTS the execution. Per-workflow granularity: one
|
|
194
|
+
* offending node stops the whole run, and the error names the node and rule.
|
|
195
|
+
*
|
|
196
|
+
* Proxy: ${proxyUrl}
|
|
197
|
+
* Generated: ${newIso()}
|
|
198
|
+
*/
|
|
199
|
+
'use strict';
|
|
200
|
+
const path = require('path');
|
|
201
|
+
const os = require('os');
|
|
202
|
+
const fs = require('fs');
|
|
203
|
+
const crypto = require('crypto');
|
|
204
|
+
const https = require('https');
|
|
205
|
+
|
|
206
|
+
// Runtime credentials
|
|
207
|
+
const _runtimeEnvPath = path.join(os.homedir(), '.intutic', 'env', 'runtime.env');
|
|
208
|
+
let _intuticHost = 'https://api.intutic.ai', _intuticKey = '', _intuticWsId = ${JSON.stringify(workspaceId)};
|
|
209
|
+
try {
|
|
210
|
+
fs.readFileSync(_runtimeEnvPath, 'utf-8').split('\\n').forEach(line => {
|
|
211
|
+
const eq = line.indexOf('='); if (eq < 0) return;
|
|
212
|
+
const k = line.slice(0, eq).trim(), v = line.slice(eq + 1).trim();
|
|
213
|
+
if (k === 'INTUTIC_HOST' && v) _intuticHost = v;
|
|
214
|
+
if (k === 'INTUTIC_API_KEY' && v) _intuticKey = v;
|
|
215
|
+
if (k === 'INTUTIC_WORKSPACE_ID' && v) _intuticWsId = v;
|
|
216
|
+
});
|
|
217
|
+
} catch {}
|
|
218
|
+
|
|
219
|
+
${emitN8nWorkflowGate()}
|
|
220
|
+
|
|
221
|
+
// Events land under HOME, not a workspace: this runs inside the n8n server
|
|
222
|
+
// process, which has no workspace root — the same reason cline logs there.
|
|
223
|
+
const HOOK_EVENTS_LOG = path.join(os.homedir(), '.intutic', 'events', 'n8n-hook-events.jsonl');
|
|
224
|
+
|
|
225
|
+
function logEvent(verdict, toolName, reason) {
|
|
226
|
+
try {
|
|
227
|
+
const ts = new Date().toISOString();
|
|
228
|
+
const incidentId = crypto.createHash('sha1').update(ts + toolName + _intuticWsId).digest('hex').slice(0, 16);
|
|
229
|
+
const entry = JSON.stringify({
|
|
230
|
+
// Passed through, not collapsed to two values: the advisory tier emits
|
|
231
|
+
// 'tool_flagged', and a ternary here silently recorded it as an allow.
|
|
232
|
+
event: verdict,
|
|
233
|
+
toolName, reason: reason || '',
|
|
234
|
+
workspaceId: _intuticWsId,
|
|
235
|
+
harnessType: 'n8n',
|
|
236
|
+
timestamp: ts,
|
|
237
|
+
incidentId,
|
|
238
|
+
}) + '\\n';
|
|
239
|
+
const dir = path.dirname(HOOK_EVENTS_LOG);
|
|
240
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
241
|
+
fs.appendFileSync(HOOK_EVENTS_LOG, entry, { flag: 'a' });
|
|
242
|
+
if (_intuticKey) {
|
|
243
|
+
try {
|
|
244
|
+
const body = JSON.stringify({ events: [JSON.parse(entry)] });
|
|
245
|
+
const urlObj = new URL('/api/v1/hook-events', _intuticHost);
|
|
246
|
+
const mod = urlObj.protocol === 'https:' ? https : require('http');
|
|
247
|
+
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 } });
|
|
248
|
+
req.on('error', () => { /* fire-and-forget */ });
|
|
249
|
+
req.write(body); req.end();
|
|
250
|
+
} catch { /* never crash the hook */ }
|
|
251
|
+
}
|
|
252
|
+
} catch { /* never crash the hook */ }
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
module.exports = {
|
|
256
|
+
workflow: {
|
|
257
|
+
preExecute: [
|
|
258
|
+
// No try/catch around the gate: a BLOCKED throw is the refusal, and an
|
|
259
|
+
// internal fault throwing also aborts the execution — fail closed. n8n
|
|
260
|
+
// shows the error message on the aborted execution.
|
|
261
|
+
async function intuticPreExecute(workflow) {
|
|
262
|
+
intuticGateWorkflow(workflow, logEvent, _intuticWsId);
|
|
263
|
+
},
|
|
264
|
+
],
|
|
265
|
+
},
|
|
266
|
+
};
|
|
267
|
+
`;
|
|
268
|
+
}
|
|
269
|
+
/** INSTALL.md explaining the deployment-side env var the daemon cannot set. */
|
|
270
|
+
function buildInstallMd(hookPath, workflowPath) {
|
|
271
|
+
return `# Intutic governance for n8n — installation
|
|
272
|
+
|
|
273
|
+
Auto-generated by the Intutic sync-daemon. Two artifacts, two install steps.
|
|
274
|
+
|
|
275
|
+
## 1. The blocking gate (manual, deployment-side)
|
|
276
|
+
|
|
277
|
+
n8n's only pre-execution hook mechanism is the \`EXTERNAL_HOOK_FILES\`
|
|
278
|
+
environment variable, read by the **n8n server process at startup**. The
|
|
279
|
+
Intutic daemon cannot set another process's environment, so this step is
|
|
280
|
+
yours:
|
|
281
|
+
|
|
282
|
+
\`\`\`bash
|
|
283
|
+
export EXTERNAL_HOOK_FILES=${hookPath}
|
|
284
|
+
# then (re)start n8n; for Docker:
|
|
285
|
+
# docker run -e EXTERNAL_HOOK_FILES=/hooks/n8n-governance-hook.js \\
|
|
286
|
+
# -v ${hookPath}:/hooks/n8n-governance-hook.js ...
|
|
287
|
+
\`\`\`
|
|
288
|
+
|
|
289
|
+
Once loaded, the module's \`workflow.preExecute\` hook runs before **every
|
|
290
|
+
workflow execution**, evaluates each node's type and parameters against the
|
|
291
|
+
compiled floor and the policy snapshot, and **throws** on a block-severity
|
|
292
|
+
match — which aborts the execution with an error naming the node and rule.
|
|
293
|
+
|
|
294
|
+
Granularity is per workflow, not per tool call: that is all n8n's hook
|
|
295
|
+
surface offers. If the env var is not set, no gate runs — the daemon
|
|
296
|
+
regenerates this file every sync cycle, but only your deployment can load it.
|
|
297
|
+
|
|
298
|
+
## 2. The event-forwarding workflow (import via the n8n UI)
|
|
299
|
+
|
|
300
|
+
Import \`${workflowPath}\`
|
|
301
|
+
(Settings > Import Workflow), activate it, and set \`INTUTIC_API_KEY\` as an
|
|
302
|
+
n8n credential. It forwards tool_blocked events to the Intutic control plane.
|
|
303
|
+
This workflow is telemetry only; the gate above is what blocks.
|
|
304
|
+
`;
|
|
305
|
+
}
|
|
162
306
|
// ─── Main export ──────────────────────────────────────────────────────────────
|
|
163
307
|
/**
|
|
164
|
-
* Write the n8n governance workflow and installation
|
|
308
|
+
* Write the n8n governance gate, workflow, and installation notes.
|
|
165
309
|
*
|
|
166
|
-
* 1. Writes `~/.intutic/n8n
|
|
167
|
-
*
|
|
310
|
+
* 1. Writes `~/.intutic/hooks/n8n-governance-hook.js` — the blocking
|
|
311
|
+
* EXTERNAL_HOOK_FILES module (manual, deployment-side install).
|
|
312
|
+
* 2. Writes `~/.intutic/n8n/governance-workflow.json` — importable n8n 1.x workflow.
|
|
313
|
+
* 3. Writes `~/.intutic/n8n/INSTALL.md` — the EXTERNAL_HOOK_FILES instructions.
|
|
314
|
+
* 4. Writes `.intutic/env/n8n.env` with webhook URL and import instructions.
|
|
168
315
|
*
|
|
169
316
|
* Safe to call repeatedly — uses atomic rename (`.intutic-tmp` → final path).
|
|
170
317
|
*
|
|
@@ -175,10 +322,20 @@ function buildN8nWorkflow(proxyUrl, workspaceId) {
|
|
|
175
322
|
export async function writeN8nHooks(workspaceRoot, proxyUrl, workspaceId = '') {
|
|
176
323
|
// ── 1. Ensure directories ──────────────────────────────────────────────────
|
|
177
324
|
const envDir = path.join(workspaceRoot, '.intutic', 'env');
|
|
325
|
+
// homedir() read at call time (unlike the module-scope N8N_DIR) so tests
|
|
326
|
+
// that move HOME before invoking are honoured — the gooseHooks lesson.
|
|
327
|
+
const hooksDir = path.join(os.homedir(), '.intutic', 'hooks');
|
|
178
328
|
await Promise.all([
|
|
179
329
|
fs.mkdir(N8N_DIR, { recursive: true }),
|
|
180
330
|
fs.mkdir(envDir, { recursive: true }),
|
|
331
|
+
fs.mkdir(hooksDir, { recursive: true }),
|
|
181
332
|
]);
|
|
333
|
+
// ── 1b. Write the blocking external-hook gate (atomic) ────────────────────
|
|
334
|
+
const hookPath = path.join(hooksDir, 'n8n-governance-hook.js');
|
|
335
|
+
const tmpHook = hookPath + '.intutic-tmp';
|
|
336
|
+
await fs.writeFile(tmpHook, buildN8nGovernanceHook(proxyUrl, workspaceId), 'utf-8');
|
|
337
|
+
await fs.rename(tmpHook, hookPath);
|
|
338
|
+
log.info({ action: 'n8n_governance_hook_written', path: hookPath }, 'n8n workflow-level gate written (requires EXTERNAL_HOOK_FILES — see INSTALL.md)');
|
|
182
339
|
// ── 2. Write governance-workflow.json (atomic) ────────────────────────────
|
|
183
340
|
const workflowPath = path.join(N8N_DIR, 'governance-workflow.json');
|
|
184
341
|
const workflow = buildN8nWorkflow(proxyUrl, workspaceId);
|
|
@@ -186,11 +343,19 @@ export async function writeN8nHooks(workspaceRoot, proxyUrl, workspaceId = '') {
|
|
|
186
343
|
await fs.writeFile(tmpWorkflow, JSON.stringify(workflow, null, 2) + '\n', 'utf-8');
|
|
187
344
|
await fs.rename(tmpWorkflow, workflowPath);
|
|
188
345
|
log.info({ action: 'n8n_workflow_written', path: workflowPath }, 'n8n governance workflow written');
|
|
346
|
+
// ── 2b. Write INSTALL.md (atomic) ─────────────────────────────────────────
|
|
347
|
+
const installPath = path.join(N8N_DIR, 'INSTALL.md');
|
|
348
|
+
const tmpInstall = installPath + '.intutic-tmp';
|
|
349
|
+
await fs.writeFile(tmpInstall, buildInstallMd(hookPath, workflowPath), 'utf-8');
|
|
350
|
+
await fs.rename(tmpInstall, installPath);
|
|
189
351
|
// ── 3. Write .intutic/env/n8n.env (atomic) ────────────────────────────────
|
|
190
352
|
const envContent = [
|
|
191
353
|
`# Intutic n8n governance env — auto-generated ${newIso()}`,
|
|
192
354
|
`# This file is managed by the Intutic sync-daemon. DO NOT EDIT.`,
|
|
193
355
|
`INTUTIC_N8N_WEBHOOK=http://localhost:5678/webhook/intutic-governance`,
|
|
356
|
+
`# BLOCKING GATE (manual, deployment-side): set in the n8n process's env`,
|
|
357
|
+
`# EXTERNAL_HOOK_FILES=${hookPath}`,
|
|
358
|
+
`# and restart n8n. See ~/.intutic/n8n/INSTALL.md.`,
|
|
194
359
|
`# Import ~/.intutic/n8n/governance-workflow.json into your n8n instance.`,
|
|
195
360
|
`# In n8n: Settings > Import Workflow > select the file above.`,
|
|
196
361
|
`# Then activate the workflow and set INTUTIC_API_KEY as an n8n credential.`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"n8nHooks.js","sourceRoot":"","sources":["../../src/harness/n8nHooks.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"n8nHooks.js","sourceRoot":"","sources":["../../src/harness/n8nHooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* openCodeHooks.ts — OpenCode plugin gate (TD-397).
|
|
3
|
+
*
|
|
4
|
+
* OpenCode (the open-source terminal coding agent: npm `opencode-ai` 1.x,
|
|
5
|
+
* `@opencode/cli` 2.x) has no hook file, no hook JSON and no stdin/stdout
|
|
6
|
+
* gate contract. What it has is a **plugin**: a JavaScript module OpenCode
|
|
7
|
+
* loads INTO ITS OWN PROCESS from `.opencode/plugins/*.{js,ts}` (project) or
|
|
8
|
+
* `~/.config/opencode/plugins/`, or from an npm package named in
|
|
9
|
+
* `opencode.json`. The plugin returns hooks; one of them runs before every
|
|
10
|
+
* tool call and vetoes it by throwing:
|
|
11
|
+
*
|
|
12
|
+
* - 1.x: `export default async (input) => ({ "tool.execute.before": async (i, o) => { … } })`,
|
|
13
|
+
* `i = { tool, sessionID, callID }`, `o = { args }`. Fired from
|
|
14
|
+
* `packages/opencode/src/session/tools.ts` before `item.execute(args, ctx)`,
|
|
15
|
+
* for built-in tools, the `task` tool and MCP tools alike. A thrown Error
|
|
16
|
+
* becomes a `tool-error` part whose message the model reads; the tool
|
|
17
|
+
* never runs. (OpenCode's own docs demonstrate exactly this shape to keep
|
|
18
|
+
* `read` away from `.env`.)
|
|
19
|
+
* - 2.x: `ctx.tool.hook("execute.before", (event) => { … })` with
|
|
20
|
+
* `event = { tool, input, sessionID, … }`; "only tool execute.before may
|
|
21
|
+
* fail: a Tool.Error rejects the call before it runs". 2.x does NOT run
|
|
22
|
+
* 1.x-shaped plugins, and it discovers local plugins as DIRECTORIES —
|
|
23
|
+
* `.opencode/plugins/<name>/index.{js,ts}` — while 1.x globs FILES —
|
|
24
|
+
* `.opencode/{plugin,plugins}/*.{js,ts}` — so each line sees only its
|
|
25
|
+
* own layout. The same module is therefore written twice: the flat file
|
|
26
|
+
* for 1.x and `<name>/index.js` for 2.x. Its default export is the 1.x
|
|
27
|
+
* plugin function, which also carries `id`, `server` and `setup` so a
|
|
28
|
+
* 2.x loader reading `default.setup()` finds the same gate. The 1.x
|
|
29
|
+
* loader accepts a function (or an object with a `.server` function) and
|
|
30
|
+
* rejects anything else, which is why the file exports exactly one value.
|
|
31
|
+
*
|
|
32
|
+
* The typed 1.x `permission.ask` hook is NOT used: it has had no trigger site
|
|
33
|
+
* since v1.0.182 and would be a gate that never fires.
|
|
34
|
+
*
|
|
35
|
+
* ## Same gate as every other hook harness
|
|
36
|
+
*
|
|
37
|
+
* The file embeds the shared `emitJsGate` body with the `'throw'` contract:
|
|
38
|
+
* the compiled-in floor, `~/.intutic/hooks/policy-snapshot.rules`, the WHERE
|
|
39
|
+
* clauses and the audit line are exactly what `codex-check.js` or
|
|
40
|
+
* `muse-check.js` enforce. This is why the plugin is a generated file and not
|
|
41
|
+
* an `@intutic/gate` npm dependency: gate-js does not carry the floor, and a
|
|
42
|
+
* dependency the user's project has to `bun install` is one more thing to
|
|
43
|
+
* drift. Nothing to register — OpenCode globs the directory.
|
|
44
|
+
*
|
|
45
|
+
* ## In-process, so no process-level handlers
|
|
46
|
+
*
|
|
47
|
+
* The plugin runs inside OpenCode. `emitJsFailClosedPrelude` (an
|
|
48
|
+
* `uncaughtException` handler that exits 2) would exit the user's editor on
|
|
49
|
+
* any unrelated fault, so it is deliberately absent — the same reasoning
|
|
50
|
+
* `emitN8nWorkflowGate` records. Fail-closed is local instead: the hook
|
|
51
|
+
* wraps the evaluation and rethrows ANY fault as a BLOCKED error, so a gate
|
|
52
|
+
* that cannot decide refuses rather than allows.
|
|
53
|
+
*
|
|
54
|
+
* ## What is not written
|
|
55
|
+
*
|
|
56
|
+
* `opencode.json` is left alone: the model base URL is a `provider.<id>.
|
|
57
|
+
* options.baseURL` field there (or `OPENCODE_CONFIG_CONTENT`), and the static
|
|
58
|
+
* `permission` deny map cannot express argument rules — both are documented
|
|
59
|
+
* for the user, not generated. OpenCode's MCP servers are not proxy-wrapped
|
|
60
|
+
* this phase (its MCP tool ids are `<server>_<tool>`, not `mcp__<server>__
|
|
61
|
+
* <tool>`, so the allowlist backstop does not compose yet — see TD-487).
|
|
62
|
+
*
|
|
63
|
+
* @module
|
|
64
|
+
*/
|
|
65
|
+
/** Where OpenCode looks for project plugins (1.x globs files here, 2.x reads sub-directories). */
|
|
66
|
+
export declare const OPENCODE_PLUGIN_DIR: string;
|
|
67
|
+
/** The 1.x layout: one flat file. */
|
|
68
|
+
export declare const OPENCODE_PLUGIN_FILE = "intutic-governance.js";
|
|
69
|
+
/** The 2.x layout: `<name>/index.js`. Same bytes as the flat file. */
|
|
70
|
+
export declare const OPENCODE_PLUGIN_V2_FILE: string;
|
|
71
|
+
/**
|
|
72
|
+
* The plugin source. ESM, because a 1.x loader does `Object.values(module)`
|
|
73
|
+
* and a CommonJS file would surface every `module.exports` property as a
|
|
74
|
+
* named export (Bun and Node both synthesise them) — a string `id` among
|
|
75
|
+
* them is a `TypeError` at load. `createRequire` keeps the gate body's own
|
|
76
|
+
* `require()` calls (fs, os, path, crypto, http) working under ESM.
|
|
77
|
+
*/
|
|
78
|
+
export declare function buildPluginScript(proxyUrl: string, workspaceRoot: string, workspaceId: string): string;
|
|
79
|
+
/**
|
|
80
|
+
* Write the OpenCode plugin under `<workspaceRoot>/.opencode/plugins/`, in
|
|
81
|
+
* both layouts (see the module doc): `intutic-governance.js` for 1.x and
|
|
82
|
+
* `intutic-governance/index.js` for 2.x — identical bytes.
|
|
83
|
+
*
|
|
84
|
+
* Safe to call repeatedly — atomic rename (`.intutic-tmp` → final path) and
|
|
85
|
+
* fixed file names, so repeated syncs replace rather than stack. Nothing
|
|
86
|
+
* else is touched: OpenCode discovers the files by scanning the directory.
|
|
87
|
+
*
|
|
88
|
+
* @param workspaceRoot - Absolute workspace root path.
|
|
89
|
+
* @param proxyUrl - Intutic proxy URL written into the plugin header.
|
|
90
|
+
* @param workspaceId - Workspace ID embedded in every hook event payload.
|
|
91
|
+
*/
|
|
92
|
+
export declare function writeOpenCodeHooks(workspaceRoot: string, proxyUrl: string, workspaceId?: string): Promise<void>;
|
|
93
|
+
//# sourceMappingURL=openCodeHooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openCodeHooks.d.ts","sourceRoot":"","sources":["../../src/harness/openCodeHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AAUH,kGAAkG;AAClG,eAAO,MAAM,mBAAmB,QAAoC,CAAA;AACpE,qCAAqC;AACrC,eAAO,MAAM,oBAAoB,0BAA0B,CAAA;AAC3D,sEAAsE;AACtE,eAAO,MAAM,uBAAuB,QAA8C,CAAA;AAElF;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAwItG;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,kBAAkB,CACtC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAK,GACf,OAAO,CAAC,IAAI,CAAC,CAcf"}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* openCodeHooks.ts — OpenCode plugin gate (TD-397).
|
|
3
|
+
*
|
|
4
|
+
* OpenCode (the open-source terminal coding agent: npm `opencode-ai` 1.x,
|
|
5
|
+
* `@opencode/cli` 2.x) has no hook file, no hook JSON and no stdin/stdout
|
|
6
|
+
* gate contract. What it has is a **plugin**: a JavaScript module OpenCode
|
|
7
|
+
* loads INTO ITS OWN PROCESS from `.opencode/plugins/*.{js,ts}` (project) or
|
|
8
|
+
* `~/.config/opencode/plugins/`, or from an npm package named in
|
|
9
|
+
* `opencode.json`. The plugin returns hooks; one of them runs before every
|
|
10
|
+
* tool call and vetoes it by throwing:
|
|
11
|
+
*
|
|
12
|
+
* - 1.x: `export default async (input) => ({ "tool.execute.before": async (i, o) => { … } })`,
|
|
13
|
+
* `i = { tool, sessionID, callID }`, `o = { args }`. Fired from
|
|
14
|
+
* `packages/opencode/src/session/tools.ts` before `item.execute(args, ctx)`,
|
|
15
|
+
* for built-in tools, the `task` tool and MCP tools alike. A thrown Error
|
|
16
|
+
* becomes a `tool-error` part whose message the model reads; the tool
|
|
17
|
+
* never runs. (OpenCode's own docs demonstrate exactly this shape to keep
|
|
18
|
+
* `read` away from `.env`.)
|
|
19
|
+
* - 2.x: `ctx.tool.hook("execute.before", (event) => { … })` with
|
|
20
|
+
* `event = { tool, input, sessionID, … }`; "only tool execute.before may
|
|
21
|
+
* fail: a Tool.Error rejects the call before it runs". 2.x does NOT run
|
|
22
|
+
* 1.x-shaped plugins, and it discovers local plugins as DIRECTORIES —
|
|
23
|
+
* `.opencode/plugins/<name>/index.{js,ts}` — while 1.x globs FILES —
|
|
24
|
+
* `.opencode/{plugin,plugins}/*.{js,ts}` — so each line sees only its
|
|
25
|
+
* own layout. The same module is therefore written twice: the flat file
|
|
26
|
+
* for 1.x and `<name>/index.js` for 2.x. Its default export is the 1.x
|
|
27
|
+
* plugin function, which also carries `id`, `server` and `setup` so a
|
|
28
|
+
* 2.x loader reading `default.setup()` finds the same gate. The 1.x
|
|
29
|
+
* loader accepts a function (or an object with a `.server` function) and
|
|
30
|
+
* rejects anything else, which is why the file exports exactly one value.
|
|
31
|
+
*
|
|
32
|
+
* The typed 1.x `permission.ask` hook is NOT used: it has had no trigger site
|
|
33
|
+
* since v1.0.182 and would be a gate that never fires.
|
|
34
|
+
*
|
|
35
|
+
* ## Same gate as every other hook harness
|
|
36
|
+
*
|
|
37
|
+
* The file embeds the shared `emitJsGate` body with the `'throw'` contract:
|
|
38
|
+
* the compiled-in floor, `~/.intutic/hooks/policy-snapshot.rules`, the WHERE
|
|
39
|
+
* clauses and the audit line are exactly what `codex-check.js` or
|
|
40
|
+
* `muse-check.js` enforce. This is why the plugin is a generated file and not
|
|
41
|
+
* an `@intutic/gate` npm dependency: gate-js does not carry the floor, and a
|
|
42
|
+
* dependency the user's project has to `bun install` is one more thing to
|
|
43
|
+
* drift. Nothing to register — OpenCode globs the directory.
|
|
44
|
+
*
|
|
45
|
+
* ## In-process, so no process-level handlers
|
|
46
|
+
*
|
|
47
|
+
* The plugin runs inside OpenCode. `emitJsFailClosedPrelude` (an
|
|
48
|
+
* `uncaughtException` handler that exits 2) would exit the user's editor on
|
|
49
|
+
* any unrelated fault, so it is deliberately absent — the same reasoning
|
|
50
|
+
* `emitN8nWorkflowGate` records. Fail-closed is local instead: the hook
|
|
51
|
+
* wraps the evaluation and rethrows ANY fault as a BLOCKED error, so a gate
|
|
52
|
+
* that cannot decide refuses rather than allows.
|
|
53
|
+
*
|
|
54
|
+
* ## What is not written
|
|
55
|
+
*
|
|
56
|
+
* `opencode.json` is left alone: the model base URL is a `provider.<id>.
|
|
57
|
+
* options.baseURL` field there (or `OPENCODE_CONFIG_CONTENT`), and the static
|
|
58
|
+
* `permission` deny map cannot express argument rules — both are documented
|
|
59
|
+
* for the user, not generated. OpenCode's MCP servers are not proxy-wrapped
|
|
60
|
+
* this phase (its MCP tool ids are `<server>_<tool>`, not `mcp__<server>__
|
|
61
|
+
* <tool>`, so the allowlist backstop does not compose yet — see TD-487).
|
|
62
|
+
*
|
|
63
|
+
* @module
|
|
64
|
+
*/
|
|
65
|
+
import * as fs from 'node:fs/promises';
|
|
66
|
+
import * as path from 'node:path';
|
|
67
|
+
import { createLogger } from '@intutic/logger';
|
|
68
|
+
import { newIso } from '@intutic/id';
|
|
69
|
+
import { emitJsGate, REVIEW_REQUESTS_BASENAME } from './gateBody.js';
|
|
70
|
+
const log = createLogger('sync-opencode-hooks');
|
|
71
|
+
/** Where OpenCode looks for project plugins (1.x globs files here, 2.x reads sub-directories). */
|
|
72
|
+
export const OPENCODE_PLUGIN_DIR = path.join('.opencode', 'plugins');
|
|
73
|
+
/** The 1.x layout: one flat file. */
|
|
74
|
+
export const OPENCODE_PLUGIN_FILE = 'intutic-governance.js';
|
|
75
|
+
/** The 2.x layout: `<name>/index.js`. Same bytes as the flat file. */
|
|
76
|
+
export const OPENCODE_PLUGIN_V2_FILE = path.join('intutic-governance', 'index.js');
|
|
77
|
+
/**
|
|
78
|
+
* The plugin source. ESM, because a 1.x loader does `Object.values(module)`
|
|
79
|
+
* and a CommonJS file would surface every `module.exports` property as a
|
|
80
|
+
* named export (Bun and Node both synthesise them) — a string `id` among
|
|
81
|
+
* them is a `TypeError` at load. `createRequire` keeps the gate body's own
|
|
82
|
+
* `require()` calls (fs, os, path, crypto, http) working under ESM.
|
|
83
|
+
*/
|
|
84
|
+
export function buildPluginScript(proxyUrl, workspaceRoot, workspaceId) {
|
|
85
|
+
const hookEventsLog = path.join(workspaceRoot, '.intutic', 'events', 'hook-events.jsonl');
|
|
86
|
+
return `/**
|
|
87
|
+
* Intutic OpenCode governance plugin.
|
|
88
|
+
* Auto-generated by intutic sync-daemon. DO NOT EDIT.
|
|
89
|
+
* Proxy: ${proxyUrl}
|
|
90
|
+
* Generated: ${newIso()}
|
|
91
|
+
*
|
|
92
|
+
* Runs INSIDE OpenCode as a plugin (1.x: tool.execute.before; 2.x: tool
|
|
93
|
+
* execute.before). Refuses a tool call by throwing — OpenCode turns the
|
|
94
|
+
* thrown message into the tool-error the model reads, and the tool never
|
|
95
|
+
* runs. See services/sync-daemon/src/harness/openCodeHooks.ts.
|
|
96
|
+
*/
|
|
97
|
+
import { createRequire } from 'node:module';
|
|
98
|
+
const require = createRequire(import.meta.url);
|
|
99
|
+
const path = require('path');
|
|
100
|
+
const os = require('os');
|
|
101
|
+
const fs = require('fs');
|
|
102
|
+
const crypto = require('crypto');
|
|
103
|
+
const https = require('https');
|
|
104
|
+
|
|
105
|
+
// Runtime credentials
|
|
106
|
+
const _runtimeEnvPath = path.join(os.homedir(), '.intutic', 'env', 'runtime.env');
|
|
107
|
+
let _intuticHost = 'https://api.intutic.ai', _intuticKey = '', _intuticWsId = ${JSON.stringify(workspaceId)};
|
|
108
|
+
try {
|
|
109
|
+
fs.readFileSync(_runtimeEnvPath, 'utf-8').split('\\n').forEach(line => {
|
|
110
|
+
const eq = line.indexOf('='); if (eq < 0) return;
|
|
111
|
+
const k = line.slice(0, eq).trim(), v = line.slice(eq + 1).trim();
|
|
112
|
+
if (k === 'INTUTIC_HOST' && v) _intuticHost = v;
|
|
113
|
+
if (k === 'INTUTIC_API_KEY' && v) _intuticKey = v;
|
|
114
|
+
if (k === 'INTUTIC_WORKSPACE_ID' && v) _intuticWsId = v;
|
|
115
|
+
});
|
|
116
|
+
} catch {}
|
|
117
|
+
|
|
118
|
+
${emitJsGate({ harness: 'opencode', contract: 'throw', reviewRequestFile: path.join(workspaceRoot, '.intutic', 'events', REVIEW_REQUESTS_BASENAME) })}
|
|
119
|
+
|
|
120
|
+
let _intuticSessionId = '';
|
|
121
|
+
function logEvent(verdict, toolName, reason) {
|
|
122
|
+
try {
|
|
123
|
+
const ts = new Date().toISOString();
|
|
124
|
+
const incidentId = crypto.createHash('sha1').update(ts + toolName + _intuticWsId).digest('hex').slice(0, 16);
|
|
125
|
+
const entry = JSON.stringify({
|
|
126
|
+
event: verdict,
|
|
127
|
+
toolName, reason: reason || '',
|
|
128
|
+
workspaceId: _intuticWsId,
|
|
129
|
+
harnessType: 'opencode',
|
|
130
|
+
timestamp: ts,
|
|
131
|
+
incidentId,
|
|
132
|
+
...(_intuticSessionId ? { sessionId: _intuticSessionId } : {}),
|
|
133
|
+
}) + '\\n';
|
|
134
|
+
const logPath = ${JSON.stringify(hookEventsLog)};
|
|
135
|
+
try { fs.mkdirSync(path.dirname(logPath), { recursive: true }); } catch {}
|
|
136
|
+
fs.appendFileSync(logPath, entry, { flag: 'a' });
|
|
137
|
+
if (_intuticKey) {
|
|
138
|
+
try {
|
|
139
|
+
const body = JSON.stringify({ events: [JSON.parse(entry)] });
|
|
140
|
+
const urlObj = new URL('/api/v1/hook-events', _intuticHost);
|
|
141
|
+
const mod = urlObj.protocol === 'https:' ? https : require('http');
|
|
142
|
+
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 } });
|
|
143
|
+
req.on('error', () => { /* fire-and-forget */ });
|
|
144
|
+
req.write(body); req.end();
|
|
145
|
+
} catch { /* never crash the hook */ }
|
|
146
|
+
}
|
|
147
|
+
} catch { /* never crash the hook */ }
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* One tool call. Returns to allow; throws '[Intutic Governance] BLOCKED: …'
|
|
152
|
+
* to refuse. \`args\` is the tool's argument object (1.x: output.args; 2.x:
|
|
153
|
+
* event.input). Anything that is not an object is refused outright — the
|
|
154
|
+
* gate reads paths and commands out of it, and an argument shape it cannot
|
|
155
|
+
* read is the same anomaly the stdin gates' envelope guard refuses.
|
|
156
|
+
*/
|
|
157
|
+
function intuticEvaluate(toolName, args, sessionID) {
|
|
158
|
+
_intuticSessionId = sessionID || '';
|
|
159
|
+
const tool = String(toolName || 'tool');
|
|
160
|
+
if (!args || typeof args !== 'object' || Array.isArray(args)) {
|
|
161
|
+
const reason = '[Intutic Governance] BLOCKED: tool "' + tool + '" was called with arguments the gate cannot read (' +
|
|
162
|
+
(args === null ? 'null' : typeof args) + '); refusing rather than allowing a call it cannot evaluate.';
|
|
163
|
+
try { logEvent('tool_blocked', tool, reason); } catch (e) {}
|
|
164
|
+
throw new Error(reason);
|
|
165
|
+
}
|
|
166
|
+
const targetPath = args.filePath || args.path || args.file_path || args.file || args.target || args.notebook_path || '';
|
|
167
|
+
const command = args.command || args.cmd || args.script || '';
|
|
168
|
+
intuticGate(tool, targetPath, command, logEvent, _intuticWsId, args);
|
|
169
|
+
logEvent('tool_allowed', tool, '');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Fail CLOSED: a fault inside the gate is a refusal, never an allow. */
|
|
173
|
+
function intuticGuarded(toolName, args, sessionID) {
|
|
174
|
+
try {
|
|
175
|
+
intuticEvaluate(toolName, args, sessionID);
|
|
176
|
+
} catch (err) {
|
|
177
|
+
const msg = String((err && err.message) || err);
|
|
178
|
+
if (msg.indexOf('[Intutic Governance]') === 0) throw err;
|
|
179
|
+
const reason = '[Intutic Governance] BLOCKED: gate fault (failing closed): ' + msg;
|
|
180
|
+
try { logEvent('tool_blocked', String(toolName || 'unknown'), reason); } catch (e) {}
|
|
181
|
+
throw new Error(reason);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// One load-time line, so a missing or stale snapshot is visible in OpenCode's
|
|
186
|
+
// stderr rather than silently enforcing only the floor.
|
|
187
|
+
function intuticAnnounce() {
|
|
188
|
+
try {
|
|
189
|
+
const snap = intuticLoadSnapshot(_intuticWsId);
|
|
190
|
+
const line = '[Intutic Governance] OpenCode plugin loaded (snapshot: ' + snap.state + ', rules: ' + snap.rules.length + ')';
|
|
191
|
+
try { process.stderr.write(line + '\\n'); } catch (e) {}
|
|
192
|
+
try { logEvent('plugin_loaded', 'opencode', 'snapshot ' + snap.state); } catch (e) {}
|
|
193
|
+
} catch (e) { /* never fail the load */ }
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ── 1.x: Plugin = (input) => Promise<Hooks> ───────────────────────────────
|
|
197
|
+
async function intuticServer(_input) {
|
|
198
|
+
if (!globalThis.__intuticOpencode) { globalThis.__intuticOpencode = true; intuticAnnounce(); }
|
|
199
|
+
return {
|
|
200
|
+
'tool.execute.before': async (input, output) => {
|
|
201
|
+
intuticGuarded(input && input.tool, output && output.args, input && input.sessionID);
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// ── 2.x: plugin.setup(ctx) registering a tool execute.before hook ─────────
|
|
207
|
+
async function intuticSetup(ctx) {
|
|
208
|
+
if (!globalThis.__intuticOpencode) { globalThis.__intuticOpencode = true; intuticAnnounce(); }
|
|
209
|
+
await ctx.tool.hook('execute.before', (event) => {
|
|
210
|
+
intuticGuarded(event && event.tool, event && event.input, event && event.sessionID);
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const plugin = intuticServer;
|
|
215
|
+
plugin.id = 'intutic-governance';
|
|
216
|
+
plugin.server = intuticServer;
|
|
217
|
+
plugin.setup = intuticSetup;
|
|
218
|
+
export default plugin;
|
|
219
|
+
`;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Write the OpenCode plugin under `<workspaceRoot>/.opencode/plugins/`, in
|
|
223
|
+
* both layouts (see the module doc): `intutic-governance.js` for 1.x and
|
|
224
|
+
* `intutic-governance/index.js` for 2.x — identical bytes.
|
|
225
|
+
*
|
|
226
|
+
* Safe to call repeatedly — atomic rename (`.intutic-tmp` → final path) and
|
|
227
|
+
* fixed file names, so repeated syncs replace rather than stack. Nothing
|
|
228
|
+
* else is touched: OpenCode discovers the files by scanning the directory.
|
|
229
|
+
*
|
|
230
|
+
* @param workspaceRoot - Absolute workspace root path.
|
|
231
|
+
* @param proxyUrl - Intutic proxy URL written into the plugin header.
|
|
232
|
+
* @param workspaceId - Workspace ID embedded in every hook event payload.
|
|
233
|
+
*/
|
|
234
|
+
export async function writeOpenCodeHooks(workspaceRoot, proxyUrl, workspaceId = '') {
|
|
235
|
+
await fs.mkdir(path.join(workspaceRoot, '.intutic', 'events'), { recursive: true });
|
|
236
|
+
const pluginDir = path.join(workspaceRoot, OPENCODE_PLUGIN_DIR);
|
|
237
|
+
await fs.mkdir(pluginDir, { recursive: true });
|
|
238
|
+
const script = buildPluginScript(proxyUrl, workspaceRoot, workspaceId);
|
|
239
|
+
for (const rel of [OPENCODE_PLUGIN_FILE, OPENCODE_PLUGIN_V2_FILE]) {
|
|
240
|
+
const pluginPath = path.join(pluginDir, rel);
|
|
241
|
+
await fs.mkdir(path.dirname(pluginPath), { recursive: true });
|
|
242
|
+
const tmp = pluginPath + '.intutic-tmp';
|
|
243
|
+
await fs.writeFile(tmp, script, 'utf-8');
|
|
244
|
+
await fs.rename(tmp, pluginPath);
|
|
245
|
+
log.info({ action: 'opencode_plugin_written', path: pluginPath }, 'OpenCode governance plugin written');
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=openCodeHooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openCodeHooks.js","sourceRoot":"","sources":["../../src/harness/openCodeHooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openWebuiHooks.d.ts","sourceRoot":"","sources":["../../src/harness/openWebuiHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;
|
|
1
|
+
{"version":3,"file":"openWebuiHooks.d.ts","sourceRoot":"","sources":["../../src/harness/openWebuiHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAgKH;;;;;;;;;;;GAWG;AACH,wBAAsB,mBAAmB,CACvC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAK,GACf,OAAO,CAAC,IAAI,CAAC,CA2Cf"}
|