clay-server 5.2.0-beta.5 → 5.2.0-beta.7
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/lib/claude-hook-installer.js +1 -267
- package/lib/claude-jsonl-watcher.js +1 -204
- package/lib/clay-control-presentation-binding.js +96 -0
- package/lib/clay-control-presentation.js +128 -0
- package/lib/clay-control-target-projection.js +26 -0
- package/lib/clay-mate-chat.js +1 -0
- package/lib/clay-mate-knowledge.js +1 -1
- package/lib/clay-product-control-tools.js +1 -1
- package/lib/clay-product-control.js +22 -12
- package/lib/clay-project-records.js +2 -1
- package/lib/clay-scheduling.js +1 -1
- package/lib/clay-screen-controls.js +13 -5
- package/lib/clay-skill-install.js +1 -33
- package/lib/clay-workspace-controls.js +2 -1
- package/lib/cli-sessions.js +1 -300
- package/lib/codex-defaults.js +1 -28
- package/lib/context-usage.js +3 -2
- package/lib/contextual-screen-snapshot.js +38 -0
- package/lib/daemon.js +5 -55
- package/lib/default-ai-runtime.js +4 -8
- package/lib/email-mcp-server.js +1 -1
- package/lib/kiro-defaults.js +1 -22
- package/lib/mate-context-service.js +138 -0
- package/lib/mate-context-tools.js +21 -0
- package/lib/mcp-proxy.js +2 -32
- package/lib/mcp-query-bridge.js +1 -1
- package/lib/mcp-source-adapters.js +1 -91
- package/lib/mcp-source-context.js +1 -42
- package/lib/mcp-source-discovery.js +1 -169
- package/lib/mcp-source-plugins.js +1 -68
- package/lib/mcp-source-restrictions.js +1 -47
- package/lib/mcp-tool-policy.js +1 -26
- package/lib/os-user-diagnostics.js +2 -4
- package/lib/os-users.js +4 -65
- package/lib/project-connection.js +14 -10
- package/lib/project-control-git-view.js +12 -0
- package/lib/project-filesystem.js +5 -1
- package/lib/project-http.js +7 -4
- package/lib/project-mcp.js +2 -2
- package/lib/project-notifications.js +5 -4
- package/lib/project-pair-result-delivery.js +2 -2
- package/lib/project-pair-usage.js +12 -13
- package/lib/project-pending-message-queue.js +3 -1
- package/lib/project-product-control.js +3 -1
- package/lib/project-scheduled-messages.js +4 -4
- package/lib/project-scheduled-task-control.js +1 -1
- package/lib/project-session-document.js +20 -5
- package/lib/project-session-history-actions.js +5 -3
- package/lib/project-session-pair.js +1 -1
- package/lib/project-session-runtime-actions.js +2 -1
- package/lib/project-session-spawn.js +2 -3
- package/lib/project-session-vendor.js +2 -1
- package/lib/project-sessions.js +18 -26
- package/lib/project-shared-browser.js +22 -5
- package/lib/project-studio-presentation.js +58 -0
- package/lib/project-user-message.js +37 -0
- package/lib/project-vendor-login.js +6 -36
- package/lib/project-workspace-query.js +11 -1
- package/lib/project.js +24 -7
- package/lib/public/app.js +25 -0
- package/lib/public/appearance-bootstrap.js +2 -0
- package/lib/public/css/contextual-clay.css +240 -0
- package/lib/public/css/messages.css +6 -2
- package/lib/public/modules/app-favicon.js +18 -9
- package/lib/public/modules/app-header.js +4 -2
- package/lib/public/modules/app-loop-ui.js +2 -0
- package/lib/public/modules/app-message-router.js +5 -1
- package/lib/public/modules/app-messages.js +50 -6
- package/lib/public/modules/app-notifications.js +11 -5
- package/lib/public/modules/app-panels.js +61 -123
- package/lib/public/modules/app-projects.js +3 -0
- package/lib/public/modules/app-rate-limit.js +7 -27
- package/lib/public/modules/chat-render-runtime.js +3 -0
- package/lib/public/modules/clay-control-activity.js +214 -0
- package/lib/public/modules/clay-control-copy.js +36 -0
- package/lib/public/modules/clay-control-detail-targets.js +44 -0
- package/lib/public/modules/clay-control-object-targets.js +52 -0
- package/lib/public/modules/clay-control-target-visibility.js +12 -0
- package/lib/public/modules/clay-control-targets.js +44 -0
- package/lib/public/modules/clay-navigation-render.js +52 -0
- package/lib/public/modules/clay-owner-navigation.js +11 -2
- package/lib/public/modules/clay-session-links.js +31 -0
- package/lib/public/modules/clay-studio-bridge.js +20 -0
- package/lib/public/modules/clay-studio-document.js +67 -0
- package/lib/public/modules/clay-studio-render.js +144 -0
- package/lib/public/modules/clay-tool-presentation.js +4 -0
- package/lib/public/modules/command-palette.js +26 -31
- package/lib/public/modules/contextual-ask-session.js +26 -0
- package/lib/public/modules/contextual-clay-geometry.js +155 -0
- package/lib/public/modules/contextual-clay-session.js +130 -0
- package/lib/public/modules/contextual-clay-workspace.js +157 -0
- package/lib/public/modules/contextual-clay.js +237 -0
- package/lib/public/modules/contextual-composer.js +91 -0
- package/lib/public/modules/contextual-mate-context.js +115 -0
- package/lib/public/modules/contextual-mate-hint.js +40 -0
- package/lib/public/modules/contextual-mate-identity.js +23 -0
- package/lib/public/modules/contextual-mate-sessions.js +134 -0
- package/lib/public/modules/contextual-search.js +137 -0
- package/lib/public/modules/contextual-session-citations.js +58 -0
- package/lib/public/modules/filebrowser.js +20 -1
- package/lib/public/modules/generation-stop.js +8 -5
- package/lib/public/modules/git-panel.js +3 -1
- package/lib/public/modules/icon-strip-tooltip.js +2 -1
- package/lib/public/modules/input.js +35 -1
- package/lib/public/modules/issues-render.js +3 -2
- package/lib/public/modules/knowledge-db-workbench.js +4 -4
- package/lib/public/modules/markdown.js +2 -34
- package/lib/public/modules/mate-knowledge-workbench.js +1 -1
- package/lib/public/modules/mcp-connection-view.js +1 -1
- package/lib/public/modules/message-delivery.js +10 -0
- package/lib/public/modules/pane-bridge.js +2 -2
- package/lib/public/modules/pending-message-queue.js +1 -0
- package/lib/public/modules/permission-control.js +5 -4
- package/lib/public/modules/product-review.js +26 -0
- package/lib/public/modules/project-chat-identity.js +13 -4
- package/lib/public/modules/project-logs-render.js +3 -0
- package/lib/public/modules/project-mate-navigation.js +58 -21
- package/lib/public/modules/scheduled-tasks.js +2 -0
- package/lib/public/modules/scheduler.js +2 -0
- package/lib/public/modules/search-clay-chat.js +5 -454
- package/lib/public/modules/session-create-form.js +10 -10
- package/lib/public/modules/settings-defaults.js +27 -28
- package/lib/public/modules/shared-browser.js +2 -0
- package/lib/public/modules/sidebar-mobile.js +4 -3
- package/lib/public/modules/sidebar-projects.js +1 -0
- package/lib/public/modules/sidebar-sessions.js +1 -0
- package/lib/public/modules/split-view.js +7 -16
- package/lib/public/modules/terminal.js +9 -0
- package/lib/public/modules/tools.js +6 -2
- package/lib/public/modules/tui-grab.js +1 -2
- package/lib/public/modules/vendor-login-browser.js +5 -3
- package/lib/public/modules/vendor-login.js +2 -2
- package/lib/public/modules/vendor-priority.js +15 -1
- package/lib/public/modules/wireframe-workbench.js +5 -2
- package/lib/public/muse-avatar.svg +4 -0
- package/lib/public/style.css +2 -0
- package/lib/safe-bash-commands.js +3 -45
- package/lib/sdk-bridge.js +131 -395
- package/lib/sdk-foreground-admission.js +16 -0
- package/lib/sdk-mcp-descriptors.js +2 -37
- package/lib/sdk-message-processor.js +50 -17
- package/lib/sdk-quota-snapshot.js +1 -1
- package/lib/sdk-response-recovery.js +1 -155
- package/lib/sdk-worker.js +2 -677
- package/lib/server-contextual-clay-settings.js +40 -0
- package/lib/server-contextual-mate-sessions.js +70 -0
- package/lib/server-home-chat.js +7 -1
- package/lib/server-home-clay-entry.js +23 -4
- package/lib/server-home-clay-session-links.js +12 -0
- package/lib/server-owner-ui.js +49 -8
- package/lib/server-settings.js +2 -0
- package/lib/server.js +18 -1
- package/lib/session-fork-history.js +21 -0
- package/lib/session-hygiene.js +3 -35
- package/lib/session-permission-mode.js +4 -3
- package/lib/session-skill-scope.js +11 -0
- package/lib/sessions.js +24 -89
- package/lib/terminal-manager.js +18 -11
- package/lib/tool-llm.js +1 -1
- package/lib/tui-transcript-index.js +1 -132
- package/lib/users-contextual-clay-preferences.js +31 -0
- package/lib/users.js +3 -0
- package/lib/vendor-login-browser.js +1 -11
- package/lib/workspace-assignment-service.js +25 -0
- package/lib/workspace-query-mcp-server.js +1 -1
- package/lib/ws-request.js +1 -0
- package/lib/ws-schema.js +10 -0
- package/lib/yoke/acp-agent-profiles.js +5 -3
- package/lib/yoke/adapters/antigravity.js +3 -14
- package/lib/yoke/adapters/claude-query-contract.js +9 -0
- package/lib/yoke/adapters/claude.js +7 -42
- package/lib/yoke/adapters/codex-query-contract.js +24 -0
- package/lib/yoke/adapters/codex.js +1 -0
- package/lib/yoke/adapters/kiro.js +3 -1
- package/lib/yoke/adapters/muse-query-contract.js +7 -0
- package/lib/yoke/adapters/muse.js +122 -0
- package/lib/yoke/auth-events.js +17 -0
- package/lib/yoke/claude-hook-installer.js +271 -0
- package/lib/yoke/claude-jsonl-watcher.js +204 -0
- package/lib/yoke/claude-maintenance.js +55 -0
- package/lib/yoke/claude-permission-patterns.js +53 -0
- package/lib/yoke/claude-response-recovery.js +155 -0
- package/lib/yoke/claude-transcript-hygiene.js +34 -0
- package/lib/yoke/clay-skill-install.js +33 -0
- package/lib/yoke/cli-sessions.js +300 -0
- package/lib/yoke/codex-app-server.js +7 -12
- package/lib/yoke/configuration.js +13 -0
- package/lib/yoke/discovery.js +70 -0
- package/lib/yoke/errors.js +8 -0
- package/lib/yoke/host-environment.js +7 -0
- package/lib/yoke/index.js +9 -51
- package/lib/yoke/interface.js +116 -0
- package/lib/yoke/kiro-acp-server.js +5 -5
- package/lib/yoke/legacy-sessions.js +89 -0
- package/lib/yoke/login-protocol.js +25 -0
- package/lib/yoke/maintenance.js +12 -0
- package/lib/yoke/mcp-proxy.js +32 -0
- package/lib/yoke/mcp-source-adapters.js +91 -0
- package/lib/yoke/mcp-source-context.js +42 -0
- package/lib/yoke/mcp-source-discovery.js +169 -0
- package/lib/yoke/mcp-source-plugins.js +68 -0
- package/lib/yoke/mcp-source-restrictions.js +47 -0
- package/lib/yoke/mcp-tool-policy-normalize.js +28 -0
- package/lib/yoke/model-preferences.js +6 -0
- package/lib/yoke/muse-artifacts.js +36 -0
- package/lib/yoke/muse-controls.js +50 -0
- package/lib/yoke/muse-input.js +64 -0
- package/lib/yoke/muse-items.js +157 -0
- package/lib/yoke/muse-policy.js +52 -0
- package/lib/yoke/muse-query.js +428 -0
- package/lib/yoke/muse-runtime.js +211 -0
- package/lib/yoke/muse-services.js +79 -0
- package/lib/yoke/muse-skills.js +79 -0
- package/lib/yoke/muse-turns.js +71 -0
- package/lib/yoke/options/acp.js +2 -0
- package/lib/yoke/options/antigravity.js +2 -0
- package/lib/yoke/options/claude.js +21 -0
- package/lib/yoke/options/codex-defaults.js +29 -0
- package/lib/yoke/options/codex-settings.js +21 -0
- package/lib/yoke/options/codex.js +6 -0
- package/lib/yoke/options/kiro-defaults.js +22 -0
- package/lib/yoke/options/kiro.js +5 -0
- package/lib/yoke/options/muse.js +5 -0
- package/lib/yoke/provider-contract.js +5 -0
- package/lib/yoke/providers/antigravity-executable.js +8 -0
- package/lib/yoke/providers/claude-executable.js +40 -0
- package/lib/yoke/providers/claude-model-descriptions.js +14 -0
- package/lib/yoke/providers/claude-process.js +97 -0
- package/lib/yoke/providers/claude-provision.js +75 -0
- package/lib/yoke/providers/codex-errors.js +8 -0
- package/lib/yoke/providers/muse-executable.js +17 -0
- package/lib/yoke/provisioning.js +6 -0
- package/lib/yoke/query-options.js +10 -0
- package/lib/yoke/response-recovery.js +41 -0
- package/lib/yoke/sdk-mcp-descriptors.js +37 -0
- package/lib/yoke/sdk-server-factory.js +7 -0
- package/lib/yoke/session-history.js +8 -0
- package/lib/yoke/session-tool-transport.js +31 -13
- package/lib/yoke/settings.js +23 -0
- package/lib/yoke/source-locations.js +4 -0
- package/lib/yoke/title-query.js +18 -7
- package/lib/yoke/tui-transcript-index.js +132 -0
- package/lib/yoke/usage.js +48 -0
- package/lib/yoke/user-input.js +13 -0
- package/lib/yoke/vendor-registry.js +81 -1
- package/package.json +3 -2
|
@@ -9,8 +9,9 @@ var execFile = require("child_process").execFile;
|
|
|
9
9
|
var execFileSync = require("child_process").execFileSync;
|
|
10
10
|
|
|
11
11
|
function findOnPath(binaryName, overrideName) {
|
|
12
|
-
var
|
|
13
|
-
|
|
12
|
+
var discovery = require("./discovery");
|
|
13
|
+
var overridePath = overrideName && discovery.explicitEntry(process.env, overrideName, false);
|
|
14
|
+
if (overridePath !== undefined && overrideName) return overridePath;
|
|
14
15
|
try {
|
|
15
16
|
var command = process.platform === "win32" ? "where" : "which";
|
|
16
17
|
var out = execFileSync(command, [binaryName], {
|
|
@@ -18,7 +19,8 @@ function findOnPath(binaryName, overrideName) {
|
|
|
18
19
|
encoding: "utf8",
|
|
19
20
|
stdio: ["pipe", "pipe", "pipe"],
|
|
20
21
|
});
|
|
21
|
-
|
|
22
|
+
var resolved = out.trim().split(/\r?\n/)[0] || null;
|
|
23
|
+
return discovery.validEntry(resolved, false) ? resolved : null;
|
|
22
24
|
} catch (e) {
|
|
23
25
|
return null;
|
|
24
26
|
}
|
|
@@ -3,19 +3,7 @@ var childProcess = require("child_process");
|
|
|
3
3
|
var readline = require("readline");
|
|
4
4
|
var skillDiscovery = require("../skill-discovery");
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
if (process.env.ANTIGRAVITY_CLI_PATH) return process.env.ANTIGRAVITY_CLI_PATH;
|
|
8
|
-
try {
|
|
9
|
-
var command = process.platform === "win32" ? "where" : "which";
|
|
10
|
-
return childProcess.execFileSync(command, ["agy"], {
|
|
11
|
-
encoding: "utf8",
|
|
12
|
-
timeout: 3000,
|
|
13
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
14
|
-
}).trim().split(/\r?\n/)[0] || null;
|
|
15
|
-
} catch (e) {
|
|
16
|
-
return null;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
6
|
+
var findBinary = require("../providers/antigravity-executable").resolve;
|
|
19
7
|
|
|
20
8
|
function modelValue(model) {
|
|
21
9
|
if (typeof model === "string") return model;
|
|
@@ -427,7 +415,8 @@ function createAntigravityAdapter(opts) {
|
|
|
427
415
|
var adapter = {
|
|
428
416
|
vendor: "antigravity",
|
|
429
417
|
init: async function(initOpts) {
|
|
430
|
-
if (
|
|
418
|
+
if (opts.linuxUser || initOpts && initOpts.linuxUser) throw new Error("Antigravity does not support mapped OS-user execution.");
|
|
419
|
+
if (!binaryPath) binaryPath = findBinary({ env: initOpts && initOpts.env || opts.env || process.env });
|
|
431
420
|
if (!binaryPath) throw new Error("Antigravity CLI binary not found: agy");
|
|
432
421
|
var fetched = opts._fetchModels ? await opts._fetchModels(binaryPath, cwd) : await fetchModels(binaryPath, cwd);
|
|
433
422
|
if (fetched && fetched.length) cachedModels = fetched;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Init-free provider transport obligations, shared by the registry and adapter.
|
|
2
|
+
module.exports = {
|
|
3
|
+
questionInput: function(name, input) { return name === "AskUserQuestion" ? input : null; },
|
|
4
|
+
toolTransport: "sdk-mcp",
|
|
5
|
+
catalogLifetime: "query",
|
|
6
|
+
permissionModes: ["auto"],
|
|
7
|
+
mcpPermissionOverrides: true,
|
|
8
|
+
interruptionMessage: "Interrupted · What should Claude do instead?",
|
|
9
|
+
};
|
|
@@ -528,7 +528,7 @@ function flattenEvent(raw) {
|
|
|
528
528
|
// --- rate_limit_event ---
|
|
529
529
|
if (raw.type === "rate_limit_event" && raw.rate_limit_info) {
|
|
530
530
|
base.yokeType = "rate_limit";
|
|
531
|
-
base.rateLimitInfo = raw.rate_limit_info;
|
|
531
|
+
base.rateLimitInfo = Object.assign({}, raw.rate_limit_info, { isUsingOverage: raw.rate_limit_info.isUsingOverage === true || raw.rate_limit_info.overageInUse === true });
|
|
532
532
|
return base;
|
|
533
533
|
}
|
|
534
534
|
|
|
@@ -1543,43 +1543,7 @@ function createWorkerQueryHandle(worker, canUseTool, onElicitation, callMcpTool,
|
|
|
1543
1543
|
|
|
1544
1544
|
// --- Adapter factory ---
|
|
1545
1545
|
|
|
1546
|
-
|
|
1547
|
-
// 1. Explicit env var override
|
|
1548
|
-
if (process.env.CLAUDE_CODE_PATH && fs.existsSync(process.env.CLAUDE_CODE_PATH)) {
|
|
1549
|
-
return process.env.CLAUDE_CODE_PATH;
|
|
1550
|
-
}
|
|
1551
|
-
|
|
1552
|
-
// 2. `which claude` in the daemon's PATH
|
|
1553
|
-
try {
|
|
1554
|
-
var result = require("child_process").execSync("which claude", { encoding: "utf8", timeout: 5000 }).trim();
|
|
1555
|
-
if (result && fs.existsSync(result)) return result;
|
|
1556
|
-
} catch (e) {}
|
|
1557
|
-
|
|
1558
|
-
// 3. Common per-user and system locations (best-effort fallback for the daemon user)
|
|
1559
|
-
var home = process.env.HOME || "";
|
|
1560
|
-
var candidates = [];
|
|
1561
|
-
if (home) {
|
|
1562
|
-
candidates.push(home + "/.npm-global/bin/claude");
|
|
1563
|
-
candidates.push(home + "/.local/bin/claude");
|
|
1564
|
-
candidates.push(home + "/.volta/bin/claude");
|
|
1565
|
-
candidates.push(home + "/.bun/bin/claude");
|
|
1566
|
-
candidates.push(home + "/bin/claude");
|
|
1567
|
-
}
|
|
1568
|
-
candidates.push("/usr/local/bin/claude");
|
|
1569
|
-
candidates.push("/usr/bin/claude");
|
|
1570
|
-
candidates.push("/opt/homebrew/bin/claude");
|
|
1571
|
-
for (var i = 0; i < candidates.length; i++) {
|
|
1572
|
-
try { if (fs.existsSync(candidates[i])) return candidates[i]; } catch (e) {}
|
|
1573
|
-
}
|
|
1574
|
-
|
|
1575
|
-
// 4. Bundled CLI entry from the SDK's peer
|
|
1576
|
-
try {
|
|
1577
|
-
var resolved = require.resolve("@anthropic-ai/claude-code/cli.js");
|
|
1578
|
-
if (resolved && fs.existsSync(resolved)) return resolved;
|
|
1579
|
-
} catch (e) {}
|
|
1580
|
-
|
|
1581
|
-
return null;
|
|
1582
|
-
}
|
|
1546
|
+
var resolveClaudeBinaryPath = require("../providers/claude-executable").resolve;
|
|
1583
1547
|
|
|
1584
1548
|
function createClaudeAdapter(opts) {
|
|
1585
1549
|
var _cwd = (opts && opts.cwd) || process.cwd();
|
|
@@ -1592,6 +1556,7 @@ function createClaudeAdapter(opts) {
|
|
|
1592
1556
|
|
|
1593
1557
|
var adapter = {
|
|
1594
1558
|
vendor: "claude",
|
|
1559
|
+
queryContract: require("./claude-query-contract"),
|
|
1595
1560
|
userInputCapability: { mode: "native", native: true, transport: "AskUserQuestion/onElicitation" },
|
|
1596
1561
|
|
|
1597
1562
|
// Path to worker script (sdk-bridge uses this to spawn worker processes)
|
|
@@ -1678,7 +1643,7 @@ function createClaudeAdapter(opts) {
|
|
|
1678
1643
|
|
|
1679
1644
|
try {
|
|
1680
1645
|
var models = await stream.supportedModels();
|
|
1681
|
-
result.models = models
|
|
1646
|
+
result.models = require("../providers/claude-model-descriptions").list(models);
|
|
1682
1647
|
_cachedModels = result.models;
|
|
1683
1648
|
} catch (e) {
|
|
1684
1649
|
// supportedModels may fail, models list will be empty
|
|
@@ -1993,7 +1958,7 @@ function createClaudeAdapter(opts) {
|
|
|
1993
1958
|
warmupDone = true;
|
|
1994
1959
|
var r = msg.result || {};
|
|
1995
1960
|
resolve({
|
|
1996
|
-
models: r.models
|
|
1961
|
+
models: require("../providers/claude-model-descriptions").list(r.models),
|
|
1997
1962
|
defaultModel: r.model || "",
|
|
1998
1963
|
skills: r.skills || [],
|
|
1999
1964
|
slashCommands: r.slashCommands || [],
|
|
@@ -2110,7 +2075,7 @@ function createClaudeAdapter(opts) {
|
|
|
2110
2075
|
|
|
2111
2076
|
try {
|
|
2112
2077
|
var models = await stream.supportedModels();
|
|
2113
|
-
result.models = models
|
|
2078
|
+
result.models = require("../providers/claude-model-descriptions").list(models);
|
|
2114
2079
|
_cachedModels = result.models;
|
|
2115
2080
|
} catch (e) {
|
|
2116
2081
|
// supportedModels may fail, models list will be empty
|
|
@@ -2161,7 +2126,7 @@ function createClaudeAdapter(opts) {
|
|
|
2161
2126
|
warmupDone = true;
|
|
2162
2127
|
var r = msg.result || {};
|
|
2163
2128
|
resolve({
|
|
2164
|
-
models: r.models
|
|
2129
|
+
models: require("../providers/claude-model-descriptions").list(r.models),
|
|
2165
2130
|
defaultModel: r.model || "",
|
|
2166
2131
|
skills: r.skills || [],
|
|
2167
2132
|
slashCommands: r.slashCommands || [],
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Codex restores dynamic definitions from the original thread catalog.
|
|
2
|
+
// Keep persisted legacy field names here so old conversations remain readable.
|
|
3
|
+
var fields = {
|
|
4
|
+
schedules: "codexDynamicToolCatalogVersion",
|
|
5
|
+
questions: "codexUserInputToolCatalogVersion",
|
|
6
|
+
browser: "codexSharedBrowserToolCatalogVersion",
|
|
7
|
+
issues: "codexIssuesToolCatalogVersion",
|
|
8
|
+
knowledge: "codexYouToolCatalogVersion",
|
|
9
|
+
linear: "codexLinearToolCatalogVersion",
|
|
10
|
+
};
|
|
11
|
+
module.exports = {
|
|
12
|
+
toolTransport: "dynamic",
|
|
13
|
+
catalogLifetime: "conversation",
|
|
14
|
+
externalToolTransport: "dynamic",
|
|
15
|
+
defaultUserInputMode: "fallback",
|
|
16
|
+
questionGuidance: require("../user-input-guidance").codexQuestionGuidance,
|
|
17
|
+
catalogVersion: function (session, family) { return Number(session[fields[family]] || 0); },
|
|
18
|
+
recordCatalog: function (session, versions) {
|
|
19
|
+
Object.keys(versions).forEach(function (family) {
|
|
20
|
+
if (fields[family]) session[fields[family]] = versions[family];
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
staleCatalogReason: "This Codex conversation predates Clay's scheduled-task tool catalog. Start a new conversation to use the assisted interview; manual task creation remains available here.",
|
|
24
|
+
};
|
|
@@ -1682,6 +1682,7 @@ function createCodexAdapter(opts) {
|
|
|
1682
1682
|
var adapter = {
|
|
1683
1683
|
vendor: "codex",
|
|
1684
1684
|
queryBoundToolCapability: CODEX_QUERY_BOUND_TOOL_CAPABILITY,
|
|
1685
|
+
queryContract: require("./codex-query-contract"),
|
|
1685
1686
|
userInputCapability: { mode: "native", native: true, transport: "item/tool/requestUserInput" },
|
|
1686
1687
|
|
|
1687
1688
|
init: function(initOpts) {
|
|
@@ -279,7 +279,7 @@ function flattenUpdate(update, state) {
|
|
|
279
279
|
|
|
280
280
|
// Token usage
|
|
281
281
|
if (type === "usage_update") {
|
|
282
|
-
if (typeof update.used === "number") state.lastInputTokens = update.used;
|
|
282
|
+
if (typeof update.used === "number") { state.lastInputTokens = update.used; state.contextMeasurement = "observed"; }
|
|
283
283
|
if (typeof update.size === "number") state.contextWindow = update.size;
|
|
284
284
|
return events;
|
|
285
285
|
}
|
|
@@ -294,6 +294,7 @@ function flattenUpdate(update, state) {
|
|
|
294
294
|
if (typeof percentage !== "number") percentage = kiroMeta.usagePercentage;
|
|
295
295
|
if (typeof percentage === "number" && state.contextWindow) {
|
|
296
296
|
state.lastInputTokens = Math.round(state.contextWindow * percentage / 100);
|
|
297
|
+
state.contextMeasurement = "estimated";
|
|
297
298
|
}
|
|
298
299
|
}
|
|
299
300
|
return events;
|
|
@@ -782,6 +783,7 @@ function createKiroQueryHandle(acp, queryOpts) {
|
|
|
782
783
|
getContextUsage: function() {
|
|
783
784
|
return Promise.resolve(state.lastInputTokens != null ? {
|
|
784
785
|
input_tokens: state.lastInputTokens,
|
|
786
|
+
measurement: state.contextMeasurement || "observed",
|
|
785
787
|
contextWindow: state.contextWindow || null,
|
|
786
788
|
} : null);
|
|
787
789
|
},
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
var museQuery = require("../muse-query");
|
|
2
|
+
var runtimeTools = require("../muse-runtime");
|
|
3
|
+
var findMusePath = require("../providers/muse-executable").resolve;
|
|
4
|
+
|
|
5
|
+
function createMuseAdapter(opts) {
|
|
6
|
+
opts = opts || {};
|
|
7
|
+
var adapter = {
|
|
8
|
+
vendor: "muse",
|
|
9
|
+
userInputCapability: { mode: "native", native: true },
|
|
10
|
+
providerContract: require("../vendor-registry").getVendorInfo("muse"),
|
|
11
|
+
queryContract: require("./muse-query-contract"),
|
|
12
|
+
cwd: opts.cwd || process.cwd(),
|
|
13
|
+
binaryPath: opts._binaryPath || null,
|
|
14
|
+
sdkPromise: null,
|
|
15
|
+
catalog: [],
|
|
16
|
+
clients: new Set(),
|
|
17
|
+
handles: new Set(),
|
|
18
|
+
shuttingDown: false,
|
|
19
|
+
requestTimeoutMs: opts._requestTimeoutMs || 9e4,
|
|
20
|
+
titleTimeoutMs: opts._titleTimeoutMs || 15e3,
|
|
21
|
+
shutdownTimeoutMs: opts._shutdownTimeoutMs == null ? 1e3 : opts._shutdownTimeoutMs
|
|
22
|
+
};
|
|
23
|
+
adapter.loadSdk = function() {
|
|
24
|
+
if (opts._sdk) return Promise.resolve(opts._sdk);
|
|
25
|
+
if (!adapter.sdkPromise) adapter.sdkPromise = import("@muse-code/sdk");
|
|
26
|
+
return adapter.sdkPromise;
|
|
27
|
+
};
|
|
28
|
+
adapter.init = async function(initOpts) {
|
|
29
|
+
if (opts.osUsers || opts.linuxUser || initOpts && (initOpts.linuxUser || initOpts.osUsers)) throw new Error("Muse Code does not support OS-owner isolation");
|
|
30
|
+
if (!adapter.binaryPath) adapter.binaryPath = findMusePath();
|
|
31
|
+
if (!adapter.binaryPath) throw new Error("Muse Code binary not found: muse. Install Muse Code or set MUSE_CODE_PATH.");
|
|
32
|
+
await adapter.loadSdk();
|
|
33
|
+
return {
|
|
34
|
+
models: [],
|
|
35
|
+
defaultModel: null,
|
|
36
|
+
skills: [],
|
|
37
|
+
slashCommands: [],
|
|
38
|
+
fastModeState: null,
|
|
39
|
+
capabilities: {
|
|
40
|
+
effort: true,
|
|
41
|
+
midSessionModelSwitch: true,
|
|
42
|
+
fork: true,
|
|
43
|
+
rollback: false,
|
|
44
|
+
sessionListing: true,
|
|
45
|
+
sessionRename: true,
|
|
46
|
+
thinking: true,
|
|
47
|
+
betas: false,
|
|
48
|
+
rewind: false,
|
|
49
|
+
sessionResume: true,
|
|
50
|
+
promptSuggestions: false,
|
|
51
|
+
elicitation: false,
|
|
52
|
+
fileCheckpointing: false,
|
|
53
|
+
contextCompacting: true,
|
|
54
|
+
queryBoundTools: {
|
|
55
|
+
mode: "stdio-mcp",
|
|
56
|
+
source: "@muse-code/sdk SessionConfig.mcpServers"
|
|
57
|
+
},
|
|
58
|
+
skillSharing: true,
|
|
59
|
+
toolPolicy: [ "ask", "allow-all" ]
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
adapter.supportedModels = async function() {
|
|
64
|
+
if (adapter.shuttingDown) throw new Error("Muse Code adapter is shutting down");
|
|
65
|
+
if (!adapter.binaryPath) await adapter.init();
|
|
66
|
+
var sdk = await adapter.loadSdk();
|
|
67
|
+
if (adapter.shuttingDown) throw new Error("Muse Code adapter is shutting down");
|
|
68
|
+
var runtime = runtimeTools.createRuntime(adapter, sdk, {
|
|
69
|
+
command: adapter.binaryPath,
|
|
70
|
+
args: [ "serve" ],
|
|
71
|
+
cwd: adapter.cwd,
|
|
72
|
+
env: Object.assign({}, process.env)
|
|
73
|
+
});
|
|
74
|
+
runtime.fail = function(error) {
|
|
75
|
+
runtime.close().catch(function(closeError) {
|
|
76
|
+
adapter.lastCloseError = closeError;
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
try {
|
|
80
|
+
var spawned = await runtime.initialize();
|
|
81
|
+
adapter.catalog = runtimeTools.normalizeCatalog(await runtime.waitRequest(spawned.connection.request("model/list", {})));
|
|
82
|
+
return adapter.catalog.slice();
|
|
83
|
+
} finally {
|
|
84
|
+
await runtime.close();
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
adapter.createToolServer = function() {
|
|
88
|
+
return null;
|
|
89
|
+
};
|
|
90
|
+
adapter.createQuery = async function(queryOpts) {
|
|
91
|
+
if (opts.osUsers || opts.linuxUser || queryOpts && queryOpts.linuxUser) throw new Error("Muse Code does not support OS-owner isolation");
|
|
92
|
+
if (!adapter.binaryPath) await adapter.init(queryOpts || {});
|
|
93
|
+
if (adapter.shuttingDown) throw new Error("Muse Code adapter is shutting down");
|
|
94
|
+
return museQuery.createMuseQueryHandle(adapter, queryOpts || {});
|
|
95
|
+
};
|
|
96
|
+
adapter.generateTitle = function(messages, titleOpts) {
|
|
97
|
+
return require("../title-query").generateTitle(adapter, messages, Object.assign({}, titleOpts, { timeoutMs: adapter.titleTimeoutMs }),
|
|
98
|
+
{ skipProjectInstructions: true, skipSkills: true, adapterOptions: { MUSE: { readOnly: true, mcpServers: [] } } });
|
|
99
|
+
};
|
|
100
|
+
adapter.shutdown = async function() {
|
|
101
|
+
adapter.shuttingDown = true;
|
|
102
|
+
var results = await Promise.allSettled(Array.from(adapter.handles).map(function(handle) {
|
|
103
|
+
return handle.close();
|
|
104
|
+
}).concat(Array.from(adapter.clients).map(function(runtime) {
|
|
105
|
+
return runtime.close();
|
|
106
|
+
})));
|
|
107
|
+
var failed = results.find(function(result) {
|
|
108
|
+
return result.status === "rejected";
|
|
109
|
+
});
|
|
110
|
+
if (failed) throw failed.reason;
|
|
111
|
+
return true;
|
|
112
|
+
};
|
|
113
|
+
require("../muse-services").attach(adapter);
|
|
114
|
+
return adapter;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
module.exports = {
|
|
118
|
+
createMuseAdapter: createMuseAdapter,
|
|
119
|
+
findMusePath: findMusePath,
|
|
120
|
+
isAuthError: runtimeTools.isAuthError,
|
|
121
|
+
chooseApprovalDecision: require("../muse-policy").chooseApprovalDecision
|
|
122
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
function loginPrompt(event, preview, provider) {
|
|
2
|
+
if (event.cost && event.cost !== 0 || String(preview || '').length >= 100) return false;
|
|
3
|
+
var text = String(preview || '').trim();
|
|
4
|
+
if (/not logged in/i.test(text) && /\/login/i.test(text)) return true;
|
|
5
|
+
var policy = require('./provider-contract').resolve(provider);
|
|
6
|
+
return typeof policy.authenticationReply === 'function' && policy.authenticationReply(text);
|
|
7
|
+
}
|
|
8
|
+
function authenticationReply(text) { return /^Failed to authenticate: OAuth session expired and could not be refreshed\.?$/i.test(text); }
|
|
9
|
+
function isAuthenticationError(detail) {
|
|
10
|
+
var text = String(detail || '').toLowerCase();
|
|
11
|
+
return /not logged in|unauthenticated|authentication|sign in|log in|please login/.test(text);
|
|
12
|
+
}
|
|
13
|
+
function normalize(event, preview, provider) {
|
|
14
|
+
if (!event || event.yokeType !== "result") return event;
|
|
15
|
+
return Object.assign({}, event, { authenticationRequired: event.authenticationRequired === true || loginPrompt(event, preview, provider) });
|
|
16
|
+
}
|
|
17
|
+
module.exports = { normalize: normalize, loginPrompt: loginPrompt, authenticationReply: authenticationReply, isAuthenticationError: isAuthenticationError };
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
// claude-hook-installer.js
|
|
2
|
+
//
|
|
3
|
+
// Registers Clay's notification webhook in `~/.claude/settings.json` so the
|
|
4
|
+
// `claude` CLI fires a request to the Clay daemon whenever it surfaces a
|
|
5
|
+
// Notification (permission requests, idle prompts, etc.). This is what makes
|
|
6
|
+
// TUI sessions feel like first-class Clay sessions: the user gets the same
|
|
7
|
+
// toasts and push notifications they would in GUI mode.
|
|
8
|
+
//
|
|
9
|
+
// Design notes:
|
|
10
|
+
// - Settings merge is idempotent: existing hooks are preserved, ours are
|
|
11
|
+
// identified by a marker substring so re-runs don't duplicate them.
|
|
12
|
+
// - The hook command uses curl with --max-time / --connect-timeout so that
|
|
13
|
+
// a downed daemon can't stall claude's UI for more than ~1s.
|
|
14
|
+
// - Multi-user mode: caller passes one home directory per OS user; we
|
|
15
|
+
// write each user's own settings.json so per-user filtering works.
|
|
16
|
+
|
|
17
|
+
var fs = require("fs");
|
|
18
|
+
var path = require("path");
|
|
19
|
+
var { buildClayBashAllowPatterns, isClayManagedBashPattern } = require("../safe-bash-commands");
|
|
20
|
+
|
|
21
|
+
// Substring that uniquely identifies a Clay-installed hook entry so we can
|
|
22
|
+
// safely remove/replace it without touching user-authored hooks.
|
|
23
|
+
var CLAY_HOOK_MARKER = "clay:tui-notify";
|
|
24
|
+
|
|
25
|
+
// Allow-patterns Clay manages in ~/.claude/settings.json `permissions.allow`.
|
|
26
|
+
// The Bash(...) patterns are GENERATED from safe-bash-commands.js so this
|
|
27
|
+
// list and sdk-bridge.js `checkToolWhitelist` can never drift - there is one
|
|
28
|
+
// source of truth for which bash commands Clay auto-approves.
|
|
29
|
+
//
|
|
30
|
+
// Pattern syntax note: claude 2.x uses the space-wildcard form `Bash(cmd *)`
|
|
31
|
+
// for prefix matching. The older colon form `Bash(cmd:*)` is flagged as
|
|
32
|
+
// legacy in the CLI and no longer reliably matches argument-bearing commands
|
|
33
|
+
// (the TUI kept prompting for read-only invocations like `ls -la`). The
|
|
34
|
+
// generator emits the modern form plus a bare `Bash(cmd)` for zero-arg use.
|
|
35
|
+
//
|
|
36
|
+
// User-authored entries are preserved -- on re-install we only strip patterns
|
|
37
|
+
// that are Clay-managed (current generated list or the legacy colon shape).
|
|
38
|
+
var CLAY_MANAGED_ALLOW = [
|
|
39
|
+
// Read-only built-in tools (no side effects).
|
|
40
|
+
"Read", "Glob", "Grep", "WebFetch", "WebSearch",
|
|
41
|
+
|
|
42
|
+
// Clay's own MCP servers. Strictly read/safe by design.
|
|
43
|
+
"mcp__clay-browser__browser_watch_tab",
|
|
44
|
+
"mcp__clay-browser__browser_unwatch_tab",
|
|
45
|
+
"mcp__clay-debate__propose_debate",
|
|
46
|
+
"mcp__clay-mate-creation__propose_mate",
|
|
47
|
+
"mcp__clay-history__*",
|
|
48
|
+
"mcp__clay-workspace__read_current_context",
|
|
49
|
+
"mcp__clay-workspace__read_selected_context",
|
|
50
|
+
"mcp__clay-workspace__open_context_conversation",
|
|
51
|
+
"mcp__clay-workspace__read_context_navigation_receipt",
|
|
52
|
+
"mcp__clay-workspace__read_owner_screen",
|
|
53
|
+
"mcp__clay-workspace__inspect_owner_screen_change",
|
|
54
|
+
"mcp__clay-workspace__control_owner_screen",
|
|
55
|
+
"mcp__clay-workspace__inspect_project_control",
|
|
56
|
+
"mcp__clay-workspace__control_project_lifecycle",
|
|
57
|
+
"mcp__clay-workspace__discover_product_controls",
|
|
58
|
+
"mcp__clay-workspace__list_owned_mates",
|
|
59
|
+
"mcp__clay-workspace__begin_schedule_control_interview",
|
|
60
|
+
"mcp__clay-workspace__read_schedule_controls",
|
|
61
|
+
"mcp__clay-workspace__inspect_schedule_change",
|
|
62
|
+
"mcp__clay-workspace__control_schedules",
|
|
63
|
+
"mcp__clay-workspace__read_mate_chat_control",
|
|
64
|
+
"mcp__clay-workspace__inspect_mate_chat_change",
|
|
65
|
+
"mcp__clay-workspace__control_mate_chat",
|
|
66
|
+
"mcp__clay-workspace__read_project_workspace",
|
|
67
|
+
"mcp__clay-workspace__read_workspace_receipt",
|
|
68
|
+
"mcp__clay-workspace__inspect_workspace_change",
|
|
69
|
+
"mcp__clay-workspace__control_project_workspace",
|
|
70
|
+
"mcp__clay-workspace__read_project_records",
|
|
71
|
+
"mcp__clay-workspace__inspect_project_record_change",
|
|
72
|
+
"mcp__clay-workspace__control_project_records",
|
|
73
|
+
"mcp__clay-workspace__read_mate_knowledge_control",
|
|
74
|
+
"mcp__clay-workspace__inspect_mate_knowledge_change",
|
|
75
|
+
"mcp__clay-workspace__control_mate_knowledge",
|
|
76
|
+
"mcp__clay-workspace__inspect_mate_control",
|
|
77
|
+
"mcp__clay-workspace__control_mate_lifecycle",
|
|
78
|
+
"mcp__clay-workspace__inspect_session_control",
|
|
79
|
+
"mcp__clay-workspace__control_project_session",
|
|
80
|
+
"mcp__clay-workspace__list_projects",
|
|
81
|
+
"mcp__clay-workspace__list_project_sessions",
|
|
82
|
+
"mcp__clay-workspace__search_project_history",
|
|
83
|
+
"mcp__clay-workspace__read_project_session",
|
|
84
|
+
"mcp__clay-workspace__search_workspace_history",
|
|
85
|
+
"mcp__clay-workspace__list_workspace_activity",
|
|
86
|
+
"mcp__clay-workspace__propose_project_assignment",
|
|
87
|
+
"mcp__clay-workspace__propose_project_follow_up",
|
|
88
|
+
"mcp__clay-workspace__get_assignment_status",
|
|
89
|
+
"mcp__clay-tools__clay_tool_list",
|
|
90
|
+
"mcp__clay-tools__clay_tool_snapshot",
|
|
91
|
+
"mcp__clay-tools__clay_tool_act",
|
|
92
|
+
"mcp__clay-tools__clay_tool_set",
|
|
93
|
+
"mcp__clay-tools__clay_tool_source",
|
|
94
|
+
// Email: read-side only. Send / reply / mark_read still prompt.
|
|
95
|
+
"mcp__clay-email__clay_read_email",
|
|
96
|
+
"mcp__clay-email__clay_read_email_body",
|
|
97
|
+
"mcp__clay-email__clay_search_email",
|
|
98
|
+
"mcp__clay-email__clay_list_labels",
|
|
99
|
+
].concat(buildClayBashAllowPatterns());
|
|
100
|
+
|
|
101
|
+
function buildHookCommand(notifyUrl) {
|
|
102
|
+
// Read stdin once (the JSON Claude Code pipes in), then post it. --max-time
|
|
103
|
+
// and --connect-timeout cap total hook latency around ~1s even if the
|
|
104
|
+
// daemon is down. --insecure (-k) is needed because Clay's optional TLS
|
|
105
|
+
// mode uses a locally generated CA that curl won't trust by default;
|
|
106
|
+
// safe to skip verification since we're going to 127.0.0.1. Output is
|
|
107
|
+
// silenced so it doesn't leak into the TUI. The marker comment keeps the
|
|
108
|
+
// entry recognizable when we re-merge.
|
|
109
|
+
var insecure = notifyUrl.indexOf("https://") === 0 ? " --insecure" : "";
|
|
110
|
+
return "curl --silent --show-error --max-time 1 --connect-timeout 1" + insecure +
|
|
111
|
+
" -X POST -H 'Content-Type: application/json' --data-binary @- " +
|
|
112
|
+
JSON.stringify(notifyUrl) + " > /dev/null 2>&1 # " + CLAY_HOOK_MARKER;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function readSettings(settingsPath) {
|
|
116
|
+
try {
|
|
117
|
+
var raw = fs.readFileSync(settingsPath, "utf8");
|
|
118
|
+
return JSON.parse(raw);
|
|
119
|
+
} catch (e) {
|
|
120
|
+
return {};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function writeSettings(settingsPath, data) {
|
|
125
|
+
var dir = path.dirname(settingsPath);
|
|
126
|
+
try { fs.mkdirSync(dir, { recursive: true }); } catch (e) {}
|
|
127
|
+
fs.writeFileSync(settingsPath, JSON.stringify(data, null, 2) + "\n");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Merge our Notification hook into the user's settings.json without
|
|
131
|
+
// clobbering anything else. Returns true if the file changed.
|
|
132
|
+
function mergeHook(settingsPath, command) {
|
|
133
|
+
var data = readSettings(settingsPath);
|
|
134
|
+
if (!data.hooks || typeof data.hooks !== "object") data.hooks = {};
|
|
135
|
+
if (!Array.isArray(data.hooks.Notification)) data.hooks.Notification = [];
|
|
136
|
+
|
|
137
|
+
// Strip any prior Clay entries before inserting the fresh one so port /
|
|
138
|
+
// URL changes propagate cleanly.
|
|
139
|
+
var before = JSON.stringify(data.hooks.Notification);
|
|
140
|
+
var cleaned = [];
|
|
141
|
+
for (var i = 0; i < data.hooks.Notification.length; i++) {
|
|
142
|
+
var group = data.hooks.Notification[i];
|
|
143
|
+
if (!group || !Array.isArray(group.hooks)) { cleaned.push(group); continue; }
|
|
144
|
+
var filtered = group.hooks.filter(function (h) {
|
|
145
|
+
return !(h && typeof h.command === "string" && h.command.indexOf(CLAY_HOOK_MARKER) !== -1);
|
|
146
|
+
});
|
|
147
|
+
if (filtered.length > 0) cleaned.push({ matcher: group.matcher || "", hooks: filtered });
|
|
148
|
+
else if (group.matcher) cleaned.push(group);
|
|
149
|
+
}
|
|
150
|
+
data.hooks.Notification = cleaned;
|
|
151
|
+
|
|
152
|
+
data.hooks.Notification.push({
|
|
153
|
+
matcher: "",
|
|
154
|
+
hooks: [{ type: "command", command: command }],
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
var after = JSON.stringify(data.hooks.Notification);
|
|
158
|
+
if (before === after) return false;
|
|
159
|
+
writeSettings(settingsPath, data);
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Install (or refresh) the Notification hook in one or more user home dirs.
|
|
165
|
+
*
|
|
166
|
+
* opts:
|
|
167
|
+
* notifyUrl -- full URL Clay listens on, e.g. "http://127.0.0.1:2633/api/tui-notify".
|
|
168
|
+
* homeDirs -- array of absolute paths to user home directories. Each one
|
|
169
|
+
* gets its own settings.json updated. Defaults to [os.homedir()].
|
|
170
|
+
*
|
|
171
|
+
* Returns { installed: [paths], errors: [{path, error}] } so the caller can log.
|
|
172
|
+
*/
|
|
173
|
+
function installNotificationHook(opts) {
|
|
174
|
+
opts = opts || {};
|
|
175
|
+
var os = require("os");
|
|
176
|
+
var homes = opts.homeDirs && opts.homeDirs.length ? opts.homeDirs : [os.homedir()];
|
|
177
|
+
var notifyUrl = opts.notifyUrl;
|
|
178
|
+
if (!notifyUrl) throw new Error("installNotificationHook: notifyUrl is required");
|
|
179
|
+
|
|
180
|
+
var command = buildHookCommand(notifyUrl);
|
|
181
|
+
var installed = [];
|
|
182
|
+
var errors = [];
|
|
183
|
+
for (var i = 0; i < homes.length; i++) {
|
|
184
|
+
var home = homes[i];
|
|
185
|
+
if (!home) continue;
|
|
186
|
+
var settingsPath = path.join(home, ".claude", "settings.json");
|
|
187
|
+
try {
|
|
188
|
+
var changed = mergeHook(settingsPath, command);
|
|
189
|
+
if (changed) installed.push(settingsPath);
|
|
190
|
+
} catch (e) {
|
|
191
|
+
errors.push({ path: settingsPath, error: e && e.message ? e.message : String(e) });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return { installed: installed, errors: errors };
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Old colon-prefix Bash patterns Clay wrote before claude 2.x: Bash(ls:*),
|
|
198
|
+
// Bash(git status:*), etc. Stripped on upgrade so they don't linger next to
|
|
199
|
+
// the modern space-wildcard form. Modern Clay patterns never contain ":)" so
|
|
200
|
+
// this only catches the deprecated shape; it can't tell a user-authored colon
|
|
201
|
+
// pattern apart, but claude is phasing the colon form out anyway.
|
|
202
|
+
function isLegacyClayPattern(p) {
|
|
203
|
+
if (typeof p !== "string") return false;
|
|
204
|
+
if (p.indexOf("Bash(") === 0 && p.indexOf(":*)") !== -1) return true;
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Merge Clay's managed allow-list into permissions.allow without disturbing
|
|
209
|
+
// the user's own entries. "Ours" = exact membership in CLAY_MANAGED_ALLOW
|
|
210
|
+
// (the non-Bash built-in / MCP entries) OR isClayManagedBashPattern (any
|
|
211
|
+
// shape of a bash command we own, so stale variants from older versions get
|
|
212
|
+
// swept) OR the legacy colon shape. Everything else is preserved, then the
|
|
213
|
+
// fresh list is appended. Because the Bash patterns are generated from
|
|
214
|
+
// safe-bash-commands.js, a re-install always writes the current set and can
|
|
215
|
+
// never leave a stale Clay block behind.
|
|
216
|
+
function mergeAllowList(settingsPath, patterns) {
|
|
217
|
+
var data = readSettings(settingsPath);
|
|
218
|
+
if (!data.permissions || typeof data.permissions !== "object") data.permissions = {};
|
|
219
|
+
var allow = Array.isArray(data.permissions.allow) ? data.permissions.allow : [];
|
|
220
|
+
|
|
221
|
+
var managedSet = {};
|
|
222
|
+
for (var i = 0; i < CLAY_MANAGED_ALLOW.length; i++) managedSet[CLAY_MANAGED_ALLOW[i]] = true;
|
|
223
|
+
|
|
224
|
+
var preserved = allow.filter(function (p) {
|
|
225
|
+
if (managedSet[p]) return false;
|
|
226
|
+
if (isClayManagedBashPattern(p)) return false;
|
|
227
|
+
if (isLegacyClayPattern(p)) return false;
|
|
228
|
+
return true;
|
|
229
|
+
});
|
|
230
|
+
var next = preserved.concat(patterns);
|
|
231
|
+
|
|
232
|
+
var before = JSON.stringify(allow);
|
|
233
|
+
var after = JSON.stringify(next);
|
|
234
|
+
if (before === after) return false;
|
|
235
|
+
|
|
236
|
+
data.permissions.allow = next;
|
|
237
|
+
writeSettings(settingsPath, data);
|
|
238
|
+
return true;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Install (or refresh) Clay's auto-approve patterns into permissions.allow
|
|
243
|
+
* for one or more user home dirs. Mirrors installNotificationHook's API.
|
|
244
|
+
*/
|
|
245
|
+
function installAllowList(opts) {
|
|
246
|
+
opts = opts || {};
|
|
247
|
+
var os = require("os");
|
|
248
|
+
var homes = opts.homeDirs && opts.homeDirs.length ? opts.homeDirs : [os.homedir()];
|
|
249
|
+
var patterns = (opts.patterns && opts.patterns.length) ? opts.patterns : CLAY_MANAGED_ALLOW;
|
|
250
|
+
var installed = [];
|
|
251
|
+
var errors = [];
|
|
252
|
+
for (var i = 0; i < homes.length; i++) {
|
|
253
|
+
var home = homes[i];
|
|
254
|
+
if (!home) continue;
|
|
255
|
+
var settingsPath = path.join(home, ".claude", "settings.json");
|
|
256
|
+
try {
|
|
257
|
+
var changed = mergeAllowList(settingsPath, patterns);
|
|
258
|
+
if (changed) installed.push(settingsPath);
|
|
259
|
+
} catch (e) {
|
|
260
|
+
errors.push({ path: settingsPath, error: e && e.message ? e.message : String(e) });
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return { installed: installed, errors: errors };
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
module.exports = {
|
|
267
|
+
installNotificationHook: installNotificationHook,
|
|
268
|
+
installAllowList: installAllowList,
|
|
269
|
+
CLAY_HOOK_MARKER: CLAY_HOOK_MARKER,
|
|
270
|
+
CLAY_MANAGED_ALLOW: CLAY_MANAGED_ALLOW,
|
|
271
|
+
};
|