@intutic/sync-daemon 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/agentReporter.d.ts +23 -11
- package/dist/agentReporter.d.ts.map +1 -1
- package/dist/agentReporter.js +20 -13
- package/dist/agentReporter.js.map +1 -1
- package/dist/configReader.d.ts +1 -3
- package/dist/configReader.d.ts.map +1 -1
- package/dist/configReader.js +0 -2
- package/dist/configReader.js.map +1 -1
- package/dist/configWriter.d.ts +11 -12
- package/dist/configWriter.d.ts.map +1 -1
- package/dist/configWriter.js +35 -58
- package/dist/configWriter.js.map +1 -1
- package/dist/disconnect/harnesses.d.ts +0 -2
- package/dist/disconnect/harnesses.d.ts.map +1 -1
- package/dist/disconnect/harnesses.js +157 -80
- package/dist/disconnect/harnesses.js.map +1 -1
- package/dist/disconnect/index.d.ts.map +1 -1
- package/dist/disconnect/index.js +11 -1
- package/dist/disconnect/index.js.map +1 -1
- package/dist/disconnect/mcp.d.ts +21 -0
- package/dist/disconnect/mcp.d.ts.map +1 -1
- package/dist/disconnect/mcp.js +71 -0
- package/dist/disconnect/mcp.js.map +1 -1
- package/dist/disconnect/recognise.d.ts +2 -2
- package/dist/disconnect/recognise.d.ts.map +1 -1
- package/dist/disconnect/recognise.js +15 -5
- package/dist/disconnect/recognise.js.map +1 -1
- package/dist/harness/aiderConfigMerger.d.ts +28 -8
- package/dist/harness/aiderConfigMerger.d.ts.map +1 -1
- package/dist/harness/aiderConfigMerger.js +59 -11
- package/dist/harness/aiderConfigMerger.js.map +1 -1
- package/dist/harness/antigravityCliHooks.d.ts +61 -0
- package/dist/harness/antigravityCliHooks.d.ts.map +1 -0
- package/dist/harness/antigravityCliHooks.js +207 -0
- package/dist/harness/antigravityCliHooks.js.map +1 -0
- package/dist/harness/antigravityHooks.d.ts +12 -2
- package/dist/harness/antigravityHooks.d.ts.map +1 -1
- package/dist/harness/antigravityHooks.js +36 -16
- package/dist/harness/antigravityHooks.js.map +1 -1
- package/dist/harness/antigravityProducts.d.ts +40 -0
- package/dist/harness/antigravityProducts.d.ts.map +1 -0
- package/dist/harness/antigravityProducts.js +109 -0
- package/dist/harness/antigravityProducts.js.map +1 -0
- package/dist/harness/claudeAgentsMd.d.ts +29 -0
- package/dist/harness/claudeAgentsMd.d.ts.map +1 -0
- package/dist/harness/claudeAgentsMd.js +80 -0
- package/dist/harness/claudeAgentsMd.js.map +1 -0
- package/dist/harness/claudeCodeHooks.d.ts +6 -5
- package/dist/harness/claudeCodeHooks.d.ts.map +1 -1
- package/dist/harness/claudeCodeHooks.js +98 -24
- package/dist/harness/claudeCodeHooks.js.map +1 -1
- package/dist/harness/clineHooks.d.ts +0 -1
- package/dist/harness/clineHooks.d.ts.map +1 -1
- package/dist/harness/clineHooks.js +10 -4
- package/dist/harness/clineHooks.js.map +1 -1
- package/dist/harness/codexHooks.d.ts +2 -0
- package/dist/harness/codexHooks.d.ts.map +1 -1
- package/dist/harness/codexHooks.js +12 -4
- package/dist/harness/codexHooks.js.map +1 -1
- package/dist/harness/cursorHooks.d.ts +5 -2
- package/dist/harness/cursorHooks.d.ts.map +1 -1
- package/dist/harness/cursorHooks.js +9 -3
- package/dist/harness/cursorHooks.js.map +1 -1
- package/dist/harness/dshHooks.d.ts +5 -5
- package/dist/harness/dshHooks.d.ts.map +1 -1
- package/dist/harness/dshHooks.js +5 -5
- package/dist/harness/gateArtifacts.d.ts +35 -0
- package/dist/harness/gateArtifacts.d.ts.map +1 -0
- package/dist/harness/gateArtifacts.js +88 -0
- package/dist/harness/gateArtifacts.js.map +1 -0
- package/dist/harness/gateBody.d.ts +186 -69
- package/dist/harness/gateBody.d.ts.map +1 -1
- package/dist/harness/gateBody.js +820 -182
- package/dist/harness/gateBody.js.map +1 -1
- package/dist/harness/gateSightings.d.ts +47 -0
- package/dist/harness/gateSightings.d.ts.map +1 -0
- package/dist/harness/gateSightings.js +159 -0
- package/dist/harness/gateSightings.js.map +1 -0
- package/dist/harness/githubCopilotHooks.d.ts.map +1 -1
- package/dist/harness/githubCopilotHooks.js +7 -1
- package/dist/harness/githubCopilotHooks.js.map +1 -1
- package/dist/harness/gooseHardener.d.ts +0 -1
- package/dist/harness/gooseHardener.d.ts.map +1 -1
- package/dist/harness/gooseHardener.js +1 -1
- package/dist/harness/gooseHardener.js.map +1 -1
- package/dist/harness/gooseHooks.d.ts +14 -1
- package/dist/harness/gooseHooks.d.ts.map +1 -1
- package/dist/harness/gooseHooks.js +55 -29
- package/dist/harness/gooseHooks.js.map +1 -1
- package/dist/harness/grokHooks.d.ts +2 -2
- package/dist/harness/grokHooks.d.ts.map +1 -1
- package/dist/harness/grokHooks.js +11 -6
- package/dist/harness/grokHooks.js.map +1 -1
- package/dist/harness/hermesHooks.d.ts +18 -8
- package/dist/harness/hermesHooks.d.ts.map +1 -1
- package/dist/harness/hermesHooks.js +48 -17
- package/dist/harness/hermesHooks.js.map +1 -1
- package/dist/harness/mcpAutoWrite.d.ts +24 -9
- package/dist/harness/mcpAutoWrite.d.ts.map +1 -1
- package/dist/harness/mcpAutoWrite.js +218 -25
- package/dist/harness/mcpAutoWrite.js.map +1 -1
- package/dist/harness/museHooks.d.ts +3 -3
- package/dist/harness/museHooks.d.ts.map +1 -1
- package/dist/harness/museHooks.js +9 -5
- package/dist/harness/museHooks.js.map +1 -1
- package/dist/harness/n8nHooks.d.ts +0 -1
- package/dist/harness/n8nHooks.d.ts.map +1 -1
- package/dist/harness/n8nHooks.js +4 -1
- package/dist/harness/n8nHooks.js.map +1 -1
- package/dist/harness/openCodeHooks.d.ts +2 -2
- package/dist/harness/openCodeHooks.d.ts.map +1 -1
- package/dist/harness/openCodeHooks.js +7 -3
- package/dist/harness/openCodeHooks.js.map +1 -1
- package/dist/harness/openWebuiHooks.d.ts +0 -1
- package/dist/harness/openWebuiHooks.d.ts.map +1 -1
- package/dist/harness/openWebuiHooks.js +5 -1
- package/dist/harness/openWebuiHooks.js.map +1 -1
- package/dist/harness/openclawHooks.d.ts +70 -18
- package/dist/harness/openclawHooks.d.ts.map +1 -1
- package/dist/harness/openclawHooks.js +213 -161
- package/dist/harness/openclawHooks.js.map +1 -1
- package/dist/harness/openhandsHooks.d.ts +24 -10
- package/dist/harness/openhandsHooks.d.ts.map +1 -1
- package/dist/harness/openhandsHooks.js +49 -48
- package/dist/harness/openhandsHooks.js.map +1 -1
- package/dist/harness/piHooks.d.ts +52 -17
- package/dist/harness/piHooks.d.ts.map +1 -1
- package/dist/harness/piHooks.js +177 -166
- package/dist/harness/piHooks.js.map +1 -1
- package/dist/harness/protectedPaths.d.ts +107 -25
- package/dist/harness/protectedPaths.d.ts.map +1 -1
- package/dist/harness/protectedPaths.js +342 -48
- package/dist/harness/protectedPaths.js.map +1 -1
- package/dist/harness/rulesSection.d.ts +62 -0
- package/dist/harness/rulesSection.d.ts.map +1 -0
- package/dist/harness/rulesSection.js +145 -0
- package/dist/harness/rulesSection.js.map +1 -0
- package/dist/harness/windsurfHooks.d.ts +0 -1
- package/dist/harness/windsurfHooks.d.ts.map +1 -1
- package/dist/harness/windsurfHooks.js +4 -1
- package/dist/harness/windsurfHooks.js.map +1 -1
- package/dist/index.d.ts +12 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -9
- package/dist/index.js.map +1 -1
- package/dist/inventory.d.ts +53 -0
- package/dist/inventory.d.ts.map +1 -0
- package/dist/inventory.js +133 -0
- package/dist/inventory.js.map +1 -0
- package/dist/lib/complianceProbes.d.ts +2 -1
- package/dist/lib/complianceProbes.d.ts.map +1 -1
- package/dist/lib/complianceProbes.js +1 -1
- package/dist/lib/complianceProbes.js.map +1 -1
- package/dist/lib/decisionsDigest.d.ts +34 -16
- package/dist/lib/decisionsDigest.d.ts.map +1 -1
- package/dist/lib/decisionsDigest.js +105 -46
- package/dist/lib/decisionsDigest.js.map +1 -1
- package/dist/lib/egressPolicy.d.ts +2 -2
- package/dist/lib/egressPolicy.js +2 -2
- package/dist/lib/mcpRegistryPy.d.ts +11 -0
- package/dist/lib/mcpRegistryPy.d.ts.map +1 -0
- package/dist/lib/mcpRegistryPy.js +173 -0
- package/dist/lib/mcpRegistryPy.js.map +1 -0
- package/dist/lib/phrasesPy.d.ts +11 -0
- package/dist/lib/phrasesPy.d.ts.map +1 -0
- package/dist/lib/phrasesPy.js +351 -0
- package/dist/lib/phrasesPy.js.map +1 -0
- package/dist/lib/policySnapshot.d.ts +46 -10
- package/dist/lib/policySnapshot.d.ts.map +1 -1
- package/dist/lib/policySnapshot.js +136 -47
- package/dist/lib/policySnapshot.js.map +1 -1
- package/dist/lib/processPoller.d.ts.map +1 -1
- package/dist/lib/processPoller.js +9 -2
- package/dist/lib/processPoller.js.map +1 -1
- package/dist/lib/regexLinearity.d.ts +43 -0
- package/dist/lib/regexLinearity.d.ts.map +1 -0
- package/dist/lib/regexLinearity.js +275 -0
- package/dist/lib/regexLinearity.js.map +1 -0
- package/dist/lib/runtimeEnv.d.ts +1 -3
- package/dist/lib/runtimeEnv.d.ts.map +1 -1
- package/dist/lib/runtimeEnv.js +0 -2
- package/dist/lib/runtimeEnv.js.map +1 -1
- package/dist/lib/sequencePy.d.ts +10 -0
- package/dist/lib/sequencePy.d.ts.map +1 -0
- package/dist/lib/sequencePy.js +102 -0
- package/dist/lib/sequencePy.js.map +1 -0
- package/dist/sessionReporter.d.ts +17 -11
- package/dist/sessionReporter.d.ts.map +1 -1
- package/dist/sessionReporter.js +36 -23
- package/dist/sessionReporter.js.map +1 -1
- package/dist/skillWriter.d.ts +23 -5
- package/dist/skillWriter.d.ts.map +1 -1
- package/dist/skillWriter.js +159 -16
- package/dist/skillWriter.js.map +1 -1
- package/dist/syncCycle.d.ts.map +1 -1
- package/dist/syncCycle.js +21 -9
- package/dist/syncCycle.js.map +1 -1
- package/dist/trajectoryMonitor.d.ts +1 -3
- package/dist/trajectoryMonitor.d.ts.map +1 -1
- package/dist/trajectoryMonitor.js +1 -4
- package/dist/trajectoryMonitor.js.map +1 -1
- package/dist/watcher/driftWatcher.d.ts +0 -1
- package/dist/watcher/driftWatcher.d.ts.map +1 -1
- package/dist/watcher/driftWatcher.js +1 -2
- package/dist/watcher/driftWatcher.js.map +1 -1
- package/dist/watcher/settingsGuard.d.ts +33 -7
- package/dist/watcher/settingsGuard.d.ts.map +1 -1
- package/dist/watcher/settingsGuard.js +373 -61
- package/dist/watcher/settingsGuard.js.map +1 -1
- package/dist/watcher/vscodeHookSettings.d.ts +30 -0
- package/dist/watcher/vscodeHookSettings.d.ts.map +1 -0
- package/dist/watcher/vscodeHookSettings.js +170 -0
- package/dist/watcher/vscodeHookSettings.js.map +1 -0
- package/dist/wsClient.d.ts +0 -1
- package/dist/wsClient.d.ts.map +1 -1
- package/dist/wsClient.js +0 -1
- package/dist/wsClient.js.map +1 -1
- package/package.json +5 -5
- package/dist/harness/claudeDesktopHooks.d.ts +0 -33
- package/dist/harness/claudeDesktopHooks.d.ts.map +0 -1
- package/dist/harness/claudeDesktopHooks.js +0 -256
- package/dist/harness/claudeDesktopHooks.js.map +0 -1
- package/dist/harness/continueHooks.d.ts +0 -63
- package/dist/harness/continueHooks.d.ts.map +0 -1
- package/dist/harness/continueHooks.js +0 -250
- package/dist/harness/continueHooks.js.map +0 -1
- package/dist/harness/gateKind.d.ts +0 -164
- package/dist/harness/gateKind.d.ts.map +0 -1
- package/dist/harness/gateKind.js +0 -144
- package/dist/harness/gateKind.js.map +0 -1
- package/dist/harness/rooCodeHooks.d.ts +0 -31
- package/dist/harness/rooCodeHooks.d.ts.map +0 -1
- package/dist/harness/rooCodeHooks.js +0 -281
- package/dist/harness/rooCodeHooks.js.map +0 -1
|
@@ -1,57 +1,96 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* openclawHooks.ts —
|
|
2
|
+
* openclawHooks.ts — OpenClaw plugin gate.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* `
|
|
4
|
+
* OpenClaw has two hook systems, and only one of them can stop a tool call.
|
|
5
|
+
* Its *internal* hooks (`hooks.internal.entries` in `~/.openclaw/openclaw.json`,
|
|
6
|
+
* `HOOK.md` handlers) react to command, session and Gateway events; the
|
|
7
|
+
* dispatcher catches a handler's error and carries on, and tool calls are not
|
|
8
|
+
* among their events at all. Earlier versions of this writer registered the
|
|
9
|
+
* gate there, as `{ event: 'PreToolUse', command }`, a shape OpenClaw keeps
|
|
10
|
+
* (hook entries are open-ended) and never runs. Tool calls are intercepted by
|
|
11
|
+
* a *plugin* hook, `before_tool_call`, registered with `api.on(...)`:
|
|
12
|
+
* https://github.com/openclaw/openclaw/blob/59309cc7777e07d52ca1be94bf8c25fd685919bc/docs/automation/hooks.md
|
|
13
|
+
* ("Choose the right surface": intercepting tools is a plugin hook).
|
|
7
14
|
*
|
|
8
|
-
*
|
|
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).
|
|
15
|
+
* ## The plugin
|
|
12
16
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
17
|
+
* A standalone plugin file needs no manifest when it is listed in
|
|
18
|
+
* `plugins.load.paths`; its id is the file's basename, so the file is
|
|
19
|
+
* `intutic-governance.cjs` and exports `{ id: 'intutic-governance', register }`
|
|
20
|
+
* (`docs/plugins/hooks/tool-policy.md` "Sender-aware policy in one file";
|
|
21
|
+
* `src/plugins/discovery.ts` `discoverFromPath`; the loader test "loads
|
|
22
|
+
* multiple manifestless standalone files"). `.cjs`, so the module format does
|
|
23
|
+
* not depend on any `package.json` above it. A plugin from a configured path
|
|
24
|
+
* is enabled by default unless a non-empty `plugins.allow` leaves it out
|
|
25
|
+
* (`src/plugins/config-activation-shared.ts`), so the id is added to an
|
|
26
|
+
* existing allowlist and nothing is written when there is none.
|
|
15
27
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
28
|
+
* `before_tool_call` receives `{ toolName, params, toolCallId?, runId?,
|
|
29
|
+
* derivedPaths? }` and `ctx.sessionId`, and refuses with `{ block: true,
|
|
30
|
+
* blockReason }`, which is terminal (`docs/plugins/hooks/tool-policy.md`,
|
|
31
|
+
* `src/plugins/hook-types.ts`). The global hook runner runs it fail-closed: a
|
|
32
|
+
* handler that throws or outlives its timeout blocks the call
|
|
33
|
+
* (`src/plugins/hook-runner-global.ts`: `before_tool_call: "fail-closed"`;
|
|
34
|
+
* default 15 s, `docs/plugins/hooks/reference.md`). That timeout cannot stop
|
|
35
|
+
* synchronous code, so the hook registers `timeoutMs: HOOK_TIMEOUT_SECONDS *
|
|
36
|
+
* 1000` and the gate runs under its own deadline inside it
|
|
37
|
+
* (`gateDeadlineMs('openclaw')`).
|
|
38
|
+
*
|
|
39
|
+
* The plugin embeds the shared `emitJsGate` body (`'throw'` contract) and
|
|
40
|
+
* returns OpenClaw's block result for the refusal it throws. Any other fault
|
|
41
|
+
* is a block too. MCP tools reach the hook as `<server>__<tool>`
|
|
42
|
+
* (`src/agents/agent-bundle-mcp-names.ts`, `TOOL_NAME_SEPARATOR`), which the
|
|
43
|
+
* plugin composes into `mcp__<server>__<tool>` for the allowlist.
|
|
44
|
+
*
|
|
45
|
+
* The file lives under `~/.intutic/hooks/`, a governance-protected path, so an
|
|
46
|
+
* agent cannot edit it under any gated harness.
|
|
19
47
|
*
|
|
20
48
|
* @module
|
|
21
49
|
*/
|
|
22
50
|
import * as fs from 'node:fs/promises';
|
|
23
51
|
import * as path from 'node:path';
|
|
24
52
|
import * as os from 'node:os';
|
|
25
|
-
import { exec } from 'node:child_process';
|
|
26
|
-
import { promisify } from 'node:util';
|
|
27
53
|
import { createLogger } from '@intutic/logger';
|
|
28
|
-
import { keepOriginal } from '../disconnect/originals.js';
|
|
54
|
+
import { keepOriginal, writeOwnedFile } from '../disconnect/originals.js';
|
|
29
55
|
import { newIso } from '@intutic/id';
|
|
30
|
-
import {
|
|
56
|
+
import { HOOK_TIMEOUT_SECONDS } from '@intutic/shared-types';
|
|
57
|
+
import { emitJsGate, REVIEW_REQUESTS_BASENAME } from './gateBody.js';
|
|
31
58
|
const log = createLogger('sync-openclaw-hooks');
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
59
|
+
/** The plugin id, which OpenClaw takes from the standalone file's basename. */
|
|
60
|
+
export const OPENCLAW_PLUGIN_ID = 'intutic-governance';
|
|
61
|
+
/** The plugin file, relative to the home directory. */
|
|
62
|
+
export const OPENCLAW_PLUGIN_FILE = path.join('.intutic', 'hooks', 'openclaw', `${OPENCLAW_PLUGIN_ID}.cjs`);
|
|
63
|
+
/** The first line of the plugin's header, which disconnect recognises it by. */
|
|
64
|
+
export const OPENCLAW_PLUGIN_MARKER = 'Intutic OpenClaw governance plugin.';
|
|
65
|
+
function openclawConfigPath() {
|
|
66
|
+
return path.join(os.homedir(), '.openclaw', 'openclaw.json');
|
|
67
|
+
}
|
|
68
|
+
/** The absolute path of the plugin file for the current home directory. */
|
|
69
|
+
export function openclawPluginPath() {
|
|
70
|
+
return path.join(os.homedir(), OPENCLAW_PLUGIN_FILE);
|
|
71
|
+
}
|
|
72
|
+
// ─── The plugin ──────────────────────────────────────────────────────────────
|
|
73
|
+
export function buildOpenclawPlugin(proxyUrl, workspaceRoot, workspaceId) {
|
|
74
|
+
const hookEventsLog = path.join(workspaceRoot, '.intutic', 'events', 'hook-events.jsonl');
|
|
75
|
+
return `/**
|
|
76
|
+
* ${OPENCLAW_PLUGIN_MARKER}
|
|
41
77
|
* Auto-generated by intutic sync-daemon. DO NOT EDIT.
|
|
42
78
|
* Proxy: ${proxyUrl}
|
|
43
79
|
* Generated: ${newIso()}
|
|
44
|
-
*
|
|
80
|
+
*
|
|
81
|
+
* A before_tool_call plugin hook, loaded through plugins.load.paths in
|
|
82
|
+
* ~/.openclaw/openclaw.json. Refuses a tool call by returning
|
|
83
|
+
* { block: true, blockReason }. See
|
|
84
|
+
* services/sync-daemon/src/harness/openclawHooks.ts.
|
|
45
85
|
*/
|
|
46
86
|
'use strict';
|
|
47
|
-
${emitJsFailClosedPrelude({ harness: 'openclaw', contract: 'exit2' })}
|
|
48
87
|
const path = require('path');
|
|
49
88
|
const os = require('os');
|
|
50
89
|
const fs = require('fs');
|
|
51
90
|
const crypto = require('crypto');
|
|
52
91
|
const https = require('https');
|
|
53
92
|
|
|
54
|
-
//
|
|
93
|
+
// Runtime credentials, read when OpenClaw loads the plugin.
|
|
55
94
|
const _runtimeEnvPath = path.join(os.homedir(), '.intutic', 'env', 'runtime.env');
|
|
56
95
|
let _intuticHost = 'https://api.intutic.ai', _intuticKey = '', _intuticWsId = ${JSON.stringify(workspaceId)};
|
|
57
96
|
try {
|
|
@@ -62,96 +101,98 @@ try {
|
|
|
62
101
|
if (k === 'INTUTIC_API_KEY' && v) _intuticKey = v;
|
|
63
102
|
if (k === 'INTUTIC_WORKSPACE_ID' && v) _intuticWsId = v;
|
|
64
103
|
});
|
|
65
|
-
} catch {
|
|
104
|
+
} catch {}
|
|
66
105
|
|
|
67
|
-
${emitJsGate({ harness: 'openclaw', contract: '
|
|
106
|
+
${emitJsGate({ harness: 'openclaw', contract: 'throw', reviewRequestFile: path.join(workspaceRoot, '.intutic', 'events', REVIEW_REQUESTS_BASENAME) })}
|
|
68
107
|
|
|
69
|
-
/**
|
|
70
|
-
* Appends a governance event to the local hook-events log (Path B)
|
|
71
|
-
* and fires a non-blocking HTTP POST to the control plane (Path A).
|
|
72
|
-
*/
|
|
73
108
|
let _intuticSessionId = '';
|
|
74
109
|
function logEvent(verdict, toolName, reason) {
|
|
75
110
|
try {
|
|
76
111
|
const ts = new Date().toISOString();
|
|
77
|
-
// incidentId = sha1(timestamp + toolName + workspaceId).slice(0,16)
|
|
78
112
|
const incidentId = crypto.createHash('sha1').update(ts + toolName + _intuticWsId).digest('hex').slice(0, 16);
|
|
113
|
+
// The event's id: random, made once here, and resent with the line it is
|
|
114
|
+
// written into, so the control plane processes the event once.
|
|
115
|
+
const eventId = crypto.randomBytes(16).toString('hex');
|
|
79
116
|
const entry = JSON.stringify({
|
|
80
|
-
// Passed through, not collapsed to two values: the advisory tier emits
|
|
81
|
-
// 'tool_flagged', and a ternary here silently recorded it as an allow.
|
|
82
117
|
event: verdict,
|
|
83
|
-
toolName,
|
|
84
|
-
reason: reason || '',
|
|
118
|
+
toolName, reason: reason || '',
|
|
85
119
|
workspaceId: _intuticWsId,
|
|
86
120
|
harnessType: 'openclaw',
|
|
87
121
|
timestamp: ts,
|
|
88
122
|
incidentId,
|
|
123
|
+
eventId,
|
|
89
124
|
...(_intuticSessionId ? { sessionId: _intuticSessionId } : {}),
|
|
90
125
|
}) + '\\n';
|
|
91
|
-
|
|
92
|
-
fs.
|
|
93
|
-
|
|
126
|
+
const logPath = ${JSON.stringify(hookEventsLog)};
|
|
127
|
+
try { fs.mkdirSync(path.dirname(logPath), { recursive: true }); } catch {}
|
|
128
|
+
fs.appendFileSync(logPath, entry, { flag: 'a' });
|
|
94
129
|
if (_intuticKey) {
|
|
95
130
|
try {
|
|
96
131
|
const body = JSON.stringify({ events: [JSON.parse(entry)] });
|
|
97
132
|
const urlObj = new URL('/api/v1/hook-events', _intuticHost);
|
|
98
133
|
const isHttps = urlObj.protocol === 'https:';
|
|
99
134
|
const mod = isHttps ? https : require('http');
|
|
100
|
-
const req = mod.request({
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
path: urlObj.pathname,
|
|
104
|
-
method: 'POST',
|
|
105
|
-
headers: {
|
|
106
|
-
'Content-Type': 'application/json',
|
|
107
|
-
'Content-Length': Buffer.byteLength(body),
|
|
108
|
-
'Authorization': 'Bearer ' + _intuticKey,
|
|
109
|
-
},
|
|
110
|
-
});
|
|
111
|
-
req.on('error', () => { /* fire-and-forget — ignore errors */ });
|
|
112
|
-
req.write(body);
|
|
113
|
-
req.end();
|
|
135
|
+
const req = mod.request({ hostname: urlObj.hostname, port: urlObj.port || (isHttps ? 443 : 80), path: urlObj.pathname, method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body), 'Authorization': 'Bearer ' + _intuticKey } });
|
|
136
|
+
req.on('error', () => { /* fire-and-forget */ });
|
|
137
|
+
req.write(body); req.end();
|
|
114
138
|
} catch { /* never crash the hook */ }
|
|
115
139
|
}
|
|
116
140
|
} catch { /* never crash the hook */ }
|
|
117
141
|
}
|
|
118
142
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
143
|
+
/**
|
|
144
|
+
* One tool call. Returns to allow; throws '[Intutic Governance] …' to refuse.
|
|
145
|
+
* Params that are not an object are refused outright: the gate reads paths
|
|
146
|
+
* and commands out of them, and a shape it cannot read is not one it can
|
|
147
|
+
* allow. apply_patch names its files in derivedPaths, and each is checked.
|
|
148
|
+
*/
|
|
149
|
+
function intuticEvaluate(event, ctx) {
|
|
150
|
+
_intuticSessionId = String((ctx && (ctx.sessionId || ctx.sessionKey)) || '');
|
|
151
|
+
let tool = String((event && event.toolName) || 'tool');
|
|
152
|
+
// OpenClaw names an MCP tool <server>__<tool>; the allowlist reads
|
|
153
|
+
// mcp__<server>__<tool>. No built-in tool id has a double underscore.
|
|
154
|
+
if (tool.indexOf('__') > 0 && tool.indexOf('mcp__') !== 0) tool = 'mcp__' + tool;
|
|
155
|
+
const params = event && event.params;
|
|
156
|
+
if (!params || typeof params !== 'object' || Array.isArray(params)) {
|
|
157
|
+
const reason = '[Intutic Governance] BLOCKED: tool "' + tool + '" was called with parameters the gate cannot read (' +
|
|
158
|
+
(params === null ? 'null' : typeof params) + '); refusing rather than allowing a call it cannot evaluate.';
|
|
159
|
+
try { logEvent('tool_blocked', tool, reason); } catch (e) {}
|
|
160
|
+
throw new Error(reason);
|
|
161
|
+
}
|
|
162
|
+
const command = params.command || params.cmd || params.script || '';
|
|
163
|
+
const direct = params.path || params.file_path || params.filePath || params.target || '';
|
|
164
|
+
const derived = event && Array.isArray(event.derivedPaths) ? event.derivedPaths.filter((p) => typeof p === 'string' && p) : [];
|
|
165
|
+
const targets = derived.length > 0 ? (direct ? [direct].concat(derived) : derived) : [direct];
|
|
166
|
+
for (const target of targets) intuticGate(tool, target, command, logEvent, _intuticWsId, params, _intuticSessionId);
|
|
167
|
+
logEvent('tool_allowed', tool, '');
|
|
168
|
+
}
|
|
142
169
|
|
|
143
|
-
|
|
144
|
-
|
|
170
|
+
/** Fail closed: a fault inside the gate is a refusal, never an allow. */
|
|
171
|
+
function beforeToolCall(event, ctx) {
|
|
172
|
+
try {
|
|
173
|
+
intuticEvaluate(event, ctx);
|
|
174
|
+
return undefined;
|
|
145
175
|
} catch (err) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
176
|
+
let reason = String((err && err.message) || err);
|
|
177
|
+
if (reason.indexOf('[Intutic Governance]') !== 0) {
|
|
178
|
+
reason = '[Intutic Governance] BLOCKED: gate fault (failing closed): ' + reason;
|
|
179
|
+
try { logEvent('tool_blocked', String((event && event.toolName) || 'unknown'), reason); } catch (e) {}
|
|
180
|
+
}
|
|
181
|
+
return { block: true, blockReason: reason };
|
|
150
182
|
}
|
|
151
|
-
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
module.exports = {
|
|
186
|
+
id: ${JSON.stringify(OPENCLAW_PLUGIN_ID)},
|
|
187
|
+
name: 'Intutic governance',
|
|
188
|
+
description: 'Refuses tool calls that break the workspace policy before they run.',
|
|
189
|
+
register(api) {
|
|
190
|
+
api.on('before_tool_call', beforeToolCall, { timeoutMs: ${HOOK_TIMEOUT_SECONDS * 1000} });
|
|
191
|
+
},
|
|
192
|
+
};
|
|
152
193
|
`;
|
|
153
194
|
}
|
|
154
|
-
// ───
|
|
195
|
+
// ─── ~/.openclaw/openclaw.json ───────────────────────────────────────────────
|
|
155
196
|
/**
|
|
156
197
|
* Attempt to parse a JSON5-like string by progressively stripping
|
|
157
198
|
* non-standard syntax. Returns a plain JS object or throws if unparseable.
|
|
@@ -176,99 +217,110 @@ export function parseJson5Like(raw) {
|
|
|
176
217
|
stripped = stripped.replace(/,(\s*[}\]])/g, '$1');
|
|
177
218
|
return JSON.parse(stripped);
|
|
178
219
|
}
|
|
220
|
+
const asObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v) ? v : {};
|
|
179
221
|
/**
|
|
180
|
-
*
|
|
181
|
-
*
|
|
222
|
+
* Adds the plugin to `plugins.load.paths`, and its id to `plugins.allow` when
|
|
223
|
+
* that list is non-empty (an empty or absent one allows every plugin).
|
|
224
|
+
* Everything else is kept, and a second run adds nothing.
|
|
182
225
|
*/
|
|
183
|
-
function mergeOpenclawConfig(existing,
|
|
184
|
-
const
|
|
185
|
-
const
|
|
186
|
-
const
|
|
226
|
+
export function mergeOpenclawConfig(existing, pluginPath) {
|
|
227
|
+
const plugins = asObject(existing.plugins);
|
|
228
|
+
const load = asObject(plugins.load);
|
|
229
|
+
const paths = Array.isArray(load.paths) ? load.paths : [];
|
|
230
|
+
const allow = Array.isArray(plugins.allow) ? plugins.allow : undefined;
|
|
187
231
|
return {
|
|
188
232
|
...existing,
|
|
189
|
-
|
|
190
|
-
...
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
entries: {
|
|
194
|
-
...entries,
|
|
195
|
-
'intutic-governance': {
|
|
196
|
-
enabled: true,
|
|
197
|
-
event: 'PreToolUse',
|
|
198
|
-
command: hookScriptPath,
|
|
199
|
-
},
|
|
200
|
-
},
|
|
201
|
-
},
|
|
233
|
+
plugins: {
|
|
234
|
+
...plugins,
|
|
235
|
+
...(allow && allow.length > 0 && !allow.includes(OPENCLAW_PLUGIN_ID) ? { allow: [...allow, OPENCLAW_PLUGIN_ID] } : {}),
|
|
236
|
+
load: { ...load, paths: paths.includes(pluginPath) ? paths : [...paths, pluginPath] },
|
|
202
237
|
},
|
|
203
238
|
};
|
|
204
239
|
}
|
|
240
|
+
/** Why the plugin will not load even once it is listed, when the config says so. */
|
|
241
|
+
function configBlocksPlugin(config) {
|
|
242
|
+
const plugins = asObject(config.plugins);
|
|
243
|
+
if (plugins.enabled === false)
|
|
244
|
+
return 'plugins.enabled is false';
|
|
245
|
+
if (Array.isArray(plugins.deny) && plugins.deny.includes(OPENCLAW_PLUGIN_ID))
|
|
246
|
+
return `plugins.deny lists ${OPENCLAW_PLUGIN_ID}`;
|
|
247
|
+
if (asObject(asObject(plugins.entries)[OPENCLAW_PLUGIN_ID]).enabled === false)
|
|
248
|
+
return `plugins.entries.${OPENCLAW_PLUGIN_ID}.enabled is false`;
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
205
251
|
// ─── Main export ──────────────────────────────────────────────────────────────
|
|
206
252
|
/**
|
|
207
|
-
* Write the
|
|
208
|
-
*
|
|
253
|
+
* Write the OpenClaw governance plugin and list it in
|
|
254
|
+
* `~/.openclaw/openclaw.json`.
|
|
209
255
|
*
|
|
210
|
-
* Safe to call repeatedly
|
|
211
|
-
*
|
|
256
|
+
* Safe to call repeatedly: the plugin is replaced by atomic rename and the
|
|
257
|
+
* config merge is idempotent. A config file that does not parse is left as
|
|
258
|
+
* it is, and reported.
|
|
212
259
|
*
|
|
213
260
|
* @param workspaceRoot - Absolute workspace root path.
|
|
214
|
-
* @param proxyUrl - Intutic proxy URL written into
|
|
261
|
+
* @param proxyUrl - Intutic proxy URL written into the plugin header.
|
|
215
262
|
* @param workspaceId - Workspace ID embedded in every hook event payload.
|
|
216
263
|
*/
|
|
217
264
|
export async function writeOpenclawHooks(workspaceRoot, proxyUrl, workspaceId = '') {
|
|
218
|
-
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
await Promise.all([
|
|
225
|
-
fs.mkdir(globalHookDir, { recursive: true }),
|
|
226
|
-
fs.mkdir(hookEventsDir, { recursive: true }),
|
|
227
|
-
fs.mkdir(envDir, { recursive: true }),
|
|
228
|
-
fs.mkdir(openclawDir, { recursive: true }),
|
|
229
|
-
]);
|
|
230
|
-
// ── 2. Write Node.js hook script ──────────────────────────────────────────
|
|
231
|
-
const hookScriptPath = path.join(globalHookDir, 'openclaw-check.js');
|
|
232
|
-
const hookEventsLog = path.join(hookEventsDir, 'hook-events.jsonl');
|
|
233
|
-
const tmpScript = hookScriptPath + '.intutic-tmp';
|
|
234
|
-
await fs.writeFile(tmpScript, buildOpenclawCheckScript(hookEventsLog, workspaceId, proxyUrl), 'utf-8');
|
|
235
|
-
await fs.rename(tmpScript, hookScriptPath);
|
|
236
|
-
await fs.chmod(hookScriptPath, 0o755);
|
|
237
|
-
log.info({ action: 'openclaw_hook_script_written', path: hookScriptPath }, 'Openclaw hook script written');
|
|
238
|
-
// ── 3. JSON5-safe merge into ~/.openclaw/openclaw.json ────────────────────
|
|
239
|
-
let existingConfig = {};
|
|
265
|
+
await fs.mkdir(path.join(workspaceRoot, '.intutic', 'events'), { recursive: true });
|
|
266
|
+
const pluginPath = openclawPluginPath();
|
|
267
|
+
await writeOwnedFile(pluginPath, workspaceRoot, buildOpenclawPlugin(proxyUrl, workspaceRoot, workspaceId));
|
|
268
|
+
log.info({ action: 'openclaw_plugin_written', path: pluginPath }, 'OpenClaw governance plugin written');
|
|
269
|
+
const configPath = openclawConfigPath();
|
|
270
|
+
let raw = null;
|
|
240
271
|
try {
|
|
241
|
-
|
|
242
|
-
existingConfig = parseJson5Like(raw);
|
|
272
|
+
raw = await fs.readFile(configPath, 'utf-8');
|
|
243
273
|
}
|
|
244
|
-
catch {
|
|
245
|
-
|
|
274
|
+
catch (err) {
|
|
275
|
+
if (err.code !== 'ENOENT') {
|
|
276
|
+
log.warn({ action: 'config_merge_skipped', path: configPath, error: String(err) }, `Could not read ${configPath} — left untouched`);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
246
279
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
await
|
|
262
|
-
await fs.
|
|
263
|
-
|
|
264
|
-
|
|
280
|
+
let existing = {};
|
|
281
|
+
if (raw !== null && raw.trim() !== '') {
|
|
282
|
+
try {
|
|
283
|
+
existing = asObject(parseJson5Like(raw));
|
|
284
|
+
}
|
|
285
|
+
catch {
|
|
286
|
+
log.warn({ action: 'config_merge_skipped', path: configPath }, `${configPath} does not parse — left untouched; fix it and the next sync will list the Intutic plugin`);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
const blocked = configBlocksPlugin(existing);
|
|
291
|
+
if (blocked) {
|
|
292
|
+
log.warn({ action: 'openclaw_plugin_disabled', path: configPath, reason: blocked }, `OpenClaw will not load the Intutic plugin: ${blocked}`);
|
|
293
|
+
}
|
|
294
|
+
await keepOriginal(configPath, workspaceRoot);
|
|
295
|
+
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
|
296
|
+
const tmp = configPath + '.intutic-tmp';
|
|
297
|
+
await fs.writeFile(tmp, JSON.stringify(mergeOpenclawConfig(existing, pluginPath), null, 2) + '\n', 'utf-8');
|
|
298
|
+
await fs.rename(tmp, configPath);
|
|
299
|
+
log.info({ action: 'openclaw_config_merged', path: configPath }, 'Listed the Intutic plugin in ~/.openclaw/openclaw.json');
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* OpenClaw's agent workspace, where it loads `AGENTS.md` and its other
|
|
303
|
+
* bootstrap files every session — never from a project:
|
|
304
|
+
* `agents.defaults.workspace` in `~/.openclaw/openclaw.json`, else
|
|
305
|
+
* `OPENCLAW_WORKSPACE_DIR`, else `~/.openclaw/workspace`
|
|
306
|
+
* (https://github.com/openclaw/openclaw/blob/98457908cf3e4fbbd0354b530e0aa0d15b560158/docs/gateway/config-agents/workspace-and-bootstrap.md).
|
|
307
|
+
*/
|
|
308
|
+
export async function openclawAgentWorkspace() {
|
|
309
|
+
const home = os.homedir();
|
|
310
|
+
const expand = (p) => (p === '~' || p.startsWith('~/') ? path.join(home, p.slice(1)) : path.resolve(p));
|
|
265
311
|
try {
|
|
266
|
-
const
|
|
267
|
-
|
|
312
|
+
const doc = parseJson5Like(await fs.readFile(path.join(home, '.openclaw', 'openclaw.json'), 'utf-8'));
|
|
313
|
+
const agents = doc.agents;
|
|
314
|
+
const configured = agents?.defaults?.workspace;
|
|
315
|
+
if (typeof configured === 'string' && configured.trim() !== '')
|
|
316
|
+
return expand(configured.trim());
|
|
268
317
|
}
|
|
269
|
-
catch
|
|
270
|
-
//
|
|
271
|
-
log.debug({ action: 'openclaw_hooks_check_skipped', err }, 'openclaw hooks check skipped (binary not in PATH or check failed)');
|
|
318
|
+
catch {
|
|
319
|
+
// No config, or one that does not parse: the defaults apply.
|
|
272
320
|
}
|
|
321
|
+
const fromEnv = process.env.OPENCLAW_WORKSPACE_DIR;
|
|
322
|
+
if (fromEnv)
|
|
323
|
+
return expand(fromEnv);
|
|
324
|
+
return path.join(home, '.openclaw', 'workspace');
|
|
273
325
|
}
|
|
274
326
|
//# sourceMappingURL=openclawHooks.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openclawHooks.js","sourceRoot":"","sources":["../../src/harness/openclawHooks.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"openclawHooks.js","sourceRoot":"","sources":["../../src/harness/openclawHooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,11 +7,21 @@
|
|
|
7
7
|
* The hooks file lives within the agent's sandbox working directory,
|
|
8
8
|
* so we also register it with the drift watcher.
|
|
9
9
|
*
|
|
10
|
-
* LLD #14 — Phase 3 cross-harness defence
|
|
11
10
|
* HLD §3.14 — Three-Tier Defense Cascade
|
|
12
11
|
*
|
|
13
12
|
* @module
|
|
14
13
|
*/
|
|
14
|
+
/**
|
|
15
|
+
* `.openhands/hooks.json` in the OpenHands SDK's schema (\`HookConfig\`): each
|
|
16
|
+
* event a list of matchers, each with its hook definitions. Earlier versions
|
|
17
|
+
* wrote a flat list of \`{type: 'PreToolUse', command, failClosed}\`, which the
|
|
18
|
+
* SDK cannot load, and had extra top-level keys beside \`hooks\`, which it
|
|
19
|
+
* drops with a warning; the marker that tells disconnect the file is ours is
|
|
20
|
+
* now each hook's \`name\`. OpenHands runs a call whose hook times out, and
|
|
21
|
+
* has no setting to refuse instead, so the gate refuses at its own deadline,
|
|
22
|
+
* inside the \`timeout\` set here (default 60 s).
|
|
23
|
+
*/
|
|
24
|
+
export declare function openHandsHooksConfig(hookScriptPath: string): Record<string, unknown>;
|
|
15
25
|
/**
|
|
16
26
|
* Write .openhands/hooks.json and the pre-tool-check shell script.
|
|
17
27
|
* Also points `[llm] base_url` in ~/.openhands/config.toml at the proxy when
|
|
@@ -34,16 +44,20 @@ export declare function isOpenHandsConfig(raw: string): boolean;
|
|
|
34
44
|
*/
|
|
35
45
|
export declare function mergeOpenHandsBaseUrl(raw: string, proxyUrl: string): string | null;
|
|
36
46
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
47
|
+
* Point `[llm] base_url` in the workspace's OpenHands `config.toml` at the
|
|
48
|
+
* proxy, in the form the configured model's SDK expects (LiteLLM appends
|
|
49
|
+
* `/v1/messages` to an Anthropic base and `/chat/completions` to an
|
|
50
|
+
* OpenAI-style one), and keep the rest of the file.
|
|
51
|
+
*
|
|
52
|
+
* Earlier versions also wrote the rule sets into an `[intutic]` table, which
|
|
53
|
+
* OpenHands never reads (the rules now go into a microagent, see the
|
|
54
|
+
* adapter); that table is dropped. Earlier still, versions replaced the
|
|
55
|
+
* whole file, so a file starting with their header holds nothing of the
|
|
56
|
+
* user's and is regenerated.
|
|
42
57
|
*
|
|
43
58
|
* Edited line by line so the user's own settings and comments survive; the
|
|
44
|
-
* result is parsed before it is returned.
|
|
45
|
-
* file
|
|
46
|
-
* is regenerated. Returns `null` when the existing file is not valid TOML.
|
|
59
|
+
* result is parsed before it is returned. Returns `null` when the existing
|
|
60
|
+
* file is not valid TOML.
|
|
47
61
|
*/
|
|
48
|
-
export declare function mergeOpenHandsToml(raw: string, proxyUrl: string
|
|
62
|
+
export declare function mergeOpenHandsToml(raw: string, proxyUrl: string): string | null;
|
|
49
63
|
//# sourceMappingURL=openhandsHooks.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openhandsHooks.d.ts","sourceRoot":"","sources":["../../src/harness/openhandsHooks.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"openhandsHooks.d.ts","sourceRoot":"","sources":["../../src/harness/openhandsHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAcH;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQpF;AAED;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CACvC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAK,GACf,OAAO,CAAC,IAAI,CAAC,CAwGf;AAqDD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAItD;AAuCD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUlF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAU/E"}
|