@aiden-ade/sandbox-agent 0.1.77 → 0.1.79
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 +2225 -341
- 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.79";
|
|
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);
|
|
@@ -49816,7 +49836,7 @@ function checkStoredEndpointConsistency(stored, endpointDefaults) {
|
|
|
49816
49836
|
}
|
|
49817
49837
|
function sleep(ms) {
|
|
49818
49838
|
if (ms <= 0) return Promise.resolve();
|
|
49819
|
-
return new Promise((
|
|
49839
|
+
return new Promise((resolve15) => setTimeout(resolve15, ms));
|
|
49820
49840
|
}
|
|
49821
49841
|
function scheduleRuntimeModelScan(getBaseMetadata, options) {
|
|
49822
49842
|
const stored = readConfig();
|
|
@@ -49837,11 +49857,11 @@ function scheduleRuntimeModelScan(getBaseMetadata, options) {
|
|
|
49837
49857
|
}
|
|
49838
49858
|
|
|
49839
49859
|
// src/daemon-start.ts
|
|
49840
|
-
var
|
|
49841
|
-
var
|
|
49860
|
+
var import_node_crypto16 = require("crypto");
|
|
49861
|
+
var import_node_fs20 = require("fs");
|
|
49842
49862
|
var import_node_http = __toESM(require("http"), 1);
|
|
49843
49863
|
var import_node_os10 = require("os");
|
|
49844
|
-
var
|
|
49864
|
+
var import_node_path22 = require("path");
|
|
49845
49865
|
|
|
49846
49866
|
// ../../node_modules/engine.io-client/build/esm-debug/transports/polling-xhr.node.js
|
|
49847
49867
|
var XMLHttpRequestModule = __toESM(require_XMLHttpRequest(), 1);
|
|
@@ -52397,9 +52417,9 @@ var Socket2 = class extends Emitter {
|
|
|
52397
52417
|
* @return a Promise that will be fulfilled when the server acknowledges the event
|
|
52398
52418
|
*/
|
|
52399
52419
|
emitWithAck(ev, ...args) {
|
|
52400
|
-
return new Promise((
|
|
52420
|
+
return new Promise((resolve15, reject) => {
|
|
52401
52421
|
const fn = (arg1, arg2) => {
|
|
52402
|
-
return arg1 ? reject(arg1) :
|
|
52422
|
+
return arg1 ? reject(arg1) : resolve15(arg2);
|
|
52403
52423
|
};
|
|
52404
52424
|
fn.withError = true;
|
|
52405
52425
|
args.push(fn);
|
|
@@ -54564,7 +54584,7 @@ function versionPolicy(npmPackage) {
|
|
|
54564
54584
|
return "latest";
|
|
54565
54585
|
}
|
|
54566
54586
|
function runNpmInstall(installSpec, env, timeoutMs) {
|
|
54567
|
-
return new Promise((
|
|
54587
|
+
return new Promise((resolve15) => {
|
|
54568
54588
|
const isWin = (0, import_node_os7.platform)() === "win32";
|
|
54569
54589
|
let child;
|
|
54570
54590
|
try {
|
|
@@ -54576,7 +54596,7 @@ function runNpmInstall(installSpec, env, timeoutMs) {
|
|
|
54576
54596
|
stdio: ["ignore", "pipe", "pipe"]
|
|
54577
54597
|
});
|
|
54578
54598
|
} catch (error61) {
|
|
54579
|
-
|
|
54599
|
+
resolve15({ status: null, stdout: "", stderr: "", error: error61 });
|
|
54580
54600
|
return;
|
|
54581
54601
|
}
|
|
54582
54602
|
let stdout = "";
|
|
@@ -54587,10 +54607,10 @@ function runNpmInstall(installSpec, env, timeoutMs) {
|
|
|
54587
54607
|
child.stderr?.setEncoding("utf8").on("data", (chunk) => {
|
|
54588
54608
|
stderr += chunk;
|
|
54589
54609
|
});
|
|
54590
|
-
child.once("error", (error61) =>
|
|
54610
|
+
child.once("error", (error61) => resolve15({ status: null, stdout, stderr, error: error61 }));
|
|
54591
54611
|
child.once(
|
|
54592
54612
|
"close",
|
|
54593
|
-
(status, signal) =>
|
|
54613
|
+
(status, signal) => resolve15({
|
|
54594
54614
|
status,
|
|
54595
54615
|
stdout,
|
|
54596
54616
|
stderr,
|
|
@@ -55333,6 +55353,147 @@ function parse5(toml, { maxDepth = 1e3, integersAsBigInt } = {}) {
|
|
|
55333
55353
|
return res;
|
|
55334
55354
|
}
|
|
55335
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
|
+
|
|
55336
55497
|
// src/mcp-registration.ts
|
|
55337
55498
|
var CONFIG_LOCK_STALE_MS = 3e4;
|
|
55338
55499
|
var CONFIG_LOCK_WAIT_MS = 2e3;
|
|
@@ -55461,7 +55622,7 @@ async function waitForConfigLock(lockPath) {
|
|
|
55461
55622
|
if (Date.now() - startedAt >= CONFIG_LOCK_WAIT_MS) {
|
|
55462
55623
|
throw new Error(`Alan could not safely update ${lockPath.slice(0, -".alan-lock".length)}`);
|
|
55463
55624
|
}
|
|
55464
|
-
await new Promise((
|
|
55625
|
+
await new Promise((resolve15) => setTimeout(resolve15, CONFIG_LOCK_POLL_MS));
|
|
55465
55626
|
}
|
|
55466
55627
|
}
|
|
55467
55628
|
}
|
|
@@ -55607,6 +55768,22 @@ async function ensureAlanMcpRegistered(backendKind, registration, home = (0, imp
|
|
|
55607
55768
|
return { ok: false, paths: [], error: err instanceof Error ? err.message : String(err) };
|
|
55608
55769
|
}
|
|
55609
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
|
+
}
|
|
55610
55787
|
async function verifyAlanMcpRegistered(backendKind, home = (0, import_node_os8.homedir)(), inspectCli = inspectAlanMcpThroughProviderCli) {
|
|
55611
55788
|
const files = alanMcpConfigFilesForBackend(backendKind, {}, home);
|
|
55612
55789
|
const missing = files.map((f) => f.path).filter((p) => !(0, import_node_fs14.existsSync)(p));
|
|
@@ -55678,51 +55855,6 @@ async function verifyAlanMcpRegistered(backendKind, home = (0, import_node_os8.h
|
|
|
55678
55855
|
providerLoadStatus: providerLoadStatusForBackend(backendKind)
|
|
55679
55856
|
};
|
|
55680
55857
|
}
|
|
55681
|
-
function removeAlanFromJsonConfig(path2, existingContent) {
|
|
55682
|
-
const config2 = parseJsonObject2(path2, existingContent);
|
|
55683
|
-
for (const containerKey of ["mcpServers", "mcp"]) {
|
|
55684
|
-
const container = config2[containerKey];
|
|
55685
|
-
if (container && typeof container === "object" && !Array.isArray(container)) {
|
|
55686
|
-
delete container.alan;
|
|
55687
|
-
}
|
|
55688
|
-
}
|
|
55689
|
-
return JSON.stringify(config2);
|
|
55690
|
-
}
|
|
55691
|
-
async function unregisterAlanMcp(backendKind, home = (0, import_node_os8.homedir)(), options = {}) {
|
|
55692
|
-
const removedPaths = [];
|
|
55693
|
-
const errors = [];
|
|
55694
|
-
const files = alanMcpConfigFilesForBackend(backendKind, {}, home);
|
|
55695
|
-
for (const file2 of files) {
|
|
55696
|
-
if (!(0, import_node_fs14.existsSync)(file2.path)) continue;
|
|
55697
|
-
const releaseLock = await waitForConfigLock(`${file2.path}.alan-lock`);
|
|
55698
|
-
try {
|
|
55699
|
-
const current = (0, import_node_fs14.readFileSync)(file2.path, "utf8");
|
|
55700
|
-
if (options.onlyIfRegistration && !registrationsMatch(
|
|
55701
|
-
parseAlanMcpRegistration(file2.path, current),
|
|
55702
|
-
options.onlyIfRegistration
|
|
55703
|
-
)) {
|
|
55704
|
-
continue;
|
|
55705
|
-
}
|
|
55706
|
-
let next;
|
|
55707
|
-
if (file2.path.endsWith(".toml")) {
|
|
55708
|
-
const stripped = stripCodexAlanMcpSection(current);
|
|
55709
|
-
next = stripped ? `${stripped}
|
|
55710
|
-
` : "";
|
|
55711
|
-
} else {
|
|
55712
|
-
next = removeAlanFromJsonConfig(file2.path, current);
|
|
55713
|
-
}
|
|
55714
|
-
if (next !== current) {
|
|
55715
|
-
atomicWriteManagedConfig(file2.path, next);
|
|
55716
|
-
removedPaths.push(file2.path);
|
|
55717
|
-
}
|
|
55718
|
-
} catch (err) {
|
|
55719
|
-
errors.push(err instanceof Error ? err.message : String(err));
|
|
55720
|
-
} finally {
|
|
55721
|
-
releaseLock();
|
|
55722
|
-
}
|
|
55723
|
-
}
|
|
55724
|
-
return { removedPaths, errors };
|
|
55725
|
-
}
|
|
55726
55858
|
function isHttpUrl(value2) {
|
|
55727
55859
|
if (typeof value2 !== "string") return false;
|
|
55728
55860
|
try {
|
|
@@ -55814,12 +55946,14 @@ function parseAlanMcpRegistration(path2, content) {
|
|
|
55814
55946
|
var CoreAgent = class _CoreAgent extends BaseMachineAgent {
|
|
55815
55947
|
childProcess = null;
|
|
55816
55948
|
_aborted = false;
|
|
55949
|
+
runId;
|
|
55817
55950
|
providerApiKey;
|
|
55818
55951
|
onProviderSpawned;
|
|
55819
55952
|
/** How long to wait after SIGTERM before escalating to SIGKILL. */
|
|
55820
55953
|
static SIGKILL_DELAY_MS = 3e3;
|
|
55821
55954
|
constructor(presenter, runtime) {
|
|
55822
55955
|
super(presenter, runtime);
|
|
55956
|
+
this.runId = runtime.runId?.trim() || void 0;
|
|
55823
55957
|
this.providerApiKey = runtime.providerApiKey;
|
|
55824
55958
|
this.onProviderSpawned = runtime.onProviderSpawned;
|
|
55825
55959
|
}
|
|
@@ -55950,6 +56084,9 @@ var CoreAgent = class _CoreAgent extends BaseMachineAgent {
|
|
|
55950
56084
|
}
|
|
55951
56085
|
async buildCliEnvironment() {
|
|
55952
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;
|
|
55953
56090
|
const backendKind = this.runtime.backendKind ?? "";
|
|
55954
56091
|
const providerApiKey = this.providerApiKey ?? "";
|
|
55955
56092
|
if (backendKind === "antigravity_cli" && providerApiKey.trim()) {
|
|
@@ -60493,14 +60630,14 @@ function inputRequiredRoundsExceededMessage(method, maxRounds) {
|
|
|
60493
60630
|
return `Multi-round-trip request '${method}' still required input after ${maxRounds} rounds (inputRequired.maxRounds)`;
|
|
60494
60631
|
}
|
|
60495
60632
|
function sleep2(ms, signal) {
|
|
60496
|
-
return new Promise((
|
|
60633
|
+
return new Promise((resolve15, reject) => {
|
|
60497
60634
|
if (signal?.aborted) {
|
|
60498
60635
|
reject(signal.reason instanceof SdkError ? signal.reason : new SdkError(SdkErrorCode.RequestTimeout, String(signal.reason)));
|
|
60499
60636
|
return;
|
|
60500
60637
|
}
|
|
60501
60638
|
const timer = setTimeout(() => {
|
|
60502
60639
|
signal?.removeEventListener("abort", onAbort);
|
|
60503
|
-
|
|
60640
|
+
resolve15();
|
|
60504
60641
|
}, ms);
|
|
60505
60642
|
const onAbort = () => {
|
|
60506
60643
|
clearTimeout(timer);
|
|
@@ -61339,7 +61476,7 @@ var Protocol = class {
|
|
|
61339
61476
|
const flowStartedAt = Date.now();
|
|
61340
61477
|
let onAbort;
|
|
61341
61478
|
let cleanupMessageId;
|
|
61342
|
-
return new Promise((
|
|
61479
|
+
return new Promise((resolve15, reject) => {
|
|
61343
61480
|
const earlyReject = (error61) => {
|
|
61344
61481
|
reject(error61);
|
|
61345
61482
|
};
|
|
@@ -61407,7 +61544,7 @@ var Protocol = class {
|
|
|
61407
61544
|
}
|
|
61408
61545
|
if (decoded.kind === "invalid") return reject(decoded.error);
|
|
61409
61546
|
if (decoded.kind === "input_required") {
|
|
61410
|
-
if (options?.allowInputRequired === true) return
|
|
61547
|
+
if (options?.allowInputRequired === true) return resolve15(manualInputRequiredValue(decoded));
|
|
61411
61548
|
const flow = {
|
|
61412
61549
|
codec: codec2,
|
|
61413
61550
|
request,
|
|
@@ -61419,11 +61556,11 @@ var Protocol = class {
|
|
|
61419
61556
|
params
|
|
61420
61557
|
}, resultSchema, legOptions)
|
|
61421
61558
|
};
|
|
61422
|
-
return
|
|
61559
|
+
return resolve15(this._resolveNonCompleteResult(decoded, flow));
|
|
61423
61560
|
}
|
|
61424
61561
|
const result = decoded.result;
|
|
61425
61562
|
validateStandardSchema(resultSchema, result).then((parseResult) => {
|
|
61426
|
-
if (parseResult.success)
|
|
61563
|
+
if (parseResult.success) resolve15(parseResult.data);
|
|
61427
61564
|
else reject(new SdkError(SdkErrorCode.InvalidResult, `Invalid result for ${request.method}: ${parseResult.error}`));
|
|
61428
61565
|
}, reject);
|
|
61429
61566
|
});
|
|
@@ -64337,7 +64474,7 @@ var require_compile = /* @__PURE__ */ __commonJSMin(((exports2) => {
|
|
|
64337
64474
|
ref = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, ref);
|
|
64338
64475
|
const schOrFunc = root.refs[ref];
|
|
64339
64476
|
if (schOrFunc) return schOrFunc;
|
|
64340
|
-
let _sch =
|
|
64477
|
+
let _sch = resolve15.call(this, root, ref);
|
|
64341
64478
|
if (_sch === void 0) {
|
|
64342
64479
|
const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
|
|
64343
64480
|
const { schemaId } = this.opts;
|
|
@@ -64363,7 +64500,7 @@ var require_compile = /* @__PURE__ */ __commonJSMin(((exports2) => {
|
|
|
64363
64500
|
function sameSchemaEnv(s1, s2) {
|
|
64364
64501
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
64365
64502
|
}
|
|
64366
|
-
function
|
|
64503
|
+
function resolve15(root, ref) {
|
|
64367
64504
|
let sch;
|
|
64368
64505
|
while (typeof (sch = this.refs[ref]) == "string") ref = sch;
|
|
64369
64506
|
return sch || this.schemas[ref] || resolveSchema.call(this, root, ref);
|
|
@@ -64813,7 +64950,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports2, module2) => {
|
|
|
64813
64950
|
else if (typeof uri === "object") uri = parse7(serialize(uri, options), options);
|
|
64814
64951
|
return uri;
|
|
64815
64952
|
}
|
|
64816
|
-
function
|
|
64953
|
+
function resolve15(baseURI, relativeURI, options) {
|
|
64817
64954
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
64818
64955
|
const resolved = resolveComponent(parse7(baseURI, schemelessOptions), parse7(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
64819
64956
|
schemelessOptions.skipEscape = true;
|
|
@@ -64987,7 +65124,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports2, module2) => {
|
|
|
64987
65124
|
const fastUri = {
|
|
64988
65125
|
SCHEMES,
|
|
64989
65126
|
normalize,
|
|
64990
|
-
resolve:
|
|
65127
|
+
resolve: resolve15,
|
|
64991
65128
|
resolveComponent,
|
|
64992
65129
|
equal,
|
|
64993
65130
|
serialize,
|
|
@@ -65463,10 +65600,10 @@ var require_core$3 = /* @__PURE__ */ __commonJSMin(((exports2) => {
|
|
|
65463
65600
|
Ajv2.ValidationError = validation_error_1.default;
|
|
65464
65601
|
Ajv2.MissingRefError = ref_error_1.default;
|
|
65465
65602
|
exports2.default = Ajv2;
|
|
65466
|
-
function checkOptions(checkOpts, options, msg,
|
|
65603
|
+
function checkOptions(checkOpts, options, msg, log2 = "error") {
|
|
65467
65604
|
for (const key in checkOpts) {
|
|
65468
65605
|
const opt = key;
|
|
65469
|
-
if (opt in options) this.logger[
|
|
65606
|
+
if (opt in options) this.logger[log2](`${msg}: option ${key}. ${checkOpts[opt]}`);
|
|
65470
65607
|
}
|
|
65471
65608
|
}
|
|
65472
65609
|
function getSchEnv(keyRef) {
|
|
@@ -68790,8 +68927,8 @@ async function random(size) {
|
|
|
68790
68927
|
const evenDistCutoff = Math.pow(2, 8) - Math.pow(2, 8) % mask.length;
|
|
68791
68928
|
let result = "";
|
|
68792
68929
|
while (result.length < size) {
|
|
68793
|
-
const
|
|
68794
|
-
for (const randomByte of
|
|
68930
|
+
const randomBytes13 = await getRandomValues(size - result.length);
|
|
68931
|
+
for (const randomByte of randomBytes13) {
|
|
68795
68932
|
if (randomByte < evenDistCutoff) {
|
|
68796
68933
|
result += mask[randomByte % mask.length];
|
|
68797
68934
|
}
|
|
@@ -70429,14 +70566,14 @@ var ProbeWindow = class ProbeWindow2 {
|
|
|
70429
70566
|
*/
|
|
70430
70567
|
async exchange(buildRequest, timeoutMs) {
|
|
70431
70568
|
const id = `server-discover-probe-${++this._probeCounter}`;
|
|
70432
|
-
return new Promise((
|
|
70569
|
+
return new Promise((resolve15) => {
|
|
70433
70570
|
let settled = false;
|
|
70434
70571
|
const settle = (reply) => {
|
|
70435
70572
|
if (settled) return;
|
|
70436
70573
|
settled = true;
|
|
70437
70574
|
clearTimeout(timer);
|
|
70438
70575
|
if (this._pending?.id === id) this._pending = void 0;
|
|
70439
|
-
|
|
70576
|
+
resolve15(reply);
|
|
70440
70577
|
};
|
|
70441
70578
|
const timer = setTimeout(() => settle({ kind: "timeout" }), timeoutMs);
|
|
70442
70579
|
this._pending = {
|
|
@@ -71642,13 +71779,13 @@ var Client = class extends Protocol {
|
|
|
71642
71779
|
let onCallerAbort;
|
|
71643
71780
|
let resolveOpening;
|
|
71644
71781
|
let rejectOpening;
|
|
71645
|
-
const opening = new Promise((
|
|
71646
|
-
resolveOpening =
|
|
71782
|
+
const opening = new Promise((resolve15, reject) => {
|
|
71783
|
+
resolveOpening = resolve15;
|
|
71647
71784
|
rejectOpening = reject;
|
|
71648
71785
|
});
|
|
71649
71786
|
let resolveClosed;
|
|
71650
|
-
const closed = new Promise((
|
|
71651
|
-
resolveClosed =
|
|
71787
|
+
const closed = new Promise((resolve15) => {
|
|
71788
|
+
resolveClosed = resolve15;
|
|
71652
71789
|
});
|
|
71653
71790
|
const settle = (outcome) => {
|
|
71654
71791
|
if (state === "closed") return;
|
|
@@ -72703,11 +72840,31 @@ function failureFromError(error61, paths) {
|
|
|
72703
72840
|
stage: "protocol",
|
|
72704
72841
|
message: "Alan's tool service returned an invalid MCP response.",
|
|
72705
72842
|
retryable: false,
|
|
72706
|
-
paths
|
|
72843
|
+
paths,
|
|
72844
|
+
diagnostic: describeReadinessError(error61)
|
|
72707
72845
|
};
|
|
72708
72846
|
}
|
|
72847
|
+
function describeReadinessError(error61) {
|
|
72848
|
+
if (!(error61 instanceof Error)) return String(error61);
|
|
72849
|
+
const parts2 = [];
|
|
72850
|
+
let current = error61;
|
|
72851
|
+
for (let depth = 0; current instanceof Error && depth < 4; depth += 1) {
|
|
72852
|
+
const { name, message } = current;
|
|
72853
|
+
const { code, status } = current;
|
|
72854
|
+
parts2.push(
|
|
72855
|
+
[
|
|
72856
|
+
name,
|
|
72857
|
+
message,
|
|
72858
|
+
code === void 0 ? "" : `code=${String(code)}`,
|
|
72859
|
+
status === void 0 ? "" : `status=${String(status)}`
|
|
72860
|
+
].filter(Boolean).join(" ")
|
|
72861
|
+
);
|
|
72862
|
+
current = current.cause;
|
|
72863
|
+
}
|
|
72864
|
+
return parts2.join(" <- ");
|
|
72865
|
+
}
|
|
72709
72866
|
async function waitBeforeRetry() {
|
|
72710
|
-
await new Promise((
|
|
72867
|
+
await new Promise((resolve15) => setTimeout(resolve15, RETRY_DELAY_MS2));
|
|
72711
72868
|
}
|
|
72712
72869
|
async function probeAlanMcpReadiness(registration, options = {}) {
|
|
72713
72870
|
const paths = options.paths ?? [];
|
|
@@ -73146,7 +73303,8 @@ async function handleAgentExecute(ctx, payload) {
|
|
|
73146
73303
|
console.warn("[alan-agent] Alan MCP readiness observed failure", {
|
|
73147
73304
|
...correlation,
|
|
73148
73305
|
code: failure.code,
|
|
73149
|
-
stage: failure.stage
|
|
73306
|
+
stage: failure.stage,
|
|
73307
|
+
...failure.diagnostic ? { diagnostic: failure.diagnostic } : {}
|
|
73150
73308
|
});
|
|
73151
73309
|
}
|
|
73152
73310
|
});
|
|
@@ -73329,6 +73487,7 @@ async function handleAgentExecute(ctx, payload) {
|
|
|
73329
73487
|
agent = new CoreAgent(presenter, {
|
|
73330
73488
|
backendKind,
|
|
73331
73489
|
runtimeCommand,
|
|
73490
|
+
runId: payload.runId,
|
|
73332
73491
|
providerApiKey: payload.providerApiKey,
|
|
73333
73492
|
onProviderSpawned: (pid) => {
|
|
73334
73493
|
releaseRunStart();
|
|
@@ -73726,6 +73885,473 @@ function respondWithLocalSkill(res, operation) {
|
|
|
73726
73885
|
);
|
|
73727
73886
|
}
|
|
73728
73887
|
|
|
73888
|
+
// src/native-hook-drain.ts
|
|
73889
|
+
var PermanentNativeHookDeliveryError = class extends Error {
|
|
73890
|
+
constructor(message) {
|
|
73891
|
+
super(message);
|
|
73892
|
+
this.name = "PermanentNativeHookDeliveryError";
|
|
73893
|
+
}
|
|
73894
|
+
};
|
|
73895
|
+
async function drainNativeHookInbox(input2) {
|
|
73896
|
+
return input2.inbox.drain(async (envelope) => {
|
|
73897
|
+
const isStart = envelope.eventType === "session_start" || envelope.eventType === "subagent_start";
|
|
73898
|
+
const isTerminal2 = envelope.eventType === "stop" || envelope.eventType === "session_end" || envelope.eventType === "subagent_end";
|
|
73899
|
+
const streamTranscript = async () => {
|
|
73900
|
+
if (isStart || !input2.transcriptStreamer || !envelope.transcriptPath) {
|
|
73901
|
+
return "unavailable";
|
|
73902
|
+
}
|
|
73903
|
+
try {
|
|
73904
|
+
const streamed = await input2.transcriptStreamer.stream(
|
|
73905
|
+
envelope,
|
|
73906
|
+
(event) => input2.sink.deliver(event)
|
|
73907
|
+
);
|
|
73908
|
+
return streamed.complete ? streamed.assistantOutputDelivered ? "replayed" : "unavailable" : "deferred";
|
|
73909
|
+
} catch (error61) {
|
|
73910
|
+
if (error61 instanceof PermanentNativeHookDeliveryError) throw error61;
|
|
73911
|
+
const normalizedError = error61 instanceof Error ? error61 : new Error(String(error61));
|
|
73912
|
+
input2.onTranscriptError?.({
|
|
73913
|
+
envelope,
|
|
73914
|
+
error: normalizedError
|
|
73915
|
+
});
|
|
73916
|
+
if (isMissingTranscriptError(normalizedError) && transcriptGraceExpired(
|
|
73917
|
+
envelope.observedAt,
|
|
73918
|
+
input2.now?.() ?? Date.now(),
|
|
73919
|
+
input2.missingTranscriptGraceMs ?? 1e4
|
|
73920
|
+
)) {
|
|
73921
|
+
return "unavailable";
|
|
73922
|
+
}
|
|
73923
|
+
return "deferred";
|
|
73924
|
+
}
|
|
73925
|
+
};
|
|
73926
|
+
if (isTerminal2) {
|
|
73927
|
+
const transcriptReplay = await streamTranscript();
|
|
73928
|
+
if (transcriptReplay === "deferred") return false;
|
|
73929
|
+
return input2.sink.deliver(
|
|
73930
|
+
transcriptReplay === "replayed" ? { ...envelope, assistantOutputReplayed: true } : envelope
|
|
73931
|
+
);
|
|
73932
|
+
}
|
|
73933
|
+
if (!await input2.sink.deliver(envelope)) return false;
|
|
73934
|
+
return await streamTranscript() !== "deferred";
|
|
73935
|
+
}, input2.limit);
|
|
73936
|
+
}
|
|
73937
|
+
function isMissingTranscriptError(error61) {
|
|
73938
|
+
return error61.code === "ENOENT";
|
|
73939
|
+
}
|
|
73940
|
+
function transcriptGraceExpired(observedAt, now, graceMs) {
|
|
73941
|
+
const observedAtMs = Date.parse(observedAt);
|
|
73942
|
+
if (!Number.isFinite(observedAtMs)) return true;
|
|
73943
|
+
return now - observedAtMs >= Math.max(0, graceMs);
|
|
73944
|
+
}
|
|
73945
|
+
|
|
73946
|
+
// src/native-hook-inbox.ts
|
|
73947
|
+
var import_node_crypto8 = require("crypto");
|
|
73948
|
+
var import_node_fs16 = require("fs");
|
|
73949
|
+
var import_node_path14 = require("path");
|
|
73950
|
+
var INBOX_FILE_VERSION = 1;
|
|
73951
|
+
var DEFAULT_MAX_BYTES = 64 * 1024 * 1024;
|
|
73952
|
+
var DEFAULT_MAX_ENTRIES = 1e4;
|
|
73953
|
+
function decodeKey2(encodedKey) {
|
|
73954
|
+
const key = Buffer.from(encodedKey, "base64url");
|
|
73955
|
+
if (key.length !== 32) throw new Error("native hook inbox key must contain exactly 32 bytes");
|
|
73956
|
+
return key;
|
|
73957
|
+
}
|
|
73958
|
+
function encryptedEntryNames(path2) {
|
|
73959
|
+
if (!(0, import_node_fs16.existsSync)(path2)) return [];
|
|
73960
|
+
return (0, import_node_fs16.readdirSync)(path2).filter((name) => name.endsWith(".event") || name.includes(".processing-")).sort();
|
|
73961
|
+
}
|
|
73962
|
+
var EncryptedNativeHookInbox = class {
|
|
73963
|
+
constructor(path2, encodedKey, options = {}) {
|
|
73964
|
+
this.path = path2;
|
|
73965
|
+
this.key = decodeKey2(encodedKey);
|
|
73966
|
+
this.maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
73967
|
+
this.maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;
|
|
73968
|
+
this.onQuarantine = options.onQuarantine;
|
|
73969
|
+
if (!Number.isSafeInteger(this.maxBytes) || this.maxBytes <= 0) {
|
|
73970
|
+
throw new Error("native hook inbox maxBytes must be a positive integer");
|
|
73971
|
+
}
|
|
73972
|
+
if (!Number.isSafeInteger(this.maxEntries) || this.maxEntries <= 0) {
|
|
73973
|
+
throw new Error("native hook inbox maxEntries must be a positive integer");
|
|
73974
|
+
}
|
|
73975
|
+
(0, import_node_fs16.mkdirSync)(this.path, { recursive: true, mode: 448 });
|
|
73976
|
+
(0, import_node_fs16.chmodSync)(this.path, 448);
|
|
73977
|
+
if (options.recoverClaims) this.recoverClaims();
|
|
73978
|
+
}
|
|
73979
|
+
key;
|
|
73980
|
+
maxBytes;
|
|
73981
|
+
maxEntries;
|
|
73982
|
+
onQuarantine;
|
|
73983
|
+
enqueue(envelope) {
|
|
73984
|
+
const plaintext = Buffer.from(JSON.stringify(envelope), "utf8");
|
|
73985
|
+
const iv = (0, import_node_crypto8.randomBytes)(12);
|
|
73986
|
+
const cipher = (0, import_node_crypto8.createCipheriv)("aes-256-gcm", this.key, iv);
|
|
73987
|
+
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
73988
|
+
const file2 = {
|
|
73989
|
+
version: INBOX_FILE_VERSION,
|
|
73990
|
+
iv: iv.toString("base64url"),
|
|
73991
|
+
authTag: cipher.getAuthTag().toString("base64url"),
|
|
73992
|
+
ciphertext: ciphertext.toString("base64url")
|
|
73993
|
+
};
|
|
73994
|
+
const monotonic = process.hrtime.bigint().toString().padStart(20, "0");
|
|
73995
|
+
const prefix = `${String(Date.now()).padStart(13, "0")}-${monotonic}-${envelope.eventId}`;
|
|
73996
|
+
const temporaryPath = (0, import_node_path14.join)(this.path, `.${prefix}-${(0, import_node_crypto8.randomUUID)()}.tmp`);
|
|
73997
|
+
const finalPath = (0, import_node_path14.join)(this.path, `${prefix}.event`);
|
|
73998
|
+
(0, import_node_fs16.writeFileSync)(temporaryPath, JSON.stringify(file2), { mode: 384, flag: "wx" });
|
|
73999
|
+
(0, import_node_fs16.chmodSync)(temporaryPath, 384);
|
|
74000
|
+
(0, import_node_fs16.renameSync)(temporaryPath, finalPath);
|
|
74001
|
+
try {
|
|
74002
|
+
this.enforceBounds();
|
|
74003
|
+
} catch (error61) {
|
|
74004
|
+
(0, import_node_fs16.rmSync)(finalPath, { force: true });
|
|
74005
|
+
throw error61;
|
|
74006
|
+
}
|
|
74007
|
+
}
|
|
74008
|
+
size() {
|
|
74009
|
+
return encryptedEntryNames(this.path).length;
|
|
74010
|
+
}
|
|
74011
|
+
async drain(consume, limit = 100) {
|
|
74012
|
+
const result = { attempted: 0, acknowledged: 0, deferred: 0 };
|
|
74013
|
+
const names = encryptedEntryNames(this.path).filter((name) => name.endsWith(".event")).slice(0, Math.max(0, limit));
|
|
74014
|
+
for (const name of names) {
|
|
74015
|
+
const sourcePath = (0, import_node_path14.join)(this.path, name);
|
|
74016
|
+
const claimedName = `${name.slice(0, -".event".length)}.processing-${(0, import_node_crypto8.randomUUID)()}`;
|
|
74017
|
+
const claimedPath = (0, import_node_path14.join)(this.path, claimedName);
|
|
74018
|
+
try {
|
|
74019
|
+
(0, import_node_fs16.renameSync)(sourcePath, claimedPath);
|
|
74020
|
+
} catch {
|
|
74021
|
+
continue;
|
|
74022
|
+
}
|
|
74023
|
+
result.attempted += 1;
|
|
74024
|
+
try {
|
|
74025
|
+
const envelope = this.readEntry(claimedPath);
|
|
74026
|
+
if (await consume(envelope)) {
|
|
74027
|
+
(0, import_node_fs16.rmSync)(claimedPath, { force: true });
|
|
74028
|
+
result.acknowledged += 1;
|
|
74029
|
+
} else {
|
|
74030
|
+
(0, import_node_fs16.renameSync)(claimedPath, sourcePath);
|
|
74031
|
+
result.deferred += 1;
|
|
74032
|
+
break;
|
|
74033
|
+
}
|
|
74034
|
+
} catch (error61) {
|
|
74035
|
+
if ((0, import_node_fs16.existsSync)(claimedPath)) {
|
|
74036
|
+
const quarantinedPath = `${claimedPath}.quarantine`;
|
|
74037
|
+
(0, import_node_fs16.renameSync)(claimedPath, quarantinedPath);
|
|
74038
|
+
this.onQuarantine?.({
|
|
74039
|
+
fileName: quarantinedPath.slice(quarantinedPath.lastIndexOf("/") + 1),
|
|
74040
|
+
error: error61 instanceof Error ? error61 : new Error(String(error61))
|
|
74041
|
+
});
|
|
74042
|
+
}
|
|
74043
|
+
result.deferred += 1;
|
|
74044
|
+
}
|
|
74045
|
+
}
|
|
74046
|
+
return result;
|
|
74047
|
+
}
|
|
74048
|
+
readEntry(path2) {
|
|
74049
|
+
const file2 = JSON.parse((0, import_node_fs16.readFileSync)(path2, "utf8"));
|
|
74050
|
+
if (file2.version !== INBOX_FILE_VERSION) throw new Error("unsupported native hook inbox entry");
|
|
74051
|
+
const decipher = (0, import_node_crypto8.createDecipheriv)("aes-256-gcm", this.key, Buffer.from(file2.iv, "base64url"));
|
|
74052
|
+
decipher.setAuthTag(Buffer.from(file2.authTag, "base64url"));
|
|
74053
|
+
const plaintext = Buffer.concat([
|
|
74054
|
+
decipher.update(Buffer.from(file2.ciphertext, "base64url")),
|
|
74055
|
+
decipher.final()
|
|
74056
|
+
]);
|
|
74057
|
+
return JSON.parse(plaintext.toString("utf8"));
|
|
74058
|
+
}
|
|
74059
|
+
recoverClaims() {
|
|
74060
|
+
for (const name of encryptedEntryNames(this.path)) {
|
|
74061
|
+
if (name.endsWith(".quarantine")) continue;
|
|
74062
|
+
const marker = name.indexOf(".processing-");
|
|
74063
|
+
if (marker === -1) continue;
|
|
74064
|
+
const claimedPath = (0, import_node_path14.join)(this.path, name);
|
|
74065
|
+
const eventPath = (0, import_node_path14.join)(this.path, `${name.slice(0, marker)}.event`);
|
|
74066
|
+
if ((0, import_node_fs16.existsSync)(eventPath)) (0, import_node_fs16.rmSync)(claimedPath, { force: true });
|
|
74067
|
+
else (0, import_node_fs16.renameSync)(claimedPath, eventPath);
|
|
74068
|
+
}
|
|
74069
|
+
}
|
|
74070
|
+
enforceBounds() {
|
|
74071
|
+
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 }));
|
|
74072
|
+
const totalBytes = entries.reduce((sum, entry) => sum + entry.bytes, 0);
|
|
74073
|
+
if (entries.length > this.maxEntries || totalBytes > this.maxBytes) {
|
|
74074
|
+
throw new Error(
|
|
74075
|
+
`native hook inbox capacity exceeded (${entries.length} entries, ${totalBytes} bytes)`
|
|
74076
|
+
);
|
|
74077
|
+
}
|
|
74078
|
+
}
|
|
74079
|
+
};
|
|
74080
|
+
function nativeHookInboxPath(configPath) {
|
|
74081
|
+
return (0, import_node_path14.join)((0, import_node_path14.dirname)(configPath), "native-hook-inbox");
|
|
74082
|
+
}
|
|
74083
|
+
|
|
74084
|
+
// src/native-transcript-cursor.ts
|
|
74085
|
+
var import_node_crypto9 = require("crypto");
|
|
74086
|
+
var import_node_fs17 = require("fs");
|
|
74087
|
+
var import_node_path15 = require("path");
|
|
74088
|
+
var CURSOR_FILE_VERSION = 1;
|
|
74089
|
+
function nativeTranscriptCursorKey(input2) {
|
|
74090
|
+
return (0, import_node_crypto9.createHash)("sha256").update(`${input2.provider}\0${input2.externalSessionId}\0${input2.transcriptPath}`).digest("base64url");
|
|
74091
|
+
}
|
|
74092
|
+
var NativeTranscriptCursorStore = class {
|
|
74093
|
+
constructor(path2) {
|
|
74094
|
+
this.path = path2;
|
|
74095
|
+
this.entries = this.read();
|
|
74096
|
+
}
|
|
74097
|
+
entries;
|
|
74098
|
+
get(key) {
|
|
74099
|
+
const value2 = this.entries[key];
|
|
74100
|
+
return value2 ? { ...value2 } : void 0;
|
|
74101
|
+
}
|
|
74102
|
+
commit(key, cursor) {
|
|
74103
|
+
if (!Number.isSafeInteger(cursor.offset) || cursor.offset < 0) {
|
|
74104
|
+
throw new Error("native transcript cursor offset must be a non-negative safe integer");
|
|
74105
|
+
}
|
|
74106
|
+
this.entries[key] = { ...cursor };
|
|
74107
|
+
this.persist();
|
|
74108
|
+
}
|
|
74109
|
+
read() {
|
|
74110
|
+
if (!(0, import_node_fs17.existsSync)(this.path)) return {};
|
|
74111
|
+
const parsed = JSON.parse((0, import_node_fs17.readFileSync)(this.path, "utf8"));
|
|
74112
|
+
if (parsed.version !== CURSOR_FILE_VERSION || !parsed.entries || typeof parsed.entries !== "object") {
|
|
74113
|
+
throw new Error("unsupported native transcript cursor file");
|
|
74114
|
+
}
|
|
74115
|
+
return parsed.entries;
|
|
74116
|
+
}
|
|
74117
|
+
persist() {
|
|
74118
|
+
(0, import_node_fs17.mkdirSync)((0, import_node_path15.dirname)(this.path), { recursive: true, mode: 448 });
|
|
74119
|
+
const pendingPath = `${this.path}.pending-${process.pid}-${(0, import_node_crypto9.randomBytes)(6).toString("hex")}`;
|
|
74120
|
+
try {
|
|
74121
|
+
(0, import_node_fs17.writeFileSync)(
|
|
74122
|
+
pendingPath,
|
|
74123
|
+
JSON.stringify({
|
|
74124
|
+
version: CURSOR_FILE_VERSION,
|
|
74125
|
+
entries: this.entries
|
|
74126
|
+
}),
|
|
74127
|
+
{ mode: 384 }
|
|
74128
|
+
);
|
|
74129
|
+
(0, import_node_fs17.chmodSync)(pendingPath, 384);
|
|
74130
|
+
(0, import_node_fs17.renameSync)(pendingPath, this.path);
|
|
74131
|
+
} finally {
|
|
74132
|
+
(0, import_node_fs17.rmSync)(pendingPath, { force: true });
|
|
74133
|
+
}
|
|
74134
|
+
}
|
|
74135
|
+
};
|
|
74136
|
+
|
|
74137
|
+
// src/native-transcript-stream.ts
|
|
74138
|
+
var import_node_crypto10 = require("crypto");
|
|
74139
|
+
var import_node_fs18 = require("fs");
|
|
74140
|
+
var DEFAULT_MAX_READ_BYTES = 4 * 1024 * 1024;
|
|
74141
|
+
var NativeTranscriptStreamer = class {
|
|
74142
|
+
constructor(cursorStore, maxReadBytes = DEFAULT_MAX_READ_BYTES) {
|
|
74143
|
+
this.cursorStore = cursorStore;
|
|
74144
|
+
this.maxReadBytes = maxReadBytes;
|
|
74145
|
+
if (!Number.isSafeInteger(maxReadBytes) || maxReadBytes <= 0) {
|
|
74146
|
+
throw new Error("native transcript maxReadBytes must be a positive integer");
|
|
74147
|
+
}
|
|
74148
|
+
}
|
|
74149
|
+
async stream(hook, deliver) {
|
|
74150
|
+
if (!hook.transcriptPath) {
|
|
74151
|
+
return {
|
|
74152
|
+
complete: true,
|
|
74153
|
+
recordsRead: 0,
|
|
74154
|
+
eventsDelivered: 0,
|
|
74155
|
+
assistantOutputDelivered: false
|
|
74156
|
+
};
|
|
74157
|
+
}
|
|
74158
|
+
const key = nativeTranscriptCursorKey({
|
|
74159
|
+
provider: hook.provider,
|
|
74160
|
+
externalSessionId: hook.externalSessionId,
|
|
74161
|
+
transcriptPath: hook.transcriptPath
|
|
74162
|
+
});
|
|
74163
|
+
let previous = this.cursorStore.get(key);
|
|
74164
|
+
if (hook.eventType === "user_prompt" && previous && previous.assistantOutputTurnId !== hook.eventId) {
|
|
74165
|
+
previous = {
|
|
74166
|
+
...previous,
|
|
74167
|
+
assistantOutputSeen: false,
|
|
74168
|
+
assistantOutputTurnId: hook.eventId
|
|
74169
|
+
};
|
|
74170
|
+
this.cursorStore.commit(key, previous);
|
|
74171
|
+
}
|
|
74172
|
+
const file2 = (0, import_node_fs18.statSync)(hook.transcriptPath);
|
|
74173
|
+
const replaced = Boolean(previous) && (previous.device !== file2.dev || previous.inode !== file2.ino);
|
|
74174
|
+
const truncated = Boolean(previous) && file2.size < previous.offset;
|
|
74175
|
+
const start = !previous || replaced || truncated ? {
|
|
74176
|
+
offset: 0,
|
|
74177
|
+
generation: previous ? previous.generation + 1 : 0,
|
|
74178
|
+
device: file2.dev,
|
|
74179
|
+
inode: file2.ino,
|
|
74180
|
+
assistantOutputSeen: previous?.assistantOutputSeen ?? false,
|
|
74181
|
+
assistantOutputTurnId: hook.eventType === "user_prompt" ? hook.eventId : previous?.assistantOutputTurnId
|
|
74182
|
+
} : previous;
|
|
74183
|
+
let assistantOutputSeen = start.assistantOutputSeen ?? false;
|
|
74184
|
+
if (file2.size <= start.offset) {
|
|
74185
|
+
return {
|
|
74186
|
+
complete: true,
|
|
74187
|
+
recordsRead: 0,
|
|
74188
|
+
eventsDelivered: 0,
|
|
74189
|
+
assistantOutputDelivered: assistantOutputSeen
|
|
74190
|
+
};
|
|
74191
|
+
}
|
|
74192
|
+
const bytesToRead = Math.min(file2.size - start.offset, this.maxReadBytes);
|
|
74193
|
+
const buffer = Buffer.allocUnsafe(bytesToRead);
|
|
74194
|
+
const fd = (0, import_node_fs18.openSync)(hook.transcriptPath, "r");
|
|
74195
|
+
let bytesRead = 0;
|
|
74196
|
+
try {
|
|
74197
|
+
bytesRead = (0, import_node_fs18.readSync)(fd, buffer, 0, bytesToRead, start.offset);
|
|
74198
|
+
} finally {
|
|
74199
|
+
(0, import_node_fs18.closeSync)(fd);
|
|
74200
|
+
}
|
|
74201
|
+
const chunk = buffer.subarray(0, bytesRead);
|
|
74202
|
+
const lastNewline = chunk.lastIndexOf(10);
|
|
74203
|
+
if (lastNewline < 0) {
|
|
74204
|
+
return {
|
|
74205
|
+
complete: false,
|
|
74206
|
+
recordsRead: 0,
|
|
74207
|
+
eventsDelivered: 0,
|
|
74208
|
+
assistantOutputDelivered: assistantOutputSeen
|
|
74209
|
+
};
|
|
74210
|
+
}
|
|
74211
|
+
let recordStart = 0;
|
|
74212
|
+
let recordsRead = 0;
|
|
74213
|
+
let eventsDelivered = 0;
|
|
74214
|
+
let committedOffset = start.offset;
|
|
74215
|
+
while (recordStart <= lastNewline) {
|
|
74216
|
+
const newline = chunk.indexOf(10, recordStart);
|
|
74217
|
+
if (newline < 0 || newline > lastNewline) break;
|
|
74218
|
+
const recordEndOffset = start.offset + newline + 1;
|
|
74219
|
+
const raw = chunk.subarray(recordStart, newline).toString("utf8").trim();
|
|
74220
|
+
recordStart = newline + 1;
|
|
74221
|
+
if (!raw) {
|
|
74222
|
+
committedOffset = recordEndOffset;
|
|
74223
|
+
this.cursorStore.commit(key, {
|
|
74224
|
+
...start,
|
|
74225
|
+
offset: committedOffset,
|
|
74226
|
+
assistantOutputSeen
|
|
74227
|
+
});
|
|
74228
|
+
continue;
|
|
74229
|
+
}
|
|
74230
|
+
const parsed = parseJsonObject3(raw);
|
|
74231
|
+
const events = parsed ? claudeRecordToAgentEvents(parsed) : [];
|
|
74232
|
+
for (let index = 0; index < events.length; index += 1) {
|
|
74233
|
+
const deliveredEvent = events[index];
|
|
74234
|
+
const sourceCursor = `${start.generation}:${recordEndOffset}:${index}`;
|
|
74235
|
+
const eventId = stableSourceEventId(hook, sourceCursor);
|
|
74236
|
+
const accepted = await deliver({
|
|
74237
|
+
schemaVersion: 1,
|
|
74238
|
+
eventId,
|
|
74239
|
+
provider: hook.provider,
|
|
74240
|
+
externalSessionId: hook.externalSessionId,
|
|
74241
|
+
sourceCursor,
|
|
74242
|
+
observedAt: timestampOf(parsed) ?? hook.observedAt,
|
|
74243
|
+
event: deliveredEvent
|
|
74244
|
+
});
|
|
74245
|
+
if (!accepted) {
|
|
74246
|
+
return {
|
|
74247
|
+
complete: false,
|
|
74248
|
+
recordsRead,
|
|
74249
|
+
eventsDelivered,
|
|
74250
|
+
assistantOutputDelivered: assistantOutputSeen,
|
|
74251
|
+
cursor: `${start.generation}:${committedOffset}`
|
|
74252
|
+
};
|
|
74253
|
+
}
|
|
74254
|
+
eventsDelivered += 1;
|
|
74255
|
+
if (deliveredEvent.type === "text" && deliveredEvent.text.length > 0) {
|
|
74256
|
+
assistantOutputSeen = true;
|
|
74257
|
+
}
|
|
74258
|
+
}
|
|
74259
|
+
committedOffset = recordEndOffset;
|
|
74260
|
+
recordsRead += 1;
|
|
74261
|
+
this.cursorStore.commit(key, {
|
|
74262
|
+
...start,
|
|
74263
|
+
offset: committedOffset,
|
|
74264
|
+
assistantOutputSeen
|
|
74265
|
+
});
|
|
74266
|
+
}
|
|
74267
|
+
return {
|
|
74268
|
+
complete: committedOffset >= file2.size,
|
|
74269
|
+
recordsRead,
|
|
74270
|
+
eventsDelivered,
|
|
74271
|
+
assistantOutputDelivered: assistantOutputSeen,
|
|
74272
|
+
cursor: `${start.generation}:${committedOffset}`
|
|
74273
|
+
};
|
|
74274
|
+
}
|
|
74275
|
+
};
|
|
74276
|
+
function stableSourceEventId(hook, sourceCursor) {
|
|
74277
|
+
const bytes = (0, import_node_crypto10.createHash)("sha256").update(`${hook.provider}\0${hook.externalSessionId}\0${hook.transcriptPath}\0${sourceCursor}`).digest().subarray(0, 16);
|
|
74278
|
+
bytes[6] = bytes[6] & 15 | 80;
|
|
74279
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
74280
|
+
const hex3 = bytes.toString("hex");
|
|
74281
|
+
return `${hex3.slice(0, 8)}-${hex3.slice(8, 12)}-${hex3.slice(12, 16)}-${hex3.slice(16, 20)}-${hex3.slice(20)}`;
|
|
74282
|
+
}
|
|
74283
|
+
function parseJsonObject3(value2) {
|
|
74284
|
+
try {
|
|
74285
|
+
const parsed = JSON.parse(value2);
|
|
74286
|
+
return isRecord2(parsed) ? parsed : null;
|
|
74287
|
+
} catch {
|
|
74288
|
+
return null;
|
|
74289
|
+
}
|
|
74290
|
+
}
|
|
74291
|
+
function claudeRecordToAgentEvents(record2) {
|
|
74292
|
+
const timestamp = timestampOf(record2) ? Date.parse(timestampOf(record2)) : Date.now();
|
|
74293
|
+
const ts = Number.isFinite(timestamp) ? timestamp : Date.now();
|
|
74294
|
+
const message = isRecord2(record2.message) ? record2.message : record2;
|
|
74295
|
+
const content = message.content;
|
|
74296
|
+
const role = typeof message.role === "string" ? message.role : typeof record2.type === "string" ? record2.type : null;
|
|
74297
|
+
if (role === "user") {
|
|
74298
|
+
if (!Array.isArray(content)) return [];
|
|
74299
|
+
return content.flatMap((block) => {
|
|
74300
|
+
if (!isRecord2(block) || block.type !== "tool_result") return [];
|
|
74301
|
+
const id = typeof block.tool_use_id === "string" ? block.tool_use_id : typeof block.toolUseId === "string" ? block.toolUseId : null;
|
|
74302
|
+
return id ? [{ type: "tool_result", id, content: extractText2(block.content), ts }] : [];
|
|
74303
|
+
});
|
|
74304
|
+
}
|
|
74305
|
+
if (record2.type === "summary" && typeof record2.summary === "string") {
|
|
74306
|
+
return [{ type: "session_notice", kind: "summary", message: record2.summary, ts }];
|
|
74307
|
+
}
|
|
74308
|
+
if (typeof record2.type === "string" && role !== "assistant") return [];
|
|
74309
|
+
if (Array.isArray(content)) {
|
|
74310
|
+
const events = content.flatMap((block) => {
|
|
74311
|
+
if (!isRecord2(block)) return [];
|
|
74312
|
+
if (block.type === "text" && typeof block.text === "string") {
|
|
74313
|
+
return [{ type: "text", text: block.text, ts }];
|
|
74314
|
+
}
|
|
74315
|
+
if (block.type === "thinking" && typeof block.thinking === "string") {
|
|
74316
|
+
return [{ type: "thinking", text: block.thinking, ts }];
|
|
74317
|
+
}
|
|
74318
|
+
if (block.type === "tool_use" && typeof block.id === "string" && typeof block.name === "string") {
|
|
74319
|
+
return [{ type: "tool_use", id: block.id, name: block.name, input: block.input, ts }];
|
|
74320
|
+
}
|
|
74321
|
+
if (block.type === "tool_result" && typeof block.tool_use_id === "string") {
|
|
74322
|
+
return [
|
|
74323
|
+
{ type: "tool_result", id: block.tool_use_id, content: extractText2(block.content), ts }
|
|
74324
|
+
];
|
|
74325
|
+
}
|
|
74326
|
+
return [];
|
|
74327
|
+
});
|
|
74328
|
+
if (events.length > 0) return events;
|
|
74329
|
+
}
|
|
74330
|
+
if (typeof content === "string" && content.length > 0)
|
|
74331
|
+
return [{ type: "text", text: content, ts }];
|
|
74332
|
+
return [];
|
|
74333
|
+
}
|
|
74334
|
+
function extractText2(value2) {
|
|
74335
|
+
if (typeof value2 === "string") return value2;
|
|
74336
|
+
if (Array.isArray(value2)) return value2.map(extractText2).filter(Boolean).join("\n");
|
|
74337
|
+
if (isRecord2(value2)) {
|
|
74338
|
+
if (typeof value2.text === "string") return value2.text;
|
|
74339
|
+
if (typeof value2.content === "string") return value2.content;
|
|
74340
|
+
}
|
|
74341
|
+
return JSON.stringify(value2);
|
|
74342
|
+
}
|
|
74343
|
+
function timestampOf(record2) {
|
|
74344
|
+
if (!record2) return null;
|
|
74345
|
+
for (const key of ["timestamp", "created_at", "createdAt"]) {
|
|
74346
|
+
const value2 = record2[key];
|
|
74347
|
+
if (typeof value2 === "string" && !Number.isNaN(Date.parse(value2))) return value2;
|
|
74348
|
+
}
|
|
74349
|
+
return null;
|
|
74350
|
+
}
|
|
74351
|
+
function isRecord2(value2) {
|
|
74352
|
+
return Boolean(value2) && typeof value2 === "object" && !Array.isArray(value2);
|
|
74353
|
+
}
|
|
74354
|
+
|
|
73729
74355
|
// src/network-diagnostics.ts
|
|
73730
74356
|
function collectEvidence(error61) {
|
|
73731
74357
|
const text = [];
|
|
@@ -73803,8 +74429,8 @@ var ObservableMap = class extends Map {
|
|
|
73803
74429
|
};
|
|
73804
74430
|
|
|
73805
74431
|
// src/run-journal.ts
|
|
73806
|
-
var
|
|
73807
|
-
var
|
|
74432
|
+
var import_node_fs19 = require("fs");
|
|
74433
|
+
var import_node_path16 = require("path");
|
|
73808
74434
|
var JOURNAL_VERSION = 1;
|
|
73809
74435
|
var INTERRUPTED_ENTRY_TTL_MS = 24 * 60 * 6e4;
|
|
73810
74436
|
function isEntry(value2) {
|
|
@@ -73823,9 +74449,9 @@ function isEntry(value2) {
|
|
|
73823
74449
|
var RuntimeRunJournal = class {
|
|
73824
74450
|
constructor(path2, nowMs = Date.now()) {
|
|
73825
74451
|
this.path = path2;
|
|
73826
|
-
if (!(0,
|
|
74452
|
+
if (!(0, import_node_fs19.existsSync)(path2)) return;
|
|
73827
74453
|
try {
|
|
73828
|
-
const parsed = JSON.parse((0,
|
|
74454
|
+
const parsed = JSON.parse((0, import_node_fs19.readFileSync)(path2, "utf8"));
|
|
73829
74455
|
if (parsed.version !== JOURNAL_VERSION || !Array.isArray(parsed.entries)) {
|
|
73830
74456
|
throw new Error("unsupported run journal format");
|
|
73831
74457
|
}
|
|
@@ -73948,18 +74574,18 @@ var RuntimeRunJournal = class {
|
|
|
73948
74574
|
}
|
|
73949
74575
|
persist() {
|
|
73950
74576
|
const pendingPath = `${this.path}.pending`;
|
|
73951
|
-
(0,
|
|
74577
|
+
(0, import_node_fs19.mkdirSync)((0, import_node_path16.dirname)(this.path), { recursive: true });
|
|
73952
74578
|
try {
|
|
73953
|
-
(0,
|
|
74579
|
+
(0, import_node_fs19.writeFileSync)(
|
|
73954
74580
|
pendingPath,
|
|
73955
74581
|
`${JSON.stringify({ version: JOURNAL_VERSION, entries: [...this.entries.values()] })}
|
|
73956
74582
|
`,
|
|
73957
74583
|
{ mode: 384 }
|
|
73958
74584
|
);
|
|
73959
|
-
(0,
|
|
73960
|
-
(0,
|
|
74585
|
+
(0, import_node_fs19.chmodSync)(pendingPath, 384);
|
|
74586
|
+
(0, import_node_fs19.renameSync)(pendingPath, this.path);
|
|
73961
74587
|
} catch (error61) {
|
|
73962
|
-
(0,
|
|
74588
|
+
(0, import_node_fs19.rmSync)(pendingPath, { force: true });
|
|
73963
74589
|
throw error61;
|
|
73964
74590
|
}
|
|
73965
74591
|
}
|
|
@@ -73971,8 +74597,8 @@ var RunStartGate = class {
|
|
|
73971
74597
|
async acquire() {
|
|
73972
74598
|
const previous = this.tail;
|
|
73973
74599
|
let releaseCurrent;
|
|
73974
|
-
const current = new Promise((
|
|
73975
|
-
releaseCurrent =
|
|
74600
|
+
const current = new Promise((resolve15) => {
|
|
74601
|
+
releaseCurrent = resolve15;
|
|
73976
74602
|
});
|
|
73977
74603
|
this.tail = previous.then(() => current);
|
|
73978
74604
|
await previous;
|
|
@@ -73987,15 +74613,15 @@ var RunStartGate = class {
|
|
|
73987
74613
|
|
|
73988
74614
|
// src/skills/local-skill-source.ts
|
|
73989
74615
|
var import_node_buffer2 = require("buffer");
|
|
73990
|
-
var
|
|
74616
|
+
var import_node_crypto12 = require("crypto");
|
|
73991
74617
|
var import_promises2 = require("fs/promises");
|
|
73992
|
-
var
|
|
74618
|
+
var import_node_path18 = __toESM(require("path"), 1);
|
|
73993
74619
|
|
|
73994
74620
|
// ../shared/dist/node/skill-package.js
|
|
73995
74621
|
var import_node_buffer = require("buffer");
|
|
73996
|
-
var
|
|
74622
|
+
var import_node_crypto11 = require("crypto");
|
|
73997
74623
|
var import_promises = require("fs/promises");
|
|
73998
|
-
var
|
|
74624
|
+
var import_node_path17 = require("path");
|
|
73999
74625
|
var import_yaml = __toESM(require_dist2(), 1);
|
|
74000
74626
|
|
|
74001
74627
|
// ../shared/dist/skills/domain.js
|
|
@@ -74419,7 +75045,7 @@ async function inspectSkillPackage(packageRoot, options = {}) {
|
|
|
74419
75045
|
}
|
|
74420
75046
|
const inspectedFiles = await Promise.all(files.map(readPackageFile));
|
|
74421
75047
|
inspectedFiles.sort(comparePackageFiles);
|
|
74422
|
-
const parsedMetadata = parseSkillMetadata(inspectedFiles.find((file2) => file2.manifest.path === "SKILL.md")?.bytes, options.expectedName === void 0 ? (0,
|
|
75048
|
+
const parsedMetadata = parseSkillMetadata(inspectedFiles.find((file2) => file2.manifest.path === "SKILL.md")?.bytes, options.expectedName === void 0 ? (0, import_node_path17.basename)(root) : options.expectedName);
|
|
74423
75049
|
const manifestFiles = inspectedFiles.map((file2) => file2.manifest);
|
|
74424
75050
|
const diagnostics = [
|
|
74425
75051
|
...parsedMetadata.conformance,
|
|
@@ -74443,7 +75069,7 @@ async function readSkillPackageFile(packageRoot, path2, expectedSha256) {
|
|
|
74443
75069
|
if (!normalizedPath || normalizedPath !== path2 || normalizedPath.includes("\\") || components.some((component) => !component || component === "." || component === "..")) {
|
|
74444
75070
|
throw new SkillPackageError("INVALID_PATH", `Invalid Skill package path: ${path2}`, path2);
|
|
74445
75071
|
}
|
|
74446
|
-
const absolutePath = await (0, import_promises.realpath)((0,
|
|
75072
|
+
const absolutePath = await (0, import_promises.realpath)((0, import_node_path17.resolve)(root, ...components)).catch(() => null);
|
|
74447
75073
|
if (!absolutePath || normalizeRelativePath(root, absolutePath) !== normalizedPath) {
|
|
74448
75074
|
throw new SkillPackageError("INVALID_PATH", `Skill package file was not found: ${path2}`, path2);
|
|
74449
75075
|
}
|
|
@@ -74464,7 +75090,7 @@ async function readSkillPackageFile(packageRoot, path2, expectedSha256) {
|
|
|
74464
75090
|
return { file: read.manifest, bytes: read.bytes };
|
|
74465
75091
|
}
|
|
74466
75092
|
async function requirePackageRoot(packageRoot) {
|
|
74467
|
-
const root = await (0, import_promises.realpath)((0,
|
|
75093
|
+
const root = await (0, import_promises.realpath)((0, import_node_path17.resolve)(packageRoot)).catch(() => null);
|
|
74468
75094
|
if (!root) {
|
|
74469
75095
|
throw new SkillPackageError("INVALID_PACKAGE_ROOT", "Skill package directory does not exist");
|
|
74470
75096
|
}
|
|
@@ -74490,7 +75116,7 @@ async function walkPackage(root, ignoredPaths) {
|
|
|
74490
75116
|
async function walkDirectory(directory) {
|
|
74491
75117
|
const entries = await (0, import_promises.readdir)(directory, { withFileTypes: true });
|
|
74492
75118
|
for (const entry of entries) {
|
|
74493
|
-
const absolutePath = (0,
|
|
75119
|
+
const absolutePath = (0, import_node_path17.resolve)(directory, entry.name);
|
|
74494
75120
|
const normalizedPath = normalizeRelativePath(root, absolutePath);
|
|
74495
75121
|
if (isIgnored(normalizedPath, entry.name, entry.isDirectory(), ignored))
|
|
74496
75122
|
continue;
|
|
@@ -74557,12 +75183,12 @@ function globToRegExp(pattern) {
|
|
|
74557
75183
|
return new RegExp(`${source}(?:/.*)?$`);
|
|
74558
75184
|
}
|
|
74559
75185
|
function normalizeRelativePath(root, absolutePath) {
|
|
74560
|
-
const nativeRelativePath = (0,
|
|
74561
|
-
if (!nativeRelativePath || nativeRelativePath.startsWith(`..${
|
|
75186
|
+
const nativeRelativePath = (0, import_node_path17.relative)(root, absolutePath);
|
|
75187
|
+
if (!nativeRelativePath || nativeRelativePath.startsWith(`..${import_node_path17.sep}`) || nativeRelativePath === "..") {
|
|
74562
75188
|
throw new SkillPackageError("INVALID_PATH", "Skill package path escapes its root");
|
|
74563
75189
|
}
|
|
74564
|
-
const normalized = nativeRelativePath.split(
|
|
74565
|
-
if (normalized.includes("\0") || normalized.includes("\uFFFD") ||
|
|
75190
|
+
const normalized = nativeRelativePath.split(import_node_path17.sep).join("/").normalize("NFC");
|
|
75191
|
+
if (normalized.includes("\0") || normalized.includes("\uFFFD") || import_node_path17.posix.isAbsolute(normalized)) {
|
|
74566
75192
|
throw new SkillPackageError("INVALID_PATH", `Invalid Skill package path: ${normalized}`);
|
|
74567
75193
|
}
|
|
74568
75194
|
return normalized;
|
|
@@ -74597,7 +75223,7 @@ function requireSafeFile(path2, size, normalizedPaths) {
|
|
|
74597
75223
|
}
|
|
74598
75224
|
}
|
|
74599
75225
|
function isSecretPath(path2) {
|
|
74600
|
-
const name =
|
|
75226
|
+
const name = import_node_path17.posix.basename(path2).toLowerCase();
|
|
74601
75227
|
if (SECRET_BASENAMES.has(name) || name.startsWith(".env."))
|
|
74602
75228
|
return true;
|
|
74603
75229
|
return name.endsWith(".private-key.pem") || name.endsWith(".private-key.key");
|
|
@@ -74625,12 +75251,12 @@ function classifyFileKind(path2) {
|
|
|
74625
75251
|
return "reference";
|
|
74626
75252
|
if (path2.startsWith("assets/"))
|
|
74627
75253
|
return "asset";
|
|
74628
|
-
if (/^(?:licen[cs]e|copying|notice)(?:\.|$)/i.test(
|
|
75254
|
+
if (/^(?:licen[cs]e|copying|notice)(?:\.|$)/i.test(import_node_path17.posix.basename(path2)))
|
|
74629
75255
|
return "license";
|
|
74630
75256
|
return "other";
|
|
74631
75257
|
}
|
|
74632
75258
|
function classifyPreview(path2, bytes) {
|
|
74633
|
-
const extension =
|
|
75259
|
+
const extension = import_node_path17.posix.extname(path2).toLowerCase();
|
|
74634
75260
|
if (IMAGE_EXTENSIONS.has(extension))
|
|
74635
75261
|
return "image";
|
|
74636
75262
|
if (bytes.includes(0))
|
|
@@ -74663,7 +75289,7 @@ function parseSkillMetadata(bytes, expectedName) {
|
|
|
74663
75289
|
} catch (error61) {
|
|
74664
75290
|
throw new SkillPackageError("INVALID_FRONTMATTER", `SKILL.md frontmatter is not valid YAML: ${error61 instanceof Error ? error61.message : String(error61)}`, "SKILL.md");
|
|
74665
75291
|
}
|
|
74666
|
-
if (!
|
|
75292
|
+
if (!isRecord3(parsed)) {
|
|
74667
75293
|
throw new SkillPackageError("INVALID_FRONTMATTER", "SKILL.md frontmatter must be a YAML mapping", "SKILL.md");
|
|
74668
75294
|
}
|
|
74669
75295
|
const admitted = skillFrontmatterAdmissionSchema.safeParse(parsed);
|
|
@@ -74734,7 +75360,7 @@ function normalizePackageReference(value2) {
|
|
|
74734
75360
|
const withoutSuffix = trimmed.split(/[?#]/, 1)[0];
|
|
74735
75361
|
if (!withoutSuffix)
|
|
74736
75362
|
return null;
|
|
74737
|
-
const normalized =
|
|
75363
|
+
const normalized = import_node_path17.posix.normalize(withoutSuffix).replace(/^\.\//, "").normalize("NFC");
|
|
74738
75364
|
if (normalized === ".." || normalized.startsWith("../"))
|
|
74739
75365
|
return null;
|
|
74740
75366
|
return normalized;
|
|
@@ -74742,14 +75368,14 @@ function normalizePackageReference(value2) {
|
|
|
74742
75368
|
function looksLikeFileReference(value2) {
|
|
74743
75369
|
return !/\s/.test(value2) && (value2.includes("/") || value2.includes("."));
|
|
74744
75370
|
}
|
|
74745
|
-
function
|
|
75371
|
+
function isRecord3(value2) {
|
|
74746
75372
|
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
74747
75373
|
}
|
|
74748
75374
|
function comparePackageFiles(left, right) {
|
|
74749
75375
|
return import_node_buffer.Buffer.compare(import_node_buffer.Buffer.from(left.manifest.path, "utf8"), import_node_buffer.Buffer.from(right.manifest.path, "utf8"));
|
|
74750
75376
|
}
|
|
74751
75377
|
function hashPackageTree(files) {
|
|
74752
|
-
const hash2 = (0,
|
|
75378
|
+
const hash2 = (0, import_node_crypto11.createHash)("sha256");
|
|
74753
75379
|
hash2.update(TREE_HASH_PREFIX);
|
|
74754
75380
|
for (const file2 of files) {
|
|
74755
75381
|
const path2 = import_node_buffer.Buffer.from(file2.path, "utf8");
|
|
@@ -74762,7 +75388,7 @@ function hashPackageTree(files) {
|
|
|
74762
75388
|
return `sha256:${hash2.digest("hex")}`;
|
|
74763
75389
|
}
|
|
74764
75390
|
function sha256(bytes) {
|
|
74765
|
-
return `sha256:${(0,
|
|
75391
|
+
return `sha256:${(0, import_node_crypto11.createHash)("sha256").update(bytes).digest("hex")}`;
|
|
74766
75392
|
}
|
|
74767
75393
|
function uint322(value2) {
|
|
74768
75394
|
const buffer = import_node_buffer.Buffer.allocUnsafe(4);
|
|
@@ -74894,7 +75520,7 @@ var LocalSkillSource = class {
|
|
|
74894
75520
|
locations = /* @__PURE__ */ new Map();
|
|
74895
75521
|
inspectionCache = /* @__PURE__ */ new Map();
|
|
74896
75522
|
async scan(input2) {
|
|
74897
|
-
const attemptId = (0,
|
|
75523
|
+
const attemptId = (0, import_node_crypto12.randomUUID)();
|
|
74898
75524
|
const diagnostics = [];
|
|
74899
75525
|
const candidates = [];
|
|
74900
75526
|
const visitedDirectories = /* @__PURE__ */ new Set();
|
|
@@ -74971,9 +75597,9 @@ var LocalSkillSource = class {
|
|
|
74971
75597
|
*/
|
|
74972
75598
|
async scanRootAsPackage(resolvedRoot, ignoredPaths, mode) {
|
|
74973
75599
|
const directory = resolvedRoot.directory;
|
|
74974
|
-
if (!await pathExists((0,
|
|
75600
|
+
if (!await pathExists((0, import_node_path18.join)(directory, "SKILL.md"))) return null;
|
|
74975
75601
|
const canonicalRoot = await (0, import_promises2.realpath)(directory).catch(() => directory);
|
|
74976
|
-
const name =
|
|
75602
|
+
const name = import_node_path18.default.basename(canonicalRoot);
|
|
74977
75603
|
try {
|
|
74978
75604
|
const fingerprint = await fingerprintPackage(canonicalRoot);
|
|
74979
75605
|
const ignoreSignature = JSON.stringify(ignoredPaths);
|
|
@@ -75043,7 +75669,7 @@ var LocalSkillSource = class {
|
|
|
75043
75669
|
);
|
|
75044
75670
|
for (const entry of entries) {
|
|
75045
75671
|
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
75046
|
-
const candidatePath = (0,
|
|
75672
|
+
const candidatePath = (0, import_node_path18.join)(resolvedRoot.directory, entry.name);
|
|
75047
75673
|
const canonicalPath = await (0, import_promises2.realpath)(candidatePath).catch(() => null);
|
|
75048
75674
|
if (!canonicalPath) {
|
|
75049
75675
|
diagnostics.push({
|
|
@@ -75101,7 +75727,7 @@ var LocalSkillSource = class {
|
|
|
75101
75727
|
}
|
|
75102
75728
|
};
|
|
75103
75729
|
async function fingerprintPackage(root) {
|
|
75104
|
-
const hash2 = (0,
|
|
75730
|
+
const hash2 = (0, import_node_crypto12.createHash)("sha256").update("alan-skill-stat-fingerprint-v1\0");
|
|
75105
75731
|
await visit2(root, "");
|
|
75106
75732
|
return `sha256:${hash2.digest("hex")}`;
|
|
75107
75733
|
async function visit2(directory, relativeDirectory) {
|
|
@@ -75109,7 +75735,7 @@ async function fingerprintPackage(root) {
|
|
|
75109
75735
|
entries.sort((left, right) => import_node_buffer2.Buffer.compare(import_node_buffer2.Buffer.from(left.name), import_node_buffer2.Buffer.from(right.name)));
|
|
75110
75736
|
for (const entry of entries) {
|
|
75111
75737
|
const path2 = relativeDirectory ? `${relativeDirectory}/${entry.name}` : entry.name;
|
|
75112
|
-
const absolutePath = (0,
|
|
75738
|
+
const absolutePath = (0, import_node_path18.join)(directory, entry.name);
|
|
75113
75739
|
const metadata = await (0, import_promises2.lstat)(absolutePath);
|
|
75114
75740
|
hash2.update(path2.normalize("NFC")).update("\0").update(
|
|
75115
75741
|
entry.isDirectory() ? "d" : entry.isFile() ? "f" : entry.isSymbolicLink() ? "l" : "o"
|
|
@@ -75134,12 +75760,12 @@ async function canonicalDirectories(directories) {
|
|
|
75134
75760
|
return [...new Set(canonical)];
|
|
75135
75761
|
}
|
|
75136
75762
|
function isWithinDirectory(parent, candidate) {
|
|
75137
|
-
const path2 = (0,
|
|
75138
|
-
return path2 !== ".." && !path2.startsWith(`..${
|
|
75763
|
+
const path2 = (0, import_node_path18.relative)(parent, candidate);
|
|
75764
|
+
return path2 !== ".." && !path2.startsWith(`..${import_node_path18.sep}`) && !(0, import_node_path18.isAbsolute)(path2);
|
|
75139
75765
|
}
|
|
75140
75766
|
async function loadSkillSourceConfig(homeDirectory) {
|
|
75141
|
-
const configDirectory = (0,
|
|
75142
|
-
const configPath = (0,
|
|
75767
|
+
const configDirectory = (0, import_node_path18.join)(homeDirectory, ".alan");
|
|
75768
|
+
const configPath = (0, import_node_path18.join)(configDirectory, "config.toml");
|
|
75143
75769
|
await (0, import_promises2.mkdir)(configDirectory, { recursive: true, mode: 448 });
|
|
75144
75770
|
let created = false;
|
|
75145
75771
|
try {
|
|
@@ -75173,7 +75799,7 @@ async function resolveSkillScanInput(homeDirectory, declared) {
|
|
|
75173
75799
|
}
|
|
75174
75800
|
const { config: config2 } = loaded;
|
|
75175
75801
|
if (!config2.enabled) return { ...declared, roots: [], ignoredPaths: config2.ignoredPaths };
|
|
75176
|
-
const draftsPath = (0,
|
|
75802
|
+
const draftsPath = (0, import_node_path18.resolve)((0, import_node_path18.join)(homeDirectory, ".alan", "skill-sources"));
|
|
75177
75803
|
const enabledProviders = new Set(declared.enabledProviderKinds ?? []);
|
|
75178
75804
|
const configured = [];
|
|
75179
75805
|
for (const source of config2.sources) {
|
|
@@ -75181,7 +75807,7 @@ async function resolveSkillScanInput(homeDirectory, declared) {
|
|
|
75181
75807
|
continue;
|
|
75182
75808
|
}
|
|
75183
75809
|
configured.push(
|
|
75184
|
-
(0,
|
|
75810
|
+
(0, import_node_path18.resolve)(source.path) === draftsPath ? { kind: "drafts", path: source.path, label: "Alan drafts" } : {
|
|
75185
75811
|
kind: "configured",
|
|
75186
75812
|
declarationId: source.declarationId,
|
|
75187
75813
|
path: source.path,
|
|
@@ -75199,7 +75825,7 @@ async function resolveSkillScanInput(homeDirectory, declared) {
|
|
|
75199
75825
|
}
|
|
75200
75826
|
function parseSkillSourceConfig(content, homeDirectory) {
|
|
75201
75827
|
const parsed = parse5(content);
|
|
75202
|
-
if (!
|
|
75828
|
+
if (!isRecord4(parsed) || parsed.version !== 1 || !isRecord4(parsed.skills)) {
|
|
75203
75829
|
throw new Error("~/.alan/config.toml must contain version = 1 and a [skills] table");
|
|
75204
75830
|
}
|
|
75205
75831
|
const skills = parsed.skills;
|
|
@@ -75215,7 +75841,7 @@ function parseSkillSourceConfig(content, homeDirectory) {
|
|
|
75215
75841
|
throw new Error("~/.alan/config.toml must declare at least one [[skills.sources]] table");
|
|
75216
75842
|
}
|
|
75217
75843
|
const sources = skills.sources.map((value2, index) => {
|
|
75218
|
-
if (!
|
|
75844
|
+
if (!isRecord4(value2) || typeof value2.path !== "string" || typeof value2.label !== "string") {
|
|
75219
75845
|
throw new Error(
|
|
75220
75846
|
`skills.sources entry ${index + 1} must contain string path and label values`
|
|
75221
75847
|
);
|
|
@@ -75228,7 +75854,7 @@ function parseSkillSourceConfig(content, homeDirectory) {
|
|
|
75228
75854
|
value2.providers,
|
|
75229
75855
|
`skills.sources[${index}].providers`
|
|
75230
75856
|
);
|
|
75231
|
-
const relativeDirectory =
|
|
75857
|
+
const relativeDirectory = import_node_path18.default.relative(homeDirectory, sourcePath);
|
|
75232
75858
|
const inferredProviders = providerKindsForUserDirectory(relativeDirectory);
|
|
75233
75859
|
const declaredProviders = value2.providers === void 0 ? inferredProviders : [...new Set(explicitProviders.map((provider) => provider.trim()).filter(Boolean))];
|
|
75234
75860
|
const stale = staleCodexDeclaration(relativeDirectory, label, declaredProviders);
|
|
@@ -75249,7 +75875,7 @@ function parseSkillSourceConfig(content, homeDirectory) {
|
|
|
75249
75875
|
return { enabled, publishChanges, discoverRepositorySkills, ignoredPaths, sources };
|
|
75250
75876
|
}
|
|
75251
75877
|
function stableSourceDeclarationId(sourcePath) {
|
|
75252
|
-
const digest = (0,
|
|
75878
|
+
const digest = (0, import_node_crypto12.createHash)("sha256").update("alan-skill-source-v1\0").update(sourcePath.normalize("NFC")).digest("hex").slice(0, 24);
|
|
75253
75879
|
return `source-${digest}`;
|
|
75254
75880
|
}
|
|
75255
75881
|
function requiredSourceId(value2, field) {
|
|
@@ -75258,7 +75884,7 @@ function requiredSourceId(value2, field) {
|
|
|
75258
75884
|
}
|
|
75259
75885
|
return value2;
|
|
75260
75886
|
}
|
|
75261
|
-
function resolveSkillSourcePath(sourcePath, homeDirectory, pathApi =
|
|
75887
|
+
function resolveSkillSourcePath(sourcePath, homeDirectory, pathApi = import_node_path18.default) {
|
|
75262
75888
|
const trimmed = sourcePath.trim();
|
|
75263
75889
|
if (trimmed === "~") return pathApi.resolve(homeDirectory);
|
|
75264
75890
|
if (trimmed.startsWith("~/") || trimmed.startsWith("~\\")) {
|
|
@@ -75293,7 +75919,7 @@ function resolveScanRoots(roots, enabledProviderKinds) {
|
|
|
75293
75919
|
).entries()) {
|
|
75294
75920
|
resolved.push({
|
|
75295
75921
|
root,
|
|
75296
|
-
directory: (0,
|
|
75922
|
+
directory: (0, import_node_path18.join)(root.path, relativeDirectory),
|
|
75297
75923
|
order: `1:${root.repositoryId}:${String(index).padStart(2, "0")}`
|
|
75298
75924
|
});
|
|
75299
75925
|
}
|
|
@@ -75315,11 +75941,11 @@ function candidateOrigin(root) {
|
|
|
75315
75941
|
}
|
|
75316
75942
|
function createLocalKey(root, skillName) {
|
|
75317
75943
|
const rootIdentity = root.kind === "configured" ? `configured:${root.declarationId}` : root.kind === "repository" ? `repository:${root.repositoryId}` : "drafts";
|
|
75318
|
-
const digest = (0,
|
|
75944
|
+
const digest = (0, import_node_crypto12.createHash)("sha256").update("alan-local-skill-v1\0").update(rootIdentity).update("\0").update(skillName.normalize("NFC")).digest("hex");
|
|
75319
75945
|
return `local:${digest}`;
|
|
75320
75946
|
}
|
|
75321
75947
|
function hashInventory(candidates) {
|
|
75322
|
-
const hash2 = (0,
|
|
75948
|
+
const hash2 = (0, import_node_crypto12.createHash)("sha256");
|
|
75323
75949
|
hash2.update("alan-skill-inventory-v1\0");
|
|
75324
75950
|
for (const candidate of candidates) {
|
|
75325
75951
|
hash2.update(candidate.localKey);
|
|
@@ -75352,7 +75978,7 @@ async function pathExists(candidate) {
|
|
|
75352
75978
|
}
|
|
75353
75979
|
async function hasManagedMarker(directory) {
|
|
75354
75980
|
try {
|
|
75355
|
-
await (0, import_promises2.access)((0,
|
|
75981
|
+
await (0, import_promises2.access)((0, import_node_path18.join)(directory, ".alan-managed.json"));
|
|
75356
75982
|
return true;
|
|
75357
75983
|
} catch {
|
|
75358
75984
|
return false;
|
|
@@ -75371,7 +75997,7 @@ function isMissingPath(error61) {
|
|
|
75371
75997
|
function isAlreadyPresent(error61) {
|
|
75372
75998
|
return typeof error61 === "object" && error61 !== null && "code" in error61 && error61.code === "EEXIST";
|
|
75373
75999
|
}
|
|
75374
|
-
function
|
|
76000
|
+
function isRecord4(value2) {
|
|
75375
76001
|
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
75376
76002
|
}
|
|
75377
76003
|
|
|
@@ -75505,8 +76131,8 @@ var RuntimeSkillCoordinator = class {
|
|
|
75505
76131
|
async runExclusively(work) {
|
|
75506
76132
|
const previous = this.tail;
|
|
75507
76133
|
let release2;
|
|
75508
|
-
this.tail = new Promise((
|
|
75509
|
-
release2 =
|
|
76134
|
+
this.tail = new Promise((resolve15) => {
|
|
76135
|
+
release2 = resolve15;
|
|
75510
76136
|
});
|
|
75511
76137
|
await previous;
|
|
75512
76138
|
try {
|
|
@@ -75519,14 +76145,14 @@ var RuntimeSkillCoordinator = class {
|
|
|
75519
76145
|
|
|
75520
76146
|
// src/skills/runtime-skill-manager.ts
|
|
75521
76147
|
var import_node_buffer5 = require("buffer");
|
|
75522
|
-
var
|
|
76148
|
+
var import_node_crypto15 = require("crypto");
|
|
75523
76149
|
var import_promises5 = require("fs/promises");
|
|
75524
|
-
var
|
|
76150
|
+
var import_node_path21 = require("path");
|
|
75525
76151
|
|
|
75526
76152
|
// src/skills/managed-skill-store.ts
|
|
75527
|
-
var
|
|
76153
|
+
var import_node_crypto13 = require("crypto");
|
|
75528
76154
|
var import_promises3 = require("fs/promises");
|
|
75529
|
-
var
|
|
76155
|
+
var import_node_path19 = require("path");
|
|
75530
76156
|
var EMPTY_STATE = {
|
|
75531
76157
|
version: 1,
|
|
75532
76158
|
sources: {},
|
|
@@ -75545,7 +76171,7 @@ var ManagedSkillStore = class _ManagedSkillStore {
|
|
|
75545
76171
|
static async open(baseDirectory) {
|
|
75546
76172
|
await (0, import_promises3.mkdir)(baseDirectory, { recursive: true, mode: 448 });
|
|
75547
76173
|
try {
|
|
75548
|
-
const parsed = JSON.parse(await (0, import_promises3.readFile)((0,
|
|
76174
|
+
const parsed = JSON.parse(await (0, import_promises3.readFile)((0, import_node_path19.join)(baseDirectory, "state.json"), "utf8"));
|
|
75549
76175
|
return new _ManagedSkillStore(baseDirectory, parseState(parsed), "verified");
|
|
75550
76176
|
} catch (error61) {
|
|
75551
76177
|
if (isMissingPath2(error61)) {
|
|
@@ -75644,10 +76270,10 @@ var ManagedSkillStore = class _ManagedSkillStore {
|
|
|
75644
76270
|
this.canRemoveManagedFiles = true;
|
|
75645
76271
|
}
|
|
75646
76272
|
async persist() {
|
|
75647
|
-
const statePath = (0,
|
|
75648
|
-
const pendingPath = (0,
|
|
76273
|
+
const statePath = (0, import_node_path19.join)(this.baseDirectory, "state.json");
|
|
76274
|
+
const pendingPath = (0, import_node_path19.join)(
|
|
75649
76275
|
this.baseDirectory,
|
|
75650
|
-
`.state.json.pending-${process.pid}-${(0,
|
|
76276
|
+
`.state.json.pending-${process.pid}-${(0, import_node_crypto13.randomBytes)(6).toString("hex")}`
|
|
75651
76277
|
);
|
|
75652
76278
|
try {
|
|
75653
76279
|
await (0, import_promises3.writeFile)(pendingPath, `${JSON.stringify(this.state, null, 2)}
|
|
@@ -75663,13 +76289,13 @@ var ManagedSkillStore = class _ManagedSkillStore {
|
|
|
75663
76289
|
}
|
|
75664
76290
|
};
|
|
75665
76291
|
function parseState(value2) {
|
|
75666
|
-
if (!
|
|
75667
|
-
if (!
|
|
76292
|
+
if (!isRecord5(value2) || value2.version !== 1) throw new Error("unsupported Skill state version");
|
|
76293
|
+
if (!isRecord5(value2.sources) || !isRecord5(value2.projections)) {
|
|
75668
76294
|
throw new Error("invalid Skill state maps");
|
|
75669
76295
|
}
|
|
75670
76296
|
const sources = {};
|
|
75671
76297
|
for (const [localKey, source] of Object.entries(value2.sources)) {
|
|
75672
|
-
if (!localKey.startsWith("local:") || !
|
|
76298
|
+
if (!localKey.startsWith("local:") || !isRecord5(source) || typeof source.absolutePath !== "string" || !isSha256(source.contentHash)) {
|
|
75673
76299
|
throw new Error("invalid Skill source state");
|
|
75674
76300
|
}
|
|
75675
76301
|
const publication = parsePublication(source.publication, source.contentHash);
|
|
@@ -75681,7 +76307,7 @@ function parseState(value2) {
|
|
|
75681
76307
|
}
|
|
75682
76308
|
const projections = {};
|
|
75683
76309
|
for (const [directory, projection] of Object.entries(value2.projections)) {
|
|
75684
|
-
if (!directory || !
|
|
76310
|
+
if (!directory || !isRecord5(projection) || typeof projection.skillId !== "string" || typeof projection.versionId !== "string" || !isSha256(projection.contentHash)) {
|
|
75685
76311
|
throw new Error("invalid Skill projection state");
|
|
75686
76312
|
}
|
|
75687
76313
|
projections[directory] = {
|
|
@@ -75702,7 +76328,7 @@ function parseState(value2) {
|
|
|
75702
76328
|
}
|
|
75703
76329
|
function parsePublication(value2, sourceHash) {
|
|
75704
76330
|
if (value2 === void 0) return void 0;
|
|
75705
|
-
if (!
|
|
76331
|
+
if (!isRecord5(value2) || typeof value2.skillId !== "string" || typeof value2.versionId !== "string" || !isSha256(value2.contentHash) || value2.contentHash !== sourceHash) {
|
|
75706
76332
|
throw new Error("invalid Skill source publication state");
|
|
75707
76333
|
}
|
|
75708
76334
|
return {
|
|
@@ -75719,7 +76345,7 @@ function optionalSha256(value2) {
|
|
|
75719
76345
|
function isSha256(value2) {
|
|
75720
76346
|
return typeof value2 === "string" && /^sha256:[a-f0-9]{64}$/.test(value2);
|
|
75721
76347
|
}
|
|
75722
|
-
function
|
|
76348
|
+
function isRecord5(value2) {
|
|
75723
76349
|
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
75724
76350
|
}
|
|
75725
76351
|
function isMissingPath2(error61) {
|
|
@@ -75728,9 +76354,9 @@ function isMissingPath2(error61) {
|
|
|
75728
76354
|
|
|
75729
76355
|
// ../shared/dist/node/skill-bundle.js
|
|
75730
76356
|
var import_node_buffer3 = require("buffer");
|
|
75731
|
-
var
|
|
76357
|
+
var import_node_crypto14 = require("crypto");
|
|
75732
76358
|
var import_promises4 = require("fs/promises");
|
|
75733
|
-
var
|
|
76359
|
+
var import_node_path20 = require("path");
|
|
75734
76360
|
var import_node_stream = require("stream");
|
|
75735
76361
|
var import_node_zlib = require("zlib");
|
|
75736
76362
|
var import_tar_stream = __toESM(require_tar_stream(), 1);
|
|
@@ -75739,7 +76365,7 @@ async function createSkillBundle(packageRoot) {
|
|
|
75739
76365
|
const archive = (0, import_tar_stream.pack)();
|
|
75740
76366
|
const tarPromise = collectStream(archive);
|
|
75741
76367
|
for (const file2 of before.files) {
|
|
75742
|
-
const bytes2 = await (0, import_promises4.readFile)((0,
|
|
76368
|
+
const bytes2 = await (0, import_promises4.readFile)((0, import_node_path20.join)(packageRoot, ...file2.path.split("/")));
|
|
75743
76369
|
await addArchiveEntry(archive, {
|
|
75744
76370
|
name: file2.path,
|
|
75745
76371
|
type: "file",
|
|
@@ -75790,8 +76416,8 @@ async function collectStream(stream) {
|
|
|
75790
76416
|
return import_node_buffer3.Buffer.concat(chunks);
|
|
75791
76417
|
}
|
|
75792
76418
|
function addArchiveEntry(archive, header, bytes) {
|
|
75793
|
-
return new Promise((
|
|
75794
|
-
archive.entry(header, bytes, (error61) => error61 ? reject(error61) :
|
|
76419
|
+
return new Promise((resolve15, reject) => {
|
|
76420
|
+
archive.entry(header, bytes, (error61) => error61 ? reject(error61) : resolve15());
|
|
75795
76421
|
});
|
|
75796
76422
|
}
|
|
75797
76423
|
async function decompressBounded(archiveBytes) {
|
|
@@ -75817,7 +76443,7 @@ async function extractTar(tarBytes, destination) {
|
|
|
75817
76443
|
const extractor = (0, import_tar_stream.extract)();
|
|
75818
76444
|
let fileCount = 0;
|
|
75819
76445
|
let totalBytes = 0;
|
|
75820
|
-
const completion = new Promise((
|
|
76446
|
+
const completion = new Promise((resolve15, reject) => {
|
|
75821
76447
|
extractor.on("entry", (header, stream, next) => {
|
|
75822
76448
|
void extractEntry(header, stream, destination, {
|
|
75823
76449
|
addFile(size) {
|
|
@@ -75832,7 +76458,7 @@ async function extractTar(tarBytes, destination) {
|
|
|
75832
76458
|
}
|
|
75833
76459
|
}).then(next, (error61) => extractor.destroy(error61 instanceof Error ? error61 : new Error(String(error61))));
|
|
75834
76460
|
});
|
|
75835
|
-
extractor.once("finish",
|
|
76461
|
+
extractor.once("finish", resolve15);
|
|
75836
76462
|
extractor.once("error", reject);
|
|
75837
76463
|
});
|
|
75838
76464
|
import_node_stream.Readable.from(tarBytes).pipe(extractor);
|
|
@@ -75854,19 +76480,19 @@ async function extractEntry(header, stream, destination, limits) {
|
|
|
75854
76480
|
if (bytes.byteLength !== declaredSize) {
|
|
75855
76481
|
throw new Error(`Skill archive file size is invalid: ${path2}`);
|
|
75856
76482
|
}
|
|
75857
|
-
const outputPath = (0,
|
|
75858
|
-
await (0, import_promises4.mkdir)((0,
|
|
76483
|
+
const outputPath = (0, import_node_path20.join)(destination, ...path2.split("/"));
|
|
76484
|
+
await (0, import_promises4.mkdir)((0, import_node_path20.dirname)(outputPath), { recursive: true, mode: 448 });
|
|
75859
76485
|
await (0, import_promises4.writeFile)(outputPath, bytes, { mode: (header.mode ?? 420) & 73 ? 493 : 420 });
|
|
75860
76486
|
}
|
|
75861
76487
|
function requireSafeArchivePath(value2) {
|
|
75862
76488
|
const normalized = value2.replaceAll("\\", "/").normalize("NFC");
|
|
75863
|
-
if (!normalized || normalized.startsWith("/") || normalized.includes("\0") ||
|
|
76489
|
+
if (!normalized || normalized.startsWith("/") || normalized.includes("\0") || import_node_path20.posix.normalize(normalized) !== normalized || normalized === ".." || normalized.startsWith("../")) {
|
|
75864
76490
|
throw new Error(`Skill archive contains an unsafe path: ${value2}`);
|
|
75865
76491
|
}
|
|
75866
76492
|
return normalized;
|
|
75867
76493
|
}
|
|
75868
76494
|
function sha2562(bytes) {
|
|
75869
|
-
return `sha256:${(0,
|
|
76495
|
+
return `sha256:${(0, import_node_crypto14.createHash)("sha256").update(bytes).digest("hex")}`;
|
|
75870
76496
|
}
|
|
75871
76497
|
|
|
75872
76498
|
// src/skills/skill-control-client.ts
|
|
@@ -75939,14 +76565,14 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
75939
76565
|
}
|
|
75940
76566
|
reconcileTail = Promise.resolve();
|
|
75941
76567
|
static async open(options) {
|
|
75942
|
-
const skillsStateDirectory = (0,
|
|
75943
|
-
const managedSkillsDirectory = (0,
|
|
76568
|
+
const skillsStateDirectory = (0, import_node_path21.join)(options.homeDirectory, ".alan", "skills");
|
|
76569
|
+
const managedSkillsDirectory = (0, import_node_path21.join)(options.homeDirectory, ".agents", "skills");
|
|
75944
76570
|
const store = await ManagedSkillStore.open(skillsStateDirectory);
|
|
75945
76571
|
return new _RuntimeSkillManager(
|
|
75946
76572
|
skillsStateDirectory,
|
|
75947
76573
|
managedSkillsDirectory,
|
|
75948
|
-
(0,
|
|
75949
|
-
(0,
|
|
76574
|
+
(0, import_node_path21.join)(options.homeDirectory, ".claude", "skills"),
|
|
76575
|
+
(0, import_node_path21.join)(options.homeDirectory, ".alan", "sync-manifest.json"),
|
|
75950
76576
|
store,
|
|
75951
76577
|
options.downloadBundle ?? downloadBundleWithFetch,
|
|
75952
76578
|
options.localSource ?? new LocalSkillSource(),
|
|
@@ -76105,7 +76731,7 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
76105
76731
|
if (!runtimeDirectory) {
|
|
76106
76732
|
throw new Error("Selected Skill version is not installed on this runtime");
|
|
76107
76733
|
}
|
|
76108
|
-
const packageDirectory = (0,
|
|
76734
|
+
const packageDirectory = (0, import_node_path21.join)(this.managedSkillsDirectory, runtimeDirectory);
|
|
76109
76735
|
const marker = await readMarker(packageDirectory);
|
|
76110
76736
|
if (!marker || marker.skillId !== invocation.skillId || marker.versionId !== invocation.versionId) {
|
|
76111
76737
|
throw new Error("Selected Skill projection is missing or no longer owned by Alan");
|
|
@@ -76119,11 +76745,11 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
76119
76745
|
async reconcileUnlocked(manifest) {
|
|
76120
76746
|
const desired = desiredSkillManifestSchema.parse(manifest);
|
|
76121
76747
|
await (0, import_promises5.mkdir)(this.managedSkillsDirectory, { recursive: true, mode: 448 });
|
|
76122
|
-
await (0, import_promises5.mkdir)((0,
|
|
76748
|
+
await (0, import_promises5.mkdir)((0, import_node_path21.join)(this.skillsStateDirectory, "cache", "sha256"), {
|
|
76123
76749
|
recursive: true,
|
|
76124
76750
|
mode: 448
|
|
76125
76751
|
});
|
|
76126
|
-
await (0, import_promises5.mkdir)((0,
|
|
76752
|
+
await (0, import_promises5.mkdir)((0, import_node_path21.join)(this.skillsStateDirectory, "tmp"), { recursive: true, mode: 448 });
|
|
76127
76753
|
const results = [];
|
|
76128
76754
|
const nextProjections = {};
|
|
76129
76755
|
for (const entry of desired.skills) {
|
|
@@ -76168,13 +76794,13 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
76168
76794
|
await this.retireLegacySyncManifest();
|
|
76169
76795
|
}
|
|
76170
76796
|
return {
|
|
76171
|
-
attemptId: (0,
|
|
76797
|
+
attemptId: (0, import_node_crypto15.randomUUID)(),
|
|
76172
76798
|
desiredRevision: desired.revision,
|
|
76173
76799
|
skills: results
|
|
76174
76800
|
};
|
|
76175
76801
|
}
|
|
76176
76802
|
async reuseCurrentProjection(entry) {
|
|
76177
|
-
const destination = (0,
|
|
76803
|
+
const destination = (0, import_node_path21.join)(this.managedSkillsDirectory, entry.runtimeDirectory);
|
|
76178
76804
|
const marker = await readMarker(destination);
|
|
76179
76805
|
if (marker?.skillId !== entry.skillId || marker.versionId !== entry.versionId || marker.contentHash !== entry.contentHash || !await packageMatches(destination, entry.contentHash)) {
|
|
76180
76806
|
return null;
|
|
@@ -76183,19 +76809,19 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
76183
76809
|
return entry.contentHash;
|
|
76184
76810
|
}
|
|
76185
76811
|
async adoptLegacyProjection(entry) {
|
|
76186
|
-
const destination = (0,
|
|
76812
|
+
const destination = (0, import_node_path21.join)(this.managedSkillsDirectory, entry.runtimeDirectory);
|
|
76187
76813
|
if (await pathExists2(destination)) return;
|
|
76188
76814
|
const manifest = await readLegacyManifest(this.legacyManifestPath);
|
|
76189
76815
|
const legacy = manifest?.skills[entry.slug];
|
|
76190
76816
|
if (!legacy) return;
|
|
76191
|
-
const source = (0,
|
|
76817
|
+
const source = (0, import_node_path21.join)(this.managedSkillsDirectory, entry.slug);
|
|
76192
76818
|
if (!await legacyFileMatches(source, legacy.hash) || !await packageMatches(source, entry.contentHash)) {
|
|
76193
76819
|
return;
|
|
76194
76820
|
}
|
|
76195
76821
|
await (0, import_promises5.rename)(source, destination);
|
|
76196
76822
|
try {
|
|
76197
76823
|
await (0, import_promises5.writeFile)(
|
|
76198
|
-
(0,
|
|
76824
|
+
(0, import_node_path21.join)(destination, ".alan-managed.json"),
|
|
76199
76825
|
`${JSON.stringify(markerFor(entry), null, 2)}
|
|
76200
76826
|
`,
|
|
76201
76827
|
{ encoding: "utf8", flag: "wx", mode: 384 }
|
|
@@ -76204,19 +76830,19 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
76204
76830
|
await (0, import_promises5.rename)(destination, source).catch(() => void 0);
|
|
76205
76831
|
throw error61;
|
|
76206
76832
|
}
|
|
76207
|
-
await removeExactLegacySkill((0,
|
|
76833
|
+
await removeExactLegacySkill((0, import_node_path21.join)(this.compatibilitySkillsDirectory, entry.slug), legacy.hash);
|
|
76208
76834
|
}
|
|
76209
76835
|
async retireLegacySyncManifest() {
|
|
76210
76836
|
const manifest = await readLegacyManifest(this.legacyManifestPath);
|
|
76211
76837
|
if (!manifest) return;
|
|
76212
76838
|
for (const [command, entry] of Object.entries(manifest.skills)) {
|
|
76213
|
-
await removeExactLegacySkill((0,
|
|
76214
|
-
await removeExactLegacySkill((0,
|
|
76839
|
+
await removeExactLegacySkill((0, import_node_path21.join)(this.managedSkillsDirectory, command), entry.hash);
|
|
76840
|
+
await removeExactLegacySkill((0, import_node_path21.join)(this.compatibilitySkillsDirectory, command), entry.hash);
|
|
76215
76841
|
}
|
|
76216
76842
|
await (0, import_promises5.rm)(this.legacyManifestPath, { force: true });
|
|
76217
76843
|
}
|
|
76218
76844
|
async ensureCached(entry) {
|
|
76219
|
-
const cacheDirectory = (0,
|
|
76845
|
+
const cacheDirectory = (0, import_node_path21.join)(
|
|
76220
76846
|
this.skillsStateDirectory,
|
|
76221
76847
|
"cache",
|
|
76222
76848
|
"sha256",
|
|
@@ -76230,10 +76856,10 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
76230
76856
|
`Downloaded Skill bundle length ${bytes.byteLength} does not match ${entry.compressedBytes}`
|
|
76231
76857
|
);
|
|
76232
76858
|
}
|
|
76233
|
-
const pendingDirectory = (0,
|
|
76859
|
+
const pendingDirectory = (0, import_node_path21.join)(
|
|
76234
76860
|
this.skillsStateDirectory,
|
|
76235
76861
|
"tmp",
|
|
76236
|
-
`cache-${process.pid}-${(0,
|
|
76862
|
+
`cache-${process.pid}-${(0, import_node_crypto15.randomBytes)(8).toString("hex")}`
|
|
76237
76863
|
);
|
|
76238
76864
|
try {
|
|
76239
76865
|
await extractSkillBundle(bytes, pendingDirectory, {
|
|
@@ -76251,22 +76877,22 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
76251
76877
|
return cacheDirectory;
|
|
76252
76878
|
}
|
|
76253
76879
|
async project(entry, cacheDirectory) {
|
|
76254
|
-
const destination = (0,
|
|
76880
|
+
const destination = (0, import_node_path21.join)(this.managedSkillsDirectory, entry.runtimeDirectory);
|
|
76255
76881
|
const existing = await readMarker(destination);
|
|
76256
76882
|
if (existing?.skillId === entry.skillId && existing.versionId === entry.versionId && existing.contentHash === entry.contentHash && await packageMatches(destination, entry.contentHash)) {
|
|
76257
76883
|
await this.ensureCompatibilityProjection(entry, destination);
|
|
76258
76884
|
return entry.contentHash;
|
|
76259
76885
|
}
|
|
76260
|
-
const suffix = `${process.pid}-${(0,
|
|
76261
|
-
const pending = (0,
|
|
76262
|
-
const backup = (0,
|
|
76886
|
+
const suffix = `${process.pid}-${(0, import_node_crypto15.randomBytes)(8).toString("hex")}`;
|
|
76887
|
+
const pending = (0, import_node_path21.join)(this.managedSkillsDirectory, `.pending-${suffix}`);
|
|
76888
|
+
const backup = (0, import_node_path21.join)(this.managedSkillsDirectory, `.backup-${suffix}`);
|
|
76263
76889
|
let movedExisting = false;
|
|
76264
76890
|
let installedPending = false;
|
|
76265
76891
|
let completed = false;
|
|
76266
76892
|
try {
|
|
76267
76893
|
await (0, import_promises5.cp)(cacheDirectory, pending, { recursive: true, errorOnExist: true, force: false });
|
|
76268
76894
|
await (0, import_promises5.writeFile)(
|
|
76269
|
-
(0,
|
|
76895
|
+
(0, import_node_path21.join)(pending, ".alan-managed.json"),
|
|
76270
76896
|
`${JSON.stringify(markerFor(entry), null, 2)}
|
|
76271
76897
|
`,
|
|
76272
76898
|
{ encoding: "utf8", flag: "wx", mode: 384 }
|
|
@@ -76307,7 +76933,7 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
76307
76933
|
}
|
|
76308
76934
|
}
|
|
76309
76935
|
async requireReplaceable(entry) {
|
|
76310
|
-
const destination = (0,
|
|
76936
|
+
const destination = (0, import_node_path21.join)(this.managedSkillsDirectory, entry.runtimeDirectory);
|
|
76311
76937
|
if (!await pathExists2(destination)) return;
|
|
76312
76938
|
const marker = await readMarker(destination);
|
|
76313
76939
|
if (!marker) throw unmanagedPathConflict(entry.runtimeDirectory);
|
|
@@ -76324,8 +76950,8 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
76324
76950
|
if (!this.store.inspect().canRemoveManagedFiles) return failures;
|
|
76325
76951
|
for (const [runtimeDirectory, recorded] of Object.entries(this.store.projections())) {
|
|
76326
76952
|
if (desired[runtimeDirectory]) continue;
|
|
76327
|
-
const destination = (0,
|
|
76328
|
-
const compatibility = (0,
|
|
76953
|
+
const destination = (0, import_node_path21.join)(this.managedSkillsDirectory, runtimeDirectory);
|
|
76954
|
+
const compatibility = (0, import_node_path21.join)(this.compatibilitySkillsDirectory, runtimeDirectory);
|
|
76329
76955
|
const marker = await readMarker(destination);
|
|
76330
76956
|
if (!await pathExists2(destination)) continue;
|
|
76331
76957
|
if (marker && markerMatchesProjection(marker, recorded)) {
|
|
@@ -76356,7 +76982,7 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
76356
76982
|
}
|
|
76357
76983
|
async ensureCompatibilityProjection(entry, canonicalDirectory) {
|
|
76358
76984
|
await (0, import_promises5.mkdir)(this.compatibilitySkillsDirectory, { recursive: true, mode: 448 });
|
|
76359
|
-
const destination = (0,
|
|
76985
|
+
const destination = (0, import_node_path21.join)(this.compatibilitySkillsDirectory, entry.runtimeDirectory);
|
|
76360
76986
|
if (await pathExists2(destination)) {
|
|
76361
76987
|
if (await compatibilityProjectionMatches(destination, canonicalDirectory, entry)) return;
|
|
76362
76988
|
throw unmanagedCompatibilityConflict(entry.runtimeDirectory);
|
|
@@ -76367,11 +76993,11 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
76367
76993
|
} catch (error61) {
|
|
76368
76994
|
if (!isSymlinkUnsupported(error61)) throw error61;
|
|
76369
76995
|
}
|
|
76370
|
-
const pending = `${destination}.pending-${process.pid}-${(0,
|
|
76996
|
+
const pending = `${destination}.pending-${process.pid}-${(0, import_node_crypto15.randomBytes)(6).toString("hex")}`;
|
|
76371
76997
|
try {
|
|
76372
76998
|
await (0, import_promises5.cp)(canonicalDirectory, pending, { recursive: true, errorOnExist: true, force: false });
|
|
76373
76999
|
await (0, import_promises5.writeFile)(
|
|
76374
|
-
(0,
|
|
77000
|
+
(0, import_node_path21.join)(pending, ".alan-compatibility.json"),
|
|
76375
77001
|
`${JSON.stringify({ ...markerFor(entry), canonicalDirectory }, null, 2)}
|
|
76376
77002
|
`,
|
|
76377
77003
|
{ encoding: "utf8", flag: "wx", mode: 384 }
|
|
@@ -76384,8 +77010,8 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
76384
77010
|
async runExclusively(work) {
|
|
76385
77011
|
const previous = this.reconcileTail;
|
|
76386
77012
|
let release2;
|
|
76387
|
-
this.reconcileTail = new Promise((
|
|
76388
|
-
release2 =
|
|
77013
|
+
this.reconcileTail = new Promise((resolve15) => {
|
|
77014
|
+
release2 = resolve15;
|
|
76389
77015
|
});
|
|
76390
77016
|
await previous;
|
|
76391
77017
|
try {
|
|
@@ -76396,14 +77022,14 @@ var RuntimeSkillManager = class _RuntimeSkillManager {
|
|
|
76396
77022
|
}
|
|
76397
77023
|
async withMachineLock(work) {
|
|
76398
77024
|
await (0, import_promises5.mkdir)(this.skillsStateDirectory, { recursive: true, mode: 448 });
|
|
76399
|
-
const lockDirectory = (0,
|
|
77025
|
+
const lockDirectory = (0, import_node_path21.join)(this.skillsStateDirectory, "manager.lock");
|
|
76400
77026
|
const deadline = Date.now() + 3e4;
|
|
76401
77027
|
while (true) {
|
|
76402
77028
|
try {
|
|
76403
77029
|
await (0, import_promises5.mkdir)(lockDirectory, { mode: 448 });
|
|
76404
77030
|
try {
|
|
76405
77031
|
await (0, import_promises5.writeFile)(
|
|
76406
|
-
(0,
|
|
77032
|
+
(0, import_node_path21.join)(lockDirectory, "owner.json"),
|
|
76407
77033
|
`${JSON.stringify({ pid: process.pid, acquiredAt: (/* @__PURE__ */ new Date()).toISOString() })}
|
|
76408
77034
|
`,
|
|
76409
77035
|
{ encoding: "utf8", flag: "wx", mode: 384 }
|
|
@@ -76453,7 +77079,7 @@ async function packageMatches(directory, expectedHash) {
|
|
|
76453
77079
|
async function readMarker(directory) {
|
|
76454
77080
|
try {
|
|
76455
77081
|
const value2 = JSON.parse(
|
|
76456
|
-
await (0, import_promises5.readFile)((0,
|
|
77082
|
+
await (0, import_promises5.readFile)((0, import_node_path21.join)(directory, ".alan-managed.json"), "utf8")
|
|
76457
77083
|
);
|
|
76458
77084
|
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)) {
|
|
76459
77085
|
return null;
|
|
@@ -76467,7 +77093,7 @@ async function compatibilityProjectionMatches(destination, canonicalDirectory, e
|
|
|
76467
77093
|
const metadata = await (0, import_promises5.lstat)(destination);
|
|
76468
77094
|
if (metadata.isSymbolicLink()) {
|
|
76469
77095
|
const target = await (0, import_promises5.readlink)(destination);
|
|
76470
|
-
return (0,
|
|
77096
|
+
return (0, import_node_path21.resolve)((0, import_node_path21.dirname)(destination), target) === (0, import_node_path21.resolve)(canonicalDirectory);
|
|
76471
77097
|
}
|
|
76472
77098
|
if (!metadata.isDirectory()) return false;
|
|
76473
77099
|
const marker = await readCompatibilityMarker(destination);
|
|
@@ -76478,7 +77104,7 @@ async function removeCompatibilityProjection(destination, canonicalDirectory, pr
|
|
|
76478
77104
|
const metadata = await (0, import_promises5.lstat)(destination);
|
|
76479
77105
|
if (metadata.isSymbolicLink()) {
|
|
76480
77106
|
const target = await (0, import_promises5.readlink)(destination);
|
|
76481
|
-
if ((0,
|
|
77107
|
+
if ((0, import_node_path21.resolve)((0, import_node_path21.dirname)(destination), target) !== (0, import_node_path21.resolve)(canonicalDirectory)) return false;
|
|
76482
77108
|
await (0, import_promises5.rm)(destination, { force: true });
|
|
76483
77109
|
return true;
|
|
76484
77110
|
}
|
|
@@ -76490,7 +77116,7 @@ async function removeCompatibilityProjection(destination, canonicalDirectory, pr
|
|
|
76490
77116
|
async function readCompatibilityMarker(directory) {
|
|
76491
77117
|
try {
|
|
76492
77118
|
const value2 = JSON.parse(
|
|
76493
|
-
await (0, import_promises5.readFile)((0,
|
|
77119
|
+
await (0, import_promises5.readFile)((0, import_node_path21.join)(directory, ".alan-compatibility.json"), "utf8")
|
|
76494
77120
|
);
|
|
76495
77121
|
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") {
|
|
76496
77122
|
return null;
|
|
@@ -76530,15 +77156,15 @@ async function legacyFileMatches(directory, expectedHash) {
|
|
|
76530
77156
|
try {
|
|
76531
77157
|
const metadata = await (0, import_promises5.lstat)(directory);
|
|
76532
77158
|
if (!metadata.isDirectory() || metadata.isSymbolicLink()) return false;
|
|
76533
|
-
const content = await (0, import_promises5.readFile)((0,
|
|
76534
|
-
return (0,
|
|
77159
|
+
const content = await (0, import_promises5.readFile)((0, import_node_path21.join)(directory, "SKILL.md"));
|
|
77160
|
+
return (0, import_node_crypto15.createHash)("sha256").update(content).digest("hex").slice(0, 16) === expectedHash;
|
|
76535
77161
|
} catch {
|
|
76536
77162
|
return false;
|
|
76537
77163
|
}
|
|
76538
77164
|
}
|
|
76539
77165
|
async function removeExactLegacySkill(directory, expectedHash) {
|
|
76540
77166
|
if (!await legacyFileMatches(directory, expectedHash)) return;
|
|
76541
|
-
await (0, import_promises5.rm)((0,
|
|
77167
|
+
await (0, import_promises5.rm)((0, import_node_path21.join)(directory, "SKILL.md"), { force: true });
|
|
76542
77168
|
const remaining = await (0, import_promises5.readdir)(directory);
|
|
76543
77169
|
if (remaining.length === 0) await (0, import_promises5.rm)(directory, { recursive: true, force: true });
|
|
76544
77170
|
}
|
|
@@ -76656,10 +77282,10 @@ var SuspensionDetector = class {
|
|
|
76656
77282
|
async function startDaemon(args) {
|
|
76657
77283
|
bindConfigPath(args);
|
|
76658
77284
|
const stored = readConfig();
|
|
76659
|
-
const installationId = stored.installationId ?? (0,
|
|
77285
|
+
const installationId = stored.installationId ?? (0, import_node_crypto16.randomUUID)();
|
|
76660
77286
|
const configPath = getConfigPath();
|
|
76661
77287
|
const endpointProfile = stored.endpointProfile ?? resolveEndpointProfile(args);
|
|
76662
|
-
const daemonSessionId = (0,
|
|
77288
|
+
const daemonSessionId = (0, import_node_crypto16.randomUUID)();
|
|
76663
77289
|
let heartbeatSeq = 0;
|
|
76664
77290
|
const runtimeId = argValue(args, "--runtime-id") ?? process.env.ALAN_RUNTIME_ID ?? stored.runtimeId;
|
|
76665
77291
|
const runtimeTokenOverride = argValue(args, "--token") ?? process.env.ALAN_RUNTIME_TOKEN;
|
|
@@ -76676,11 +77302,11 @@ async function startDaemon(args) {
|
|
|
76676
77302
|
wsUrl = endpointMismatch.expectedWsUrl;
|
|
76677
77303
|
}
|
|
76678
77304
|
let localApiPort = getLocalApiPort(args, stored);
|
|
76679
|
-
const localApiToken = stored.localApiToken ?? (0,
|
|
76680
|
-
const localServiceId = stored.localServiceId ?? (0,
|
|
76681
|
-
const localServiceSecret = stored.localServiceSecret ?? (0,
|
|
77305
|
+
const localApiToken = stored.localApiToken ?? (0, import_node_crypto16.randomBytes)(24).toString("hex");
|
|
77306
|
+
const localServiceId = stored.localServiceId ?? (0, import_node_crypto16.randomUUID)();
|
|
77307
|
+
const localServiceSecret = stored.localServiceSecret ?? (0, import_node_crypto16.randomBytes)(32).toString("base64url");
|
|
76682
77308
|
const localServiceStartedAtMs = Date.now();
|
|
76683
|
-
const eventSpoolKey = stored.eventSpoolKey ?? (0,
|
|
77309
|
+
const eventSpoolKey = stored.eventSpoolKey ?? (0, import_node_crypto16.randomBytes)(32).toString("base64url");
|
|
76684
77310
|
if (stored.daemonEpoch !== void 0 && (!Number.isSafeInteger(stored.daemonEpoch) || stored.daemonEpoch < 0 || stored.daemonEpoch >= Number.MAX_SAFE_INTEGER)) {
|
|
76685
77311
|
throw new Error("daemon config contains an invalid durable epoch; repair the runtime config");
|
|
76686
77312
|
}
|
|
@@ -76778,17 +77404,17 @@ async function startDaemon(args) {
|
|
|
76778
77404
|
installationId
|
|
76779
77405
|
});
|
|
76780
77406
|
const recentLogs = [];
|
|
76781
|
-
const daemonLogPath = (0,
|
|
77407
|
+
const daemonLogPath = (0, import_node_path22.join)((0, import_node_path22.dirname)(configPath), "logs", "daemon.log");
|
|
76782
77408
|
const persistDaemonLog = (line) => {
|
|
76783
77409
|
try {
|
|
76784
|
-
(0,
|
|
77410
|
+
(0, import_node_fs20.mkdirSync)((0, import_node_path22.dirname)(daemonLogPath), { recursive: true });
|
|
76785
77411
|
try {
|
|
76786
|
-
if ((0,
|
|
76787
|
-
(0,
|
|
77412
|
+
if ((0, import_node_fs20.statSync)(daemonLogPath).size > 5 * 1024 * 1024) {
|
|
77413
|
+
(0, import_node_fs20.renameSync)(daemonLogPath, `${daemonLogPath}.1`);
|
|
76788
77414
|
}
|
|
76789
77415
|
} catch {
|
|
76790
77416
|
}
|
|
76791
|
-
(0,
|
|
77417
|
+
(0, import_node_fs20.appendFileSync)(daemonLogPath, `${line}
|
|
76792
77418
|
`, "utf8");
|
|
76793
77419
|
} catch {
|
|
76794
77420
|
}
|
|
@@ -76807,24 +77433,28 @@ async function startDaemon(args) {
|
|
|
76807
77433
|
const alanMcpRegisteredRuns = /* @__PURE__ */ new Map();
|
|
76808
77434
|
const alanMcpRegisteredBackends = /* @__PURE__ */ new Set();
|
|
76809
77435
|
const finalizeAlanMcpForRun = (runId) => {
|
|
76810
|
-
const registration = alanMcpRegisteredRuns.get(runId);
|
|
76811
77436
|
if (!alanMcpRegisteredRuns.delete(runId)) return;
|
|
76812
77437
|
if (alanMcpRegisteredRuns.size > 0) return;
|
|
76813
|
-
const backends = [...alanMcpRegisteredBackends];
|
|
76814
|
-
alanMcpRegisteredBackends.clear();
|
|
76815
77438
|
void (async () => {
|
|
76816
|
-
|
|
77439
|
+
const releaseRunStart = await runStartGate.acquire();
|
|
77440
|
+
try {
|
|
76817
77441
|
if (alanMcpRegisteredRuns.size > 0) return;
|
|
76818
|
-
|
|
76819
|
-
|
|
76820
|
-
|
|
76821
|
-
|
|
76822
|
-
|
|
76823
|
-
|
|
77442
|
+
const backends = [...alanMcpRegisteredBackends];
|
|
77443
|
+
alanMcpRegisteredBackends.clear();
|
|
77444
|
+
for (const backend of backends) {
|
|
77445
|
+
try {
|
|
77446
|
+
const result = await restoreStaticAlanMcp(backend, apiUrl);
|
|
77447
|
+
if (result.ok) {
|
|
77448
|
+
pushLog(`mcp-static-restored backend=${backend} run=${runId}`);
|
|
77449
|
+
} else {
|
|
77450
|
+
pushLog(`mcp-static-restore-failed backend=${backend} ${result.error ?? "unknown"}`);
|
|
77451
|
+
}
|
|
77452
|
+
} catch (err) {
|
|
77453
|
+
pushLog(`mcp-static-restore-failed backend=${backend} ${err.message}`);
|
|
76824
77454
|
}
|
|
76825
|
-
} catch (err) {
|
|
76826
|
-
pushLog(`mcp-unregister-failed backend=${backend} ${err.message}`);
|
|
76827
77455
|
}
|
|
77456
|
+
} finally {
|
|
77457
|
+
releaseRunStart();
|
|
76828
77458
|
}
|
|
76829
77459
|
})();
|
|
76830
77460
|
};
|
|
@@ -76836,6 +77466,8 @@ async function startDaemon(args) {
|
|
|
76836
77466
|
return workspaceAccessTracker.recoveryPath() ?? void 0;
|
|
76837
77467
|
};
|
|
76838
77468
|
let eventOutbox;
|
|
77469
|
+
let nativeHookInbox;
|
|
77470
|
+
let nativeTranscriptStreamer;
|
|
76839
77471
|
let runJournal;
|
|
76840
77472
|
try {
|
|
76841
77473
|
eventOutbox = new EncryptedEventOutbox(
|
|
@@ -76843,6 +77475,13 @@ async function startDaemon(args) {
|
|
|
76843
77475
|
eventSpoolKey,
|
|
76844
77476
|
pushLog
|
|
76845
77477
|
);
|
|
77478
|
+
nativeHookInbox = new EncryptedNativeHookInbox(nativeHookInboxPath(configPath), eventSpoolKey, {
|
|
77479
|
+
recoverClaims: true,
|
|
77480
|
+
onQuarantine: ({ fileName, error: error61 }) => pushLog(`native-hook-quarantined file=${fileName} error=${error61.message}`)
|
|
77481
|
+
});
|
|
77482
|
+
nativeTranscriptStreamer = new NativeTranscriptStreamer(
|
|
77483
|
+
new NativeTranscriptCursorStore(`${configPath}.native-transcript-cursors.json`)
|
|
77484
|
+
);
|
|
76846
77485
|
runJournal = new RuntimeRunJournal(`${configPath}.run-journal.json`);
|
|
76847
77486
|
} catch (error61) {
|
|
76848
77487
|
releaseOwnerLease();
|
|
@@ -76850,9 +77489,57 @@ async function startDaemon(args) {
|
|
|
76850
77489
|
throw error61;
|
|
76851
77490
|
}
|
|
76852
77491
|
const workspaceRelocations = /* @__PURE__ */ new Map();
|
|
77492
|
+
let nativeHookDrainInFlight = false;
|
|
77493
|
+
const flushNativeHookInbox = async () => {
|
|
77494
|
+
if (nativeHookDrainInFlight || !socket.connected) return;
|
|
77495
|
+
nativeHookDrainInFlight = true;
|
|
77496
|
+
try {
|
|
77497
|
+
const result = await drainNativeHookInbox({
|
|
77498
|
+
inbox: nativeHookInbox,
|
|
77499
|
+
transcriptStreamer: nativeTranscriptStreamer,
|
|
77500
|
+
sink: {
|
|
77501
|
+
deliver: (envelope) => new Promise((resolveDelivery, rejectDelivery) => {
|
|
77502
|
+
socket.timeout(5e3).emit(
|
|
77503
|
+
"native.session.event",
|
|
77504
|
+
envelope,
|
|
77505
|
+
(error61, acknowledgement) => {
|
|
77506
|
+
const accepted = !error61 && acknowledgement?.ok === true;
|
|
77507
|
+
if (!accepted) {
|
|
77508
|
+
pushLog(
|
|
77509
|
+
`native-hook-delivery-deferred event=${envelope.eventId} provider=${envelope.provider} error=${error61?.message ?? "none"} ack=${acknowledgement?.ok ?? "missing"} retryable=${acknowledgement?.retryable ?? "missing"}`
|
|
77510
|
+
);
|
|
77511
|
+
}
|
|
77512
|
+
if (!error61 && acknowledgement?.ok === false && acknowledgement.retryable === false) {
|
|
77513
|
+
rejectDelivery(
|
|
77514
|
+
new PermanentNativeHookDeliveryError(
|
|
77515
|
+
`backend permanently rejected native event ${envelope.eventId}`
|
|
77516
|
+
)
|
|
77517
|
+
);
|
|
77518
|
+
return;
|
|
77519
|
+
}
|
|
77520
|
+
resolveDelivery(accepted);
|
|
77521
|
+
}
|
|
77522
|
+
);
|
|
77523
|
+
})
|
|
77524
|
+
},
|
|
77525
|
+
onTranscriptError: ({ envelope, error: error61 }) => pushLog(
|
|
77526
|
+
`native-transcript-unavailable event=${envelope.eventId} provider=${envelope.provider} error=${error61.message}`
|
|
77527
|
+
)
|
|
77528
|
+
});
|
|
77529
|
+
if (result.attempted > 0) {
|
|
77530
|
+
pushLog(
|
|
77531
|
+
`native-hook-drain attempted=${result.attempted} acknowledged=${result.acknowledged} deferred=${result.deferred}`
|
|
77532
|
+
);
|
|
77533
|
+
}
|
|
77534
|
+
} catch (error61) {
|
|
77535
|
+
pushLog(`native-hook-drain-failed ${error61.message}`);
|
|
77536
|
+
} finally {
|
|
77537
|
+
nativeHookDrainInFlight = false;
|
|
77538
|
+
}
|
|
77539
|
+
};
|
|
76853
77540
|
for (const relocation of stored.workspaceRelocations ?? []) {
|
|
76854
77541
|
if (relocation?.conversationId && relocation.runId && relocation.previousPath && relocation.canonicalPath) {
|
|
76855
|
-
workspaceRelocations.set((0,
|
|
77542
|
+
workspaceRelocations.set((0, import_node_path22.resolve)(relocation.previousPath), relocation);
|
|
76856
77543
|
}
|
|
76857
77544
|
}
|
|
76858
77545
|
if (stored.localApiPort !== localApiPort || stored.localApiToken !== localApiToken || stored.localServiceId !== localServiceId || stored.localServiceSecret !== localServiceSecret || stored.eventSpoolKey !== eventSpoolKey || stored.daemonEpoch !== daemonEpoch || stored.installationId !== installationId) {
|
|
@@ -77039,6 +77726,7 @@ async function startDaemon(args) {
|
|
|
77039
77726
|
if (!socket.connected) return;
|
|
77040
77727
|
emitRunnerPresence();
|
|
77041
77728
|
flushWorkspaceRelocations();
|
|
77729
|
+
void flushNativeHookInbox();
|
|
77042
77730
|
}, 15e3);
|
|
77043
77731
|
let pendingWakeGapMs = null;
|
|
77044
77732
|
const flushWakeGap = () => {
|
|
@@ -77401,7 +78089,7 @@ async function startDaemon(args) {
|
|
|
77401
78089
|
`${describeOccupiedLocalDaemon(localApiPort)} The token in ${getConfigPath()} no longer matches the running daemon; quit Alan desktop or run setup again.`
|
|
77402
78090
|
);
|
|
77403
78091
|
}
|
|
77404
|
-
const localApiListening = new Promise((
|
|
78092
|
+
const localApiListening = new Promise((resolve15, reject) => {
|
|
77405
78093
|
const onError = (error61) => {
|
|
77406
78094
|
clearInterval(heartbeat);
|
|
77407
78095
|
clearInterval(versionRefresh);
|
|
@@ -77431,7 +78119,7 @@ async function startDaemon(args) {
|
|
|
77431
78119
|
if (address && typeof address === "object") localApiPort = address.port;
|
|
77432
78120
|
pushLog(`local_api listening port=${localApiPort}`);
|
|
77433
78121
|
console.info("[alan-agent] Local daemon API listening", { port: localApiPort });
|
|
77434
|
-
|
|
78122
|
+
resolve15();
|
|
77435
78123
|
});
|
|
77436
78124
|
});
|
|
77437
78125
|
try {
|
|
@@ -77451,7 +78139,7 @@ async function startDaemon(args) {
|
|
|
77451
78139
|
clearInterval(workspaceLeaseMonitor);
|
|
77452
78140
|
suspensionDetector.stop();
|
|
77453
78141
|
socket.disconnect();
|
|
77454
|
-
await new Promise((
|
|
78142
|
+
await new Promise((resolve15) => localServer.close(() => resolve15()));
|
|
77455
78143
|
releaseOwnerLease();
|
|
77456
78144
|
releaseStartupLease();
|
|
77457
78145
|
throw error61;
|
|
@@ -77461,6 +78149,8 @@ async function startDaemon(args) {
|
|
|
77461
78149
|
skillSyncInterval.unref?.();
|
|
77462
78150
|
const skillAuditInterval = setInterval(() => runSkillSync("refresh"), 24 * 60 * 6e4);
|
|
77463
78151
|
skillAuditInterval.unref?.();
|
|
78152
|
+
const nativeHookDrainInterval = setInterval(() => void flushNativeHookInbox(), 1e3);
|
|
78153
|
+
nativeHookDrainInterval.unref?.();
|
|
77464
78154
|
let stopped = false;
|
|
77465
78155
|
const stop = async () => {
|
|
77466
78156
|
if (stopped) return;
|
|
@@ -77471,14 +78161,15 @@ async function startDaemon(args) {
|
|
|
77471
78161
|
clearInterval(workspaceLeaseMonitor);
|
|
77472
78162
|
clearInterval(skillSyncInterval);
|
|
77473
78163
|
clearInterval(skillAuditInterval);
|
|
78164
|
+
clearInterval(nativeHookDrainInterval);
|
|
77474
78165
|
suspensionDetector.stop();
|
|
77475
78166
|
for (const entry of activeAgents.values()) {
|
|
77476
78167
|
abortActiveAgent(entry, { userInitiated: true });
|
|
77477
78168
|
}
|
|
77478
78169
|
activeAgents.clear();
|
|
77479
78170
|
socket.disconnect();
|
|
77480
|
-
await new Promise((
|
|
77481
|
-
localServer.close(() =>
|
|
78171
|
+
await new Promise((resolve15) => {
|
|
78172
|
+
localServer.close(() => resolve15());
|
|
77482
78173
|
});
|
|
77483
78174
|
} finally {
|
|
77484
78175
|
sleepInhibitor.dispose();
|
|
@@ -77558,7 +78249,7 @@ async function setupDaemon(args) {
|
|
|
77558
78249
|
const token = argValue(args, "--token") ?? process.env.ALAN_SETUP_TOKEN;
|
|
77559
78250
|
const displayName = argValue(args, "--name") ?? (0, import_node_os11.hostname)();
|
|
77560
78251
|
const scope = argValue(args, "--scope") ?? process.env.ALAN_RUNTIME_SCOPE ?? (setupToken ? "team" : "user");
|
|
77561
|
-
const installationId = argValue(args, "--installation-id") ?? previousConfig.installationId ?? (0,
|
|
78252
|
+
const installationId = argValue(args, "--installation-id") ?? previousConfig.installationId ?? (0, import_node_crypto17.randomUUID)();
|
|
77562
78253
|
if (scope !== "team" && scope !== "user") {
|
|
77563
78254
|
throw new Error("setup --scope must be either 'team' or 'user'");
|
|
77564
78255
|
}
|
|
@@ -77567,7 +78258,7 @@ async function setupDaemon(args) {
|
|
|
77567
78258
|
"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"
|
|
77568
78259
|
);
|
|
77569
78260
|
}
|
|
77570
|
-
const registrationAttemptId = setupToken ? getOrCreateSetupAttemptId(setupToken) : (0,
|
|
78261
|
+
const registrationAttemptId = setupToken ? getOrCreateSetupAttemptId(setupToken) : (0, import_node_crypto17.randomUUID)();
|
|
77571
78262
|
const registrationUrl = `${apiUrl}/public/runtimes${setupToken ? "/register-with-setup-token" : ""}`;
|
|
77572
78263
|
const registrationInit = {
|
|
77573
78264
|
method: "POST",
|
|
@@ -77602,7 +78293,7 @@ async function setupDaemon(args) {
|
|
|
77602
78293
|
}
|
|
77603
78294
|
const retriableStatus = response && (response.status === 408 || response.status === 429 || response.status >= 500);
|
|
77604
78295
|
if (response && (!retriableStatus || response.ok) || attempt === 3) break;
|
|
77605
|
-
await new Promise((
|
|
78296
|
+
await new Promise((resolve15) => setTimeout(resolve15, attempt * 100));
|
|
77606
78297
|
}
|
|
77607
78298
|
if (!response) {
|
|
77608
78299
|
const detail = transportError instanceof Error ? transportError.message : String(transportError);
|
|
@@ -77633,10 +78324,10 @@ async function setupDaemon(args) {
|
|
|
77633
78324
|
runtimeToken: body.runtimeToken,
|
|
77634
78325
|
runtimeRenewalToken: body.runtimeRenewalToken,
|
|
77635
78326
|
localApiPort: getLocalApiPort(args, {}),
|
|
77636
|
-
localApiToken: previousConfig.localApiToken ?? (0,
|
|
77637
|
-
localServiceId: previousConfig.localServiceId ?? (0,
|
|
77638
|
-
localServiceSecret: previousConfig.localServiceSecret ?? (0,
|
|
77639
|
-
eventSpoolKey: previousConfig.eventSpoolKey ?? (0,
|
|
78327
|
+
localApiToken: previousConfig.localApiToken ?? (0, import_node_crypto17.randomBytes)(24).toString("hex"),
|
|
78328
|
+
localServiceId: previousConfig.localServiceId ?? (0, import_node_crypto17.randomUUID)(),
|
|
78329
|
+
localServiceSecret: previousConfig.localServiceSecret ?? (0, import_node_crypto17.randomBytes)(32).toString("base64url"),
|
|
78330
|
+
eventSpoolKey: previousConfig.eventSpoolKey ?? (0, import_node_crypto17.randomBytes)(32).toString("base64url"),
|
|
77640
78331
|
daemonEpoch: previousConfig.daemonEpoch,
|
|
77641
78332
|
displayName: body.runtime.displayName ?? displayName,
|
|
77642
78333
|
installationId
|
|
@@ -77666,7 +78357,7 @@ async function loginWithDeviceCode(args) {
|
|
|
77666
78357
|
const displayName = argValue(args, "--name") ?? (0, import_node_os11.hostname)();
|
|
77667
78358
|
const maxPolls = Number.parseInt(argValue(args, "--max-polls") ?? "300", 10);
|
|
77668
78359
|
const resumed = readPendingDeviceAuthorization(apiUrl);
|
|
77669
|
-
const installationId = resumed?.installationId ?? previousConfig.installationId ?? (0,
|
|
78360
|
+
const installationId = resumed?.installationId ?? previousConfig.installationId ?? (0, import_node_crypto17.randomUUID)();
|
|
77670
78361
|
let authorization = resumed ? { ...resumed, operationId: operation.id } : null;
|
|
77671
78362
|
if (!authorization) {
|
|
77672
78363
|
clearPendingDeviceAuthorization();
|
|
@@ -77742,10 +78433,10 @@ async function loginWithDeviceCode(args) {
|
|
|
77742
78433
|
runtimeToken: body.runtimeToken,
|
|
77743
78434
|
runtimeRenewalToken: body.runtimeRenewalToken,
|
|
77744
78435
|
localApiPort: getLocalApiPort(args, {}),
|
|
77745
|
-
localApiToken: previousConfig.localApiToken ?? (0,
|
|
77746
|
-
localServiceId: previousConfig.localServiceId ?? (0,
|
|
77747
|
-
localServiceSecret: previousConfig.localServiceSecret ?? (0,
|
|
77748
|
-
eventSpoolKey: previousConfig.eventSpoolKey ?? (0,
|
|
78436
|
+
localApiToken: previousConfig.localApiToken ?? (0, import_node_crypto17.randomBytes)(24).toString("hex"),
|
|
78437
|
+
localServiceId: previousConfig.localServiceId ?? (0, import_node_crypto17.randomUUID)(),
|
|
78438
|
+
localServiceSecret: previousConfig.localServiceSecret ?? (0, import_node_crypto17.randomBytes)(32).toString("base64url"),
|
|
78439
|
+
eventSpoolKey: previousConfig.eventSpoolKey ?? (0, import_node_crypto17.randomBytes)(32).toString("base64url"),
|
|
77749
78440
|
daemonEpoch: previousConfig.daemonEpoch,
|
|
77750
78441
|
displayName: body.runtime.displayName ?? displayName,
|
|
77751
78442
|
installationId
|
|
@@ -78096,6 +78787,860 @@ async function printDoctor(args = []) {
|
|
|
78096
78787
|
);
|
|
78097
78788
|
}
|
|
78098
78789
|
|
|
78790
|
+
// src/mcp-config-adapters.ts
|
|
78791
|
+
var import_node_crypto18 = require("crypto");
|
|
78792
|
+
var import_node_fs21 = require("fs");
|
|
78793
|
+
var import_node_os12 = require("os");
|
|
78794
|
+
var import_node_path23 = require("path");
|
|
78795
|
+
var log = {
|
|
78796
|
+
info: (...args) => console.info("[mcp-config-adapters]", ...args),
|
|
78797
|
+
warn: (...args) => console.warn("[mcp-config-adapters]", ...args),
|
|
78798
|
+
debug: () => {
|
|
78799
|
+
}
|
|
78800
|
+
};
|
|
78801
|
+
var HOME = (0, import_node_os12.homedir)();
|
|
78802
|
+
var PATHS = {
|
|
78803
|
+
claude: (0, import_node_path23.join)(HOME, ".claude.json"),
|
|
78804
|
+
codex: (0, import_node_path23.join)(HOME, ".codex", "config.toml"),
|
|
78805
|
+
cursor: (0, import_node_path23.join)(HOME, ".cursor", "mcp.json"),
|
|
78806
|
+
antigravity: (0, import_node_path23.join)(HOME, ".gemini", "config", "mcp_config.json"),
|
|
78807
|
+
kimi: (0, import_node_path23.join)(HOME, ".kimi", "mcp.json"),
|
|
78808
|
+
opencode: (0, import_node_path23.join)(HOME, ".config", "opencode", "opencode.json"),
|
|
78809
|
+
factory: (0, import_node_path23.join)(HOME, ".factory", "mcp.json"),
|
|
78810
|
+
amp: (0, import_node_path23.join)(HOME, ".config", "amp", "settings.json")
|
|
78811
|
+
};
|
|
78812
|
+
var MalformedToolConfigError = class extends Error {
|
|
78813
|
+
};
|
|
78814
|
+
function parseJsonSafe2(content) {
|
|
78815
|
+
if (!content) return {};
|
|
78816
|
+
try {
|
|
78817
|
+
const parsed = JSON.parse(content);
|
|
78818
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
78819
|
+
return parsed;
|
|
78820
|
+
}
|
|
78821
|
+
} catch {
|
|
78822
|
+
}
|
|
78823
|
+
throw new MalformedToolConfigError("Existing tool config is malformed");
|
|
78824
|
+
}
|
|
78825
|
+
function readFileOr(path2) {
|
|
78826
|
+
return (0, import_node_fs21.existsSync)(path2) ? (0, import_node_fs21.readFileSync)(path2, "utf-8") : null;
|
|
78827
|
+
}
|
|
78828
|
+
function acquireConfigLock2(path2) {
|
|
78829
|
+
(0, import_node_fs21.mkdirSync)((0, import_node_path23.dirname)(path2), { recursive: true });
|
|
78830
|
+
const lockPath = `${path2}.alan-lock`;
|
|
78831
|
+
const startedAt = Date.now();
|
|
78832
|
+
while (true) {
|
|
78833
|
+
try {
|
|
78834
|
+
(0, import_node_fs21.mkdirSync)(lockPath);
|
|
78835
|
+
return () => (0, import_node_fs21.rmSync)(lockPath, { recursive: true, force: true });
|
|
78836
|
+
} catch (error61) {
|
|
78837
|
+
const code = error61 && typeof error61 === "object" && "code" in error61 ? error61.code : void 0;
|
|
78838
|
+
if (code !== "EEXIST") throw error61;
|
|
78839
|
+
try {
|
|
78840
|
+
if (Date.now() - (0, import_node_fs21.statSync)(lockPath).mtimeMs > 3e4) {
|
|
78841
|
+
(0, import_node_fs21.rmSync)(lockPath, { recursive: true, force: true });
|
|
78842
|
+
continue;
|
|
78843
|
+
}
|
|
78844
|
+
} catch {
|
|
78845
|
+
continue;
|
|
78846
|
+
}
|
|
78847
|
+
if (Date.now() - startedAt >= 2e3) {
|
|
78848
|
+
throw new Error(`Alan could not safely update ${path2}`);
|
|
78849
|
+
}
|
|
78850
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 20);
|
|
78851
|
+
}
|
|
78852
|
+
}
|
|
78853
|
+
}
|
|
78854
|
+
function writeFileSafe2(path2, content) {
|
|
78855
|
+
(0, import_node_fs21.mkdirSync)((0, import_node_path23.dirname)(path2), { recursive: true });
|
|
78856
|
+
const pendingPath = `${path2}.alan-pending-${process.pid}-${(0, import_node_crypto18.randomBytes)(6).toString("hex")}`;
|
|
78857
|
+
try {
|
|
78858
|
+
(0, import_node_fs21.writeFileSync)(pendingPath, content, { encoding: "utf8", mode: 384, flag: "wx" });
|
|
78859
|
+
(0, import_node_fs21.renameSync)(pendingPath, path2);
|
|
78860
|
+
} finally {
|
|
78861
|
+
(0, import_node_fs21.rmSync)(pendingPath, { force: true });
|
|
78862
|
+
}
|
|
78863
|
+
}
|
|
78864
|
+
function saveMalformedBackup2(path2, content) {
|
|
78865
|
+
const hash2 = (0, import_node_crypto18.createHash)("sha256").update(content).digest("hex").slice(0, 16);
|
|
78866
|
+
const backupPath = `${path2}.alan-backup-${hash2}`;
|
|
78867
|
+
if (!(0, import_node_fs21.existsSync)(backupPath)) (0, import_node_fs21.copyFileSync)(path2, backupPath);
|
|
78868
|
+
return backupPath;
|
|
78869
|
+
}
|
|
78870
|
+
var claudeAdapter = {
|
|
78871
|
+
cli: "Claude Code",
|
|
78872
|
+
configPath: PATHS.claude,
|
|
78873
|
+
guardDir: HOME,
|
|
78874
|
+
// ~/.claude.json lives in home — always exists
|
|
78875
|
+
merge(existing, servers) {
|
|
78876
|
+
const config2 = parseJsonSafe2(existing);
|
|
78877
|
+
const mcpServers = config2.mcpServers ?? {};
|
|
78878
|
+
for (const s of servers) {
|
|
78879
|
+
if (s.kind === "http") {
|
|
78880
|
+
mcpServers[s.name] = { type: "http", url: s.url, headers: s.headers };
|
|
78881
|
+
} else {
|
|
78882
|
+
mcpServers[s.name] = {
|
|
78883
|
+
command: s.command,
|
|
78884
|
+
...s.args?.length ? { args: s.args } : {},
|
|
78885
|
+
...s.env && Object.keys(s.env).length > 0 ? { env: s.env } : {}
|
|
78886
|
+
};
|
|
78887
|
+
}
|
|
78888
|
+
}
|
|
78889
|
+
config2.mcpServers = mcpServers;
|
|
78890
|
+
return JSON.stringify(config2, null, 2);
|
|
78891
|
+
},
|
|
78892
|
+
remove(existing, serverNames) {
|
|
78893
|
+
const config2 = parseJsonSafe2(existing);
|
|
78894
|
+
const mcpServers = config2.mcpServers ?? {};
|
|
78895
|
+
for (const name of serverNames) delete mcpServers[name];
|
|
78896
|
+
config2.mcpServers = mcpServers;
|
|
78897
|
+
return JSON.stringify(config2, null, 2);
|
|
78898
|
+
}
|
|
78899
|
+
};
|
|
78900
|
+
var codexAdapter = {
|
|
78901
|
+
cli: "Codex",
|
|
78902
|
+
configPath: PATHS.codex,
|
|
78903
|
+
guardDir: (0, import_node_path23.join)(HOME, ".codex"),
|
|
78904
|
+
merge(existing, servers) {
|
|
78905
|
+
let config2 = {};
|
|
78906
|
+
if (existing) {
|
|
78907
|
+
try {
|
|
78908
|
+
config2 = parse5(existing);
|
|
78909
|
+
} catch {
|
|
78910
|
+
throw new MalformedToolConfigError("Existing tool config is malformed");
|
|
78911
|
+
}
|
|
78912
|
+
}
|
|
78913
|
+
const mcpServers = config2.mcp_servers ?? {};
|
|
78914
|
+
for (const s of servers) {
|
|
78915
|
+
if (s.kind === "http") {
|
|
78916
|
+
mcpServers[s.name] = { url: s.url, http_headers: s.headers };
|
|
78917
|
+
} else {
|
|
78918
|
+
mcpServers[s.name] = {
|
|
78919
|
+
command: s.command,
|
|
78920
|
+
...s.args?.length ? { args: s.args } : {},
|
|
78921
|
+
...s.env && Object.keys(s.env).length > 0 ? { env: s.env } : {}
|
|
78922
|
+
};
|
|
78923
|
+
}
|
|
78924
|
+
}
|
|
78925
|
+
config2.mcp_servers = mcpServers;
|
|
78926
|
+
return stringify(config2);
|
|
78927
|
+
},
|
|
78928
|
+
remove(existing, serverNames) {
|
|
78929
|
+
if (!existing) return "";
|
|
78930
|
+
let config2 = {};
|
|
78931
|
+
try {
|
|
78932
|
+
config2 = parse5(existing);
|
|
78933
|
+
} catch {
|
|
78934
|
+
return existing;
|
|
78935
|
+
}
|
|
78936
|
+
const mcpServers = config2.mcp_servers ?? {};
|
|
78937
|
+
for (const name of serverNames) delete mcpServers[name];
|
|
78938
|
+
config2.mcp_servers = mcpServers;
|
|
78939
|
+
return stringify(config2);
|
|
78940
|
+
}
|
|
78941
|
+
};
|
|
78942
|
+
var cursorAdapter = {
|
|
78943
|
+
cli: "Cursor",
|
|
78944
|
+
configPath: PATHS.cursor,
|
|
78945
|
+
guardDir: (0, import_node_path23.join)(HOME, ".cursor"),
|
|
78946
|
+
merge(existing, servers) {
|
|
78947
|
+
const config2 = parseJsonSafe2(existing);
|
|
78948
|
+
const mcpServers = config2.mcpServers ?? {};
|
|
78949
|
+
for (const s of servers) {
|
|
78950
|
+
if (s.kind === "http") {
|
|
78951
|
+
mcpServers[s.name] = { type: "http", url: s.url, headers: s.headers };
|
|
78952
|
+
} else {
|
|
78953
|
+
mcpServers[s.name] = {
|
|
78954
|
+
command: s.command,
|
|
78955
|
+
...s.args?.length ? { args: s.args } : {},
|
|
78956
|
+
...s.env && Object.keys(s.env).length > 0 ? { env: s.env } : {}
|
|
78957
|
+
};
|
|
78958
|
+
}
|
|
78959
|
+
}
|
|
78960
|
+
config2.mcpServers = mcpServers;
|
|
78961
|
+
return JSON.stringify(config2, null, 2);
|
|
78962
|
+
},
|
|
78963
|
+
remove(existing, serverNames) {
|
|
78964
|
+
const config2 = parseJsonSafe2(existing);
|
|
78965
|
+
const mcpServers = config2.mcpServers ?? {};
|
|
78966
|
+
for (const name of serverNames) delete mcpServers[name];
|
|
78967
|
+
config2.mcpServers = mcpServers;
|
|
78968
|
+
return JSON.stringify(config2, null, 2);
|
|
78969
|
+
}
|
|
78970
|
+
};
|
|
78971
|
+
function createAntigravityAdapter() {
|
|
78972
|
+
return {
|
|
78973
|
+
cli: "Antigravity CLI",
|
|
78974
|
+
configPath: PATHS.antigravity,
|
|
78975
|
+
guardDir: (0, import_node_path23.join)(HOME, ".gemini", "config"),
|
|
78976
|
+
merge(existing, servers) {
|
|
78977
|
+
const config2 = parseJsonSafe2(existing);
|
|
78978
|
+
const mcpServers = config2.mcpServers ?? {};
|
|
78979
|
+
for (const s of servers) {
|
|
78980
|
+
if (s.kind === "http") {
|
|
78981
|
+
mcpServers[s.name] = { serverUrl: s.url, headers: s.headers };
|
|
78982
|
+
} else {
|
|
78983
|
+
mcpServers[s.name] = {
|
|
78984
|
+
command: s.command,
|
|
78985
|
+
...s.args?.length ? { args: s.args } : {},
|
|
78986
|
+
...s.env && Object.keys(s.env).length > 0 ? { env: s.env } : {}
|
|
78987
|
+
};
|
|
78988
|
+
}
|
|
78989
|
+
}
|
|
78990
|
+
config2.mcpServers = mcpServers;
|
|
78991
|
+
return JSON.stringify(config2, null, 2);
|
|
78992
|
+
},
|
|
78993
|
+
remove(existing, serverNames) {
|
|
78994
|
+
const config2 = parseJsonSafe2(existing);
|
|
78995
|
+
const mcpServers = config2.mcpServers ?? {};
|
|
78996
|
+
for (const name of serverNames) delete mcpServers[name];
|
|
78997
|
+
config2.mcpServers = mcpServers;
|
|
78998
|
+
return JSON.stringify(config2, null, 2);
|
|
78999
|
+
}
|
|
79000
|
+
};
|
|
79001
|
+
}
|
|
79002
|
+
var antigravityAdapter = createAntigravityAdapter();
|
|
79003
|
+
var kimiAdapter = {
|
|
79004
|
+
cli: "Kimi CLI",
|
|
79005
|
+
configPath: PATHS.kimi,
|
|
79006
|
+
guardDir: (0, import_node_path23.join)(HOME, ".kimi"),
|
|
79007
|
+
merge(existing, servers) {
|
|
79008
|
+
const config2 = parseJsonSafe2(existing);
|
|
79009
|
+
const mcpServers = config2.mcpServers ?? {};
|
|
79010
|
+
for (const s of servers) {
|
|
79011
|
+
if (s.kind === "http") {
|
|
79012
|
+
mcpServers[s.name] = { url: s.url, headers: s.headers };
|
|
79013
|
+
} else {
|
|
79014
|
+
mcpServers[s.name] = {
|
|
79015
|
+
command: s.command,
|
|
79016
|
+
...s.args?.length ? { args: s.args } : {},
|
|
79017
|
+
...s.env && Object.keys(s.env).length > 0 ? { env: s.env } : {}
|
|
79018
|
+
};
|
|
79019
|
+
}
|
|
79020
|
+
}
|
|
79021
|
+
config2.mcpServers = mcpServers;
|
|
79022
|
+
return JSON.stringify(config2, null, 2);
|
|
79023
|
+
},
|
|
79024
|
+
remove(existing, serverNames) {
|
|
79025
|
+
const config2 = parseJsonSafe2(existing);
|
|
79026
|
+
const mcpServers = config2.mcpServers ?? {};
|
|
79027
|
+
for (const name of serverNames) delete mcpServers[name];
|
|
79028
|
+
config2.mcpServers = mcpServers;
|
|
79029
|
+
return JSON.stringify(config2, null, 2);
|
|
79030
|
+
}
|
|
79031
|
+
};
|
|
79032
|
+
var opencodeAdapter = {
|
|
79033
|
+
cli: "OpenCode",
|
|
79034
|
+
configPath: PATHS.opencode,
|
|
79035
|
+
guardDir: (0, import_node_path23.join)(HOME, ".config", "opencode"),
|
|
79036
|
+
merge(existing, servers) {
|
|
79037
|
+
const config2 = parseJsonSafe2(existing);
|
|
79038
|
+
const mcp = config2.mcp ?? {};
|
|
79039
|
+
for (const s of servers) {
|
|
79040
|
+
if (s.kind === "http") {
|
|
79041
|
+
mcp[s.name] = { type: "remote", url: s.url, headers: s.headers };
|
|
79042
|
+
} else {
|
|
79043
|
+
mcp[s.name] = {
|
|
79044
|
+
type: "local",
|
|
79045
|
+
command: [s.command, ...s.args ?? []],
|
|
79046
|
+
...s.env && Object.keys(s.env).length > 0 ? { environment: s.env } : {}
|
|
79047
|
+
};
|
|
79048
|
+
}
|
|
79049
|
+
}
|
|
79050
|
+
config2.mcp = mcp;
|
|
79051
|
+
return JSON.stringify(config2, null, 2);
|
|
79052
|
+
},
|
|
79053
|
+
remove(existing, serverNames) {
|
|
79054
|
+
const config2 = parseJsonSafe2(existing);
|
|
79055
|
+
const mcp = config2.mcp ?? {};
|
|
79056
|
+
for (const name of serverNames) delete mcp[name];
|
|
79057
|
+
config2.mcp = mcp;
|
|
79058
|
+
return JSON.stringify(config2, null, 2);
|
|
79059
|
+
}
|
|
79060
|
+
};
|
|
79061
|
+
var factoryAdapter = {
|
|
79062
|
+
cli: "Factory Droid",
|
|
79063
|
+
configPath: PATHS.factory,
|
|
79064
|
+
guardDir: (0, import_node_path23.join)(HOME, ".factory"),
|
|
79065
|
+
merge(existing, servers) {
|
|
79066
|
+
const config2 = parseJsonSafe2(existing);
|
|
79067
|
+
const mcpServers = config2.mcpServers ?? {};
|
|
79068
|
+
for (const s of servers) {
|
|
79069
|
+
if (s.kind === "http") {
|
|
79070
|
+
mcpServers[s.name] = {
|
|
79071
|
+
type: "http",
|
|
79072
|
+
url: s.url,
|
|
79073
|
+
disabled: false,
|
|
79074
|
+
...s.headers && Object.keys(s.headers).length > 0 ? { headers: s.headers } : {}
|
|
79075
|
+
};
|
|
79076
|
+
} else {
|
|
79077
|
+
mcpServers[s.name] = {
|
|
79078
|
+
type: "stdio",
|
|
79079
|
+
command: s.command,
|
|
79080
|
+
disabled: false,
|
|
79081
|
+
...s.args?.length ? { args: s.args } : {},
|
|
79082
|
+
...s.env && Object.keys(s.env).length > 0 ? { env: s.env } : {}
|
|
79083
|
+
};
|
|
79084
|
+
}
|
|
79085
|
+
}
|
|
79086
|
+
config2.mcpServers = mcpServers;
|
|
79087
|
+
return JSON.stringify(config2, null, 2);
|
|
79088
|
+
},
|
|
79089
|
+
remove(existing, serverNames) {
|
|
79090
|
+
const config2 = parseJsonSafe2(existing);
|
|
79091
|
+
const mcpServers = config2.mcpServers ?? {};
|
|
79092
|
+
for (const name of serverNames) delete mcpServers[name];
|
|
79093
|
+
config2.mcpServers = mcpServers;
|
|
79094
|
+
return JSON.stringify(config2, null, 2);
|
|
79095
|
+
}
|
|
79096
|
+
};
|
|
79097
|
+
var ampAdapter = {
|
|
79098
|
+
cli: "Amp",
|
|
79099
|
+
configPath: PATHS.amp,
|
|
79100
|
+
guardDir: (0, import_node_path23.join)(HOME, ".config", "amp"),
|
|
79101
|
+
merge(existing, servers) {
|
|
79102
|
+
const config2 = parseJsonSafe2(existing);
|
|
79103
|
+
const mcpServers = config2["amp.mcpServers"] ?? {};
|
|
79104
|
+
for (const s of servers) {
|
|
79105
|
+
if (s.kind === "http") {
|
|
79106
|
+
mcpServers[s.name] = { url: s.url, headers: s.headers };
|
|
79107
|
+
} else {
|
|
79108
|
+
mcpServers[s.name] = {
|
|
79109
|
+
command: s.command,
|
|
79110
|
+
...s.args?.length ? { args: s.args } : {}
|
|
79111
|
+
};
|
|
79112
|
+
}
|
|
79113
|
+
}
|
|
79114
|
+
config2["amp.mcpServers"] = mcpServers;
|
|
79115
|
+
return JSON.stringify(config2, null, 2);
|
|
79116
|
+
},
|
|
79117
|
+
remove(existing, serverNames) {
|
|
79118
|
+
const config2 = parseJsonSafe2(existing);
|
|
79119
|
+
const mcpServers = config2["amp.mcpServers"] ?? {};
|
|
79120
|
+
for (const name of serverNames) delete mcpServers[name];
|
|
79121
|
+
config2["amp.mcpServers"] = mcpServers;
|
|
79122
|
+
return JSON.stringify(config2, null, 2);
|
|
79123
|
+
}
|
|
79124
|
+
};
|
|
79125
|
+
var ALL_ADAPTERS = [
|
|
79126
|
+
claudeAdapter,
|
|
79127
|
+
codexAdapter,
|
|
79128
|
+
cursorAdapter,
|
|
79129
|
+
antigravityAdapter,
|
|
79130
|
+
kimiAdapter,
|
|
79131
|
+
opencodeAdapter,
|
|
79132
|
+
factoryAdapter,
|
|
79133
|
+
ampAdapter
|
|
79134
|
+
];
|
|
79135
|
+
function serverEntry(adapter, content, name) {
|
|
79136
|
+
if (adapter === codexAdapter) {
|
|
79137
|
+
const parsed2 = parse5(content);
|
|
79138
|
+
const servers2 = parsed2.mcp_servers;
|
|
79139
|
+
return servers2 && typeof servers2 === "object" ? servers2[name] : void 0;
|
|
79140
|
+
}
|
|
79141
|
+
const parsed = parseJsonSafe2(content);
|
|
79142
|
+
const key = adapter === opencodeAdapter ? "mcp" : adapter === ampAdapter ? "amp.mcpServers" : "mcpServers";
|
|
79143
|
+
const servers = parsed[key];
|
|
79144
|
+
return servers && typeof servers === "object" ? servers[name] : void 0;
|
|
79145
|
+
}
|
|
79146
|
+
function canonicalize(value2) {
|
|
79147
|
+
if (Array.isArray(value2)) return value2.map(canonicalize);
|
|
79148
|
+
if (value2 && typeof value2 === "object") {
|
|
79149
|
+
return Object.fromEntries(
|
|
79150
|
+
Object.entries(value2).sort(([a], [b]) => a.localeCompare(b)).map(([key, entry]) => [key, canonicalize(entry)])
|
|
79151
|
+
);
|
|
79152
|
+
}
|
|
79153
|
+
return value2;
|
|
79154
|
+
}
|
|
79155
|
+
function entriesMatch(actual, expected) {
|
|
79156
|
+
return JSON.stringify(canonicalize(actual)) === JSON.stringify(canonicalize(expected));
|
|
79157
|
+
}
|
|
79158
|
+
var ALAN_SESSION_HEADER_NAMES = /* @__PURE__ */ new Set([
|
|
79159
|
+
"x-conversation-id",
|
|
79160
|
+
"x-team-id",
|
|
79161
|
+
"x-allowed-tools",
|
|
79162
|
+
"x-alan-run-reference"
|
|
79163
|
+
]);
|
|
79164
|
+
function normalizeAlanSessionHeaders(actual, expected) {
|
|
79165
|
+
if (!actual || typeof actual !== "object" || Array.isArray(actual)) return actual;
|
|
79166
|
+
if (!expected || typeof expected !== "object" || Array.isArray(expected)) return actual;
|
|
79167
|
+
const normalized = { ...actual };
|
|
79168
|
+
const expectedHeaders = expected;
|
|
79169
|
+
const hadAlanSessionMarker = Object.keys(normalized).some(
|
|
79170
|
+
(key) => ALAN_SESSION_HEADER_NAMES.has(key.toLowerCase())
|
|
79171
|
+
);
|
|
79172
|
+
for (const key of Object.keys(normalized)) {
|
|
79173
|
+
if (ALAN_SESSION_HEADER_NAMES.has(key.toLowerCase())) delete normalized[key];
|
|
79174
|
+
}
|
|
79175
|
+
if (hadAlanSessionMarker) {
|
|
79176
|
+
const expectedAuthorization = Object.entries(expectedHeaders).find(
|
|
79177
|
+
([key]) => key.toLowerCase() === "authorization"
|
|
79178
|
+
);
|
|
79179
|
+
for (const key of Object.keys(normalized)) {
|
|
79180
|
+
if (key.toLowerCase() === "authorization") delete normalized[key];
|
|
79181
|
+
}
|
|
79182
|
+
if (expectedAuthorization) {
|
|
79183
|
+
normalized[expectedAuthorization[0]] = expectedAuthorization[1];
|
|
79184
|
+
}
|
|
79185
|
+
}
|
|
79186
|
+
return normalized;
|
|
79187
|
+
}
|
|
79188
|
+
function normalizeAlanRuntimeEntry(actual, expected) {
|
|
79189
|
+
if (Array.isArray(actual)) {
|
|
79190
|
+
if (!Array.isArray(expected)) return actual;
|
|
79191
|
+
return actual.map((entry, index) => normalizeAlanRuntimeEntry(entry, expected[index]));
|
|
79192
|
+
}
|
|
79193
|
+
if (!actual || typeof actual !== "object" || Array.isArray(actual)) return actual;
|
|
79194
|
+
if (!expected || typeof expected !== "object" || Array.isArray(expected)) return actual;
|
|
79195
|
+
const expectedRecord = expected;
|
|
79196
|
+
return Object.fromEntries(
|
|
79197
|
+
Object.entries(actual).map(([key, value2]) => [
|
|
79198
|
+
key,
|
|
79199
|
+
key.toLowerCase() === "headers" || key.toLowerCase() === "http_headers" ? normalizeAlanSessionHeaders(value2, expectedRecord[key]) : normalizeAlanRuntimeEntry(value2, expectedRecord[key])
|
|
79200
|
+
])
|
|
79201
|
+
);
|
|
79202
|
+
}
|
|
79203
|
+
function matchesAlanManagedEntry(actual, expected) {
|
|
79204
|
+
return entriesMatch(actual, expected) || entriesMatch(normalizeAlanRuntimeEntry(actual, expected), expected);
|
|
79205
|
+
}
|
|
79206
|
+
function hasAlanManagedSignature(entry) {
|
|
79207
|
+
if (!entry || typeof entry !== "object") return false;
|
|
79208
|
+
for (const [key, value2] of Object.entries(entry)) {
|
|
79209
|
+
const lower = key.toLowerCase();
|
|
79210
|
+
if ((lower === "headers" || lower === "http_headers") && value2 && typeof value2 === "object") {
|
|
79211
|
+
const headerNames = Object.keys(value2);
|
|
79212
|
+
if (headerNames.some((name) => ALAN_SESSION_HEADER_NAMES.has(name.toLowerCase()))) {
|
|
79213
|
+
return true;
|
|
79214
|
+
}
|
|
79215
|
+
}
|
|
79216
|
+
if (value2 && typeof value2 === "object" && hasAlanManagedSignature(value2)) return true;
|
|
79217
|
+
}
|
|
79218
|
+
return false;
|
|
79219
|
+
}
|
|
79220
|
+
function expectedServerEntry(adapter, server) {
|
|
79221
|
+
return serverEntry(adapter, adapter.merge(null, [server]), server.name);
|
|
79222
|
+
}
|
|
79223
|
+
function isManagedServerList(entries) {
|
|
79224
|
+
return entries.length === 0 || typeof entries[0] !== "string";
|
|
79225
|
+
}
|
|
79226
|
+
function syncMcpToAllClis(servers, oldManagedServers = []) {
|
|
79227
|
+
const written = [];
|
|
79228
|
+
const failures = [];
|
|
79229
|
+
for (const adapter of ALL_ADAPTERS) {
|
|
79230
|
+
if (!(0, import_node_fs21.existsSync)(adapter.guardDir)) {
|
|
79231
|
+
log.debug("MCP Adapters", `Skipping ${adapter.cli} \u2014 guard dir not found`, {
|
|
79232
|
+
guardDir: adapter.guardDir
|
|
79233
|
+
});
|
|
79234
|
+
continue;
|
|
79235
|
+
}
|
|
79236
|
+
const releaseLock = acquireConfigLock2(adapter.configPath);
|
|
79237
|
+
try {
|
|
79238
|
+
const existing = readFileOr(adapter.configPath);
|
|
79239
|
+
const previousServers = isManagedServerList(oldManagedServers) ? oldManagedServers : [];
|
|
79240
|
+
const previousByName = new Map(previousServers.map((server) => [server.name, server]));
|
|
79241
|
+
if (existing) {
|
|
79242
|
+
for (const server of servers) {
|
|
79243
|
+
const currentEntry = serverEntry(adapter, existing, server.name);
|
|
79244
|
+
if (currentEntry === void 0) continue;
|
|
79245
|
+
const desiredEntry = expectedServerEntry(adapter, server);
|
|
79246
|
+
const previous = previousByName.get(server.name);
|
|
79247
|
+
const previousEntry = previous ? expectedServerEntry(adapter, previous) : void 0;
|
|
79248
|
+
if (!hasAlanManagedSignature(currentEntry) && !matchesAlanManagedEntry(currentEntry, desiredEntry) && (!previousEntry || !matchesAlanManagedEntry(currentEntry, previousEntry))) {
|
|
79249
|
+
throw new Error(`an unowned entry already uses the name ${server.name}`);
|
|
79250
|
+
}
|
|
79251
|
+
}
|
|
79252
|
+
}
|
|
79253
|
+
const currentNames = new Set(servers.map((s) => s.name));
|
|
79254
|
+
const toRemove = previousServers.filter((server) => !currentNames.has(server.name));
|
|
79255
|
+
let content;
|
|
79256
|
+
if (toRemove.length > 0) {
|
|
79257
|
+
const ownedNames = existing ? toRemove.filter(
|
|
79258
|
+
(server) => matchesAlanManagedEntry(
|
|
79259
|
+
serverEntry(adapter, existing, server.name),
|
|
79260
|
+
expectedServerEntry(adapter, server)
|
|
79261
|
+
)
|
|
79262
|
+
).map((server) => server.name) : [];
|
|
79263
|
+
const cleaned = adapter.remove(existing, ownedNames);
|
|
79264
|
+
content = adapter.merge(cleaned, servers);
|
|
79265
|
+
} else {
|
|
79266
|
+
content = adapter.merge(existing, servers);
|
|
79267
|
+
}
|
|
79268
|
+
writeFileSafe2(adapter.configPath, content);
|
|
79269
|
+
written.push(adapter.cli);
|
|
79270
|
+
log.info("MCP Adapters", `Synced to ${adapter.cli}`, {
|
|
79271
|
+
configPath: adapter.configPath,
|
|
79272
|
+
serverCount: servers.length
|
|
79273
|
+
});
|
|
79274
|
+
} catch (err) {
|
|
79275
|
+
const existing = readFileOr(adapter.configPath);
|
|
79276
|
+
const backupPath = err instanceof MalformedToolConfigError && existing !== null ? saveMalformedBackup2(adapter.configPath, existing) : void 0;
|
|
79277
|
+
const message = `${adapter.cli}: ${err instanceof Error ? err.message : String(err)}${backupPath ? `; review ${backupPath}` : ""}`;
|
|
79278
|
+
failures.push(message);
|
|
79279
|
+
log.warn("MCP Adapters", `Failed to sync to ${adapter.cli}`, {
|
|
79280
|
+
error: message
|
|
79281
|
+
});
|
|
79282
|
+
} finally {
|
|
79283
|
+
releaseLock();
|
|
79284
|
+
}
|
|
79285
|
+
}
|
|
79286
|
+
if (failures.length > 0) throw new Error(failures.join(" | "));
|
|
79287
|
+
return written;
|
|
79288
|
+
}
|
|
79289
|
+
function removeMcpFromAllClis(managedServers) {
|
|
79290
|
+
if (managedServers.length === 0) return;
|
|
79291
|
+
const ownershipKnown = isManagedServerList(managedServers);
|
|
79292
|
+
for (const adapter of ALL_ADAPTERS) {
|
|
79293
|
+
if (!(0, import_node_fs21.existsSync)(adapter.guardDir)) continue;
|
|
79294
|
+
const releaseLock = acquireConfigLock2(adapter.configPath);
|
|
79295
|
+
try {
|
|
79296
|
+
const existing = readFileOr(adapter.configPath);
|
|
79297
|
+
if (!existing) continue;
|
|
79298
|
+
const ownedNames = ownershipKnown ? managedServers.filter(
|
|
79299
|
+
(server) => matchesAlanManagedEntry(
|
|
79300
|
+
serverEntry(adapter, existing, server.name),
|
|
79301
|
+
expectedServerEntry(adapter, server)
|
|
79302
|
+
)
|
|
79303
|
+
).map((server) => server.name) : [];
|
|
79304
|
+
if (ownedNames.length === 0) continue;
|
|
79305
|
+
const content = adapter.remove(existing, ownedNames);
|
|
79306
|
+
writeFileSafe2(adapter.configPath, content);
|
|
79307
|
+
log.info("MCP Adapters", `Cleaned up ${adapter.cli}`, {
|
|
79308
|
+
removed: ownedNames
|
|
79309
|
+
});
|
|
79310
|
+
} catch (err) {
|
|
79311
|
+
const existing = readFileOr(adapter.configPath);
|
|
79312
|
+
const backupPath = err instanceof MalformedToolConfigError && existing !== null ? saveMalformedBackup2(adapter.configPath, existing) : void 0;
|
|
79313
|
+
log.warn("MCP Adapters", `Failed to clean up ${adapter.cli}`, {
|
|
79314
|
+
error: `${err instanceof Error ? err.message : String(err)}${backupPath ? `; review ${backupPath}` : ""}`
|
|
79315
|
+
});
|
|
79316
|
+
} finally {
|
|
79317
|
+
releaseLock();
|
|
79318
|
+
}
|
|
79319
|
+
}
|
|
79320
|
+
}
|
|
79321
|
+
var DEFAULT_ALAN_MCP_NAMES = ["alan"];
|
|
79322
|
+
function inspectAlanMcpPresence(expectedServers = [], candidateNames = DEFAULT_ALAN_MCP_NAMES) {
|
|
79323
|
+
const configuredClis = [];
|
|
79324
|
+
const inspectedClis = [];
|
|
79325
|
+
const serverNamesFound = /* @__PURE__ */ new Set();
|
|
79326
|
+
const names = /* @__PURE__ */ new Set([
|
|
79327
|
+
...candidateNames,
|
|
79328
|
+
...expectedServers.map((server) => server.name)
|
|
79329
|
+
]);
|
|
79330
|
+
const expectedByName = new Map(expectedServers.map((server) => [server.name, server]));
|
|
79331
|
+
for (const adapter of ALL_ADAPTERS) {
|
|
79332
|
+
if (!(0, import_node_fs21.existsSync)(adapter.guardDir)) continue;
|
|
79333
|
+
inspectedClis.push(adapter.cli);
|
|
79334
|
+
const existing = readFileOr(adapter.configPath);
|
|
79335
|
+
if (!existing) continue;
|
|
79336
|
+
let foundOnCli = false;
|
|
79337
|
+
for (const name of names) {
|
|
79338
|
+
let entry;
|
|
79339
|
+
try {
|
|
79340
|
+
entry = serverEntry(adapter, existing, name);
|
|
79341
|
+
} catch {
|
|
79342
|
+
continue;
|
|
79343
|
+
}
|
|
79344
|
+
if (entry === void 0) continue;
|
|
79345
|
+
const expected = expectedByName.get(name);
|
|
79346
|
+
const matchesExpected = expected ? matchesAlanManagedEntry(entry, expectedServerEntry(adapter, expected)) : false;
|
|
79347
|
+
if (!hasAlanManagedSignature(entry) && !matchesExpected) continue;
|
|
79348
|
+
foundOnCli = true;
|
|
79349
|
+
serverNamesFound.add(name);
|
|
79350
|
+
}
|
|
79351
|
+
if (foundOnCli) configuredClis.push(adapter.cli);
|
|
79352
|
+
}
|
|
79353
|
+
return {
|
|
79354
|
+
configured: configuredClis.length > 0,
|
|
79355
|
+
configuredClis,
|
|
79356
|
+
inspectedClis,
|
|
79357
|
+
serverNamesFound: [...serverNamesFound]
|
|
79358
|
+
};
|
|
79359
|
+
}
|
|
79360
|
+
function toManagedMcpServers(mcpServers) {
|
|
79361
|
+
const result = [];
|
|
79362
|
+
for (const [name, config2] of Object.entries(mcpServers)) {
|
|
79363
|
+
if (!config2 || typeof config2 !== "object") continue;
|
|
79364
|
+
const c = config2;
|
|
79365
|
+
if (c.command && typeof c.command === "string") {
|
|
79366
|
+
result.push({
|
|
79367
|
+
name,
|
|
79368
|
+
kind: "stdio",
|
|
79369
|
+
command: c.command,
|
|
79370
|
+
args: c.args ?? void 0,
|
|
79371
|
+
env: c.env ?? void 0
|
|
79372
|
+
});
|
|
79373
|
+
} else {
|
|
79374
|
+
result.push({
|
|
79375
|
+
name,
|
|
79376
|
+
kind: "http",
|
|
79377
|
+
url: c.url ?? "",
|
|
79378
|
+
headers: c.headers ?? {}
|
|
79379
|
+
});
|
|
79380
|
+
}
|
|
79381
|
+
}
|
|
79382
|
+
return result;
|
|
79383
|
+
}
|
|
79384
|
+
|
|
79385
|
+
// src/mcp-sync.ts
|
|
79386
|
+
var McpSyncService = class {
|
|
79387
|
+
constructor(options) {
|
|
79388
|
+
this.options = options;
|
|
79389
|
+
this.managedServerNames = [...options.managedServerNames ?? ["alan"]];
|
|
79390
|
+
this.legacyServerNames = [...options.legacyServerNames ?? []];
|
|
79391
|
+
}
|
|
79392
|
+
managedServerNames;
|
|
79393
|
+
legacyServerNames;
|
|
79394
|
+
getStatus() {
|
|
79395
|
+
const presence = inspectAlanMcpPresence([], this.managedServerNames);
|
|
79396
|
+
return {
|
|
79397
|
+
configured: presence.configured,
|
|
79398
|
+
lastSyncAt: null,
|
|
79399
|
+
configuredClis: presence.configuredClis,
|
|
79400
|
+
inspectedClis: presence.inspectedClis,
|
|
79401
|
+
managedServerNames: [...this.managedServerNames],
|
|
79402
|
+
mcpAutoInjectEnabled: this.options.enabled
|
|
79403
|
+
};
|
|
79404
|
+
}
|
|
79405
|
+
async sync(apiUrl, token, onProgress) {
|
|
79406
|
+
if (!this.options.enabled) return { errors: [], mcpServersUpdated: false };
|
|
79407
|
+
onProgress?.({ phase: "fetching" });
|
|
79408
|
+
let config2;
|
|
79409
|
+
try {
|
|
79410
|
+
const response = await fetch(`${apiUrl}/public/ade/config`, {
|
|
79411
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
79412
|
+
signal: AbortSignal.timeout(15e3)
|
|
79413
|
+
});
|
|
79414
|
+
if (!response.ok) throw new Error(`HTTP ${response.status} ${response.statusText}`);
|
|
79415
|
+
config2 = await response.json();
|
|
79416
|
+
} catch (error61) {
|
|
79417
|
+
return {
|
|
79418
|
+
errors: [`Fetch failed: ${error61 instanceof Error ? error61.message : String(error61)}`],
|
|
79419
|
+
mcpServersUpdated: false
|
|
79420
|
+
};
|
|
79421
|
+
}
|
|
79422
|
+
return this.syncServers(config2.mcpServers, apiUrl, onProgress);
|
|
79423
|
+
}
|
|
79424
|
+
syncServers(mcpServers, apiUrl, onProgress) {
|
|
79425
|
+
if (!this.options.enabled) return { errors: [], mcpServersUpdated: false };
|
|
79426
|
+
onProgress?.({ phase: "mcp" });
|
|
79427
|
+
try {
|
|
79428
|
+
const rawServers = this.options.transformMcpServers ? this.options.transformMcpServers(mcpServers, apiUrl) : mcpServers;
|
|
79429
|
+
const managed = toManagedMcpServers(rawServers);
|
|
79430
|
+
const previousNames = [...this.managedServerNames, ...this.legacyServerNames];
|
|
79431
|
+
const written = syncMcpToAllClis(managed, previousNames);
|
|
79432
|
+
return { errors: [], mcpServersUpdated: written.length > 0 };
|
|
79433
|
+
} catch (error61) {
|
|
79434
|
+
return {
|
|
79435
|
+
errors: [`MCP sync: ${error61 instanceof Error ? error61.message : String(error61)}`],
|
|
79436
|
+
mcpServersUpdated: false
|
|
79437
|
+
};
|
|
79438
|
+
}
|
|
79439
|
+
}
|
|
79440
|
+
cleanup(previousServers = []) {
|
|
79441
|
+
removeMcpFromAllClis(previousServers);
|
|
79442
|
+
}
|
|
79443
|
+
};
|
|
79444
|
+
|
|
79445
|
+
// src/native-hook-config.ts
|
|
79446
|
+
var import_node_fs22 = require("fs");
|
|
79447
|
+
var import_node_os13 = require("os");
|
|
79448
|
+
var import_node_path24 = require("path");
|
|
79449
|
+
var CLAUDE_HOOK_EVENTS = [
|
|
79450
|
+
"SessionStart",
|
|
79451
|
+
"UserPromptSubmit",
|
|
79452
|
+
"PostToolUse",
|
|
79453
|
+
"Stop",
|
|
79454
|
+
"SubagentStart",
|
|
79455
|
+
"SubagentStop",
|
|
79456
|
+
"SessionEnd"
|
|
79457
|
+
];
|
|
79458
|
+
var ALAN_HOOK_MARKER = "# alan-native-session-hook";
|
|
79459
|
+
function shellQuote(value2) {
|
|
79460
|
+
return `'${value2.replaceAll("'", `'\\''`)}'`;
|
|
79461
|
+
}
|
|
79462
|
+
function powershellQuote(value2) {
|
|
79463
|
+
return `'${value2.replaceAll("'", "''")}'`;
|
|
79464
|
+
}
|
|
79465
|
+
function nativeHookCommand(input2) {
|
|
79466
|
+
const args = [
|
|
79467
|
+
input2.cliEntry,
|
|
79468
|
+
"hook",
|
|
79469
|
+
"--config-path",
|
|
79470
|
+
input2.configPath,
|
|
79471
|
+
"--provider",
|
|
79472
|
+
input2.provider,
|
|
79473
|
+
"--event",
|
|
79474
|
+
input2.event,
|
|
79475
|
+
ALAN_HOOK_MARKER
|
|
79476
|
+
];
|
|
79477
|
+
const environment = Object.entries(input2.environment ?? {});
|
|
79478
|
+
for (const [key] of environment) {
|
|
79479
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
|
|
79480
|
+
throw new Error(`Invalid native hook environment variable: ${key}`);
|
|
79481
|
+
}
|
|
79482
|
+
}
|
|
79483
|
+
if (environment.length === 0) {
|
|
79484
|
+
return {
|
|
79485
|
+
type: "command",
|
|
79486
|
+
command: input2.nodeExecutable,
|
|
79487
|
+
args,
|
|
79488
|
+
timeout: 5
|
|
79489
|
+
};
|
|
79490
|
+
}
|
|
79491
|
+
if ((input2.platform ?? (0, import_node_os13.platform)()) === "win32") {
|
|
79492
|
+
const environmentPrefix2 = environment.map(([key, value2]) => `$env:${key}=${powershellQuote(value2)}`).join("; ");
|
|
79493
|
+
const command2 = [input2.nodeExecutable, ...args].map(powershellQuote).join(" ");
|
|
79494
|
+
return {
|
|
79495
|
+
type: "command",
|
|
79496
|
+
shell: "powershell",
|
|
79497
|
+
command: `${environmentPrefix2}; & ${command2}`,
|
|
79498
|
+
timeout: 5
|
|
79499
|
+
};
|
|
79500
|
+
}
|
|
79501
|
+
const environmentPrefix = environment.map(([key, value2]) => `${key}=${shellQuote(value2)}`).join(" ");
|
|
79502
|
+
const command = [input2.nodeExecutable, ...args].map(shellQuote).join(" ");
|
|
79503
|
+
return {
|
|
79504
|
+
type: "command",
|
|
79505
|
+
shell: "bash",
|
|
79506
|
+
command: `${environmentPrefix} ${command}`,
|
|
79507
|
+
timeout: 5
|
|
79508
|
+
};
|
|
79509
|
+
}
|
|
79510
|
+
function readSettings(path2) {
|
|
79511
|
+
if (!(0, import_node_fs22.existsSync)(path2)) return {};
|
|
79512
|
+
try {
|
|
79513
|
+
const value2 = JSON.parse((0, import_node_fs22.readFileSync)(path2, "utf8"));
|
|
79514
|
+
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) {
|
|
79515
|
+
throw new Error("settings root must be an object");
|
|
79516
|
+
}
|
|
79517
|
+
return value2;
|
|
79518
|
+
} catch (error61) {
|
|
79519
|
+
const backup = `${path2}.malformed-${Date.now()}.bak`;
|
|
79520
|
+
(0, import_node_fs22.copyFileSync)(path2, backup);
|
|
79521
|
+
throw new Error(
|
|
79522
|
+
`Claude settings were malformed and moved to ${backup}: ${error61 instanceof Error ? error61.message : String(error61)}`
|
|
79523
|
+
);
|
|
79524
|
+
}
|
|
79525
|
+
}
|
|
79526
|
+
function withoutAlanHookGroups(value2) {
|
|
79527
|
+
if (!Array.isArray(value2)) return [];
|
|
79528
|
+
return value2.filter((group) => !JSON.stringify(group).includes(ALAN_HOOK_MARKER));
|
|
79529
|
+
}
|
|
79530
|
+
function writeSettings(path2, settings) {
|
|
79531
|
+
(0, import_node_fs22.mkdirSync)((0, import_node_path24.dirname)(path2), { recursive: true, mode: 448 });
|
|
79532
|
+
const temporary = `${path2}.${process.pid}.tmp`;
|
|
79533
|
+
(0, import_node_fs22.writeFileSync)(temporary, `${JSON.stringify(settings, null, 2)}
|
|
79534
|
+
`, { mode: 384 });
|
|
79535
|
+
(0, import_node_fs22.renameSync)(temporary, path2);
|
|
79536
|
+
}
|
|
79537
|
+
function reconcileClaudeNativeHooks(input2) {
|
|
79538
|
+
const homeDir = input2?.homeDir ?? (0, import_node_os13.homedir)();
|
|
79539
|
+
const settingsPath = (0, import_node_path24.join)(homeDir, ".claude", "settings.json");
|
|
79540
|
+
const nodeExecutable = input2?.nodeExecutable ?? process.execPath;
|
|
79541
|
+
const cliEntry = input2?.cliEntry ?? process.argv[1];
|
|
79542
|
+
const configPath = input2?.configPath ?? process.env.ALAN_AGENT_CONFIG_PATH;
|
|
79543
|
+
if (!cliEntry) throw new Error("Cannot resolve the alan-agent CLI entrypoint");
|
|
79544
|
+
if (!configPath?.trim()) {
|
|
79545
|
+
throw new Error("Cannot install Claude hooks without an exact Alan agent config path");
|
|
79546
|
+
}
|
|
79547
|
+
const settings = readSettings(settingsPath);
|
|
79548
|
+
const currentHooks = settings.hooks && typeof settings.hooks === "object" && !Array.isArray(settings.hooks) ? settings.hooks : {};
|
|
79549
|
+
const hooks = { ...currentHooks };
|
|
79550
|
+
for (const event of CLAUDE_HOOK_EVENTS) {
|
|
79551
|
+
hooks[event] = [
|
|
79552
|
+
...withoutAlanHookGroups(currentHooks[event]),
|
|
79553
|
+
{
|
|
79554
|
+
hooks: [
|
|
79555
|
+
nativeHookCommand({
|
|
79556
|
+
configPath,
|
|
79557
|
+
nodeExecutable,
|
|
79558
|
+
cliEntry,
|
|
79559
|
+
environment: input2?.environment,
|
|
79560
|
+
platform: input2?.platform,
|
|
79561
|
+
provider: "claude_cli",
|
|
79562
|
+
event
|
|
79563
|
+
})
|
|
79564
|
+
]
|
|
79565
|
+
}
|
|
79566
|
+
];
|
|
79567
|
+
}
|
|
79568
|
+
const next = { ...settings, hooks };
|
|
79569
|
+
const previous = (0, import_node_fs22.existsSync)(settingsPath) ? (0, import_node_fs22.readFileSync)(settingsPath, "utf8") : null;
|
|
79570
|
+
const serialized = `${JSON.stringify(next, null, 2)}
|
|
79571
|
+
`;
|
|
79572
|
+
if (previous === serialized) return false;
|
|
79573
|
+
writeSettings(settingsPath, next);
|
|
79574
|
+
return true;
|
|
79575
|
+
}
|
|
79576
|
+
function removeClaudeNativeHooks(homeDir = (0, import_node_os13.homedir)()) {
|
|
79577
|
+
const settingsPath = (0, import_node_path24.join)(homeDir, ".claude", "settings.json");
|
|
79578
|
+
if (!(0, import_node_fs22.existsSync)(settingsPath)) return false;
|
|
79579
|
+
const settings = readSettings(settingsPath);
|
|
79580
|
+
if (!settings.hooks || typeof settings.hooks !== "object" || Array.isArray(settings.hooks)) {
|
|
79581
|
+
return false;
|
|
79582
|
+
}
|
|
79583
|
+
const hooks = {};
|
|
79584
|
+
let removed = false;
|
|
79585
|
+
for (const [event, groups] of Object.entries(settings.hooks)) {
|
|
79586
|
+
const retained = withoutAlanHookGroups(groups);
|
|
79587
|
+
if (Array.isArray(groups) && retained.length !== groups.length) removed = true;
|
|
79588
|
+
if (retained.length > 0) hooks[event] = retained;
|
|
79589
|
+
}
|
|
79590
|
+
if (!removed) return false;
|
|
79591
|
+
writeSettings(settingsPath, { ...settings, hooks });
|
|
79592
|
+
return true;
|
|
79593
|
+
}
|
|
79594
|
+
|
|
79595
|
+
// src/integration-cli.ts
|
|
79596
|
+
function alanMcpServers(apiUrl) {
|
|
79597
|
+
return {
|
|
79598
|
+
alan: {
|
|
79599
|
+
type: "http",
|
|
79600
|
+
url: new URL("/mcp", apiUrl).toString()
|
|
79601
|
+
}
|
|
79602
|
+
};
|
|
79603
|
+
}
|
|
79604
|
+
function runMcpIntegrationCommand(args, options = {}) {
|
|
79605
|
+
bindConfigPath(args);
|
|
79606
|
+
const action = args[0] ?? "status";
|
|
79607
|
+
const config2 = readConfig();
|
|
79608
|
+
const endpoints = resolveEndpoints(args.slice(1), config2);
|
|
79609
|
+
const service = new McpSyncService({ enabled: true });
|
|
79610
|
+
if (action === "status") {
|
|
79611
|
+
process.stdout.write(`${JSON.stringify(service.getStatus(), null, 2)}
|
|
79612
|
+
`);
|
|
79613
|
+
return;
|
|
79614
|
+
}
|
|
79615
|
+
if (action === "reconcile") {
|
|
79616
|
+
if (!config2.runtimeId || !config2.runtimeToken) {
|
|
79617
|
+
throw new Error(
|
|
79618
|
+
"Alan is not configured. Run `alan-agent login` or `alan-agent setup` first."
|
|
79619
|
+
);
|
|
79620
|
+
}
|
|
79621
|
+
const result = service.syncServers(alanMcpServers(endpoints.apiUrl), endpoints.apiUrl);
|
|
79622
|
+
if (result.errors.length > 0) throw new Error(result.errors.join("; "));
|
|
79623
|
+
const hooksUpdated = resolveCliExecutable("claude", getDaemonCliEnvironment()) ? reconcileClaudeNativeHooks({
|
|
79624
|
+
configPath: getConfigPath(),
|
|
79625
|
+
nodeExecutable: options.nativeHookRuntime?.executable,
|
|
79626
|
+
cliEntry: options.nativeHookRuntime?.cliEntry,
|
|
79627
|
+
environment: options.nativeHookRuntime?.environment
|
|
79628
|
+
}) : false;
|
|
79629
|
+
process.stdout.write(
|
|
79630
|
+
`${result.mcpServersUpdated || hooksUpdated ? "Alan CLI integrations updated" : "Alan CLI integrations already current"}.
|
|
79631
|
+
`
|
|
79632
|
+
);
|
|
79633
|
+
return;
|
|
79634
|
+
}
|
|
79635
|
+
if (action === "uninstall") {
|
|
79636
|
+
service.cleanup(toManagedMcpServers(alanMcpServers(endpoints.apiUrl)));
|
|
79637
|
+
removeClaudeNativeHooks();
|
|
79638
|
+
process.stdout.write("Removed Alan-owned MCP configuration.\n");
|
|
79639
|
+
return;
|
|
79640
|
+
}
|
|
79641
|
+
throw new Error(`Unknown integrations action: ${action}`);
|
|
79642
|
+
}
|
|
79643
|
+
|
|
78099
79644
|
// src/lifecycle-logger.ts
|
|
78100
79645
|
var AgentLifecycleLogger = class _AgentLifecycleLogger {
|
|
78101
79646
|
context;
|
|
@@ -78168,6 +79713,293 @@ function omitUndefined(record2) {
|
|
|
78168
79713
|
return Object.fromEntries(Object.entries(record2).filter(([, value2]) => value2 !== void 0));
|
|
78169
79714
|
}
|
|
78170
79715
|
|
|
79716
|
+
// src/native-hook-command.ts
|
|
79717
|
+
var import_node_crypto19 = require("crypto");
|
|
79718
|
+
var import_node_fs23 = require("fs");
|
|
79719
|
+
var import_node_path25 = require("path");
|
|
79720
|
+
|
|
79721
|
+
// src/native-session.ts
|
|
79722
|
+
var NATIVE_SESSION_PROVIDER_CAPABILITIES = [
|
|
79723
|
+
{
|
|
79724
|
+
providerKind: "claude_cli",
|
|
79725
|
+
provider: "claude",
|
|
79726
|
+
command: "claude",
|
|
79727
|
+
capabilities: {
|
|
79728
|
+
mcp: true,
|
|
79729
|
+
lifecycleHooks: true,
|
|
79730
|
+
transcriptDelta: true,
|
|
79731
|
+
subagents: true,
|
|
79732
|
+
sessionEnd: true,
|
|
79733
|
+
state: "supported"
|
|
79734
|
+
},
|
|
79735
|
+
supportedHookEvents: [
|
|
79736
|
+
"SessionStart",
|
|
79737
|
+
"UserPromptSubmit",
|
|
79738
|
+
"PostToolUse",
|
|
79739
|
+
"Stop",
|
|
79740
|
+
"SubagentStart",
|
|
79741
|
+
"SubagentStop",
|
|
79742
|
+
"SessionEnd"
|
|
79743
|
+
]
|
|
79744
|
+
},
|
|
79745
|
+
{
|
|
79746
|
+
providerKind: "codex_app_server",
|
|
79747
|
+
provider: "codex",
|
|
79748
|
+
command: "codex",
|
|
79749
|
+
capabilities: {
|
|
79750
|
+
mcp: true,
|
|
79751
|
+
lifecycleHooks: false,
|
|
79752
|
+
transcriptDelta: false,
|
|
79753
|
+
subagents: false,
|
|
79754
|
+
sessionEnd: false,
|
|
79755
|
+
state: "degraded",
|
|
79756
|
+
reason: "Codex exposes a turn-complete notification, not full lifecycle hooks."
|
|
79757
|
+
},
|
|
79758
|
+
supportedHookEvents: ["agent-turn-complete"]
|
|
79759
|
+
},
|
|
79760
|
+
{
|
|
79761
|
+
providerKind: "copilot_cli",
|
|
79762
|
+
provider: "copilot",
|
|
79763
|
+
command: "copilot",
|
|
79764
|
+
capabilities: unsupportedMcpOnlyCapability(),
|
|
79765
|
+
supportedHookEvents: []
|
|
79766
|
+
},
|
|
79767
|
+
{
|
|
79768
|
+
providerKind: "cursor_agent_cli",
|
|
79769
|
+
provider: "cursor",
|
|
79770
|
+
command: "cursor-agent",
|
|
79771
|
+
capabilities: unsupportedMcpOnlyCapability(),
|
|
79772
|
+
supportedHookEvents: []
|
|
79773
|
+
},
|
|
79774
|
+
{
|
|
79775
|
+
providerKind: "antigravity_cli",
|
|
79776
|
+
provider: "antigravity",
|
|
79777
|
+
command: "agy",
|
|
79778
|
+
capabilities: unsupportedMcpOnlyCapability(),
|
|
79779
|
+
supportedHookEvents: []
|
|
79780
|
+
},
|
|
79781
|
+
{
|
|
79782
|
+
providerKind: "kimi_cli",
|
|
79783
|
+
provider: "kimi",
|
|
79784
|
+
command: "kimi-cli",
|
|
79785
|
+
capabilities: unsupportedMcpOnlyCapability(),
|
|
79786
|
+
supportedHookEvents: []
|
|
79787
|
+
},
|
|
79788
|
+
{
|
|
79789
|
+
providerKind: "grok_cli",
|
|
79790
|
+
provider: "grok",
|
|
79791
|
+
command: "grok",
|
|
79792
|
+
capabilities: unsupportedMcpOnlyCapability(),
|
|
79793
|
+
supportedHookEvents: []
|
|
79794
|
+
},
|
|
79795
|
+
{
|
|
79796
|
+
providerKind: "opencode_cli",
|
|
79797
|
+
provider: "opencode",
|
|
79798
|
+
command: "opencode",
|
|
79799
|
+
capabilities: unsupportedMcpOnlyCapability(),
|
|
79800
|
+
supportedHookEvents: []
|
|
79801
|
+
},
|
|
79802
|
+
{
|
|
79803
|
+
providerKind: "droid_cli",
|
|
79804
|
+
provider: "droid",
|
|
79805
|
+
command: "droid",
|
|
79806
|
+
capabilities: unsupportedMcpOnlyCapability(),
|
|
79807
|
+
supportedHookEvents: []
|
|
79808
|
+
},
|
|
79809
|
+
{
|
|
79810
|
+
providerKind: "supatest_cli",
|
|
79811
|
+
provider: "supatest",
|
|
79812
|
+
command: "supatest",
|
|
79813
|
+
capabilities: unsupportedMcpOnlyCapability(),
|
|
79814
|
+
supportedHookEvents: []
|
|
79815
|
+
}
|
|
79816
|
+
];
|
|
79817
|
+
function unsupportedMcpOnlyCapability() {
|
|
79818
|
+
return {
|
|
79819
|
+
mcp: true,
|
|
79820
|
+
lifecycleHooks: false,
|
|
79821
|
+
transcriptDelta: false,
|
|
79822
|
+
subagents: false,
|
|
79823
|
+
sessionEnd: false,
|
|
79824
|
+
state: "unsupported",
|
|
79825
|
+
reason: "No native hook adapter is installed for this provider."
|
|
79826
|
+
};
|
|
79827
|
+
}
|
|
79828
|
+
var CAPABILITIES_BY_PROVIDER_KIND = new Map(
|
|
79829
|
+
NATIVE_SESSION_PROVIDER_CAPABILITIES.map((capability) => [capability.providerKind, capability])
|
|
79830
|
+
);
|
|
79831
|
+
function isNativeSessionProviderKind(value2) {
|
|
79832
|
+
return CAPABILITIES_BY_PROVIDER_KIND.has(value2);
|
|
79833
|
+
}
|
|
79834
|
+
|
|
79835
|
+
// src/native-hook-command.ts
|
|
79836
|
+
var MAX_NATIVE_HOOK_STDIN_BYTES = 1024 * 1024;
|
|
79837
|
+
function argumentValue(args, name) {
|
|
79838
|
+
const index = args.indexOf(name);
|
|
79839
|
+
if (index >= 0) return args[index + 1];
|
|
79840
|
+
const prefix = `${name}=`;
|
|
79841
|
+
return args.find((arg) => arg.startsWith(prefix))?.slice(prefix.length);
|
|
79842
|
+
}
|
|
79843
|
+
function stringField4(payload, ...names) {
|
|
79844
|
+
for (const name of names) {
|
|
79845
|
+
const value2 = payload[name];
|
|
79846
|
+
if (typeof value2 === "string" && value2.trim()) return value2.trim();
|
|
79847
|
+
}
|
|
79848
|
+
return void 0;
|
|
79849
|
+
}
|
|
79850
|
+
function nonNegativeIntegerField(payload, ...names) {
|
|
79851
|
+
for (const name of names) {
|
|
79852
|
+
const value2 = payload[name];
|
|
79853
|
+
if (typeof value2 === "number" && Number.isSafeInteger(value2) && value2 >= 0) return value2;
|
|
79854
|
+
}
|
|
79855
|
+
return void 0;
|
|
79856
|
+
}
|
|
79857
|
+
function parseNativeHookEnvelope(input2) {
|
|
79858
|
+
if (!isNativeSessionProviderKind(input2.provider)) {
|
|
79859
|
+
throw new Error(`unsupported native session provider: ${input2.provider}`);
|
|
79860
|
+
}
|
|
79861
|
+
const rawHookEvent = input2.hookEvent ?? stringField4(input2.payload, "hook_event_name", "hookEvent", "type") ?? "notification";
|
|
79862
|
+
const cwd = stringField4(input2.payload, "cwd", "working_directory", "workingDirectory");
|
|
79863
|
+
const transcriptPath = stringField4(input2.payload, "transcript_path", "transcriptPath");
|
|
79864
|
+
const capability = NATIVE_SESSION_PROVIDER_CAPABILITIES.find(
|
|
79865
|
+
(candidate) => candidate.providerKind === input2.provider
|
|
79866
|
+
);
|
|
79867
|
+
if (!capability) throw new Error(`unsupported native session provider: ${input2.provider}`);
|
|
79868
|
+
const externalSessionId = stringField4(
|
|
79869
|
+
input2.payload,
|
|
79870
|
+
"session_id",
|
|
79871
|
+
"sessionId",
|
|
79872
|
+
"conversation_id",
|
|
79873
|
+
"conversationId",
|
|
79874
|
+
"thread_id",
|
|
79875
|
+
"threadId"
|
|
79876
|
+
);
|
|
79877
|
+
if (!externalSessionId) throw new Error("hook payload is missing a provider session id");
|
|
79878
|
+
const cursor = stringField4(input2.payload, "source_cursor", "sourceCursor") ?? nonNegativeIntegerField(input2.payload, "byte_offset", "byteOffset")?.toString() ?? nonNegativeIntegerField(input2.payload, "sequence", "seq")?.toString();
|
|
79879
|
+
return {
|
|
79880
|
+
schemaVersion: 1,
|
|
79881
|
+
eventId: (0, import_node_crypto19.randomUUID)(),
|
|
79882
|
+
provider: capability.provider,
|
|
79883
|
+
eventType: normalizeHookEvent(rawHookEvent),
|
|
79884
|
+
observedAt: (input2.now ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
79885
|
+
externalSessionId,
|
|
79886
|
+
parentExternalSessionId: stringField4(input2.payload, "parent_session_id", "parentSessionId"),
|
|
79887
|
+
...cwd ? { cwd: (0, import_node_path25.resolve)(cwd) } : {},
|
|
79888
|
+
...transcriptPath ? { transcriptPath: (0, import_node_path25.resolve)(transcriptPath) } : {},
|
|
79889
|
+
...cursor ? { sourceCursor: cursor } : {},
|
|
79890
|
+
providerPayload: input2.payload
|
|
79891
|
+
};
|
|
79892
|
+
}
|
|
79893
|
+
function normalizeHookEvent(raw) {
|
|
79894
|
+
switch (raw.toLowerCase().replaceAll("-", "_").replaceAll(" ", "_")) {
|
|
79895
|
+
case "sessionstart":
|
|
79896
|
+
case "session_start":
|
|
79897
|
+
return "session_start";
|
|
79898
|
+
case "userpromptsubmit":
|
|
79899
|
+
case "user_prompt":
|
|
79900
|
+
return "user_prompt";
|
|
79901
|
+
case "subagentstart":
|
|
79902
|
+
case "subagent_start":
|
|
79903
|
+
return "subagent_start";
|
|
79904
|
+
case "subagentstop":
|
|
79905
|
+
case "subagent_end":
|
|
79906
|
+
return "subagent_end";
|
|
79907
|
+
case "sessionend":
|
|
79908
|
+
case "session_end":
|
|
79909
|
+
return "session_end";
|
|
79910
|
+
case "stop":
|
|
79911
|
+
case "agent_turn_complete":
|
|
79912
|
+
return "stop";
|
|
79913
|
+
default:
|
|
79914
|
+
return "transcript_update";
|
|
79915
|
+
}
|
|
79916
|
+
}
|
|
79917
|
+
function readBoundedHookPayload(fd = 0, maxBytes = MAX_NATIVE_HOOK_STDIN_BYTES) {
|
|
79918
|
+
const chunks = [];
|
|
79919
|
+
let totalBytes = 0;
|
|
79920
|
+
while (true) {
|
|
79921
|
+
const chunk = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1 - totalBytes));
|
|
79922
|
+
const bytesRead = (0, import_node_fs23.readSync)(fd, chunk, 0, chunk.byteLength, null);
|
|
79923
|
+
if (bytesRead === 0) break;
|
|
79924
|
+
totalBytes += bytesRead;
|
|
79925
|
+
if (totalBytes > maxBytes) throw new Error(`hook input exceeds ${maxBytes} bytes`);
|
|
79926
|
+
chunks.push(chunk.subarray(0, bytesRead));
|
|
79927
|
+
}
|
|
79928
|
+
const body = Buffer.concat(chunks, totalBytes);
|
|
79929
|
+
if (body.byteLength === 0) return {};
|
|
79930
|
+
const parsed = JSON.parse(body.toString("utf8"));
|
|
79931
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
79932
|
+
throw new Error("hook input must be a JSON object");
|
|
79933
|
+
}
|
|
79934
|
+
return parsed;
|
|
79935
|
+
}
|
|
79936
|
+
function enqueueNativeHookFromConfig(input2) {
|
|
79937
|
+
if (process.env.ALAN_MANAGED_RUN === "1" || process.env.ALAN_RUN_ID?.trim()) return false;
|
|
79938
|
+
bindConfigPath(input2.args);
|
|
79939
|
+
const config2 = input2.config ?? readConfig();
|
|
79940
|
+
const provider = argumentValue(input2.args, "--provider");
|
|
79941
|
+
if (!provider) throw new Error("hook requires --provider");
|
|
79942
|
+
if (!config2.eventSpoolKey) return false;
|
|
79943
|
+
const envelope = parseNativeHookEnvelope({
|
|
79944
|
+
provider,
|
|
79945
|
+
hookEvent: argumentValue(input2.args, "--event"),
|
|
79946
|
+
payload: input2.payload
|
|
79947
|
+
});
|
|
79948
|
+
new EncryptedNativeHookInbox(nativeHookInboxPath(getConfigPath()), config2.eventSpoolKey).enqueue(
|
|
79949
|
+
envelope
|
|
79950
|
+
);
|
|
79951
|
+
return true;
|
|
79952
|
+
}
|
|
79953
|
+
function runNativeHookCommand(args) {
|
|
79954
|
+
try {
|
|
79955
|
+
const payload = readBoundedHookPayload();
|
|
79956
|
+
const enqueued = enqueueNativeHookFromConfig({ args, payload });
|
|
79957
|
+
const hookEvent = argumentValue(args, "--event")?.toLowerCase();
|
|
79958
|
+
if (enqueued && (hookEvent === "userpromptsubmit" || hookEvent === "user_prompt")) {
|
|
79959
|
+
const providerKind = argumentValue(args, "--provider");
|
|
79960
|
+
const externalSessionId = stringField4(payload, "session_id", "sessionId");
|
|
79961
|
+
const capability = providerKind ? NATIVE_SESSION_PROVIDER_CAPABILITIES.find(
|
|
79962
|
+
(candidate) => candidate.providerKind === providerKind
|
|
79963
|
+
) : void 0;
|
|
79964
|
+
if (capability && externalSessionId) {
|
|
79965
|
+
process.stdout.write(
|
|
79966
|
+
`${JSON.stringify({
|
|
79967
|
+
hookSpecificOutput: {
|
|
79968
|
+
hookEventName: "UserPromptSubmit",
|
|
79969
|
+
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.`
|
|
79970
|
+
}
|
|
79971
|
+
})}
|
|
79972
|
+
`
|
|
79973
|
+
);
|
|
79974
|
+
}
|
|
79975
|
+
}
|
|
79976
|
+
} catch (error61) {
|
|
79977
|
+
if (process.env.ALAN_HOOK_DEBUG === "1") {
|
|
79978
|
+
process.stderr.write(
|
|
79979
|
+
`[alan-agent] native hook skipped: ${error61 instanceof Error ? error61.message : String(error61)}
|
|
79980
|
+
`
|
|
79981
|
+
);
|
|
79982
|
+
}
|
|
79983
|
+
}
|
|
79984
|
+
}
|
|
79985
|
+
function collectNativeIntegrationStatus(args = []) {
|
|
79986
|
+
bindConfigPath(args);
|
|
79987
|
+
const config2 = readConfig();
|
|
79988
|
+
const env = getDaemonCliEnvironment();
|
|
79989
|
+
return NATIVE_SESSION_PROVIDER_CAPABILITIES.map((capability) => ({
|
|
79990
|
+
provider: capability.providerKind,
|
|
79991
|
+
command: capability.command,
|
|
79992
|
+
installed: resolveCliExecutable(capability.command, env) !== null,
|
|
79993
|
+
hookCoverage: capability.capabilities.lifecycleHooks ? "lifecycle" : capability.capabilities.state === "degraded" ? "notification" : "none",
|
|
79994
|
+
transcriptStreaming: capability.capabilities.transcriptDelta,
|
|
79995
|
+
inboxReady: Boolean(config2.eventSpoolKey)
|
|
79996
|
+
}));
|
|
79997
|
+
}
|
|
79998
|
+
function printNativeIntegrationStatus(args) {
|
|
79999
|
+
process.stdout.write(`${JSON.stringify(collectNativeIntegrationStatus(args), null, 2)}
|
|
80000
|
+
`);
|
|
80001
|
+
}
|
|
80002
|
+
|
|
78171
80003
|
// src/resume-fallback-env.ts
|
|
78172
80004
|
var RESUME_FALLBACK_CONTEXT_ENV = "ALAN_RESUME_FALLBACK_CONTEXT_B64";
|
|
78173
80005
|
function decodeResumeFallbackContext(encoded) {
|
|
@@ -78181,7 +80013,7 @@ function decodeResumeFallbackContext(encoded) {
|
|
|
78181
80013
|
}
|
|
78182
80014
|
|
|
78183
80015
|
// src/sandbox.ts
|
|
78184
|
-
var
|
|
80016
|
+
var import_node_os14 = require("os");
|
|
78185
80017
|
|
|
78186
80018
|
// src/execution-activity-tracker.ts
|
|
78187
80019
|
var ExecutionActivityTracker = class {
|
|
@@ -78506,11 +80338,11 @@ async function readErrorBody(response) {
|
|
|
78506
80338
|
}
|
|
78507
80339
|
}
|
|
78508
80340
|
function delay2(ms) {
|
|
78509
|
-
return new Promise((
|
|
80341
|
+
return new Promise((resolve15) => setTimeout(resolve15, ms));
|
|
78510
80342
|
}
|
|
78511
80343
|
|
|
78512
80344
|
// src/web-presenter.ts
|
|
78513
|
-
var
|
|
80345
|
+
var import_node_crypto20 = require("crypto");
|
|
78514
80346
|
var WebPresenter = class {
|
|
78515
80347
|
constructor(wsClient, conversationId, cwd) {
|
|
78516
80348
|
this.conversationId = conversationId;
|
|
@@ -78696,9 +80528,12 @@ var WebPresenter = class {
|
|
|
78696
80528
|
this.applicationProblem = createAlanProblem({
|
|
78697
80529
|
domain: "application",
|
|
78698
80530
|
code: input2.code,
|
|
78699
|
-
id: (0,
|
|
80531
|
+
id: (0, import_node_crypto20.randomUUID)(),
|
|
78700
80532
|
title: APPLICATION_PROBLEM_TITLES[input2.code],
|
|
78701
80533
|
detail: input2.message,
|
|
80534
|
+
// The schema's slot for the technical cause, so the problem carries it
|
|
80535
|
+
// without putting an SDK error message in front of the person.
|
|
80536
|
+
...input2.diagnostic ? { diagnosticsRef: input2.diagnostic } : {},
|
|
78702
80537
|
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
78703
80538
|
severity: "terminal",
|
|
78704
80539
|
scope: {
|
|
@@ -78763,7 +80598,7 @@ var WebPresenter = class {
|
|
|
78763
80598
|
};
|
|
78764
80599
|
|
|
78765
80600
|
// src/ws-client.ts
|
|
78766
|
-
var
|
|
80601
|
+
var import_node_crypto22 = require("crypto");
|
|
78767
80602
|
|
|
78768
80603
|
// ../shared/dist/agent-liveness.js
|
|
78769
80604
|
var AGENT_LIVENESS_PROTOCOL_VERSION = 1;
|
|
@@ -78791,8 +80626,8 @@ var agentProbeAckSchema = external_exports.object({
|
|
|
78791
80626
|
});
|
|
78792
80627
|
|
|
78793
80628
|
// src/sandbox-outbox.ts
|
|
78794
|
-
var
|
|
78795
|
-
var
|
|
80629
|
+
var import_node_crypto21 = require("crypto");
|
|
80630
|
+
var import_node_fs24 = require("fs");
|
|
78796
80631
|
var SANDBOX_EVENT_OUTBOX_ENV = "ALAN_EVENT_OUTBOX_ENABLED";
|
|
78797
80632
|
function isSandboxEventOutboxEnabled(env = process.env) {
|
|
78798
80633
|
return env[SANDBOX_EVENT_OUTBOX_ENV] === "true";
|
|
@@ -78801,7 +80636,7 @@ function deriveSandboxOutboxKey(sessionToken) {
|
|
|
78801
80636
|
if (!sessionToken) {
|
|
78802
80637
|
throw new Error("sandbox event outbox requires ALAN_SESSION_TOKEN for key derivation");
|
|
78803
80638
|
}
|
|
78804
|
-
return (0,
|
|
80639
|
+
return (0, import_node_crypto21.createHash)("sha256").update(sessionToken, "utf8").digest("base64url");
|
|
78805
80640
|
}
|
|
78806
80641
|
function sandboxOutboxSpoolPath(conversationId) {
|
|
78807
80642
|
return `/tmp/alan-agent-outbox-${conversationId}.enc`;
|
|
@@ -78815,7 +80650,7 @@ function createSandboxEventOutbox(input2) {
|
|
|
78815
80650
|
input2.pushLog?.(
|
|
78816
80651
|
`sandbox_outbox_spool_reset ${error61 instanceof Error ? error61.message : String(error61)}`
|
|
78817
80652
|
);
|
|
78818
|
-
(0,
|
|
80653
|
+
(0, import_node_fs24.rmSync)(path2, { force: true });
|
|
78819
80654
|
return new EncryptedEventOutbox(path2, key, input2.pushLog, input2.options);
|
|
78820
80655
|
}
|
|
78821
80656
|
}
|
|
@@ -78953,7 +80788,7 @@ var WSClient = class {
|
|
|
78953
80788
|
}
|
|
78954
80789
|
socket;
|
|
78955
80790
|
/** Stable id for this agent process; fences stale heartbeats server-side. */
|
|
78956
|
-
agentSessionId = (0,
|
|
80791
|
+
agentSessionId = (0, import_node_crypto22.randomUUID)();
|
|
78957
80792
|
/** Monotonic heartbeat sequence — advances on every hello + heartbeat. */
|
|
78958
80793
|
heartbeatSeq = 0;
|
|
78959
80794
|
heartbeatTimer = null;
|
|
@@ -79037,9 +80872,9 @@ var WSClient = class {
|
|
|
79037
80872
|
});
|
|
79038
80873
|
}
|
|
79039
80874
|
waitForConnection(timeoutMs = 15e3) {
|
|
79040
|
-
return new Promise((
|
|
80875
|
+
return new Promise((resolve15, reject) => {
|
|
79041
80876
|
if (this.socket.connected) {
|
|
79042
|
-
|
|
80877
|
+
resolve15();
|
|
79043
80878
|
return;
|
|
79044
80879
|
}
|
|
79045
80880
|
let lastError;
|
|
@@ -79050,7 +80885,7 @@ var WSClient = class {
|
|
|
79050
80885
|
};
|
|
79051
80886
|
const handleConnect = () => {
|
|
79052
80887
|
cleanup();
|
|
79053
|
-
|
|
80888
|
+
resolve15();
|
|
79054
80889
|
};
|
|
79055
80890
|
const handleConnectError = (error61) => {
|
|
79056
80891
|
lastError = error61;
|
|
@@ -79284,9 +81119,10 @@ var WSClient = class {
|
|
|
79284
81119
|
|
|
79285
81120
|
// src/sandbox.ts
|
|
79286
81121
|
var RequiredToolsUnavailableError = class extends Error {
|
|
79287
|
-
constructor(code, message) {
|
|
81122
|
+
constructor(code, message, diagnostic) {
|
|
79288
81123
|
super(message);
|
|
79289
81124
|
this.code = code;
|
|
81125
|
+
this.diagnostic = diagnostic;
|
|
79290
81126
|
this.name = "RequiredToolsUnavailableError";
|
|
79291
81127
|
}
|
|
79292
81128
|
};
|
|
@@ -79383,9 +81219,9 @@ async function runSandbox(config2) {
|
|
|
79383
81219
|
onUserMessage: (payload) => {
|
|
79384
81220
|
if (payload === null) {
|
|
79385
81221
|
if (pendingMessageResolve) {
|
|
79386
|
-
const
|
|
81222
|
+
const resolve15 = pendingMessageResolve;
|
|
79387
81223
|
pendingMessageResolve = null;
|
|
79388
|
-
|
|
81224
|
+
resolve15(null);
|
|
79389
81225
|
}
|
|
79390
81226
|
return;
|
|
79391
81227
|
}
|
|
@@ -79407,9 +81243,9 @@ async function runSandbox(config2) {
|
|
|
79407
81243
|
fileCount: payload.files?.length ?? 0
|
|
79408
81244
|
});
|
|
79409
81245
|
if (pendingMessageResolve) {
|
|
79410
|
-
const
|
|
81246
|
+
const resolve15 = pendingMessageResolve;
|
|
79411
81247
|
pendingMessageResolve = null;
|
|
79412
|
-
|
|
81248
|
+
resolve15(payload);
|
|
79413
81249
|
} else {
|
|
79414
81250
|
queuedMessages.push(payload);
|
|
79415
81251
|
}
|
|
@@ -79474,7 +81310,7 @@ async function runSandbox(config2) {
|
|
|
79474
81310
|
}
|
|
79475
81311
|
const presenter = new WebPresenter(wsClient, config2.sessionId, config2.projectPath);
|
|
79476
81312
|
presenter.setSuppressSessionLifecycle(true);
|
|
79477
|
-
const runtimeSkillManager = await RuntimeSkillManager.open({ homeDirectory: (0,
|
|
81313
|
+
const runtimeSkillManager = await RuntimeSkillManager.open({ homeDirectory: (0, import_node_os14.homedir)() });
|
|
79478
81314
|
let lastProviderSessionId = config2.providerSessionId;
|
|
79479
81315
|
let resumeFallbackContext = config2.resumeFallbackContext;
|
|
79480
81316
|
let activeBackendKind = config2.backendKind || "claude_cli";
|
|
@@ -79488,12 +81324,13 @@ async function runSandbox(config2) {
|
|
|
79488
81324
|
let currentAlanMcp;
|
|
79489
81325
|
while (!stopped) {
|
|
79490
81326
|
if (currentTask?.trim() || currentSkillInvocation) {
|
|
81327
|
+
const activeRunId = currentRunId ?? config2.runId;
|
|
79491
81328
|
const agent = new CoreAgent(presenter, {
|
|
79492
81329
|
backendKind: activeBackendKind,
|
|
81330
|
+
runId: activeRunId,
|
|
79493
81331
|
providerApiKey: activeProviderApiKey
|
|
79494
81332
|
});
|
|
79495
81333
|
currentAgent = agent;
|
|
79496
|
-
const activeRunId = currentRunId ?? config2.runId;
|
|
79497
81334
|
const rootExecutionActivity = executionActivityTracker && activeRunId && currentTaskId ? executionActivityTracker.register({
|
|
79498
81335
|
ownerId: `root:${activeRunId}`,
|
|
79499
81336
|
leaseRunId: activeRunId,
|
|
@@ -79558,10 +81395,17 @@ async function runSandbox(config2) {
|
|
|
79558
81395
|
lifecycle.warn("sandbox_agent_mcp_readiness_failed", {
|
|
79559
81396
|
backendKind: activeBackendKind,
|
|
79560
81397
|
code: readiness.code,
|
|
81398
|
+
// The protocol branch cannot describe itself; without this the
|
|
81399
|
+
// event names a category and nothing else.
|
|
81400
|
+
...readiness.diagnostic ? { diagnostic: readiness.diagnostic } : {},
|
|
79561
81401
|
stage: readiness.stage,
|
|
79562
81402
|
retryable: readiness.retryable
|
|
79563
81403
|
});
|
|
79564
|
-
throw new RequiredToolsUnavailableError(
|
|
81404
|
+
throw new RequiredToolsUnavailableError(
|
|
81405
|
+
readiness.code,
|
|
81406
|
+
readiness.message,
|
|
81407
|
+
readiness.diagnostic
|
|
81408
|
+
);
|
|
79565
81409
|
}
|
|
79566
81410
|
} else if (currentRunId ?? config2.runId) {
|
|
79567
81411
|
lifecycle.warn("sandbox_agent_mcp_contract_missing", {
|
|
@@ -79652,7 +81496,11 @@ async function runSandbox(config2) {
|
|
|
79652
81496
|
error: errorMessage
|
|
79653
81497
|
};
|
|
79654
81498
|
if (error61 instanceof RequiredToolsUnavailableError) {
|
|
79655
|
-
presenter.onApplicationError({
|
|
81499
|
+
presenter.onApplicationError({
|
|
81500
|
+
code: error61.code,
|
|
81501
|
+
message: error61.message,
|
|
81502
|
+
...error61.diagnostic ? { diagnostic: error61.diagnostic } : {}
|
|
81503
|
+
});
|
|
79656
81504
|
} else {
|
|
79657
81505
|
presenter.onError(errorMessage, false);
|
|
79658
81506
|
}
|
|
@@ -79681,8 +81529,8 @@ async function runSandbox(config2) {
|
|
|
79681
81529
|
});
|
|
79682
81530
|
if (stopped) break;
|
|
79683
81531
|
}
|
|
79684
|
-
const nextPayload = queuedMessages.shift() ?? await new Promise((
|
|
79685
|
-
pendingMessageResolve =
|
|
81532
|
+
const nextPayload = queuedMessages.shift() ?? await new Promise((resolve15) => {
|
|
81533
|
+
pendingMessageResolve = resolve15;
|
|
79686
81534
|
});
|
|
79687
81535
|
if (nextPayload === null || stopped) {
|
|
79688
81536
|
break;
|
|
@@ -79761,9 +81609,9 @@ async function runSandbox(config2) {
|
|
|
79761
81609
|
|
|
79762
81610
|
// src/service-manager.ts
|
|
79763
81611
|
var import_node_child_process9 = require("child_process");
|
|
79764
|
-
var
|
|
79765
|
-
var
|
|
79766
|
-
var
|
|
81612
|
+
var import_node_fs25 = require("fs");
|
|
81613
|
+
var import_node_os15 = require("os");
|
|
81614
|
+
var import_node_path26 = require("path");
|
|
79767
81615
|
var SERVICE_LABEL = "ai.tryalan.agent";
|
|
79768
81616
|
var SYSTEMD_UNIT = "alan-agent.service";
|
|
79769
81617
|
var WINDOWS_TASK = "Alan Agent";
|
|
@@ -79783,9 +81631,16 @@ function buildDaemonServicePlan(input2) {
|
|
|
79783
81631
|
if (userId === void 0) throw new Error("Cannot determine the current macOS user ID");
|
|
79784
81632
|
const domain2 = `gui/${userId}`;
|
|
79785
81633
|
const serviceTarget = `${domain2}/${SERVICE_LABEL}`;
|
|
79786
|
-
const manifestPath = (0,
|
|
79787
|
-
const logDir = (0,
|
|
81634
|
+
const manifestPath = (0, import_node_path26.join)(input2.homeDir, "Library", "LaunchAgents", `${SERVICE_LABEL}.plist`);
|
|
81635
|
+
const logDir = (0, import_node_path26.join)(input2.homeDir, ".alan", "agent", "logs");
|
|
79788
81636
|
const programArguments = daemonArgs.map((arg) => ` <string>${xml(arg)}</string>`).join("\n");
|
|
81637
|
+
const environmentEntries = Object.entries(input2.environment ?? {}).map(([key, value2]) => ` <key>${xml(key)}</key>
|
|
81638
|
+
<string>${xml(value2)}</string>`).join("\n");
|
|
81639
|
+
const environmentBlock = environmentEntries ? ` <key>EnvironmentVariables</key>
|
|
81640
|
+
<dict>
|
|
81641
|
+
${environmentEntries}
|
|
81642
|
+
</dict>
|
|
81643
|
+
` : "";
|
|
79789
81644
|
const manifest = `<?xml version="1.0" encoding="UTF-8"?>
|
|
79790
81645
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
79791
81646
|
<plist version="1.0">
|
|
@@ -79796,7 +81651,7 @@ function buildDaemonServicePlan(input2) {
|
|
|
79796
81651
|
<array>
|
|
79797
81652
|
${programArguments}
|
|
79798
81653
|
</array>
|
|
79799
|
-
<key>RunAtLoad</key>
|
|
81654
|
+
${environmentBlock} <key>RunAtLoad</key>
|
|
79800
81655
|
<true/>
|
|
79801
81656
|
<key>KeepAlive</key>
|
|
79802
81657
|
<dict>
|
|
@@ -79806,9 +81661,9 @@ ${programArguments}
|
|
|
79806
81661
|
<key>ThrottleInterval</key>
|
|
79807
81662
|
<integer>5</integer>
|
|
79808
81663
|
<key>StandardOutPath</key>
|
|
79809
|
-
<string>${xml((0,
|
|
81664
|
+
<string>${xml((0, import_node_path26.join)(logDir, "daemon.log"))}</string>
|
|
79810
81665
|
<key>StandardErrorPath</key>
|
|
79811
|
-
<string>${xml((0,
|
|
81666
|
+
<string>${xml((0, import_node_path26.join)(logDir, "daemon-error.log"))}</string>
|
|
79812
81667
|
</dict>
|
|
79813
81668
|
</plist>
|
|
79814
81669
|
`;
|
|
@@ -79847,7 +81702,8 @@ ${programArguments}
|
|
|
79847
81702
|
};
|
|
79848
81703
|
}
|
|
79849
81704
|
if (input2.platform === "linux") {
|
|
79850
|
-
const manifestPath = (0,
|
|
81705
|
+
const manifestPath = (0, import_node_path26.join)(input2.homeDir, ".config", "systemd", "user", SYSTEMD_UNIT);
|
|
81706
|
+
const environmentLines = Object.entries(input2.environment ?? {}).map(([key, value2]) => `Environment=${systemdArg(`${key}=${value2}`)}`).join("\n");
|
|
79851
81707
|
const manifest = `[Unit]
|
|
79852
81708
|
Description=Alan local agent daemon
|
|
79853
81709
|
After=network-online.target
|
|
@@ -79857,7 +81713,8 @@ StartLimitBurst=3
|
|
|
79857
81713
|
|
|
79858
81714
|
[Service]
|
|
79859
81715
|
Type=simple
|
|
79860
|
-
|
|
81716
|
+
${environmentLines ? `${environmentLines}
|
|
81717
|
+
` : ""}ExecStart=${daemonArgs.map(systemdArg).join(" ")}
|
|
79861
81718
|
Restart=on-failure
|
|
79862
81719
|
RestartSec=5
|
|
79863
81720
|
|
|
@@ -79898,9 +81755,11 @@ WantedBy=default.target
|
|
|
79898
81755
|
}
|
|
79899
81756
|
};
|
|
79900
81757
|
}
|
|
79901
|
-
const
|
|
81758
|
+
const environmentPrefix = Object.entries(input2.environment ?? {}).map(([key, value2]) => `$env:${key} = ${powershellLiteral(value2)}`).join("; ");
|
|
81759
|
+
const directArgument = [input2.cliEntry, "daemon", "--profile", input2.profile].map((part) => part.includes(" ") ? `"${part}"` : part).join(" ");
|
|
81760
|
+
const executable = powershellLiteral(environmentPrefix ? "powershell.exe" : input2.nodeExecutable);
|
|
79902
81761
|
const argument = powershellLiteral(
|
|
79903
|
-
|
|
81762
|
+
environmentPrefix ? `-NoProfile -NonInteractive -WindowStyle Hidden -Command "${environmentPrefix}; & ${powershellLiteral(input2.nodeExecutable)} ${powershellLiteral(input2.cliEntry)} daemon --profile ${input2.profile}"` : directArgument
|
|
79904
81763
|
);
|
|
79905
81764
|
const installScript = [
|
|
79906
81765
|
`$action = New-ScheduledTaskAction -Execute ${executable} -Argument ${argument}`,
|
|
@@ -79943,31 +81802,32 @@ WantedBy=default.target
|
|
|
79943
81802
|
}
|
|
79944
81803
|
};
|
|
79945
81804
|
}
|
|
79946
|
-
function currentServicePlan(args) {
|
|
79947
|
-
const currentPlatform = (0,
|
|
81805
|
+
function currentServicePlan(args, runtime) {
|
|
81806
|
+
const currentPlatform = (0, import_node_os15.platform)();
|
|
79948
81807
|
if (currentPlatform !== "darwin" && currentPlatform !== "linux" && currentPlatform !== "win32") {
|
|
79949
81808
|
throw new Error(`Daemon service installation is not supported on ${currentPlatform}`);
|
|
79950
81809
|
}
|
|
79951
81810
|
const profile = resolveEndpointProfileFromArgs(args);
|
|
79952
|
-
const cliEntry = process.argv[1];
|
|
81811
|
+
const cliEntry = runtime?.cliEntry ?? process.argv[1];
|
|
79953
81812
|
if (!cliEntry) throw new Error("Cannot determine the alan-agent executable path");
|
|
79954
81813
|
return buildDaemonServicePlan({
|
|
79955
81814
|
platform: currentPlatform,
|
|
79956
|
-
homeDir: (0,
|
|
79957
|
-
nodeExecutable: process.execPath,
|
|
79958
|
-
cliEntry: (0,
|
|
79959
|
-
profile
|
|
81815
|
+
homeDir: (0, import_node_os15.homedir)(),
|
|
81816
|
+
nodeExecutable: runtime?.executable ?? process.execPath,
|
|
81817
|
+
cliEntry: (0, import_node_path26.resolve)(cliEntry),
|
|
81818
|
+
profile,
|
|
81819
|
+
environment: runtime?.environment
|
|
79960
81820
|
});
|
|
79961
81821
|
}
|
|
79962
81822
|
function writeManifest(path2, contents) {
|
|
79963
|
-
(0,
|
|
81823
|
+
(0, import_node_fs25.mkdirSync)((0, import_node_path26.dirname)(path2), { recursive: true, mode: 448 });
|
|
79964
81824
|
const pendingPath = `${path2}.pending-${process.pid}`;
|
|
79965
81825
|
try {
|
|
79966
|
-
(0,
|
|
79967
|
-
(0,
|
|
79968
|
-
(0,
|
|
81826
|
+
(0, import_node_fs25.writeFileSync)(pendingPath, contents, { mode: 384 });
|
|
81827
|
+
(0, import_node_fs25.chmodSync)(pendingPath, 384);
|
|
81828
|
+
(0, import_node_fs25.renameSync)(pendingPath, path2);
|
|
79969
81829
|
} finally {
|
|
79970
|
-
(0,
|
|
81830
|
+
(0, import_node_fs25.rmSync)(pendingPath, { force: true });
|
|
79971
81831
|
}
|
|
79972
81832
|
}
|
|
79973
81833
|
function runServiceCommand(command) {
|
|
@@ -79981,16 +81841,16 @@ function runServiceCommand(command) {
|
|
|
79981
81841
|
}
|
|
79982
81842
|
return { status, output: output2 };
|
|
79983
81843
|
}
|
|
79984
|
-
function installDaemonService(args = []) {
|
|
81844
|
+
function installDaemonService(args = [], runtime) {
|
|
79985
81845
|
const profile = resolveEndpointProfileFromArgs(args);
|
|
79986
81846
|
const configPath = resolveAgentConfigPath({ profile });
|
|
79987
81847
|
if (!configExistsAtPath(configPath)) {
|
|
79988
81848
|
throw new Error(`No ${profile} runtime is configured. Run alan-agent setup or login first.`);
|
|
79989
81849
|
}
|
|
79990
|
-
const plan = currentServicePlan(args);
|
|
81850
|
+
const plan = currentServicePlan(args, runtime);
|
|
79991
81851
|
if (plan.manifestPath && plan.manifest) {
|
|
79992
|
-
if ((0,
|
|
79993
|
-
(0,
|
|
81852
|
+
if ((0, import_node_os15.platform)() === "darwin") {
|
|
81853
|
+
(0, import_node_fs25.mkdirSync)((0, import_node_path26.join)((0, import_node_os15.homedir)(), ".alan", "agent", "logs"), { recursive: true, mode: 448 });
|
|
79994
81854
|
}
|
|
79995
81855
|
writeManifest(plan.manifestPath, plan.manifest);
|
|
79996
81856
|
}
|
|
@@ -80000,7 +81860,7 @@ function installDaemonService(args = []) {
|
|
|
80000
81860
|
function uninstallDaemonService(args = []) {
|
|
80001
81861
|
const plan = currentServicePlan(args);
|
|
80002
81862
|
for (const command of plan.uninstallCommands) runServiceCommand(command);
|
|
80003
|
-
if (plan.manifestPath) (0,
|
|
81863
|
+
if (plan.manifestPath) (0, import_node_fs25.rmSync)(plan.manifestPath, { force: true });
|
|
80004
81864
|
console.info("[alan-agent] Per-user daemon service removed");
|
|
80005
81865
|
}
|
|
80006
81866
|
function printDaemonServiceStatus(args = []) {
|
|
@@ -80012,11 +81872,11 @@ function printDaemonServiceStatus(args = []) {
|
|
|
80012
81872
|
|
|
80013
81873
|
// src/skills/skill-cli.ts
|
|
80014
81874
|
var import_promises6 = require("fs/promises");
|
|
80015
|
-
var
|
|
80016
|
-
var
|
|
81875
|
+
var import_node_os16 = require("os");
|
|
81876
|
+
var import_node_path27 = require("path");
|
|
80017
81877
|
async function runSkillsCommand(args) {
|
|
80018
81878
|
const [subcommand, ...options] = args;
|
|
80019
|
-
const homeDirectory = option(options, "--home") ?? (0,
|
|
81879
|
+
const homeDirectory = option(options, "--home") ?? (0, import_node_os16.homedir)();
|
|
80020
81880
|
const manager = await RuntimeSkillManager.open({ homeDirectory });
|
|
80021
81881
|
if (subcommand === "scan") {
|
|
80022
81882
|
const inputPath = option(options, "--input");
|
|
@@ -80057,7 +81917,7 @@ async function defaultScanInput(homeDirectory) {
|
|
|
80057
81917
|
});
|
|
80058
81918
|
}
|
|
80059
81919
|
async function readProtectedJson(path2) {
|
|
80060
|
-
const absolutePath = (0,
|
|
81920
|
+
const absolutePath = (0, import_node_path27.resolve)(path2);
|
|
80061
81921
|
const stat2 = await (0, import_promises6.lstat)(absolutePath);
|
|
80062
81922
|
if (!stat2.isFile()) throw new Error(`Skill input is not a regular file: ${absolutePath}`);
|
|
80063
81923
|
if (process.platform !== "win32" && (stat2.mode & 63) !== 0) {
|
|
@@ -80207,6 +82067,17 @@ async function main() {
|
|
|
80207
82067
|
await setupDaemon(commandArgs);
|
|
80208
82068
|
return;
|
|
80209
82069
|
}
|
|
82070
|
+
if (command === "install") {
|
|
82071
|
+
bindConfigPath(commandArgs);
|
|
82072
|
+
const configured = readConfig();
|
|
82073
|
+
if (!configured.runtimeId || !configured.runtimeToken) {
|
|
82074
|
+
if (commandArgs.includes("--setup-token")) await setupDaemon(commandArgs);
|
|
82075
|
+
else await loginWithDeviceCode(commandArgs);
|
|
82076
|
+
}
|
|
82077
|
+
installDaemonService(commandArgs);
|
|
82078
|
+
runMcpIntegrationCommand(["reconcile", ...commandArgs]);
|
|
82079
|
+
return;
|
|
82080
|
+
}
|
|
80210
82081
|
if (command === "login") {
|
|
80211
82082
|
await loginWithDeviceCode(commandArgs);
|
|
80212
82083
|
return;
|
|
@@ -80217,6 +82088,7 @@ async function main() {
|
|
|
80217
82088
|
}
|
|
80218
82089
|
if (command === "service" && commandArgs[0] === "install") {
|
|
80219
82090
|
installDaemonService(commandArgs.slice(1));
|
|
82091
|
+
runMcpIntegrationCommand(["reconcile", ...commandArgs.slice(1)]);
|
|
80220
82092
|
return;
|
|
80221
82093
|
}
|
|
80222
82094
|
if (command === "service" && commandArgs[0] === "uninstall") {
|
|
@@ -80227,6 +82099,18 @@ async function main() {
|
|
|
80227
82099
|
printDaemonServiceStatus(commandArgs.slice(1));
|
|
80228
82100
|
return;
|
|
80229
82101
|
}
|
|
82102
|
+
if (command === "integrations" && commandArgs[0] === "capabilities") {
|
|
82103
|
+
printNativeIntegrationStatus(commandArgs.slice(1));
|
|
82104
|
+
return;
|
|
82105
|
+
}
|
|
82106
|
+
if (command === "integrations") {
|
|
82107
|
+
runMcpIntegrationCommand(commandArgs);
|
|
82108
|
+
return;
|
|
82109
|
+
}
|
|
82110
|
+
if (command === "hook") {
|
|
82111
|
+
runNativeHookCommand(commandArgs);
|
|
82112
|
+
return;
|
|
82113
|
+
}
|
|
80230
82114
|
if (command === "stop") {
|
|
80231
82115
|
await stopDaemon(commandArgs);
|
|
80232
82116
|
return;
|