@aiden-ade/sandbox-agent 0.1.76 → 0.1.78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +2338 -367
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1022,7 +1022,7 @@ var require_node = __commonJS({
|
|
|
1022
1022
|
var tty = require("tty");
|
|
1023
1023
|
var util = require("util");
|
|
1024
1024
|
exports2.init = init;
|
|
1025
|
-
exports2.log =
|
|
1025
|
+
exports2.log = log2;
|
|
1026
1026
|
exports2.formatArgs = formatArgs;
|
|
1027
1027
|
exports2.save = save;
|
|
1028
1028
|
exports2.load = load;
|
|
@@ -1157,7 +1157,7 @@ var require_node = __commonJS({
|
|
|
1157
1157
|
}
|
|
1158
1158
|
return (/* @__PURE__ */ new Date()).toISOString() + " ";
|
|
1159
1159
|
}
|
|
1160
|
-
function
|
|
1160
|
+
function log2(...args) {
|
|
1161
1161
|
return process.stderr.write(util.formatWithOptions(exports2.inspectOpts, ...args) + "\n");
|
|
1162
1162
|
}
|
|
1163
1163
|
function save(namespaces) {
|
|
@@ -3404,7 +3404,7 @@ var require_websocket = __commonJS({
|
|
|
3404
3404
|
var http2 = require("http");
|
|
3405
3405
|
var net = require("net");
|
|
3406
3406
|
var tls = require("tls");
|
|
3407
|
-
var { randomBytes:
|
|
3407
|
+
var { randomBytes: randomBytes13, createHash: createHash16 } = require("crypto");
|
|
3408
3408
|
var { Duplex, Readable: Readable2 } = require("stream");
|
|
3409
3409
|
var { URL: URL2 } = require("url");
|
|
3410
3410
|
var PerMessageDeflate = require_permessage_deflate();
|
|
@@ -3931,7 +3931,7 @@ var require_websocket = __commonJS({
|
|
|
3931
3931
|
}
|
|
3932
3932
|
}
|
|
3933
3933
|
const defaultPort = isSecure ? 443 : 80;
|
|
3934
|
-
const key =
|
|
3934
|
+
const key = randomBytes13(16).toString("base64");
|
|
3935
3935
|
const request = isSecure ? https.request : http2.request;
|
|
3936
3936
|
const protocolSet = /* @__PURE__ */ new Set();
|
|
3937
3937
|
let perMessageDeflate;
|
|
@@ -4061,7 +4061,7 @@ var require_websocket = __commonJS({
|
|
|
4061
4061
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
4062
4062
|
return;
|
|
4063
4063
|
}
|
|
4064
|
-
const digest =
|
|
4064
|
+
const digest = createHash16("sha1").update(key + GUID).digest("base64");
|
|
4065
4065
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
4066
4066
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
4067
4067
|
return;
|
|
@@ -4428,7 +4428,7 @@ var require_websocket_server = __commonJS({
|
|
|
4428
4428
|
var EventEmitter = require("events");
|
|
4429
4429
|
var http2 = require("http");
|
|
4430
4430
|
var { Duplex } = require("stream");
|
|
4431
|
-
var { createHash:
|
|
4431
|
+
var { createHash: createHash16 } = require("crypto");
|
|
4432
4432
|
var extension = require_extension();
|
|
4433
4433
|
var PerMessageDeflate = require_permessage_deflate();
|
|
4434
4434
|
var subprotocol = require_subprotocol();
|
|
@@ -4725,7 +4725,7 @@ var require_websocket_server = __commonJS({
|
|
|
4725
4725
|
);
|
|
4726
4726
|
}
|
|
4727
4727
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
4728
|
-
const digest =
|
|
4728
|
+
const digest = createHash16("sha1").update(key + GUID).digest("base64");
|
|
4729
4729
|
const headers = [
|
|
4730
4730
|
"HTTP/1.1 101 Switching Protocols",
|
|
4731
4731
|
"Upgrade: websocket",
|
|
@@ -6530,7 +6530,7 @@ var require_merge = __commonJS({
|
|
|
6530
6530
|
var require_addPairToJSMap = __commonJS({
|
|
6531
6531
|
"../../node_modules/yaml/dist/nodes/addPairToJSMap.js"(exports2) {
|
|
6532
6532
|
"use strict";
|
|
6533
|
-
var
|
|
6533
|
+
var log2 = require_log();
|
|
6534
6534
|
var merge2 = require_merge();
|
|
6535
6535
|
var stringify2 = require_stringify();
|
|
6536
6536
|
var identity = require_identity();
|
|
@@ -6579,7 +6579,7 @@ var require_addPairToJSMap = __commonJS({
|
|
|
6579
6579
|
let jsonStr = JSON.stringify(strKey);
|
|
6580
6580
|
if (jsonStr.length > 40)
|
|
6581
6581
|
jsonStr = jsonStr.substring(0, 36) + '..."';
|
|
6582
|
-
|
|
6582
|
+
log2.warn(ctx.doc.options.logLevel, `Keys with collection values will be stringified due to JS Object restrictions: ${jsonStr}. Set mapAsMap: true to use object keys.`);
|
|
6583
6583
|
ctx.mapKeyWarned = true;
|
|
6584
6584
|
}
|
|
6585
6585
|
return strKey;
|
|
@@ -11995,7 +11995,7 @@ var require_public_api = __commonJS({
|
|
|
11995
11995
|
var composer = require_composer();
|
|
11996
11996
|
var Document = require_Document();
|
|
11997
11997
|
var errors = require_errors2();
|
|
11998
|
-
var
|
|
11998
|
+
var log2 = require_log();
|
|
11999
11999
|
var identity = require_identity();
|
|
12000
12000
|
var lineCounter = require_line_counter();
|
|
12001
12001
|
var parser = require_parser();
|
|
@@ -12047,7 +12047,7 @@ var require_public_api = __commonJS({
|
|
|
12047
12047
|
const doc = parseDocument(src, options);
|
|
12048
12048
|
if (!doc)
|
|
12049
12049
|
return null;
|
|
12050
|
-
doc.warnings.forEach((warning) =>
|
|
12050
|
+
doc.warnings.forEach((warning) => log2.warn(doc.options.logLevel, warning));
|
|
12051
12051
|
if (doc.errors.length > 0) {
|
|
12052
12052
|
if (doc.options.logLevel !== "silent")
|
|
12053
12053
|
throw doc.errors[0];
|
|
@@ -13386,8 +13386,8 @@ var require_streamx = __commonJS({
|
|
|
13386
13386
|
return this;
|
|
13387
13387
|
},
|
|
13388
13388
|
next() {
|
|
13389
|
-
return new Promise(function(
|
|
13390
|
-
promiseResolve =
|
|
13389
|
+
return new Promise(function(resolve15, reject) {
|
|
13390
|
+
promiseResolve = resolve15;
|
|
13391
13391
|
promiseReject = reject;
|
|
13392
13392
|
const data = stream.read();
|
|
13393
13393
|
if (data !== null) ondata(data);
|
|
@@ -13420,11 +13420,11 @@ var require_streamx = __commonJS({
|
|
|
13420
13420
|
}
|
|
13421
13421
|
function destroy(err) {
|
|
13422
13422
|
stream.destroy(err);
|
|
13423
|
-
return new Promise((
|
|
13424
|
-
if (stream._duplexState & DESTROYED) return
|
|
13423
|
+
return new Promise((resolve15, reject) => {
|
|
13424
|
+
if (stream._duplexState & DESTROYED) return resolve15({ value: void 0, done: true });
|
|
13425
13425
|
stream.once("close", function() {
|
|
13426
13426
|
if (err) reject(err);
|
|
13427
|
-
else
|
|
13427
|
+
else resolve15({ value: void 0, done: true });
|
|
13428
13428
|
});
|
|
13429
13429
|
});
|
|
13430
13430
|
}
|
|
@@ -13468,8 +13468,8 @@ var require_streamx = __commonJS({
|
|
|
13468
13468
|
const writes = pending + (ws._duplexState & WRITE_WRITING ? 1 : 0);
|
|
13469
13469
|
if (writes === 0) return Promise.resolve(true);
|
|
13470
13470
|
if (state.drains === null) state.drains = [];
|
|
13471
|
-
return new Promise((
|
|
13472
|
-
state.drains.push({ writes, resolve:
|
|
13471
|
+
return new Promise((resolve15) => {
|
|
13472
|
+
state.drains.push({ writes, resolve: resolve15 });
|
|
13473
13473
|
});
|
|
13474
13474
|
}
|
|
13475
13475
|
write(data) {
|
|
@@ -13574,10 +13574,10 @@ var require_streamx = __commonJS({
|
|
|
13574
13574
|
cb(null);
|
|
13575
13575
|
}
|
|
13576
13576
|
function pipelinePromise(...streams) {
|
|
13577
|
-
return new Promise((
|
|
13577
|
+
return new Promise((resolve15, reject) => {
|
|
13578
13578
|
return pipeline(...streams, (err) => {
|
|
13579
13579
|
if (err) return reject(err);
|
|
13580
|
-
|
|
13580
|
+
resolve15();
|
|
13581
13581
|
});
|
|
13582
13582
|
});
|
|
13583
13583
|
}
|
|
@@ -14249,16 +14249,16 @@ var require_extract = __commonJS({
|
|
|
14249
14249
|
entryCallback = null;
|
|
14250
14250
|
cb(err);
|
|
14251
14251
|
}
|
|
14252
|
-
function onnext(
|
|
14252
|
+
function onnext(resolve15, reject) {
|
|
14253
14253
|
if (error61) {
|
|
14254
14254
|
return reject(error61);
|
|
14255
14255
|
}
|
|
14256
14256
|
if (entryStream) {
|
|
14257
|
-
|
|
14257
|
+
resolve15({ value: entryStream, done: false });
|
|
14258
14258
|
entryStream = null;
|
|
14259
14259
|
return;
|
|
14260
14260
|
}
|
|
14261
|
-
promiseResolve =
|
|
14261
|
+
promiseResolve = resolve15;
|
|
14262
14262
|
promiseReject = reject;
|
|
14263
14263
|
consumeCallback(null);
|
|
14264
14264
|
if (extract2._finished && promiseResolve) {
|
|
@@ -14286,11 +14286,11 @@ var require_extract = __commonJS({
|
|
|
14286
14286
|
function destroy(err) {
|
|
14287
14287
|
extract2.destroy(err);
|
|
14288
14288
|
consumeCallback(err);
|
|
14289
|
-
return new Promise((
|
|
14290
|
-
if (extract2.destroyed) return
|
|
14289
|
+
return new Promise((resolve15, reject) => {
|
|
14290
|
+
if (extract2.destroyed) return resolve15({ value: void 0, done: true });
|
|
14291
14291
|
extract2.once("close", function() {
|
|
14292
14292
|
if (err) reject(err);
|
|
14293
|
-
else
|
|
14293
|
+
else resolve15({ value: void 0, done: true });
|
|
14294
14294
|
});
|
|
14295
14295
|
});
|
|
14296
14296
|
}
|
|
@@ -14719,7 +14719,7 @@ function resolveViaWhere(command, env) {
|
|
|
14719
14719
|
return null;
|
|
14720
14720
|
}
|
|
14721
14721
|
function runCliProbeAsync(executable, env, args = ["--version"], timeoutMs = 3e3, spawnOptions) {
|
|
14722
|
-
return new Promise((
|
|
14722
|
+
return new Promise((resolve15) => {
|
|
14723
14723
|
const isWin = (0, import_node_os.platform)() === "win32";
|
|
14724
14724
|
let command = executable;
|
|
14725
14725
|
let commandArgs = args;
|
|
@@ -14739,7 +14739,7 @@ function runCliProbeAsync(executable, env, args = ["--version"], timeoutMs = 3e3
|
|
|
14739
14739
|
stdio: closeStdin ? ["pipe", "pipe", "pipe"] : void 0
|
|
14740
14740
|
});
|
|
14741
14741
|
} catch (error61) {
|
|
14742
|
-
|
|
14742
|
+
resolve15({ status: null, stdout: "", stderr: "", error: error61 });
|
|
14743
14743
|
return;
|
|
14744
14744
|
}
|
|
14745
14745
|
if (closeStdin) {
|
|
@@ -14753,10 +14753,10 @@ function runCliProbeAsync(executable, env, args = ["--version"], timeoutMs = 3e3
|
|
|
14753
14753
|
child.stderr?.setEncoding("utf8").on("data", (chunk) => {
|
|
14754
14754
|
stderr += chunk;
|
|
14755
14755
|
});
|
|
14756
|
-
child.once("error", (error61) =>
|
|
14756
|
+
child.once("error", (error61) => resolve15({ status: null, stdout, stderr, error: error61 }));
|
|
14757
14757
|
child.once(
|
|
14758
14758
|
"close",
|
|
14759
|
-
(status, signal) =>
|
|
14759
|
+
(status, signal) => resolve15({ status, stdout, stderr, signal: signal ?? null })
|
|
14760
14760
|
);
|
|
14761
14761
|
});
|
|
14762
14762
|
}
|
|
@@ -14920,10 +14920,13 @@ Usage:
|
|
|
14920
14920
|
alan-agent <command> [options]
|
|
14921
14921
|
|
|
14922
14922
|
Commands:
|
|
14923
|
+
install Login/setup, install the daemon service, MCP, and supported hooks
|
|
14923
14924
|
setup Register this computer with a setup token from Alan
|
|
14924
14925
|
login Register this computer through browser authorization
|
|
14925
14926
|
daemon Start the durable runtime daemon
|
|
14926
14927
|
service Install, uninstall, or inspect the per-user daemon service
|
|
14928
|
+
integrations Reconcile, inspect, or uninstall Alan CLI integrations
|
|
14929
|
+
hook Internal: durably spool a native CLI hook event (fail-open)
|
|
14927
14930
|
stop Stop the local daemon (or active runs only via the local API /stop)
|
|
14928
14931
|
recover Redial and safely restart a stuck local daemon connection
|
|
14929
14932
|
status Show local runtime configuration without secrets
|
|
@@ -14935,11 +14938,17 @@ Commands:
|
|
|
14935
14938
|
run-session Internal: run one ephemeral sandbox/session from ALAN_* env vars
|
|
14936
14939
|
|
|
14937
14940
|
Common flows:
|
|
14941
|
+
alan-agent install
|
|
14942
|
+
alan-agent install --setup-token <token>
|
|
14938
14943
|
alan-agent login
|
|
14939
14944
|
alan-agent login --cancel
|
|
14940
14945
|
alan-agent setup --setup-token <token>
|
|
14941
14946
|
alan-agent setup --setup-token <token> --scope user
|
|
14942
14947
|
alan-agent service install
|
|
14948
|
+
alan-agent integrations reconcile
|
|
14949
|
+
alan-agent integrations status
|
|
14950
|
+
alan-agent integrations capabilities
|
|
14951
|
+
alan-agent integrations uninstall
|
|
14943
14952
|
alan-agent stop
|
|
14944
14953
|
|
|
14945
14954
|
Local development:
|
|
@@ -14963,7 +14972,7 @@ function resolveCommand(args, env) {
|
|
|
14963
14972
|
}
|
|
14964
14973
|
|
|
14965
14974
|
// src/daemon.ts
|
|
14966
|
-
var
|
|
14975
|
+
var import_node_crypto17 = require("crypto");
|
|
14967
14976
|
var import_node_os11 = require("os");
|
|
14968
14977
|
|
|
14969
14978
|
// src/sleep-inhibitor.ts
|
|
@@ -14986,8 +14995,8 @@ var ChildProcessBackend = class {
|
|
|
14986
14995
|
*/
|
|
14987
14996
|
unavailable = false;
|
|
14988
14997
|
log;
|
|
14989
|
-
constructor(
|
|
14990
|
-
this.log =
|
|
14998
|
+
constructor(log2) {
|
|
14999
|
+
this.log = log2;
|
|
14991
15000
|
}
|
|
14992
15001
|
/**
|
|
14993
15002
|
* Let a multi-candidate backend react when its active helper terminates.
|
|
@@ -15120,14 +15129,14 @@ var WindowsPowerRequestBackend = class extends ChildProcessBackend {
|
|
|
15120
15129
|
}
|
|
15121
15130
|
};
|
|
15122
15131
|
var externalBackends = /* @__PURE__ */ new Set();
|
|
15123
|
-
function createPlatformBackends(
|
|
15132
|
+
function createPlatformBackends(log2, hasExternalBackend) {
|
|
15124
15133
|
switch (process.platform) {
|
|
15125
15134
|
case "darwin":
|
|
15126
|
-
return [new MacCaffeinateBackend(
|
|
15135
|
+
return [new MacCaffeinateBackend(log2)];
|
|
15127
15136
|
case "linux":
|
|
15128
|
-
return [new LinuxIdleInhibitBackend(
|
|
15137
|
+
return [new LinuxIdleInhibitBackend(log2)];
|
|
15129
15138
|
case "win32":
|
|
15130
|
-
return hasExternalBackend ? [] : [new WindowsPowerRequestBackend(
|
|
15139
|
+
return hasExternalBackend ? [] : [new WindowsPowerRequestBackend(log2)];
|
|
15131
15140
|
default:
|
|
15132
15141
|
return [];
|
|
15133
15142
|
}
|
|
@@ -15231,7 +15240,7 @@ function describeError(error61) {
|
|
|
15231
15240
|
}
|
|
15232
15241
|
|
|
15233
15242
|
// src/version.ts
|
|
15234
|
-
var AGENT_VERSION = "0.1.
|
|
15243
|
+
var AGENT_VERSION = "0.1.78";
|
|
15235
15244
|
|
|
15236
15245
|
// src/daemon-worktree.ts
|
|
15237
15246
|
var import_node_child_process3 = require("child_process");
|
|
@@ -16513,8 +16522,8 @@ async function withTimeout(promise2, timeoutMs, fallback) {
|
|
|
16513
16522
|
try {
|
|
16514
16523
|
return await Promise.race([
|
|
16515
16524
|
promise2,
|
|
16516
|
-
new Promise((
|
|
16517
|
-
timer = setTimeout(() =>
|
|
16525
|
+
new Promise((resolve15) => {
|
|
16526
|
+
timer = setTimeout(() => resolve15(fallback), timeoutMs);
|
|
16518
16527
|
})
|
|
16519
16528
|
]);
|
|
16520
16529
|
} finally {
|
|
@@ -16527,7 +16536,7 @@ async function collectCodexLimits(env) {
|
|
|
16527
16536
|
const codexCommand = resolveExecutable("codex", runtimeEnv, "ALAN_CODEX_PATH");
|
|
16528
16537
|
if (!codexCommand)
|
|
16529
16538
|
return [];
|
|
16530
|
-
return withTimeout(new Promise((
|
|
16539
|
+
return withTimeout(new Promise((resolve15) => {
|
|
16531
16540
|
const child = (0, import_node_child_process4.spawn)(codexCommand, ["-s", "read-only", "-a", "untrusted", "app-server"], {
|
|
16532
16541
|
env: runtimeEnv,
|
|
16533
16542
|
stdio: ["pipe", "pipe", "ignore"]
|
|
@@ -16539,7 +16548,7 @@ async function collectCodexLimits(env) {
|
|
|
16539
16548
|
pending.clear();
|
|
16540
16549
|
if (!child.killed)
|
|
16541
16550
|
child.kill();
|
|
16542
|
-
|
|
16551
|
+
resolve15(windows);
|
|
16543
16552
|
};
|
|
16544
16553
|
const request = (method, params = {}) => {
|
|
16545
16554
|
const id = nextId++;
|
|
@@ -16686,7 +16695,7 @@ function antigravityWindow(groupLabel, raw) {
|
|
|
16686
16695
|
};
|
|
16687
16696
|
}
|
|
16688
16697
|
function runOneShotCli(command, args, env, timeoutMs) {
|
|
16689
|
-
return new Promise((
|
|
16698
|
+
return new Promise((resolve15) => {
|
|
16690
16699
|
const child = (0, import_node_child_process4.spawn)(command, args, { env, stdio: ["ignore", "pipe", "ignore"] });
|
|
16691
16700
|
let stdout = "";
|
|
16692
16701
|
let settled = false;
|
|
@@ -16695,7 +16704,7 @@ function runOneShotCli(command, args, env, timeoutMs) {
|
|
|
16695
16704
|
return;
|
|
16696
16705
|
settled = true;
|
|
16697
16706
|
clearTimeout(timer);
|
|
16698
|
-
|
|
16707
|
+
resolve15(value2);
|
|
16699
16708
|
};
|
|
16700
16709
|
const timer = setTimeout(() => {
|
|
16701
16710
|
if (!child.killed)
|
|
@@ -35745,7 +35754,18 @@ function resolveEndpointProfileFromArgs(args) {
|
|
|
35745
35754
|
const raw = index === -1 ? process.env.ALAN_AGENT_PROFILE : args[index + 1];
|
|
35746
35755
|
return normalizeAgentEndpointProfile(raw) ?? "production";
|
|
35747
35756
|
}
|
|
35757
|
+
function explicitConfigPathFromArgs(args) {
|
|
35758
|
+
const index = args.indexOf("--config-path");
|
|
35759
|
+
if (index >= 0) return args[index + 1]?.trim() || void 0;
|
|
35760
|
+
const prefix = "--config-path=";
|
|
35761
|
+
return args.find((arg) => arg.startsWith(prefix))?.slice(prefix.length).trim() || void 0;
|
|
35762
|
+
}
|
|
35748
35763
|
function bindAgentConfigPathFromArgs(args) {
|
|
35764
|
+
const argumentPath = explicitConfigPathFromArgs(args);
|
|
35765
|
+
if (argumentPath) {
|
|
35766
|
+
process.env.ALAN_AGENT_CONFIG_PATH = argumentPath;
|
|
35767
|
+
return argumentPath;
|
|
35768
|
+
}
|
|
35749
35769
|
const explicit = process.env.ALAN_AGENT_CONFIG_PATH?.trim();
|
|
35750
35770
|
if (explicit) return explicit;
|
|
35751
35771
|
const profile = resolveEndpointProfileFromArgs(args);
|
|
@@ -37172,6 +37192,13 @@ var ACTIVE_AGENT_RUN_STATUSES = [
|
|
|
37172
37192
|
];
|
|
37173
37193
|
var STOPPABLE_AGENT_RUN_STATUSES = [...ACTIVE_AGENT_RUN_STATUSES, "paused"];
|
|
37174
37194
|
|
|
37195
|
+
// ../shared/dist/agent/types.js
|
|
37196
|
+
function resolveEffectiveBackendKind(backendKind) {
|
|
37197
|
+
if (backendKind === "codex")
|
|
37198
|
+
return "codex_app_server";
|
|
37199
|
+
return backendKind || "claude_cli";
|
|
37200
|
+
}
|
|
37201
|
+
|
|
37175
37202
|
// ../shared/dist/effort.js
|
|
37176
37203
|
var EFFORT_LEVELS = ["minimal", "low", "medium", "high", "xhigh", "max", "ultra"];
|
|
37177
37204
|
var CLAUDE_EFFORT_LEVELS = [
|
|
@@ -41653,7 +41680,7 @@ function createClaudeCliBackend(command = "claude", defaultArgs = []) {
|
|
|
41653
41680
|
} else if (resumeId && context.config.systemPromptAppend?.trim()) {
|
|
41654
41681
|
args.push("--append-system-prompt", context.config.systemPromptAppend.trim());
|
|
41655
41682
|
}
|
|
41656
|
-
if (context.config.
|
|
41683
|
+
if (context.config.alanMcp && await cliSupportsFlag(command, "--approve-mcps", context.env)) {
|
|
41657
41684
|
args.push("--approve-mcps");
|
|
41658
41685
|
}
|
|
41659
41686
|
const baseModel = collapseModelIdToBase(context.config.selectedModel?.trim() || "");
|
|
@@ -42389,7 +42416,15 @@ function emitCodexFileChanges(context, state, itemId, changes) {
|
|
|
42389
42416
|
const toolName = kind === "add" ? "write_file" : "edit_file";
|
|
42390
42417
|
const toolId = changes.length > 1 ? `${itemId}:${index}` : itemId;
|
|
42391
42418
|
trackCodexStreamedToolId(state, toolId);
|
|
42392
|
-
|
|
42419
|
+
const diffMetadata = {};
|
|
42420
|
+
for (const key of ["additions", "deletions", "patch", "diff"]) {
|
|
42421
|
+
if (record2[key] !== void 0) diffMetadata[key] = record2[key];
|
|
42422
|
+
}
|
|
42423
|
+
void context.presenter.onToolUse(
|
|
42424
|
+
toolName,
|
|
42425
|
+
{ file_path: filePath, kind, ...diffMetadata },
|
|
42426
|
+
toolId
|
|
42427
|
+
);
|
|
42393
42428
|
void context.presenter.onToolResult?.(toolId, `${kind} ${filePath}`);
|
|
42394
42429
|
});
|
|
42395
42430
|
}
|
|
@@ -43794,7 +43829,7 @@ function createCursorAgentCliBackend(command = "cursor-agent", defaultArgs = [])
|
|
|
43794
43829
|
context.config.images,
|
|
43795
43830
|
context.cwd
|
|
43796
43831
|
);
|
|
43797
|
-
const approveMcps = context.config.
|
|
43832
|
+
const approveMcps = context.config.alanMcp ? await cliSupportsFlag(command, "--approve-mcps", context.env) : false;
|
|
43798
43833
|
try {
|
|
43799
43834
|
return await createGenericCliBackend({
|
|
43800
43835
|
kind: "cursor_agent_cli",
|
|
@@ -46251,10 +46286,6 @@ ${ctx.promptText}` : ctx.promptText;
|
|
|
46251
46286
|
}
|
|
46252
46287
|
var DROID_DEFAULT_MODEL = "claude-opus-4-8";
|
|
46253
46288
|
var AGENT_ACTIVITY_HEARTBEAT_MS = 1e4;
|
|
46254
|
-
function normalizeBackendKind(kind) {
|
|
46255
|
-
if (kind === "codex") return "codex_app_server";
|
|
46256
|
-
return kind || "claude_cli";
|
|
46257
|
-
}
|
|
46258
46289
|
function firstCatalogModelId(backendKind) {
|
|
46259
46290
|
const fromPicker = catalogModelsForProvider(backendKind, PROVIDER_MODELS[backendKind]).find(
|
|
46260
46291
|
(model) => model.id.trim()
|
|
@@ -46501,7 +46532,7 @@ var BaseMachineAgent = class _BaseMachineAgent {
|
|
|
46501
46532
|
return message.includes("aborted") || message.includes("interrupted") || message.includes("cancelled") || message.includes("canceled");
|
|
46502
46533
|
}
|
|
46503
46534
|
resolveBackendKind(config2) {
|
|
46504
|
-
return
|
|
46535
|
+
return resolveEffectiveBackendKind(this.runtime.backendKind || config2.backendKind);
|
|
46505
46536
|
}
|
|
46506
46537
|
getDefaultSupportTier(backendKind) {
|
|
46507
46538
|
switch (backendKind) {
|
|
@@ -49447,7 +49478,7 @@ async function discoverProviderModelDetails(provider, executable, env) {
|
|
|
49447
49478
|
}
|
|
49448
49479
|
|
|
49449
49480
|
// src/daemon-provider-scan.ts
|
|
49450
|
-
function
|
|
49481
|
+
function normalizeBackendKind(backendKind) {
|
|
49451
49482
|
if (backendKind === "codex") return "codex_app_server";
|
|
49452
49483
|
return backendKind;
|
|
49453
49484
|
}
|
|
@@ -49805,7 +49836,7 @@ function checkStoredEndpointConsistency(stored, endpointDefaults) {
|
|
|
49805
49836
|
}
|
|
49806
49837
|
function sleep(ms) {
|
|
49807
49838
|
if (ms <= 0) return Promise.resolve();
|
|
49808
|
-
return new Promise((
|
|
49839
|
+
return new Promise((resolve15) => setTimeout(resolve15, ms));
|
|
49809
49840
|
}
|
|
49810
49841
|
function scheduleRuntimeModelScan(getBaseMetadata, options) {
|
|
49811
49842
|
const stored = readConfig();
|
|
@@ -49826,11 +49857,11 @@ function scheduleRuntimeModelScan(getBaseMetadata, options) {
|
|
|
49826
49857
|
}
|
|
49827
49858
|
|
|
49828
49859
|
// src/daemon-start.ts
|
|
49829
|
-
var
|
|
49830
|
-
var
|
|
49860
|
+
var import_node_crypto16 = require("crypto");
|
|
49861
|
+
var import_node_fs20 = require("fs");
|
|
49831
49862
|
var import_node_http = __toESM(require("http"), 1);
|
|
49832
49863
|
var import_node_os10 = require("os");
|
|
49833
|
-
var
|
|
49864
|
+
var import_node_path22 = require("path");
|
|
49834
49865
|
|
|
49835
49866
|
// ../../node_modules/engine.io-client/build/esm-debug/transports/polling-xhr.node.js
|
|
49836
49867
|
var XMLHttpRequestModule = __toESM(require_XMLHttpRequest(), 1);
|
|
@@ -52386,9 +52417,9 @@ var Socket2 = class extends Emitter {
|
|
|
52386
52417
|
* @return a Promise that will be fulfilled when the server acknowledges the event
|
|
52387
52418
|
*/
|
|
52388
52419
|
emitWithAck(ev, ...args) {
|
|
52389
|
-
return new Promise((
|
|
52420
|
+
return new Promise((resolve15, reject) => {
|
|
52390
52421
|
const fn = (arg1, arg2) => {
|
|
52391
|
-
return arg1 ? reject(arg1) :
|
|
52422
|
+
return arg1 ? reject(arg1) : resolve15(arg2);
|
|
52392
52423
|
};
|
|
52393
52424
|
fn.withError = true;
|
|
52394
52425
|
args.push(fn);
|
|
@@ -54553,7 +54584,7 @@ function versionPolicy(npmPackage) {
|
|
|
54553
54584
|
return "latest";
|
|
54554
54585
|
}
|
|
54555
54586
|
function runNpmInstall(installSpec, env, timeoutMs) {
|
|
54556
|
-
return new Promise((
|
|
54587
|
+
return new Promise((resolve15) => {
|
|
54557
54588
|
const isWin = (0, import_node_os7.platform)() === "win32";
|
|
54558
54589
|
let child;
|
|
54559
54590
|
try {
|
|
@@ -54565,7 +54596,7 @@ function runNpmInstall(installSpec, env, timeoutMs) {
|
|
|
54565
54596
|
stdio: ["ignore", "pipe", "pipe"]
|
|
54566
54597
|
});
|
|
54567
54598
|
} catch (error61) {
|
|
54568
|
-
|
|
54599
|
+
resolve15({ status: null, stdout: "", stderr: "", error: error61 });
|
|
54569
54600
|
return;
|
|
54570
54601
|
}
|
|
54571
54602
|
let stdout = "";
|
|
@@ -54576,10 +54607,10 @@ function runNpmInstall(installSpec, env, timeoutMs) {
|
|
|
54576
54607
|
child.stderr?.setEncoding("utf8").on("data", (chunk) => {
|
|
54577
54608
|
stderr += chunk;
|
|
54578
54609
|
});
|
|
54579
|
-
child.once("error", (error61) =>
|
|
54610
|
+
child.once("error", (error61) => resolve15({ status: null, stdout, stderr, error: error61 }));
|
|
54580
54611
|
child.once(
|
|
54581
54612
|
"close",
|
|
54582
|
-
(status, signal) =>
|
|
54613
|
+
(status, signal) => resolve15({
|
|
54583
54614
|
status,
|
|
54584
54615
|
stdout,
|
|
54585
54616
|
stderr,
|
|
@@ -55322,6 +55353,147 @@ function parse5(toml, { maxDepth = 1e3, integersAsBigInt } = {}) {
|
|
|
55322
55353
|
return res;
|
|
55323
55354
|
}
|
|
55324
55355
|
|
|
55356
|
+
// ../../node_modules/smol-toml/dist/stringify.js
|
|
55357
|
+
var BARE_KEY = /^[a-z0-9-_]+$/i;
|
|
55358
|
+
function extendedTypeOf(obj) {
|
|
55359
|
+
let type = typeof obj;
|
|
55360
|
+
if (type === "object") {
|
|
55361
|
+
if (Array.isArray(obj))
|
|
55362
|
+
return "array";
|
|
55363
|
+
if (obj instanceof Date)
|
|
55364
|
+
return "date";
|
|
55365
|
+
}
|
|
55366
|
+
return type;
|
|
55367
|
+
}
|
|
55368
|
+
function isArrayOfTables(obj) {
|
|
55369
|
+
for (let i = 0; i < obj.length; i++) {
|
|
55370
|
+
if (extendedTypeOf(obj[i]) !== "object")
|
|
55371
|
+
return false;
|
|
55372
|
+
}
|
|
55373
|
+
return obj.length != 0;
|
|
55374
|
+
}
|
|
55375
|
+
function formatString(s) {
|
|
55376
|
+
return JSON.stringify(s).replace(/\x7f/g, "\\u007f");
|
|
55377
|
+
}
|
|
55378
|
+
function stringifyValue(val, type, depth, numberAsFloat) {
|
|
55379
|
+
if (depth === 0) {
|
|
55380
|
+
throw new Error("Could not stringify the object: maximum object depth exceeded");
|
|
55381
|
+
}
|
|
55382
|
+
if (type === "number") {
|
|
55383
|
+
if (isNaN(val))
|
|
55384
|
+
return "nan";
|
|
55385
|
+
if (val === Infinity)
|
|
55386
|
+
return "inf";
|
|
55387
|
+
if (val === -Infinity)
|
|
55388
|
+
return "-inf";
|
|
55389
|
+
if (numberAsFloat && Number.isInteger(val))
|
|
55390
|
+
return val.toFixed(1);
|
|
55391
|
+
return val.toString();
|
|
55392
|
+
}
|
|
55393
|
+
if (type === "bigint" || type === "boolean") {
|
|
55394
|
+
return val.toString();
|
|
55395
|
+
}
|
|
55396
|
+
if (type === "string") {
|
|
55397
|
+
return formatString(val);
|
|
55398
|
+
}
|
|
55399
|
+
if (type === "date") {
|
|
55400
|
+
if (isNaN(val.getTime())) {
|
|
55401
|
+
throw new TypeError("cannot serialize invalid date");
|
|
55402
|
+
}
|
|
55403
|
+
return val.toISOString();
|
|
55404
|
+
}
|
|
55405
|
+
if (type === "object") {
|
|
55406
|
+
return stringifyInlineTable(val, depth, numberAsFloat);
|
|
55407
|
+
}
|
|
55408
|
+
if (type === "array") {
|
|
55409
|
+
return stringifyArray(val, depth, numberAsFloat);
|
|
55410
|
+
}
|
|
55411
|
+
}
|
|
55412
|
+
function stringifyInlineTable(obj, depth, numberAsFloat) {
|
|
55413
|
+
let keys = Object.keys(obj);
|
|
55414
|
+
if (keys.length === 0)
|
|
55415
|
+
return "{}";
|
|
55416
|
+
let res = "{ ";
|
|
55417
|
+
for (let i = 0; i < keys.length; i++) {
|
|
55418
|
+
let k = keys[i];
|
|
55419
|
+
if (i)
|
|
55420
|
+
res += ", ";
|
|
55421
|
+
res += BARE_KEY.test(k) ? k : formatString(k);
|
|
55422
|
+
res += " = ";
|
|
55423
|
+
res += stringifyValue(obj[k], extendedTypeOf(obj[k]), depth - 1, numberAsFloat);
|
|
55424
|
+
}
|
|
55425
|
+
return res + " }";
|
|
55426
|
+
}
|
|
55427
|
+
function stringifyArray(array2, depth, numberAsFloat) {
|
|
55428
|
+
if (array2.length === 0)
|
|
55429
|
+
return "[]";
|
|
55430
|
+
let res = "[ ";
|
|
55431
|
+
for (let i = 0; i < array2.length; i++) {
|
|
55432
|
+
if (i)
|
|
55433
|
+
res += ", ";
|
|
55434
|
+
if (array2[i] === null || array2[i] === void 0) {
|
|
55435
|
+
throw new TypeError("arrays cannot contain null or undefined values");
|
|
55436
|
+
}
|
|
55437
|
+
res += stringifyValue(array2[i], extendedTypeOf(array2[i]), depth - 1, numberAsFloat);
|
|
55438
|
+
}
|
|
55439
|
+
return res + " ]";
|
|
55440
|
+
}
|
|
55441
|
+
function stringifyArrayTable(array2, key, depth, numberAsFloat) {
|
|
55442
|
+
if (depth === 0) {
|
|
55443
|
+
throw new Error("Could not stringify the object: maximum object depth exceeded");
|
|
55444
|
+
}
|
|
55445
|
+
let res = "";
|
|
55446
|
+
for (let i = 0; i < array2.length; i++) {
|
|
55447
|
+
res += `${res && "\n"}[[${key}]]
|
|
55448
|
+
`;
|
|
55449
|
+
res += stringifyTable(0, array2[i], key, depth, numberAsFloat);
|
|
55450
|
+
}
|
|
55451
|
+
return res;
|
|
55452
|
+
}
|
|
55453
|
+
function stringifyTable(tableKey, obj, prefix, depth, numberAsFloat) {
|
|
55454
|
+
if (depth === 0) {
|
|
55455
|
+
throw new Error("Could not stringify the object: maximum object depth exceeded");
|
|
55456
|
+
}
|
|
55457
|
+
let preamble = "";
|
|
55458
|
+
let tables = "";
|
|
55459
|
+
let keys = Object.keys(obj);
|
|
55460
|
+
for (let i = 0; i < keys.length; i++) {
|
|
55461
|
+
let k = keys[i];
|
|
55462
|
+
if (obj[k] !== null && obj[k] !== void 0) {
|
|
55463
|
+
let type = extendedTypeOf(obj[k]);
|
|
55464
|
+
if (type === "symbol" || type === "function") {
|
|
55465
|
+
throw new TypeError(`cannot serialize values of type '${type}'`);
|
|
55466
|
+
}
|
|
55467
|
+
let key = BARE_KEY.test(k) ? k : formatString(k);
|
|
55468
|
+
if (type === "array" && isArrayOfTables(obj[k])) {
|
|
55469
|
+
tables += (tables && "\n") + stringifyArrayTable(obj[k], prefix ? `${prefix}.${key}` : key, depth - 1, numberAsFloat);
|
|
55470
|
+
} else if (type === "object") {
|
|
55471
|
+
let tblKey = prefix ? `${prefix}.${key}` : key;
|
|
55472
|
+
tables += (tables && "\n") + stringifyTable(tblKey, obj[k], tblKey, depth - 1, numberAsFloat);
|
|
55473
|
+
} else {
|
|
55474
|
+
preamble += key;
|
|
55475
|
+
preamble += " = ";
|
|
55476
|
+
preamble += stringifyValue(obj[k], type, depth, numberAsFloat);
|
|
55477
|
+
preamble += "\n";
|
|
55478
|
+
}
|
|
55479
|
+
}
|
|
55480
|
+
}
|
|
55481
|
+
if (tableKey && (preamble || !tables))
|
|
55482
|
+
preamble = preamble ? `[${tableKey}]
|
|
55483
|
+
${preamble}` : `[${tableKey}]`;
|
|
55484
|
+
return preamble && tables ? `${preamble}
|
|
55485
|
+
${tables}` : preamble || tables;
|
|
55486
|
+
}
|
|
55487
|
+
function stringify(obj, { maxDepth = 1e3, numbersAsFloat = false } = {}) {
|
|
55488
|
+
if (extendedTypeOf(obj) !== "object") {
|
|
55489
|
+
throw new TypeError("stringify can only be called with an object");
|
|
55490
|
+
}
|
|
55491
|
+
let str = stringifyTable(0, obj, "", maxDepth, numbersAsFloat);
|
|
55492
|
+
if (str[str.length - 1] !== "\n")
|
|
55493
|
+
return str + "\n";
|
|
55494
|
+
return str;
|
|
55495
|
+
}
|
|
55496
|
+
|
|
55325
55497
|
// src/mcp-registration.ts
|
|
55326
55498
|
var CONFIG_LOCK_STALE_MS = 3e4;
|
|
55327
55499
|
var CONFIG_LOCK_WAIT_MS = 2e3;
|
|
@@ -55450,7 +55622,7 @@ async function waitForConfigLock(lockPath) {
|
|
|
55450
55622
|
if (Date.now() - startedAt >= CONFIG_LOCK_WAIT_MS) {
|
|
55451
55623
|
throw new Error(`Alan could not safely update ${lockPath.slice(0, -".alan-lock".length)}`);
|
|
55452
55624
|
}
|
|
55453
|
-
await new Promise((
|
|
55625
|
+
await new Promise((resolve15) => setTimeout(resolve15, CONFIG_LOCK_POLL_MS));
|
|
55454
55626
|
}
|
|
55455
55627
|
}
|
|
55456
55628
|
}
|
|
@@ -55596,6 +55768,22 @@ async function ensureAlanMcpRegistered(backendKind, registration, home = (0, imp
|
|
|
55596
55768
|
return { ok: false, paths: [], error: err instanceof Error ? err.message : String(err) };
|
|
55597
55769
|
}
|
|
55598
55770
|
}
|
|
55771
|
+
async function restoreStaticAlanMcp(backendKind, apiUrl, home = (0, import_node_os8.homedir)()) {
|
|
55772
|
+
let url3;
|
|
55773
|
+
try {
|
|
55774
|
+
url3 = new URL("/mcp", apiUrl).toString();
|
|
55775
|
+
} catch (err) {
|
|
55776
|
+
return {
|
|
55777
|
+
ok: false,
|
|
55778
|
+
paths: [],
|
|
55779
|
+
error: err instanceof Error ? err.message : String(err)
|
|
55780
|
+
};
|
|
55781
|
+
}
|
|
55782
|
+
return ensureAlanMcpRegistered(backendKind, { url: url3, headers: {} }, home, () => ({
|
|
55783
|
+
ok: true,
|
|
55784
|
+
verification: { method: "file" }
|
|
55785
|
+
}));
|
|
55786
|
+
}
|
|
55599
55787
|
async function verifyAlanMcpRegistered(backendKind, home = (0, import_node_os8.homedir)(), inspectCli = inspectAlanMcpThroughProviderCli) {
|
|
55600
55788
|
const files = alanMcpConfigFilesForBackend(backendKind, {}, home);
|
|
55601
55789
|
const missing = files.map((f) => f.path).filter((p) => !(0, import_node_fs14.existsSync)(p));
|
|
@@ -55667,45 +55855,6 @@ async function verifyAlanMcpRegistered(backendKind, home = (0, import_node_os8.h
|
|
|
55667
55855
|
providerLoadStatus: providerLoadStatusForBackend(backendKind)
|
|
55668
55856
|
};
|
|
55669
55857
|
}
|
|
55670
|
-
function removeAlanFromJsonConfig(path2, existingContent) {
|
|
55671
|
-
const config2 = parseJsonObject2(path2, existingContent);
|
|
55672
|
-
for (const containerKey of ["mcpServers", "mcp"]) {
|
|
55673
|
-
const container = config2[containerKey];
|
|
55674
|
-
if (container && typeof container === "object" && !Array.isArray(container)) {
|
|
55675
|
-
delete container.alan;
|
|
55676
|
-
}
|
|
55677
|
-
}
|
|
55678
|
-
return JSON.stringify(config2);
|
|
55679
|
-
}
|
|
55680
|
-
async function unregisterAlanMcp(backendKind, home = (0, import_node_os8.homedir)()) {
|
|
55681
|
-
const removedPaths = [];
|
|
55682
|
-
const errors = [];
|
|
55683
|
-
const files = alanMcpConfigFilesForBackend(backendKind, {}, home);
|
|
55684
|
-
for (const file2 of files) {
|
|
55685
|
-
if (!(0, import_node_fs14.existsSync)(file2.path)) continue;
|
|
55686
|
-
const releaseLock = await waitForConfigLock(`${file2.path}.alan-lock`);
|
|
55687
|
-
try {
|
|
55688
|
-
const current = (0, import_node_fs14.readFileSync)(file2.path, "utf8");
|
|
55689
|
-
let next;
|
|
55690
|
-
if (file2.path.endsWith(".toml")) {
|
|
55691
|
-
const stripped = stripCodexAlanMcpSection(current);
|
|
55692
|
-
next = stripped ? `${stripped}
|
|
55693
|
-
` : "";
|
|
55694
|
-
} else {
|
|
55695
|
-
next = removeAlanFromJsonConfig(file2.path, current);
|
|
55696
|
-
}
|
|
55697
|
-
if (next !== current) {
|
|
55698
|
-
atomicWriteManagedConfig(file2.path, next);
|
|
55699
|
-
removedPaths.push(file2.path);
|
|
55700
|
-
}
|
|
55701
|
-
} catch (err) {
|
|
55702
|
-
errors.push(err instanceof Error ? err.message : String(err));
|
|
55703
|
-
} finally {
|
|
55704
|
-
releaseLock();
|
|
55705
|
-
}
|
|
55706
|
-
}
|
|
55707
|
-
return { removedPaths, errors };
|
|
55708
|
-
}
|
|
55709
55858
|
function isHttpUrl(value2) {
|
|
55710
55859
|
if (typeof value2 !== "string") return false;
|
|
55711
55860
|
try {
|
|
@@ -55742,7 +55891,9 @@ function hasUsableRegistrationCredential(headers) {
|
|
|
55742
55891
|
return Boolean(normalized.get("x-alan-run-reference") || normalized.get("authorization"));
|
|
55743
55892
|
}
|
|
55744
55893
|
function readAlanMcpRegistration(path2) {
|
|
55745
|
-
|
|
55894
|
+
return parseAlanMcpRegistration(path2, (0, import_node_fs14.readFileSync)(path2, "utf8"));
|
|
55895
|
+
}
|
|
55896
|
+
function parseAlanMcpRegistration(path2, content) {
|
|
55746
55897
|
if (path2.endsWith(".toml")) {
|
|
55747
55898
|
try {
|
|
55748
55899
|
const parsed = parse5(content);
|
|
@@ -55795,12 +55946,14 @@ function readAlanMcpRegistration(path2) {
|
|
|
55795
55946
|
var CoreAgent = class _CoreAgent extends BaseMachineAgent {
|
|
55796
55947
|
childProcess = null;
|
|
55797
55948
|
_aborted = false;
|
|
55949
|
+
runId;
|
|
55798
55950
|
providerApiKey;
|
|
55799
55951
|
onProviderSpawned;
|
|
55800
55952
|
/** How long to wait after SIGTERM before escalating to SIGKILL. */
|
|
55801
55953
|
static SIGKILL_DELAY_MS = 3e3;
|
|
55802
55954
|
constructor(presenter, runtime) {
|
|
55803
55955
|
super(presenter, runtime);
|
|
55956
|
+
this.runId = runtime.runId?.trim() || void 0;
|
|
55804
55957
|
this.providerApiKey = runtime.providerApiKey;
|
|
55805
55958
|
this.onProviderSpawned = runtime.onProviderSpawned;
|
|
55806
55959
|
}
|
|
@@ -55931,6 +56084,9 @@ var CoreAgent = class _CoreAgent extends BaseMachineAgent {
|
|
|
55931
56084
|
}
|
|
55932
56085
|
async buildCliEnvironment() {
|
|
55933
56086
|
const augmented = { ...getDaemonCliEnvironment() };
|
|
56087
|
+
augmented.ALAN_MANAGED_RUN = "1";
|
|
56088
|
+
if (this.runId) augmented.ALAN_RUN_ID = this.runId;
|
|
56089
|
+
else delete augmented.ALAN_RUN_ID;
|
|
55934
56090
|
const backendKind = this.runtime.backendKind ?? "";
|
|
55935
56091
|
const providerApiKey = this.providerApiKey ?? "";
|
|
55936
56092
|
if (backendKind === "antigravity_cli" && providerApiKey.trim()) {
|
|
@@ -60474,14 +60630,14 @@ function inputRequiredRoundsExceededMessage(method, maxRounds) {
|
|
|
60474
60630
|
return `Multi-round-trip request '${method}' still required input after ${maxRounds} rounds (inputRequired.maxRounds)`;
|
|
60475
60631
|
}
|
|
60476
60632
|
function sleep2(ms, signal) {
|
|
60477
|
-
return new Promise((
|
|
60633
|
+
return new Promise((resolve15, reject) => {
|
|
60478
60634
|
if (signal?.aborted) {
|
|
60479
60635
|
reject(signal.reason instanceof SdkError ? signal.reason : new SdkError(SdkErrorCode.RequestTimeout, String(signal.reason)));
|
|
60480
60636
|
return;
|
|
60481
60637
|
}
|
|
60482
60638
|
const timer = setTimeout(() => {
|
|
60483
60639
|
signal?.removeEventListener("abort", onAbort);
|
|
60484
|
-
|
|
60640
|
+
resolve15();
|
|
60485
60641
|
}, ms);
|
|
60486
60642
|
const onAbort = () => {
|
|
60487
60643
|
clearTimeout(timer);
|
|
@@ -61320,7 +61476,7 @@ var Protocol = class {
|
|
|
61320
61476
|
const flowStartedAt = Date.now();
|
|
61321
61477
|
let onAbort;
|
|
61322
61478
|
let cleanupMessageId;
|
|
61323
|
-
return new Promise((
|
|
61479
|
+
return new Promise((resolve15, reject) => {
|
|
61324
61480
|
const earlyReject = (error61) => {
|
|
61325
61481
|
reject(error61);
|
|
61326
61482
|
};
|
|
@@ -61388,7 +61544,7 @@ var Protocol = class {
|
|
|
61388
61544
|
}
|
|
61389
61545
|
if (decoded.kind === "invalid") return reject(decoded.error);
|
|
61390
61546
|
if (decoded.kind === "input_required") {
|
|
61391
|
-
if (options?.allowInputRequired === true) return
|
|
61547
|
+
if (options?.allowInputRequired === true) return resolve15(manualInputRequiredValue(decoded));
|
|
61392
61548
|
const flow = {
|
|
61393
61549
|
codec: codec2,
|
|
61394
61550
|
request,
|
|
@@ -61400,11 +61556,11 @@ var Protocol = class {
|
|
|
61400
61556
|
params
|
|
61401
61557
|
}, resultSchema, legOptions)
|
|
61402
61558
|
};
|
|
61403
|
-
return
|
|
61559
|
+
return resolve15(this._resolveNonCompleteResult(decoded, flow));
|
|
61404
61560
|
}
|
|
61405
61561
|
const result = decoded.result;
|
|
61406
61562
|
validateStandardSchema(resultSchema, result).then((parseResult) => {
|
|
61407
|
-
if (parseResult.success)
|
|
61563
|
+
if (parseResult.success) resolve15(parseResult.data);
|
|
61408
61564
|
else reject(new SdkError(SdkErrorCode.InvalidResult, `Invalid result for ${request.method}: ${parseResult.error}`));
|
|
61409
61565
|
}, reject);
|
|
61410
61566
|
});
|
|
@@ -64318,7 +64474,7 @@ var require_compile = /* @__PURE__ */ __commonJSMin(((exports2) => {
|
|
|
64318
64474
|
ref = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, ref);
|
|
64319
64475
|
const schOrFunc = root.refs[ref];
|
|
64320
64476
|
if (schOrFunc) return schOrFunc;
|
|
64321
|
-
let _sch =
|
|
64477
|
+
let _sch = resolve15.call(this, root, ref);
|
|
64322
64478
|
if (_sch === void 0) {
|
|
64323
64479
|
const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
|
|
64324
64480
|
const { schemaId } = this.opts;
|
|
@@ -64344,7 +64500,7 @@ var require_compile = /* @__PURE__ */ __commonJSMin(((exports2) => {
|
|
|
64344
64500
|
function sameSchemaEnv(s1, s2) {
|
|
64345
64501
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
64346
64502
|
}
|
|
64347
|
-
function
|
|
64503
|
+
function resolve15(root, ref) {
|
|
64348
64504
|
let sch;
|
|
64349
64505
|
while (typeof (sch = this.refs[ref]) == "string") ref = sch;
|
|
64350
64506
|
return sch || this.schemas[ref] || resolveSchema.call(this, root, ref);
|
|
@@ -64794,7 +64950,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports2, module2) => {
|
|
|
64794
64950
|
else if (typeof uri === "object") uri = parse7(serialize(uri, options), options);
|
|
64795
64951
|
return uri;
|
|
64796
64952
|
}
|
|
64797
|
-
function
|
|
64953
|
+
function resolve15(baseURI, relativeURI, options) {
|
|
64798
64954
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
64799
64955
|
const resolved = resolveComponent(parse7(baseURI, schemelessOptions), parse7(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
64800
64956
|
schemelessOptions.skipEscape = true;
|
|
@@ -64968,7 +65124,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports2, module2) => {
|
|
|
64968
65124
|
const fastUri = {
|
|
64969
65125
|
SCHEMES,
|
|
64970
65126
|
normalize,
|
|
64971
|
-
resolve:
|
|
65127
|
+
resolve: resolve15,
|
|
64972
65128
|
resolveComponent,
|
|
64973
65129
|
equal,
|
|
64974
65130
|
serialize,
|
|
@@ -65444,10 +65600,10 @@ var require_core$3 = /* @__PURE__ */ __commonJSMin(((exports2) => {
|
|
|
65444
65600
|
Ajv2.ValidationError = validation_error_1.default;
|
|
65445
65601
|
Ajv2.MissingRefError = ref_error_1.default;
|
|
65446
65602
|
exports2.default = Ajv2;
|
|
65447
|
-
function checkOptions(checkOpts, options, msg,
|
|
65603
|
+
function checkOptions(checkOpts, options, msg, log2 = "error") {
|
|
65448
65604
|
for (const key in checkOpts) {
|
|
65449
65605
|
const opt = key;
|
|
65450
|
-
if (opt in options) this.logger[
|
|
65606
|
+
if (opt in options) this.logger[log2](`${msg}: option ${key}. ${checkOpts[opt]}`);
|
|
65451
65607
|
}
|
|
65452
65608
|
}
|
|
65453
65609
|
function getSchEnv(keyRef) {
|
|
@@ -68771,8 +68927,8 @@ async function random(size) {
|
|
|
68771
68927
|
const evenDistCutoff = Math.pow(2, 8) - Math.pow(2, 8) % mask.length;
|
|
68772
68928
|
let result = "";
|
|
68773
68929
|
while (result.length < size) {
|
|
68774
|
-
const
|
|
68775
|
-
for (const randomByte of
|
|
68930
|
+
const randomBytes13 = await getRandomValues(size - result.length);
|
|
68931
|
+
for (const randomByte of randomBytes13) {
|
|
68776
68932
|
if (randomByte < evenDistCutoff) {
|
|
68777
68933
|
result += mask[randomByte % mask.length];
|
|
68778
68934
|
}
|
|
@@ -70410,14 +70566,14 @@ var ProbeWindow = class ProbeWindow2 {
|
|
|
70410
70566
|
*/
|
|
70411
70567
|
async exchange(buildRequest, timeoutMs) {
|
|
70412
70568
|
const id = `server-discover-probe-${++this._probeCounter}`;
|
|
70413
|
-
return new Promise((
|
|
70569
|
+
return new Promise((resolve15) => {
|
|
70414
70570
|
let settled = false;
|
|
70415
70571
|
const settle = (reply) => {
|
|
70416
70572
|
if (settled) return;
|
|
70417
70573
|
settled = true;
|
|
70418
70574
|
clearTimeout(timer);
|
|
70419
70575
|
if (this._pending?.id === id) this._pending = void 0;
|
|
70420
|
-
|
|
70576
|
+
resolve15(reply);
|
|
70421
70577
|
};
|
|
70422
70578
|
const timer = setTimeout(() => settle({ kind: "timeout" }), timeoutMs);
|
|
70423
70579
|
this._pending = {
|
|
@@ -71623,13 +71779,13 @@ var Client = class extends Protocol {
|
|
|
71623
71779
|
let onCallerAbort;
|
|
71624
71780
|
let resolveOpening;
|
|
71625
71781
|
let rejectOpening;
|
|
71626
|
-
const opening = new Promise((
|
|
71627
|
-
resolveOpening =
|
|
71782
|
+
const opening = new Promise((resolve15, reject) => {
|
|
71783
|
+
resolveOpening = resolve15;
|
|
71628
71784
|
rejectOpening = reject;
|
|
71629
71785
|
});
|
|
71630
71786
|
let resolveClosed;
|
|
71631
|
-
const closed = new Promise((
|
|
71632
|
-
resolveClosed =
|
|
71787
|
+
const closed = new Promise((resolve15) => {
|
|
71788
|
+
resolveClosed = resolve15;
|
|
71633
71789
|
});
|
|
71634
71790
|
const settle = (outcome) => {
|
|
71635
71791
|
if (state === "closed") return;
|
|
@@ -72688,7 +72844,7 @@ function failureFromError(error61, paths) {
|
|
|
72688
72844
|
};
|
|
72689
72845
|
}
|
|
72690
72846
|
async function waitBeforeRetry() {
|
|
72691
|
-
await new Promise((
|
|
72847
|
+
await new Promise((resolve15) => setTimeout(resolve15, RETRY_DELAY_MS2));
|
|
72692
72848
|
}
|
|
72693
72849
|
async function probeAlanMcpReadiness(registration, options = {}) {
|
|
72694
72850
|
const paths = options.paths ?? [];
|
|
@@ -72928,7 +73084,7 @@ async function handleAgentExecute(ctx, payload) {
|
|
|
72928
73084
|
});
|
|
72929
73085
|
return;
|
|
72930
73086
|
}
|
|
72931
|
-
const backendKind =
|
|
73087
|
+
const backendKind = normalizeBackendKind(payload.backendKind);
|
|
72932
73088
|
let runtimeCommand = resolveBackendRuntimeCommand(backendKind ?? payload.backendKind);
|
|
72933
73089
|
if (!runtimeCommand && backendKind) {
|
|
72934
73090
|
runtimeCommand = revalidateBackendRuntimeCommand(backendKind);
|
|
@@ -73113,12 +73269,13 @@ async function handleAgentExecute(ctx, payload) {
|
|
|
73113
73269
|
});
|
|
73114
73270
|
return;
|
|
73115
73271
|
}
|
|
73272
|
+
const registrationBackendKind = resolveEffectiveBackendKind(payload.backendKind);
|
|
73116
73273
|
const authPromise = backendKind && runtimeCommand && !payload.providerApiKey ? preflightProviderAuth({
|
|
73117
73274
|
backendKind,
|
|
73118
73275
|
executable: runtimeCommand,
|
|
73119
73276
|
env: getDaemonCliEnvironment()
|
|
73120
73277
|
}).catch(() => ({ status: "unknown" })) : Promise.resolve({ status: "unknown" });
|
|
73121
|
-
const mcpPromise =
|
|
73278
|
+
const mcpPromise = prepareAlanMcpForRun(registrationBackendKind, alanMcp, {
|
|
73122
73279
|
// Observe mode resolves after the run has already been accepted, so
|
|
73123
73280
|
// the verdict is logged here rather than folded into the result.
|
|
73124
73281
|
onObservedResult: (failure) => {
|
|
@@ -73129,7 +73286,7 @@ async function handleAgentExecute(ctx, payload) {
|
|
|
73129
73286
|
stage: failure.stage
|
|
73130
73287
|
});
|
|
73131
73288
|
}
|
|
73132
|
-
})
|
|
73289
|
+
});
|
|
73133
73290
|
const skillTaskPromise = prepareSkillInvocationTask({
|
|
73134
73291
|
manager: runtimeSkillManager,
|
|
73135
73292
|
runtimeId,
|
|
@@ -73165,7 +73322,7 @@ async function handleAgentExecute(ctx, payload) {
|
|
|
73165
73322
|
});
|
|
73166
73323
|
return;
|
|
73167
73324
|
}
|
|
73168
|
-
if (
|
|
73325
|
+
if (!mcpOutcome.ok) {
|
|
73169
73326
|
pendingRunStarts.delete(payload.runId);
|
|
73170
73327
|
releaseRunStart();
|
|
73171
73328
|
const readinessCode = mcpOutcome.code;
|
|
@@ -73182,7 +73339,7 @@ async function handleAgentExecute(ctx, payload) {
|
|
|
73182
73339
|
});
|
|
73183
73340
|
return;
|
|
73184
73341
|
}
|
|
73185
|
-
if (
|
|
73342
|
+
if ("paths" in mcpOutcome) {
|
|
73186
73343
|
pushLog(
|
|
73187
73344
|
`mcp-ready run=${payload.runId} verification=${"verification" in mcpOutcome ? mcpOutcome.verification : "file"}`
|
|
73188
73345
|
);
|
|
@@ -73309,6 +73466,7 @@ async function handleAgentExecute(ctx, payload) {
|
|
|
73309
73466
|
agent = new CoreAgent(presenter, {
|
|
73310
73467
|
backendKind,
|
|
73311
73468
|
runtimeCommand,
|
|
73469
|
+
runId: payload.runId,
|
|
73312
73470
|
providerApiKey: payload.providerApiKey,
|
|
73313
73471
|
onProviderSpawned: (pid) => {
|
|
73314
73472
|
releaseRunStart();
|
|
@@ -73351,10 +73509,8 @@ async function handleAgentExecute(ctx, payload) {
|
|
|
73351
73509
|
workspaceLease: workspaceLeaseCapture.lease
|
|
73352
73510
|
});
|
|
73353
73511
|
pendingRunStarts.delete(payload.runId);
|
|
73354
|
-
|
|
73355
|
-
|
|
73356
|
-
alanMcpRegisteredBackends.add(backendKind);
|
|
73357
|
-
}
|
|
73512
|
+
alanMcpRegisteredRuns.set(payload.runId, alanMcp);
|
|
73513
|
+
alanMcpRegisteredBackends.add(registrationBackendKind);
|
|
73358
73514
|
void agent.run({
|
|
73359
73515
|
task: preparedTask,
|
|
73360
73516
|
maxIterations: payload.maxIterations ?? 50,
|
|
@@ -73708,6 +73864,473 @@ function respondWithLocalSkill(res, operation) {
|
|
|
73708
73864
|
);
|
|
73709
73865
|
}
|
|
73710
73866
|
|
|
73867
|
+
// src/native-hook-drain.ts
|
|
73868
|
+
var PermanentNativeHookDeliveryError = class extends Error {
|
|
73869
|
+
constructor(message) {
|
|
73870
|
+
super(message);
|
|
73871
|
+
this.name = "PermanentNativeHookDeliveryError";
|
|
73872
|
+
}
|
|
73873
|
+
};
|
|
73874
|
+
async function drainNativeHookInbox(input2) {
|
|
73875
|
+
return input2.inbox.drain(async (envelope) => {
|
|
73876
|
+
const isStart = envelope.eventType === "session_start" || envelope.eventType === "subagent_start";
|
|
73877
|
+
const isTerminal2 = envelope.eventType === "stop" || envelope.eventType === "session_end" || envelope.eventType === "subagent_end";
|
|
73878
|
+
const streamTranscript = async () => {
|
|
73879
|
+
if (isStart || !input2.transcriptStreamer || !envelope.transcriptPath) {
|
|
73880
|
+
return "unavailable";
|
|
73881
|
+
}
|
|
73882
|
+
try {
|
|
73883
|
+
const streamed = await input2.transcriptStreamer.stream(
|
|
73884
|
+
envelope,
|
|
73885
|
+
(event) => input2.sink.deliver(event)
|
|
73886
|
+
);
|
|
73887
|
+
return streamed.complete ? streamed.assistantOutputDelivered ? "replayed" : "unavailable" : "deferred";
|
|
73888
|
+
} catch (error61) {
|
|
73889
|
+
if (error61 instanceof PermanentNativeHookDeliveryError) throw error61;
|
|
73890
|
+
const normalizedError = error61 instanceof Error ? error61 : new Error(String(error61));
|
|
73891
|
+
input2.onTranscriptError?.({
|
|
73892
|
+
envelope,
|
|
73893
|
+
error: normalizedError
|
|
73894
|
+
});
|
|
73895
|
+
if (isMissingTranscriptError(normalizedError) && transcriptGraceExpired(
|
|
73896
|
+
envelope.observedAt,
|
|
73897
|
+
input2.now?.() ?? Date.now(),
|
|
73898
|
+
input2.missingTranscriptGraceMs ?? 1e4
|
|
73899
|
+
)) {
|
|
73900
|
+
return "unavailable";
|
|
73901
|
+
}
|
|
73902
|
+
return "deferred";
|
|
73903
|
+
}
|
|
73904
|
+
};
|
|
73905
|
+
if (isTerminal2) {
|
|
73906
|
+
const transcriptReplay = await streamTranscript();
|
|
73907
|
+
if (transcriptReplay === "deferred") return false;
|
|
73908
|
+
return input2.sink.deliver(
|
|
73909
|
+
transcriptReplay === "replayed" ? { ...envelope, assistantOutputReplayed: true } : envelope
|
|
73910
|
+
);
|
|
73911
|
+
}
|
|
73912
|
+
if (!await input2.sink.deliver(envelope)) return false;
|
|
73913
|
+
return await streamTranscript() !== "deferred";
|
|
73914
|
+
}, input2.limit);
|
|
73915
|
+
}
|
|
73916
|
+
function isMissingTranscriptError(error61) {
|
|
73917
|
+
return error61.code === "ENOENT";
|
|
73918
|
+
}
|
|
73919
|
+
function transcriptGraceExpired(observedAt, now, graceMs) {
|
|
73920
|
+
const observedAtMs = Date.parse(observedAt);
|
|
73921
|
+
if (!Number.isFinite(observedAtMs)) return true;
|
|
73922
|
+
return now - observedAtMs >= Math.max(0, graceMs);
|
|
73923
|
+
}
|
|
73924
|
+
|
|
73925
|
+
// src/native-hook-inbox.ts
|
|
73926
|
+
var import_node_crypto8 = require("crypto");
|
|
73927
|
+
var import_node_fs16 = require("fs");
|
|
73928
|
+
var import_node_path14 = require("path");
|
|
73929
|
+
var INBOX_FILE_VERSION = 1;
|
|
73930
|
+
var DEFAULT_MAX_BYTES = 64 * 1024 * 1024;
|
|
73931
|
+
var DEFAULT_MAX_ENTRIES = 1e4;
|
|
73932
|
+
function decodeKey2(encodedKey) {
|
|
73933
|
+
const key = Buffer.from(encodedKey, "base64url");
|
|
73934
|
+
if (key.length !== 32) throw new Error("native hook inbox key must contain exactly 32 bytes");
|
|
73935
|
+
return key;
|
|
73936
|
+
}
|
|
73937
|
+
function encryptedEntryNames(path2) {
|
|
73938
|
+
if (!(0, import_node_fs16.existsSync)(path2)) return [];
|
|
73939
|
+
return (0, import_node_fs16.readdirSync)(path2).filter((name) => name.endsWith(".event") || name.includes(".processing-")).sort();
|
|
73940
|
+
}
|
|
73941
|
+
var EncryptedNativeHookInbox = class {
|
|
73942
|
+
constructor(path2, encodedKey, options = {}) {
|
|
73943
|
+
this.path = path2;
|
|
73944
|
+
this.key = decodeKey2(encodedKey);
|
|
73945
|
+
this.maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
73946
|
+
this.maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;
|
|
73947
|
+
this.onQuarantine = options.onQuarantine;
|
|
73948
|
+
if (!Number.isSafeInteger(this.maxBytes) || this.maxBytes <= 0) {
|
|
73949
|
+
throw new Error("native hook inbox maxBytes must be a positive integer");
|
|
73950
|
+
}
|
|
73951
|
+
if (!Number.isSafeInteger(this.maxEntries) || this.maxEntries <= 0) {
|
|
73952
|
+
throw new Error("native hook inbox maxEntries must be a positive integer");
|
|
73953
|
+
}
|
|
73954
|
+
(0, import_node_fs16.mkdirSync)(this.path, { recursive: true, mode: 448 });
|
|
73955
|
+
(0, import_node_fs16.chmodSync)(this.path, 448);
|
|
73956
|
+
if (options.recoverClaims) this.recoverClaims();
|
|
73957
|
+
}
|
|
73958
|
+
key;
|
|
73959
|
+
maxBytes;
|
|
73960
|
+
maxEntries;
|
|
73961
|
+
onQuarantine;
|
|
73962
|
+
enqueue(envelope) {
|
|
73963
|
+
const plaintext = Buffer.from(JSON.stringify(envelope), "utf8");
|
|
73964
|
+
const iv = (0, import_node_crypto8.randomBytes)(12);
|
|
73965
|
+
const cipher = (0, import_node_crypto8.createCipheriv)("aes-256-gcm", this.key, iv);
|
|
73966
|
+
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
73967
|
+
const file2 = {
|
|
73968
|
+
version: INBOX_FILE_VERSION,
|
|
73969
|
+
iv: iv.toString("base64url"),
|
|
73970
|
+
authTag: cipher.getAuthTag().toString("base64url"),
|
|
73971
|
+
ciphertext: ciphertext.toString("base64url")
|
|
73972
|
+
};
|
|
73973
|
+
const monotonic = process.hrtime.bigint().toString().padStart(20, "0");
|
|
73974
|
+
const prefix = `${String(Date.now()).padStart(13, "0")}-${monotonic}-${envelope.eventId}`;
|
|
73975
|
+
const temporaryPath = (0, import_node_path14.join)(this.path, `.${prefix}-${(0, import_node_crypto8.randomUUID)()}.tmp`);
|
|
73976
|
+
const finalPath = (0, import_node_path14.join)(this.path, `${prefix}.event`);
|
|
73977
|
+
(0, import_node_fs16.writeFileSync)(temporaryPath, JSON.stringify(file2), { mode: 384, flag: "wx" });
|
|
73978
|
+
(0, import_node_fs16.chmodSync)(temporaryPath, 384);
|
|
73979
|
+
(0, import_node_fs16.renameSync)(temporaryPath, finalPath);
|
|
73980
|
+
try {
|
|
73981
|
+
this.enforceBounds();
|
|
73982
|
+
} catch (error61) {
|
|
73983
|
+
(0, import_node_fs16.rmSync)(finalPath, { force: true });
|
|
73984
|
+
throw error61;
|
|
73985
|
+
}
|
|
73986
|
+
}
|
|
73987
|
+
size() {
|
|
73988
|
+
return encryptedEntryNames(this.path).length;
|
|
73989
|
+
}
|
|
73990
|
+
async drain(consume, limit = 100) {
|
|
73991
|
+
const result = { attempted: 0, acknowledged: 0, deferred: 0 };
|
|
73992
|
+
const names = encryptedEntryNames(this.path).filter((name) => name.endsWith(".event")).slice(0, Math.max(0, limit));
|
|
73993
|
+
for (const name of names) {
|
|
73994
|
+
const sourcePath = (0, import_node_path14.join)(this.path, name);
|
|
73995
|
+
const claimedName = `${name.slice(0, -".event".length)}.processing-${(0, import_node_crypto8.randomUUID)()}`;
|
|
73996
|
+
const claimedPath = (0, import_node_path14.join)(this.path, claimedName);
|
|
73997
|
+
try {
|
|
73998
|
+
(0, import_node_fs16.renameSync)(sourcePath, claimedPath);
|
|
73999
|
+
} catch {
|
|
74000
|
+
continue;
|
|
74001
|
+
}
|
|
74002
|
+
result.attempted += 1;
|
|
74003
|
+
try {
|
|
74004
|
+
const envelope = this.readEntry(claimedPath);
|
|
74005
|
+
if (await consume(envelope)) {
|
|
74006
|
+
(0, import_node_fs16.rmSync)(claimedPath, { force: true });
|
|
74007
|
+
result.acknowledged += 1;
|
|
74008
|
+
} else {
|
|
74009
|
+
(0, import_node_fs16.renameSync)(claimedPath, sourcePath);
|
|
74010
|
+
result.deferred += 1;
|
|
74011
|
+
break;
|
|
74012
|
+
}
|
|
74013
|
+
} catch (error61) {
|
|
74014
|
+
if ((0, import_node_fs16.existsSync)(claimedPath)) {
|
|
74015
|
+
const quarantinedPath = `${claimedPath}.quarantine`;
|
|
74016
|
+
(0, import_node_fs16.renameSync)(claimedPath, quarantinedPath);
|
|
74017
|
+
this.onQuarantine?.({
|
|
74018
|
+
fileName: quarantinedPath.slice(quarantinedPath.lastIndexOf("/") + 1),
|
|
74019
|
+
error: error61 instanceof Error ? error61 : new Error(String(error61))
|
|
74020
|
+
});
|
|
74021
|
+
}
|
|
74022
|
+
result.deferred += 1;
|
|
74023
|
+
}
|
|
74024
|
+
}
|
|
74025
|
+
return result;
|
|
74026
|
+
}
|
|
74027
|
+
readEntry(path2) {
|
|
74028
|
+
const file2 = JSON.parse((0, import_node_fs16.readFileSync)(path2, "utf8"));
|
|
74029
|
+
if (file2.version !== INBOX_FILE_VERSION) throw new Error("unsupported native hook inbox entry");
|
|
74030
|
+
const decipher = (0, import_node_crypto8.createDecipheriv)("aes-256-gcm", this.key, Buffer.from(file2.iv, "base64url"));
|
|
74031
|
+
decipher.setAuthTag(Buffer.from(file2.authTag, "base64url"));
|
|
74032
|
+
const plaintext = Buffer.concat([
|
|
74033
|
+
decipher.update(Buffer.from(file2.ciphertext, "base64url")),
|
|
74034
|
+
decipher.final()
|
|
74035
|
+
]);
|
|
74036
|
+
return JSON.parse(plaintext.toString("utf8"));
|
|
74037
|
+
}
|
|
74038
|
+
recoverClaims() {
|
|
74039
|
+
for (const name of encryptedEntryNames(this.path)) {
|
|
74040
|
+
if (name.endsWith(".quarantine")) continue;
|
|
74041
|
+
const marker = name.indexOf(".processing-");
|
|
74042
|
+
if (marker === -1) continue;
|
|
74043
|
+
const claimedPath = (0, import_node_path14.join)(this.path, name);
|
|
74044
|
+
const eventPath = (0, import_node_path14.join)(this.path, `${name.slice(0, marker)}.event`);
|
|
74045
|
+
if ((0, import_node_fs16.existsSync)(eventPath)) (0, import_node_fs16.rmSync)(claimedPath, { force: true });
|
|
74046
|
+
else (0, import_node_fs16.renameSync)(claimedPath, eventPath);
|
|
74047
|
+
}
|
|
74048
|
+
}
|
|
74049
|
+
enforceBounds() {
|
|
74050
|
+
const entries = encryptedEntryNames(this.path).filter((name) => name.endsWith(".event")).map((name) => ({ name, bytes: (0, import_node_fs16.statSync)((0, import_node_path14.join)(this.path, name)).size }));
|
|
74051
|
+
const totalBytes = entries.reduce((sum, entry) => sum + entry.bytes, 0);
|
|
74052
|
+
if (entries.length > this.maxEntries || totalBytes > this.maxBytes) {
|
|
74053
|
+
throw new Error(
|
|
74054
|
+
`native hook inbox capacity exceeded (${entries.length} entries, ${totalBytes} bytes)`
|
|
74055
|
+
);
|
|
74056
|
+
}
|
|
74057
|
+
}
|
|
74058
|
+
};
|
|
74059
|
+
function nativeHookInboxPath(configPath) {
|
|
74060
|
+
return (0, import_node_path14.join)((0, import_node_path14.dirname)(configPath), "native-hook-inbox");
|
|
74061
|
+
}
|
|
74062
|
+
|
|
74063
|
+
// src/native-transcript-cursor.ts
|
|
74064
|
+
var import_node_crypto9 = require("crypto");
|
|
74065
|
+
var import_node_fs17 = require("fs");
|
|
74066
|
+
var import_node_path15 = require("path");
|
|
74067
|
+
var CURSOR_FILE_VERSION = 1;
|
|
74068
|
+
function nativeTranscriptCursorKey(input2) {
|
|
74069
|
+
return (0, import_node_crypto9.createHash)("sha256").update(`${input2.provider}\0${input2.externalSessionId}\0${input2.transcriptPath}`).digest("base64url");
|
|
74070
|
+
}
|
|
74071
|
+
var NativeTranscriptCursorStore = class {
|
|
74072
|
+
constructor(path2) {
|
|
74073
|
+
this.path = path2;
|
|
74074
|
+
this.entries = this.read();
|
|
74075
|
+
}
|
|
74076
|
+
entries;
|
|
74077
|
+
get(key) {
|
|
74078
|
+
const value2 = this.entries[key];
|
|
74079
|
+
return value2 ? { ...value2 } : void 0;
|
|
74080
|
+
}
|
|
74081
|
+
commit(key, cursor) {
|
|
74082
|
+
if (!Number.isSafeInteger(cursor.offset) || cursor.offset < 0) {
|
|
74083
|
+
throw new Error("native transcript cursor offset must be a non-negative safe integer");
|
|
74084
|
+
}
|
|
74085
|
+
this.entries[key] = { ...cursor };
|
|
74086
|
+
this.persist();
|
|
74087
|
+
}
|
|
74088
|
+
read() {
|
|
74089
|
+
if (!(0, import_node_fs17.existsSync)(this.path)) return {};
|
|
74090
|
+
const parsed = JSON.parse((0, import_node_fs17.readFileSync)(this.path, "utf8"));
|
|
74091
|
+
if (parsed.version !== CURSOR_FILE_VERSION || !parsed.entries || typeof parsed.entries !== "object") {
|
|
74092
|
+
throw new Error("unsupported native transcript cursor file");
|
|
74093
|
+
}
|
|
74094
|
+
return parsed.entries;
|
|
74095
|
+
}
|
|
74096
|
+
persist() {
|
|
74097
|
+
(0, import_node_fs17.mkdirSync)((0, import_node_path15.dirname)(this.path), { recursive: true, mode: 448 });
|
|
74098
|
+
const pendingPath = `${this.path}.pending-${process.pid}-${(0, import_node_crypto9.randomBytes)(6).toString("hex")}`;
|
|
74099
|
+
try {
|
|
74100
|
+
(0, import_node_fs17.writeFileSync)(
|
|
74101
|
+
pendingPath,
|
|
74102
|
+
JSON.stringify({
|
|
74103
|
+
version: CURSOR_FILE_VERSION,
|
|
74104
|
+
entries: this.entries
|
|
74105
|
+
}),
|
|
74106
|
+
{ mode: 384 }
|
|
74107
|
+
);
|
|
74108
|
+
(0, import_node_fs17.chmodSync)(pendingPath, 384);
|
|
74109
|
+
(0, import_node_fs17.renameSync)(pendingPath, this.path);
|
|
74110
|
+
} finally {
|
|
74111
|
+
(0, import_node_fs17.rmSync)(pendingPath, { force: true });
|
|
74112
|
+
}
|
|
74113
|
+
}
|
|
74114
|
+
};
|
|
74115
|
+
|
|
74116
|
+
// src/native-transcript-stream.ts
|
|
74117
|
+
var import_node_crypto10 = require("crypto");
|
|
74118
|
+
var import_node_fs18 = require("fs");
|
|
74119
|
+
var DEFAULT_MAX_READ_BYTES = 4 * 1024 * 1024;
|
|
74120
|
+
var NativeTranscriptStreamer = class {
|
|
74121
|
+
constructor(cursorStore, maxReadBytes = DEFAULT_MAX_READ_BYTES) {
|
|
74122
|
+
this.cursorStore = cursorStore;
|
|
74123
|
+
this.maxReadBytes = maxReadBytes;
|
|
74124
|
+
if (!Number.isSafeInteger(maxReadBytes) || maxReadBytes <= 0) {
|
|
74125
|
+
throw new Error("native transcript maxReadBytes must be a positive integer");
|
|
74126
|
+
}
|
|
74127
|
+
}
|
|
74128
|
+
async stream(hook, deliver) {
|
|
74129
|
+
if (!hook.transcriptPath) {
|
|
74130
|
+
return {
|
|
74131
|
+
complete: true,
|
|
74132
|
+
recordsRead: 0,
|
|
74133
|
+
eventsDelivered: 0,
|
|
74134
|
+
assistantOutputDelivered: false
|
|
74135
|
+
};
|
|
74136
|
+
}
|
|
74137
|
+
const key = nativeTranscriptCursorKey({
|
|
74138
|
+
provider: hook.provider,
|
|
74139
|
+
externalSessionId: hook.externalSessionId,
|
|
74140
|
+
transcriptPath: hook.transcriptPath
|
|
74141
|
+
});
|
|
74142
|
+
let previous = this.cursorStore.get(key);
|
|
74143
|
+
if (hook.eventType === "user_prompt" && previous && previous.assistantOutputTurnId !== hook.eventId) {
|
|
74144
|
+
previous = {
|
|
74145
|
+
...previous,
|
|
74146
|
+
assistantOutputSeen: false,
|
|
74147
|
+
assistantOutputTurnId: hook.eventId
|
|
74148
|
+
};
|
|
74149
|
+
this.cursorStore.commit(key, previous);
|
|
74150
|
+
}
|
|
74151
|
+
const file2 = (0, import_node_fs18.statSync)(hook.transcriptPath);
|
|
74152
|
+
const replaced = Boolean(previous) && (previous.device !== file2.dev || previous.inode !== file2.ino);
|
|
74153
|
+
const truncated = Boolean(previous) && file2.size < previous.offset;
|
|
74154
|
+
const start = !previous || replaced || truncated ? {
|
|
74155
|
+
offset: 0,
|
|
74156
|
+
generation: previous ? previous.generation + 1 : 0,
|
|
74157
|
+
device: file2.dev,
|
|
74158
|
+
inode: file2.ino,
|
|
74159
|
+
assistantOutputSeen: previous?.assistantOutputSeen ?? false,
|
|
74160
|
+
assistantOutputTurnId: hook.eventType === "user_prompt" ? hook.eventId : previous?.assistantOutputTurnId
|
|
74161
|
+
} : previous;
|
|
74162
|
+
let assistantOutputSeen = start.assistantOutputSeen ?? false;
|
|
74163
|
+
if (file2.size <= start.offset) {
|
|
74164
|
+
return {
|
|
74165
|
+
complete: true,
|
|
74166
|
+
recordsRead: 0,
|
|
74167
|
+
eventsDelivered: 0,
|
|
74168
|
+
assistantOutputDelivered: assistantOutputSeen
|
|
74169
|
+
};
|
|
74170
|
+
}
|
|
74171
|
+
const bytesToRead = Math.min(file2.size - start.offset, this.maxReadBytes);
|
|
74172
|
+
const buffer = Buffer.allocUnsafe(bytesToRead);
|
|
74173
|
+
const fd = (0, import_node_fs18.openSync)(hook.transcriptPath, "r");
|
|
74174
|
+
let bytesRead = 0;
|
|
74175
|
+
try {
|
|
74176
|
+
bytesRead = (0, import_node_fs18.readSync)(fd, buffer, 0, bytesToRead, start.offset);
|
|
74177
|
+
} finally {
|
|
74178
|
+
(0, import_node_fs18.closeSync)(fd);
|
|
74179
|
+
}
|
|
74180
|
+
const chunk = buffer.subarray(0, bytesRead);
|
|
74181
|
+
const lastNewline = chunk.lastIndexOf(10);
|
|
74182
|
+
if (lastNewline < 0) {
|
|
74183
|
+
return {
|
|
74184
|
+
complete: false,
|
|
74185
|
+
recordsRead: 0,
|
|
74186
|
+
eventsDelivered: 0,
|
|
74187
|
+
assistantOutputDelivered: assistantOutputSeen
|
|
74188
|
+
};
|
|
74189
|
+
}
|
|
74190
|
+
let recordStart = 0;
|
|
74191
|
+
let recordsRead = 0;
|
|
74192
|
+
let eventsDelivered = 0;
|
|
74193
|
+
let committedOffset = start.offset;
|
|
74194
|
+
while (recordStart <= lastNewline) {
|
|
74195
|
+
const newline = chunk.indexOf(10, recordStart);
|
|
74196
|
+
if (newline < 0 || newline > lastNewline) break;
|
|
74197
|
+
const recordEndOffset = start.offset + newline + 1;
|
|
74198
|
+
const raw = chunk.subarray(recordStart, newline).toString("utf8").trim();
|
|
74199
|
+
recordStart = newline + 1;
|
|
74200
|
+
if (!raw) {
|
|
74201
|
+
committedOffset = recordEndOffset;
|
|
74202
|
+
this.cursorStore.commit(key, {
|
|
74203
|
+
...start,
|
|
74204
|
+
offset: committedOffset,
|
|
74205
|
+
assistantOutputSeen
|
|
74206
|
+
});
|
|
74207
|
+
continue;
|
|
74208
|
+
}
|
|
74209
|
+
const parsed = parseJsonObject3(raw);
|
|
74210
|
+
const events = parsed ? claudeRecordToAgentEvents(parsed) : [];
|
|
74211
|
+
for (let index = 0; index < events.length; index += 1) {
|
|
74212
|
+
const deliveredEvent = events[index];
|
|
74213
|
+
const sourceCursor = `${start.generation}:${recordEndOffset}:${index}`;
|
|
74214
|
+
const eventId = stableSourceEventId(hook, sourceCursor);
|
|
74215
|
+
const accepted = await deliver({
|
|
74216
|
+
schemaVersion: 1,
|
|
74217
|
+
eventId,
|
|
74218
|
+
provider: hook.provider,
|
|
74219
|
+
externalSessionId: hook.externalSessionId,
|
|
74220
|
+
sourceCursor,
|
|
74221
|
+
observedAt: timestampOf(parsed) ?? hook.observedAt,
|
|
74222
|
+
event: deliveredEvent
|
|
74223
|
+
});
|
|
74224
|
+
if (!accepted) {
|
|
74225
|
+
return {
|
|
74226
|
+
complete: false,
|
|
74227
|
+
recordsRead,
|
|
74228
|
+
eventsDelivered,
|
|
74229
|
+
assistantOutputDelivered: assistantOutputSeen,
|
|
74230
|
+
cursor: `${start.generation}:${committedOffset}`
|
|
74231
|
+
};
|
|
74232
|
+
}
|
|
74233
|
+
eventsDelivered += 1;
|
|
74234
|
+
if (deliveredEvent.type === "text" && deliveredEvent.text.length > 0) {
|
|
74235
|
+
assistantOutputSeen = true;
|
|
74236
|
+
}
|
|
74237
|
+
}
|
|
74238
|
+
committedOffset = recordEndOffset;
|
|
74239
|
+
recordsRead += 1;
|
|
74240
|
+
this.cursorStore.commit(key, {
|
|
74241
|
+
...start,
|
|
74242
|
+
offset: committedOffset,
|
|
74243
|
+
assistantOutputSeen
|
|
74244
|
+
});
|
|
74245
|
+
}
|
|
74246
|
+
return {
|
|
74247
|
+
complete: committedOffset >= file2.size,
|
|
74248
|
+
recordsRead,
|
|
74249
|
+
eventsDelivered,
|
|
74250
|
+
assistantOutputDelivered: assistantOutputSeen,
|
|
74251
|
+
cursor: `${start.generation}:${committedOffset}`
|
|
74252
|
+
};
|
|
74253
|
+
}
|
|
74254
|
+
};
|
|
74255
|
+
function stableSourceEventId(hook, sourceCursor) {
|
|
74256
|
+
const bytes = (0, import_node_crypto10.createHash)("sha256").update(`${hook.provider}\0${hook.externalSessionId}\0${hook.transcriptPath}\0${sourceCursor}`).digest().subarray(0, 16);
|
|
74257
|
+
bytes[6] = bytes[6] & 15 | 80;
|
|
74258
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
74259
|
+
const hex3 = bytes.toString("hex");
|
|
74260
|
+
return `${hex3.slice(0, 8)}-${hex3.slice(8, 12)}-${hex3.slice(12, 16)}-${hex3.slice(16, 20)}-${hex3.slice(20)}`;
|
|
74261
|
+
}
|
|
74262
|
+
function parseJsonObject3(value2) {
|
|
74263
|
+
try {
|
|
74264
|
+
const parsed = JSON.parse(value2);
|
|
74265
|
+
return isRecord2(parsed) ? parsed : null;
|
|
74266
|
+
} catch {
|
|
74267
|
+
return null;
|
|
74268
|
+
}
|
|
74269
|
+
}
|
|
74270
|
+
function claudeRecordToAgentEvents(record2) {
|
|
74271
|
+
const timestamp = timestampOf(record2) ? Date.parse(timestampOf(record2)) : Date.now();
|
|
74272
|
+
const ts = Number.isFinite(timestamp) ? timestamp : Date.now();
|
|
74273
|
+
const message = isRecord2(record2.message) ? record2.message : record2;
|
|
74274
|
+
const content = message.content;
|
|
74275
|
+
const role = typeof message.role === "string" ? message.role : typeof record2.type === "string" ? record2.type : null;
|
|
74276
|
+
if (role === "user") {
|
|
74277
|
+
if (!Array.isArray(content)) return [];
|
|
74278
|
+
return content.flatMap((block) => {
|
|
74279
|
+
if (!isRecord2(block) || block.type !== "tool_result") return [];
|
|
74280
|
+
const id = typeof block.tool_use_id === "string" ? block.tool_use_id : typeof block.toolUseId === "string" ? block.toolUseId : null;
|
|
74281
|
+
return id ? [{ type: "tool_result", id, content: extractText2(block.content), ts }] : [];
|
|
74282
|
+
});
|
|
74283
|
+
}
|
|
74284
|
+
if (record2.type === "summary" && typeof record2.summary === "string") {
|
|
74285
|
+
return [{ type: "session_notice", kind: "summary", message: record2.summary, ts }];
|
|
74286
|
+
}
|
|
74287
|
+
if (typeof record2.type === "string" && role !== "assistant") return [];
|
|
74288
|
+
if (Array.isArray(content)) {
|
|
74289
|
+
const events = content.flatMap((block) => {
|
|
74290
|
+
if (!isRecord2(block)) return [];
|
|
74291
|
+
if (block.type === "text" && typeof block.text === "string") {
|
|
74292
|
+
return [{ type: "text", text: block.text, ts }];
|
|
74293
|
+
}
|
|
74294
|
+
if (block.type === "thinking" && typeof block.thinking === "string") {
|
|
74295
|
+
return [{ type: "thinking", text: block.thinking, ts }];
|
|
74296
|
+
}
|
|
74297
|
+
if (block.type === "tool_use" && typeof block.id === "string" && typeof block.name === "string") {
|
|
74298
|
+
return [{ type: "tool_use", id: block.id, name: block.name, input: block.input, ts }];
|
|
74299
|
+
}
|
|
74300
|
+
if (block.type === "tool_result" && typeof block.tool_use_id === "string") {
|
|
74301
|
+
return [
|
|
74302
|
+
{ type: "tool_result", id: block.tool_use_id, content: extractText2(block.content), ts }
|
|
74303
|
+
];
|
|
74304
|
+
}
|
|
74305
|
+
return [];
|
|
74306
|
+
});
|
|
74307
|
+
if (events.length > 0) return events;
|
|
74308
|
+
}
|
|
74309
|
+
if (typeof content === "string" && content.length > 0)
|
|
74310
|
+
return [{ type: "text", text: content, ts }];
|
|
74311
|
+
return [];
|
|
74312
|
+
}
|
|
74313
|
+
function extractText2(value2) {
|
|
74314
|
+
if (typeof value2 === "string") return value2;
|
|
74315
|
+
if (Array.isArray(value2)) return value2.map(extractText2).filter(Boolean).join("\n");
|
|
74316
|
+
if (isRecord2(value2)) {
|
|
74317
|
+
if (typeof value2.text === "string") return value2.text;
|
|
74318
|
+
if (typeof value2.content === "string") return value2.content;
|
|
74319
|
+
}
|
|
74320
|
+
return JSON.stringify(value2);
|
|
74321
|
+
}
|
|
74322
|
+
function timestampOf(record2) {
|
|
74323
|
+
if (!record2) return null;
|
|
74324
|
+
for (const key of ["timestamp", "created_at", "createdAt"]) {
|
|
74325
|
+
const value2 = record2[key];
|
|
74326
|
+
if (typeof value2 === "string" && !Number.isNaN(Date.parse(value2))) return value2;
|
|
74327
|
+
}
|
|
74328
|
+
return null;
|
|
74329
|
+
}
|
|
74330
|
+
function isRecord2(value2) {
|
|
74331
|
+
return Boolean(value2) && typeof value2 === "object" && !Array.isArray(value2);
|
|
74332
|
+
}
|
|
74333
|
+
|
|
73711
74334
|
// src/network-diagnostics.ts
|
|
73712
74335
|
function collectEvidence(error61) {
|
|
73713
74336
|
const text = [];
|
|
@@ -73785,8 +74408,8 @@ var ObservableMap = class extends Map {
|
|
|
73785
74408
|
};
|
|
73786
74409
|
|
|
73787
74410
|
// src/run-journal.ts
|
|
73788
|
-
var
|
|
73789
|
-
var
|
|
74411
|
+
var import_node_fs19 = require("fs");
|
|
74412
|
+
var import_node_path16 = require("path");
|
|
73790
74413
|
var JOURNAL_VERSION = 1;
|
|
73791
74414
|
var INTERRUPTED_ENTRY_TTL_MS = 24 * 60 * 6e4;
|
|
73792
74415
|
function isEntry(value2) {
|
|
@@ -73805,9 +74428,9 @@ function isEntry(value2) {
|
|
|
73805
74428
|
var RuntimeRunJournal = class {
|
|
73806
74429
|
constructor(path2, nowMs = Date.now()) {
|
|
73807
74430
|
this.path = path2;
|
|
73808
|
-
if (!(0,
|
|
74431
|
+
if (!(0, import_node_fs19.existsSync)(path2)) return;
|
|
73809
74432
|
try {
|
|
73810
|
-
const parsed = JSON.parse((0,
|
|
74433
|
+
const parsed = JSON.parse((0, import_node_fs19.readFileSync)(path2, "utf8"));
|
|
73811
74434
|
if (parsed.version !== JOURNAL_VERSION || !Array.isArray(parsed.entries)) {
|
|
73812
74435
|
throw new Error("unsupported run journal format");
|
|
73813
74436
|
}
|
|
@@ -73930,18 +74553,18 @@ var RuntimeRunJournal = class {
|
|
|
73930
74553
|
}
|
|
73931
74554
|
persist() {
|
|
73932
74555
|
const pendingPath = `${this.path}.pending`;
|
|
73933
|
-
(0,
|
|
74556
|
+
(0, import_node_fs19.mkdirSync)((0, import_node_path16.dirname)(this.path), { recursive: true });
|
|
73934
74557
|
try {
|
|
73935
|
-
(0,
|
|
74558
|
+
(0, import_node_fs19.writeFileSync)(
|
|
73936
74559
|
pendingPath,
|
|
73937
74560
|
`${JSON.stringify({ version: JOURNAL_VERSION, entries: [...this.entries.values()] })}
|
|
73938
74561
|
`,
|
|
73939
74562
|
{ mode: 384 }
|
|
73940
74563
|
);
|
|
73941
|
-
(0,
|
|
73942
|
-
(0,
|
|
74564
|
+
(0, import_node_fs19.chmodSync)(pendingPath, 384);
|
|
74565
|
+
(0, import_node_fs19.renameSync)(pendingPath, this.path);
|
|
73943
74566
|
} catch (error61) {
|
|
73944
|
-
(0,
|
|
74567
|
+
(0, import_node_fs19.rmSync)(pendingPath, { force: true });
|
|
73945
74568
|
throw error61;
|
|
73946
74569
|
}
|
|
73947
74570
|
}
|
|
@@ -73953,8 +74576,8 @@ var RunStartGate = class {
|
|
|
73953
74576
|
async acquire() {
|
|
73954
74577
|
const previous = this.tail;
|
|
73955
74578
|
let releaseCurrent;
|
|
73956
|
-
const current = new Promise((
|
|
73957
|
-
releaseCurrent =
|
|
74579
|
+
const current = new Promise((resolve15) => {
|
|
74580
|
+
releaseCurrent = resolve15;
|
|
73958
74581
|
});
|
|
73959
74582
|
this.tail = previous.then(() => current);
|
|
73960
74583
|
await previous;
|
|
@@ -73969,15 +74592,15 @@ var RunStartGate = class {
|
|
|
73969
74592
|
|
|
73970
74593
|
// src/skills/local-skill-source.ts
|
|
73971
74594
|
var import_node_buffer2 = require("buffer");
|
|
73972
|
-
var
|
|
74595
|
+
var import_node_crypto12 = require("crypto");
|
|
73973
74596
|
var import_promises2 = require("fs/promises");
|
|
73974
|
-
var
|
|
74597
|
+
var import_node_path18 = __toESM(require("path"), 1);
|
|
73975
74598
|
|
|
73976
74599
|
// ../shared/dist/node/skill-package.js
|
|
73977
74600
|
var import_node_buffer = require("buffer");
|
|
73978
|
-
var
|
|
74601
|
+
var import_node_crypto11 = require("crypto");
|
|
73979
74602
|
var import_promises = require("fs/promises");
|
|
73980
|
-
var
|
|
74603
|
+
var import_node_path17 = require("path");
|
|
73981
74604
|
var import_yaml = __toESM(require_dist2(), 1);
|
|
73982
74605
|
|
|
73983
74606
|
// ../shared/dist/skills/domain.js
|
|
@@ -74401,7 +75024,7 @@ async function inspectSkillPackage(packageRoot, options = {}) {
|
|
|
74401
75024
|
}
|
|
74402
75025
|
const inspectedFiles = await Promise.all(files.map(readPackageFile));
|
|
74403
75026
|
inspectedFiles.sort(comparePackageFiles);
|
|
74404
|
-
const parsedMetadata = parseSkillMetadata(inspectedFiles.find((file2) => file2.manifest.path === "SKILL.md")?.bytes, options.expectedName === void 0 ? (0,
|
|
75027
|
+
const parsedMetadata = parseSkillMetadata(inspectedFiles.find((file2) => file2.manifest.path === "SKILL.md")?.bytes, options.expectedName === void 0 ? (0, import_node_path17.basename)(root) : options.expectedName);
|
|
74405
75028
|
const manifestFiles = inspectedFiles.map((file2) => file2.manifest);
|
|
74406
75029
|
const diagnostics = [
|
|
74407
75030
|
...parsedMetadata.conformance,
|
|
@@ -74425,7 +75048,7 @@ async function readSkillPackageFile(packageRoot, path2, expectedSha256) {
|
|
|
74425
75048
|
if (!normalizedPath || normalizedPath !== path2 || normalizedPath.includes("\\") || components.some((component) => !component || component === "." || component === "..")) {
|
|
74426
75049
|
throw new SkillPackageError("INVALID_PATH", `Invalid Skill package path: ${path2}`, path2);
|
|
74427
75050
|
}
|
|
74428
|
-
const absolutePath = await (0, import_promises.realpath)((0,
|
|
75051
|
+
const absolutePath = await (0, import_promises.realpath)((0, import_node_path17.resolve)(root, ...components)).catch(() => null);
|
|
74429
75052
|
if (!absolutePath || normalizeRelativePath(root, absolutePath) !== normalizedPath) {
|
|
74430
75053
|
throw new SkillPackageError("INVALID_PATH", `Skill package file was not found: ${path2}`, path2);
|
|
74431
75054
|
}
|
|
@@ -74446,7 +75069,7 @@ async function readSkillPackageFile(packageRoot, path2, expectedSha256) {
|
|
|
74446
75069
|
return { file: read.manifest, bytes: read.bytes };
|
|
74447
75070
|
}
|
|
74448
75071
|
async function requirePackageRoot(packageRoot) {
|
|
74449
|
-
const root = await (0, import_promises.realpath)((0,
|
|
75072
|
+
const root = await (0, import_promises.realpath)((0, import_node_path17.resolve)(packageRoot)).catch(() => null);
|
|
74450
75073
|
if (!root) {
|
|
74451
75074
|
throw new SkillPackageError("INVALID_PACKAGE_ROOT", "Skill package directory does not exist");
|
|
74452
75075
|
}
|
|
@@ -74472,7 +75095,7 @@ async function walkPackage(root, ignoredPaths) {
|
|
|
74472
75095
|
async function walkDirectory(directory) {
|
|
74473
75096
|
const entries = await (0, import_promises.readdir)(directory, { withFileTypes: true });
|
|
74474
75097
|
for (const entry of entries) {
|
|
74475
|
-
const absolutePath = (0,
|
|
75098
|
+
const absolutePath = (0, import_node_path17.resolve)(directory, entry.name);
|
|
74476
75099
|
const normalizedPath = normalizeRelativePath(root, absolutePath);
|
|
74477
75100
|
if (isIgnored(normalizedPath, entry.name, entry.isDirectory(), ignored))
|
|
74478
75101
|
continue;
|
|
@@ -74539,12 +75162,12 @@ function globToRegExp(pattern) {
|
|
|
74539
75162
|
return new RegExp(`${source}(?:/.*)?$`);
|
|
74540
75163
|
}
|
|
74541
75164
|
function normalizeRelativePath(root, absolutePath) {
|
|
74542
|
-
const nativeRelativePath = (0,
|
|
74543
|
-
if (!nativeRelativePath || nativeRelativePath.startsWith(`..${
|
|
75165
|
+
const nativeRelativePath = (0, import_node_path17.relative)(root, absolutePath);
|
|
75166
|
+
if (!nativeRelativePath || nativeRelativePath.startsWith(`..${import_node_path17.sep}`) || nativeRelativePath === "..") {
|
|
74544
75167
|
throw new SkillPackageError("INVALID_PATH", "Skill package path escapes its root");
|
|
74545
75168
|
}
|
|
74546
|
-
const normalized = nativeRelativePath.split(
|
|
74547
|
-
if (normalized.includes("\0") || normalized.includes("\uFFFD") ||
|
|
75169
|
+
const normalized = nativeRelativePath.split(import_node_path17.sep).join("/").normalize("NFC");
|
|
75170
|
+
if (normalized.includes("\0") || normalized.includes("\uFFFD") || import_node_path17.posix.isAbsolute(normalized)) {
|
|
74548
75171
|
throw new SkillPackageError("INVALID_PATH", `Invalid Skill package path: ${normalized}`);
|
|
74549
75172
|
}
|
|
74550
75173
|
return normalized;
|
|
@@ -74579,7 +75202,7 @@ function requireSafeFile(path2, size, normalizedPaths) {
|
|
|
74579
75202
|
}
|
|
74580
75203
|
}
|
|
74581
75204
|
function isSecretPath(path2) {
|
|
74582
|
-
const name =
|
|
75205
|
+
const name = import_node_path17.posix.basename(path2).toLowerCase();
|
|
74583
75206
|
if (SECRET_BASENAMES.has(name) || name.startsWith(".env."))
|
|
74584
75207
|
return true;
|
|
74585
75208
|
return name.endsWith(".private-key.pem") || name.endsWith(".private-key.key");
|
|
@@ -74607,12 +75230,12 @@ function classifyFileKind(path2) {
|
|
|
74607
75230
|
return "reference";
|
|
74608
75231
|
if (path2.startsWith("assets/"))
|
|
74609
75232
|
return "asset";
|
|
74610
|
-
if (/^(?:licen[cs]e|copying|notice)(?:\.|$)/i.test(
|
|
75233
|
+
if (/^(?:licen[cs]e|copying|notice)(?:\.|$)/i.test(import_node_path17.posix.basename(path2)))
|
|
74611
75234
|
return "license";
|
|
74612
75235
|
return "other";
|
|
74613
75236
|
}
|
|
74614
75237
|
function classifyPreview(path2, bytes) {
|
|
74615
|
-
const extension =
|
|
75238
|
+
const extension = import_node_path17.posix.extname(path2).toLowerCase();
|
|
74616
75239
|
if (IMAGE_EXTENSIONS.has(extension))
|
|
74617
75240
|
return "image";
|
|
74618
75241
|
if (bytes.includes(0))
|
|
@@ -74645,7 +75268,7 @@ function parseSkillMetadata(bytes, expectedName) {
|
|
|
74645
75268
|
} catch (error61) {
|
|
74646
75269
|
throw new SkillPackageError("INVALID_FRONTMATTER", `SKILL.md frontmatter is not valid YAML: ${error61 instanceof Error ? error61.message : String(error61)}`, "SKILL.md");
|
|
74647
75270
|
}
|
|
74648
|
-
if (!
|
|
75271
|
+
if (!isRecord3(parsed)) {
|
|
74649
75272
|
throw new SkillPackageError("INVALID_FRONTMATTER", "SKILL.md frontmatter must be a YAML mapping", "SKILL.md");
|
|
74650
75273
|
}
|
|
74651
75274
|
const admitted = skillFrontmatterAdmissionSchema.safeParse(parsed);
|
|
@@ -74716,7 +75339,7 @@ function normalizePackageReference(value2) {
|
|
|
74716
75339
|
const withoutSuffix = trimmed.split(/[?#]/, 1)[0];
|
|
74717
75340
|
if (!withoutSuffix)
|
|
74718
75341
|
return null;
|
|
74719
|
-
const normalized =
|
|
75342
|
+
const normalized = import_node_path17.posix.normalize(withoutSuffix).replace(/^\.\//, "").normalize("NFC");
|
|
74720
75343
|
if (normalized === ".." || normalized.startsWith("../"))
|
|
74721
75344
|
return null;
|
|
74722
75345
|
return normalized;
|
|
@@ -74724,14 +75347,14 @@ function normalizePackageReference(value2) {
|
|
|
74724
75347
|
function looksLikeFileReference(value2) {
|
|
74725
75348
|
return !/\s/.test(value2) && (value2.includes("/") || value2.includes("."));
|
|
74726
75349
|
}
|
|
74727
|
-
function
|
|
75350
|
+
function isRecord3(value2) {
|
|
74728
75351
|
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
74729
75352
|
}
|
|
74730
75353
|
function comparePackageFiles(left, right) {
|
|
74731
75354
|
return import_node_buffer.Buffer.compare(import_node_buffer.Buffer.from(left.manifest.path, "utf8"), import_node_buffer.Buffer.from(right.manifest.path, "utf8"));
|
|
74732
75355
|
}
|
|
74733
75356
|
function hashPackageTree(files) {
|
|
74734
|
-
const hash2 = (0,
|
|
75357
|
+
const hash2 = (0, import_node_crypto11.createHash)("sha256");
|
|
74735
75358
|
hash2.update(TREE_HASH_PREFIX);
|
|
74736
75359
|
for (const file2 of files) {
|
|
74737
75360
|
const path2 = import_node_buffer.Buffer.from(file2.path, "utf8");
|
|
@@ -74744,7 +75367,7 @@ function hashPackageTree(files) {
|
|
|
74744
75367
|
return `sha256:${hash2.digest("hex")}`;
|
|
74745
75368
|
}
|
|
74746
75369
|
function sha256(bytes) {
|
|
74747
|
-
return `sha256:${(0,
|
|
75370
|
+
return `sha256:${(0, import_node_crypto11.createHash)("sha256").update(bytes).digest("hex")}`;
|
|
74748
75371
|
}
|
|
74749
75372
|
function uint322(value2) {
|
|
74750
75373
|
const buffer = import_node_buffer.Buffer.allocUnsafe(4);
|
|
@@ -74771,6 +75394,15 @@ var SKILL_PROVIDER_SOURCE_DEFINITIONS = [
|
|
|
74771
75394
|
userDirectories: [".cursor/skills"],
|
|
74772
75395
|
repositoryDirectories: [".cursor/skills"]
|
|
74773
75396
|
},
|
|
75397
|
+
{
|
|
75398
|
+
// Canonical kind, not the `codex_cli` / `codex` aliases: LEGACY_PROVIDER_KIND_MAP
|
|
75399
|
+
// normalizes both to this, and the enabled-provider set is built from
|
|
75400
|
+
// normalized kinds, so a source declared under an alias never matches.
|
|
75401
|
+
providerKind: "codex_app_server",
|
|
75402
|
+
label: "Codex skills",
|
|
75403
|
+
userDirectories: [".codex/skills"],
|
|
75404
|
+
repositoryDirectories: [".codex/skills"]
|
|
75405
|
+
},
|
|
74774
75406
|
{
|
|
74775
75407
|
providerKind: "copilot_cli",
|
|
74776
75408
|
label: "Copilot skills",
|
|
@@ -74812,8 +75444,6 @@ function providerKindsForUserDirectory(relativeDirectory) {
|
|
|
74812
75444
|
const providers = SKILL_PROVIDER_SOURCE_DEFINITIONS.flatMap((definition) => definition.userDirectories.includes(normalized) ? [definition.providerKind] : []);
|
|
74813
75445
|
if (providers.length > 0)
|
|
74814
75446
|
return providers;
|
|
74815
|
-
if (normalized === ".codex/skills")
|
|
74816
|
-
return ["cursor_agent_cli"];
|
|
74817
75447
|
if (normalized === ".gemini/config/skills")
|
|
74818
75448
|
return [];
|
|
74819
75449
|
return void 0;
|
|
@@ -74849,6 +75479,11 @@ path = "~/.cursor/skills"
|
|
|
74849
75479
|
label = "Cursor skills"
|
|
74850
75480
|
providers = ["cursor_agent_cli"]
|
|
74851
75481
|
|
|
75482
|
+
[[skills.sources]]
|
|
75483
|
+
path = "~/.codex/skills"
|
|
75484
|
+
label = "Codex skills"
|
|
75485
|
+
providers = ["codex_app_server"]
|
|
75486
|
+
|
|
74852
75487
|
[[skills.sources]]
|
|
74853
75488
|
path = "~/.copilot/skills"
|
|
74854
75489
|
label = "Copilot skills"
|
|
@@ -74864,17 +75499,20 @@ var LocalSkillSource = class {
|
|
|
74864
75499
|
locations = /* @__PURE__ */ new Map();
|
|
74865
75500
|
inspectionCache = /* @__PURE__ */ new Map();
|
|
74866
75501
|
async scan(input2) {
|
|
74867
|
-
const attemptId = (0,
|
|
75502
|
+
const attemptId = (0, import_node_crypto12.randomUUID)();
|
|
74868
75503
|
const diagnostics = [];
|
|
74869
75504
|
const candidates = [];
|
|
74870
75505
|
const visitedDirectories = /* @__PURE__ */ new Set();
|
|
74871
75506
|
let incompleteRoot = false;
|
|
74872
|
-
|
|
75507
|
+
const resolvedRoots = resolveScanRoots(input2.roots, input2.enabledProviderKinds ?? []);
|
|
75508
|
+
const declaredRoots = await canonicalDirectories(resolvedRoots.map((root) => root.directory));
|
|
75509
|
+
for (const resolvedRoot of resolvedRoots) {
|
|
74873
75510
|
const rootResult = await this.scanRoot(
|
|
74874
75511
|
resolvedRoot,
|
|
74875
75512
|
visitedDirectories,
|
|
74876
75513
|
input2.ignoredPaths ?? [],
|
|
74877
|
-
input2.mode
|
|
75514
|
+
input2.mode,
|
|
75515
|
+
declaredRoots
|
|
74878
75516
|
);
|
|
74879
75517
|
candidates.push(...rootResult.candidates);
|
|
74880
75518
|
diagnostics.push(...rootResult.diagnostics);
|
|
@@ -74929,7 +75567,63 @@ var LocalSkillSource = class {
|
|
|
74929
75567
|
if (cached2?.contentHash === expectedContentHash) return cached2;
|
|
74930
75568
|
return inspectSkillPackage(absolutePath, { expectedName: null });
|
|
74931
75569
|
}
|
|
74932
|
-
|
|
75570
|
+
/**
|
|
75571
|
+
* The root read as one package, or null when it does not hold a SKILL.md.
|
|
75572
|
+
*
|
|
75573
|
+
* `expectedName` is null here on purpose. A package inside a library must be
|
|
75574
|
+
* named for its directory, but a root is whatever the person called the
|
|
75575
|
+
* folder, so that rule would reject it with DIRECTORY_NAME_MISMATCH.
|
|
75576
|
+
*/
|
|
75577
|
+
async scanRootAsPackage(resolvedRoot, ignoredPaths, mode) {
|
|
75578
|
+
const directory = resolvedRoot.directory;
|
|
75579
|
+
if (!await pathExists((0, import_node_path18.join)(directory, "SKILL.md"))) return null;
|
|
75580
|
+
const canonicalRoot = await (0, import_promises2.realpath)(directory).catch(() => directory);
|
|
75581
|
+
const name = import_node_path18.default.basename(canonicalRoot);
|
|
75582
|
+
try {
|
|
75583
|
+
const fingerprint = await fingerprintPackage(canonicalRoot);
|
|
75584
|
+
const ignoreSignature = JSON.stringify(ignoredPaths);
|
|
75585
|
+
const cached2 = this.inspectionCache.get(canonicalRoot);
|
|
75586
|
+
const inspected = mode === "incremental" && cached2?.fingerprint === fingerprint && cached2.ignoreSignature === ignoreSignature ? cached2.inspected : await inspectSkillPackage(canonicalRoot, { ignoredPaths, expectedName: null });
|
|
75587
|
+
this.inspectionCache.set(canonicalRoot, { fingerprint, ignoreSignature, inspected });
|
|
75588
|
+
return {
|
|
75589
|
+
candidates: [
|
|
75590
|
+
{
|
|
75591
|
+
absolutePath: canonicalRoot,
|
|
75592
|
+
rootPath: canonicalRoot,
|
|
75593
|
+
order: `${resolvedRoot.order}:${name}`,
|
|
75594
|
+
candidate: {
|
|
75595
|
+
localKey: createLocalKey(resolvedRoot.root, inspected.metadata.name),
|
|
75596
|
+
name: inspected.metadata.name,
|
|
75597
|
+
description: inspected.metadata.description,
|
|
75598
|
+
contentHash: inspected.contentHash,
|
|
75599
|
+
fileCount: inspected.fileCount,
|
|
75600
|
+
totalBytes: inspected.totalBytes,
|
|
75601
|
+
origin: candidateOrigin(resolvedRoot.root),
|
|
75602
|
+
diagnostics: inspected.diagnostics
|
|
75603
|
+
}
|
|
75604
|
+
}
|
|
75605
|
+
],
|
|
75606
|
+
diagnostics: [],
|
|
75607
|
+
incomplete: false
|
|
75608
|
+
};
|
|
75609
|
+
} catch (error61) {
|
|
75610
|
+
return {
|
|
75611
|
+
candidates: [],
|
|
75612
|
+
diagnostics: [
|
|
75613
|
+
{
|
|
75614
|
+
code: "INVALID_SKILL",
|
|
75615
|
+
message: error61 instanceof Error ? error61.message : String(error61),
|
|
75616
|
+
rootLabel: resolvedRoot.root.label,
|
|
75617
|
+
localKey: createLocalKey(resolvedRoot.root, name)
|
|
75618
|
+
}
|
|
75619
|
+
],
|
|
75620
|
+
incomplete: false
|
|
75621
|
+
};
|
|
75622
|
+
}
|
|
75623
|
+
}
|
|
75624
|
+
async scanRoot(resolvedRoot, visitedDirectories, ignoredPaths, mode, declaredRoots) {
|
|
75625
|
+
const rootPackage = await this.scanRootAsPackage(resolvedRoot, ignoredPaths, mode);
|
|
75626
|
+
if (rootPackage) return rootPackage;
|
|
74933
75627
|
let entries;
|
|
74934
75628
|
try {
|
|
74935
75629
|
entries = await (0, import_promises2.readdir)(resolvedRoot.directory, { withFileTypes: true });
|
|
@@ -74954,7 +75648,7 @@ var LocalSkillSource = class {
|
|
|
74954
75648
|
);
|
|
74955
75649
|
for (const entry of entries) {
|
|
74956
75650
|
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
74957
|
-
const candidatePath = (0,
|
|
75651
|
+
const candidatePath = (0, import_node_path18.join)(resolvedRoot.directory, entry.name);
|
|
74958
75652
|
const canonicalPath = await (0, import_promises2.realpath)(candidatePath).catch(() => null);
|
|
74959
75653
|
if (!canonicalPath) {
|
|
74960
75654
|
diagnostics.push({
|
|
@@ -74964,10 +75658,11 @@ var LocalSkillSource = class {
|
|
|
74964
75658
|
});
|
|
74965
75659
|
continue;
|
|
74966
75660
|
}
|
|
74967
|
-
|
|
75661
|
+
const roots = declaredRoots.length > 0 ? declaredRoots : [canonicalRoot];
|
|
75662
|
+
if (!roots.some((root) => isWithinDirectory(root, canonicalPath))) {
|
|
74968
75663
|
diagnostics.push({
|
|
74969
75664
|
code: "SOURCE_OUTSIDE_DECLARED_ROOT",
|
|
74970
|
-
message: `Skill ${entry.name} resolves outside
|
|
75665
|
+
message: `Skill ${entry.name} resolves outside every declared Skill source`,
|
|
74971
75666
|
rootLabel: resolvedRoot.root.label
|
|
74972
75667
|
});
|
|
74973
75668
|
continue;
|
|
@@ -74985,6 +75680,7 @@ var LocalSkillSource = class {
|
|
|
74985
75680
|
const localKey = createLocalKey(resolvedRoot.root, entry.name);
|
|
74986
75681
|
candidates.push({
|
|
74987
75682
|
absolutePath: canonicalPath,
|
|
75683
|
+
rootPath: canonicalRoot,
|
|
74988
75684
|
order: `${resolvedRoot.order}:${entry.name}`,
|
|
74989
75685
|
candidate: {
|
|
74990
75686
|
localKey,
|
|
@@ -75010,7 +75706,7 @@ var LocalSkillSource = class {
|
|
|
75010
75706
|
}
|
|
75011
75707
|
};
|
|
75012
75708
|
async function fingerprintPackage(root) {
|
|
75013
|
-
const hash2 = (0,
|
|
75709
|
+
const hash2 = (0, import_node_crypto12.createHash)("sha256").update("alan-skill-stat-fingerprint-v1\0");
|
|
75014
75710
|
await visit2(root, "");
|
|
75015
75711
|
return `sha256:${hash2.digest("hex")}`;
|
|
75016
75712
|
async function visit2(directory, relativeDirectory) {
|
|
@@ -75018,7 +75714,7 @@ async function fingerprintPackage(root) {
|
|
|
75018
75714
|
entries.sort((left, right) => import_node_buffer2.Buffer.compare(import_node_buffer2.Buffer.from(left.name), import_node_buffer2.Buffer.from(right.name)));
|
|
75019
75715
|
for (const entry of entries) {
|
|
75020
75716
|
const path2 = relativeDirectory ? `${relativeDirectory}/${entry.name}` : entry.name;
|
|
75021
|
-
const absolutePath = (0,
|
|
75717
|
+
const absolutePath = (0, import_node_path18.join)(directory, entry.name);
|
|
75022
75718
|
const metadata = await (0, import_promises2.lstat)(absolutePath);
|
|
75023
75719
|
hash2.update(path2.normalize("NFC")).update("\0").update(
|
|
75024
75720
|
entry.isDirectory() ? "d" : entry.isFile() ? "f" : entry.isSymbolicLink() ? "l" : "o"
|
|
@@ -75027,13 +75723,28 @@ async function fingerprintPackage(root) {
|
|
|
75027
75723
|
}
|
|
75028
75724
|
}
|
|
75029
75725
|
}
|
|
75726
|
+
var CODEX_SOURCE_LABEL = "Codex skills";
|
|
75727
|
+
var STALE_CODEX_LABEL = "Legacy Cursor-compatible skills";
|
|
75728
|
+
var STALE_CODEX_PROVIDER = "cursor_agent_cli";
|
|
75729
|
+
function staleCodexDeclaration(relativeDirectory, label, providerKinds) {
|
|
75730
|
+
const normalized = relativeDirectory.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
75731
|
+
if (normalized !== ".codex/skills") return false;
|
|
75732
|
+
const ownedByCursor = providerKinds !== void 0 && providerKinds.length === 1 && providerKinds[0] === STALE_CODEX_PROVIDER;
|
|
75733
|
+
return ownedByCursor || label === STALE_CODEX_LABEL;
|
|
75734
|
+
}
|
|
75735
|
+
async function canonicalDirectories(directories) {
|
|
75736
|
+
const canonical = await Promise.all(
|
|
75737
|
+
directories.map((directory) => (0, import_promises2.realpath)(directory).catch(() => directory))
|
|
75738
|
+
);
|
|
75739
|
+
return [...new Set(canonical)];
|
|
75740
|
+
}
|
|
75030
75741
|
function isWithinDirectory(parent, candidate) {
|
|
75031
|
-
const path2 = (0,
|
|
75032
|
-
return path2 !== ".." && !path2.startsWith(`..${
|
|
75742
|
+
const path2 = (0, import_node_path18.relative)(parent, candidate);
|
|
75743
|
+
return path2 !== ".." && !path2.startsWith(`..${import_node_path18.sep}`) && !(0, import_node_path18.isAbsolute)(path2);
|
|
75033
75744
|
}
|
|
75034
75745
|
async function loadSkillSourceConfig(homeDirectory) {
|
|
75035
|
-
const configDirectory = (0,
|
|
75036
|
-
const configPath = (0,
|
|
75746
|
+
const configDirectory = (0, import_node_path18.join)(homeDirectory, ".alan");
|
|
75747
|
+
const configPath = (0, import_node_path18.join)(configDirectory, "config.toml");
|
|
75037
75748
|
await (0, import_promises2.mkdir)(configDirectory, { recursive: true, mode: 448 });
|
|
75038
75749
|
let created = false;
|
|
75039
75750
|
try {
|
|
@@ -75067,7 +75778,7 @@ async function resolveSkillScanInput(homeDirectory, declared) {
|
|
|
75067
75778
|
}
|
|
75068
75779
|
const { config: config2 } = loaded;
|
|
75069
75780
|
if (!config2.enabled) return { ...declared, roots: [], ignoredPaths: config2.ignoredPaths };
|
|
75070
|
-
const draftsPath = (0,
|
|
75781
|
+
const draftsPath = (0, import_node_path18.resolve)((0, import_node_path18.join)(homeDirectory, ".alan", "skill-sources"));
|
|
75071
75782
|
const enabledProviders = new Set(declared.enabledProviderKinds ?? []);
|
|
75072
75783
|
const configured = [];
|
|
75073
75784
|
for (const source of config2.sources) {
|
|
@@ -75075,7 +75786,7 @@ async function resolveSkillScanInput(homeDirectory, declared) {
|
|
|
75075
75786
|
continue;
|
|
75076
75787
|
}
|
|
75077
75788
|
configured.push(
|
|
75078
|
-
(0,
|
|
75789
|
+
(0, import_node_path18.resolve)(source.path) === draftsPath ? { kind: "drafts", path: source.path, label: "Alan drafts" } : {
|
|
75079
75790
|
kind: "configured",
|
|
75080
75791
|
declarationId: source.declarationId,
|
|
75081
75792
|
path: source.path,
|
|
@@ -75093,7 +75804,7 @@ async function resolveSkillScanInput(homeDirectory, declared) {
|
|
|
75093
75804
|
}
|
|
75094
75805
|
function parseSkillSourceConfig(content, homeDirectory) {
|
|
75095
75806
|
const parsed = parse5(content);
|
|
75096
|
-
if (!
|
|
75807
|
+
if (!isRecord4(parsed) || parsed.version !== 1 || !isRecord4(parsed.skills)) {
|
|
75097
75808
|
throw new Error("~/.alan/config.toml must contain version = 1 and a [skills] table");
|
|
75098
75809
|
}
|
|
75099
75810
|
const skills = parsed.skills;
|
|
@@ -75109,7 +75820,7 @@ function parseSkillSourceConfig(content, homeDirectory) {
|
|
|
75109
75820
|
throw new Error("~/.alan/config.toml must declare at least one [[skills.sources]] table");
|
|
75110
75821
|
}
|
|
75111
75822
|
const sources = skills.sources.map((value2, index) => {
|
|
75112
|
-
if (!
|
|
75823
|
+
if (!isRecord4(value2) || typeof value2.path !== "string" || typeof value2.label !== "string") {
|
|
75113
75824
|
throw new Error(
|
|
75114
75825
|
`skills.sources entry ${index + 1} must contain string path and label values`
|
|
75115
75826
|
);
|
|
@@ -75122,14 +75833,16 @@ function parseSkillSourceConfig(content, homeDirectory) {
|
|
|
75122
75833
|
value2.providers,
|
|
75123
75834
|
`skills.sources[${index}].providers`
|
|
75124
75835
|
);
|
|
75125
|
-
const relativeDirectory =
|
|
75836
|
+
const relativeDirectory = import_node_path18.default.relative(homeDirectory, sourcePath);
|
|
75126
75837
|
const inferredProviders = providerKindsForUserDirectory(relativeDirectory);
|
|
75838
|
+
const declaredProviders = value2.providers === void 0 ? inferredProviders : [...new Set(explicitProviders.map((provider) => provider.trim()).filter(Boolean))];
|
|
75839
|
+
const stale = staleCodexDeclaration(relativeDirectory, label, declaredProviders);
|
|
75127
75840
|
return {
|
|
75128
75841
|
declarationId,
|
|
75129
75842
|
path: sourcePath,
|
|
75130
|
-
label,
|
|
75843
|
+
label: stale ? CODEX_SOURCE_LABEL : label,
|
|
75131
75844
|
precedence: index,
|
|
75132
|
-
providerKinds:
|
|
75845
|
+
providerKinds: stale ? inferredProviders : declaredProviders
|
|
75133
75846
|
};
|
|
75134
75847
|
});
|
|
75135
75848
|
const duplicateId = sources.find(
|
|
@@ -75141,7 +75854,7 @@ function parseSkillSourceConfig(content, homeDirectory) {
|
|
|
75141
75854
|
return { enabled, publishChanges, discoverRepositorySkills, ignoredPaths, sources };
|
|
75142
75855
|
}
|
|
75143
75856
|
function stableSourceDeclarationId(sourcePath) {
|
|
75144
|
-
const digest = (0,
|
|
75857
|
+
const digest = (0, import_node_crypto12.createHash)("sha256").update("alan-skill-source-v1\0").update(sourcePath.normalize("NFC")).digest("hex").slice(0, 24);
|
|
75145
75858
|
return `source-${digest}`;
|
|
75146
75859
|
}
|
|
75147
75860
|
function requiredSourceId(value2, field) {
|
|
@@ -75150,7 +75863,7 @@ function requiredSourceId(value2, field) {
|
|
|
75150
75863
|
}
|
|
75151
75864
|
return value2;
|
|
75152
75865
|
}
|
|
75153
|
-
function resolveSkillSourcePath(sourcePath, homeDirectory, pathApi =
|
|
75866
|
+
function resolveSkillSourcePath(sourcePath, homeDirectory, pathApi = import_node_path18.default) {
|
|
75154
75867
|
const trimmed = sourcePath.trim();
|
|
75155
75868
|
if (trimmed === "~") return pathApi.resolve(homeDirectory);
|
|
75156
75869
|
if (trimmed.startsWith("~/") || trimmed.startsWith("~\\")) {
|
|
@@ -75185,7 +75898,7 @@ function resolveScanRoots(roots, enabledProviderKinds) {
|
|
|
75185
75898
|
).entries()) {
|
|
75186
75899
|
resolved.push({
|
|
75187
75900
|
root,
|
|
75188
|
-
directory: (0,
|
|
75901
|
+
directory: (0, import_node_path18.join)(root.path, relativeDirectory),
|
|
75189
75902
|
order: `1:${root.repositoryId}:${String(index).padStart(2, "0")}`
|
|
75190
75903
|
});
|
|
75191
75904
|
}
|
|
@@ -75207,11 +75920,11 @@ function candidateOrigin(root) {
|
|
|
75207
75920
|
}
|
|
75208
75921
|
function createLocalKey(root, skillName) {
|
|
75209
75922
|
const rootIdentity = root.kind === "configured" ? `configured:${root.declarationId}` : root.kind === "repository" ? `repository:${root.repositoryId}` : "drafts";
|
|
75210
|
-
const digest = (0,
|
|
75923
|
+
const digest = (0, import_node_crypto12.createHash)("sha256").update("alan-local-skill-v1\0").update(rootIdentity).update("\0").update(skillName.normalize("NFC")).digest("hex");
|
|
75211
75924
|
return `local:${digest}`;
|
|
75212
75925
|
}
|
|
75213
75926
|
function hashInventory(candidates) {
|
|
75214
|
-
const hash2 = (0,
|
|
75927
|
+
const hash2 = (0, import_node_crypto12.createHash)("sha256");
|
|
75215
75928
|
hash2.update("alan-skill-inventory-v1\0");
|
|
75216
75929
|
for (const candidate of candidates) {
|
|
75217
75930
|
hash2.update(candidate.localKey);
|
|
@@ -75224,16 +75937,27 @@ function hashInventory(candidates) {
|
|
|
75224
75937
|
return `sha256:${hash2.digest("hex")}`;
|
|
75225
75938
|
}
|
|
75226
75939
|
function deduplicateCandidatesByContent(candidates) {
|
|
75227
|
-
const
|
|
75228
|
-
|
|
75229
|
-
|
|
75230
|
-
|
|
75940
|
+
const bestByContent = /* @__PURE__ */ new Map();
|
|
75941
|
+
for (const located of candidates) {
|
|
75942
|
+
const existing = bestByContent.get(located.candidate.contentHash);
|
|
75943
|
+
if (!existing || located.rootPath.length > existing.rootPath.length) {
|
|
75944
|
+
bestByContent.set(located.candidate.contentHash, located);
|
|
75945
|
+
}
|
|
75946
|
+
}
|
|
75947
|
+
const winners = new Set(bestByContent.values());
|
|
75948
|
+
return candidates.filter((located) => winners.has(located));
|
|
75949
|
+
}
|
|
75950
|
+
async function pathExists(candidate) {
|
|
75951
|
+
try {
|
|
75952
|
+
await (0, import_promises2.access)(candidate);
|
|
75231
75953
|
return true;
|
|
75232
|
-
}
|
|
75954
|
+
} catch {
|
|
75955
|
+
return false;
|
|
75956
|
+
}
|
|
75233
75957
|
}
|
|
75234
75958
|
async function hasManagedMarker(directory) {
|
|
75235
75959
|
try {
|
|
75236
|
-
await (0, import_promises2.access)((0,
|
|
75960
|
+
await (0, import_promises2.access)((0, import_node_path18.join)(directory, ".alan-managed.json"));
|
|
75237
75961
|
return true;
|
|
75238
75962
|
} catch {
|
|
75239
75963
|
return false;
|
|
@@ -75252,7 +75976,7 @@ function isMissingPath(error61) {
|
|
|
75252
75976
|
function isAlreadyPresent(error61) {
|
|
75253
75977
|
return typeof error61 === "object" && error61 !== null && "code" in error61 && error61.code === "EEXIST";
|
|
75254
75978
|
}
|
|
75255
|
-
function
|
|
75979
|
+
function isRecord4(value2) {
|
|
75256
75980
|
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
75257
75981
|
}
|
|
75258
75982
|
|
|
@@ -75386,8 +76110,8 @@ var RuntimeSkillCoordinator = class {
|
|
|
75386
76110
|
async runExclusively(work) {
|
|
75387
76111
|
const previous = this.tail;
|
|
75388
76112
|
let release2;
|
|
75389
|
-
this.tail = new Promise((
|
|
75390
|
-
release2 =
|
|
76113
|
+
this.tail = new Promise((resolve15) => {
|
|
76114
|
+
release2 = resolve15;
|
|
75391
76115
|
});
|
|
75392
76116
|
await previous;
|
|
75393
76117
|
try {
|
|
@@ -75400,14 +76124,14 @@ var RuntimeSkillCoordinator = class {
|
|
|
75400
76124
|
|
|
75401
76125
|
// src/skills/runtime-skill-manager.ts
|
|
75402
76126
|
var import_node_buffer5 = require("buffer");
|
|
75403
|
-
var
|
|
76127
|
+
var import_node_crypto15 = require("crypto");
|
|
75404
76128
|
var import_promises5 = require("fs/promises");
|
|
75405
|
-
var
|
|
76129
|
+
var import_node_path21 = require("path");
|
|
75406
76130
|
|
|
75407
76131
|
// src/skills/managed-skill-store.ts
|
|
75408
|
-
var
|
|
76132
|
+
var import_node_crypto13 = require("crypto");
|
|
75409
76133
|
var import_promises3 = require("fs/promises");
|
|
75410
|
-
var
|
|
76134
|
+
var import_node_path19 = require("path");
|
|
75411
76135
|
var EMPTY_STATE = {
|
|
75412
76136
|
version: 1,
|
|
75413
76137
|
sources: {},
|
|
@@ -75426,7 +76150,7 @@ var ManagedSkillStore = class _ManagedSkillStore {
|
|
|
75426
76150
|
static async open(baseDirectory) {
|
|
75427
76151
|
await (0, import_promises3.mkdir)(baseDirectory, { recursive: true, mode: 448 });
|
|
75428
76152
|
try {
|
|
75429
|
-
const parsed = JSON.parse(await (0, import_promises3.readFile)((0,
|
|
76153
|
+
const parsed = JSON.parse(await (0, import_promises3.readFile)((0, import_node_path19.join)(baseDirectory, "state.json"), "utf8"));
|
|
75430
76154
|
return new _ManagedSkillStore(baseDirectory, parseState(parsed), "verified");
|
|
75431
76155
|
} catch (error61) {
|
|
75432
76156
|
if (isMissingPath2(error61)) {
|
|
@@ -75525,10 +76249,10 @@ var ManagedSkillStore = class _ManagedSkillStore {
|
|
|
75525
76249
|
this.canRemoveManagedFiles = true;
|
|
75526
76250
|
}
|
|
75527
76251
|
async persist() {
|
|
75528
|
-
const statePath = (0,
|
|
75529
|
-
const pendingPath = (0,
|
|
76252
|
+
const statePath = (0, import_node_path19.join)(this.baseDirectory, "state.json");
|
|
76253
|
+
const pendingPath = (0, import_node_path19.join)(
|
|
75530
76254
|
this.baseDirectory,
|
|
75531
|
-
`.state.json.pending-${process.pid}-${(0,
|
|
76255
|
+
`.state.json.pending-${process.pid}-${(0, import_node_crypto13.randomBytes)(6).toString("hex")}`
|
|
75532
76256
|
);
|
|
75533
76257
|
try {
|
|
75534
76258
|
await (0, import_promises3.writeFile)(pendingPath, `${JSON.stringify(this.state, null, 2)}
|
|
@@ -75544,13 +76268,13 @@ var ManagedSkillStore = class _ManagedSkillStore {
|
|
|
75544
76268
|
}
|
|
75545
76269
|
};
|
|
75546
76270
|
function parseState(value2) {
|
|
75547
|
-
if (!
|
|
75548
|
-
if (!
|
|
76271
|
+
if (!isRecord5(value2) || value2.version !== 1) throw new Error("unsupported Skill state version");
|
|
76272
|
+
if (!isRecord5(value2.sources) || !isRecord5(value2.projections)) {
|
|
75549
76273
|
throw new Error("invalid Skill state maps");
|
|
75550
76274
|
}
|
|
75551
76275
|
const sources = {};
|
|
75552
76276
|
for (const [localKey, source] of Object.entries(value2.sources)) {
|
|
75553
|
-
if (!localKey.startsWith("local:") || !
|
|
76277
|
+
if (!localKey.startsWith("local:") || !isRecord5(source) || typeof source.absolutePath !== "string" || !isSha256(source.contentHash)) {
|
|
75554
76278
|
throw new Error("invalid Skill source state");
|
|
75555
76279
|
}
|
|
75556
76280
|
const publication = parsePublication(source.publication, source.contentHash);
|
|
@@ -75562,7 +76286,7 @@ function parseState(value2) {
|
|
|
75562
76286
|
}
|
|
75563
76287
|
const projections = {};
|
|
75564
76288
|
for (const [directory, projection] of Object.entries(value2.projections)) {
|
|
75565
|
-
if (!directory || !
|
|
76289
|
+
if (!directory || !isRecord5(projection) || typeof projection.skillId !== "string" || typeof projection.versionId !== "string" || !isSha256(projection.contentHash)) {
|
|
75566
76290
|
throw new Error("invalid Skill projection state");
|
|
75567
76291
|
}
|
|
75568
76292
|
projections[directory] = {
|
|
@@ -75583,7 +76307,7 @@ function parseState(value2) {
|
|
|
75583
76307
|
}
|
|
75584
76308
|
function parsePublication(value2, sourceHash) {
|
|
75585
76309
|
if (value2 === void 0) return void 0;
|
|
75586
|
-
if (!
|
|
76310
|
+
if (!isRecord5(value2) || typeof value2.skillId !== "string" || typeof value2.versionId !== "string" || !isSha256(value2.contentHash) || value2.contentHash !== sourceHash) {
|
|
75587
76311
|
throw new Error("invalid Skill source publication state");
|
|
75588
76312
|
}
|
|
75589
76313
|
return {
|
|
@@ -75600,7 +76324,7 @@ function optionalSha256(value2) {
|
|
|
75600
76324
|
function isSha256(value2) {
|
|
75601
76325
|
return typeof value2 === "string" && /^sha256:[a-f0-9]{64}$/.test(value2);
|
|
75602
76326
|
}
|
|
75603
|
-
function
|
|
76327
|
+
function isRecord5(value2) {
|
|
75604
76328
|
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
75605
76329
|
}
|
|
75606
76330
|
function isMissingPath2(error61) {
|
|
@@ -75609,9 +76333,9 @@ function isMissingPath2(error61) {
|
|
|
75609
76333
|
|
|
75610
76334
|
// ../shared/dist/node/skill-bundle.js
|
|
75611
76335
|
var import_node_buffer3 = require("buffer");
|
|
75612
|
-
var
|
|
76336
|
+
var import_node_crypto14 = require("crypto");
|
|
75613
76337
|
var import_promises4 = require("fs/promises");
|
|
75614
|
-
var
|
|
76338
|
+
var import_node_path20 = require("path");
|
|
75615
76339
|
var import_node_stream = require("stream");
|
|
75616
76340
|
var import_node_zlib = require("zlib");
|
|
75617
76341
|
var import_tar_stream = __toESM(require_tar_stream(), 1);
|
|
@@ -75620,7 +76344,7 @@ async function createSkillBundle(packageRoot) {
|
|
|
75620
76344
|
const archive = (0, import_tar_stream.pack)();
|
|
75621
76345
|
const tarPromise = collectStream(archive);
|
|
75622
76346
|
for (const file2 of before.files) {
|
|
75623
|
-
const bytes2 = await (0, import_promises4.readFile)((0,
|
|
76347
|
+
const bytes2 = await (0, import_promises4.readFile)((0, import_node_path20.join)(packageRoot, ...file2.path.split("/")));
|
|
75624
76348
|
await addArchiveEntry(archive, {
|
|
75625
76349
|
name: file2.path,
|
|
75626
76350
|
type: "file",
|
|
@@ -75671,8 +76395,8 @@ async function collectStream(stream) {
|
|
|
75671
76395
|
return import_node_buffer3.Buffer.concat(chunks);
|
|
75672
76396
|
}
|
|
75673
76397
|
function addArchiveEntry(archive, header, bytes) {
|
|
75674
|
-
return new Promise((
|
|
75675
|
-
archive.entry(header, bytes, (error61) => error61 ? reject(error61) :
|
|
76398
|
+
return new Promise((resolve15, reject) => {
|
|
76399
|
+
archive.entry(header, bytes, (error61) => error61 ? reject(error61) : resolve15());
|
|
75676
76400
|
});
|
|
75677
76401
|
}
|
|
75678
76402
|
async function decompressBounded(archiveBytes) {
|
|
@@ -75698,7 +76422,7 @@ async function extractTar(tarBytes, destination) {
|
|
|
75698
76422
|
const extractor = (0, import_tar_stream.extract)();
|
|
75699
76423
|
let fileCount = 0;
|
|
75700
76424
|
let totalBytes = 0;
|
|
75701
|
-
const completion = new Promise((
|
|
76425
|
+
const completion = new Promise((resolve15, reject) => {
|
|
75702
76426
|
extractor.on("entry", (header, stream, next) => {
|
|
75703
76427
|
void extractEntry(header, stream, destination, {
|
|
75704
76428
|
addFile(size) {
|
|
@@ -75713,7 +76437,7 @@ async function extractTar(tarBytes, destination) {
|
|
|
75713
76437
|
}
|
|
75714
76438
|
}).then(next, (error61) => extractor.destroy(error61 instanceof Error ? error61 : new Error(String(error61))));
|
|
75715
76439
|
});
|
|
75716
|
-
extractor.once("finish",
|
|
76440
|
+
extractor.once("finish", resolve15);
|
|
75717
76441
|
extractor.once("error", reject);
|
|
75718
76442
|
});
|
|
75719
76443
|
import_node_stream.Readable.from(tarBytes).pipe(extractor);
|
|
@@ -75735,19 +76459,19 @@ async function extractEntry(header, stream, destination, limits) {
|
|
|
75735
76459
|
if (bytes.byteLength !== declaredSize) {
|
|
75736
76460
|
throw new Error(`Skill archive file size is invalid: ${path2}`);
|
|
75737
76461
|
}
|
|
75738
|
-
const outputPath = (0,
|
|
75739
|
-
await (0, import_promises4.mkdir)((0,
|
|
76462
|
+
const outputPath = (0, import_node_path20.join)(destination, ...path2.split("/"));
|
|
76463
|
+
await (0, import_promises4.mkdir)((0, import_node_path20.dirname)(outputPath), { recursive: true, mode: 448 });
|
|
75740
76464
|
await (0, import_promises4.writeFile)(outputPath, bytes, { mode: (header.mode ?? 420) & 73 ? 493 : 420 });
|
|
75741
76465
|
}
|
|
75742
76466
|
function requireSafeArchivePath(value2) {
|
|
75743
76467
|
const normalized = value2.replaceAll("\\", "/").normalize("NFC");
|
|
75744
|
-
if (!normalized || normalized.startsWith("/") || normalized.includes("\0") ||
|
|
76468
|
+
if (!normalized || normalized.startsWith("/") || normalized.includes("\0") || import_node_path20.posix.normalize(normalized) !== normalized || normalized === ".." || normalized.startsWith("../")) {
|
|
75745
76469
|
throw new Error(`Skill archive contains an unsafe path: ${value2}`);
|
|
75746
76470
|
}
|
|
75747
76471
|
return normalized;
|
|
75748
76472
|
}
|
|
75749
76473
|
function sha2562(bytes) {
|
|
75750
|
-
return `sha256:${(0,
|
|
76474
|
+
return `sha256:${(0, import_node_crypto14.createHash)("sha256").update(bytes).digest("hex")}`;
|
|
75751
76475
|
}
|
|
75752
76476
|
|
|
75753
76477
|
// src/skills/skill-control-client.ts
|
|
@@ -75820,14 +76544,14 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
75820
76544
|
}
|
|
75821
76545
|
reconcileTail = Promise.resolve();
|
|
75822
76546
|
static async open(options) {
|
|
75823
|
-
const skillsStateDirectory = (0,
|
|
75824
|
-
const managedSkillsDirectory = (0,
|
|
76547
|
+
const skillsStateDirectory = (0, import_node_path21.join)(options.homeDirectory, ".alan", "skills");
|
|
76548
|
+
const managedSkillsDirectory = (0, import_node_path21.join)(options.homeDirectory, ".agents", "skills");
|
|
75825
76549
|
const store = await ManagedSkillStore.open(skillsStateDirectory);
|
|
75826
76550
|
return new _RuntimeSkillManager(
|
|
75827
76551
|
skillsStateDirectory,
|
|
75828
76552
|
managedSkillsDirectory,
|
|
75829
|
-
(0,
|
|
75830
|
-
(0,
|
|
76553
|
+
(0, import_node_path21.join)(options.homeDirectory, ".claude", "skills"),
|
|
76554
|
+
(0, import_node_path21.join)(options.homeDirectory, ".alan", "sync-manifest.json"),
|
|
75831
76555
|
store,
|
|
75832
76556
|
options.downloadBundle ?? downloadBundleWithFetch,
|
|
75833
76557
|
options.localSource ?? new LocalSkillSource(),
|
|
@@ -75986,7 +76710,7 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
75986
76710
|
if (!runtimeDirectory) {
|
|
75987
76711
|
throw new Error("Selected Skill version is not installed on this runtime");
|
|
75988
76712
|
}
|
|
75989
|
-
const packageDirectory = (0,
|
|
76713
|
+
const packageDirectory = (0, import_node_path21.join)(this.managedSkillsDirectory, runtimeDirectory);
|
|
75990
76714
|
const marker = await readMarker(packageDirectory);
|
|
75991
76715
|
if (!marker || marker.skillId !== invocation.skillId || marker.versionId !== invocation.versionId) {
|
|
75992
76716
|
throw new Error("Selected Skill projection is missing or no longer owned by Alan");
|
|
@@ -76000,11 +76724,11 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
76000
76724
|
async reconcileUnlocked(manifest) {
|
|
76001
76725
|
const desired = desiredSkillManifestSchema.parse(manifest);
|
|
76002
76726
|
await (0, import_promises5.mkdir)(this.managedSkillsDirectory, { recursive: true, mode: 448 });
|
|
76003
|
-
await (0, import_promises5.mkdir)((0,
|
|
76727
|
+
await (0, import_promises5.mkdir)((0, import_node_path21.join)(this.skillsStateDirectory, "cache", "sha256"), {
|
|
76004
76728
|
recursive: true,
|
|
76005
76729
|
mode: 448
|
|
76006
76730
|
});
|
|
76007
|
-
await (0, import_promises5.mkdir)((0,
|
|
76731
|
+
await (0, import_promises5.mkdir)((0, import_node_path21.join)(this.skillsStateDirectory, "tmp"), { recursive: true, mode: 448 });
|
|
76008
76732
|
const results = [];
|
|
76009
76733
|
const nextProjections = {};
|
|
76010
76734
|
for (const entry of desired.skills) {
|
|
@@ -76049,13 +76773,13 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
76049
76773
|
await this.retireLegacySyncManifest();
|
|
76050
76774
|
}
|
|
76051
76775
|
return {
|
|
76052
|
-
attemptId: (0,
|
|
76776
|
+
attemptId: (0, import_node_crypto15.randomUUID)(),
|
|
76053
76777
|
desiredRevision: desired.revision,
|
|
76054
76778
|
skills: results
|
|
76055
76779
|
};
|
|
76056
76780
|
}
|
|
76057
76781
|
async reuseCurrentProjection(entry) {
|
|
76058
|
-
const destination = (0,
|
|
76782
|
+
const destination = (0, import_node_path21.join)(this.managedSkillsDirectory, entry.runtimeDirectory);
|
|
76059
76783
|
const marker = await readMarker(destination);
|
|
76060
76784
|
if (marker?.skillId !== entry.skillId || marker.versionId !== entry.versionId || marker.contentHash !== entry.contentHash || !await packageMatches(destination, entry.contentHash)) {
|
|
76061
76785
|
return null;
|
|
@@ -76064,19 +76788,19 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
76064
76788
|
return entry.contentHash;
|
|
76065
76789
|
}
|
|
76066
76790
|
async adoptLegacyProjection(entry) {
|
|
76067
|
-
const destination = (0,
|
|
76068
|
-
if (await
|
|
76791
|
+
const destination = (0, import_node_path21.join)(this.managedSkillsDirectory, entry.runtimeDirectory);
|
|
76792
|
+
if (await pathExists2(destination)) return;
|
|
76069
76793
|
const manifest = await readLegacyManifest(this.legacyManifestPath);
|
|
76070
76794
|
const legacy = manifest?.skills[entry.slug];
|
|
76071
76795
|
if (!legacy) return;
|
|
76072
|
-
const source = (0,
|
|
76796
|
+
const source = (0, import_node_path21.join)(this.managedSkillsDirectory, entry.slug);
|
|
76073
76797
|
if (!await legacyFileMatches(source, legacy.hash) || !await packageMatches(source, entry.contentHash)) {
|
|
76074
76798
|
return;
|
|
76075
76799
|
}
|
|
76076
76800
|
await (0, import_promises5.rename)(source, destination);
|
|
76077
76801
|
try {
|
|
76078
76802
|
await (0, import_promises5.writeFile)(
|
|
76079
|
-
(0,
|
|
76803
|
+
(0, import_node_path21.join)(destination, ".alan-managed.json"),
|
|
76080
76804
|
`${JSON.stringify(markerFor(entry), null, 2)}
|
|
76081
76805
|
`,
|
|
76082
76806
|
{ encoding: "utf8", flag: "wx", mode: 384 }
|
|
@@ -76085,19 +76809,19 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
76085
76809
|
await (0, import_promises5.rename)(destination, source).catch(() => void 0);
|
|
76086
76810
|
throw error61;
|
|
76087
76811
|
}
|
|
76088
|
-
await removeExactLegacySkill((0,
|
|
76812
|
+
await removeExactLegacySkill((0, import_node_path21.join)(this.compatibilitySkillsDirectory, entry.slug), legacy.hash);
|
|
76089
76813
|
}
|
|
76090
76814
|
async retireLegacySyncManifest() {
|
|
76091
76815
|
const manifest = await readLegacyManifest(this.legacyManifestPath);
|
|
76092
76816
|
if (!manifest) return;
|
|
76093
76817
|
for (const [command, entry] of Object.entries(manifest.skills)) {
|
|
76094
|
-
await removeExactLegacySkill((0,
|
|
76095
|
-
await removeExactLegacySkill((0,
|
|
76818
|
+
await removeExactLegacySkill((0, import_node_path21.join)(this.managedSkillsDirectory, command), entry.hash);
|
|
76819
|
+
await removeExactLegacySkill((0, import_node_path21.join)(this.compatibilitySkillsDirectory, command), entry.hash);
|
|
76096
76820
|
}
|
|
76097
76821
|
await (0, import_promises5.rm)(this.legacyManifestPath, { force: true });
|
|
76098
76822
|
}
|
|
76099
76823
|
async ensureCached(entry) {
|
|
76100
|
-
const cacheDirectory = (0,
|
|
76824
|
+
const cacheDirectory = (0, import_node_path21.join)(
|
|
76101
76825
|
this.skillsStateDirectory,
|
|
76102
76826
|
"cache",
|
|
76103
76827
|
"sha256",
|
|
@@ -76111,10 +76835,10 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
76111
76835
|
`Downloaded Skill bundle length ${bytes.byteLength} does not match ${entry.compressedBytes}`
|
|
76112
76836
|
);
|
|
76113
76837
|
}
|
|
76114
|
-
const pendingDirectory = (0,
|
|
76838
|
+
const pendingDirectory = (0, import_node_path21.join)(
|
|
76115
76839
|
this.skillsStateDirectory,
|
|
76116
76840
|
"tmp",
|
|
76117
|
-
`cache-${process.pid}-${(0,
|
|
76841
|
+
`cache-${process.pid}-${(0, import_node_crypto15.randomBytes)(8).toString("hex")}`
|
|
76118
76842
|
);
|
|
76119
76843
|
try {
|
|
76120
76844
|
await extractSkillBundle(bytes, pendingDirectory, {
|
|
@@ -76132,22 +76856,22 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
76132
76856
|
return cacheDirectory;
|
|
76133
76857
|
}
|
|
76134
76858
|
async project(entry, cacheDirectory) {
|
|
76135
|
-
const destination = (0,
|
|
76859
|
+
const destination = (0, import_node_path21.join)(this.managedSkillsDirectory, entry.runtimeDirectory);
|
|
76136
76860
|
const existing = await readMarker(destination);
|
|
76137
76861
|
if (existing?.skillId === entry.skillId && existing.versionId === entry.versionId && existing.contentHash === entry.contentHash && await packageMatches(destination, entry.contentHash)) {
|
|
76138
76862
|
await this.ensureCompatibilityProjection(entry, destination);
|
|
76139
76863
|
return entry.contentHash;
|
|
76140
76864
|
}
|
|
76141
|
-
const suffix = `${process.pid}-${(0,
|
|
76142
|
-
const pending = (0,
|
|
76143
|
-
const backup = (0,
|
|
76865
|
+
const suffix = `${process.pid}-${(0, import_node_crypto15.randomBytes)(8).toString("hex")}`;
|
|
76866
|
+
const pending = (0, import_node_path21.join)(this.managedSkillsDirectory, `.pending-${suffix}`);
|
|
76867
|
+
const backup = (0, import_node_path21.join)(this.managedSkillsDirectory, `.backup-${suffix}`);
|
|
76144
76868
|
let movedExisting = false;
|
|
76145
76869
|
let installedPending = false;
|
|
76146
76870
|
let completed = false;
|
|
76147
76871
|
try {
|
|
76148
76872
|
await (0, import_promises5.cp)(cacheDirectory, pending, { recursive: true, errorOnExist: true, force: false });
|
|
76149
76873
|
await (0, import_promises5.writeFile)(
|
|
76150
|
-
(0,
|
|
76874
|
+
(0, import_node_path21.join)(pending, ".alan-managed.json"),
|
|
76151
76875
|
`${JSON.stringify(markerFor(entry), null, 2)}
|
|
76152
76876
|
`,
|
|
76153
76877
|
{ encoding: "utf8", flag: "wx", mode: 384 }
|
|
@@ -76155,7 +76879,7 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
76155
76879
|
if (!await packageMatches(pending, entry.contentHash)) {
|
|
76156
76880
|
throw new Error("Pending managed Skill projection failed content verification");
|
|
76157
76881
|
}
|
|
76158
|
-
if (await
|
|
76882
|
+
if (await pathExists2(destination)) {
|
|
76159
76883
|
await (0, import_promises5.rename)(destination, backup);
|
|
76160
76884
|
movedExisting = true;
|
|
76161
76885
|
}
|
|
@@ -76177,7 +76901,7 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
76177
76901
|
} catch (error61) {
|
|
76178
76902
|
if (!completed && installedPending) {
|
|
76179
76903
|
await (0, import_promises5.rm)(destination, { recursive: true, force: true });
|
|
76180
|
-
if (movedExisting && await
|
|
76904
|
+
if (movedExisting && await pathExists2(backup)) await (0, import_promises5.rename)(backup, destination);
|
|
76181
76905
|
}
|
|
76182
76906
|
throw error61;
|
|
76183
76907
|
} finally {
|
|
@@ -76188,8 +76912,8 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
76188
76912
|
}
|
|
76189
76913
|
}
|
|
76190
76914
|
async requireReplaceable(entry) {
|
|
76191
|
-
const destination = (0,
|
|
76192
|
-
if (!await
|
|
76915
|
+
const destination = (0, import_node_path21.join)(this.managedSkillsDirectory, entry.runtimeDirectory);
|
|
76916
|
+
if (!await pathExists2(destination)) return;
|
|
76193
76917
|
const marker = await readMarker(destination);
|
|
76194
76918
|
if (!marker) throw unmanagedPathConflict(entry.runtimeDirectory);
|
|
76195
76919
|
if (marker.skillId === entry.skillId && marker.versionId === entry.versionId && marker.contentHash === entry.contentHash && await packageMatches(destination, entry.contentHash)) {
|
|
@@ -76205,10 +76929,10 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
76205
76929
|
if (!this.store.inspect().canRemoveManagedFiles) return failures;
|
|
76206
76930
|
for (const [runtimeDirectory, recorded] of Object.entries(this.store.projections())) {
|
|
76207
76931
|
if (desired[runtimeDirectory]) continue;
|
|
76208
|
-
const destination = (0,
|
|
76209
|
-
const compatibility = (0,
|
|
76932
|
+
const destination = (0, import_node_path21.join)(this.managedSkillsDirectory, runtimeDirectory);
|
|
76933
|
+
const compatibility = (0, import_node_path21.join)(this.compatibilitySkillsDirectory, runtimeDirectory);
|
|
76210
76934
|
const marker = await readMarker(destination);
|
|
76211
|
-
if (!await
|
|
76935
|
+
if (!await pathExists2(destination)) continue;
|
|
76212
76936
|
if (marker && markerMatchesProjection(marker, recorded)) {
|
|
76213
76937
|
if (!await removeCompatibilityProjection(compatibility, destination, recorded)) {
|
|
76214
76938
|
failures.push({
|
|
@@ -76237,8 +76961,8 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
76237
76961
|
}
|
|
76238
76962
|
async ensureCompatibilityProjection(entry, canonicalDirectory) {
|
|
76239
76963
|
await (0, import_promises5.mkdir)(this.compatibilitySkillsDirectory, { recursive: true, mode: 448 });
|
|
76240
|
-
const destination = (0,
|
|
76241
|
-
if (await
|
|
76964
|
+
const destination = (0, import_node_path21.join)(this.compatibilitySkillsDirectory, entry.runtimeDirectory);
|
|
76965
|
+
if (await pathExists2(destination)) {
|
|
76242
76966
|
if (await compatibilityProjectionMatches(destination, canonicalDirectory, entry)) return;
|
|
76243
76967
|
throw unmanagedCompatibilityConflict(entry.runtimeDirectory);
|
|
76244
76968
|
}
|
|
@@ -76248,11 +76972,11 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
76248
76972
|
} catch (error61) {
|
|
76249
76973
|
if (!isSymlinkUnsupported(error61)) throw error61;
|
|
76250
76974
|
}
|
|
76251
|
-
const pending = `${destination}.pending-${process.pid}-${(0,
|
|
76975
|
+
const pending = `${destination}.pending-${process.pid}-${(0, import_node_crypto15.randomBytes)(6).toString("hex")}`;
|
|
76252
76976
|
try {
|
|
76253
76977
|
await (0, import_promises5.cp)(canonicalDirectory, pending, { recursive: true, errorOnExist: true, force: false });
|
|
76254
76978
|
await (0, import_promises5.writeFile)(
|
|
76255
|
-
(0,
|
|
76979
|
+
(0, import_node_path21.join)(pending, ".alan-compatibility.json"),
|
|
76256
76980
|
`${JSON.stringify({ ...markerFor(entry), canonicalDirectory }, null, 2)}
|
|
76257
76981
|
`,
|
|
76258
76982
|
{ encoding: "utf8", flag: "wx", mode: 384 }
|
|
@@ -76265,8 +76989,8 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
76265
76989
|
async runExclusively(work) {
|
|
76266
76990
|
const previous = this.reconcileTail;
|
|
76267
76991
|
let release2;
|
|
76268
|
-
this.reconcileTail = new Promise((
|
|
76269
|
-
release2 =
|
|
76992
|
+
this.reconcileTail = new Promise((resolve15) => {
|
|
76993
|
+
release2 = resolve15;
|
|
76270
76994
|
});
|
|
76271
76995
|
await previous;
|
|
76272
76996
|
try {
|
|
@@ -76277,14 +77001,14 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
76277
77001
|
}
|
|
76278
77002
|
async withMachineLock(work) {
|
|
76279
77003
|
await (0, import_promises5.mkdir)(this.skillsStateDirectory, { recursive: true, mode: 448 });
|
|
76280
|
-
const lockDirectory = (0,
|
|
77004
|
+
const lockDirectory = (0, import_node_path21.join)(this.skillsStateDirectory, "manager.lock");
|
|
76281
77005
|
const deadline = Date.now() + 3e4;
|
|
76282
77006
|
while (true) {
|
|
76283
77007
|
try {
|
|
76284
77008
|
await (0, import_promises5.mkdir)(lockDirectory, { mode: 448 });
|
|
76285
77009
|
try {
|
|
76286
77010
|
await (0, import_promises5.writeFile)(
|
|
76287
|
-
(0,
|
|
77011
|
+
(0, import_node_path21.join)(lockDirectory, "owner.json"),
|
|
76288
77012
|
`${JSON.stringify({ pid: process.pid, acquiredAt: (/* @__PURE__ */ new Date()).toISOString() })}
|
|
76289
77013
|
`,
|
|
76290
77014
|
{ encoding: "utf8", flag: "wx", mode: 384 }
|
|
@@ -76334,7 +77058,7 @@ async function packageMatches(directory, expectedHash) {
|
|
|
76334
77058
|
async function readMarker(directory) {
|
|
76335
77059
|
try {
|
|
76336
77060
|
const value2 = JSON.parse(
|
|
76337
|
-
await (0, import_promises5.readFile)((0,
|
|
77061
|
+
await (0, import_promises5.readFile)((0, import_node_path21.join)(directory, ".alan-managed.json"), "utf8")
|
|
76338
77062
|
);
|
|
76339
77063
|
if (typeof value2 !== "object" || value2 === null || !("protocolVersion" in value2) || value2.protocolVersion !== SKILLS_PROTOCOL_VERSION || !("skillId" in value2) || typeof value2.skillId !== "string" || !("versionId" in value2) || typeof value2.versionId !== "string" || !("contentHash" in value2) || typeof value2.contentHash !== "string" || !/^sha256:[a-f0-9]{64}$/.test(value2.contentHash)) {
|
|
76340
77064
|
return null;
|
|
@@ -76348,18 +77072,18 @@ async function compatibilityProjectionMatches(destination, canonicalDirectory, e
|
|
|
76348
77072
|
const metadata = await (0, import_promises5.lstat)(destination);
|
|
76349
77073
|
if (metadata.isSymbolicLink()) {
|
|
76350
77074
|
const target = await (0, import_promises5.readlink)(destination);
|
|
76351
|
-
return (0,
|
|
77075
|
+
return (0, import_node_path21.resolve)((0, import_node_path21.dirname)(destination), target) === (0, import_node_path21.resolve)(canonicalDirectory);
|
|
76352
77076
|
}
|
|
76353
77077
|
if (!metadata.isDirectory()) return false;
|
|
76354
77078
|
const marker = await readCompatibilityMarker(destination);
|
|
76355
77079
|
return marker?.canonicalDirectory === canonicalDirectory && marker.skillId === entry.skillId && marker.versionId === entry.versionId && marker.contentHash === entry.contentHash && await packageMatches(destination, entry.contentHash);
|
|
76356
77080
|
}
|
|
76357
77081
|
async function removeCompatibilityProjection(destination, canonicalDirectory, projection) {
|
|
76358
|
-
if (!await
|
|
77082
|
+
if (!await pathExists2(destination)) return true;
|
|
76359
77083
|
const metadata = await (0, import_promises5.lstat)(destination);
|
|
76360
77084
|
if (metadata.isSymbolicLink()) {
|
|
76361
77085
|
const target = await (0, import_promises5.readlink)(destination);
|
|
76362
|
-
if ((0,
|
|
77086
|
+
if ((0, import_node_path21.resolve)((0, import_node_path21.dirname)(destination), target) !== (0, import_node_path21.resolve)(canonicalDirectory)) return false;
|
|
76363
77087
|
await (0, import_promises5.rm)(destination, { force: true });
|
|
76364
77088
|
return true;
|
|
76365
77089
|
}
|
|
@@ -76371,7 +77095,7 @@ async function removeCompatibilityProjection(destination, canonicalDirectory, pr
|
|
|
76371
77095
|
async function readCompatibilityMarker(directory) {
|
|
76372
77096
|
try {
|
|
76373
77097
|
const value2 = JSON.parse(
|
|
76374
|
-
await (0, import_promises5.readFile)((0,
|
|
77098
|
+
await (0, import_promises5.readFile)((0, import_node_path21.join)(directory, ".alan-compatibility.json"), "utf8")
|
|
76375
77099
|
);
|
|
76376
77100
|
if (value2.protocolVersion !== SKILLS_PROTOCOL_VERSION || typeof value2.skillId !== "string" || typeof value2.versionId !== "string" || !/^sha256:[a-f0-9]{64}$/.test(value2.contentHash) || typeof value2.canonicalDirectory !== "string") {
|
|
76377
77101
|
return null;
|
|
@@ -76411,15 +77135,15 @@ async function legacyFileMatches(directory, expectedHash) {
|
|
|
76411
77135
|
try {
|
|
76412
77136
|
const metadata = await (0, import_promises5.lstat)(directory);
|
|
76413
77137
|
if (!metadata.isDirectory() || metadata.isSymbolicLink()) return false;
|
|
76414
|
-
const content = await (0, import_promises5.readFile)((0,
|
|
76415
|
-
return (0,
|
|
77138
|
+
const content = await (0, import_promises5.readFile)((0, import_node_path21.join)(directory, "SKILL.md"));
|
|
77139
|
+
return (0, import_node_crypto15.createHash)("sha256").update(content).digest("hex").slice(0, 16) === expectedHash;
|
|
76416
77140
|
} catch {
|
|
76417
77141
|
return false;
|
|
76418
77142
|
}
|
|
76419
77143
|
}
|
|
76420
77144
|
async function removeExactLegacySkill(directory, expectedHash) {
|
|
76421
77145
|
if (!await legacyFileMatches(directory, expectedHash)) return;
|
|
76422
|
-
await (0, import_promises5.rm)((0,
|
|
77146
|
+
await (0, import_promises5.rm)((0, import_node_path21.join)(directory, "SKILL.md"), { force: true });
|
|
76423
77147
|
const remaining = await (0, import_promises5.readdir)(directory);
|
|
76424
77148
|
if (remaining.length === 0) await (0, import_promises5.rm)(directory, { recursive: true, force: true });
|
|
76425
77149
|
}
|
|
@@ -76442,7 +77166,7 @@ function localRasterMediaType(path2) {
|
|
|
76442
77166
|
function localTextMediaType(path2) {
|
|
76443
77167
|
return path2.toLowerCase().endsWith(".md") ? "text/markdown; charset=utf-8" : "text/plain; charset=utf-8";
|
|
76444
77168
|
}
|
|
76445
|
-
async function
|
|
77169
|
+
async function pathExists2(path2) {
|
|
76446
77170
|
try {
|
|
76447
77171
|
await (0, import_promises5.lstat)(path2);
|
|
76448
77172
|
return true;
|
|
@@ -76537,10 +77261,10 @@ var SuspensionDetector = class {
|
|
|
76537
77261
|
async function startDaemon(args) {
|
|
76538
77262
|
bindConfigPath(args);
|
|
76539
77263
|
const stored = readConfig();
|
|
76540
|
-
const installationId = stored.installationId ?? (0,
|
|
77264
|
+
const installationId = stored.installationId ?? (0, import_node_crypto16.randomUUID)();
|
|
76541
77265
|
const configPath = getConfigPath();
|
|
76542
77266
|
const endpointProfile = stored.endpointProfile ?? resolveEndpointProfile(args);
|
|
76543
|
-
const daemonSessionId = (0,
|
|
77267
|
+
const daemonSessionId = (0, import_node_crypto16.randomUUID)();
|
|
76544
77268
|
let heartbeatSeq = 0;
|
|
76545
77269
|
const runtimeId = argValue(args, "--runtime-id") ?? process.env.ALAN_RUNTIME_ID ?? stored.runtimeId;
|
|
76546
77270
|
const runtimeTokenOverride = argValue(args, "--token") ?? process.env.ALAN_RUNTIME_TOKEN;
|
|
@@ -76557,11 +77281,11 @@ async function startDaemon(args) {
|
|
|
76557
77281
|
wsUrl = endpointMismatch.expectedWsUrl;
|
|
76558
77282
|
}
|
|
76559
77283
|
let localApiPort = getLocalApiPort(args, stored);
|
|
76560
|
-
const localApiToken = stored.localApiToken ?? (0,
|
|
76561
|
-
const localServiceId = stored.localServiceId ?? (0,
|
|
76562
|
-
const localServiceSecret = stored.localServiceSecret ?? (0,
|
|
77284
|
+
const localApiToken = stored.localApiToken ?? (0, import_node_crypto16.randomBytes)(24).toString("hex");
|
|
77285
|
+
const localServiceId = stored.localServiceId ?? (0, import_node_crypto16.randomUUID)();
|
|
77286
|
+
const localServiceSecret = stored.localServiceSecret ?? (0, import_node_crypto16.randomBytes)(32).toString("base64url");
|
|
76563
77287
|
const localServiceStartedAtMs = Date.now();
|
|
76564
|
-
const eventSpoolKey = stored.eventSpoolKey ?? (0,
|
|
77288
|
+
const eventSpoolKey = stored.eventSpoolKey ?? (0, import_node_crypto16.randomBytes)(32).toString("base64url");
|
|
76565
77289
|
if (stored.daemonEpoch !== void 0 && (!Number.isSafeInteger(stored.daemonEpoch) || stored.daemonEpoch < 0 || stored.daemonEpoch >= Number.MAX_SAFE_INTEGER)) {
|
|
76566
77290
|
throw new Error("daemon config contains an invalid durable epoch; repair the runtime config");
|
|
76567
77291
|
}
|
|
@@ -76659,17 +77383,17 @@ async function startDaemon(args) {
|
|
|
76659
77383
|
installationId
|
|
76660
77384
|
});
|
|
76661
77385
|
const recentLogs = [];
|
|
76662
|
-
const daemonLogPath = (0,
|
|
77386
|
+
const daemonLogPath = (0, import_node_path22.join)((0, import_node_path22.dirname)(configPath), "logs", "daemon.log");
|
|
76663
77387
|
const persistDaemonLog = (line) => {
|
|
76664
77388
|
try {
|
|
76665
|
-
(0,
|
|
77389
|
+
(0, import_node_fs20.mkdirSync)((0, import_node_path22.dirname)(daemonLogPath), { recursive: true });
|
|
76666
77390
|
try {
|
|
76667
|
-
if ((0,
|
|
76668
|
-
(0,
|
|
77391
|
+
if ((0, import_node_fs20.statSync)(daemonLogPath).size > 5 * 1024 * 1024) {
|
|
77392
|
+
(0, import_node_fs20.renameSync)(daemonLogPath, `${daemonLogPath}.1`);
|
|
76669
77393
|
}
|
|
76670
77394
|
} catch {
|
|
76671
77395
|
}
|
|
76672
|
-
(0,
|
|
77396
|
+
(0, import_node_fs20.appendFileSync)(daemonLogPath, `${line}
|
|
76673
77397
|
`, "utf8");
|
|
76674
77398
|
} catch {
|
|
76675
77399
|
}
|
|
@@ -76685,23 +77409,31 @@ async function startDaemon(args) {
|
|
|
76685
77409
|
if (sleepInhibitor.backendNames().length > 0) {
|
|
76686
77410
|
pushLog(`sleep-inhibitor backends=${sleepInhibitor.backendNames().join(",")}`);
|
|
76687
77411
|
}
|
|
76688
|
-
const alanMcpRegisteredRuns = /* @__PURE__ */ new
|
|
77412
|
+
const alanMcpRegisteredRuns = /* @__PURE__ */ new Map();
|
|
76689
77413
|
const alanMcpRegisteredBackends = /* @__PURE__ */ new Set();
|
|
76690
77414
|
const finalizeAlanMcpForRun = (runId) => {
|
|
76691
77415
|
if (!alanMcpRegisteredRuns.delete(runId)) return;
|
|
76692
77416
|
if (alanMcpRegisteredRuns.size > 0) return;
|
|
76693
|
-
const backends = [...alanMcpRegisteredBackends];
|
|
76694
|
-
alanMcpRegisteredBackends.clear();
|
|
76695
77417
|
void (async () => {
|
|
76696
|
-
|
|
76697
|
-
|
|
76698
|
-
|
|
76699
|
-
|
|
76700
|
-
|
|
77418
|
+
const releaseRunStart = await runStartGate.acquire();
|
|
77419
|
+
try {
|
|
77420
|
+
if (alanMcpRegisteredRuns.size > 0) return;
|
|
77421
|
+
const backends = [...alanMcpRegisteredBackends];
|
|
77422
|
+
alanMcpRegisteredBackends.clear();
|
|
77423
|
+
for (const backend of backends) {
|
|
77424
|
+
try {
|
|
77425
|
+
const result = await restoreStaticAlanMcp(backend, apiUrl);
|
|
77426
|
+
if (result.ok) {
|
|
77427
|
+
pushLog(`mcp-static-restored backend=${backend} run=${runId}`);
|
|
77428
|
+
} else {
|
|
77429
|
+
pushLog(`mcp-static-restore-failed backend=${backend} ${result.error ?? "unknown"}`);
|
|
77430
|
+
}
|
|
77431
|
+
} catch (err) {
|
|
77432
|
+
pushLog(`mcp-static-restore-failed backend=${backend} ${err.message}`);
|
|
76701
77433
|
}
|
|
76702
|
-
} catch (err) {
|
|
76703
|
-
pushLog(`mcp-unregister-failed backend=${backend} ${err.message}`);
|
|
76704
77434
|
}
|
|
77435
|
+
} finally {
|
|
77436
|
+
releaseRunStart();
|
|
76705
77437
|
}
|
|
76706
77438
|
})();
|
|
76707
77439
|
};
|
|
@@ -76713,6 +77445,8 @@ async function startDaemon(args) {
|
|
|
76713
77445
|
return workspaceAccessTracker.recoveryPath() ?? void 0;
|
|
76714
77446
|
};
|
|
76715
77447
|
let eventOutbox;
|
|
77448
|
+
let nativeHookInbox;
|
|
77449
|
+
let nativeTranscriptStreamer;
|
|
76716
77450
|
let runJournal;
|
|
76717
77451
|
try {
|
|
76718
77452
|
eventOutbox = new EncryptedEventOutbox(
|
|
@@ -76720,6 +77454,13 @@ async function startDaemon(args) {
|
|
|
76720
77454
|
eventSpoolKey,
|
|
76721
77455
|
pushLog
|
|
76722
77456
|
);
|
|
77457
|
+
nativeHookInbox = new EncryptedNativeHookInbox(nativeHookInboxPath(configPath), eventSpoolKey, {
|
|
77458
|
+
recoverClaims: true,
|
|
77459
|
+
onQuarantine: ({ fileName, error: error61 }) => pushLog(`native-hook-quarantined file=${fileName} error=${error61.message}`)
|
|
77460
|
+
});
|
|
77461
|
+
nativeTranscriptStreamer = new NativeTranscriptStreamer(
|
|
77462
|
+
new NativeTranscriptCursorStore(`${configPath}.native-transcript-cursors.json`)
|
|
77463
|
+
);
|
|
76723
77464
|
runJournal = new RuntimeRunJournal(`${configPath}.run-journal.json`);
|
|
76724
77465
|
} catch (error61) {
|
|
76725
77466
|
releaseOwnerLease();
|
|
@@ -76727,9 +77468,57 @@ async function startDaemon(args) {
|
|
|
76727
77468
|
throw error61;
|
|
76728
77469
|
}
|
|
76729
77470
|
const workspaceRelocations = /* @__PURE__ */ new Map();
|
|
77471
|
+
let nativeHookDrainInFlight = false;
|
|
77472
|
+
const flushNativeHookInbox = async () => {
|
|
77473
|
+
if (nativeHookDrainInFlight || !socket.connected) return;
|
|
77474
|
+
nativeHookDrainInFlight = true;
|
|
77475
|
+
try {
|
|
77476
|
+
const result = await drainNativeHookInbox({
|
|
77477
|
+
inbox: nativeHookInbox,
|
|
77478
|
+
transcriptStreamer: nativeTranscriptStreamer,
|
|
77479
|
+
sink: {
|
|
77480
|
+
deliver: (envelope) => new Promise((resolveDelivery, rejectDelivery) => {
|
|
77481
|
+
socket.timeout(5e3).emit(
|
|
77482
|
+
"native.session.event",
|
|
77483
|
+
envelope,
|
|
77484
|
+
(error61, acknowledgement) => {
|
|
77485
|
+
const accepted = !error61 && acknowledgement?.ok === true;
|
|
77486
|
+
if (!accepted) {
|
|
77487
|
+
pushLog(
|
|
77488
|
+
`native-hook-delivery-deferred event=${envelope.eventId} provider=${envelope.provider} error=${error61?.message ?? "none"} ack=${acknowledgement?.ok ?? "missing"} retryable=${acknowledgement?.retryable ?? "missing"}`
|
|
77489
|
+
);
|
|
77490
|
+
}
|
|
77491
|
+
if (!error61 && acknowledgement?.ok === false && acknowledgement.retryable === false) {
|
|
77492
|
+
rejectDelivery(
|
|
77493
|
+
new PermanentNativeHookDeliveryError(
|
|
77494
|
+
`backend permanently rejected native event ${envelope.eventId}`
|
|
77495
|
+
)
|
|
77496
|
+
);
|
|
77497
|
+
return;
|
|
77498
|
+
}
|
|
77499
|
+
resolveDelivery(accepted);
|
|
77500
|
+
}
|
|
77501
|
+
);
|
|
77502
|
+
})
|
|
77503
|
+
},
|
|
77504
|
+
onTranscriptError: ({ envelope, error: error61 }) => pushLog(
|
|
77505
|
+
`native-transcript-unavailable event=${envelope.eventId} provider=${envelope.provider} error=${error61.message}`
|
|
77506
|
+
)
|
|
77507
|
+
});
|
|
77508
|
+
if (result.attempted > 0) {
|
|
77509
|
+
pushLog(
|
|
77510
|
+
`native-hook-drain attempted=${result.attempted} acknowledged=${result.acknowledged} deferred=${result.deferred}`
|
|
77511
|
+
);
|
|
77512
|
+
}
|
|
77513
|
+
} catch (error61) {
|
|
77514
|
+
pushLog(`native-hook-drain-failed ${error61.message}`);
|
|
77515
|
+
} finally {
|
|
77516
|
+
nativeHookDrainInFlight = false;
|
|
77517
|
+
}
|
|
77518
|
+
};
|
|
76730
77519
|
for (const relocation of stored.workspaceRelocations ?? []) {
|
|
76731
77520
|
if (relocation?.conversationId && relocation.runId && relocation.previousPath && relocation.canonicalPath) {
|
|
76732
|
-
workspaceRelocations.set((0,
|
|
77521
|
+
workspaceRelocations.set((0, import_node_path22.resolve)(relocation.previousPath), relocation);
|
|
76733
77522
|
}
|
|
76734
77523
|
}
|
|
76735
77524
|
if (stored.localApiPort !== localApiPort || stored.localApiToken !== localApiToken || stored.localServiceId !== localServiceId || stored.localServiceSecret !== localServiceSecret || stored.eventSpoolKey !== eventSpoolKey || stored.daemonEpoch !== daemonEpoch || stored.installationId !== installationId) {
|
|
@@ -76916,6 +77705,7 @@ async function startDaemon(args) {
|
|
|
76916
77705
|
if (!socket.connected) return;
|
|
76917
77706
|
emitRunnerPresence();
|
|
76918
77707
|
flushWorkspaceRelocations();
|
|
77708
|
+
void flushNativeHookInbox();
|
|
76919
77709
|
}, 15e3);
|
|
76920
77710
|
let pendingWakeGapMs = null;
|
|
76921
77711
|
const flushWakeGap = () => {
|
|
@@ -77278,7 +78068,7 @@ async function startDaemon(args) {
|
|
|
77278
78068
|
`${describeOccupiedLocalDaemon(localApiPort)} The token in ${getConfigPath()} no longer matches the running daemon; quit Alan desktop or run setup again.`
|
|
77279
78069
|
);
|
|
77280
78070
|
}
|
|
77281
|
-
const localApiListening = new Promise((
|
|
78071
|
+
const localApiListening = new Promise((resolve15, reject) => {
|
|
77282
78072
|
const onError = (error61) => {
|
|
77283
78073
|
clearInterval(heartbeat);
|
|
77284
78074
|
clearInterval(versionRefresh);
|
|
@@ -77308,7 +78098,7 @@ async function startDaemon(args) {
|
|
|
77308
78098
|
if (address && typeof address === "object") localApiPort = address.port;
|
|
77309
78099
|
pushLog(`local_api listening port=${localApiPort}`);
|
|
77310
78100
|
console.info("[alan-agent] Local daemon API listening", { port: localApiPort });
|
|
77311
|
-
|
|
78101
|
+
resolve15();
|
|
77312
78102
|
});
|
|
77313
78103
|
});
|
|
77314
78104
|
try {
|
|
@@ -77328,7 +78118,7 @@ async function startDaemon(args) {
|
|
|
77328
78118
|
clearInterval(workspaceLeaseMonitor);
|
|
77329
78119
|
suspensionDetector.stop();
|
|
77330
78120
|
socket.disconnect();
|
|
77331
|
-
await new Promise((
|
|
78121
|
+
await new Promise((resolve15) => localServer.close(() => resolve15()));
|
|
77332
78122
|
releaseOwnerLease();
|
|
77333
78123
|
releaseStartupLease();
|
|
77334
78124
|
throw error61;
|
|
@@ -77338,6 +78128,8 @@ async function startDaemon(args) {
|
|
|
77338
78128
|
skillSyncInterval.unref?.();
|
|
77339
78129
|
const skillAuditInterval = setInterval(() => runSkillSync("refresh"), 24 * 60 * 6e4);
|
|
77340
78130
|
skillAuditInterval.unref?.();
|
|
78131
|
+
const nativeHookDrainInterval = setInterval(() => void flushNativeHookInbox(), 1e3);
|
|
78132
|
+
nativeHookDrainInterval.unref?.();
|
|
77341
78133
|
let stopped = false;
|
|
77342
78134
|
const stop = async () => {
|
|
77343
78135
|
if (stopped) return;
|
|
@@ -77348,14 +78140,15 @@ async function startDaemon(args) {
|
|
|
77348
78140
|
clearInterval(workspaceLeaseMonitor);
|
|
77349
78141
|
clearInterval(skillSyncInterval);
|
|
77350
78142
|
clearInterval(skillAuditInterval);
|
|
78143
|
+
clearInterval(nativeHookDrainInterval);
|
|
77351
78144
|
suspensionDetector.stop();
|
|
77352
78145
|
for (const entry of activeAgents.values()) {
|
|
77353
78146
|
abortActiveAgent(entry, { userInitiated: true });
|
|
77354
78147
|
}
|
|
77355
78148
|
activeAgents.clear();
|
|
77356
78149
|
socket.disconnect();
|
|
77357
|
-
await new Promise((
|
|
77358
|
-
localServer.close(() =>
|
|
78150
|
+
await new Promise((resolve15) => {
|
|
78151
|
+
localServer.close(() => resolve15());
|
|
77359
78152
|
});
|
|
77360
78153
|
} finally {
|
|
77361
78154
|
sleepInhibitor.dispose();
|
|
@@ -77435,7 +78228,7 @@ async function setupDaemon(args) {
|
|
|
77435
78228
|
const token = argValue(args, "--token") ?? process.env.ALAN_SETUP_TOKEN;
|
|
77436
78229
|
const displayName = argValue(args, "--name") ?? (0, import_node_os11.hostname)();
|
|
77437
78230
|
const scope = argValue(args, "--scope") ?? process.env.ALAN_RUNTIME_SCOPE ?? (setupToken ? "team" : "user");
|
|
77438
|
-
const installationId = argValue(args, "--installation-id") ?? previousConfig.installationId ?? (0,
|
|
78231
|
+
const installationId = argValue(args, "--installation-id") ?? previousConfig.installationId ?? (0, import_node_crypto17.randomUUID)();
|
|
77439
78232
|
if (scope !== "team" && scope !== "user") {
|
|
77440
78233
|
throw new Error("setup --scope must be either 'team' or 'user'");
|
|
77441
78234
|
}
|
|
@@ -77444,7 +78237,7 @@ async function setupDaemon(args) {
|
|
|
77444
78237
|
"setup requires --setup-token <token> or --token <Alan access token>.\nFor normal setup, copy the setup token from Alan and run: alan-agent setup --setup-token <token>\nFor browser authorization, run: alan-agent login"
|
|
77445
78238
|
);
|
|
77446
78239
|
}
|
|
77447
|
-
const registrationAttemptId = setupToken ? getOrCreateSetupAttemptId(setupToken) : (0,
|
|
78240
|
+
const registrationAttemptId = setupToken ? getOrCreateSetupAttemptId(setupToken) : (0, import_node_crypto17.randomUUID)();
|
|
77448
78241
|
const registrationUrl = `${apiUrl}/public/runtimes${setupToken ? "/register-with-setup-token" : ""}`;
|
|
77449
78242
|
const registrationInit = {
|
|
77450
78243
|
method: "POST",
|
|
@@ -77479,7 +78272,7 @@ async function setupDaemon(args) {
|
|
|
77479
78272
|
}
|
|
77480
78273
|
const retriableStatus = response && (response.status === 408 || response.status === 429 || response.status >= 500);
|
|
77481
78274
|
if (response && (!retriableStatus || response.ok) || attempt === 3) break;
|
|
77482
|
-
await new Promise((
|
|
78275
|
+
await new Promise((resolve15) => setTimeout(resolve15, attempt * 100));
|
|
77483
78276
|
}
|
|
77484
78277
|
if (!response) {
|
|
77485
78278
|
const detail = transportError instanceof Error ? transportError.message : String(transportError);
|
|
@@ -77510,10 +78303,10 @@ async function setupDaemon(args) {
|
|
|
77510
78303
|
runtimeToken: body.runtimeToken,
|
|
77511
78304
|
runtimeRenewalToken: body.runtimeRenewalToken,
|
|
77512
78305
|
localApiPort: getLocalApiPort(args, {}),
|
|
77513
|
-
localApiToken: previousConfig.localApiToken ?? (0,
|
|
77514
|
-
localServiceId: previousConfig.localServiceId ?? (0,
|
|
77515
|
-
localServiceSecret: previousConfig.localServiceSecret ?? (0,
|
|
77516
|
-
eventSpoolKey: previousConfig.eventSpoolKey ?? (0,
|
|
78306
|
+
localApiToken: previousConfig.localApiToken ?? (0, import_node_crypto17.randomBytes)(24).toString("hex"),
|
|
78307
|
+
localServiceId: previousConfig.localServiceId ?? (0, import_node_crypto17.randomUUID)(),
|
|
78308
|
+
localServiceSecret: previousConfig.localServiceSecret ?? (0, import_node_crypto17.randomBytes)(32).toString("base64url"),
|
|
78309
|
+
eventSpoolKey: previousConfig.eventSpoolKey ?? (0, import_node_crypto17.randomBytes)(32).toString("base64url"),
|
|
77517
78310
|
daemonEpoch: previousConfig.daemonEpoch,
|
|
77518
78311
|
displayName: body.runtime.displayName ?? displayName,
|
|
77519
78312
|
installationId
|
|
@@ -77543,7 +78336,7 @@ async function loginWithDeviceCode(args) {
|
|
|
77543
78336
|
const displayName = argValue(args, "--name") ?? (0, import_node_os11.hostname)();
|
|
77544
78337
|
const maxPolls = Number.parseInt(argValue(args, "--max-polls") ?? "300", 10);
|
|
77545
78338
|
const resumed = readPendingDeviceAuthorization(apiUrl);
|
|
77546
|
-
const installationId = resumed?.installationId ?? previousConfig.installationId ?? (0,
|
|
78339
|
+
const installationId = resumed?.installationId ?? previousConfig.installationId ?? (0, import_node_crypto17.randomUUID)();
|
|
77547
78340
|
let authorization = resumed ? { ...resumed, operationId: operation.id } : null;
|
|
77548
78341
|
if (!authorization) {
|
|
77549
78342
|
clearPendingDeviceAuthorization();
|
|
@@ -77619,10 +78412,10 @@ async function loginWithDeviceCode(args) {
|
|
|
77619
78412
|
runtimeToken: body.runtimeToken,
|
|
77620
78413
|
runtimeRenewalToken: body.runtimeRenewalToken,
|
|
77621
78414
|
localApiPort: getLocalApiPort(args, {}),
|
|
77622
|
-
localApiToken: previousConfig.localApiToken ?? (0,
|
|
77623
|
-
localServiceId: previousConfig.localServiceId ?? (0,
|
|
77624
|
-
localServiceSecret: previousConfig.localServiceSecret ?? (0,
|
|
77625
|
-
eventSpoolKey: previousConfig.eventSpoolKey ?? (0,
|
|
78415
|
+
localApiToken: previousConfig.localApiToken ?? (0, import_node_crypto17.randomBytes)(24).toString("hex"),
|
|
78416
|
+
localServiceId: previousConfig.localServiceId ?? (0, import_node_crypto17.randomUUID)(),
|
|
78417
|
+
localServiceSecret: previousConfig.localServiceSecret ?? (0, import_node_crypto17.randomBytes)(32).toString("base64url"),
|
|
78418
|
+
eventSpoolKey: previousConfig.eventSpoolKey ?? (0, import_node_crypto17.randomBytes)(32).toString("base64url"),
|
|
77626
78419
|
daemonEpoch: previousConfig.daemonEpoch,
|
|
77627
78420
|
displayName: body.runtime.displayName ?? displayName,
|
|
77628
78421
|
installationId
|
|
@@ -77973,6 +78766,860 @@ async function printDoctor(args = []) {
|
|
|
77973
78766
|
);
|
|
77974
78767
|
}
|
|
77975
78768
|
|
|
78769
|
+
// src/mcp-config-adapters.ts
|
|
78770
|
+
var import_node_crypto18 = require("crypto");
|
|
78771
|
+
var import_node_fs21 = require("fs");
|
|
78772
|
+
var import_node_os12 = require("os");
|
|
78773
|
+
var import_node_path23 = require("path");
|
|
78774
|
+
var log = {
|
|
78775
|
+
info: (...args) => console.info("[mcp-config-adapters]", ...args),
|
|
78776
|
+
warn: (...args) => console.warn("[mcp-config-adapters]", ...args),
|
|
78777
|
+
debug: () => {
|
|
78778
|
+
}
|
|
78779
|
+
};
|
|
78780
|
+
var HOME = (0, import_node_os12.homedir)();
|
|
78781
|
+
var PATHS = {
|
|
78782
|
+
claude: (0, import_node_path23.join)(HOME, ".claude.json"),
|
|
78783
|
+
codex: (0, import_node_path23.join)(HOME, ".codex", "config.toml"),
|
|
78784
|
+
cursor: (0, import_node_path23.join)(HOME, ".cursor", "mcp.json"),
|
|
78785
|
+
antigravity: (0, import_node_path23.join)(HOME, ".gemini", "config", "mcp_config.json"),
|
|
78786
|
+
kimi: (0, import_node_path23.join)(HOME, ".kimi", "mcp.json"),
|
|
78787
|
+
opencode: (0, import_node_path23.join)(HOME, ".config", "opencode", "opencode.json"),
|
|
78788
|
+
factory: (0, import_node_path23.join)(HOME, ".factory", "mcp.json"),
|
|
78789
|
+
amp: (0, import_node_path23.join)(HOME, ".config", "amp", "settings.json")
|
|
78790
|
+
};
|
|
78791
|
+
var MalformedToolConfigError = class extends Error {
|
|
78792
|
+
};
|
|
78793
|
+
function parseJsonSafe2(content) {
|
|
78794
|
+
if (!content) return {};
|
|
78795
|
+
try {
|
|
78796
|
+
const parsed = JSON.parse(content);
|
|
78797
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
78798
|
+
return parsed;
|
|
78799
|
+
}
|
|
78800
|
+
} catch {
|
|
78801
|
+
}
|
|
78802
|
+
throw new MalformedToolConfigError("Existing tool config is malformed");
|
|
78803
|
+
}
|
|
78804
|
+
function readFileOr(path2) {
|
|
78805
|
+
return (0, import_node_fs21.existsSync)(path2) ? (0, import_node_fs21.readFileSync)(path2, "utf-8") : null;
|
|
78806
|
+
}
|
|
78807
|
+
function acquireConfigLock2(path2) {
|
|
78808
|
+
(0, import_node_fs21.mkdirSync)((0, import_node_path23.dirname)(path2), { recursive: true });
|
|
78809
|
+
const lockPath = `${path2}.alan-lock`;
|
|
78810
|
+
const startedAt = Date.now();
|
|
78811
|
+
while (true) {
|
|
78812
|
+
try {
|
|
78813
|
+
(0, import_node_fs21.mkdirSync)(lockPath);
|
|
78814
|
+
return () => (0, import_node_fs21.rmSync)(lockPath, { recursive: true, force: true });
|
|
78815
|
+
} catch (error61) {
|
|
78816
|
+
const code = error61 && typeof error61 === "object" && "code" in error61 ? error61.code : void 0;
|
|
78817
|
+
if (code !== "EEXIST") throw error61;
|
|
78818
|
+
try {
|
|
78819
|
+
if (Date.now() - (0, import_node_fs21.statSync)(lockPath).mtimeMs > 3e4) {
|
|
78820
|
+
(0, import_node_fs21.rmSync)(lockPath, { recursive: true, force: true });
|
|
78821
|
+
continue;
|
|
78822
|
+
}
|
|
78823
|
+
} catch {
|
|
78824
|
+
continue;
|
|
78825
|
+
}
|
|
78826
|
+
if (Date.now() - startedAt >= 2e3) {
|
|
78827
|
+
throw new Error(`Alan could not safely update ${path2}`);
|
|
78828
|
+
}
|
|
78829
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 20);
|
|
78830
|
+
}
|
|
78831
|
+
}
|
|
78832
|
+
}
|
|
78833
|
+
function writeFileSafe2(path2, content) {
|
|
78834
|
+
(0, import_node_fs21.mkdirSync)((0, import_node_path23.dirname)(path2), { recursive: true });
|
|
78835
|
+
const pendingPath = `${path2}.alan-pending-${process.pid}-${(0, import_node_crypto18.randomBytes)(6).toString("hex")}`;
|
|
78836
|
+
try {
|
|
78837
|
+
(0, import_node_fs21.writeFileSync)(pendingPath, content, { encoding: "utf8", mode: 384, flag: "wx" });
|
|
78838
|
+
(0, import_node_fs21.renameSync)(pendingPath, path2);
|
|
78839
|
+
} finally {
|
|
78840
|
+
(0, import_node_fs21.rmSync)(pendingPath, { force: true });
|
|
78841
|
+
}
|
|
78842
|
+
}
|
|
78843
|
+
function saveMalformedBackup2(path2, content) {
|
|
78844
|
+
const hash2 = (0, import_node_crypto18.createHash)("sha256").update(content).digest("hex").slice(0, 16);
|
|
78845
|
+
const backupPath = `${path2}.alan-backup-${hash2}`;
|
|
78846
|
+
if (!(0, import_node_fs21.existsSync)(backupPath)) (0, import_node_fs21.copyFileSync)(path2, backupPath);
|
|
78847
|
+
return backupPath;
|
|
78848
|
+
}
|
|
78849
|
+
var claudeAdapter = {
|
|
78850
|
+
cli: "Claude Code",
|
|
78851
|
+
configPath: PATHS.claude,
|
|
78852
|
+
guardDir: HOME,
|
|
78853
|
+
// ~/.claude.json lives in home — always exists
|
|
78854
|
+
merge(existing, servers) {
|
|
78855
|
+
const config2 = parseJsonSafe2(existing);
|
|
78856
|
+
const mcpServers = config2.mcpServers ?? {};
|
|
78857
|
+
for (const s of servers) {
|
|
78858
|
+
if (s.kind === "http") {
|
|
78859
|
+
mcpServers[s.name] = { type: "http", url: s.url, headers: s.headers };
|
|
78860
|
+
} else {
|
|
78861
|
+
mcpServers[s.name] = {
|
|
78862
|
+
command: s.command,
|
|
78863
|
+
...s.args?.length ? { args: s.args } : {},
|
|
78864
|
+
...s.env && Object.keys(s.env).length > 0 ? { env: s.env } : {}
|
|
78865
|
+
};
|
|
78866
|
+
}
|
|
78867
|
+
}
|
|
78868
|
+
config2.mcpServers = mcpServers;
|
|
78869
|
+
return JSON.stringify(config2, null, 2);
|
|
78870
|
+
},
|
|
78871
|
+
remove(existing, serverNames) {
|
|
78872
|
+
const config2 = parseJsonSafe2(existing);
|
|
78873
|
+
const mcpServers = config2.mcpServers ?? {};
|
|
78874
|
+
for (const name of serverNames) delete mcpServers[name];
|
|
78875
|
+
config2.mcpServers = mcpServers;
|
|
78876
|
+
return JSON.stringify(config2, null, 2);
|
|
78877
|
+
}
|
|
78878
|
+
};
|
|
78879
|
+
var codexAdapter = {
|
|
78880
|
+
cli: "Codex",
|
|
78881
|
+
configPath: PATHS.codex,
|
|
78882
|
+
guardDir: (0, import_node_path23.join)(HOME, ".codex"),
|
|
78883
|
+
merge(existing, servers) {
|
|
78884
|
+
let config2 = {};
|
|
78885
|
+
if (existing) {
|
|
78886
|
+
try {
|
|
78887
|
+
config2 = parse5(existing);
|
|
78888
|
+
} catch {
|
|
78889
|
+
throw new MalformedToolConfigError("Existing tool config is malformed");
|
|
78890
|
+
}
|
|
78891
|
+
}
|
|
78892
|
+
const mcpServers = config2.mcp_servers ?? {};
|
|
78893
|
+
for (const s of servers) {
|
|
78894
|
+
if (s.kind === "http") {
|
|
78895
|
+
mcpServers[s.name] = { url: s.url, http_headers: s.headers };
|
|
78896
|
+
} else {
|
|
78897
|
+
mcpServers[s.name] = {
|
|
78898
|
+
command: s.command,
|
|
78899
|
+
...s.args?.length ? { args: s.args } : {},
|
|
78900
|
+
...s.env && Object.keys(s.env).length > 0 ? { env: s.env } : {}
|
|
78901
|
+
};
|
|
78902
|
+
}
|
|
78903
|
+
}
|
|
78904
|
+
config2.mcp_servers = mcpServers;
|
|
78905
|
+
return stringify(config2);
|
|
78906
|
+
},
|
|
78907
|
+
remove(existing, serverNames) {
|
|
78908
|
+
if (!existing) return "";
|
|
78909
|
+
let config2 = {};
|
|
78910
|
+
try {
|
|
78911
|
+
config2 = parse5(existing);
|
|
78912
|
+
} catch {
|
|
78913
|
+
return existing;
|
|
78914
|
+
}
|
|
78915
|
+
const mcpServers = config2.mcp_servers ?? {};
|
|
78916
|
+
for (const name of serverNames) delete mcpServers[name];
|
|
78917
|
+
config2.mcp_servers = mcpServers;
|
|
78918
|
+
return stringify(config2);
|
|
78919
|
+
}
|
|
78920
|
+
};
|
|
78921
|
+
var cursorAdapter = {
|
|
78922
|
+
cli: "Cursor",
|
|
78923
|
+
configPath: PATHS.cursor,
|
|
78924
|
+
guardDir: (0, import_node_path23.join)(HOME, ".cursor"),
|
|
78925
|
+
merge(existing, servers) {
|
|
78926
|
+
const config2 = parseJsonSafe2(existing);
|
|
78927
|
+
const mcpServers = config2.mcpServers ?? {};
|
|
78928
|
+
for (const s of servers) {
|
|
78929
|
+
if (s.kind === "http") {
|
|
78930
|
+
mcpServers[s.name] = { type: "http", url: s.url, headers: s.headers };
|
|
78931
|
+
} else {
|
|
78932
|
+
mcpServers[s.name] = {
|
|
78933
|
+
command: s.command,
|
|
78934
|
+
...s.args?.length ? { args: s.args } : {},
|
|
78935
|
+
...s.env && Object.keys(s.env).length > 0 ? { env: s.env } : {}
|
|
78936
|
+
};
|
|
78937
|
+
}
|
|
78938
|
+
}
|
|
78939
|
+
config2.mcpServers = mcpServers;
|
|
78940
|
+
return JSON.stringify(config2, null, 2);
|
|
78941
|
+
},
|
|
78942
|
+
remove(existing, serverNames) {
|
|
78943
|
+
const config2 = parseJsonSafe2(existing);
|
|
78944
|
+
const mcpServers = config2.mcpServers ?? {};
|
|
78945
|
+
for (const name of serverNames) delete mcpServers[name];
|
|
78946
|
+
config2.mcpServers = mcpServers;
|
|
78947
|
+
return JSON.stringify(config2, null, 2);
|
|
78948
|
+
}
|
|
78949
|
+
};
|
|
78950
|
+
function createAntigravityAdapter() {
|
|
78951
|
+
return {
|
|
78952
|
+
cli: "Antigravity CLI",
|
|
78953
|
+
configPath: PATHS.antigravity,
|
|
78954
|
+
guardDir: (0, import_node_path23.join)(HOME, ".gemini", "config"),
|
|
78955
|
+
merge(existing, servers) {
|
|
78956
|
+
const config2 = parseJsonSafe2(existing);
|
|
78957
|
+
const mcpServers = config2.mcpServers ?? {};
|
|
78958
|
+
for (const s of servers) {
|
|
78959
|
+
if (s.kind === "http") {
|
|
78960
|
+
mcpServers[s.name] = { serverUrl: s.url, headers: s.headers };
|
|
78961
|
+
} else {
|
|
78962
|
+
mcpServers[s.name] = {
|
|
78963
|
+
command: s.command,
|
|
78964
|
+
...s.args?.length ? { args: s.args } : {},
|
|
78965
|
+
...s.env && Object.keys(s.env).length > 0 ? { env: s.env } : {}
|
|
78966
|
+
};
|
|
78967
|
+
}
|
|
78968
|
+
}
|
|
78969
|
+
config2.mcpServers = mcpServers;
|
|
78970
|
+
return JSON.stringify(config2, null, 2);
|
|
78971
|
+
},
|
|
78972
|
+
remove(existing, serverNames) {
|
|
78973
|
+
const config2 = parseJsonSafe2(existing);
|
|
78974
|
+
const mcpServers = config2.mcpServers ?? {};
|
|
78975
|
+
for (const name of serverNames) delete mcpServers[name];
|
|
78976
|
+
config2.mcpServers = mcpServers;
|
|
78977
|
+
return JSON.stringify(config2, null, 2);
|
|
78978
|
+
}
|
|
78979
|
+
};
|
|
78980
|
+
}
|
|
78981
|
+
var antigravityAdapter = createAntigravityAdapter();
|
|
78982
|
+
var kimiAdapter = {
|
|
78983
|
+
cli: "Kimi CLI",
|
|
78984
|
+
configPath: PATHS.kimi,
|
|
78985
|
+
guardDir: (0, import_node_path23.join)(HOME, ".kimi"),
|
|
78986
|
+
merge(existing, servers) {
|
|
78987
|
+
const config2 = parseJsonSafe2(existing);
|
|
78988
|
+
const mcpServers = config2.mcpServers ?? {};
|
|
78989
|
+
for (const s of servers) {
|
|
78990
|
+
if (s.kind === "http") {
|
|
78991
|
+
mcpServers[s.name] = { url: s.url, headers: s.headers };
|
|
78992
|
+
} else {
|
|
78993
|
+
mcpServers[s.name] = {
|
|
78994
|
+
command: s.command,
|
|
78995
|
+
...s.args?.length ? { args: s.args } : {},
|
|
78996
|
+
...s.env && Object.keys(s.env).length > 0 ? { env: s.env } : {}
|
|
78997
|
+
};
|
|
78998
|
+
}
|
|
78999
|
+
}
|
|
79000
|
+
config2.mcpServers = mcpServers;
|
|
79001
|
+
return JSON.stringify(config2, null, 2);
|
|
79002
|
+
},
|
|
79003
|
+
remove(existing, serverNames) {
|
|
79004
|
+
const config2 = parseJsonSafe2(existing);
|
|
79005
|
+
const mcpServers = config2.mcpServers ?? {};
|
|
79006
|
+
for (const name of serverNames) delete mcpServers[name];
|
|
79007
|
+
config2.mcpServers = mcpServers;
|
|
79008
|
+
return JSON.stringify(config2, null, 2);
|
|
79009
|
+
}
|
|
79010
|
+
};
|
|
79011
|
+
var opencodeAdapter = {
|
|
79012
|
+
cli: "OpenCode",
|
|
79013
|
+
configPath: PATHS.opencode,
|
|
79014
|
+
guardDir: (0, import_node_path23.join)(HOME, ".config", "opencode"),
|
|
79015
|
+
merge(existing, servers) {
|
|
79016
|
+
const config2 = parseJsonSafe2(existing);
|
|
79017
|
+
const mcp = config2.mcp ?? {};
|
|
79018
|
+
for (const s of servers) {
|
|
79019
|
+
if (s.kind === "http") {
|
|
79020
|
+
mcp[s.name] = { type: "remote", url: s.url, headers: s.headers };
|
|
79021
|
+
} else {
|
|
79022
|
+
mcp[s.name] = {
|
|
79023
|
+
type: "local",
|
|
79024
|
+
command: [s.command, ...s.args ?? []],
|
|
79025
|
+
...s.env && Object.keys(s.env).length > 0 ? { environment: s.env } : {}
|
|
79026
|
+
};
|
|
79027
|
+
}
|
|
79028
|
+
}
|
|
79029
|
+
config2.mcp = mcp;
|
|
79030
|
+
return JSON.stringify(config2, null, 2);
|
|
79031
|
+
},
|
|
79032
|
+
remove(existing, serverNames) {
|
|
79033
|
+
const config2 = parseJsonSafe2(existing);
|
|
79034
|
+
const mcp = config2.mcp ?? {};
|
|
79035
|
+
for (const name of serverNames) delete mcp[name];
|
|
79036
|
+
config2.mcp = mcp;
|
|
79037
|
+
return JSON.stringify(config2, null, 2);
|
|
79038
|
+
}
|
|
79039
|
+
};
|
|
79040
|
+
var factoryAdapter = {
|
|
79041
|
+
cli: "Factory Droid",
|
|
79042
|
+
configPath: PATHS.factory,
|
|
79043
|
+
guardDir: (0, import_node_path23.join)(HOME, ".factory"),
|
|
79044
|
+
merge(existing, servers) {
|
|
79045
|
+
const config2 = parseJsonSafe2(existing);
|
|
79046
|
+
const mcpServers = config2.mcpServers ?? {};
|
|
79047
|
+
for (const s of servers) {
|
|
79048
|
+
if (s.kind === "http") {
|
|
79049
|
+
mcpServers[s.name] = {
|
|
79050
|
+
type: "http",
|
|
79051
|
+
url: s.url,
|
|
79052
|
+
disabled: false,
|
|
79053
|
+
...s.headers && Object.keys(s.headers).length > 0 ? { headers: s.headers } : {}
|
|
79054
|
+
};
|
|
79055
|
+
} else {
|
|
79056
|
+
mcpServers[s.name] = {
|
|
79057
|
+
type: "stdio",
|
|
79058
|
+
command: s.command,
|
|
79059
|
+
disabled: false,
|
|
79060
|
+
...s.args?.length ? { args: s.args } : {},
|
|
79061
|
+
...s.env && Object.keys(s.env).length > 0 ? { env: s.env } : {}
|
|
79062
|
+
};
|
|
79063
|
+
}
|
|
79064
|
+
}
|
|
79065
|
+
config2.mcpServers = mcpServers;
|
|
79066
|
+
return JSON.stringify(config2, null, 2);
|
|
79067
|
+
},
|
|
79068
|
+
remove(existing, serverNames) {
|
|
79069
|
+
const config2 = parseJsonSafe2(existing);
|
|
79070
|
+
const mcpServers = config2.mcpServers ?? {};
|
|
79071
|
+
for (const name of serverNames) delete mcpServers[name];
|
|
79072
|
+
config2.mcpServers = mcpServers;
|
|
79073
|
+
return JSON.stringify(config2, null, 2);
|
|
79074
|
+
}
|
|
79075
|
+
};
|
|
79076
|
+
var ampAdapter = {
|
|
79077
|
+
cli: "Amp",
|
|
79078
|
+
configPath: PATHS.amp,
|
|
79079
|
+
guardDir: (0, import_node_path23.join)(HOME, ".config", "amp"),
|
|
79080
|
+
merge(existing, servers) {
|
|
79081
|
+
const config2 = parseJsonSafe2(existing);
|
|
79082
|
+
const mcpServers = config2["amp.mcpServers"] ?? {};
|
|
79083
|
+
for (const s of servers) {
|
|
79084
|
+
if (s.kind === "http") {
|
|
79085
|
+
mcpServers[s.name] = { url: s.url, headers: s.headers };
|
|
79086
|
+
} else {
|
|
79087
|
+
mcpServers[s.name] = {
|
|
79088
|
+
command: s.command,
|
|
79089
|
+
...s.args?.length ? { args: s.args } : {}
|
|
79090
|
+
};
|
|
79091
|
+
}
|
|
79092
|
+
}
|
|
79093
|
+
config2["amp.mcpServers"] = mcpServers;
|
|
79094
|
+
return JSON.stringify(config2, null, 2);
|
|
79095
|
+
},
|
|
79096
|
+
remove(existing, serverNames) {
|
|
79097
|
+
const config2 = parseJsonSafe2(existing);
|
|
79098
|
+
const mcpServers = config2["amp.mcpServers"] ?? {};
|
|
79099
|
+
for (const name of serverNames) delete mcpServers[name];
|
|
79100
|
+
config2["amp.mcpServers"] = mcpServers;
|
|
79101
|
+
return JSON.stringify(config2, null, 2);
|
|
79102
|
+
}
|
|
79103
|
+
};
|
|
79104
|
+
var ALL_ADAPTERS = [
|
|
79105
|
+
claudeAdapter,
|
|
79106
|
+
codexAdapter,
|
|
79107
|
+
cursorAdapter,
|
|
79108
|
+
antigravityAdapter,
|
|
79109
|
+
kimiAdapter,
|
|
79110
|
+
opencodeAdapter,
|
|
79111
|
+
factoryAdapter,
|
|
79112
|
+
ampAdapter
|
|
79113
|
+
];
|
|
79114
|
+
function serverEntry(adapter, content, name) {
|
|
79115
|
+
if (adapter === codexAdapter) {
|
|
79116
|
+
const parsed2 = parse5(content);
|
|
79117
|
+
const servers2 = parsed2.mcp_servers;
|
|
79118
|
+
return servers2 && typeof servers2 === "object" ? servers2[name] : void 0;
|
|
79119
|
+
}
|
|
79120
|
+
const parsed = parseJsonSafe2(content);
|
|
79121
|
+
const key = adapter === opencodeAdapter ? "mcp" : adapter === ampAdapter ? "amp.mcpServers" : "mcpServers";
|
|
79122
|
+
const servers = parsed[key];
|
|
79123
|
+
return servers && typeof servers === "object" ? servers[name] : void 0;
|
|
79124
|
+
}
|
|
79125
|
+
function canonicalize(value2) {
|
|
79126
|
+
if (Array.isArray(value2)) return value2.map(canonicalize);
|
|
79127
|
+
if (value2 && typeof value2 === "object") {
|
|
79128
|
+
return Object.fromEntries(
|
|
79129
|
+
Object.entries(value2).sort(([a], [b]) => a.localeCompare(b)).map(([key, entry]) => [key, canonicalize(entry)])
|
|
79130
|
+
);
|
|
79131
|
+
}
|
|
79132
|
+
return value2;
|
|
79133
|
+
}
|
|
79134
|
+
function entriesMatch(actual, expected) {
|
|
79135
|
+
return JSON.stringify(canonicalize(actual)) === JSON.stringify(canonicalize(expected));
|
|
79136
|
+
}
|
|
79137
|
+
var ALAN_SESSION_HEADER_NAMES = /* @__PURE__ */ new Set([
|
|
79138
|
+
"x-conversation-id",
|
|
79139
|
+
"x-team-id",
|
|
79140
|
+
"x-allowed-tools",
|
|
79141
|
+
"x-alan-run-reference"
|
|
79142
|
+
]);
|
|
79143
|
+
function normalizeAlanSessionHeaders(actual, expected) {
|
|
79144
|
+
if (!actual || typeof actual !== "object" || Array.isArray(actual)) return actual;
|
|
79145
|
+
if (!expected || typeof expected !== "object" || Array.isArray(expected)) return actual;
|
|
79146
|
+
const normalized = { ...actual };
|
|
79147
|
+
const expectedHeaders = expected;
|
|
79148
|
+
const hadAlanSessionMarker = Object.keys(normalized).some(
|
|
79149
|
+
(key) => ALAN_SESSION_HEADER_NAMES.has(key.toLowerCase())
|
|
79150
|
+
);
|
|
79151
|
+
for (const key of Object.keys(normalized)) {
|
|
79152
|
+
if (ALAN_SESSION_HEADER_NAMES.has(key.toLowerCase())) delete normalized[key];
|
|
79153
|
+
}
|
|
79154
|
+
if (hadAlanSessionMarker) {
|
|
79155
|
+
const expectedAuthorization = Object.entries(expectedHeaders).find(
|
|
79156
|
+
([key]) => key.toLowerCase() === "authorization"
|
|
79157
|
+
);
|
|
79158
|
+
for (const key of Object.keys(normalized)) {
|
|
79159
|
+
if (key.toLowerCase() === "authorization") delete normalized[key];
|
|
79160
|
+
}
|
|
79161
|
+
if (expectedAuthorization) {
|
|
79162
|
+
normalized[expectedAuthorization[0]] = expectedAuthorization[1];
|
|
79163
|
+
}
|
|
79164
|
+
}
|
|
79165
|
+
return normalized;
|
|
79166
|
+
}
|
|
79167
|
+
function normalizeAlanRuntimeEntry(actual, expected) {
|
|
79168
|
+
if (Array.isArray(actual)) {
|
|
79169
|
+
if (!Array.isArray(expected)) return actual;
|
|
79170
|
+
return actual.map((entry, index) => normalizeAlanRuntimeEntry(entry, expected[index]));
|
|
79171
|
+
}
|
|
79172
|
+
if (!actual || typeof actual !== "object" || Array.isArray(actual)) return actual;
|
|
79173
|
+
if (!expected || typeof expected !== "object" || Array.isArray(expected)) return actual;
|
|
79174
|
+
const expectedRecord = expected;
|
|
79175
|
+
return Object.fromEntries(
|
|
79176
|
+
Object.entries(actual).map(([key, value2]) => [
|
|
79177
|
+
key,
|
|
79178
|
+
key.toLowerCase() === "headers" || key.toLowerCase() === "http_headers" ? normalizeAlanSessionHeaders(value2, expectedRecord[key]) : normalizeAlanRuntimeEntry(value2, expectedRecord[key])
|
|
79179
|
+
])
|
|
79180
|
+
);
|
|
79181
|
+
}
|
|
79182
|
+
function matchesAlanManagedEntry(actual, expected) {
|
|
79183
|
+
return entriesMatch(actual, expected) || entriesMatch(normalizeAlanRuntimeEntry(actual, expected), expected);
|
|
79184
|
+
}
|
|
79185
|
+
function hasAlanManagedSignature(entry) {
|
|
79186
|
+
if (!entry || typeof entry !== "object") return false;
|
|
79187
|
+
for (const [key, value2] of Object.entries(entry)) {
|
|
79188
|
+
const lower = key.toLowerCase();
|
|
79189
|
+
if ((lower === "headers" || lower === "http_headers") && value2 && typeof value2 === "object") {
|
|
79190
|
+
const headerNames = Object.keys(value2);
|
|
79191
|
+
if (headerNames.some((name) => ALAN_SESSION_HEADER_NAMES.has(name.toLowerCase()))) {
|
|
79192
|
+
return true;
|
|
79193
|
+
}
|
|
79194
|
+
}
|
|
79195
|
+
if (value2 && typeof value2 === "object" && hasAlanManagedSignature(value2)) return true;
|
|
79196
|
+
}
|
|
79197
|
+
return false;
|
|
79198
|
+
}
|
|
79199
|
+
function expectedServerEntry(adapter, server) {
|
|
79200
|
+
return serverEntry(adapter, adapter.merge(null, [server]), server.name);
|
|
79201
|
+
}
|
|
79202
|
+
function isManagedServerList(entries) {
|
|
79203
|
+
return entries.length === 0 || typeof entries[0] !== "string";
|
|
79204
|
+
}
|
|
79205
|
+
function syncMcpToAllClis(servers, oldManagedServers = []) {
|
|
79206
|
+
const written = [];
|
|
79207
|
+
const failures = [];
|
|
79208
|
+
for (const adapter of ALL_ADAPTERS) {
|
|
79209
|
+
if (!(0, import_node_fs21.existsSync)(adapter.guardDir)) {
|
|
79210
|
+
log.debug("MCP Adapters", `Skipping ${adapter.cli} \u2014 guard dir not found`, {
|
|
79211
|
+
guardDir: adapter.guardDir
|
|
79212
|
+
});
|
|
79213
|
+
continue;
|
|
79214
|
+
}
|
|
79215
|
+
const releaseLock = acquireConfigLock2(adapter.configPath);
|
|
79216
|
+
try {
|
|
79217
|
+
const existing = readFileOr(adapter.configPath);
|
|
79218
|
+
const previousServers = isManagedServerList(oldManagedServers) ? oldManagedServers : [];
|
|
79219
|
+
const previousByName = new Map(previousServers.map((server) => [server.name, server]));
|
|
79220
|
+
if (existing) {
|
|
79221
|
+
for (const server of servers) {
|
|
79222
|
+
const currentEntry = serverEntry(adapter, existing, server.name);
|
|
79223
|
+
if (currentEntry === void 0) continue;
|
|
79224
|
+
const desiredEntry = expectedServerEntry(adapter, server);
|
|
79225
|
+
const previous = previousByName.get(server.name);
|
|
79226
|
+
const previousEntry = previous ? expectedServerEntry(adapter, previous) : void 0;
|
|
79227
|
+
if (!hasAlanManagedSignature(currentEntry) && !matchesAlanManagedEntry(currentEntry, desiredEntry) && (!previousEntry || !matchesAlanManagedEntry(currentEntry, previousEntry))) {
|
|
79228
|
+
throw new Error(`an unowned entry already uses the name ${server.name}`);
|
|
79229
|
+
}
|
|
79230
|
+
}
|
|
79231
|
+
}
|
|
79232
|
+
const currentNames = new Set(servers.map((s) => s.name));
|
|
79233
|
+
const toRemove = previousServers.filter((server) => !currentNames.has(server.name));
|
|
79234
|
+
let content;
|
|
79235
|
+
if (toRemove.length > 0) {
|
|
79236
|
+
const ownedNames = existing ? toRemove.filter(
|
|
79237
|
+
(server) => matchesAlanManagedEntry(
|
|
79238
|
+
serverEntry(adapter, existing, server.name),
|
|
79239
|
+
expectedServerEntry(adapter, server)
|
|
79240
|
+
)
|
|
79241
|
+
).map((server) => server.name) : [];
|
|
79242
|
+
const cleaned = adapter.remove(existing, ownedNames);
|
|
79243
|
+
content = adapter.merge(cleaned, servers);
|
|
79244
|
+
} else {
|
|
79245
|
+
content = adapter.merge(existing, servers);
|
|
79246
|
+
}
|
|
79247
|
+
writeFileSafe2(adapter.configPath, content);
|
|
79248
|
+
written.push(adapter.cli);
|
|
79249
|
+
log.info("MCP Adapters", `Synced to ${adapter.cli}`, {
|
|
79250
|
+
configPath: adapter.configPath,
|
|
79251
|
+
serverCount: servers.length
|
|
79252
|
+
});
|
|
79253
|
+
} catch (err) {
|
|
79254
|
+
const existing = readFileOr(adapter.configPath);
|
|
79255
|
+
const backupPath = err instanceof MalformedToolConfigError && existing !== null ? saveMalformedBackup2(adapter.configPath, existing) : void 0;
|
|
79256
|
+
const message = `${adapter.cli}: ${err instanceof Error ? err.message : String(err)}${backupPath ? `; review ${backupPath}` : ""}`;
|
|
79257
|
+
failures.push(message);
|
|
79258
|
+
log.warn("MCP Adapters", `Failed to sync to ${adapter.cli}`, {
|
|
79259
|
+
error: message
|
|
79260
|
+
});
|
|
79261
|
+
} finally {
|
|
79262
|
+
releaseLock();
|
|
79263
|
+
}
|
|
79264
|
+
}
|
|
79265
|
+
if (failures.length > 0) throw new Error(failures.join(" | "));
|
|
79266
|
+
return written;
|
|
79267
|
+
}
|
|
79268
|
+
function removeMcpFromAllClis(managedServers) {
|
|
79269
|
+
if (managedServers.length === 0) return;
|
|
79270
|
+
const ownershipKnown = isManagedServerList(managedServers);
|
|
79271
|
+
for (const adapter of ALL_ADAPTERS) {
|
|
79272
|
+
if (!(0, import_node_fs21.existsSync)(adapter.guardDir)) continue;
|
|
79273
|
+
const releaseLock = acquireConfigLock2(adapter.configPath);
|
|
79274
|
+
try {
|
|
79275
|
+
const existing = readFileOr(adapter.configPath);
|
|
79276
|
+
if (!existing) continue;
|
|
79277
|
+
const ownedNames = ownershipKnown ? managedServers.filter(
|
|
79278
|
+
(server) => matchesAlanManagedEntry(
|
|
79279
|
+
serverEntry(adapter, existing, server.name),
|
|
79280
|
+
expectedServerEntry(adapter, server)
|
|
79281
|
+
)
|
|
79282
|
+
).map((server) => server.name) : [];
|
|
79283
|
+
if (ownedNames.length === 0) continue;
|
|
79284
|
+
const content = adapter.remove(existing, ownedNames);
|
|
79285
|
+
writeFileSafe2(adapter.configPath, content);
|
|
79286
|
+
log.info("MCP Adapters", `Cleaned up ${adapter.cli}`, {
|
|
79287
|
+
removed: ownedNames
|
|
79288
|
+
});
|
|
79289
|
+
} catch (err) {
|
|
79290
|
+
const existing = readFileOr(adapter.configPath);
|
|
79291
|
+
const backupPath = err instanceof MalformedToolConfigError && existing !== null ? saveMalformedBackup2(adapter.configPath, existing) : void 0;
|
|
79292
|
+
log.warn("MCP Adapters", `Failed to clean up ${adapter.cli}`, {
|
|
79293
|
+
error: `${err instanceof Error ? err.message : String(err)}${backupPath ? `; review ${backupPath}` : ""}`
|
|
79294
|
+
});
|
|
79295
|
+
} finally {
|
|
79296
|
+
releaseLock();
|
|
79297
|
+
}
|
|
79298
|
+
}
|
|
79299
|
+
}
|
|
79300
|
+
var DEFAULT_ALAN_MCP_NAMES = ["alan"];
|
|
79301
|
+
function inspectAlanMcpPresence(expectedServers = [], candidateNames = DEFAULT_ALAN_MCP_NAMES) {
|
|
79302
|
+
const configuredClis = [];
|
|
79303
|
+
const inspectedClis = [];
|
|
79304
|
+
const serverNamesFound = /* @__PURE__ */ new Set();
|
|
79305
|
+
const names = /* @__PURE__ */ new Set([
|
|
79306
|
+
...candidateNames,
|
|
79307
|
+
...expectedServers.map((server) => server.name)
|
|
79308
|
+
]);
|
|
79309
|
+
const expectedByName = new Map(expectedServers.map((server) => [server.name, server]));
|
|
79310
|
+
for (const adapter of ALL_ADAPTERS) {
|
|
79311
|
+
if (!(0, import_node_fs21.existsSync)(adapter.guardDir)) continue;
|
|
79312
|
+
inspectedClis.push(adapter.cli);
|
|
79313
|
+
const existing = readFileOr(adapter.configPath);
|
|
79314
|
+
if (!existing) continue;
|
|
79315
|
+
let foundOnCli = false;
|
|
79316
|
+
for (const name of names) {
|
|
79317
|
+
let entry;
|
|
79318
|
+
try {
|
|
79319
|
+
entry = serverEntry(adapter, existing, name);
|
|
79320
|
+
} catch {
|
|
79321
|
+
continue;
|
|
79322
|
+
}
|
|
79323
|
+
if (entry === void 0) continue;
|
|
79324
|
+
const expected = expectedByName.get(name);
|
|
79325
|
+
const matchesExpected = expected ? matchesAlanManagedEntry(entry, expectedServerEntry(adapter, expected)) : false;
|
|
79326
|
+
if (!hasAlanManagedSignature(entry) && !matchesExpected) continue;
|
|
79327
|
+
foundOnCli = true;
|
|
79328
|
+
serverNamesFound.add(name);
|
|
79329
|
+
}
|
|
79330
|
+
if (foundOnCli) configuredClis.push(adapter.cli);
|
|
79331
|
+
}
|
|
79332
|
+
return {
|
|
79333
|
+
configured: configuredClis.length > 0,
|
|
79334
|
+
configuredClis,
|
|
79335
|
+
inspectedClis,
|
|
79336
|
+
serverNamesFound: [...serverNamesFound]
|
|
79337
|
+
};
|
|
79338
|
+
}
|
|
79339
|
+
function toManagedMcpServers(mcpServers) {
|
|
79340
|
+
const result = [];
|
|
79341
|
+
for (const [name, config2] of Object.entries(mcpServers)) {
|
|
79342
|
+
if (!config2 || typeof config2 !== "object") continue;
|
|
79343
|
+
const c = config2;
|
|
79344
|
+
if (c.command && typeof c.command === "string") {
|
|
79345
|
+
result.push({
|
|
79346
|
+
name,
|
|
79347
|
+
kind: "stdio",
|
|
79348
|
+
command: c.command,
|
|
79349
|
+
args: c.args ?? void 0,
|
|
79350
|
+
env: c.env ?? void 0
|
|
79351
|
+
});
|
|
79352
|
+
} else {
|
|
79353
|
+
result.push({
|
|
79354
|
+
name,
|
|
79355
|
+
kind: "http",
|
|
79356
|
+
url: c.url ?? "",
|
|
79357
|
+
headers: c.headers ?? {}
|
|
79358
|
+
});
|
|
79359
|
+
}
|
|
79360
|
+
}
|
|
79361
|
+
return result;
|
|
79362
|
+
}
|
|
79363
|
+
|
|
79364
|
+
// src/mcp-sync.ts
|
|
79365
|
+
var McpSyncService = class {
|
|
79366
|
+
constructor(options) {
|
|
79367
|
+
this.options = options;
|
|
79368
|
+
this.managedServerNames = [...options.managedServerNames ?? ["alan"]];
|
|
79369
|
+
this.legacyServerNames = [...options.legacyServerNames ?? []];
|
|
79370
|
+
}
|
|
79371
|
+
managedServerNames;
|
|
79372
|
+
legacyServerNames;
|
|
79373
|
+
getStatus() {
|
|
79374
|
+
const presence = inspectAlanMcpPresence([], this.managedServerNames);
|
|
79375
|
+
return {
|
|
79376
|
+
configured: presence.configured,
|
|
79377
|
+
lastSyncAt: null,
|
|
79378
|
+
configuredClis: presence.configuredClis,
|
|
79379
|
+
inspectedClis: presence.inspectedClis,
|
|
79380
|
+
managedServerNames: [...this.managedServerNames],
|
|
79381
|
+
mcpAutoInjectEnabled: this.options.enabled
|
|
79382
|
+
};
|
|
79383
|
+
}
|
|
79384
|
+
async sync(apiUrl, token, onProgress) {
|
|
79385
|
+
if (!this.options.enabled) return { errors: [], mcpServersUpdated: false };
|
|
79386
|
+
onProgress?.({ phase: "fetching" });
|
|
79387
|
+
let config2;
|
|
79388
|
+
try {
|
|
79389
|
+
const response = await fetch(`${apiUrl}/public/ade/config`, {
|
|
79390
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
79391
|
+
signal: AbortSignal.timeout(15e3)
|
|
79392
|
+
});
|
|
79393
|
+
if (!response.ok) throw new Error(`HTTP ${response.status} ${response.statusText}`);
|
|
79394
|
+
config2 = await response.json();
|
|
79395
|
+
} catch (error61) {
|
|
79396
|
+
return {
|
|
79397
|
+
errors: [`Fetch failed: ${error61 instanceof Error ? error61.message : String(error61)}`],
|
|
79398
|
+
mcpServersUpdated: false
|
|
79399
|
+
};
|
|
79400
|
+
}
|
|
79401
|
+
return this.syncServers(config2.mcpServers, apiUrl, onProgress);
|
|
79402
|
+
}
|
|
79403
|
+
syncServers(mcpServers, apiUrl, onProgress) {
|
|
79404
|
+
if (!this.options.enabled) return { errors: [], mcpServersUpdated: false };
|
|
79405
|
+
onProgress?.({ phase: "mcp" });
|
|
79406
|
+
try {
|
|
79407
|
+
const rawServers = this.options.transformMcpServers ? this.options.transformMcpServers(mcpServers, apiUrl) : mcpServers;
|
|
79408
|
+
const managed = toManagedMcpServers(rawServers);
|
|
79409
|
+
const previousNames = [...this.managedServerNames, ...this.legacyServerNames];
|
|
79410
|
+
const written = syncMcpToAllClis(managed, previousNames);
|
|
79411
|
+
return { errors: [], mcpServersUpdated: written.length > 0 };
|
|
79412
|
+
} catch (error61) {
|
|
79413
|
+
return {
|
|
79414
|
+
errors: [`MCP sync: ${error61 instanceof Error ? error61.message : String(error61)}`],
|
|
79415
|
+
mcpServersUpdated: false
|
|
79416
|
+
};
|
|
79417
|
+
}
|
|
79418
|
+
}
|
|
79419
|
+
cleanup(previousServers = []) {
|
|
79420
|
+
removeMcpFromAllClis(previousServers);
|
|
79421
|
+
}
|
|
79422
|
+
};
|
|
79423
|
+
|
|
79424
|
+
// src/native-hook-config.ts
|
|
79425
|
+
var import_node_fs22 = require("fs");
|
|
79426
|
+
var import_node_os13 = require("os");
|
|
79427
|
+
var import_node_path24 = require("path");
|
|
79428
|
+
var CLAUDE_HOOK_EVENTS = [
|
|
79429
|
+
"SessionStart",
|
|
79430
|
+
"UserPromptSubmit",
|
|
79431
|
+
"PostToolUse",
|
|
79432
|
+
"Stop",
|
|
79433
|
+
"SubagentStart",
|
|
79434
|
+
"SubagentStop",
|
|
79435
|
+
"SessionEnd"
|
|
79436
|
+
];
|
|
79437
|
+
var ALAN_HOOK_MARKER = "# alan-native-session-hook";
|
|
79438
|
+
function shellQuote(value2) {
|
|
79439
|
+
return `'${value2.replaceAll("'", `'\\''`)}'`;
|
|
79440
|
+
}
|
|
79441
|
+
function powershellQuote(value2) {
|
|
79442
|
+
return `'${value2.replaceAll("'", "''")}'`;
|
|
79443
|
+
}
|
|
79444
|
+
function nativeHookCommand(input2) {
|
|
79445
|
+
const args = [
|
|
79446
|
+
input2.cliEntry,
|
|
79447
|
+
"hook",
|
|
79448
|
+
"--config-path",
|
|
79449
|
+
input2.configPath,
|
|
79450
|
+
"--provider",
|
|
79451
|
+
input2.provider,
|
|
79452
|
+
"--event",
|
|
79453
|
+
input2.event,
|
|
79454
|
+
ALAN_HOOK_MARKER
|
|
79455
|
+
];
|
|
79456
|
+
const environment = Object.entries(input2.environment ?? {});
|
|
79457
|
+
for (const [key] of environment) {
|
|
79458
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
|
|
79459
|
+
throw new Error(`Invalid native hook environment variable: ${key}`);
|
|
79460
|
+
}
|
|
79461
|
+
}
|
|
79462
|
+
if (environment.length === 0) {
|
|
79463
|
+
return {
|
|
79464
|
+
type: "command",
|
|
79465
|
+
command: input2.nodeExecutable,
|
|
79466
|
+
args,
|
|
79467
|
+
timeout: 5
|
|
79468
|
+
};
|
|
79469
|
+
}
|
|
79470
|
+
if ((input2.platform ?? (0, import_node_os13.platform)()) === "win32") {
|
|
79471
|
+
const environmentPrefix2 = environment.map(([key, value2]) => `$env:${key}=${powershellQuote(value2)}`).join("; ");
|
|
79472
|
+
const command2 = [input2.nodeExecutable, ...args].map(powershellQuote).join(" ");
|
|
79473
|
+
return {
|
|
79474
|
+
type: "command",
|
|
79475
|
+
shell: "powershell",
|
|
79476
|
+
command: `${environmentPrefix2}; & ${command2}`,
|
|
79477
|
+
timeout: 5
|
|
79478
|
+
};
|
|
79479
|
+
}
|
|
79480
|
+
const environmentPrefix = environment.map(([key, value2]) => `${key}=${shellQuote(value2)}`).join(" ");
|
|
79481
|
+
const command = [input2.nodeExecutable, ...args].map(shellQuote).join(" ");
|
|
79482
|
+
return {
|
|
79483
|
+
type: "command",
|
|
79484
|
+
shell: "bash",
|
|
79485
|
+
command: `${environmentPrefix} ${command}`,
|
|
79486
|
+
timeout: 5
|
|
79487
|
+
};
|
|
79488
|
+
}
|
|
79489
|
+
function readSettings(path2) {
|
|
79490
|
+
if (!(0, import_node_fs22.existsSync)(path2)) return {};
|
|
79491
|
+
try {
|
|
79492
|
+
const value2 = JSON.parse((0, import_node_fs22.readFileSync)(path2, "utf8"));
|
|
79493
|
+
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) {
|
|
79494
|
+
throw new Error("settings root must be an object");
|
|
79495
|
+
}
|
|
79496
|
+
return value2;
|
|
79497
|
+
} catch (error61) {
|
|
79498
|
+
const backup = `${path2}.malformed-${Date.now()}.bak`;
|
|
79499
|
+
(0, import_node_fs22.copyFileSync)(path2, backup);
|
|
79500
|
+
throw new Error(
|
|
79501
|
+
`Claude settings were malformed and moved to ${backup}: ${error61 instanceof Error ? error61.message : String(error61)}`
|
|
79502
|
+
);
|
|
79503
|
+
}
|
|
79504
|
+
}
|
|
79505
|
+
function withoutAlanHookGroups(value2) {
|
|
79506
|
+
if (!Array.isArray(value2)) return [];
|
|
79507
|
+
return value2.filter((group) => !JSON.stringify(group).includes(ALAN_HOOK_MARKER));
|
|
79508
|
+
}
|
|
79509
|
+
function writeSettings(path2, settings) {
|
|
79510
|
+
(0, import_node_fs22.mkdirSync)((0, import_node_path24.dirname)(path2), { recursive: true, mode: 448 });
|
|
79511
|
+
const temporary = `${path2}.${process.pid}.tmp`;
|
|
79512
|
+
(0, import_node_fs22.writeFileSync)(temporary, `${JSON.stringify(settings, null, 2)}
|
|
79513
|
+
`, { mode: 384 });
|
|
79514
|
+
(0, import_node_fs22.renameSync)(temporary, path2);
|
|
79515
|
+
}
|
|
79516
|
+
function reconcileClaudeNativeHooks(input2) {
|
|
79517
|
+
const homeDir = input2?.homeDir ?? (0, import_node_os13.homedir)();
|
|
79518
|
+
const settingsPath = (0, import_node_path24.join)(homeDir, ".claude", "settings.json");
|
|
79519
|
+
const nodeExecutable = input2?.nodeExecutable ?? process.execPath;
|
|
79520
|
+
const cliEntry = input2?.cliEntry ?? process.argv[1];
|
|
79521
|
+
const configPath = input2?.configPath ?? process.env.ALAN_AGENT_CONFIG_PATH;
|
|
79522
|
+
if (!cliEntry) throw new Error("Cannot resolve the alan-agent CLI entrypoint");
|
|
79523
|
+
if (!configPath?.trim()) {
|
|
79524
|
+
throw new Error("Cannot install Claude hooks without an exact Alan agent config path");
|
|
79525
|
+
}
|
|
79526
|
+
const settings = readSettings(settingsPath);
|
|
79527
|
+
const currentHooks = settings.hooks && typeof settings.hooks === "object" && !Array.isArray(settings.hooks) ? settings.hooks : {};
|
|
79528
|
+
const hooks = { ...currentHooks };
|
|
79529
|
+
for (const event of CLAUDE_HOOK_EVENTS) {
|
|
79530
|
+
hooks[event] = [
|
|
79531
|
+
...withoutAlanHookGroups(currentHooks[event]),
|
|
79532
|
+
{
|
|
79533
|
+
hooks: [
|
|
79534
|
+
nativeHookCommand({
|
|
79535
|
+
configPath,
|
|
79536
|
+
nodeExecutable,
|
|
79537
|
+
cliEntry,
|
|
79538
|
+
environment: input2?.environment,
|
|
79539
|
+
platform: input2?.platform,
|
|
79540
|
+
provider: "claude_cli",
|
|
79541
|
+
event
|
|
79542
|
+
})
|
|
79543
|
+
]
|
|
79544
|
+
}
|
|
79545
|
+
];
|
|
79546
|
+
}
|
|
79547
|
+
const next = { ...settings, hooks };
|
|
79548
|
+
const previous = (0, import_node_fs22.existsSync)(settingsPath) ? (0, import_node_fs22.readFileSync)(settingsPath, "utf8") : null;
|
|
79549
|
+
const serialized = `${JSON.stringify(next, null, 2)}
|
|
79550
|
+
`;
|
|
79551
|
+
if (previous === serialized) return false;
|
|
79552
|
+
writeSettings(settingsPath, next);
|
|
79553
|
+
return true;
|
|
79554
|
+
}
|
|
79555
|
+
function removeClaudeNativeHooks(homeDir = (0, import_node_os13.homedir)()) {
|
|
79556
|
+
const settingsPath = (0, import_node_path24.join)(homeDir, ".claude", "settings.json");
|
|
79557
|
+
if (!(0, import_node_fs22.existsSync)(settingsPath)) return false;
|
|
79558
|
+
const settings = readSettings(settingsPath);
|
|
79559
|
+
if (!settings.hooks || typeof settings.hooks !== "object" || Array.isArray(settings.hooks)) {
|
|
79560
|
+
return false;
|
|
79561
|
+
}
|
|
79562
|
+
const hooks = {};
|
|
79563
|
+
let removed = false;
|
|
79564
|
+
for (const [event, groups] of Object.entries(settings.hooks)) {
|
|
79565
|
+
const retained = withoutAlanHookGroups(groups);
|
|
79566
|
+
if (Array.isArray(groups) && retained.length !== groups.length) removed = true;
|
|
79567
|
+
if (retained.length > 0) hooks[event] = retained;
|
|
79568
|
+
}
|
|
79569
|
+
if (!removed) return false;
|
|
79570
|
+
writeSettings(settingsPath, { ...settings, hooks });
|
|
79571
|
+
return true;
|
|
79572
|
+
}
|
|
79573
|
+
|
|
79574
|
+
// src/integration-cli.ts
|
|
79575
|
+
function alanMcpServers(apiUrl) {
|
|
79576
|
+
return {
|
|
79577
|
+
alan: {
|
|
79578
|
+
type: "http",
|
|
79579
|
+
url: new URL("/mcp", apiUrl).toString()
|
|
79580
|
+
}
|
|
79581
|
+
};
|
|
79582
|
+
}
|
|
79583
|
+
function runMcpIntegrationCommand(args, options = {}) {
|
|
79584
|
+
bindConfigPath(args);
|
|
79585
|
+
const action = args[0] ?? "status";
|
|
79586
|
+
const config2 = readConfig();
|
|
79587
|
+
const endpoints = resolveEndpoints(args.slice(1), config2);
|
|
79588
|
+
const service = new McpSyncService({ enabled: true });
|
|
79589
|
+
if (action === "status") {
|
|
79590
|
+
process.stdout.write(`${JSON.stringify(service.getStatus(), null, 2)}
|
|
79591
|
+
`);
|
|
79592
|
+
return;
|
|
79593
|
+
}
|
|
79594
|
+
if (action === "reconcile") {
|
|
79595
|
+
if (!config2.runtimeId || !config2.runtimeToken) {
|
|
79596
|
+
throw new Error(
|
|
79597
|
+
"Alan is not configured. Run `alan-agent login` or `alan-agent setup` first."
|
|
79598
|
+
);
|
|
79599
|
+
}
|
|
79600
|
+
const result = service.syncServers(alanMcpServers(endpoints.apiUrl), endpoints.apiUrl);
|
|
79601
|
+
if (result.errors.length > 0) throw new Error(result.errors.join("; "));
|
|
79602
|
+
const hooksUpdated = resolveCliExecutable("claude", getDaemonCliEnvironment()) ? reconcileClaudeNativeHooks({
|
|
79603
|
+
configPath: getConfigPath(),
|
|
79604
|
+
nodeExecutable: options.nativeHookRuntime?.executable,
|
|
79605
|
+
cliEntry: options.nativeHookRuntime?.cliEntry,
|
|
79606
|
+
environment: options.nativeHookRuntime?.environment
|
|
79607
|
+
}) : false;
|
|
79608
|
+
process.stdout.write(
|
|
79609
|
+
`${result.mcpServersUpdated || hooksUpdated ? "Alan CLI integrations updated" : "Alan CLI integrations already current"}.
|
|
79610
|
+
`
|
|
79611
|
+
);
|
|
79612
|
+
return;
|
|
79613
|
+
}
|
|
79614
|
+
if (action === "uninstall") {
|
|
79615
|
+
service.cleanup(toManagedMcpServers(alanMcpServers(endpoints.apiUrl)));
|
|
79616
|
+
removeClaudeNativeHooks();
|
|
79617
|
+
process.stdout.write("Removed Alan-owned MCP configuration.\n");
|
|
79618
|
+
return;
|
|
79619
|
+
}
|
|
79620
|
+
throw new Error(`Unknown integrations action: ${action}`);
|
|
79621
|
+
}
|
|
79622
|
+
|
|
77976
79623
|
// src/lifecycle-logger.ts
|
|
77977
79624
|
var AgentLifecycleLogger = class _AgentLifecycleLogger {
|
|
77978
79625
|
context;
|
|
@@ -78045,6 +79692,293 @@ function omitUndefined(record2) {
|
|
|
78045
79692
|
return Object.fromEntries(Object.entries(record2).filter(([, value2]) => value2 !== void 0));
|
|
78046
79693
|
}
|
|
78047
79694
|
|
|
79695
|
+
// src/native-hook-command.ts
|
|
79696
|
+
var import_node_crypto19 = require("crypto");
|
|
79697
|
+
var import_node_fs23 = require("fs");
|
|
79698
|
+
var import_node_path25 = require("path");
|
|
79699
|
+
|
|
79700
|
+
// src/native-session.ts
|
|
79701
|
+
var NATIVE_SESSION_PROVIDER_CAPABILITIES = [
|
|
79702
|
+
{
|
|
79703
|
+
providerKind: "claude_cli",
|
|
79704
|
+
provider: "claude",
|
|
79705
|
+
command: "claude",
|
|
79706
|
+
capabilities: {
|
|
79707
|
+
mcp: true,
|
|
79708
|
+
lifecycleHooks: true,
|
|
79709
|
+
transcriptDelta: true,
|
|
79710
|
+
subagents: true,
|
|
79711
|
+
sessionEnd: true,
|
|
79712
|
+
state: "supported"
|
|
79713
|
+
},
|
|
79714
|
+
supportedHookEvents: [
|
|
79715
|
+
"SessionStart",
|
|
79716
|
+
"UserPromptSubmit",
|
|
79717
|
+
"PostToolUse",
|
|
79718
|
+
"Stop",
|
|
79719
|
+
"SubagentStart",
|
|
79720
|
+
"SubagentStop",
|
|
79721
|
+
"SessionEnd"
|
|
79722
|
+
]
|
|
79723
|
+
},
|
|
79724
|
+
{
|
|
79725
|
+
providerKind: "codex_app_server",
|
|
79726
|
+
provider: "codex",
|
|
79727
|
+
command: "codex",
|
|
79728
|
+
capabilities: {
|
|
79729
|
+
mcp: true,
|
|
79730
|
+
lifecycleHooks: false,
|
|
79731
|
+
transcriptDelta: false,
|
|
79732
|
+
subagents: false,
|
|
79733
|
+
sessionEnd: false,
|
|
79734
|
+
state: "degraded",
|
|
79735
|
+
reason: "Codex exposes a turn-complete notification, not full lifecycle hooks."
|
|
79736
|
+
},
|
|
79737
|
+
supportedHookEvents: ["agent-turn-complete"]
|
|
79738
|
+
},
|
|
79739
|
+
{
|
|
79740
|
+
providerKind: "copilot_cli",
|
|
79741
|
+
provider: "copilot",
|
|
79742
|
+
command: "copilot",
|
|
79743
|
+
capabilities: unsupportedMcpOnlyCapability(),
|
|
79744
|
+
supportedHookEvents: []
|
|
79745
|
+
},
|
|
79746
|
+
{
|
|
79747
|
+
providerKind: "cursor_agent_cli",
|
|
79748
|
+
provider: "cursor",
|
|
79749
|
+
command: "cursor-agent",
|
|
79750
|
+
capabilities: unsupportedMcpOnlyCapability(),
|
|
79751
|
+
supportedHookEvents: []
|
|
79752
|
+
},
|
|
79753
|
+
{
|
|
79754
|
+
providerKind: "antigravity_cli",
|
|
79755
|
+
provider: "antigravity",
|
|
79756
|
+
command: "agy",
|
|
79757
|
+
capabilities: unsupportedMcpOnlyCapability(),
|
|
79758
|
+
supportedHookEvents: []
|
|
79759
|
+
},
|
|
79760
|
+
{
|
|
79761
|
+
providerKind: "kimi_cli",
|
|
79762
|
+
provider: "kimi",
|
|
79763
|
+
command: "kimi-cli",
|
|
79764
|
+
capabilities: unsupportedMcpOnlyCapability(),
|
|
79765
|
+
supportedHookEvents: []
|
|
79766
|
+
},
|
|
79767
|
+
{
|
|
79768
|
+
providerKind: "grok_cli",
|
|
79769
|
+
provider: "grok",
|
|
79770
|
+
command: "grok",
|
|
79771
|
+
capabilities: unsupportedMcpOnlyCapability(),
|
|
79772
|
+
supportedHookEvents: []
|
|
79773
|
+
},
|
|
79774
|
+
{
|
|
79775
|
+
providerKind: "opencode_cli",
|
|
79776
|
+
provider: "opencode",
|
|
79777
|
+
command: "opencode",
|
|
79778
|
+
capabilities: unsupportedMcpOnlyCapability(),
|
|
79779
|
+
supportedHookEvents: []
|
|
79780
|
+
},
|
|
79781
|
+
{
|
|
79782
|
+
providerKind: "droid_cli",
|
|
79783
|
+
provider: "droid",
|
|
79784
|
+
command: "droid",
|
|
79785
|
+
capabilities: unsupportedMcpOnlyCapability(),
|
|
79786
|
+
supportedHookEvents: []
|
|
79787
|
+
},
|
|
79788
|
+
{
|
|
79789
|
+
providerKind: "supatest_cli",
|
|
79790
|
+
provider: "supatest",
|
|
79791
|
+
command: "supatest",
|
|
79792
|
+
capabilities: unsupportedMcpOnlyCapability(),
|
|
79793
|
+
supportedHookEvents: []
|
|
79794
|
+
}
|
|
79795
|
+
];
|
|
79796
|
+
function unsupportedMcpOnlyCapability() {
|
|
79797
|
+
return {
|
|
79798
|
+
mcp: true,
|
|
79799
|
+
lifecycleHooks: false,
|
|
79800
|
+
transcriptDelta: false,
|
|
79801
|
+
subagents: false,
|
|
79802
|
+
sessionEnd: false,
|
|
79803
|
+
state: "unsupported",
|
|
79804
|
+
reason: "No native hook adapter is installed for this provider."
|
|
79805
|
+
};
|
|
79806
|
+
}
|
|
79807
|
+
var CAPABILITIES_BY_PROVIDER_KIND = new Map(
|
|
79808
|
+
NATIVE_SESSION_PROVIDER_CAPABILITIES.map((capability) => [capability.providerKind, capability])
|
|
79809
|
+
);
|
|
79810
|
+
function isNativeSessionProviderKind(value2) {
|
|
79811
|
+
return CAPABILITIES_BY_PROVIDER_KIND.has(value2);
|
|
79812
|
+
}
|
|
79813
|
+
|
|
79814
|
+
// src/native-hook-command.ts
|
|
79815
|
+
var MAX_NATIVE_HOOK_STDIN_BYTES = 1024 * 1024;
|
|
79816
|
+
function argumentValue(args, name) {
|
|
79817
|
+
const index = args.indexOf(name);
|
|
79818
|
+
if (index >= 0) return args[index + 1];
|
|
79819
|
+
const prefix = `${name}=`;
|
|
79820
|
+
return args.find((arg) => arg.startsWith(prefix))?.slice(prefix.length);
|
|
79821
|
+
}
|
|
79822
|
+
function stringField4(payload, ...names) {
|
|
79823
|
+
for (const name of names) {
|
|
79824
|
+
const value2 = payload[name];
|
|
79825
|
+
if (typeof value2 === "string" && value2.trim()) return value2.trim();
|
|
79826
|
+
}
|
|
79827
|
+
return void 0;
|
|
79828
|
+
}
|
|
79829
|
+
function nonNegativeIntegerField(payload, ...names) {
|
|
79830
|
+
for (const name of names) {
|
|
79831
|
+
const value2 = payload[name];
|
|
79832
|
+
if (typeof value2 === "number" && Number.isSafeInteger(value2) && value2 >= 0) return value2;
|
|
79833
|
+
}
|
|
79834
|
+
return void 0;
|
|
79835
|
+
}
|
|
79836
|
+
function parseNativeHookEnvelope(input2) {
|
|
79837
|
+
if (!isNativeSessionProviderKind(input2.provider)) {
|
|
79838
|
+
throw new Error(`unsupported native session provider: ${input2.provider}`);
|
|
79839
|
+
}
|
|
79840
|
+
const rawHookEvent = input2.hookEvent ?? stringField4(input2.payload, "hook_event_name", "hookEvent", "type") ?? "notification";
|
|
79841
|
+
const cwd = stringField4(input2.payload, "cwd", "working_directory", "workingDirectory");
|
|
79842
|
+
const transcriptPath = stringField4(input2.payload, "transcript_path", "transcriptPath");
|
|
79843
|
+
const capability = NATIVE_SESSION_PROVIDER_CAPABILITIES.find(
|
|
79844
|
+
(candidate) => candidate.providerKind === input2.provider
|
|
79845
|
+
);
|
|
79846
|
+
if (!capability) throw new Error(`unsupported native session provider: ${input2.provider}`);
|
|
79847
|
+
const externalSessionId = stringField4(
|
|
79848
|
+
input2.payload,
|
|
79849
|
+
"session_id",
|
|
79850
|
+
"sessionId",
|
|
79851
|
+
"conversation_id",
|
|
79852
|
+
"conversationId",
|
|
79853
|
+
"thread_id",
|
|
79854
|
+
"threadId"
|
|
79855
|
+
);
|
|
79856
|
+
if (!externalSessionId) throw new Error("hook payload is missing a provider session id");
|
|
79857
|
+
const cursor = stringField4(input2.payload, "source_cursor", "sourceCursor") ?? nonNegativeIntegerField(input2.payload, "byte_offset", "byteOffset")?.toString() ?? nonNegativeIntegerField(input2.payload, "sequence", "seq")?.toString();
|
|
79858
|
+
return {
|
|
79859
|
+
schemaVersion: 1,
|
|
79860
|
+
eventId: (0, import_node_crypto19.randomUUID)(),
|
|
79861
|
+
provider: capability.provider,
|
|
79862
|
+
eventType: normalizeHookEvent(rawHookEvent),
|
|
79863
|
+
observedAt: (input2.now ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
79864
|
+
externalSessionId,
|
|
79865
|
+
parentExternalSessionId: stringField4(input2.payload, "parent_session_id", "parentSessionId"),
|
|
79866
|
+
...cwd ? { cwd: (0, import_node_path25.resolve)(cwd) } : {},
|
|
79867
|
+
...transcriptPath ? { transcriptPath: (0, import_node_path25.resolve)(transcriptPath) } : {},
|
|
79868
|
+
...cursor ? { sourceCursor: cursor } : {},
|
|
79869
|
+
providerPayload: input2.payload
|
|
79870
|
+
};
|
|
79871
|
+
}
|
|
79872
|
+
function normalizeHookEvent(raw) {
|
|
79873
|
+
switch (raw.toLowerCase().replaceAll("-", "_").replaceAll(" ", "_")) {
|
|
79874
|
+
case "sessionstart":
|
|
79875
|
+
case "session_start":
|
|
79876
|
+
return "session_start";
|
|
79877
|
+
case "userpromptsubmit":
|
|
79878
|
+
case "user_prompt":
|
|
79879
|
+
return "user_prompt";
|
|
79880
|
+
case "subagentstart":
|
|
79881
|
+
case "subagent_start":
|
|
79882
|
+
return "subagent_start";
|
|
79883
|
+
case "subagentstop":
|
|
79884
|
+
case "subagent_end":
|
|
79885
|
+
return "subagent_end";
|
|
79886
|
+
case "sessionend":
|
|
79887
|
+
case "session_end":
|
|
79888
|
+
return "session_end";
|
|
79889
|
+
case "stop":
|
|
79890
|
+
case "agent_turn_complete":
|
|
79891
|
+
return "stop";
|
|
79892
|
+
default:
|
|
79893
|
+
return "transcript_update";
|
|
79894
|
+
}
|
|
79895
|
+
}
|
|
79896
|
+
function readBoundedHookPayload(fd = 0, maxBytes = MAX_NATIVE_HOOK_STDIN_BYTES) {
|
|
79897
|
+
const chunks = [];
|
|
79898
|
+
let totalBytes = 0;
|
|
79899
|
+
while (true) {
|
|
79900
|
+
const chunk = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1 - totalBytes));
|
|
79901
|
+
const bytesRead = (0, import_node_fs23.readSync)(fd, chunk, 0, chunk.byteLength, null);
|
|
79902
|
+
if (bytesRead === 0) break;
|
|
79903
|
+
totalBytes += bytesRead;
|
|
79904
|
+
if (totalBytes > maxBytes) throw new Error(`hook input exceeds ${maxBytes} bytes`);
|
|
79905
|
+
chunks.push(chunk.subarray(0, bytesRead));
|
|
79906
|
+
}
|
|
79907
|
+
const body = Buffer.concat(chunks, totalBytes);
|
|
79908
|
+
if (body.byteLength === 0) return {};
|
|
79909
|
+
const parsed = JSON.parse(body.toString("utf8"));
|
|
79910
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
79911
|
+
throw new Error("hook input must be a JSON object");
|
|
79912
|
+
}
|
|
79913
|
+
return parsed;
|
|
79914
|
+
}
|
|
79915
|
+
function enqueueNativeHookFromConfig(input2) {
|
|
79916
|
+
if (process.env.ALAN_MANAGED_RUN === "1" || process.env.ALAN_RUN_ID?.trim()) return false;
|
|
79917
|
+
bindConfigPath(input2.args);
|
|
79918
|
+
const config2 = input2.config ?? readConfig();
|
|
79919
|
+
const provider = argumentValue(input2.args, "--provider");
|
|
79920
|
+
if (!provider) throw new Error("hook requires --provider");
|
|
79921
|
+
if (!config2.eventSpoolKey) return false;
|
|
79922
|
+
const envelope = parseNativeHookEnvelope({
|
|
79923
|
+
provider,
|
|
79924
|
+
hookEvent: argumentValue(input2.args, "--event"),
|
|
79925
|
+
payload: input2.payload
|
|
79926
|
+
});
|
|
79927
|
+
new EncryptedNativeHookInbox(nativeHookInboxPath(getConfigPath()), config2.eventSpoolKey).enqueue(
|
|
79928
|
+
envelope
|
|
79929
|
+
);
|
|
79930
|
+
return true;
|
|
79931
|
+
}
|
|
79932
|
+
function runNativeHookCommand(args) {
|
|
79933
|
+
try {
|
|
79934
|
+
const payload = readBoundedHookPayload();
|
|
79935
|
+
const enqueued = enqueueNativeHookFromConfig({ args, payload });
|
|
79936
|
+
const hookEvent = argumentValue(args, "--event")?.toLowerCase();
|
|
79937
|
+
if (enqueued && (hookEvent === "userpromptsubmit" || hookEvent === "user_prompt")) {
|
|
79938
|
+
const providerKind = argumentValue(args, "--provider");
|
|
79939
|
+
const externalSessionId = stringField4(payload, "session_id", "sessionId");
|
|
79940
|
+
const capability = providerKind ? NATIVE_SESSION_PROVIDER_CAPABILITIES.find(
|
|
79941
|
+
(candidate) => candidate.providerKind === providerKind
|
|
79942
|
+
) : void 0;
|
|
79943
|
+
if (capability && externalSessionId) {
|
|
79944
|
+
process.stdout.write(
|
|
79945
|
+
`${JSON.stringify({
|
|
79946
|
+
hookSpecificOutput: {
|
|
79947
|
+
hookEventName: "UserPromptSubmit",
|
|
79948
|
+
additionalContext: `First call get_native_session_link_status with provider=${capability.provider} and externalSessionId=${externalSessionId}. If status is linked, continue without asking or linking again. If status is syncing, wait briefly and retry the status check. Only when status is pending, ask the user one question: work on an Alan task or continue as a direct session. Resolve a named task with find_records, then call link_native_session. Do not guess a task and do not change task status.`
|
|
79949
|
+
}
|
|
79950
|
+
})}
|
|
79951
|
+
`
|
|
79952
|
+
);
|
|
79953
|
+
}
|
|
79954
|
+
}
|
|
79955
|
+
} catch (error61) {
|
|
79956
|
+
if (process.env.ALAN_HOOK_DEBUG === "1") {
|
|
79957
|
+
process.stderr.write(
|
|
79958
|
+
`[alan-agent] native hook skipped: ${error61 instanceof Error ? error61.message : String(error61)}
|
|
79959
|
+
`
|
|
79960
|
+
);
|
|
79961
|
+
}
|
|
79962
|
+
}
|
|
79963
|
+
}
|
|
79964
|
+
function collectNativeIntegrationStatus(args = []) {
|
|
79965
|
+
bindConfigPath(args);
|
|
79966
|
+
const config2 = readConfig();
|
|
79967
|
+
const env = getDaemonCliEnvironment();
|
|
79968
|
+
return NATIVE_SESSION_PROVIDER_CAPABILITIES.map((capability) => ({
|
|
79969
|
+
provider: capability.providerKind,
|
|
79970
|
+
command: capability.command,
|
|
79971
|
+
installed: resolveCliExecutable(capability.command, env) !== null,
|
|
79972
|
+
hookCoverage: capability.capabilities.lifecycleHooks ? "lifecycle" : capability.capabilities.state === "degraded" ? "notification" : "none",
|
|
79973
|
+
transcriptStreaming: capability.capabilities.transcriptDelta,
|
|
79974
|
+
inboxReady: Boolean(config2.eventSpoolKey)
|
|
79975
|
+
}));
|
|
79976
|
+
}
|
|
79977
|
+
function printNativeIntegrationStatus(args) {
|
|
79978
|
+
process.stdout.write(`${JSON.stringify(collectNativeIntegrationStatus(args), null, 2)}
|
|
79979
|
+
`);
|
|
79980
|
+
}
|
|
79981
|
+
|
|
78048
79982
|
// src/resume-fallback-env.ts
|
|
78049
79983
|
var RESUME_FALLBACK_CONTEXT_ENV = "ALAN_RESUME_FALLBACK_CONTEXT_B64";
|
|
78050
79984
|
function decodeResumeFallbackContext(encoded) {
|
|
@@ -78058,7 +79992,7 @@ function decodeResumeFallbackContext(encoded) {
|
|
|
78058
79992
|
}
|
|
78059
79993
|
|
|
78060
79994
|
// src/sandbox.ts
|
|
78061
|
-
var
|
|
79995
|
+
var import_node_os14 = require("os");
|
|
78062
79996
|
|
|
78063
79997
|
// src/execution-activity-tracker.ts
|
|
78064
79998
|
var ExecutionActivityTracker = class {
|
|
@@ -78383,11 +80317,11 @@ async function readErrorBody(response) {
|
|
|
78383
80317
|
}
|
|
78384
80318
|
}
|
|
78385
80319
|
function delay2(ms) {
|
|
78386
|
-
return new Promise((
|
|
80320
|
+
return new Promise((resolve15) => setTimeout(resolve15, ms));
|
|
78387
80321
|
}
|
|
78388
80322
|
|
|
78389
80323
|
// src/web-presenter.ts
|
|
78390
|
-
var
|
|
80324
|
+
var import_node_crypto20 = require("crypto");
|
|
78391
80325
|
var WebPresenter = class {
|
|
78392
80326
|
constructor(wsClient, conversationId, cwd) {
|
|
78393
80327
|
this.conversationId = conversationId;
|
|
@@ -78573,7 +80507,7 @@ var WebPresenter = class {
|
|
|
78573
80507
|
this.applicationProblem = createAlanProblem({
|
|
78574
80508
|
domain: "application",
|
|
78575
80509
|
code: input2.code,
|
|
78576
|
-
id: (0,
|
|
80510
|
+
id: (0, import_node_crypto20.randomUUID)(),
|
|
78577
80511
|
title: APPLICATION_PROBLEM_TITLES[input2.code],
|
|
78578
80512
|
detail: input2.message,
|
|
78579
80513
|
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -78640,7 +80574,7 @@ var WebPresenter = class {
|
|
|
78640
80574
|
};
|
|
78641
80575
|
|
|
78642
80576
|
// src/ws-client.ts
|
|
78643
|
-
var
|
|
80577
|
+
var import_node_crypto22 = require("crypto");
|
|
78644
80578
|
|
|
78645
80579
|
// ../shared/dist/agent-liveness.js
|
|
78646
80580
|
var AGENT_LIVENESS_PROTOCOL_VERSION = 1;
|
|
@@ -78668,8 +80602,8 @@ var agentProbeAckSchema = external_exports.object({
|
|
|
78668
80602
|
});
|
|
78669
80603
|
|
|
78670
80604
|
// src/sandbox-outbox.ts
|
|
78671
|
-
var
|
|
78672
|
-
var
|
|
80605
|
+
var import_node_crypto21 = require("crypto");
|
|
80606
|
+
var import_node_fs24 = require("fs");
|
|
78673
80607
|
var SANDBOX_EVENT_OUTBOX_ENV = "ALAN_EVENT_OUTBOX_ENABLED";
|
|
78674
80608
|
function isSandboxEventOutboxEnabled(env = process.env) {
|
|
78675
80609
|
return env[SANDBOX_EVENT_OUTBOX_ENV] === "true";
|
|
@@ -78678,7 +80612,7 @@ function deriveSandboxOutboxKey(sessionToken) {
|
|
|
78678
80612
|
if (!sessionToken) {
|
|
78679
80613
|
throw new Error("sandbox event outbox requires ALAN_SESSION_TOKEN for key derivation");
|
|
78680
80614
|
}
|
|
78681
|
-
return (0,
|
|
80615
|
+
return (0, import_node_crypto21.createHash)("sha256").update(sessionToken, "utf8").digest("base64url");
|
|
78682
80616
|
}
|
|
78683
80617
|
function sandboxOutboxSpoolPath(conversationId) {
|
|
78684
80618
|
return `/tmp/alan-agent-outbox-${conversationId}.enc`;
|
|
@@ -78692,7 +80626,7 @@ function createSandboxEventOutbox(input2) {
|
|
|
78692
80626
|
input2.pushLog?.(
|
|
78693
80627
|
`sandbox_outbox_spool_reset ${error61 instanceof Error ? error61.message : String(error61)}`
|
|
78694
80628
|
);
|
|
78695
|
-
(0,
|
|
80629
|
+
(0, import_node_fs24.rmSync)(path2, { force: true });
|
|
78696
80630
|
return new EncryptedEventOutbox(path2, key, input2.pushLog, input2.options);
|
|
78697
80631
|
}
|
|
78698
80632
|
}
|
|
@@ -78830,7 +80764,7 @@ var WSClient = class {
|
|
|
78830
80764
|
}
|
|
78831
80765
|
socket;
|
|
78832
80766
|
/** Stable id for this agent process; fences stale heartbeats server-side. */
|
|
78833
|
-
agentSessionId = (0,
|
|
80767
|
+
agentSessionId = (0, import_node_crypto22.randomUUID)();
|
|
78834
80768
|
/** Monotonic heartbeat sequence — advances on every hello + heartbeat. */
|
|
78835
80769
|
heartbeatSeq = 0;
|
|
78836
80770
|
heartbeatTimer = null;
|
|
@@ -78914,9 +80848,9 @@ var WSClient = class {
|
|
|
78914
80848
|
});
|
|
78915
80849
|
}
|
|
78916
80850
|
waitForConnection(timeoutMs = 15e3) {
|
|
78917
|
-
return new Promise((
|
|
80851
|
+
return new Promise((resolve15, reject) => {
|
|
78918
80852
|
if (this.socket.connected) {
|
|
78919
|
-
|
|
80853
|
+
resolve15();
|
|
78920
80854
|
return;
|
|
78921
80855
|
}
|
|
78922
80856
|
let lastError;
|
|
@@ -78927,7 +80861,7 @@ var WSClient = class {
|
|
|
78927
80861
|
};
|
|
78928
80862
|
const handleConnect = () => {
|
|
78929
80863
|
cleanup();
|
|
78930
|
-
|
|
80864
|
+
resolve15();
|
|
78931
80865
|
};
|
|
78932
80866
|
const handleConnectError = (error61) => {
|
|
78933
80867
|
lastError = error61;
|
|
@@ -79260,9 +81194,9 @@ async function runSandbox(config2) {
|
|
|
79260
81194
|
onUserMessage: (payload) => {
|
|
79261
81195
|
if (payload === null) {
|
|
79262
81196
|
if (pendingMessageResolve) {
|
|
79263
|
-
const
|
|
81197
|
+
const resolve15 = pendingMessageResolve;
|
|
79264
81198
|
pendingMessageResolve = null;
|
|
79265
|
-
|
|
81199
|
+
resolve15(null);
|
|
79266
81200
|
}
|
|
79267
81201
|
return;
|
|
79268
81202
|
}
|
|
@@ -79284,9 +81218,9 @@ async function runSandbox(config2) {
|
|
|
79284
81218
|
fileCount: payload.files?.length ?? 0
|
|
79285
81219
|
});
|
|
79286
81220
|
if (pendingMessageResolve) {
|
|
79287
|
-
const
|
|
81221
|
+
const resolve15 = pendingMessageResolve;
|
|
79288
81222
|
pendingMessageResolve = null;
|
|
79289
|
-
|
|
81223
|
+
resolve15(payload);
|
|
79290
81224
|
} else {
|
|
79291
81225
|
queuedMessages.push(payload);
|
|
79292
81226
|
}
|
|
@@ -79351,7 +81285,7 @@ async function runSandbox(config2) {
|
|
|
79351
81285
|
}
|
|
79352
81286
|
const presenter = new WebPresenter(wsClient, config2.sessionId, config2.projectPath);
|
|
79353
81287
|
presenter.setSuppressSessionLifecycle(true);
|
|
79354
|
-
const runtimeSkillManager = await RuntimeSkillManager.open({ homeDirectory: (0,
|
|
81288
|
+
const runtimeSkillManager = await RuntimeSkillManager.open({ homeDirectory: (0, import_node_os14.homedir)() });
|
|
79355
81289
|
let lastProviderSessionId = config2.providerSessionId;
|
|
79356
81290
|
let resumeFallbackContext = config2.resumeFallbackContext;
|
|
79357
81291
|
let activeBackendKind = config2.backendKind || "claude_cli";
|
|
@@ -79365,12 +81299,13 @@ async function runSandbox(config2) {
|
|
|
79365
81299
|
let currentAlanMcp;
|
|
79366
81300
|
while (!stopped) {
|
|
79367
81301
|
if (currentTask?.trim() || currentSkillInvocation) {
|
|
81302
|
+
const activeRunId = currentRunId ?? config2.runId;
|
|
79368
81303
|
const agent = new CoreAgent(presenter, {
|
|
79369
81304
|
backendKind: activeBackendKind,
|
|
81305
|
+
runId: activeRunId,
|
|
79370
81306
|
providerApiKey: activeProviderApiKey
|
|
79371
81307
|
});
|
|
79372
81308
|
currentAgent = agent;
|
|
79373
|
-
const activeRunId = currentRunId ?? config2.runId;
|
|
79374
81309
|
const rootExecutionActivity = executionActivityTracker && activeRunId && currentTaskId ? executionActivityTracker.register({
|
|
79375
81310
|
ownerId: `root:${activeRunId}`,
|
|
79376
81311
|
leaseRunId: activeRunId,
|
|
@@ -79558,8 +81493,8 @@ async function runSandbox(config2) {
|
|
|
79558
81493
|
});
|
|
79559
81494
|
if (stopped) break;
|
|
79560
81495
|
}
|
|
79561
|
-
const nextPayload = queuedMessages.shift() ?? await new Promise((
|
|
79562
|
-
pendingMessageResolve =
|
|
81496
|
+
const nextPayload = queuedMessages.shift() ?? await new Promise((resolve15) => {
|
|
81497
|
+
pendingMessageResolve = resolve15;
|
|
79563
81498
|
});
|
|
79564
81499
|
if (nextPayload === null || stopped) {
|
|
79565
81500
|
break;
|
|
@@ -79638,9 +81573,9 @@ async function runSandbox(config2) {
|
|
|
79638
81573
|
|
|
79639
81574
|
// src/service-manager.ts
|
|
79640
81575
|
var import_node_child_process9 = require("child_process");
|
|
79641
|
-
var
|
|
79642
|
-
var
|
|
79643
|
-
var
|
|
81576
|
+
var import_node_fs25 = require("fs");
|
|
81577
|
+
var import_node_os15 = require("os");
|
|
81578
|
+
var import_node_path26 = require("path");
|
|
79644
81579
|
var SERVICE_LABEL = "ai.tryalan.agent";
|
|
79645
81580
|
var SYSTEMD_UNIT = "alan-agent.service";
|
|
79646
81581
|
var WINDOWS_TASK = "Alan Agent";
|
|
@@ -79660,9 +81595,16 @@ function buildDaemonServicePlan(input2) {
|
|
|
79660
81595
|
if (userId === void 0) throw new Error("Cannot determine the current macOS user ID");
|
|
79661
81596
|
const domain2 = `gui/${userId}`;
|
|
79662
81597
|
const serviceTarget = `${domain2}/${SERVICE_LABEL}`;
|
|
79663
|
-
const manifestPath = (0,
|
|
79664
|
-
const logDir = (0,
|
|
81598
|
+
const manifestPath = (0, import_node_path26.join)(input2.homeDir, "Library", "LaunchAgents", `${SERVICE_LABEL}.plist`);
|
|
81599
|
+
const logDir = (0, import_node_path26.join)(input2.homeDir, ".alan", "agent", "logs");
|
|
79665
81600
|
const programArguments = daemonArgs.map((arg) => ` <string>${xml(arg)}</string>`).join("\n");
|
|
81601
|
+
const environmentEntries = Object.entries(input2.environment ?? {}).map(([key, value2]) => ` <key>${xml(key)}</key>
|
|
81602
|
+
<string>${xml(value2)}</string>`).join("\n");
|
|
81603
|
+
const environmentBlock = environmentEntries ? ` <key>EnvironmentVariables</key>
|
|
81604
|
+
<dict>
|
|
81605
|
+
${environmentEntries}
|
|
81606
|
+
</dict>
|
|
81607
|
+
` : "";
|
|
79666
81608
|
const manifest = `<?xml version="1.0" encoding="UTF-8"?>
|
|
79667
81609
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
79668
81610
|
<plist version="1.0">
|
|
@@ -79673,7 +81615,7 @@ function buildDaemonServicePlan(input2) {
|
|
|
79673
81615
|
<array>
|
|
79674
81616
|
${programArguments}
|
|
79675
81617
|
</array>
|
|
79676
|
-
<key>RunAtLoad</key>
|
|
81618
|
+
${environmentBlock} <key>RunAtLoad</key>
|
|
79677
81619
|
<true/>
|
|
79678
81620
|
<key>KeepAlive</key>
|
|
79679
81621
|
<dict>
|
|
@@ -79683,9 +81625,9 @@ ${programArguments}
|
|
|
79683
81625
|
<key>ThrottleInterval</key>
|
|
79684
81626
|
<integer>5</integer>
|
|
79685
81627
|
<key>StandardOutPath</key>
|
|
79686
|
-
<string>${xml((0,
|
|
81628
|
+
<string>${xml((0, import_node_path26.join)(logDir, "daemon.log"))}</string>
|
|
79687
81629
|
<key>StandardErrorPath</key>
|
|
79688
|
-
<string>${xml((0,
|
|
81630
|
+
<string>${xml((0, import_node_path26.join)(logDir, "daemon-error.log"))}</string>
|
|
79689
81631
|
</dict>
|
|
79690
81632
|
</plist>
|
|
79691
81633
|
`;
|
|
@@ -79724,7 +81666,8 @@ ${programArguments}
|
|
|
79724
81666
|
};
|
|
79725
81667
|
}
|
|
79726
81668
|
if (input2.platform === "linux") {
|
|
79727
|
-
const manifestPath = (0,
|
|
81669
|
+
const manifestPath = (0, import_node_path26.join)(input2.homeDir, ".config", "systemd", "user", SYSTEMD_UNIT);
|
|
81670
|
+
const environmentLines = Object.entries(input2.environment ?? {}).map(([key, value2]) => `Environment=${systemdArg(`${key}=${value2}`)}`).join("\n");
|
|
79728
81671
|
const manifest = `[Unit]
|
|
79729
81672
|
Description=Alan local agent daemon
|
|
79730
81673
|
After=network-online.target
|
|
@@ -79734,7 +81677,8 @@ StartLimitBurst=3
|
|
|
79734
81677
|
|
|
79735
81678
|
[Service]
|
|
79736
81679
|
Type=simple
|
|
79737
|
-
|
|
81680
|
+
${environmentLines ? `${environmentLines}
|
|
81681
|
+
` : ""}ExecStart=${daemonArgs.map(systemdArg).join(" ")}
|
|
79738
81682
|
Restart=on-failure
|
|
79739
81683
|
RestartSec=5
|
|
79740
81684
|
|
|
@@ -79775,9 +81719,11 @@ WantedBy=default.target
|
|
|
79775
81719
|
}
|
|
79776
81720
|
};
|
|
79777
81721
|
}
|
|
79778
|
-
const
|
|
81722
|
+
const environmentPrefix = Object.entries(input2.environment ?? {}).map(([key, value2]) => `$env:${key} = ${powershellLiteral(value2)}`).join("; ");
|
|
81723
|
+
const directArgument = [input2.cliEntry, "daemon", "--profile", input2.profile].map((part) => part.includes(" ") ? `"${part}"` : part).join(" ");
|
|
81724
|
+
const executable = powershellLiteral(environmentPrefix ? "powershell.exe" : input2.nodeExecutable);
|
|
79779
81725
|
const argument = powershellLiteral(
|
|
79780
|
-
|
|
81726
|
+
environmentPrefix ? `-NoProfile -NonInteractive -WindowStyle Hidden -Command "${environmentPrefix}; & ${powershellLiteral(input2.nodeExecutable)} ${powershellLiteral(input2.cliEntry)} daemon --profile ${input2.profile}"` : directArgument
|
|
79781
81727
|
);
|
|
79782
81728
|
const installScript = [
|
|
79783
81729
|
`$action = New-ScheduledTaskAction -Execute ${executable} -Argument ${argument}`,
|
|
@@ -79820,31 +81766,32 @@ WantedBy=default.target
|
|
|
79820
81766
|
}
|
|
79821
81767
|
};
|
|
79822
81768
|
}
|
|
79823
|
-
function currentServicePlan(args) {
|
|
79824
|
-
const currentPlatform = (0,
|
|
81769
|
+
function currentServicePlan(args, runtime) {
|
|
81770
|
+
const currentPlatform = (0, import_node_os15.platform)();
|
|
79825
81771
|
if (currentPlatform !== "darwin" && currentPlatform !== "linux" && currentPlatform !== "win32") {
|
|
79826
81772
|
throw new Error(`Daemon service installation is not supported on ${currentPlatform}`);
|
|
79827
81773
|
}
|
|
79828
81774
|
const profile = resolveEndpointProfileFromArgs(args);
|
|
79829
|
-
const cliEntry = process.argv[1];
|
|
81775
|
+
const cliEntry = runtime?.cliEntry ?? process.argv[1];
|
|
79830
81776
|
if (!cliEntry) throw new Error("Cannot determine the alan-agent executable path");
|
|
79831
81777
|
return buildDaemonServicePlan({
|
|
79832
81778
|
platform: currentPlatform,
|
|
79833
|
-
homeDir: (0,
|
|
79834
|
-
nodeExecutable: process.execPath,
|
|
79835
|
-
cliEntry: (0,
|
|
79836
|
-
profile
|
|
81779
|
+
homeDir: (0, import_node_os15.homedir)(),
|
|
81780
|
+
nodeExecutable: runtime?.executable ?? process.execPath,
|
|
81781
|
+
cliEntry: (0, import_node_path26.resolve)(cliEntry),
|
|
81782
|
+
profile,
|
|
81783
|
+
environment: runtime?.environment
|
|
79837
81784
|
});
|
|
79838
81785
|
}
|
|
79839
81786
|
function writeManifest(path2, contents) {
|
|
79840
|
-
(0,
|
|
81787
|
+
(0, import_node_fs25.mkdirSync)((0, import_node_path26.dirname)(path2), { recursive: true, mode: 448 });
|
|
79841
81788
|
const pendingPath = `${path2}.pending-${process.pid}`;
|
|
79842
81789
|
try {
|
|
79843
|
-
(0,
|
|
79844
|
-
(0,
|
|
79845
|
-
(0,
|
|
81790
|
+
(0, import_node_fs25.writeFileSync)(pendingPath, contents, { mode: 384 });
|
|
81791
|
+
(0, import_node_fs25.chmodSync)(pendingPath, 384);
|
|
81792
|
+
(0, import_node_fs25.renameSync)(pendingPath, path2);
|
|
79846
81793
|
} finally {
|
|
79847
|
-
(0,
|
|
81794
|
+
(0, import_node_fs25.rmSync)(pendingPath, { force: true });
|
|
79848
81795
|
}
|
|
79849
81796
|
}
|
|
79850
81797
|
function runServiceCommand(command) {
|
|
@@ -79858,16 +81805,16 @@ function runServiceCommand(command) {
|
|
|
79858
81805
|
}
|
|
79859
81806
|
return { status, output: output2 };
|
|
79860
81807
|
}
|
|
79861
|
-
function installDaemonService(args = []) {
|
|
81808
|
+
function installDaemonService(args = [], runtime) {
|
|
79862
81809
|
const profile = resolveEndpointProfileFromArgs(args);
|
|
79863
81810
|
const configPath = resolveAgentConfigPath({ profile });
|
|
79864
81811
|
if (!configExistsAtPath(configPath)) {
|
|
79865
81812
|
throw new Error(`No ${profile} runtime is configured. Run alan-agent setup or login first.`);
|
|
79866
81813
|
}
|
|
79867
|
-
const plan = currentServicePlan(args);
|
|
81814
|
+
const plan = currentServicePlan(args, runtime);
|
|
79868
81815
|
if (plan.manifestPath && plan.manifest) {
|
|
79869
|
-
if ((0,
|
|
79870
|
-
(0,
|
|
81816
|
+
if ((0, import_node_os15.platform)() === "darwin") {
|
|
81817
|
+
(0, import_node_fs25.mkdirSync)((0, import_node_path26.join)((0, import_node_os15.homedir)(), ".alan", "agent", "logs"), { recursive: true, mode: 448 });
|
|
79871
81818
|
}
|
|
79872
81819
|
writeManifest(plan.manifestPath, plan.manifest);
|
|
79873
81820
|
}
|
|
@@ -79877,7 +81824,7 @@ function installDaemonService(args = []) {
|
|
|
79877
81824
|
function uninstallDaemonService(args = []) {
|
|
79878
81825
|
const plan = currentServicePlan(args);
|
|
79879
81826
|
for (const command of plan.uninstallCommands) runServiceCommand(command);
|
|
79880
|
-
if (plan.manifestPath) (0,
|
|
81827
|
+
if (plan.manifestPath) (0, import_node_fs25.rmSync)(plan.manifestPath, { force: true });
|
|
79881
81828
|
console.info("[alan-agent] Per-user daemon service removed");
|
|
79882
81829
|
}
|
|
79883
81830
|
function printDaemonServiceStatus(args = []) {
|
|
@@ -79889,11 +81836,11 @@ function printDaemonServiceStatus(args = []) {
|
|
|
79889
81836
|
|
|
79890
81837
|
// src/skills/skill-cli.ts
|
|
79891
81838
|
var import_promises6 = require("fs/promises");
|
|
79892
|
-
var
|
|
79893
|
-
var
|
|
81839
|
+
var import_node_os16 = require("os");
|
|
81840
|
+
var import_node_path27 = require("path");
|
|
79894
81841
|
async function runSkillsCommand(args) {
|
|
79895
81842
|
const [subcommand, ...options] = args;
|
|
79896
|
-
const homeDirectory = option(options, "--home") ?? (0,
|
|
81843
|
+
const homeDirectory = option(options, "--home") ?? (0, import_node_os16.homedir)();
|
|
79897
81844
|
const manager = await RuntimeSkillManager.open({ homeDirectory });
|
|
79898
81845
|
if (subcommand === "scan") {
|
|
79899
81846
|
const inputPath = option(options, "--input");
|
|
@@ -79934,7 +81881,7 @@ async function defaultScanInput(homeDirectory) {
|
|
|
79934
81881
|
});
|
|
79935
81882
|
}
|
|
79936
81883
|
async function readProtectedJson(path2) {
|
|
79937
|
-
const absolutePath = (0,
|
|
81884
|
+
const absolutePath = (0, import_node_path27.resolve)(path2);
|
|
79938
81885
|
const stat2 = await (0, import_promises6.lstat)(absolutePath);
|
|
79939
81886
|
if (!stat2.isFile()) throw new Error(`Skill input is not a regular file: ${absolutePath}`);
|
|
79940
81887
|
if (process.platform !== "win32" && (stat2.mode & 63) !== 0) {
|
|
@@ -80084,6 +82031,17 @@ async function main() {
|
|
|
80084
82031
|
await setupDaemon(commandArgs);
|
|
80085
82032
|
return;
|
|
80086
82033
|
}
|
|
82034
|
+
if (command === "install") {
|
|
82035
|
+
bindConfigPath(commandArgs);
|
|
82036
|
+
const configured = readConfig();
|
|
82037
|
+
if (!configured.runtimeId || !configured.runtimeToken) {
|
|
82038
|
+
if (commandArgs.includes("--setup-token")) await setupDaemon(commandArgs);
|
|
82039
|
+
else await loginWithDeviceCode(commandArgs);
|
|
82040
|
+
}
|
|
82041
|
+
installDaemonService(commandArgs);
|
|
82042
|
+
runMcpIntegrationCommand(["reconcile", ...commandArgs]);
|
|
82043
|
+
return;
|
|
82044
|
+
}
|
|
80087
82045
|
if (command === "login") {
|
|
80088
82046
|
await loginWithDeviceCode(commandArgs);
|
|
80089
82047
|
return;
|
|
@@ -80094,6 +82052,7 @@ async function main() {
|
|
|
80094
82052
|
}
|
|
80095
82053
|
if (command === "service" && commandArgs[0] === "install") {
|
|
80096
82054
|
installDaemonService(commandArgs.slice(1));
|
|
82055
|
+
runMcpIntegrationCommand(["reconcile", ...commandArgs.slice(1)]);
|
|
80097
82056
|
return;
|
|
80098
82057
|
}
|
|
80099
82058
|
if (command === "service" && commandArgs[0] === "uninstall") {
|
|
@@ -80104,6 +82063,18 @@ async function main() {
|
|
|
80104
82063
|
printDaemonServiceStatus(commandArgs.slice(1));
|
|
80105
82064
|
return;
|
|
80106
82065
|
}
|
|
82066
|
+
if (command === "integrations" && commandArgs[0] === "capabilities") {
|
|
82067
|
+
printNativeIntegrationStatus(commandArgs.slice(1));
|
|
82068
|
+
return;
|
|
82069
|
+
}
|
|
82070
|
+
if (command === "integrations") {
|
|
82071
|
+
runMcpIntegrationCommand(commandArgs);
|
|
82072
|
+
return;
|
|
82073
|
+
}
|
|
82074
|
+
if (command === "hook") {
|
|
82075
|
+
runNativeHookCommand(commandArgs);
|
|
82076
|
+
return;
|
|
82077
|
+
}
|
|
80107
82078
|
if (command === "stop") {
|
|
80108
82079
|
await stopDaemon(commandArgs);
|
|
80109
82080
|
return;
|