chat-recall 0.5.22 → 0.5.24
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/cli.js +347 -197
- package/dist/cli.js.map +1 -1
- package/dist/conformant-server.d.ts +17 -0
- package/dist/conformant-server.d.ts.map +1 -0
- package/dist/conformant-server.js +116 -0
- package/dist/conformant-server.js.map +1 -0
- package/dist/device-auth.d.ts +0 -4
- package/dist/device-auth.d.ts.map +1 -1
- package/dist/device-auth.js +43 -2
- package/dist/device-auth.js.map +1 -1
- package/dist/hostile-server.d.ts +47 -0
- package/dist/hostile-server.d.ts.map +1 -0
- package/dist/hostile-server.js +95 -0
- package/dist/hostile-server.js.map +1 -0
- package/dist/mcp.js +160 -8
- package/dist/server-probe.d.ts +16 -2
- package/dist/server-probe.d.ts.map +1 -1
- package/dist/server-probe.js +59 -4
- package/dist/server-probe.js.map +1 -1
- package/dist/sync-client.d.ts.map +1 -1
- package/dist/sync-client.js +47 -2
- package/dist/sync-client.js.map +1 -1
- package/dist/watch.js +263 -117
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -7134,13 +7134,13 @@ async function parseCodexSubagents(sessionPath) {
|
|
|
7134
7134
|
if (meta?.type !== "session_meta")
|
|
7135
7135
|
continue;
|
|
7136
7136
|
const payload = meta.payload || {};
|
|
7137
|
-
const
|
|
7138
|
-
if (!
|
|
7137
|
+
const spawn3 = payload.source?.subagent?.thread_spawn;
|
|
7138
|
+
if (!spawn3)
|
|
7139
7139
|
continue;
|
|
7140
|
-
if (
|
|
7140
|
+
if (spawn3.parent_thread_id !== parentId)
|
|
7141
7141
|
continue;
|
|
7142
7142
|
const id = fileName.replace(/\.jsonl$/i, "");
|
|
7143
|
-
const role = String(
|
|
7143
|
+
const role = String(spawn3.agent_role || payload.agent_role || "").toLowerCase();
|
|
7144
7144
|
const kind = role.includes("compact") ? "compact" : role.includes("aside") ? "aside" : role.includes("worker") || role.includes("explore") ? "explore" : "other";
|
|
7145
7145
|
let messages = [];
|
|
7146
7146
|
try {
|
|
@@ -7152,8 +7152,8 @@ async function parseCodexSubagents(sessionPath) {
|
|
|
7152
7152
|
subagents.push({
|
|
7153
7153
|
id,
|
|
7154
7154
|
kind,
|
|
7155
|
-
agentType:
|
|
7156
|
-
description:
|
|
7155
|
+
agentType: spawn3.agent_role || payload.agent_role,
|
|
7156
|
+
description: spawn3.agent_nickname || payload.agent_nickname,
|
|
7157
7157
|
filePath,
|
|
7158
7158
|
messageCount: messages.length,
|
|
7159
7159
|
toolUseCount,
|
|
@@ -7488,7 +7488,7 @@ async function archiveRawSession(store, sessionId) {
|
|
|
7488
7488
|
}
|
|
7489
7489
|
function detectForkPredecessor(sessionPath) {
|
|
7490
7490
|
const { readFileSync: readFileSync50, readdirSync: readdirSync35 } = requireFs();
|
|
7491
|
-
const { dirname: dirname28, join: join61, basename:
|
|
7491
|
+
const { dirname: dirname28, join: join61, basename: basename30 } = requirePath();
|
|
7492
7492
|
let text;
|
|
7493
7493
|
try {
|
|
7494
7494
|
text = readFileSync50(sessionPath, "utf-8");
|
|
@@ -7514,7 +7514,7 @@ function detectForkPredecessor(sessionPath) {
|
|
|
7514
7514
|
if (text.includes(`"uuid":"${headParent}"`))
|
|
7515
7515
|
return null;
|
|
7516
7516
|
const dir = dirname28(sessionPath);
|
|
7517
|
-
const self =
|
|
7517
|
+
const self = basename30(sessionPath);
|
|
7518
7518
|
let entries;
|
|
7519
7519
|
try {
|
|
7520
7520
|
entries = readdirSync35(dir);
|
|
@@ -7526,7 +7526,7 @@ function detectForkPredecessor(sessionPath) {
|
|
|
7526
7526
|
continue;
|
|
7527
7527
|
try {
|
|
7528
7528
|
if (readFileSync50(join61(dir, f), "utf-8").includes(`"uuid":"${headParent}"`)) {
|
|
7529
|
-
return
|
|
7529
|
+
return basename30(f, ".jsonl");
|
|
7530
7530
|
}
|
|
7531
7531
|
} catch {
|
|
7532
7532
|
}
|
|
@@ -9781,8 +9781,8 @@ var init_codex2 = __esm({
|
|
|
9781
9781
|
} catch {
|
|
9782
9782
|
continue;
|
|
9783
9783
|
}
|
|
9784
|
-
const
|
|
9785
|
-
if (!
|
|
9784
|
+
const spawn3 = meta?.payload?.source?.subagent?.thread_spawn;
|
|
9785
|
+
if (!spawn3 || spawn3.parent_thread_id !== parentUuid)
|
|
9786
9786
|
continue;
|
|
9787
9787
|
events.push(...this.parseRollout(path));
|
|
9788
9788
|
}
|
|
@@ -9975,10 +9975,10 @@ var init_codex2 = __esm({
|
|
|
9975
9975
|
const meta = JSON.parse(first);
|
|
9976
9976
|
const payload = meta?.payload || {};
|
|
9977
9977
|
projectPath = payload.cwd || "";
|
|
9978
|
-
const
|
|
9979
|
-
if (
|
|
9978
|
+
const spawn3 = payload.source?.subagent?.thread_spawn;
|
|
9979
|
+
if (spawn3 || payload.agent_role || payload.agent_nickname) {
|
|
9980
9980
|
isSubagent = true;
|
|
9981
|
-
parentId =
|
|
9981
|
+
parentId = spawn3?.parent_thread_id || null;
|
|
9982
9982
|
}
|
|
9983
9983
|
}
|
|
9984
9984
|
} catch {
|
|
@@ -13470,8 +13470,11 @@ function isInside(child, parent) {
|
|
|
13470
13470
|
return c.startsWith(p);
|
|
13471
13471
|
}
|
|
13472
13472
|
function globMatch(pattern, path) {
|
|
13473
|
-
const re = new RegExp("^" + globToRegex(pattern) + "$");
|
|
13474
|
-
return re.test(path);
|
|
13473
|
+
const re = new RegExp("^" + globToRegex(toPosix(pattern)) + "$");
|
|
13474
|
+
return re.test(toPosix(path));
|
|
13475
|
+
}
|
|
13476
|
+
function toPosix(p) {
|
|
13477
|
+
return p.replace(/\\/g, "/");
|
|
13475
13478
|
}
|
|
13476
13479
|
function globToRegex(glob) {
|
|
13477
13480
|
let out = "";
|
|
@@ -19727,7 +19730,7 @@ var init_http = __esm({
|
|
|
19727
19730
|
CLI_VERSION = JSON.parse(readFileSync33(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
|
|
19728
19731
|
} catch {
|
|
19729
19732
|
try {
|
|
19730
|
-
if (true) CLI_VERSION = "0.5.
|
|
19733
|
+
if (true) CLI_VERSION = "0.5.24";
|
|
19731
19734
|
} catch {
|
|
19732
19735
|
}
|
|
19733
19736
|
}
|
|
@@ -20239,7 +20242,7 @@ var init_telemetry = __esm({
|
|
|
20239
20242
|
init_sync_client();
|
|
20240
20243
|
cliVersion2 = () => {
|
|
20241
20244
|
try {
|
|
20242
|
-
return true ? "0.5.
|
|
20245
|
+
return true ? "0.5.24" : "dev";
|
|
20243
20246
|
} catch {
|
|
20244
20247
|
return "dev";
|
|
20245
20248
|
}
|
|
@@ -20351,6 +20354,143 @@ var init_collector_health = __esm({
|
|
|
20351
20354
|
}
|
|
20352
20355
|
});
|
|
20353
20356
|
|
|
20357
|
+
// src/server-probe.ts
|
|
20358
|
+
var server_probe_exports = {};
|
|
20359
|
+
__export(server_probe_exports, {
|
|
20360
|
+
classifyUnreachable: () => classifyUnreachable,
|
|
20361
|
+
describeCause: () => describeCause,
|
|
20362
|
+
describeUnreachable: () => describeUnreachable,
|
|
20363
|
+
probeAdvice: () => probeAdvice,
|
|
20364
|
+
probeOk: () => probeOk,
|
|
20365
|
+
probeServer: () => probeServer
|
|
20366
|
+
});
|
|
20367
|
+
function probeOk(r) {
|
|
20368
|
+
return r.kind === "ok" && r.apiVersion >= 2;
|
|
20369
|
+
}
|
|
20370
|
+
async function probeServer(base2, opts = {}) {
|
|
20371
|
+
const f = opts.fetchImpl || fetch;
|
|
20372
|
+
let res;
|
|
20373
|
+
try {
|
|
20374
|
+
res = await f(`${base2.replace(/\/+$/, "")}/api/capabilities`, {
|
|
20375
|
+
signal: AbortSignal.timeout(opts.timeoutMs ?? 1e4)
|
|
20376
|
+
});
|
|
20377
|
+
} catch (err) {
|
|
20378
|
+
return {
|
|
20379
|
+
kind: "unreachable",
|
|
20380
|
+
error: describeCause(err),
|
|
20381
|
+
reason: classifyUnreachable(err)
|
|
20382
|
+
};
|
|
20383
|
+
}
|
|
20384
|
+
if (res.status === 401 || res.status === 403) return { kind: "unauthorized", status: res.status };
|
|
20385
|
+
if (res.status === 404 || res.status === 410) return { kind: "moved", status: res.status };
|
|
20386
|
+
if (res.status >= 500) return { kind: "server_error", status: res.status };
|
|
20387
|
+
if (!res.ok) return { kind: "moved", status: res.status };
|
|
20388
|
+
let body;
|
|
20389
|
+
try {
|
|
20390
|
+
body = await res.text();
|
|
20391
|
+
} catch (err) {
|
|
20392
|
+
return { kind: "unreachable", error: describeCause(err), reason: classifyUnreachable(err) };
|
|
20393
|
+
}
|
|
20394
|
+
let parsed;
|
|
20395
|
+
try {
|
|
20396
|
+
parsed = JSON.parse(body);
|
|
20397
|
+
} catch {
|
|
20398
|
+
return { kind: "not_api", status: res.status, snippet: body.trim().slice(0, 60) };
|
|
20399
|
+
}
|
|
20400
|
+
if (typeof parsed?.apiVersion !== "number") {
|
|
20401
|
+
return { kind: "not_api", status: res.status, snippet: body.trim().slice(0, 60) };
|
|
20402
|
+
}
|
|
20403
|
+
return {
|
|
20404
|
+
kind: "ok",
|
|
20405
|
+
apiVersion: parsed.apiVersion,
|
|
20406
|
+
edition: parsed.edition,
|
|
20407
|
+
cliVersion: parsed.cli?.version,
|
|
20408
|
+
raw: parsed
|
|
20409
|
+
};
|
|
20410
|
+
}
|
|
20411
|
+
function probeAdvice(r, base2) {
|
|
20412
|
+
switch (r.kind) {
|
|
20413
|
+
case "ok":
|
|
20414
|
+
return r.apiVersion >= 2 ? `apiVersion ${r.apiVersion}${r.edition ? `, edition ${r.edition}` : ""}` : `apiVersion ${r.apiVersion} is too old for this CLI \u2014 upgrade the server`;
|
|
20415
|
+
case "moved":
|
|
20416
|
+
return `HTTP ${r.status} \u2014 nothing is served at ${base2}. If the host moved, re-point it: \`chat-recall login <new-server-url>\``;
|
|
20417
|
+
case "unauthorized":
|
|
20418
|
+
return `HTTP ${r.status} \u2014 ${base2} rejected the credential. Re-login: \`chat-recall login ` + base2 + "`";
|
|
20419
|
+
case "server_error":
|
|
20420
|
+
return `HTTP ${r.status} \u2014 ${base2} is up but failing. Nothing to fix locally; check the server.`;
|
|
20421
|
+
case "not_api":
|
|
20422
|
+
return `${base2} answered ${r.status} with something that is not the chat-recall API (${JSON.stringify(r.snippet)}) \u2014 check the URL, a proxy or a captive portal`;
|
|
20423
|
+
case "unreachable":
|
|
20424
|
+
switch (r.reason) {
|
|
20425
|
+
case "dns":
|
|
20426
|
+
return `cannot resolve the host in ${base2} (${r.error}) \u2014 check the URL for a typo, and your DNS or VPN`;
|
|
20427
|
+
case "refused":
|
|
20428
|
+
return `${base2} resolves but refused the connection (${r.error}) \u2014 the server is not running, or the port is wrong`;
|
|
20429
|
+
case "tls":
|
|
20430
|
+
return `the TLS certificate at ${base2} was rejected (${r.error}) \u2014 expired, self-signed, or issued for another host. A corporate proxy that re-signs traffic does this.`;
|
|
20431
|
+
case "timeout":
|
|
20432
|
+
return `${base2} did not answer in time (${r.error}) \u2014 a firewall or proxy that drops traffic silently looks exactly like this`;
|
|
20433
|
+
case "reset":
|
|
20434
|
+
return `the connection to ${base2} was cut (${r.error}) \u2014 often a proxy or a captive portal`;
|
|
20435
|
+
case "bad_port":
|
|
20436
|
+
return `${base2} names a port that Node and every browser refuse to open (the WHATWG bad-port list) \u2014 use the server's real port`;
|
|
20437
|
+
default:
|
|
20438
|
+
return `no HTTP response from ${base2} (${r.error}) \u2014 check the URL, DNS, or that the server is running`;
|
|
20439
|
+
}
|
|
20440
|
+
}
|
|
20441
|
+
}
|
|
20442
|
+
function classifyUnreachable(err) {
|
|
20443
|
+
if (err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError")) return "timeout";
|
|
20444
|
+
for (const e of walkErrorChain(err)) {
|
|
20445
|
+
const code2 = String(e.code ?? "");
|
|
20446
|
+
const msg = String(e.message ?? "");
|
|
20447
|
+
if (code2 === "ENOTFOUND" || code2 === "EAI_AGAIN") return "dns";
|
|
20448
|
+
if (code2 === "ECONNREFUSED") return "refused";
|
|
20449
|
+
if (code2 === "ECONNRESET" || code2 === "EPIPE") return "reset";
|
|
20450
|
+
if (code2 === "ETIMEDOUT" || code2 === "UND_ERR_CONNECT_TIMEOUT" || code2 === "UND_ERR_HEADERS_TIMEOUT") return "timeout";
|
|
20451
|
+
if (code2.startsWith("CERT_") || code2.startsWith("ERR_TLS") || code2 === "DEPTH_ZERO_SELF_SIGNED_CERT" || code2 === "SELF_SIGNED_CERT_IN_CHAIN" || code2 === "UNABLE_TO_VERIFY_LEAF_SIGNATURE" || /certificate/i.test(msg)) return "tls";
|
|
20452
|
+
if (/^bad port$/i.test(msg)) return "bad_port";
|
|
20453
|
+
}
|
|
20454
|
+
return "unknown";
|
|
20455
|
+
}
|
|
20456
|
+
function* walkErrorChain(err, depth = 0) {
|
|
20457
|
+
if (!err || typeof err !== "object" || depth > 5) return;
|
|
20458
|
+
yield err;
|
|
20459
|
+
const members = err.errors;
|
|
20460
|
+
if (Array.isArray(members)) {
|
|
20461
|
+
for (const m of members) yield* walkErrorChain(m, depth + 1);
|
|
20462
|
+
}
|
|
20463
|
+
yield* walkErrorChain(err.cause, depth + 1);
|
|
20464
|
+
}
|
|
20465
|
+
function describeCause(err) {
|
|
20466
|
+
const parts = [];
|
|
20467
|
+
for (const e of walkErrorChain(err)) {
|
|
20468
|
+
const code2 = e.code;
|
|
20469
|
+
if (typeof code2 === "string" && code2 && !parts.includes(code2)) parts.push(code2);
|
|
20470
|
+
}
|
|
20471
|
+
const base2 = err instanceof Error ? err.message : String(err);
|
|
20472
|
+
return parts.length ? `${base2}: ${parts.join(" \u2192 ")}` : base2;
|
|
20473
|
+
}
|
|
20474
|
+
function describeUnreachable(err) {
|
|
20475
|
+
const reason = classifyUnreachable(err);
|
|
20476
|
+
const detail = describeCause(err);
|
|
20477
|
+
const phrase = {
|
|
20478
|
+
dns: "the host name does not resolve",
|
|
20479
|
+
refused: "the connection was refused \u2014 nothing is listening there",
|
|
20480
|
+
tls: "the TLS certificate was rejected",
|
|
20481
|
+
timeout: "no answer before the timeout",
|
|
20482
|
+
reset: "the connection was cut mid-request",
|
|
20483
|
+
bad_port: "that port is one Node and every browser refuse to open",
|
|
20484
|
+
unknown: "no HTTP response"
|
|
20485
|
+
};
|
|
20486
|
+
return `${phrase[reason]} (${detail})`;
|
|
20487
|
+
}
|
|
20488
|
+
var init_server_probe = __esm({
|
|
20489
|
+
"src/server-probe.ts"() {
|
|
20490
|
+
"use strict";
|
|
20491
|
+
}
|
|
20492
|
+
});
|
|
20493
|
+
|
|
20354
20494
|
// src/client-events.ts
|
|
20355
20495
|
var client_events_exports = {};
|
|
20356
20496
|
__export(client_events_exports, {
|
|
@@ -20758,6 +20898,7 @@ async function fetchTenantSyncConfig(cred) {
|
|
|
20758
20898
|
persistSyncConfig(base2, config2);
|
|
20759
20899
|
return config2;
|
|
20760
20900
|
} catch (err) {
|
|
20901
|
+
if (isSyncRulesRefusal(err)) throw err;
|
|
20761
20902
|
return lastKnownSyncConfig(base2, err instanceof Error ? err.message : "request failed");
|
|
20762
20903
|
}
|
|
20763
20904
|
}
|
|
@@ -20767,9 +20908,14 @@ function lastKnownSyncConfig(base2, reason) {
|
|
|
20767
20908
|
console.error(`[sync] could not read server-side sync rules (${reason}) \u2014 applying the last known set from ${new Date(remembered.fetchedAt).toISOString()}`);
|
|
20768
20909
|
return remembered.config;
|
|
20769
20910
|
}
|
|
20770
|
-
|
|
20911
|
+
const refusal = new Error(
|
|
20771
20912
|
`Cannot read the server-side sync rules from ${base2} (${reason}), and this machine has never successfully read them. Refusing to sync: uploading without knowing your exclusions could ship a project you told the dashboard to hold back. Fix the server or retry.`
|
|
20772
20913
|
);
|
|
20914
|
+
refusal[SYNC_RULES_REFUSAL] = true;
|
|
20915
|
+
throw refusal;
|
|
20916
|
+
}
|
|
20917
|
+
function isSyncRulesRefusal(err) {
|
|
20918
|
+
return !!err && typeof err === "object" && err[SYNC_RULES_REFUSAL] === true;
|
|
20773
20919
|
}
|
|
20774
20920
|
function deviceLabel() {
|
|
20775
20921
|
try {
|
|
@@ -21030,7 +21176,12 @@ async function verifyServerApi(serverUrl, minApi) {
|
|
|
21030
21176
|
const base2 = serverUrl.replace(/\/$/, "");
|
|
21031
21177
|
const seen = capsOk.get(base2);
|
|
21032
21178
|
if (seen !== void 0 && Date.now() - seen < CAPS_TTL_MS) return true;
|
|
21033
|
-
const
|
|
21179
|
+
const { probeServer: probeServer2, probeAdvice: probeAdvice2 } = await Promise.resolve().then(() => (init_server_probe(), server_probe_exports));
|
|
21180
|
+
const probe = await probeServer2(base2, {
|
|
21181
|
+
fetchImpl: ((url, init) => fetchWithTimeout(String(url), init ?? {}))
|
|
21182
|
+
});
|
|
21183
|
+
if (probe.kind !== "ok") throw new Error(probeAdvice2(probe, base2));
|
|
21184
|
+
const caps = probe.raw;
|
|
21034
21185
|
if ((caps.apiVersion ?? 0) < minApi) {
|
|
21035
21186
|
throw new Error(`server too old: apiVersion ${caps.apiVersion ?? "none"} < required ${minApi} \u2014 update the server image before syncing`);
|
|
21036
21187
|
}
|
|
@@ -22020,7 +22171,7 @@ async function syncIncremental(opts = {}) {
|
|
|
22020
22171
|
lock.release();
|
|
22021
22172
|
}
|
|
22022
22173
|
}
|
|
22023
|
-
var ownVersion, walkProgressForUpload, lastServerPackSpec, credPath, _securityConfigCache, SECURITY_CONFIG_TTL_MS, _syncConfigCache, EMPTY_SYNC_CONFIG, _fetchTenantSyncConfigForTests, hashPath, MAX_DERIVED_ROW_BYTES, isLocalHost, CAPS_TTL_MS, capsOk, advertisedIngestConc, advertisedGzipBody, FULL_BUILD_MAX_BYTES, FULL_BUILD_TAIL_BYTES, BATCH_SCAN_MAX_BYTES;
|
|
22174
|
+
var ownVersion, walkProgressForUpload, lastServerPackSpec, credPath, _securityConfigCache, SECURITY_CONFIG_TTL_MS, _syncConfigCache, EMPTY_SYNC_CONFIG, _fetchTenantSyncConfigForTests, SYNC_RULES_REFUSAL, hashPath, MAX_DERIVED_ROW_BYTES, isLocalHost, CAPS_TTL_MS, capsOk, advertisedIngestConc, advertisedGzipBody, FULL_BUILD_MAX_BYTES, FULL_BUILD_TAIL_BYTES, BATCH_SCAN_MAX_BYTES;
|
|
22024
22175
|
var init_sync_client = __esm({
|
|
22025
22176
|
"src/sync-client.ts"() {
|
|
22026
22177
|
"use strict";
|
|
@@ -22066,7 +22217,7 @@ var init_sync_client = __esm({
|
|
|
22066
22217
|
ownVersion = JSON.parse(readFileSync37(join44(dir, "../package.json"), "utf-8")).version || ownVersion;
|
|
22067
22218
|
} catch {
|
|
22068
22219
|
try {
|
|
22069
|
-
if (true) ownVersion = "0.5.
|
|
22220
|
+
if (true) ownVersion = "0.5.24";
|
|
22070
22221
|
} catch {
|
|
22071
22222
|
}
|
|
22072
22223
|
}
|
|
@@ -22078,6 +22229,7 @@ var init_sync_client = __esm({
|
|
|
22078
22229
|
_syncConfigCache = /* @__PURE__ */ new Map();
|
|
22079
22230
|
EMPTY_SYNC_CONFIG = { excludeTools: [], excludeProjects: [], excludeSources: [], approveSources: [] };
|
|
22080
22231
|
_fetchTenantSyncConfigForTests = fetchTenantSyncConfig;
|
|
22232
|
+
SYNC_RULES_REFUSAL = /* @__PURE__ */ Symbol.for("chat-recall.syncRulesRefusal");
|
|
22081
22233
|
hashPath = (p) => p ? "p_" + createHash13("sha256").update(p).digest("hex").slice(0, 12) : "";
|
|
22082
22234
|
MAX_DERIVED_ROW_BYTES = 2 * 1024 * 1024;
|
|
22083
22235
|
isLocalHost = isPrivateHost;
|
|
@@ -22882,7 +23034,7 @@ var init_install_skills = __esm({
|
|
|
22882
23034
|
|
|
22883
23035
|
// ../engine/dist/core/artifact-codec.js
|
|
22884
23036
|
import { readFileSync as readFileSync42 } from "fs";
|
|
22885
|
-
import { join as join50 } from "path";
|
|
23037
|
+
import { basename as basename24, join as join50 } from "path";
|
|
22886
23038
|
import { homedir as homedir17 } from "os";
|
|
22887
23039
|
function parseCommandText(content, format, fallbackName) {
|
|
22888
23040
|
if (format === "toml") {
|
|
@@ -23010,8 +23162,7 @@ function emit(type, art, toTool, projectPath) {
|
|
|
23010
23162
|
};
|
|
23011
23163
|
}
|
|
23012
23164
|
function base(filePath) {
|
|
23013
|
-
|
|
23014
|
-
return b.replace(/\.(md|toml)$/, "");
|
|
23165
|
+
return basename24(filePath).replace(/\.(md|toml)$/, "");
|
|
23015
23166
|
}
|
|
23016
23167
|
var init_artifact_codec = __esm({
|
|
23017
23168
|
"../engine/dist/core/artifact-codec.js"() {
|
|
@@ -23046,7 +23197,7 @@ __export(toolkit_sync_exports, {
|
|
|
23046
23197
|
writeMcpEntry: () => writeMcpEntry
|
|
23047
23198
|
});
|
|
23048
23199
|
import { existsSync as existsSync47, mkdirSync as mkdirSync13, readFileSync as readFileSync43, writeFileSync as writeFileSync14, cpSync as cpSync2 } from "fs";
|
|
23049
|
-
import { dirname as dirname21, join as join51, basename as
|
|
23200
|
+
import { dirname as dirname21, join as join51, basename as basename25 } from "path";
|
|
23050
23201
|
import { homedir as homedir18 } from "os";
|
|
23051
23202
|
function supportedTargetsFor(type) {
|
|
23052
23203
|
return SUPPORTED_TARGETS[type];
|
|
@@ -23104,7 +23255,7 @@ function copySkill(row, toTool) {
|
|
|
23104
23255
|
const skillDir = row.extra.skillDir || dirname21(row.filePath);
|
|
23105
23256
|
if (!existsSync47(skillDir))
|
|
23106
23257
|
return { ok: false, status: 404, error: `Source dir missing: ${skillDir}` };
|
|
23107
|
-
const name = row.extra.skillName ||
|
|
23258
|
+
const name = row.extra.skillName || basename25(skillDir);
|
|
23108
23259
|
const targetDir = join51(skillsDirFor(toTool), name);
|
|
23109
23260
|
if (existsSync47(targetDir))
|
|
23110
23261
|
return { ok: false, status: 409, error: `Already exists: ${targetDir}` };
|
|
@@ -23502,7 +23653,7 @@ __export(toolkit_pull_exports, {
|
|
|
23502
23653
|
portableCommand: () => portableCommand
|
|
23503
23654
|
});
|
|
23504
23655
|
import { existsSync as existsSync48, mkdirSync as mkdirSync14, statSync as statSync42, writeFileSync as writeFileSync15 } from "node:fs";
|
|
23505
|
-
import {
|
|
23656
|
+
import { dirname as dirname22, join as join52, basename as basename26, isAbsolute as isAbsolute3 } from "node:path";
|
|
23506
23657
|
function isExecutableFile(p) {
|
|
23507
23658
|
try {
|
|
23508
23659
|
return statSync42(p).isFile();
|
|
@@ -23511,13 +23662,7 @@ function isExecutableFile(p) {
|
|
|
23511
23662
|
}
|
|
23512
23663
|
}
|
|
23513
23664
|
function onPath(name) {
|
|
23514
|
-
|
|
23515
|
-
for (const d of dirs) {
|
|
23516
|
-
const candidate = join52(d, name);
|
|
23517
|
-
if (isExecutableFile(candidate))
|
|
23518
|
-
return candidate;
|
|
23519
|
-
}
|
|
23520
|
-
return null;
|
|
23665
|
+
return resolveOnPath(name);
|
|
23521
23666
|
}
|
|
23522
23667
|
function portableCommand(cmd) {
|
|
23523
23668
|
if (!isAbsolute3(cmd)) {
|
|
@@ -23525,7 +23670,7 @@ function portableCommand(cmd) {
|
|
|
23525
23670
|
}
|
|
23526
23671
|
if (isExecutableFile(cmd))
|
|
23527
23672
|
return { command: cmd, rewritten: false };
|
|
23528
|
-
const base2 =
|
|
23673
|
+
const base2 = basename26(cmd);
|
|
23529
23674
|
if (onPath(base2))
|
|
23530
23675
|
return { command: base2, rewritten: true };
|
|
23531
23676
|
return { missing: cmd };
|
|
@@ -23779,6 +23924,7 @@ var NOT_REBUILDABLE;
|
|
|
23779
23924
|
var init_toolkit_pull = __esm({
|
|
23780
23925
|
"../engine/dist/core/toolkit-pull.js"() {
|
|
23781
23926
|
"use strict";
|
|
23927
|
+
init_which();
|
|
23782
23928
|
init_toolkit_sync();
|
|
23783
23929
|
init_artifact_codec();
|
|
23784
23930
|
NOT_REBUILDABLE = {
|
|
@@ -24005,7 +24151,7 @@ __export(escalate_exports, {
|
|
|
24005
24151
|
extractLearnings: () => extractLearnings,
|
|
24006
24152
|
runEscalate: () => runEscalate
|
|
24007
24153
|
});
|
|
24008
|
-
import { basename as
|
|
24154
|
+
import { basename as basename27 } from "node:path";
|
|
24009
24155
|
function isNoiseUserMessage(content) {
|
|
24010
24156
|
const c = content.trimStart();
|
|
24011
24157
|
return c.length === 0 || c.length > 4e3 || c.startsWith("Caveat:") || c.startsWith("[Request interrupted") || c.includes("<command-name>") || c.includes("<local-command") || c.startsWith("<system-reminder>");
|
|
@@ -24072,10 +24218,10 @@ async function runEscalate(deps, opts) {
|
|
|
24072
24218
|
return null;
|
|
24073
24219
|
}
|
|
24074
24220
|
sessionId = row.sessionId;
|
|
24075
|
-
if (!projectName && row.projectPath) projectName =
|
|
24221
|
+
if (!projectName && row.projectPath) projectName = basename27(row.projectPath);
|
|
24076
24222
|
}
|
|
24077
24223
|
if (!sessionId) throw new Error("pass a session id or --latest");
|
|
24078
|
-
if (!projectName) projectName =
|
|
24224
|
+
if (!projectName) projectName = basename27(opts.cwd);
|
|
24079
24225
|
const convo = await deps.getSoft(`/api/conversations/${encodeURIComponent(sessionId)}?limit=0`);
|
|
24080
24226
|
if (!convo.data || convo.data.messages.length === 0) {
|
|
24081
24227
|
deps.log(`escalate: session ${sessionId} not synced yet${convo.message ? ` (${convo.message})` : ""} \u2014 nothing to do.`);
|
|
@@ -24145,117 +24291,6 @@ var init_escalate = __esm({
|
|
|
24145
24291
|
}
|
|
24146
24292
|
});
|
|
24147
24293
|
|
|
24148
|
-
// src/server-probe.ts
|
|
24149
|
-
var server_probe_exports = {};
|
|
24150
|
-
__export(server_probe_exports, {
|
|
24151
|
-
classifyUnreachable: () => classifyUnreachable,
|
|
24152
|
-
describeCause: () => describeCause,
|
|
24153
|
-
probeAdvice: () => probeAdvice,
|
|
24154
|
-
probeOk: () => probeOk,
|
|
24155
|
-
probeServer: () => probeServer
|
|
24156
|
-
});
|
|
24157
|
-
function probeOk(r) {
|
|
24158
|
-
return r.kind === "ok" && r.apiVersion >= 2;
|
|
24159
|
-
}
|
|
24160
|
-
async function probeServer(base2, opts = {}) {
|
|
24161
|
-
const f = opts.fetchImpl || fetch;
|
|
24162
|
-
let res;
|
|
24163
|
-
try {
|
|
24164
|
-
res = await f(`${base2.replace(/\/+$/, "")}/api/capabilities`, {
|
|
24165
|
-
signal: AbortSignal.timeout(opts.timeoutMs ?? 1e4)
|
|
24166
|
-
});
|
|
24167
|
-
} catch (err) {
|
|
24168
|
-
return {
|
|
24169
|
-
kind: "unreachable",
|
|
24170
|
-
error: describeCause(err),
|
|
24171
|
-
reason: classifyUnreachable(err)
|
|
24172
|
-
};
|
|
24173
|
-
}
|
|
24174
|
-
if (res.status === 401 || res.status === 403) return { kind: "unauthorized", status: res.status };
|
|
24175
|
-
if (res.status === 404 || res.status === 410) return { kind: "moved", status: res.status };
|
|
24176
|
-
if (res.status >= 500) return { kind: "server_error", status: res.status };
|
|
24177
|
-
if (!res.ok) return { kind: "moved", status: res.status };
|
|
24178
|
-
let body;
|
|
24179
|
-
try {
|
|
24180
|
-
body = await res.text();
|
|
24181
|
-
} catch (err) {
|
|
24182
|
-
return { kind: "unreachable", error: describeCause(err), reason: classifyUnreachable(err) };
|
|
24183
|
-
}
|
|
24184
|
-
let parsed;
|
|
24185
|
-
try {
|
|
24186
|
-
parsed = JSON.parse(body);
|
|
24187
|
-
} catch {
|
|
24188
|
-
return { kind: "not_api", status: res.status, snippet: body.trim().slice(0, 60) };
|
|
24189
|
-
}
|
|
24190
|
-
if (typeof parsed?.apiVersion !== "number") {
|
|
24191
|
-
return { kind: "not_api", status: res.status, snippet: body.trim().slice(0, 60) };
|
|
24192
|
-
}
|
|
24193
|
-
return {
|
|
24194
|
-
kind: "ok",
|
|
24195
|
-
apiVersion: parsed.apiVersion,
|
|
24196
|
-
edition: parsed.edition,
|
|
24197
|
-
cliVersion: parsed.cli?.version
|
|
24198
|
-
};
|
|
24199
|
-
}
|
|
24200
|
-
function probeAdvice(r, base2) {
|
|
24201
|
-
switch (r.kind) {
|
|
24202
|
-
case "ok":
|
|
24203
|
-
return r.apiVersion >= 2 ? `apiVersion ${r.apiVersion}${r.edition ? `, edition ${r.edition}` : ""}` : `apiVersion ${r.apiVersion} is too old for this CLI \u2014 upgrade the server`;
|
|
24204
|
-
case "moved":
|
|
24205
|
-
return `HTTP ${r.status} \u2014 nothing is served at ${base2}. If the host moved, re-point it: \`chat-recall login <new-server-url>\``;
|
|
24206
|
-
case "unauthorized":
|
|
24207
|
-
return `HTTP ${r.status} \u2014 ${base2} rejected the credential. Re-login: \`chat-recall login ` + base2 + "`";
|
|
24208
|
-
case "server_error":
|
|
24209
|
-
return `HTTP ${r.status} \u2014 ${base2} is up but failing. Nothing to fix locally; check the server.`;
|
|
24210
|
-
case "not_api":
|
|
24211
|
-
return `${base2} answered ${r.status} with something that is not the chat-recall API (${JSON.stringify(r.snippet)}) \u2014 check the URL, a proxy or a captive portal`;
|
|
24212
|
-
case "unreachable":
|
|
24213
|
-
switch (r.reason) {
|
|
24214
|
-
case "dns":
|
|
24215
|
-
return `cannot resolve the host in ${base2} (${r.error}) \u2014 check the URL for a typo, and your DNS or VPN`;
|
|
24216
|
-
case "refused":
|
|
24217
|
-
return `${base2} resolves but refused the connection (${r.error}) \u2014 the server is not running, or the port is wrong`;
|
|
24218
|
-
case "tls":
|
|
24219
|
-
return `the TLS certificate at ${base2} was rejected (${r.error}) \u2014 expired, self-signed, or issued for another host. A corporate proxy that re-signs traffic does this.`;
|
|
24220
|
-
case "timeout":
|
|
24221
|
-
return `${base2} did not answer in time (${r.error}) \u2014 a firewall or proxy that drops traffic silently looks exactly like this`;
|
|
24222
|
-
case "reset":
|
|
24223
|
-
return `the connection to ${base2} was cut (${r.error}) \u2014 often a proxy or a captive portal`;
|
|
24224
|
-
default:
|
|
24225
|
-
return `no HTTP response from ${base2} (${r.error}) \u2014 check the URL, DNS, or that the server is running`;
|
|
24226
|
-
}
|
|
24227
|
-
}
|
|
24228
|
-
}
|
|
24229
|
-
function classifyUnreachable(err) {
|
|
24230
|
-
if (err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError")) return "timeout";
|
|
24231
|
-
for (let e = err, depth = 0; e && depth < 5; depth++) {
|
|
24232
|
-
const code2 = String(e.code ?? "");
|
|
24233
|
-
const msg = String(e.message ?? "");
|
|
24234
|
-
if (code2 === "ENOTFOUND" || code2 === "EAI_AGAIN") return "dns";
|
|
24235
|
-
if (code2 === "ECONNREFUSED") return "refused";
|
|
24236
|
-
if (code2 === "ECONNRESET" || code2 === "EPIPE") return "reset";
|
|
24237
|
-
if (code2 === "ETIMEDOUT" || code2 === "UND_ERR_CONNECT_TIMEOUT" || code2 === "UND_ERR_HEADERS_TIMEOUT") return "timeout";
|
|
24238
|
-
if (code2.startsWith("CERT_") || code2.startsWith("ERR_TLS") || code2 === "DEPTH_ZERO_SELF_SIGNED_CERT" || code2 === "SELF_SIGNED_CERT_IN_CHAIN" || code2 === "UNABLE_TO_VERIFY_LEAF_SIGNATURE" || /certificate/i.test(msg)) return "tls";
|
|
24239
|
-
e = e.cause;
|
|
24240
|
-
}
|
|
24241
|
-
return "unknown";
|
|
24242
|
-
}
|
|
24243
|
-
function describeCause(err) {
|
|
24244
|
-
const parts = [];
|
|
24245
|
-
for (let e = err, depth = 0; e && depth < 5; depth++) {
|
|
24246
|
-
const code2 = e.code;
|
|
24247
|
-
if (typeof code2 === "string" && code2 && !parts.includes(code2)) parts.push(code2);
|
|
24248
|
-
e = e.cause;
|
|
24249
|
-
}
|
|
24250
|
-
const base2 = err instanceof Error ? err.message : String(err);
|
|
24251
|
-
return parts.length ? `${base2}: ${parts.join(" \u2192 ")}` : base2;
|
|
24252
|
-
}
|
|
24253
|
-
var init_server_probe = __esm({
|
|
24254
|
-
"src/server-probe.ts"() {
|
|
24255
|
-
"use strict";
|
|
24256
|
-
}
|
|
24257
|
-
});
|
|
24258
|
-
|
|
24259
24294
|
// src/verify-deep.ts
|
|
24260
24295
|
var verify_deep_exports = {};
|
|
24261
24296
|
__export(verify_deep_exports, {
|
|
@@ -24516,10 +24551,10 @@ __export(collector_exports, {
|
|
|
24516
24551
|
isSecretRule: () => isSecretRule,
|
|
24517
24552
|
resolveCodeindexBin: () => resolveCodeindexBin
|
|
24518
24553
|
});
|
|
24519
|
-
import { spawn } from "node:child_process";
|
|
24554
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
24520
24555
|
import { execSync as execSync4 } from "node:child_process";
|
|
24521
24556
|
import { readFileSync as readFileSync44 } from "node:fs";
|
|
24522
|
-
import { join as join53, basename as
|
|
24557
|
+
import { join as join53, basename as basename28 } from "node:path";
|
|
24523
24558
|
async function resolveCodeindexBin(autoInstall = true) {
|
|
24524
24559
|
let st = checkCodeindexStatus();
|
|
24525
24560
|
if (!st.installed && autoInstall)
|
|
@@ -24531,7 +24566,7 @@ async function resolveCodeindexBin(autoInstall = true) {
|
|
|
24531
24566
|
}
|
|
24532
24567
|
function runMcp(binPath, workspace, calls, timeoutMs = 18e4) {
|
|
24533
24568
|
return new Promise((resolve3, reject) => {
|
|
24534
|
-
const p =
|
|
24569
|
+
const p = spawn2(binPath, ["--mcp", "--workspace", workspace], { stdio: ["pipe", "pipe", "ignore"] });
|
|
24535
24570
|
const res = /* @__PURE__ */ new Map();
|
|
24536
24571
|
let pending = "";
|
|
24537
24572
|
let failed = false;
|
|
@@ -24897,7 +24932,7 @@ async function collectCode(opts) {
|
|
|
24897
24932
|
file,
|
|
24898
24933
|
line: null,
|
|
24899
24934
|
rule: "god_module",
|
|
24900
|
-
title: `God module ${
|
|
24935
|
+
title: `God module ${basename28(file)}`,
|
|
24901
24936
|
snippet: `fan-in ${m.fan_in}, fan-out ${m.fan_out}`,
|
|
24902
24937
|
why: FINDING_WHY.coupling,
|
|
24903
24938
|
agentPrompt: couplingPrompt(file, m.fan_in ?? 0, m.fan_out ?? 0, Math.round((m.instability ?? 0) * 100) / 100)
|
|
@@ -25065,7 +25100,7 @@ async function collectCode(opts) {
|
|
|
25065
25100
|
file,
|
|
25066
25101
|
line: v.line ?? null,
|
|
25067
25102
|
rule: v.violation_type ?? "manifest_violation",
|
|
25068
|
-
title: `${v.violation_type} in ${
|
|
25103
|
+
title: `${v.violation_type} in ${basename28(file)}`,
|
|
25069
25104
|
snippet: v.description ?? "",
|
|
25070
25105
|
why: FINDING_WHY.manifest,
|
|
25071
25106
|
agentPrompt: manifestPrompt(v.violation_type ?? "", file, v.line ?? null, v.description ?? "")
|
|
@@ -25836,7 +25871,7 @@ var init_intent_drain = __esm({
|
|
|
25836
25871
|
ownVersion2 = JSON.parse(readFileSync46(join55(dir, "../package.json"), "utf-8")).version || ownVersion2;
|
|
25837
25872
|
} catch {
|
|
25838
25873
|
try {
|
|
25839
|
-
if (true) ownVersion2 = "0.5.
|
|
25874
|
+
if (true) ownVersion2 = "0.5.24";
|
|
25840
25875
|
} catch {
|
|
25841
25876
|
}
|
|
25842
25877
|
}
|
|
@@ -27111,6 +27146,24 @@ __export(device_auth_exports, {
|
|
|
27111
27146
|
deviceLogin: () => deviceLogin
|
|
27112
27147
|
});
|
|
27113
27148
|
import { createHash as createHash19, randomBytes } from "node:crypto";
|
|
27149
|
+
async function readDeviceCode(res, where) {
|
|
27150
|
+
const text = await res.text().catch(() => "");
|
|
27151
|
+
let parsed;
|
|
27152
|
+
try {
|
|
27153
|
+
parsed = JSON.parse(text);
|
|
27154
|
+
} catch {
|
|
27155
|
+
throw new Error(
|
|
27156
|
+
`${where} answered ${res.status} with something that is not a device-code response (${JSON.stringify(text.trim().slice(0, 60))}) \u2014 this host is not serving the chat-recall API`
|
|
27157
|
+
);
|
|
27158
|
+
}
|
|
27159
|
+
const d = parsed;
|
|
27160
|
+
if (typeof d?.device_code !== "string" || typeof d?.user_code !== "string" || typeof d?.verification_uri !== "string" || typeof d?.expires_in !== "number") {
|
|
27161
|
+
throw new Error(
|
|
27162
|
+
`${where} answered ${res.status} with JSON that is not a device-code response (${JSON.stringify(text.trim().slice(0, 60))}) \u2014 this host is not serving the chat-recall API`
|
|
27163
|
+
);
|
|
27164
|
+
}
|
|
27165
|
+
return d;
|
|
27166
|
+
}
|
|
27114
27167
|
async function deviceLogin(opts, onPrompt) {
|
|
27115
27168
|
const issuer = (opts.issuer || DEFAULT_ISSUER).replace(/\/+$/, "");
|
|
27116
27169
|
if (!issuer) {
|
|
@@ -27130,7 +27183,7 @@ async function deviceLogin(opts, onPrompt) {
|
|
|
27130
27183
|
if (!startRes.ok) {
|
|
27131
27184
|
throw new Error(`device-auth init failed: HTTP ${startRes.status} ${await startRes.text().catch(() => "")}`);
|
|
27132
27185
|
}
|
|
27133
|
-
const start = await startRes
|
|
27186
|
+
const start = await readDeviceCode(startRes, issuer);
|
|
27134
27187
|
onPrompt({
|
|
27135
27188
|
url: start.verification_uri_complete || start.verification_uri,
|
|
27136
27189
|
userCode: start.user_code,
|
|
@@ -27184,7 +27237,7 @@ async function betterAuthDeviceLogin(serverUrl, onPrompt) {
|
|
|
27184
27237
|
if (!startRes.ok) {
|
|
27185
27238
|
throw new Error(`device-auth init failed: HTTP ${startRes.status} ${await startRes.text().catch(() => "")}`);
|
|
27186
27239
|
}
|
|
27187
|
-
const start = await startRes
|
|
27240
|
+
const start = await readDeviceCode(startRes, base2);
|
|
27188
27241
|
onPrompt({
|
|
27189
27242
|
url: start.verification_uri_complete || start.verification_uri,
|
|
27190
27243
|
userCode: start.user_code,
|
|
@@ -27328,7 +27381,7 @@ import { Command } from "commander";
|
|
|
27328
27381
|
import chalk from "chalk";
|
|
27329
27382
|
import { existsSync as existsSync56, readFileSync as readFileSync49, writeFileSync as writeFileSync21, realpathSync as realpathSync5 } from "fs";
|
|
27330
27383
|
import { homedir as homedir22 } from "os";
|
|
27331
|
-
import { join as join60, resolve as resolve2, dirname as dirname27, basename as
|
|
27384
|
+
import { join as join60, resolve as resolve2, dirname as dirname27, basename as basename29 } from "path";
|
|
27332
27385
|
|
|
27333
27386
|
// ../engine/dist/core/score-tier.js
|
|
27334
27387
|
var STRONG_RATIO = 0.85;
|
|
@@ -27355,6 +27408,34 @@ function tierAll(results) {
|
|
|
27355
27408
|
// src/cli.ts
|
|
27356
27409
|
init_sync_client();
|
|
27357
27410
|
init_update_notice();
|
|
27411
|
+
init_server_probe();
|
|
27412
|
+
|
|
27413
|
+
// src/open-browser.ts
|
|
27414
|
+
import { spawn } from "node:child_process";
|
|
27415
|
+
function isSafeUrl(url) {
|
|
27416
|
+
try {
|
|
27417
|
+
const u = new URL(url);
|
|
27418
|
+
return u.protocol === "https:" || u.protocol === "http:";
|
|
27419
|
+
} catch {
|
|
27420
|
+
return false;
|
|
27421
|
+
}
|
|
27422
|
+
}
|
|
27423
|
+
function openBrowser(url) {
|
|
27424
|
+
if (!isSafeUrl(url)) return false;
|
|
27425
|
+
if (process.env.CHAT_RECALL_NO_BROWSER === "1" || process.env.CI) return false;
|
|
27426
|
+
const [cmd, args] = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]];
|
|
27427
|
+
try {
|
|
27428
|
+
const child = spawn(cmd, args, { stdio: "ignore", detached: true });
|
|
27429
|
+
child.on("error", () => {
|
|
27430
|
+
});
|
|
27431
|
+
child.unref();
|
|
27432
|
+
return true;
|
|
27433
|
+
} catch {
|
|
27434
|
+
return false;
|
|
27435
|
+
}
|
|
27436
|
+
}
|
|
27437
|
+
|
|
27438
|
+
// src/cli.ts
|
|
27358
27439
|
init_which();
|
|
27359
27440
|
init_collector_health();
|
|
27360
27441
|
init_telemetry_consent();
|
|
@@ -27396,14 +27477,9 @@ function firstTarget() {
|
|
|
27396
27477
|
return { base: cred.serverUrl.replace(/\/+$/, ""), token: cred.token };
|
|
27397
27478
|
}
|
|
27398
27479
|
function describeProbeError(err) {
|
|
27399
|
-
const
|
|
27400
|
-
|
|
27401
|
-
|
|
27402
|
-
if (typeof code2 === "string" && code2 && !codes.includes(code2)) codes.push(code2);
|
|
27403
|
-
e = e.cause;
|
|
27404
|
-
}
|
|
27405
|
-
const base2 = err instanceof Error ? err.message : String(err);
|
|
27406
|
-
return codes.length ? `${base2}: ${codes.join(" \u2192 ")}` : base2;
|
|
27480
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
27481
|
+
if (!/fetch failed|ECONN|ENOTFOUND|EAI_AGAIN|ETIMEDOUT|CERT_|aborted|timeout/i.test(msg)) return msg;
|
|
27482
|
+
return describeUnreachable(err);
|
|
27407
27483
|
}
|
|
27408
27484
|
function failLogin(opts, message) {
|
|
27409
27485
|
if (opts.fromInit) throw new Error(message);
|
|
@@ -27418,11 +27494,21 @@ function requireTarget() {
|
|
|
27418
27494
|
}
|
|
27419
27495
|
return t;
|
|
27420
27496
|
}
|
|
27497
|
+
async function readJsonBody(res, base2, what) {
|
|
27498
|
+
const text = await res.text().catch(() => "");
|
|
27499
|
+
try {
|
|
27500
|
+
return JSON.parse(text);
|
|
27501
|
+
} catch {
|
|
27502
|
+
throw new Error(
|
|
27503
|
+
`${base2} answered ${res.status} with something that is not JSON (${JSON.stringify(text.trim().slice(0, 60))}) for ${what} \u2014 a proxy, a captive portal or a host that no longer serves the chat-recall API`
|
|
27504
|
+
);
|
|
27505
|
+
}
|
|
27506
|
+
}
|
|
27421
27507
|
async function serverGet(path) {
|
|
27422
27508
|
const t = requireTarget();
|
|
27423
27509
|
const res = await fetch(t.base + path, { headers: t.token ? { authorization: `Bearer ${t.token}` } : {} });
|
|
27424
27510
|
if (!res.ok) throw new Error(`server ${path}: HTTP ${res.status} ${await res.text().catch(() => "")}`);
|
|
27425
|
-
return res.
|
|
27511
|
+
return readJsonBody(res, t.base, `GET ${path}`);
|
|
27426
27512
|
}
|
|
27427
27513
|
async function serverGetSoft(path) {
|
|
27428
27514
|
const t = requireTarget();
|
|
@@ -27432,7 +27518,7 @@ async function serverGetSoft(path) {
|
|
|
27432
27518
|
return { status: res.status, data: null, message: body.message };
|
|
27433
27519
|
}
|
|
27434
27520
|
if (!res.ok) throw new Error(`server ${path}: HTTP ${res.status} ${await res.text().catch(() => "")}`);
|
|
27435
|
-
return { status: res.status, data: await res.
|
|
27521
|
+
return { status: res.status, data: await readJsonBody(res, t.base, `GET ${path}`) };
|
|
27436
27522
|
}
|
|
27437
27523
|
async function serverPost(path, body) {
|
|
27438
27524
|
const t = requireTarget();
|
|
@@ -27442,7 +27528,7 @@ async function serverPost(path, body) {
|
|
|
27442
27528
|
body: JSON.stringify(body)
|
|
27443
27529
|
});
|
|
27444
27530
|
if (!res.ok) throw new Error(`server ${path}: HTTP ${res.status} ${await res.text().catch(() => "")}`);
|
|
27445
|
-
return res.
|
|
27531
|
+
return readJsonBody(res, t.base, `POST ${path}`);
|
|
27446
27532
|
}
|
|
27447
27533
|
async function serverPatch(path, body) {
|
|
27448
27534
|
const t = requireTarget();
|
|
@@ -27452,7 +27538,7 @@ async function serverPatch(path, body) {
|
|
|
27452
27538
|
body: JSON.stringify(body)
|
|
27453
27539
|
});
|
|
27454
27540
|
if (!res.ok) throw new Error(`server ${path}: HTTP ${res.status} ${await res.text().catch(() => "")}`);
|
|
27455
|
-
return res.
|
|
27541
|
+
return readJsonBody(res, t.base, `PATCH ${path}`);
|
|
27456
27542
|
}
|
|
27457
27543
|
async function serverDelete(path, body) {
|
|
27458
27544
|
const t = requireTarget();
|
|
@@ -27462,7 +27548,7 @@ async function serverDelete(path, body) {
|
|
|
27462
27548
|
body: JSON.stringify(body)
|
|
27463
27549
|
});
|
|
27464
27550
|
if (!res.ok) throw new Error(`server ${path}: HTTP ${res.status} ${await res.text().catch(() => "")}`);
|
|
27465
|
-
return res.
|
|
27551
|
+
return readJsonBody(res, t.base, `DELETE ${path}`);
|
|
27466
27552
|
}
|
|
27467
27553
|
var pkgEngines = (() => {
|
|
27468
27554
|
try {
|
|
@@ -27474,7 +27560,7 @@ var pkgEngines = (() => {
|
|
|
27474
27560
|
var pkgVersion = JSON.parse(
|
|
27475
27561
|
readFileSync49(new URL("../package.json", import.meta.url), "utf-8")
|
|
27476
27562
|
).version;
|
|
27477
|
-
var DEFAULT_SERVER = "https://chatrecall.dev";
|
|
27563
|
+
var DEFAULT_SERVER = (process.env.CHAT_RECALL_DEFAULT_SERVER || "https://chatrecall.dev").replace(/\/+$/, "");
|
|
27478
27564
|
var DEFAULT_ALLOW = [
|
|
27479
27565
|
"recall_search",
|
|
27480
27566
|
"recall_show",
|
|
@@ -27586,6 +27672,7 @@ program.command("init").description("Set up chat-recall: connect to your server,
|
|
|
27586
27672
|
console.log();
|
|
27587
27673
|
console.log(chalk.bold("2. Connecting to your server..."));
|
|
27588
27674
|
let target = firstTarget();
|
|
27675
|
+
let deadTarget = false;
|
|
27589
27676
|
if (options.server) {
|
|
27590
27677
|
try {
|
|
27591
27678
|
await runLogin(options.server, { token: options.token, fromInit: true, promptJson: options.promptJson });
|
|
@@ -27605,7 +27692,34 @@ program.command("init").description("Set up chat-recall: connect to your server,
|
|
|
27605
27692
|
console.log(` ${chalk.dim(`Run ${chalk.bold("chat-recall login <server-url>")} when ready, or self-host: ${SELF_HOST_DOCS}`)}`);
|
|
27606
27693
|
}
|
|
27607
27694
|
}
|
|
27608
|
-
if (target) {
|
|
27695
|
+
if (target && !options.server) {
|
|
27696
|
+
const { probeServer: probeServer2, probeOk: probeOk2, probeAdvice: probeAdvice2 } = await Promise.resolve().then(() => (init_server_probe(), server_probe_exports));
|
|
27697
|
+
const probe = await probeServer2(target.base, { timeoutMs: 1e4 });
|
|
27698
|
+
if (probeOk2(probe)) {
|
|
27699
|
+
console.log(` ${chalk.green("Connected")} \u2192 ${target.base}`);
|
|
27700
|
+
} else if (probe.kind === "moved" || probe.kind === "not_api") {
|
|
27701
|
+
const retired = target.base;
|
|
27702
|
+
console.log(` ${chalk.yellow("The saved server no longer serves chat-recall")} \u2192 ${retired}`);
|
|
27703
|
+
console.log(` ${probeAdvice2(probe, retired)}`);
|
|
27704
|
+
console.log(` Switching to ${chalk.bold(DEFAULT_SERVER)} \u2014 sign in to approve this machine.`);
|
|
27705
|
+
try {
|
|
27706
|
+
await runLogin(DEFAULT_SERVER, { token: options.token, fromInit: true, promptJson: options.promptJson });
|
|
27707
|
+
const { removeCredentials: removeCredentials2 } = await Promise.resolve().then(() => (init_sync_client(), sync_client_exports));
|
|
27708
|
+
if (removeCredentials2(retired)) {
|
|
27709
|
+
console.log(` ${chalk.dim(`Removed the retired target ${retired} (log in there again any time).`)}`);
|
|
27710
|
+
}
|
|
27711
|
+
target = firstTarget();
|
|
27712
|
+
if (target) console.log(` ${chalk.green("Connected")} \u2192 ${target.base}`);
|
|
27713
|
+
} catch (e) {
|
|
27714
|
+
console.log(` ${chalk.yellow("Not connected.")} ${describeProbeError(e)}`);
|
|
27715
|
+
console.log(` ${chalk.dim(`Run ${chalk.bold(`chat-recall login ${DEFAULT_SERVER}`)} when ready, then ${chalk.bold(`chat-recall logout ${retired}`)}.`)}`);
|
|
27716
|
+
deadTarget = true;
|
|
27717
|
+
}
|
|
27718
|
+
} else {
|
|
27719
|
+
console.log(` ${chalk.yellow("Saved server did not answer")} \u2192 ${target.base}`);
|
|
27720
|
+
console.log(` ${probeAdvice2(probe, target.base)}`);
|
|
27721
|
+
}
|
|
27722
|
+
} else if (target) {
|
|
27609
27723
|
console.log(` ${chalk.green("Connected")} \u2192 ${target.base}`);
|
|
27610
27724
|
}
|
|
27611
27725
|
console.log();
|
|
@@ -27714,22 +27828,40 @@ program.command("init").description("Set up chat-recall: connect to your server,
|
|
|
27714
27828
|
const { executePull: executePull2 } = await Promise.resolve().then(() => (init_toolkit_pull(), toolkit_pull_exports));
|
|
27715
27829
|
const { hostname: hostname3 } = await import("node:os");
|
|
27716
27830
|
const rows = [];
|
|
27831
|
+
let answered = 0;
|
|
27832
|
+
const askFailed = [];
|
|
27717
27833
|
for (const t of loadAllCredentials2()) {
|
|
27718
27834
|
try {
|
|
27719
27835
|
const res = await fetch(`${t.serverUrl}/api/toolkit/browse/mcp?limit=1000`, {
|
|
27720
27836
|
headers: t.token ? { authorization: `Bearer ${t.token}` } : {},
|
|
27721
27837
|
signal: AbortSignal.timeout(2e4)
|
|
27722
27838
|
});
|
|
27723
|
-
if (!res.ok)
|
|
27724
|
-
|
|
27839
|
+
if (!res.ok) {
|
|
27840
|
+
askFailed.push(`${t.serverUrl} (HTTP ${res.status})`);
|
|
27841
|
+
continue;
|
|
27842
|
+
}
|
|
27843
|
+
const text = await res.text();
|
|
27844
|
+
let body;
|
|
27845
|
+
try {
|
|
27846
|
+
body = JSON.parse(text);
|
|
27847
|
+
} catch {
|
|
27848
|
+
askFailed.push(`${t.serverUrl} (not JSON: ${JSON.stringify(text.trim().slice(0, 40))})`);
|
|
27849
|
+
continue;
|
|
27850
|
+
}
|
|
27851
|
+
answered++;
|
|
27725
27852
|
for (const it of body.items || []) rows.push(it);
|
|
27726
|
-
} catch {
|
|
27853
|
+
} catch (e) {
|
|
27854
|
+
askFailed.push(`${t.serverUrl} (${describeProbeError(e)})`);
|
|
27727
27855
|
}
|
|
27728
27856
|
}
|
|
27729
27857
|
const report = executePull2(rows, { thisDeviceId: hostname3(), types: ["mcp"] });
|
|
27730
27858
|
const written = report.outcomes.filter((o) => o.status === "written");
|
|
27731
27859
|
const present = report.outcomes.filter((o) => o.status === "present").length;
|
|
27732
|
-
if (
|
|
27860
|
+
if (answered === 0) {
|
|
27861
|
+
console.log(` ${chalk.yellow("Could not ask your server what MCP servers are on your account")}`);
|
|
27862
|
+
for (const f of askFailed) console.log(` ${chalk.dim(f)}`);
|
|
27863
|
+
console.log(` ${chalk.dim("Re-run `chat-recall toolkit pull` once the server answers.")}`);
|
|
27864
|
+
} else if (written.length === 0 && present === 0) {
|
|
27733
27865
|
console.log(chalk.dim(" Nothing on your account to install yet."));
|
|
27734
27866
|
} else {
|
|
27735
27867
|
console.log(` ${chalk.green(`Installed ${written.length}`)} ${chalk.dim(`(${present} already present)`)}`);
|
|
@@ -27744,7 +27876,10 @@ program.command("init").description("Set up chat-recall: connect to your server,
|
|
|
27744
27876
|
}
|
|
27745
27877
|
console.log();
|
|
27746
27878
|
}
|
|
27747
|
-
if (
|
|
27879
|
+
if (deadTarget) {
|
|
27880
|
+
console.log(chalk.bold("5. Skipping first sync \u2014 the saved server is not serving chat-recall"));
|
|
27881
|
+
console.log(` ${chalk.dim(`Re-point with the two commands above, then run ${chalk.bold("chat-recall sync")}.`)}`);
|
|
27882
|
+
} else if (!options.skipSync && firstTarget()) {
|
|
27748
27883
|
console.log(chalk.bold("5. Indexing and uploading your sessions"));
|
|
27749
27884
|
try {
|
|
27750
27885
|
const { syncSessions: syncSessions2 } = await Promise.resolve().then(() => (init_sync_client(), sync_client_exports));
|
|
@@ -28246,7 +28381,7 @@ program.command("doctor").description("Quick health check across login, server,
|
|
|
28246
28381
|
}
|
|
28247
28382
|
}
|
|
28248
28383
|
for (const hooksJson of claudeHookConfigFiles()) {
|
|
28249
|
-
const profile =
|
|
28384
|
+
const profile = basename29(dirname27(hooksJson));
|
|
28250
28385
|
const label = `Claude hooks (${profile})`;
|
|
28251
28386
|
if (!existsSync57(hooksJson)) {
|
|
28252
28387
|
note(false, label, `no ${hooksJson} \u2014 run \`chat-recall install-hooks\``);
|
|
@@ -29602,7 +29737,7 @@ async function runLogin(serverUrl, opts) {
|
|
|
29602
29737
|
});
|
|
29603
29738
|
return { ok: res.ok, status: res.status };
|
|
29604
29739
|
} catch (err) {
|
|
29605
|
-
return { ok: false, error:
|
|
29740
|
+
return { ok: false, error: describeUnreachable(err) };
|
|
29606
29741
|
}
|
|
29607
29742
|
};
|
|
29608
29743
|
if (opts.token) {
|
|
@@ -29611,12 +29746,21 @@ async function runLogin(serverUrl, opts) {
|
|
|
29611
29746
|
console.error(chalk.dim("Use the Copy button next to the token in the dashboard and paste the full line."));
|
|
29612
29747
|
return failLogin(opts, 'the token contains non-ASCII characters \u2014 this looks like a masked/truncated copy (e.g. "ct_12345678\u2026")');
|
|
29613
29748
|
}
|
|
29749
|
+
const { probeServer: identifyServer, probeOk: identified, probeAdvice: identifyAdvice } = await Promise.resolve().then(() => (init_server_probe(), server_probe_exports));
|
|
29750
|
+
const who = await identifyServer(base2, { timeoutMs: 1e4 });
|
|
29751
|
+
if (!identified(who)) return failLogin(opts, identifyAdvice(who, base2));
|
|
29614
29752
|
const check = await verifyToken(token);
|
|
29615
29753
|
if (!check.ok) {
|
|
29616
29754
|
if (check.status === 401 || check.status === 403) {
|
|
29617
29755
|
console.error(chalk.dim("Tokens are shown once \u2014 mint a fresh one (Account \u2192 Connect your machine) and copy the whole line."));
|
|
29618
29756
|
return failLogin(opts, `${base2} rejected the token (HTTP ${check.status})`);
|
|
29619
29757
|
}
|
|
29758
|
+
if (check.status !== void 0 && check.status >= 500) {
|
|
29759
|
+
return failLogin(opts, `${base2} answered HTTP ${check.status} \u2014 the server is up but failing. The token was never checked. Nothing to fix locally.`);
|
|
29760
|
+
}
|
|
29761
|
+
if (check.status === 404 || check.status === 410) {
|
|
29762
|
+
return failLogin(opts, `${base2} does not serve the chat-recall API (HTTP ${check.status}). If the server moved, log in to its new URL.`);
|
|
29763
|
+
}
|
|
29620
29764
|
return failLogin(opts, `could not verify the token against ${base2}: ${check.error || `HTTP ${check.status}`}`);
|
|
29621
29765
|
}
|
|
29622
29766
|
saveCredentials2({ serverUrl, token });
|
|
@@ -29629,11 +29773,15 @@ async function runLogin(serverUrl, opts) {
|
|
|
29629
29773
|
return;
|
|
29630
29774
|
}
|
|
29631
29775
|
try {
|
|
29632
|
-
const
|
|
29633
|
-
|
|
29634
|
-
|
|
29635
|
-
|
|
29636
|
-
|
|
29776
|
+
const { probeServer: probeServer2, probeOk: probeOk2 } = await Promise.resolve().then(() => (init_server_probe(), server_probe_exports));
|
|
29777
|
+
const identified = await probeServer2(base2, { timeoutMs: 8e3 });
|
|
29778
|
+
if (probeOk2(identified)) {
|
|
29779
|
+
const probe = await fetch(`${base2}/api/status`, { signal: AbortSignal.timeout(8e3) });
|
|
29780
|
+
if (probe.status === 200) {
|
|
29781
|
+
saveCredentials2({ serverUrl, token: "" });
|
|
29782
|
+
console.log(chalk.green("\u2713 Logged in.") + chalk.dim(` ${serverUrl} (local server \u2014 no auth, no token needed)`));
|
|
29783
|
+
return;
|
|
29784
|
+
}
|
|
29637
29785
|
}
|
|
29638
29786
|
} catch {
|
|
29639
29787
|
}
|
|
@@ -29651,14 +29799,12 @@ async function runLogin(serverUrl, opts) {
|
|
|
29651
29799
|
for (let attempt = 1; attempt <= 3 && !authProvider; attempt++) {
|
|
29652
29800
|
const probe = await probeServer2(base2, { timeoutMs: 15e3 });
|
|
29653
29801
|
if (probeOk2(probe)) {
|
|
29654
|
-
|
|
29655
|
-
|
|
29656
|
-
|
|
29657
|
-
|
|
29658
|
-
|
|
29659
|
-
|
|
29660
|
-
capsError = describeProbeError(err);
|
|
29661
|
-
}
|
|
29802
|
+
const caps = probe.raw;
|
|
29803
|
+
authProvider = caps?.authProvider;
|
|
29804
|
+
if (caps?.oidcIssuer) issuer = caps.oidcIssuer;
|
|
29805
|
+
capsError = null;
|
|
29806
|
+
} else if (probe.kind === "moved" || probe.kind === "not_api") {
|
|
29807
|
+
return failLogin(opts, probeAdvice2(probe, base2));
|
|
29662
29808
|
} else {
|
|
29663
29809
|
capsError = probeAdvice2(probe, base2);
|
|
29664
29810
|
}
|
|
@@ -29686,12 +29832,16 @@ async function runLogin(serverUrl, opts) {
|
|
|
29686
29832
|
console.log(chalk.bold("To log in, open:"));
|
|
29687
29833
|
console.log(" " + chalk.cyan(p.url));
|
|
29688
29834
|
console.log(chalk.dim(` (if prompted, enter code: ${chalk.bold(p.userCode)} at ${p.verificationUri})`));
|
|
29835
|
+
const opened = openBrowser(p.url);
|
|
29836
|
+
if (opened) console.log(chalk.dim(" (opening your browser\u2026 if nothing happens, use the link above)"));
|
|
29689
29837
|
console.log(chalk.dim("After approving, come back to THIS terminal \u2014 it finishes the login by itself."));
|
|
29690
29838
|
console.log(chalk.dim("Waiting for approval\u2026"));
|
|
29691
29839
|
};
|
|
29692
29840
|
const tokens = authProvider === "better-auth" ? await betterAuthDeviceLogin2(base2, onPrompt) : await deviceLogin2({ issuer, clientId: opts.clientId }, onPrompt);
|
|
29693
29841
|
const authHdr = { authorization: `Bearer ${tokens.accessToken}`, "content-type": "application/json" };
|
|
29694
|
-
const
|
|
29842
|
+
const meRes = await fetch(`${base2}/api/me`, { headers: authHdr });
|
|
29843
|
+
if (!meRes.ok) throw new Error(`${base2}/api/me: HTTP ${meRes.status} ${await meRes.text().catch(() => "")}`);
|
|
29844
|
+
const me = await readJsonBody(meRes, base2, "GET /api/me");
|
|
29695
29845
|
const teams = me.teams || [];
|
|
29696
29846
|
let slug = opts.team;
|
|
29697
29847
|
if (!slug) {
|
|
@@ -29700,7 +29850,7 @@ async function runLogin(serverUrl, opts) {
|
|
|
29700
29850
|
const name = me.user?.email?.split("@")[0]?.replace(/[^a-z0-9]/gi, "") || "workspace";
|
|
29701
29851
|
const created = await fetch(`${base2}/api/teams`, { method: "POST", headers: authHdr, body: JSON.stringify({ name }) });
|
|
29702
29852
|
if (!created.ok) throw new Error(`workspace create failed: HTTP ${created.status} ${await created.text().catch(() => "")}`);
|
|
29703
|
-
slug = (await created
|
|
29853
|
+
slug = (await readJsonBody(created, base2, "POST /api/teams")).slug;
|
|
29704
29854
|
console.log(chalk.dim(`Created workspace "${name}" (${slug}) \u2014 you're its owner.`));
|
|
29705
29855
|
} else {
|
|
29706
29856
|
teams.forEach((t) => console.error(` ${t.team_slug} ${chalk.dim(t.name)}`));
|
|
@@ -29714,7 +29864,7 @@ async function runLogin(serverUrl, opts) {
|
|
|
29714
29864
|
body: JSON.stringify({ device_id: deviceId })
|
|
29715
29865
|
});
|
|
29716
29866
|
if (!mint.ok) throw new Error(`token mint failed: HTTP ${mint.status} ${await mint.text().catch(() => "")}`);
|
|
29717
|
-
const { token } = await mint
|
|
29867
|
+
const { token } = await readJsonBody(mint, base2, "POST /api/teams/:slug/tokens");
|
|
29718
29868
|
saveCredentials2({ serverUrl, token });
|
|
29719
29869
|
console.log(chalk.green(`\u2713 Logged in to ${chalk.bold(slug)}`) + chalk.dim(` (device: ${deviceId}) server: ${serverUrl}`));
|
|
29720
29870
|
if (!opts.fromInit) {
|