@intutic/sync-daemon 1.6.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/LICENSE +21 -0
- package/README.md +73 -0
- package/dist/brainIndexer.d.ts +51 -0
- package/dist/brainIndexer.d.ts.map +1 -0
- package/dist/brainIndexer.js +148 -0
- package/dist/brainIndexer.js.map +1 -0
- package/dist/configReader.d.ts +46 -0
- package/dist/configReader.d.ts.map +1 -0
- package/dist/configReader.js +133 -0
- package/dist/configReader.js.map +1 -0
- package/dist/configWriter.d.ts +53 -0
- package/dist/configWriter.d.ts.map +1 -0
- package/dist/configWriter.js +503 -0
- package/dist/configWriter.js.map +1 -0
- package/dist/harness/aiderConfigMerger.d.ts +27 -0
- package/dist/harness/aiderConfigMerger.d.ts.map +1 -0
- package/dist/harness/aiderConfigMerger.js +139 -0
- package/dist/harness/aiderConfigMerger.js.map +1 -0
- package/dist/harness/antigravityHooks.d.ts +43 -0
- package/dist/harness/antigravityHooks.d.ts.map +1 -0
- package/dist/harness/antigravityHooks.js +193 -0
- package/dist/harness/antigravityHooks.js.map +1 -0
- package/dist/harness/claudeCodeHooks.d.ts +59 -0
- package/dist/harness/claudeCodeHooks.d.ts.map +1 -0
- package/dist/harness/claudeCodeHooks.js +414 -0
- package/dist/harness/claudeCodeHooks.js.map +1 -0
- package/dist/harness/claudeDesktopHooks.d.ts +30 -0
- package/dist/harness/claudeDesktopHooks.d.ts.map +1 -0
- package/dist/harness/claudeDesktopHooks.js +267 -0
- package/dist/harness/claudeDesktopHooks.js.map +1 -0
- package/dist/harness/clineHooks.d.ts +23 -0
- package/dist/harness/clineHooks.d.ts.map +1 -0
- package/dist/harness/clineHooks.js +169 -0
- package/dist/harness/clineHooks.js.map +1 -0
- package/dist/harness/continueHooks.d.ts +33 -0
- package/dist/harness/continueHooks.d.ts.map +1 -0
- package/dist/harness/continueHooks.js +124 -0
- package/dist/harness/continueHooks.js.map +1 -0
- package/dist/harness/cursorHooks.d.ts +27 -0
- package/dist/harness/cursorHooks.d.ts.map +1 -0
- package/dist/harness/cursorHooks.js +221 -0
- package/dist/harness/cursorHooks.js.map +1 -0
- package/dist/harness/cursorHooksJson.d.ts +29 -0
- package/dist/harness/cursorHooksJson.d.ts.map +1 -0
- package/dist/harness/cursorHooksJson.js +130 -0
- package/dist/harness/cursorHooksJson.js.map +1 -0
- package/dist/harness/gooseHardener.d.ts +39 -0
- package/dist/harness/gooseHardener.d.ts.map +1 -0
- package/dist/harness/gooseHardener.js +152 -0
- package/dist/harness/gooseHardener.js.map +1 -0
- package/dist/harness/gooseHooks.d.ts +24 -0
- package/dist/harness/gooseHooks.d.ts.map +1 -0
- package/dist/harness/gooseHooks.js +175 -0
- package/dist/harness/gooseHooks.js.map +1 -0
- package/dist/harness/gooseHooksWriter.d.ts +27 -0
- package/dist/harness/gooseHooksWriter.d.ts.map +1 -0
- package/dist/harness/gooseHooksWriter.js +147 -0
- package/dist/harness/gooseHooksWriter.js.map +1 -0
- package/dist/harness/hermesHooks.d.ts +31 -0
- package/dist/harness/hermesHooks.d.ts.map +1 -0
- package/dist/harness/hermesHooks.js +245 -0
- package/dist/harness/hermesHooks.js.map +1 -0
- package/dist/harness/mcpAutoWrite.d.ts +35 -0
- package/dist/harness/mcpAutoWrite.d.ts.map +1 -0
- package/dist/harness/mcpAutoWrite.js +334 -0
- package/dist/harness/mcpAutoWrite.js.map +1 -0
- package/dist/harness/n8nHooks.d.ts +28 -0
- package/dist/harness/n8nHooks.d.ts.map +1 -0
- package/dist/harness/n8nHooks.js +205 -0
- package/dist/harness/n8nHooks.js.map +1 -0
- package/dist/harness/openWebuiHooks.d.ts +31 -0
- package/dist/harness/openWebuiHooks.d.ts.map +1 -0
- package/dist/harness/openWebuiHooks.js +166 -0
- package/dist/harness/openWebuiHooks.js.map +1 -0
- package/dist/harness/openclawHooks.d.ts +34 -0
- package/dist/harness/openclawHooks.d.ts.map +1 -0
- package/dist/harness/openclawHooks.js +296 -0
- package/dist/harness/openclawHooks.js.map +1 -0
- package/dist/harness/openhandsHooks.d.ts +24 -0
- package/dist/harness/openhandsHooks.d.ts.map +1 -0
- package/dist/harness/openhandsHooks.js +178 -0
- package/dist/harness/openhandsHooks.js.map +1 -0
- package/dist/harness/openhandsHooksWriter.d.ts +23 -0
- package/dist/harness/openhandsHooksWriter.d.ts.map +1 -0
- package/dist/harness/openhandsHooksWriter.js +146 -0
- package/dist/harness/openhandsHooksWriter.js.map +1 -0
- package/dist/harness/piHooks.d.ts +32 -0
- package/dist/harness/piHooks.d.ts.map +1 -0
- package/dist/harness/piHooks.js +257 -0
- package/dist/harness/piHooks.js.map +1 -0
- package/dist/harness/rooCodeHooks.d.ts +31 -0
- package/dist/harness/rooCodeHooks.d.ts.map +1 -0
- package/dist/harness/rooCodeHooks.js +300 -0
- package/dist/harness/rooCodeHooks.js.map +1 -0
- package/dist/harness/windsurfHooks.d.ts +27 -0
- package/dist/harness/windsurfHooks.d.ts.map +1 -0
- package/dist/harness/windsurfHooks.js +202 -0
- package/dist/harness/windsurfHooks.js.map +1 -0
- package/dist/hashReporter.d.ts +33 -0
- package/dist/hashReporter.d.ts.map +1 -0
- package/dist/hashReporter.js +69 -0
- package/dist/hashReporter.js.map +1 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/integrityStore.d.ts +43 -0
- package/dist/integrityStore.d.ts.map +1 -0
- package/dist/integrityStore.js +94 -0
- package/dist/integrityStore.js.map +1 -0
- package/dist/lib/complianceProbes.d.ts +39 -0
- package/dist/lib/complianceProbes.d.ts.map +1 -0
- package/dist/lib/complianceProbes.js +115 -0
- package/dist/lib/complianceProbes.js.map +1 -0
- package/dist/lib/processPoller.d.ts +2 -0
- package/dist/lib/processPoller.d.ts.map +1 -0
- package/dist/lib/processPoller.js +87 -0
- package/dist/lib/processPoller.js.map +1 -0
- package/dist/lib/runtimeEnv.d.ts +49 -0
- package/dist/lib/runtimeEnv.d.ts.map +1 -0
- package/dist/lib/runtimeEnv.js +70 -0
- package/dist/lib/runtimeEnv.js.map +1 -0
- package/dist/skillWriter.d.ts +24 -0
- package/dist/skillWriter.d.ts.map +1 -0
- package/dist/skillWriter.js +112 -0
- package/dist/skillWriter.js.map +1 -0
- package/dist/syncLoop.d.ts +70 -0
- package/dist/syncLoop.d.ts.map +1 -0
- package/dist/syncLoop.js +546 -0
- package/dist/syncLoop.js.map +1 -0
- package/dist/trajectoryMonitor.d.ts +126 -0
- package/dist/trajectoryMonitor.d.ts.map +1 -0
- package/dist/trajectoryMonitor.js +291 -0
- package/dist/trajectoryMonitor.js.map +1 -0
- package/dist/watcher/driftWatcher.d.ts +25 -0
- package/dist/watcher/driftWatcher.d.ts.map +1 -0
- package/dist/watcher/driftWatcher.js +72 -0
- package/dist/watcher/driftWatcher.js.map +1 -0
- package/dist/watcher/settingsGuard.d.ts +32 -0
- package/dist/watcher/settingsGuard.d.ts.map +1 -0
- package/dist/watcher/settingsGuard.js +250 -0
- package/dist/watcher/settingsGuard.js.map +1 -0
- package/dist/wsClient.d.ts +49 -0
- package/dist/wsClient.d.ts.map +1 -0
- package/dist/wsClient.js +149 -0
- package/dist/wsClient.js.map +1 -0
- package/package.json +96 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"n8nHooks.js","sourceRoot":"","sources":["../../src/harness/n8nHooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* openWebuiHooks.ts — Open WebUI governance filter generator.
|
|
3
|
+
*
|
|
4
|
+
* Open WebUI uses Python `inlet()` filter functions loaded from the admin UI.
|
|
5
|
+
* This module generates an `intutic-governance-filter.py` file and an env
|
|
6
|
+
* snippet with installation instructions.
|
|
7
|
+
*
|
|
8
|
+
* The generated Python filter:
|
|
9
|
+
* - Reads ~/.intutic/env/runtime.env at call time for fresh credentials.
|
|
10
|
+
* - Logs every prompt (inlet) via dual-path: file append + urllib.request.
|
|
11
|
+
* - Generates incidentId via hashlib.sha1.
|
|
12
|
+
*
|
|
13
|
+
* LLD #14 — Phase 3 cross-harness defence (Gap 3, WS-B)
|
|
14
|
+
* HLD §3.14 — Three-Tier Defense Cascade (Tier 1 Native Gating)
|
|
15
|
+
*
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Write the Open WebUI governance filter and installation instructions.
|
|
20
|
+
*
|
|
21
|
+
* 1. Creates `~/.open-webui/intutic-governance-filter.py` — Python inlet filter.
|
|
22
|
+
* 2. Writes `.intutic/env/open-webui.env` with installation instructions.
|
|
23
|
+
*
|
|
24
|
+
* Safe to call repeatedly — uses atomic rename (`.intutic-tmp` → final path).
|
|
25
|
+
*
|
|
26
|
+
* @param workspaceRoot - Absolute workspace root path.
|
|
27
|
+
* @param proxyUrl - Intutic proxy URL embedded in filter header.
|
|
28
|
+
* @param workspaceId - Workspace ID embedded in every filter event payload.
|
|
29
|
+
*/
|
|
30
|
+
export declare function writeOpenWebuiHooks(workspaceRoot: string, proxyUrl: string, workspaceId?: string): Promise<void>;
|
|
31
|
+
//# sourceMappingURL=openWebuiHooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openWebuiHooks.d.ts","sourceRoot":"","sources":["../../src/harness/openWebuiHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAmHH;;;;;;;;;;;GAWG;AACH,wBAAsB,mBAAmB,CACvC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAK,GACf,OAAO,CAAC,IAAI,CAAC,CA2Cf"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* openWebuiHooks.ts — Open WebUI governance filter generator.
|
|
3
|
+
*
|
|
4
|
+
* Open WebUI uses Python `inlet()` filter functions loaded from the admin UI.
|
|
5
|
+
* This module generates an `intutic-governance-filter.py` file and an env
|
|
6
|
+
* snippet with installation instructions.
|
|
7
|
+
*
|
|
8
|
+
* The generated Python filter:
|
|
9
|
+
* - Reads ~/.intutic/env/runtime.env at call time for fresh credentials.
|
|
10
|
+
* - Logs every prompt (inlet) via dual-path: file append + urllib.request.
|
|
11
|
+
* - Generates incidentId via hashlib.sha1.
|
|
12
|
+
*
|
|
13
|
+
* LLD #14 — Phase 3 cross-harness defence (Gap 3, WS-B)
|
|
14
|
+
* HLD §3.14 — Three-Tier Defense Cascade (Tier 1 Native Gating)
|
|
15
|
+
*
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
import * as fs from 'node:fs/promises';
|
|
19
|
+
import * as path from 'node:path';
|
|
20
|
+
import * as os from 'node:os';
|
|
21
|
+
import { createLogger } from '@intutic/logger';
|
|
22
|
+
import { newIso } from '@intutic/id';
|
|
23
|
+
const log = createLogger('sync-open-webui-hooks');
|
|
24
|
+
/** Default target directory for the Open WebUI governance filter. */
|
|
25
|
+
const OPEN_WEBUI_DIR = path.join(os.homedir(), '.open-webui');
|
|
26
|
+
// ─── Python filter template ───────────────────────────────────────────────────
|
|
27
|
+
function buildOpenWebuiFilter(workspaceId, proxyUrl) {
|
|
28
|
+
return `"""
|
|
29
|
+
Intutic Governance Filter for Open WebUI.
|
|
30
|
+
Auto-generated by intutic sync-daemon. DO NOT EDIT.
|
|
31
|
+
Generated: ${newIso()}
|
|
32
|
+
Proxy: ${proxyUrl}
|
|
33
|
+
Workspace: ${workspaceId}
|
|
34
|
+
"""
|
|
35
|
+
import os
|
|
36
|
+
import json
|
|
37
|
+
import urllib.request
|
|
38
|
+
import hashlib
|
|
39
|
+
import time
|
|
40
|
+
from typing import Optional
|
|
41
|
+
from datetime import datetime, timezone
|
|
42
|
+
|
|
43
|
+
WORKSPACE_ID = ${JSON.stringify(workspaceId)}
|
|
44
|
+
HARNESS_TYPE = "open-webui"
|
|
45
|
+
RUNTIME_ENV_PATH = os.path.expanduser("~/.intutic/env/runtime.env")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _load_runtime_env():
|
|
49
|
+
env = {
|
|
50
|
+
"INTUTIC_HOST": "https://api.intutic.ai",
|
|
51
|
+
"INTUTIC_API_KEY": "",
|
|
52
|
+
"INTUTIC_WORKSPACE_ID": WORKSPACE_ID,
|
|
53
|
+
}
|
|
54
|
+
try:
|
|
55
|
+
with open(RUNTIME_ENV_PATH) as f:
|
|
56
|
+
for line in f:
|
|
57
|
+
line = line.strip()
|
|
58
|
+
if "=" in line and not line.startswith("#"):
|
|
59
|
+
k, v = line.split("=", 1)
|
|
60
|
+
env[k.strip()] = v.strip()
|
|
61
|
+
except Exception:
|
|
62
|
+
pass
|
|
63
|
+
return env
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _log_event(env: dict, event: dict):
|
|
67
|
+
"""Dual-path log: Path B file append + Path A urllib.request (fire-and-forget)."""
|
|
68
|
+
# Path B — reliable file append
|
|
69
|
+
try:
|
|
70
|
+
log_path = os.path.expanduser("~/.intutic/events/open-webui-hook-events.jsonl")
|
|
71
|
+
os.makedirs(os.path.dirname(log_path), exist_ok=True)
|
|
72
|
+
with open(log_path, "a") as f:
|
|
73
|
+
f.write(json.dumps(event) + "\\n")
|
|
74
|
+
except Exception:
|
|
75
|
+
pass
|
|
76
|
+
|
|
77
|
+
# Path A — fire-and-forget HTTP POST
|
|
78
|
+
if env.get("INTUTIC_API_KEY"):
|
|
79
|
+
try:
|
|
80
|
+
body = json.dumps({"events": [event]}).encode()
|
|
81
|
+
req = urllib.request.Request(
|
|
82
|
+
f"{env['INTUTIC_HOST']}/api/v1/hook-events",
|
|
83
|
+
data=body,
|
|
84
|
+
headers={
|
|
85
|
+
"Content-Type": "application/json",
|
|
86
|
+
"Authorization": f"Bearer {env['INTUTIC_API_KEY']}",
|
|
87
|
+
},
|
|
88
|
+
method="POST",
|
|
89
|
+
)
|
|
90
|
+
urllib.request.urlopen(req, timeout=3)
|
|
91
|
+
except Exception:
|
|
92
|
+
pass
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class Filter:
|
|
96
|
+
"""Intutic governance inlet filter for Open WebUI."""
|
|
97
|
+
|
|
98
|
+
def inlet(self, body: dict, __user__: Optional[dict] = None) -> dict:
|
|
99
|
+
"""Called before every prompt — log to Intutic governance."""
|
|
100
|
+
env = _load_runtime_env()
|
|
101
|
+
ts = datetime.now(timezone.utc).isoformat()
|
|
102
|
+
model = body.get("model", "unknown")
|
|
103
|
+
workspace_id = env["INTUTIC_WORKSPACE_ID"]
|
|
104
|
+
incident_id = hashlib.sha1(
|
|
105
|
+
f"{ts}{model}{workspace_id}".encode()
|
|
106
|
+
).hexdigest()[:16]
|
|
107
|
+
event = {
|
|
108
|
+
"event": "tool_allowed",
|
|
109
|
+
"toolName": f"open-webui:{model}",
|
|
110
|
+
"reason": "",
|
|
111
|
+
"workspaceId": workspace_id,
|
|
112
|
+
"harnessType": HARNESS_TYPE,
|
|
113
|
+
"timestamp": ts,
|
|
114
|
+
"incidentId": incident_id,
|
|
115
|
+
}
|
|
116
|
+
_log_event(env, event)
|
|
117
|
+
return body
|
|
118
|
+
|
|
119
|
+
def outlet(self, body: dict, __user__: Optional[dict] = None) -> dict:
|
|
120
|
+
"""Pass-through outlet — no modification required."""
|
|
121
|
+
return body
|
|
122
|
+
`;
|
|
123
|
+
}
|
|
124
|
+
// ─── Main export ──────────────────────────────────────────────────────────────
|
|
125
|
+
/**
|
|
126
|
+
* Write the Open WebUI governance filter and installation instructions.
|
|
127
|
+
*
|
|
128
|
+
* 1. Creates `~/.open-webui/intutic-governance-filter.py` — Python inlet filter.
|
|
129
|
+
* 2. Writes `.intutic/env/open-webui.env` with installation instructions.
|
|
130
|
+
*
|
|
131
|
+
* Safe to call repeatedly — uses atomic rename (`.intutic-tmp` → final path).
|
|
132
|
+
*
|
|
133
|
+
* @param workspaceRoot - Absolute workspace root path.
|
|
134
|
+
* @param proxyUrl - Intutic proxy URL embedded in filter header.
|
|
135
|
+
* @param workspaceId - Workspace ID embedded in every filter event payload.
|
|
136
|
+
*/
|
|
137
|
+
export async function writeOpenWebuiHooks(workspaceRoot, proxyUrl, workspaceId = '') {
|
|
138
|
+
// ── 1. Ensure directories ──────────────────────────────────────────────────
|
|
139
|
+
const envDir = path.join(workspaceRoot, '.intutic', 'env');
|
|
140
|
+
await Promise.all([
|
|
141
|
+
fs.mkdir(OPEN_WEBUI_DIR, { recursive: true }),
|
|
142
|
+
fs.mkdir(envDir, { recursive: true }),
|
|
143
|
+
]);
|
|
144
|
+
// ── 2. Write Python filter (atomic) ───────────────────────────────────────
|
|
145
|
+
const filterPath = path.join(OPEN_WEBUI_DIR, 'intutic-governance-filter.py');
|
|
146
|
+
const tmpFilter = filterPath + '.intutic-tmp';
|
|
147
|
+
await fs.writeFile(tmpFilter, buildOpenWebuiFilter(workspaceId, proxyUrl), 'utf-8');
|
|
148
|
+
await fs.rename(tmpFilter, filterPath);
|
|
149
|
+
log.info({ action: 'open_webui_filter_written', path: filterPath }, 'Open WebUI governance filter written');
|
|
150
|
+
// ── 3. Write .intutic/env/open-webui.env (atomic) ─────────────────────────
|
|
151
|
+
const envContent = [
|
|
152
|
+
`# Intutic Open WebUI governance env — auto-generated ${newIso()}`,
|
|
153
|
+
`# This file is managed by the Intutic sync-daemon. DO NOT EDIT.`,
|
|
154
|
+
`INTUTIC_OPEN_WEBUI_FILTER=${filterPath}`,
|
|
155
|
+
`# To install: copy the filter file to your Open WebUI Functions directory via the admin UI.`,
|
|
156
|
+
`# Admin > Settings > Functions > + New Function > paste content of:`,
|
|
157
|
+
`# ${filterPath}`,
|
|
158
|
+
workspaceId ? `INTUTIC_WORKSPACE_ID=${workspaceId}` : '',
|
|
159
|
+
].filter((l) => l !== '').join('\n') + '\n';
|
|
160
|
+
const envFilePath = path.join(envDir, 'open-webui.env');
|
|
161
|
+
const tmpEnv = envFilePath + '.intutic-tmp';
|
|
162
|
+
await fs.writeFile(tmpEnv, envContent, 'utf-8');
|
|
163
|
+
await fs.rename(tmpEnv, envFilePath);
|
|
164
|
+
log.info({ action: 'open_webui_env_written', path: envFilePath }, 'Open WebUI env snippet written');
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=openWebuiHooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openWebuiHooks.js","sourceRoot":"","sources":["../../src/harness/openWebuiHooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* openclawHooks.ts — Openclaw governance hook injection.
|
|
3
|
+
*
|
|
4
|
+
* Openclaw uses `~/.openclaw/openclaw.json` (JSON5 format — may have comments
|
|
5
|
+
* and trailing commas). The hook entry is registered under
|
|
6
|
+
* `hooks.internal.entries["intutic-governance"]`. This module:
|
|
7
|
+
*
|
|
8
|
+
* 1. JSON5-safe-merges `~/.openclaw/openclaw.json` with the hook entry.
|
|
9
|
+
* 2. Writes `~/.intutic/hooks/openclaw-check.js` — dual-path Node.js hook.
|
|
10
|
+
* 3. Writes `.intutic/env/openclaw.env` — env snippet.
|
|
11
|
+
* 4. Optionally validates with `openclaw hooks check` (non-fatal).
|
|
12
|
+
*
|
|
13
|
+
* Hook protocol: stdin JSON `{tool_name, tool_input, transcript_path}`,
|
|
14
|
+
* exit 0 = allow, exit 2 = block (stderr shown to agent).
|
|
15
|
+
*
|
|
16
|
+
* WS-C2 — Openclaw harness (Phase 3 cross-harness defence)
|
|
17
|
+
* LLD #14 — Phase 3 cross-harness defence (Gap 3)
|
|
18
|
+
* HLD §3.14 — Three-Tier Defense Cascade (Tier 1 Native Gating)
|
|
19
|
+
*
|
|
20
|
+
* @module
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Write the Openclaw governance hook script and merge hook configuration
|
|
24
|
+
* into `~/.openclaw/openclaw.json`.
|
|
25
|
+
*
|
|
26
|
+
* Safe to call repeatedly — uses atomic rename so partial writes never
|
|
27
|
+
* leave a corrupt state.
|
|
28
|
+
*
|
|
29
|
+
* @param workspaceRoot - Absolute workspace root path.
|
|
30
|
+
* @param proxyUrl - Intutic proxy URL written into hook headers.
|
|
31
|
+
* @param workspaceId - Workspace ID embedded in every hook event payload.
|
|
32
|
+
*/
|
|
33
|
+
export declare function writeOpenclawHooks(workspaceRoot: string, proxyUrl: string, workspaceId?: string): Promise<void>;
|
|
34
|
+
//# sourceMappingURL=openclawHooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openclawHooks.d.ts","sourceRoot":"","sources":["../../src/harness/openclawHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAiOH;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,CACtC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAK,GACf,OAAO,CAAC,IAAI,CAAC,CAiFf"}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* openclawHooks.ts — Openclaw governance hook injection.
|
|
3
|
+
*
|
|
4
|
+
* Openclaw uses `~/.openclaw/openclaw.json` (JSON5 format — may have comments
|
|
5
|
+
* and trailing commas). The hook entry is registered under
|
|
6
|
+
* `hooks.internal.entries["intutic-governance"]`. This module:
|
|
7
|
+
*
|
|
8
|
+
* 1. JSON5-safe-merges `~/.openclaw/openclaw.json` with the hook entry.
|
|
9
|
+
* 2. Writes `~/.intutic/hooks/openclaw-check.js` — dual-path Node.js hook.
|
|
10
|
+
* 3. Writes `.intutic/env/openclaw.env` — env snippet.
|
|
11
|
+
* 4. Optionally validates with `openclaw hooks check` (non-fatal).
|
|
12
|
+
*
|
|
13
|
+
* Hook protocol: stdin JSON `{tool_name, tool_input, transcript_path}`,
|
|
14
|
+
* exit 0 = allow, exit 2 = block (stderr shown to agent).
|
|
15
|
+
*
|
|
16
|
+
* WS-C2 — Openclaw harness (Phase 3 cross-harness defence)
|
|
17
|
+
* LLD #14 — Phase 3 cross-harness defence (Gap 3)
|
|
18
|
+
* HLD §3.14 — Three-Tier Defense Cascade (Tier 1 Native Gating)
|
|
19
|
+
*
|
|
20
|
+
* @module
|
|
21
|
+
*/
|
|
22
|
+
import * as fs from 'node:fs/promises';
|
|
23
|
+
import * as path from 'node:path';
|
|
24
|
+
import * as os from 'node:os';
|
|
25
|
+
import { exec } from 'node:child_process';
|
|
26
|
+
import { promisify } from 'node:util';
|
|
27
|
+
import { createLogger } from '@intutic/logger';
|
|
28
|
+
import { newIso } from '@intutic/id';
|
|
29
|
+
const log = createLogger('sync-openclaw-hooks');
|
|
30
|
+
const execAsync = promisify(exec);
|
|
31
|
+
/** Path to the Openclaw global config file. */
|
|
32
|
+
const OPENCLAW_CONFIG = path.join(os.homedir(), '.openclaw', 'openclaw.json');
|
|
33
|
+
/** Governance-sensitive paths that the hook gate protects. */
|
|
34
|
+
const PROTECTED_PATHS = [
|
|
35
|
+
'.gemini/settings.json',
|
|
36
|
+
'.intutic/hooks',
|
|
37
|
+
'.intutic/integrity.json',
|
|
38
|
+
'.intutic/events',
|
|
39
|
+
'.claude/settings.json',
|
|
40
|
+
'.cursor/hooks.json',
|
|
41
|
+
'.codeium/windsurf/hooks.json',
|
|
42
|
+
'.cline/hooks',
|
|
43
|
+
'.agents/plugins/intutic-governance',
|
|
44
|
+
'.openhands/hooks.json',
|
|
45
|
+
'.openclaw/openclaw.json',
|
|
46
|
+
];
|
|
47
|
+
// ─── Node.js hook script template ────────────────────────────────────────────
|
|
48
|
+
function buildOpenclawCheckScript(hookEventsLog, workspaceId) {
|
|
49
|
+
return `#!/usr/bin/env node
|
|
50
|
+
/**
|
|
51
|
+
* Intutic Openclaw PreToolUse execution gate.
|
|
52
|
+
* Auto-generated by intutic sync-daemon. DO NOT EDIT.
|
|
53
|
+
* Generated: ${newIso()}
|
|
54
|
+
* Workspace: ${workspaceId}
|
|
55
|
+
*/
|
|
56
|
+
'use strict';
|
|
57
|
+
const path = require('path');
|
|
58
|
+
const os = require('os');
|
|
59
|
+
const fs = require('fs');
|
|
60
|
+
const crypto = require('crypto');
|
|
61
|
+
const https = require('https');
|
|
62
|
+
|
|
63
|
+
// ── Runtime credentials (sourced from ~/.intutic/env/runtime.env at invocation time) ──
|
|
64
|
+
const _runtimeEnvPath = path.join(os.homedir(), '.intutic', 'env', 'runtime.env');
|
|
65
|
+
let _intuticHost = 'https://api.intutic.ai', _intuticKey = '', _intuticWsId = ${JSON.stringify(workspaceId)};
|
|
66
|
+
try {
|
|
67
|
+
fs.readFileSync(_runtimeEnvPath, 'utf-8').split('\\n').forEach(line => {
|
|
68
|
+
const eq = line.indexOf('='); if (eq < 0) return;
|
|
69
|
+
const k = line.slice(0, eq).trim(), v = line.slice(eq + 1).trim();
|
|
70
|
+
if (k === 'INTUTIC_HOST' && v) _intuticHost = v;
|
|
71
|
+
if (k === 'INTUTIC_API_KEY' && v) _intuticKey = v;
|
|
72
|
+
if (k === 'INTUTIC_WORKSPACE_ID' && v) _intuticWsId = v;
|
|
73
|
+
});
|
|
74
|
+
} catch { /* runtime.env not yet written — use defaults */ }
|
|
75
|
+
|
|
76
|
+
// Governance-sensitive paths that no agent may modify.
|
|
77
|
+
const PROTECTED_PATHS = ${JSON.stringify(PROTECTED_PATHS, null, 2)};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Appends a governance event to the local hook-events log (Path B)
|
|
81
|
+
* and fires a non-blocking HTTP POST to the control plane (Path A).
|
|
82
|
+
*/
|
|
83
|
+
function logEvent(verdict, toolName, reason) {
|
|
84
|
+
try {
|
|
85
|
+
const ts = new Date().toISOString();
|
|
86
|
+
// incidentId = sha1(timestamp + toolName + workspaceId).slice(0,16)
|
|
87
|
+
const incidentId = crypto.createHash('sha1').update(ts + toolName + _intuticWsId).digest('hex').slice(0, 16);
|
|
88
|
+
const entry = JSON.stringify({
|
|
89
|
+
event: verdict === 'blocked' ? 'tool_blocked' : 'tool_allowed',
|
|
90
|
+
toolName,
|
|
91
|
+
reason: reason || '',
|
|
92
|
+
workspaceId: _intuticWsId,
|
|
93
|
+
harnessType: 'openclaw',
|
|
94
|
+
timestamp: ts,
|
|
95
|
+
incidentId,
|
|
96
|
+
}) + '\\n';
|
|
97
|
+
// Path B: reliable file append (sync-daemon drains on FSEvents change)
|
|
98
|
+
fs.appendFileSync(${JSON.stringify(hookEventsLog)}, entry, { flag: 'a' });
|
|
99
|
+
// Path A: fire-and-forget HTTP POST (near-real-time dashboard, non-blocking)
|
|
100
|
+
if (_intuticKey) {
|
|
101
|
+
try {
|
|
102
|
+
const body = JSON.stringify({ events: [JSON.parse(entry)] });
|
|
103
|
+
const urlObj = new URL('/api/v1/hook-events', _intuticHost);
|
|
104
|
+
const isHttps = urlObj.protocol === 'https:';
|
|
105
|
+
const mod = isHttps ? https : require('http');
|
|
106
|
+
const req = mod.request({
|
|
107
|
+
hostname: urlObj.hostname,
|
|
108
|
+
port: urlObj.port || (isHttps ? 443 : 80),
|
|
109
|
+
path: urlObj.pathname,
|
|
110
|
+
method: 'POST',
|
|
111
|
+
headers: {
|
|
112
|
+
'Content-Type': 'application/json',
|
|
113
|
+
'Content-Length': Buffer.byteLength(body),
|
|
114
|
+
'Authorization': 'Bearer ' + _intuticKey,
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
req.on('error', () => { /* fire-and-forget — ignore errors */ });
|
|
118
|
+
req.write(body);
|
|
119
|
+
req.end();
|
|
120
|
+
} catch { /* never crash the hook */ }
|
|
121
|
+
}
|
|
122
|
+
} catch { /* never crash the hook */ }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Read stdin containing Openclaw's tool context
|
|
126
|
+
let inputData = '';
|
|
127
|
+
process.stdin.on('data', (chunk) => { inputData += chunk; });
|
|
128
|
+
process.stdin.on('end', () => {
|
|
129
|
+
try {
|
|
130
|
+
const ctx = JSON.parse(inputData);
|
|
131
|
+
const toolName = (ctx.tool_name || ctx.toolName || '').toLowerCase();
|
|
132
|
+
const toolInput = ctx.tool_input || ctx.toolInput || {};
|
|
133
|
+
|
|
134
|
+
// 1. Protected-path guard
|
|
135
|
+
const targetPath = toolInput.path || toolInput.file_path || toolInput.new_path || toolInput.target || '';
|
|
136
|
+
for (const p of PROTECTED_PATHS) {
|
|
137
|
+
if (String(targetPath).includes(p) || String(targetPath).startsWith(p)) {
|
|
138
|
+
const reason = \`Attempt to modify governance-protected path: "\${targetPath}". \` +
|
|
139
|
+
'To change governance policy, update your SOP via the Intutic control plane.';
|
|
140
|
+
console.error(\`[Intutic Governance] BLOCKED: \${reason}\`);
|
|
141
|
+
logEvent('blocked', toolName, reason);
|
|
142
|
+
process.exit(2);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// 2. Bash/shell command guard — block governance bypass patterns
|
|
147
|
+
const command = toolInput.command || toolInput.cmd || '';
|
|
148
|
+
const BYPASS_PATTERNS = [
|
|
149
|
+
'chflags nouchg',
|
|
150
|
+
'chattr -i',
|
|
151
|
+
'rm -rf .intutic',
|
|
152
|
+
'rm -rf .openclaw',
|
|
153
|
+
'OPENCLAW_PRE_TOOL_HOOK=',
|
|
154
|
+
'rm -rf .gemini/settings',
|
|
155
|
+
];
|
|
156
|
+
for (const pattern of BYPASS_PATTERNS) {
|
|
157
|
+
if (String(command).includes(pattern)) {
|
|
158
|
+
const reason = \`Shell command matches governance bypass pattern: "\${pattern}"\`;
|
|
159
|
+
console.error(\`[Intutic Governance] BLOCKED: \${reason}\`);
|
|
160
|
+
logEvent('blocked', toolName, reason);
|
|
161
|
+
process.exit(2);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
logEvent('allowed', toolName, '');
|
|
166
|
+
process.exit(0);
|
|
167
|
+
} catch (err) {
|
|
168
|
+
// Fail CLOSED — any hook execution error blocks the tool call.
|
|
169
|
+
console.error('[Intutic Governance] Hook error (blocking for safety):', err);
|
|
170
|
+
logEvent('blocked', 'unknown', String(err));
|
|
171
|
+
process.exit(2);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
`;
|
|
175
|
+
}
|
|
176
|
+
// ─── JSON5 merge helpers ──────────────────────────────────────────────────────
|
|
177
|
+
/**
|
|
178
|
+
* Attempt to parse a JSON5-like string by progressively stripping
|
|
179
|
+
* non-standard syntax. Returns a plain JS object or throws if unparseable.
|
|
180
|
+
*/
|
|
181
|
+
function parseJson5Like(raw) {
|
|
182
|
+
// Attempt 1: vanilla JSON
|
|
183
|
+
try {
|
|
184
|
+
return JSON.parse(raw);
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
// pass
|
|
188
|
+
}
|
|
189
|
+
// Attempt 2: strip single-line // comments
|
|
190
|
+
let stripped = raw.replace(/\/\/[^\n]*/g, '');
|
|
191
|
+
try {
|
|
192
|
+
return JSON.parse(stripped);
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
// pass
|
|
196
|
+
}
|
|
197
|
+
// Attempt 3: also strip trailing commas before } or ]
|
|
198
|
+
stripped = stripped.replace(/,(\s*[}\]])/g, '$1');
|
|
199
|
+
return JSON.parse(stripped);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Deep-merge `hooks.internal.entries["intutic-governance"]` into the parsed
|
|
203
|
+
* openclaw config object and return the merged result.
|
|
204
|
+
*/
|
|
205
|
+
function mergeOpenclawConfig(existing, hookScriptPath) {
|
|
206
|
+
const hooks = existing.hooks ?? {};
|
|
207
|
+
const internal = hooks.internal ?? {};
|
|
208
|
+
const entries = internal.entries ?? {};
|
|
209
|
+
return {
|
|
210
|
+
...existing,
|
|
211
|
+
hooks: {
|
|
212
|
+
...hooks,
|
|
213
|
+
internal: {
|
|
214
|
+
...internal,
|
|
215
|
+
entries: {
|
|
216
|
+
...entries,
|
|
217
|
+
'intutic-governance': {
|
|
218
|
+
enabled: true,
|
|
219
|
+
event: 'PreToolUse',
|
|
220
|
+
command: hookScriptPath,
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
// ─── Main export ──────────────────────────────────────────────────────────────
|
|
228
|
+
/**
|
|
229
|
+
* Write the Openclaw governance hook script and merge hook configuration
|
|
230
|
+
* into `~/.openclaw/openclaw.json`.
|
|
231
|
+
*
|
|
232
|
+
* Safe to call repeatedly — uses atomic rename so partial writes never
|
|
233
|
+
* leave a corrupt state.
|
|
234
|
+
*
|
|
235
|
+
* @param workspaceRoot - Absolute workspace root path.
|
|
236
|
+
* @param proxyUrl - Intutic proxy URL written into hook headers.
|
|
237
|
+
* @param workspaceId - Workspace ID embedded in every hook event payload.
|
|
238
|
+
*/
|
|
239
|
+
export async function writeOpenclawHooks(workspaceRoot, proxyUrl, workspaceId = '') {
|
|
240
|
+
// ── 1. Ensure directories ──────────────────────────────────────────────────
|
|
241
|
+
const globalHookDir = path.join(os.homedir(), '.intutic', 'hooks');
|
|
242
|
+
const hookEventsDir = path.join(workspaceRoot, '.intutic', 'events');
|
|
243
|
+
const envDir = path.join(workspaceRoot, '.intutic', 'env');
|
|
244
|
+
const openclawDir = path.join(os.homedir(), '.openclaw');
|
|
245
|
+
await Promise.all([
|
|
246
|
+
fs.mkdir(globalHookDir, { recursive: true }),
|
|
247
|
+
fs.mkdir(hookEventsDir, { recursive: true }),
|
|
248
|
+
fs.mkdir(envDir, { recursive: true }),
|
|
249
|
+
fs.mkdir(openclawDir, { recursive: true }),
|
|
250
|
+
]);
|
|
251
|
+
// ── 2. Write Node.js hook script ──────────────────────────────────────────
|
|
252
|
+
const hookScriptPath = path.join(globalHookDir, 'openclaw-check.js');
|
|
253
|
+
const hookEventsLog = path.join(hookEventsDir, 'hook-events.jsonl');
|
|
254
|
+
const tmpScript = hookScriptPath + '.intutic-tmp';
|
|
255
|
+
await fs.writeFile(tmpScript, buildOpenclawCheckScript(hookEventsLog, workspaceId), 'utf-8');
|
|
256
|
+
await fs.rename(tmpScript, hookScriptPath);
|
|
257
|
+
await fs.chmod(hookScriptPath, 0o755);
|
|
258
|
+
log.info({ action: 'openclaw_hook_script_written', path: hookScriptPath }, 'Openclaw hook script written');
|
|
259
|
+
// ── 3. JSON5-safe merge into ~/.openclaw/openclaw.json ────────────────────
|
|
260
|
+
let existingConfig = {};
|
|
261
|
+
try {
|
|
262
|
+
const raw = await fs.readFile(OPENCLAW_CONFIG, 'utf-8');
|
|
263
|
+
existingConfig = parseJson5Like(raw);
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
// File doesn't exist or is unparseable — start fresh
|
|
267
|
+
}
|
|
268
|
+
const mergedConfig = mergeOpenclawConfig(existingConfig, hookScriptPath);
|
|
269
|
+
const tmpConfig = OPENCLAW_CONFIG + '.intutic-tmp';
|
|
270
|
+
await fs.writeFile(tmpConfig, JSON.stringify(mergedConfig, null, 2) + '\n', 'utf-8');
|
|
271
|
+
await fs.rename(tmpConfig, OPENCLAW_CONFIG);
|
|
272
|
+
log.info({ action: 'openclaw_config_merged', path: OPENCLAW_CONFIG }, 'Merged Intutic hook into ~/.openclaw/openclaw.json');
|
|
273
|
+
// ── 4. Write env snippet ───────────────────────────────────────────────────
|
|
274
|
+
const envSnippet = [
|
|
275
|
+
`# Intutic openclaw governance env`,
|
|
276
|
+
`INTUITIC_OPENCLAW_HOOK=~/.intutic/hooks/openclaw-check.js`,
|
|
277
|
+
`INTUITIC_OPENCLAW_CONFIG=~/.openclaw/openclaw.json`,
|
|
278
|
+
`# Validate: openclaw hooks check`,
|
|
279
|
+
].join('\n') + '\n';
|
|
280
|
+
const envFilePath = path.join(envDir, 'openclaw.env');
|
|
281
|
+
const tmpEnv = envFilePath + '.intutic-tmp';
|
|
282
|
+
await fs.writeFile(tmpEnv, envSnippet, 'utf-8');
|
|
283
|
+
await fs.rename(tmpEnv, envFilePath);
|
|
284
|
+
log.info({ action: 'openclaw_env_written', path: envFilePath }, 'Openclaw env snippet written');
|
|
285
|
+
// ── 5. Optional validation (non-fatal) ────────────────────────────────────
|
|
286
|
+
try {
|
|
287
|
+
const { stdout, stderr } = await execAsync('openclaw hooks check', { timeout: 5000 });
|
|
288
|
+
log.info({ action: 'openclaw_hooks_check', stdout: stdout.trim(), stderr: stderr.trim() }, 'openclaw hooks check passed');
|
|
289
|
+
}
|
|
290
|
+
catch (err) {
|
|
291
|
+
// Not installed or check failed — non-fatal
|
|
292
|
+
log.debug({ action: 'openclaw_hooks_check_skipped', err }, 'openclaw hooks check skipped (binary not in PATH or check failed)');
|
|
293
|
+
}
|
|
294
|
+
void proxyUrl; // proxyUrl captured in hook script header via newIso comment
|
|
295
|
+
}
|
|
296
|
+
//# sourceMappingURL=openclawHooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openclawHooks.js","sourceRoot":"","sources":["../../src/harness/openclawHooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* openhandsHooks.ts — OpenHands .openhands/hooks.json injection
|
|
3
|
+
* and llm.base_url proxy write.
|
|
4
|
+
*
|
|
5
|
+
* OpenHands supports PreToolUse, Stop, SessionStart hooks via
|
|
6
|
+
* a JSON file at .openhands/hooks.json in the repository root.
|
|
7
|
+
* The hooks file lives within the agent's sandbox working directory,
|
|
8
|
+
* so we also register it with the drift watcher.
|
|
9
|
+
*
|
|
10
|
+
* LLD #14 — Phase 3 cross-harness defence
|
|
11
|
+
* HLD §3.14 — Three-Tier Defense Cascade
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Write .openhands/hooks.json and the pre-tool-check shell script.
|
|
17
|
+
* Also merges llm.base_url into config.toml if it exists.
|
|
18
|
+
*
|
|
19
|
+
* @param workspaceRoot - Absolute workspace root.
|
|
20
|
+
* @param proxyUrl - Intutic proxy URL.
|
|
21
|
+
* @param workspaceId - Workspace ID embedded in hook event payloads.
|
|
22
|
+
*/
|
|
23
|
+
export declare function writeOpenHandsHooks(workspaceRoot: string, proxyUrl: string, workspaceId?: string): Promise<void>;
|
|
24
|
+
//# sourceMappingURL=openhandsHooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openhandsHooks.d.ts","sourceRoot":"","sources":["../../src/harness/openhandsHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAoBH;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAK,GACf,OAAO,CAAC,IAAI,CAAC,CA6Gf"}
|