@taskless-app/swarm 0.6.26 → 0.6.28
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 +271 -218
- package/dist/overmind.js +494 -241
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -340,26 +340,6 @@ var init_timeline = __esm({
|
|
|
340
340
|
}
|
|
341
341
|
});
|
|
342
342
|
|
|
343
|
-
// ../swarm-core/src/reviewPhase.ts
|
|
344
|
-
var REVIEW_PHASE_TEXT, PHASE_BY_LENGTH;
|
|
345
|
-
var init_reviewPhase = __esm({
|
|
346
|
-
"../swarm-core/src/reviewPhase.ts"() {
|
|
347
|
-
"use strict";
|
|
348
|
-
REVIEW_PHASE_TEXT = {
|
|
349
|
-
checks: "\u0418\u0434\u0443\u0442 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438",
|
|
350
|
-
awaiting_reviewer: "\u0417\u0435\u043B\u0451\u043D\u044B\u0439 \u2014 \u0436\u0434\u0451\u0442 \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430",
|
|
351
|
-
ci_fixing: "CI \u043A\u0440\u0430\u0441\u043D\u044B\u0439 \u2014 \u0434\u0440\u043E\u043D \u0447\u0438\u043D\u0438\u0442",
|
|
352
|
-
in_review: "\u041D\u0430 \u0440\u0435\u0432\u044C\u044E",
|
|
353
|
-
changes: "\u041D\u0443\u0436\u043D\u044B \u043F\u0440\u0430\u0432\u043A\u0438 \u2014 \u0434\u0440\u043E\u043D \u043F\u0440\u0430\u0432\u0438\u0442",
|
|
354
|
-
ready: "\u0417\u0435\u043B\u0451\u043D\u044B\u0439 \u2014 \u0433\u043E\u0442\u043E\u0432 \u043A \u043C\u0435\u0440\u0436\u0443",
|
|
355
|
-
reviewer_dead: "\u0420\u0435\u0432\u044C\u044E\u0435\u0440 \u0443\u043C\u0435\u0440 \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u043D\u0435\u0442, \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A"
|
|
356
|
-
};
|
|
357
|
-
PHASE_BY_LENGTH = Object.keys(REVIEW_PHASE_TEXT).sort(
|
|
358
|
-
(a, b) => REVIEW_PHASE_TEXT[b].length - REVIEW_PHASE_TEXT[a].length
|
|
359
|
-
);
|
|
360
|
-
}
|
|
361
|
-
});
|
|
362
|
-
|
|
363
343
|
// ../swarm-core/src/verifyVerdict.ts
|
|
364
344
|
function isVerifyVerdict(v) {
|
|
365
345
|
return typeof v === "string" && VERIFY_VERDICT_SET.has(v);
|
|
@@ -428,6 +408,31 @@ var init_verifyVerdict = __esm({
|
|
|
428
408
|
}
|
|
429
409
|
});
|
|
430
410
|
|
|
411
|
+
// ../swarm-core/src/reviewPhase.ts
|
|
412
|
+
var REVIEW_PHASE_TEXT, PHASE_BY_LENGTH;
|
|
413
|
+
var init_reviewPhase = __esm({
|
|
414
|
+
"../swarm-core/src/reviewPhase.ts"() {
|
|
415
|
+
"use strict";
|
|
416
|
+
init_verifyVerdict();
|
|
417
|
+
REVIEW_PHASE_TEXT = {
|
|
418
|
+
checks: "\u0418\u0434\u0443\u0442 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438",
|
|
419
|
+
awaiting_reviewer: "\u0417\u0435\u043B\u0451\u043D\u044B\u0439 \u2014 \u0436\u0434\u0451\u0442 \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430",
|
|
420
|
+
ci_fixing: "CI \u043A\u0440\u0430\u0441\u043D\u044B\u0439 \u2014 \u0434\u0440\u043E\u043D \u0447\u0438\u043D\u0438\u0442",
|
|
421
|
+
in_review: "\u041D\u0430 \u0440\u0435\u0432\u044C\u044E",
|
|
422
|
+
changes: "\u041D\u0443\u0436\u043D\u044B \u043F\u0440\u0430\u0432\u043A\u0438 \u2014 \u0434\u0440\u043E\u043D \u043F\u0440\u0430\u0432\u0438\u0442",
|
|
423
|
+
verify_found: "\u0412\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u043D\u0430\u0448\u0451\u043B \u2014 \u043D\u0443\u0436\u043D\u044B \u043F\u0440\u0430\u0432\u043A\u0438",
|
|
424
|
+
verify_not_reproduced: "\u0412\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u043D\u0435 \u0432\u043E\u0441\u043F\u0440\u043E\u0438\u0437\u0432\u0451\u043B \u2014 \u043D\u0443\u0436\u043D\u044B \u043F\u0440\u0430\u0432\u043A\u0438",
|
|
425
|
+
awaiting_verify: "\u0417\u0435\u043B\u0451\u043D\u044B\u0439 \u2014 \u0436\u0434\u0451\u043C \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044E",
|
|
426
|
+
verify_dead: "\u0412\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u043D\u0435 \u0434\u0430\u043B \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A",
|
|
427
|
+
ready: "\u0417\u0435\u043B\u0451\u043D\u044B\u0439 \u2014 \u0433\u043E\u0442\u043E\u0432 \u043A \u043C\u0435\u0440\u0436\u0443",
|
|
428
|
+
reviewer_dead: "\u0420\u0435\u0432\u044C\u044E\u0435\u0440 \u0443\u043C\u0435\u0440 \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u043D\u0435\u0442, \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A"
|
|
429
|
+
};
|
|
430
|
+
PHASE_BY_LENGTH = Object.keys(REVIEW_PHASE_TEXT).sort(
|
|
431
|
+
(a, b) => REVIEW_PHASE_TEXT[b].length - REVIEW_PHASE_TEXT[a].length
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
|
|
431
436
|
// ../swarm-core/src/mergeGate.ts
|
|
432
437
|
function computeMergeGate(input) {
|
|
433
438
|
if (!input.hasRun) {
|
|
@@ -1619,6 +1624,13 @@ var init_zoneMemory = __esm({
|
|
|
1619
1624
|
}
|
|
1620
1625
|
});
|
|
1621
1626
|
|
|
1627
|
+
// ../swarm-core/src/taskPrs.ts
|
|
1628
|
+
var init_taskPrs = __esm({
|
|
1629
|
+
"../swarm-core/src/taskPrs.ts"() {
|
|
1630
|
+
"use strict";
|
|
1631
|
+
}
|
|
1632
|
+
});
|
|
1633
|
+
|
|
1622
1634
|
// ../swarm-core/src/index.ts
|
|
1623
1635
|
var init_src = __esm({
|
|
1624
1636
|
"../swarm-core/src/index.ts"() {
|
|
@@ -1653,6 +1665,7 @@ var init_src = __esm({
|
|
|
1653
1665
|
init_effort();
|
|
1654
1666
|
init_queuePlan();
|
|
1655
1667
|
init_zoneMemory();
|
|
1668
|
+
init_taskPrs();
|
|
1656
1669
|
}
|
|
1657
1670
|
});
|
|
1658
1671
|
|
|
@@ -2757,6 +2770,192 @@ var init_storm = __esm({
|
|
|
2757
2770
|
}
|
|
2758
2771
|
});
|
|
2759
2772
|
|
|
2773
|
+
// src/agentToken.ts
|
|
2774
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, unlinkSync } from "fs";
|
|
2775
|
+
import { join as join6 } from "path";
|
|
2776
|
+
function loadAgentToken() {
|
|
2777
|
+
try {
|
|
2778
|
+
const s = JSON.parse(readFileSync4(STORE_PATH, "utf8"));
|
|
2779
|
+
return s.token ? s : null;
|
|
2780
|
+
} catch {
|
|
2781
|
+
return null;
|
|
2782
|
+
}
|
|
2783
|
+
}
|
|
2784
|
+
function saveAgentToken(s) {
|
|
2785
|
+
writeFileSync4(STORE_PATH, JSON.stringify(s, null, 2), { mode: 384 });
|
|
2786
|
+
}
|
|
2787
|
+
function clearAgentToken() {
|
|
2788
|
+
try {
|
|
2789
|
+
unlinkSync(STORE_PATH);
|
|
2790
|
+
} catch {
|
|
2791
|
+
}
|
|
2792
|
+
}
|
|
2793
|
+
async function mintAgentToken(args, fetchFn = fetch) {
|
|
2794
|
+
const res = await fetchFn(`${base(args.api)}/api/swarm/agent/token`, {
|
|
2795
|
+
method: "POST",
|
|
2796
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${args.bearer}` },
|
|
2797
|
+
body: JSON.stringify({
|
|
2798
|
+
...args.label ? { label: args.label } : {},
|
|
2799
|
+
...args.hostId ? { hostId: args.hostId } : {},
|
|
2800
|
+
...args.hostName ? { hostName: args.hostName } : {}
|
|
2801
|
+
})
|
|
2802
|
+
});
|
|
2803
|
+
const text = await res.text();
|
|
2804
|
+
if (!res.ok) throw new Error(`agent token: HTTP ${res.status} ${text.slice(0, 200)}`);
|
|
2805
|
+
const j = JSON.parse(text);
|
|
2806
|
+
if (!j.token) throw new Error("agent token: \u0432 \u043E\u0442\u0432\u0435\u0442\u0435 \u043D\u0435\u0442 token");
|
|
2807
|
+
return { token: j.token, ...j.tokenId ? { tokenId: j.tokenId } : {} };
|
|
2808
|
+
}
|
|
2809
|
+
async function probeAgentToken(args, fetchFn = fetch) {
|
|
2810
|
+
try {
|
|
2811
|
+
const res = await fetchFn(`${base(args.api)}/api/auth/me`, {
|
|
2812
|
+
headers: { Authorization: `Bearer ${args.token}` },
|
|
2813
|
+
// Оборвётся по таймауту — попадём в catch ниже, то есть в "unreachable": это ровно
|
|
2814
|
+
// то, чем висящая сеть и является. Соседи по CLI (daemonLink) живут так же.
|
|
2815
|
+
signal: AbortSignal.timeout(args.timeoutMs ?? TOKEN_PROBE_TIMEOUT_MS)
|
|
2816
|
+
});
|
|
2817
|
+
if (res.status === 401 || res.status === 403) return "revoked";
|
|
2818
|
+
return res.ok ? "ok" : "unreachable";
|
|
2819
|
+
} catch {
|
|
2820
|
+
return "unreachable";
|
|
2821
|
+
}
|
|
2822
|
+
}
|
|
2823
|
+
function isRevokedTokenError(e) {
|
|
2824
|
+
if (e && typeof e === "object" && e.code === 401) return true;
|
|
2825
|
+
const text = e instanceof Error ? e.message : typeof e === "string" ? e : "";
|
|
2826
|
+
return /invalid_token|(?:http|status|code)[ =:]*401\b|\(401\)|401 unauthorized/i.test(text);
|
|
2827
|
+
}
|
|
2828
|
+
function revokedTokenLine() {
|
|
2829
|
+
return "[taskless] \u0442\u043E\u043A\u0435\u043D \u0440\u043E\u044F \u043E\u0442\u043E\u0437\u0432\u0430\u043D (401 invalid_token) \u2014 \u0440\u043E\u0439 \u043D\u0435 \u0431\u0435\u0440\u0451\u0442 \u0440\u0430\u0431\u043E\u0442\u0443. \u041F\u043E\u0447\u0438\u043D\u0438 \u043E\u0434\u043D\u043E\u0439 \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439: swarm login";
|
|
2830
|
+
}
|
|
2831
|
+
function revokedTokenComplaint() {
|
|
2832
|
+
return [
|
|
2833
|
+
"",
|
|
2834
|
+
"\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E",
|
|
2835
|
+
"\u2502 \u0422\u041E\u041A\u0415\u041D \u0420\u041E\u042F \u041D\u0415 \u041F\u0420\u0418\u041D\u042F\u0422 (401). \u0414\u043E\u0441\u0442\u0443\u043F \u043A Taskless \u0440\u0430\u0437\u043E\u0440\u0432\u0430\u043D. \u2502",
|
|
2836
|
+
"\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F",
|
|
2837
|
+
"\u041F\u0440\u0438\u0447\u0438\u043D\u0430 \u2014 \u043E\u0434\u043D\u0430 \u0438\u0437 \u0442\u0440\u0451\u0445, \u0432\u0441\u0435 \u0442\u0440\u0438 \u0440\u0435\u0448\u0430\u044E\u0442\u0441\u044F \u0432\u043B\u0430\u0434\u0435\u043B\u044C\u0446\u0435\u043C:",
|
|
2838
|
+
" \u2022 \xAB\u043E\u0442\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u044D\u0442\u043E\u0442 \u043A\u043E\u043C\u043F\u044C\u044E\u0442\u0435\u0440\xBB \u0432 \u043F\u0430\u0440\u043A\u0435 \u043C\u0430\u0448\u0438\u043D \u2014 \u043E\u0442\u043E\u0437\u0432\u0430\u043D \u0442\u043E\u043A\u0435\u043D \u042D\u0422\u041E\u0419 \u043C\u0430\u0448\u0438\u043D\u044B;",
|
|
2839
|
+
" \u2022 \xAB\u0441\u0431\u0440\u043E\u0441\u0438\u0442\u044C \u0434\u043E\u0441\u0442\u0443\u043F \u0440\u043E\u044F\xBB \u0432 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0430\u0445 (\u041F\u0440\u043E\u0444\u0438\u043B\u044C \u2192 \u0420\u043E\u0439) \u2014 \u043E\u0442\u043E\u0437\u0432\u0430\u043D\u044B \u0432\u0441\u0435;",
|
|
2840
|
+
" \u2022 \u0440\u043E\u0439 \u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D \u043C\u0430\u0441\u0442\u0435\u0440-\u0442\u0443\u043C\u0431\u043B\u0435\u0440\u043E\u043C \u2014 \u0442\u043E\u043A\u0435\u043D \u0430\u0433\u0435\u043D\u0442\u0430 \u043F\u0435\u0440\u0435\u0441\u0442\u0430\u0451\u0442 \u0434\u0435\u0439\u0441\u0442\u0432\u043E\u0432\u0430\u0442\u044C.",
|
|
2841
|
+
"",
|
|
2842
|
+
"\u0412\u0445\u043E\u0434 \u043D\u0430 \u0414\u0420\u0423\u0413\u041E\u0419 \u043C\u0430\u0448\u0438\u043D\u0435 \u044D\u0442\u043E\u0442 \u0442\u043E\u043A\u0435\u043D \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u0433\u0430\u0441\u0438\u0442 (TASWM-324): \u0443 \u043A\u0430\u0436\u0434\u043E\u0439",
|
|
2843
|
+
"\u043C\u0430\u0448\u0438\u043D\u044B \u0441\u0432\u043E\u0439 \u0442\u043E\u043A\u0435\u043D, \u043F\u0440\u0438\u0432\u044F\u0437\u0430\u043D\u043D\u044B\u0439 \u043A \u0435\u0451 hostId.",
|
|
2844
|
+
"",
|
|
2845
|
+
"\u0414\u0435\u043C\u043E\u043D \u041D\u0415 \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u0432\u0441\u043B\u0435\u043F\u0443\u044E: \u043F\u043E\u043A\u0430 \u0442\u043E\u043A\u0435\u043D\u0430 \u043D\u0435\u0442, \u043F\u0440\u043E\u0433\u043E\u043D\u044B \u043D\u0435 \u0431\u0435\u0440\u0443\u0442\u0441\u044F \u0438 \u043E\u0442\u0447\u0451\u0442\u044B",
|
|
2846
|
+
"\u043D\u0435 \u043F\u0438\u0448\u0443\u0442\u0441\u044F. \u041F\u043E\u0447\u0438\u043D\u0438 \u0432\u0445\u043E\u0434\u043E\u043C \u0437\u0430\u043D\u043E\u0432\u043E: swarm login",
|
|
2847
|
+
"\u041F\u0440\u043E\u0431\u0430 \u0442\u043E\u043A\u0435\u043D\u0430 \u043D\u0435 \u043F\u0440\u043E\u0445\u043E\u0434\u0438\u0442, \u0430 \u0432\u043E\u0439\u0442\u0438 \u043C\u0435\u0448\u0430\u0435\u0442 \u0441\u0442\u0430\u0440\u044B\u0439 \u0444\u0430\u0439\u043B: swarm login --force",
|
|
2848
|
+
""
|
|
2849
|
+
].join("\n");
|
|
2850
|
+
}
|
|
2851
|
+
var STORE_PATH, base, TOKEN_PROBE_TIMEOUT_MS;
|
|
2852
|
+
var init_agentToken = __esm({
|
|
2853
|
+
"src/agentToken.ts"() {
|
|
2854
|
+
"use strict";
|
|
2855
|
+
init_config();
|
|
2856
|
+
STORE_PATH = join6(SWARM_HOME, "agent.json");
|
|
2857
|
+
base = (api) => api.replace(/\/$/, "");
|
|
2858
|
+
TOKEN_PROBE_TIMEOUT_MS = 8e3;
|
|
2859
|
+
}
|
|
2860
|
+
});
|
|
2861
|
+
|
|
2862
|
+
// src/linkFailure.ts
|
|
2863
|
+
function describeError(e, maxDepth = 4) {
|
|
2864
|
+
const parts = [];
|
|
2865
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2866
|
+
let cur = e;
|
|
2867
|
+
for (let depth = 0; depth < maxDepth && cur != null && !seen.has(cur); depth++) {
|
|
2868
|
+
seen.add(cur);
|
|
2869
|
+
const msg = (cur instanceof Error ? cur.message : typeof cur === "string" ? cur : String(cur)).trim();
|
|
2870
|
+
const code = cur.code;
|
|
2871
|
+
const codeText = code === void 0 || code === null ? "" : String(code);
|
|
2872
|
+
const label2 = codeText && !msg.includes(codeText) ? `${msg} [code=${codeText}]` : msg;
|
|
2873
|
+
if (label2 && !parts.includes(label2)) parts.push(label2);
|
|
2874
|
+
cur = cur.cause;
|
|
2875
|
+
}
|
|
2876
|
+
return parts.join(" \u2190 ") || String(e);
|
|
2877
|
+
}
|
|
2878
|
+
function explainLinkFailure(e) {
|
|
2879
|
+
const detail = describeError(e);
|
|
2880
|
+
if (detail.includes(UNKNOWN_LINK_REASON)) return { kind: "unknown", detail };
|
|
2881
|
+
if (isRevokedTokenError(e) || isRevokedTokenError(detail)) return { kind: "unauthorized", detail };
|
|
2882
|
+
if (MISSING_TOOL_RE.test(detail)) return { kind: "missing_tool", detail };
|
|
2883
|
+
if (OFFLINE_RE.test(detail)) return { kind: "offline", detail };
|
|
2884
|
+
return { kind: "rejected", detail };
|
|
2885
|
+
}
|
|
2886
|
+
var UNKNOWN_LINK_REASON, OFFLINE_RE, MISSING_TOOL_RE;
|
|
2887
|
+
var init_linkFailure = __esm({
|
|
2888
|
+
"src/linkFailure.ts"() {
|
|
2889
|
+
"use strict";
|
|
2890
|
+
init_agentToken();
|
|
2891
|
+
UNKNOWN_LINK_REASON = "\u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u044F \u043D\u0435\u0442, \u0430 \u043F\u0440\u0438\u0447\u0438\u043D\u0443 \u043A\u043E\u043D\u043D\u0435\u043A\u0442\u043E\u0440 \u043D\u0435 \u043D\u0430\u0437\u0432\u0430\u043B";
|
|
2892
|
+
OFFLINE_RE = /fetch failed|econnreset|econnrefused|econnaborted|enotfound|eai_again|etimedout|ehostunreach|enetunreach|epipe|socket hang up|und_err|network error|connection (?:closed|refused|reset|error)|connect timeout/i;
|
|
2893
|
+
MISSING_TOOL_RE = /tool[^.]{0,40}not found|unknown tool|tool_not_found|method not found|-32601\b/i;
|
|
2894
|
+
}
|
|
2895
|
+
});
|
|
2896
|
+
|
|
2897
|
+
// src/toolFailure.ts
|
|
2898
|
+
function classifyToolFailure(raw) {
|
|
2899
|
+
if (MISMATCH_RE.test(raw)) return TOOL_FAILURE.WORKSPACE_MISMATCH;
|
|
2900
|
+
if (FORBIDDEN_RE.test(raw)) return TOOL_FAILURE.FORBIDDEN;
|
|
2901
|
+
if (NOT_FOUND_RE.test(raw)) return TOOL_FAILURE.NOT_FOUND;
|
|
2902
|
+
return TOOL_FAILURE.TOOL_ERROR;
|
|
2903
|
+
}
|
|
2904
|
+
function classifyThrown(e) {
|
|
2905
|
+
const { kind, detail } = explainLinkFailure(e);
|
|
2906
|
+
const code = kind === "offline" ? TOOL_FAILURE.UNREACHABLE : kind === "unauthorized" ? TOOL_FAILURE.UNAUTHORIZED : (
|
|
2907
|
+
// «ручки нет» и «сервер отказал по существу» разбираем доменно: там может быть
|
|
2908
|
+
// и task_not_found, и отказ по правам — то есть наши коды, а не транспортные.
|
|
2909
|
+
classifyToolFailure(detail)
|
|
2910
|
+
);
|
|
2911
|
+
return { code, detail };
|
|
2912
|
+
}
|
|
2913
|
+
var TOOL_FAILURE, TOOL_FAILURE_TEXT, TOOL_FAILURE_RETRIABLE, NOT_FOUND_RE, FORBIDDEN_RE, MISMATCH_RE;
|
|
2914
|
+
var init_toolFailure = __esm({
|
|
2915
|
+
"src/toolFailure.ts"() {
|
|
2916
|
+
"use strict";
|
|
2917
|
+
init_linkFailure();
|
|
2918
|
+
TOOL_FAILURE = {
|
|
2919
|
+
/** Тул отработал, но искомого в ответе нет: задачи с таким слагом в воркспейсе не существует. */
|
|
2920
|
+
NOT_FOUND: "not_found",
|
|
2921
|
+
/** Объект есть, но токену роя не виден (приватный проект, спейс, чужой воркспейс). */
|
|
2922
|
+
FORBIDDEN: "forbidden",
|
|
2923
|
+
/** Токен роя не состоит в воркспейсе, которым искали. */
|
|
2924
|
+
WORKSPACE_MISMATCH: "workspace_mismatch",
|
|
2925
|
+
/** До прода не достучались: сеть, DNS, таймаут, оборванный сокет. */
|
|
2926
|
+
UNREACHABLE: "unreachable",
|
|
2927
|
+
/** Прод ответил, но токен роя отвергнут (отозван, протух). Сетью это не лечится. */
|
|
2928
|
+
UNAUTHORIZED: "unauthorized",
|
|
2929
|
+
/** Тул ответил ошибкой, которую мы не узнали, — текст кладём в detail как есть. */
|
|
2930
|
+
TOOL_ERROR: "tool_error",
|
|
2931
|
+
/** Успех, но полезной нагрузки в ответе нет (форма ответа разошлась с ожиданием). */
|
|
2932
|
+
EMPTY: "empty"
|
|
2933
|
+
};
|
|
2934
|
+
TOOL_FAILURE_TEXT = {
|
|
2935
|
+
[TOOL_FAILURE.NOT_FOUND]: "\u0432 \u044D\u0442\u043E\u043C \u0432\u043E\u0440\u043A\u0441\u043F\u0435\u0439\u0441\u0435 \u0442\u0430\u043A\u043E\u0433\u043E \u043D\u0435\u0442 \u2014 \u043F\u0440\u043E\u0432\u0435\u0440\u044C \u0441\u043B\u0430\u0433 \u0438 \u0432\u043E\u0440\u043A\u0441\u043F\u0435\u0439\u0441 \u043F\u0440\u043E\u0433\u043E\u043D\u0430",
|
|
2936
|
+
[TOOL_FAILURE.FORBIDDEN]: "\u0434\u043E\u0441\u0442\u0443\u043F\u0430 \u043D\u0435\u0442: \u043E\u0431\u044A\u0435\u043A\u0442 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442, \u043D\u043E \u0442\u043E\u043A\u0435\u043D\u0443 \u0440\u043E\u044F \u043D\u0435 \u0432\u0438\u0434\u0435\u043D \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A",
|
|
2937
|
+
[TOOL_FAILURE.WORKSPACE_MISMATCH]: "\u0442\u043E\u043A\u0435\u043D \u0440\u043E\u044F \u043D\u0435 \u0441\u043E\u0441\u0442\u043E\u0438\u0442 \u0432 \u044D\u0442\u043E\u043C \u0432\u043E\u0440\u043A\u0441\u043F\u0435\u0439\u0441\u0435",
|
|
2938
|
+
[TOOL_FAILURE.UNREACHABLE]: "Taskless \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u2014 \u0441\u0435\u0442\u044C, DNS \u0438\u043B\u0438 \u0442\u0430\u0439\u043C\u0430\u0443\u0442",
|
|
2939
|
+
[TOOL_FAILURE.UNAUTHORIZED]: "\u0442\u043E\u043A\u0435\u043D \u0440\u043E\u044F \u043E\u0442\u0432\u0435\u0440\u0433\u043D\u0443\u0442 \u043F\u0440\u043E\u0434\u043E\u043C \u2014 \u043D\u0443\u0436\u0435\u043D swarm login \u043D\u0430 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435",
|
|
2940
|
+
[TOOL_FAILURE.TOOL_ERROR]: "\u0442\u0443\u043B \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u043E\u0448\u0438\u0431\u043A\u043E\u0439",
|
|
2941
|
+
[TOOL_FAILURE.EMPTY]: "\u0442\u0443\u043B \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u0443\u0441\u043F\u0435\u0445\u043E\u043C, \u043D\u043E \u0434\u0430\u043D\u043D\u044B\u0445 \u0432 \u043E\u0442\u0432\u0435\u0442\u0435 \u043D\u0435\u0442"
|
|
2942
|
+
};
|
|
2943
|
+
TOOL_FAILURE_RETRIABLE = {
|
|
2944
|
+
[TOOL_FAILURE.NOT_FOUND]: false,
|
|
2945
|
+
[TOOL_FAILURE.FORBIDDEN]: false,
|
|
2946
|
+
[TOOL_FAILURE.WORKSPACE_MISMATCH]: false,
|
|
2947
|
+
[TOOL_FAILURE.UNREACHABLE]: true,
|
|
2948
|
+
// Отозванный токен повтором не чинится — его меняет человек (`swarm login`).
|
|
2949
|
+
[TOOL_FAILURE.UNAUTHORIZED]: false,
|
|
2950
|
+
[TOOL_FAILURE.TOOL_ERROR]: false,
|
|
2951
|
+
[TOOL_FAILURE.EMPTY]: false
|
|
2952
|
+
};
|
|
2953
|
+
NOT_FOUND_RE = /task_not_found|not_found|не найдена|не найден/i;
|
|
2954
|
+
FORBIDDEN_RE = /forbidden|not visible|no access|нет доступа|(?:http|status|code)[ =:]*403\b|\(403\)|403 forbidden/i;
|
|
2955
|
+
MISMATCH_RE = /workspace_mismatch|not a member of that workspace/i;
|
|
2956
|
+
}
|
|
2957
|
+
});
|
|
2958
|
+
|
|
2760
2959
|
// src/procList.ts
|
|
2761
2960
|
function parsePsRows(out) {
|
|
2762
2961
|
const rows = [];
|
|
@@ -3040,7 +3239,7 @@ var init_package = __esm({
|
|
|
3040
3239
|
"package.json"() {
|
|
3041
3240
|
package_default = {
|
|
3042
3241
|
name: "@taskless-app/swarm",
|
|
3043
|
-
version: "0.6.
|
|
3242
|
+
version: "0.6.28",
|
|
3044
3243
|
type: "module",
|
|
3045
3244
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
3046
3245
|
keywords: [
|
|
@@ -3091,7 +3290,7 @@ var init_package = __esm({
|
|
|
3091
3290
|
execa: "^10.0.0",
|
|
3092
3291
|
zod: "3.25.76"
|
|
3093
3292
|
},
|
|
3094
|
-
gitHead: "
|
|
3293
|
+
gitHead: "62910fc63850764f4562b7ec3bd65b44d3557050"
|
|
3095
3294
|
};
|
|
3096
3295
|
}
|
|
3097
3296
|
});
|
|
@@ -3223,18 +3422,18 @@ var init_netTimeout = __esm({
|
|
|
3223
3422
|
|
|
3224
3423
|
// src/droneChannel.ts
|
|
3225
3424
|
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
3226
|
-
import { chmodSync as chmodSync2, mkdirSync as mkdirSync7, readFileSync as
|
|
3425
|
+
import { chmodSync as chmodSync2, mkdirSync as mkdirSync7, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
|
|
3227
3426
|
import { dirname as dirname3 } from "path";
|
|
3228
3427
|
import { serve } from "@hono/node-server";
|
|
3229
3428
|
function loadChannelSecret(path) {
|
|
3230
3429
|
try {
|
|
3231
|
-
const s =
|
|
3430
|
+
const s = readFileSync8(path, "utf8").trim();
|
|
3232
3431
|
if (s.length >= 32) return s;
|
|
3233
3432
|
} catch {
|
|
3234
3433
|
}
|
|
3235
3434
|
const secret = randomBytes(32).toString("hex");
|
|
3236
3435
|
mkdirSync7(dirname3(path), { recursive: true });
|
|
3237
|
-
|
|
3436
|
+
writeFileSync7(path, secret, { mode: 384 });
|
|
3238
3437
|
try {
|
|
3239
3438
|
chmodSync2(path, 384);
|
|
3240
3439
|
} catch {
|
|
@@ -3273,95 +3472,6 @@ var init_droneChannel = __esm({
|
|
|
3273
3472
|
}
|
|
3274
3473
|
});
|
|
3275
3474
|
|
|
3276
|
-
// src/agentToken.ts
|
|
3277
|
-
import { readFileSync as readFileSync8, writeFileSync as writeFileSync7, unlinkSync as unlinkSync2 } from "fs";
|
|
3278
|
-
import { join as join10 } from "path";
|
|
3279
|
-
function loadAgentToken() {
|
|
3280
|
-
try {
|
|
3281
|
-
const s = JSON.parse(readFileSync8(STORE_PATH, "utf8"));
|
|
3282
|
-
return s.token ? s : null;
|
|
3283
|
-
} catch {
|
|
3284
|
-
return null;
|
|
3285
|
-
}
|
|
3286
|
-
}
|
|
3287
|
-
function saveAgentToken(s) {
|
|
3288
|
-
writeFileSync7(STORE_PATH, JSON.stringify(s, null, 2), { mode: 384 });
|
|
3289
|
-
}
|
|
3290
|
-
function clearAgentToken() {
|
|
3291
|
-
try {
|
|
3292
|
-
unlinkSync2(STORE_PATH);
|
|
3293
|
-
} catch {
|
|
3294
|
-
}
|
|
3295
|
-
}
|
|
3296
|
-
async function mintAgentToken(args, fetchFn = fetch) {
|
|
3297
|
-
const res = await fetchFn(`${base(args.api)}/api/swarm/agent/token`, {
|
|
3298
|
-
method: "POST",
|
|
3299
|
-
headers: { "Content-Type": "application/json", Authorization: `Bearer ${args.bearer}` },
|
|
3300
|
-
body: JSON.stringify({
|
|
3301
|
-
...args.label ? { label: args.label } : {},
|
|
3302
|
-
...args.hostId ? { hostId: args.hostId } : {},
|
|
3303
|
-
...args.hostName ? { hostName: args.hostName } : {}
|
|
3304
|
-
})
|
|
3305
|
-
});
|
|
3306
|
-
const text = await res.text();
|
|
3307
|
-
if (!res.ok) throw new Error(`agent token: HTTP ${res.status} ${text.slice(0, 200)}`);
|
|
3308
|
-
const j = JSON.parse(text);
|
|
3309
|
-
if (!j.token) throw new Error("agent token: \u0432 \u043E\u0442\u0432\u0435\u0442\u0435 \u043D\u0435\u0442 token");
|
|
3310
|
-
return { token: j.token, ...j.tokenId ? { tokenId: j.tokenId } : {} };
|
|
3311
|
-
}
|
|
3312
|
-
async function probeAgentToken(args, fetchFn = fetch) {
|
|
3313
|
-
try {
|
|
3314
|
-
const res = await fetchFn(`${base(args.api)}/api/auth/me`, {
|
|
3315
|
-
headers: { Authorization: `Bearer ${args.token}` },
|
|
3316
|
-
// Оборвётся по таймауту — попадём в catch ниже, то есть в "unreachable": это ровно
|
|
3317
|
-
// то, чем висящая сеть и является. Соседи по CLI (daemonLink) живут так же.
|
|
3318
|
-
signal: AbortSignal.timeout(args.timeoutMs ?? TOKEN_PROBE_TIMEOUT_MS)
|
|
3319
|
-
});
|
|
3320
|
-
if (res.status === 401 || res.status === 403) return "revoked";
|
|
3321
|
-
return res.ok ? "ok" : "unreachable";
|
|
3322
|
-
} catch {
|
|
3323
|
-
return "unreachable";
|
|
3324
|
-
}
|
|
3325
|
-
}
|
|
3326
|
-
function isRevokedTokenError(e) {
|
|
3327
|
-
if (e && typeof e === "object" && e.code === 401) return true;
|
|
3328
|
-
const text = e instanceof Error ? e.message : typeof e === "string" ? e : "";
|
|
3329
|
-
return /invalid_token|(?:http|status|code)[ =:]*401\b|\(401\)|401 unauthorized/i.test(text);
|
|
3330
|
-
}
|
|
3331
|
-
function revokedTokenLine() {
|
|
3332
|
-
return "[taskless] \u0442\u043E\u043A\u0435\u043D \u0440\u043E\u044F \u043E\u0442\u043E\u0437\u0432\u0430\u043D (401 invalid_token) \u2014 \u0440\u043E\u0439 \u043D\u0435 \u0431\u0435\u0440\u0451\u0442 \u0440\u0430\u0431\u043E\u0442\u0443. \u041F\u043E\u0447\u0438\u043D\u0438 \u043E\u0434\u043D\u043E\u0439 \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439: swarm login";
|
|
3333
|
-
}
|
|
3334
|
-
function revokedTokenComplaint() {
|
|
3335
|
-
return [
|
|
3336
|
-
"",
|
|
3337
|
-
"\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E",
|
|
3338
|
-
"\u2502 \u0422\u041E\u041A\u0415\u041D \u0420\u041E\u042F \u041D\u0415 \u041F\u0420\u0418\u041D\u042F\u0422 (401). \u0414\u043E\u0441\u0442\u0443\u043F \u043A Taskless \u0440\u0430\u0437\u043E\u0440\u0432\u0430\u043D. \u2502",
|
|
3339
|
-
"\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F",
|
|
3340
|
-
"\u041F\u0440\u0438\u0447\u0438\u043D\u0430 \u2014 \u043E\u0434\u043D\u0430 \u0438\u0437 \u0442\u0440\u0451\u0445, \u0432\u0441\u0435 \u0442\u0440\u0438 \u0440\u0435\u0448\u0430\u044E\u0442\u0441\u044F \u0432\u043B\u0430\u0434\u0435\u043B\u044C\u0446\u0435\u043C:",
|
|
3341
|
-
" \u2022 \xAB\u043E\u0442\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u044D\u0442\u043E\u0442 \u043A\u043E\u043C\u043F\u044C\u044E\u0442\u0435\u0440\xBB \u0432 \u043F\u0430\u0440\u043A\u0435 \u043C\u0430\u0448\u0438\u043D \u2014 \u043E\u0442\u043E\u0437\u0432\u0430\u043D \u0442\u043E\u043A\u0435\u043D \u042D\u0422\u041E\u0419 \u043C\u0430\u0448\u0438\u043D\u044B;",
|
|
3342
|
-
" \u2022 \xAB\u0441\u0431\u0440\u043E\u0441\u0438\u0442\u044C \u0434\u043E\u0441\u0442\u0443\u043F \u0440\u043E\u044F\xBB \u0432 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0430\u0445 (\u041F\u0440\u043E\u0444\u0438\u043B\u044C \u2192 \u0420\u043E\u0439) \u2014 \u043E\u0442\u043E\u0437\u0432\u0430\u043D\u044B \u0432\u0441\u0435;",
|
|
3343
|
-
" \u2022 \u0440\u043E\u0439 \u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D \u043C\u0430\u0441\u0442\u0435\u0440-\u0442\u0443\u043C\u0431\u043B\u0435\u0440\u043E\u043C \u2014 \u0442\u043E\u043A\u0435\u043D \u0430\u0433\u0435\u043D\u0442\u0430 \u043F\u0435\u0440\u0435\u0441\u0442\u0430\u0451\u0442 \u0434\u0435\u0439\u0441\u0442\u0432\u043E\u0432\u0430\u0442\u044C.",
|
|
3344
|
-
"",
|
|
3345
|
-
"\u0412\u0445\u043E\u0434 \u043D\u0430 \u0414\u0420\u0423\u0413\u041E\u0419 \u043C\u0430\u0448\u0438\u043D\u0435 \u044D\u0442\u043E\u0442 \u0442\u043E\u043A\u0435\u043D \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u0433\u0430\u0441\u0438\u0442 (TASWM-324): \u0443 \u043A\u0430\u0436\u0434\u043E\u0439",
|
|
3346
|
-
"\u043C\u0430\u0448\u0438\u043D\u044B \u0441\u0432\u043E\u0439 \u0442\u043E\u043A\u0435\u043D, \u043F\u0440\u0438\u0432\u044F\u0437\u0430\u043D\u043D\u044B\u0439 \u043A \u0435\u0451 hostId.",
|
|
3347
|
-
"",
|
|
3348
|
-
"\u0414\u0435\u043C\u043E\u043D \u041D\u0415 \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u0432\u0441\u043B\u0435\u043F\u0443\u044E: \u043F\u043E\u043A\u0430 \u0442\u043E\u043A\u0435\u043D\u0430 \u043D\u0435\u0442, \u043F\u0440\u043E\u0433\u043E\u043D\u044B \u043D\u0435 \u0431\u0435\u0440\u0443\u0442\u0441\u044F \u0438 \u043E\u0442\u0447\u0451\u0442\u044B",
|
|
3349
|
-
"\u043D\u0435 \u043F\u0438\u0448\u0443\u0442\u0441\u044F. \u041F\u043E\u0447\u0438\u043D\u0438 \u0432\u0445\u043E\u0434\u043E\u043C \u0437\u0430\u043D\u043E\u0432\u043E: swarm login",
|
|
3350
|
-
"\u041F\u0440\u043E\u0431\u0430 \u0442\u043E\u043A\u0435\u043D\u0430 \u043D\u0435 \u043F\u0440\u043E\u0445\u043E\u0434\u0438\u0442, \u0430 \u0432\u043E\u0439\u0442\u0438 \u043C\u0435\u0448\u0430\u0435\u0442 \u0441\u0442\u0430\u0440\u044B\u0439 \u0444\u0430\u0439\u043B: swarm login --force",
|
|
3351
|
-
""
|
|
3352
|
-
].join("\n");
|
|
3353
|
-
}
|
|
3354
|
-
var STORE_PATH, base, TOKEN_PROBE_TIMEOUT_MS;
|
|
3355
|
-
var init_agentToken = __esm({
|
|
3356
|
-
"src/agentToken.ts"() {
|
|
3357
|
-
"use strict";
|
|
3358
|
-
init_config();
|
|
3359
|
-
STORE_PATH = join10(SWARM_HOME, "agent.json");
|
|
3360
|
-
base = (api) => api.replace(/\/$/, "");
|
|
3361
|
-
TOKEN_PROBE_TIMEOUT_MS = 8e3;
|
|
3362
|
-
}
|
|
3363
|
-
});
|
|
3364
|
-
|
|
3365
3475
|
// src/authState.ts
|
|
3366
3476
|
function noteTokenRejected(nowMs = Date.now()) {
|
|
3367
3477
|
if (!denial) {
|
|
@@ -3805,85 +3915,6 @@ var init_shotUpload = __esm({
|
|
|
3805
3915
|
}
|
|
3806
3916
|
});
|
|
3807
3917
|
|
|
3808
|
-
// src/linkFailure.ts
|
|
3809
|
-
function describeError(e, maxDepth = 4) {
|
|
3810
|
-
const parts = [];
|
|
3811
|
-
const seen = /* @__PURE__ */ new Set();
|
|
3812
|
-
let cur = e;
|
|
3813
|
-
for (let depth = 0; depth < maxDepth && cur != null && !seen.has(cur); depth++) {
|
|
3814
|
-
seen.add(cur);
|
|
3815
|
-
const msg = (cur instanceof Error ? cur.message : typeof cur === "string" ? cur : String(cur)).trim();
|
|
3816
|
-
const code = cur.code;
|
|
3817
|
-
const codeText = code === void 0 || code === null ? "" : String(code);
|
|
3818
|
-
const label2 = codeText && !msg.includes(codeText) ? `${msg} [code=${codeText}]` : msg;
|
|
3819
|
-
if (label2 && !parts.includes(label2)) parts.push(label2);
|
|
3820
|
-
cur = cur.cause;
|
|
3821
|
-
}
|
|
3822
|
-
return parts.join(" \u2190 ") || String(e);
|
|
3823
|
-
}
|
|
3824
|
-
function explainLinkFailure(e) {
|
|
3825
|
-
const detail = describeError(e);
|
|
3826
|
-
if (detail.includes(UNKNOWN_LINK_REASON)) return { kind: "unknown", detail };
|
|
3827
|
-
if (isRevokedTokenError(e) || isRevokedTokenError(detail)) return { kind: "unauthorized", detail };
|
|
3828
|
-
if (MISSING_TOOL_RE.test(detail)) return { kind: "missing_tool", detail };
|
|
3829
|
-
if (OFFLINE_RE.test(detail)) return { kind: "offline", detail };
|
|
3830
|
-
return { kind: "rejected", detail };
|
|
3831
|
-
}
|
|
3832
|
-
var UNKNOWN_LINK_REASON, OFFLINE_RE, MISSING_TOOL_RE;
|
|
3833
|
-
var init_linkFailure = __esm({
|
|
3834
|
-
"src/linkFailure.ts"() {
|
|
3835
|
-
"use strict";
|
|
3836
|
-
init_agentToken();
|
|
3837
|
-
UNKNOWN_LINK_REASON = "\u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u044F \u043D\u0435\u0442, \u0430 \u043F\u0440\u0438\u0447\u0438\u043D\u0443 \u043A\u043E\u043D\u043D\u0435\u043A\u0442\u043E\u0440 \u043D\u0435 \u043D\u0430\u0437\u0432\u0430\u043B";
|
|
3838
|
-
OFFLINE_RE = /fetch failed|econnreset|econnrefused|econnaborted|enotfound|eai_again|etimedout|ehostunreach|enetunreach|epipe|socket hang up|und_err|network error|connection (?:closed|refused|reset|error)|connect timeout/i;
|
|
3839
|
-
MISSING_TOOL_RE = /tool[^.]{0,40}not found|unknown tool|tool_not_found|method not found|-32601\b/i;
|
|
3840
|
-
}
|
|
3841
|
-
});
|
|
3842
|
-
|
|
3843
|
-
// src/toolFailure.ts
|
|
3844
|
-
var TOOL_FAILURE, TOOL_FAILURE_TEXT, TOOL_FAILURE_RETRIABLE;
|
|
3845
|
-
var init_toolFailure = __esm({
|
|
3846
|
-
"src/toolFailure.ts"() {
|
|
3847
|
-
"use strict";
|
|
3848
|
-
init_linkFailure();
|
|
3849
|
-
TOOL_FAILURE = {
|
|
3850
|
-
/** Тул отработал, но искомого в ответе нет: задачи с таким слагом в воркспейсе не существует. */
|
|
3851
|
-
NOT_FOUND: "not_found",
|
|
3852
|
-
/** Объект есть, но токену роя не виден (приватный проект, спейс, чужой воркспейс). */
|
|
3853
|
-
FORBIDDEN: "forbidden",
|
|
3854
|
-
/** Токен роя не состоит в воркспейсе, которым искали. */
|
|
3855
|
-
WORKSPACE_MISMATCH: "workspace_mismatch",
|
|
3856
|
-
/** До прода не достучались: сеть, DNS, таймаут, оборванный сокет. */
|
|
3857
|
-
UNREACHABLE: "unreachable",
|
|
3858
|
-
/** Прод ответил, но токен роя отвергнут (отозван, протух). Сетью это не лечится. */
|
|
3859
|
-
UNAUTHORIZED: "unauthorized",
|
|
3860
|
-
/** Тул ответил ошибкой, которую мы не узнали, — текст кладём в detail как есть. */
|
|
3861
|
-
TOOL_ERROR: "tool_error",
|
|
3862
|
-
/** Успех, но полезной нагрузки в ответе нет (форма ответа разошлась с ожиданием). */
|
|
3863
|
-
EMPTY: "empty"
|
|
3864
|
-
};
|
|
3865
|
-
TOOL_FAILURE_TEXT = {
|
|
3866
|
-
[TOOL_FAILURE.NOT_FOUND]: "\u0432 \u044D\u0442\u043E\u043C \u0432\u043E\u0440\u043A\u0441\u043F\u0435\u0439\u0441\u0435 \u0442\u0430\u043A\u043E\u0433\u043E \u043D\u0435\u0442 \u2014 \u043F\u0440\u043E\u0432\u0435\u0440\u044C \u0441\u043B\u0430\u0433 \u0438 \u0432\u043E\u0440\u043A\u0441\u043F\u0435\u0439\u0441 \u043F\u0440\u043E\u0433\u043E\u043D\u0430",
|
|
3867
|
-
[TOOL_FAILURE.FORBIDDEN]: "\u0434\u043E\u0441\u0442\u0443\u043F\u0430 \u043D\u0435\u0442: \u043E\u0431\u044A\u0435\u043A\u0442 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442, \u043D\u043E \u0442\u043E\u043A\u0435\u043D\u0443 \u0440\u043E\u044F \u043D\u0435 \u0432\u0438\u0434\u0435\u043D \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A",
|
|
3868
|
-
[TOOL_FAILURE.WORKSPACE_MISMATCH]: "\u0442\u043E\u043A\u0435\u043D \u0440\u043E\u044F \u043D\u0435 \u0441\u043E\u0441\u0442\u043E\u0438\u0442 \u0432 \u044D\u0442\u043E\u043C \u0432\u043E\u0440\u043A\u0441\u043F\u0435\u0439\u0441\u0435",
|
|
3869
|
-
[TOOL_FAILURE.UNREACHABLE]: "Taskless \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u2014 \u0441\u0435\u0442\u044C, DNS \u0438\u043B\u0438 \u0442\u0430\u0439\u043C\u0430\u0443\u0442",
|
|
3870
|
-
[TOOL_FAILURE.UNAUTHORIZED]: "\u0442\u043E\u043A\u0435\u043D \u0440\u043E\u044F \u043E\u0442\u0432\u0435\u0440\u0433\u043D\u0443\u0442 \u043F\u0440\u043E\u0434\u043E\u043C \u2014 \u043D\u0443\u0436\u0435\u043D swarm login \u043D\u0430 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435",
|
|
3871
|
-
[TOOL_FAILURE.TOOL_ERROR]: "\u0442\u0443\u043B \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u043E\u0448\u0438\u0431\u043A\u043E\u0439",
|
|
3872
|
-
[TOOL_FAILURE.EMPTY]: "\u0442\u0443\u043B \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u0443\u0441\u043F\u0435\u0445\u043E\u043C, \u043D\u043E \u0434\u0430\u043D\u043D\u044B\u0445 \u0432 \u043E\u0442\u0432\u0435\u0442\u0435 \u043D\u0435\u0442"
|
|
3873
|
-
};
|
|
3874
|
-
TOOL_FAILURE_RETRIABLE = {
|
|
3875
|
-
[TOOL_FAILURE.NOT_FOUND]: false,
|
|
3876
|
-
[TOOL_FAILURE.FORBIDDEN]: false,
|
|
3877
|
-
[TOOL_FAILURE.WORKSPACE_MISMATCH]: false,
|
|
3878
|
-
[TOOL_FAILURE.UNREACHABLE]: true,
|
|
3879
|
-
// Отозванный токен повтором не чинится — его меняет человек (`swarm login`).
|
|
3880
|
-
[TOOL_FAILURE.UNAUTHORIZED]: false,
|
|
3881
|
-
[TOOL_FAILURE.TOOL_ERROR]: false,
|
|
3882
|
-
[TOOL_FAILURE.EMPTY]: false
|
|
3883
|
-
};
|
|
3884
|
-
}
|
|
3885
|
-
});
|
|
3886
|
-
|
|
3887
3918
|
// src/workspace.ts
|
|
3888
3919
|
import { readFileSync as readFileSync10, writeFileSync as writeFileSync9, unlinkSync as unlinkSync4 } from "fs";
|
|
3889
3920
|
import { join as join17 } from "path";
|
|
@@ -6866,7 +6897,7 @@ init_config();
|
|
|
6866
6897
|
init_resources();
|
|
6867
6898
|
init_names2();
|
|
6868
6899
|
init_storm();
|
|
6869
|
-
import { appendFileSync, existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as
|
|
6900
|
+
import { appendFileSync, existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
6870
6901
|
import { dirname as dirname2 } from "path";
|
|
6871
6902
|
import { randomUUID } from "crypto";
|
|
6872
6903
|
|
|
@@ -6893,20 +6924,23 @@ function parsePendingRepoRules(raw) {
|
|
|
6893
6924
|
return out;
|
|
6894
6925
|
}
|
|
6895
6926
|
|
|
6927
|
+
// src/swarmClient.ts
|
|
6928
|
+
init_toolFailure();
|
|
6929
|
+
|
|
6896
6930
|
// src/logTail.ts
|
|
6897
6931
|
import { closeSync, existsSync as existsSync5, openSync, readSync, readdirSync as readdirSync5, statSync as statSync4 } from "fs";
|
|
6898
|
-
import { join as
|
|
6932
|
+
import { join as join8 } from "path";
|
|
6899
6933
|
|
|
6900
6934
|
// src/runLogDir.ts
|
|
6901
6935
|
import { existsSync as existsSync4, mkdirSync as mkdirSync5, readdirSync as readdirSync4, renameSync as renameSync2, statSync as statSync3 } from "fs";
|
|
6902
|
-
import { join as
|
|
6936
|
+
import { join as join7 } from "path";
|
|
6903
6937
|
function runLogDirName(runLogKey) {
|
|
6904
6938
|
const safe = runLogKey.replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^[-_]+|[-_]+$/g, "");
|
|
6905
6939
|
if (!safe) throw new Error(`\u043A\u043B\u044E\u0447 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u043D\u0435 \u0433\u043E\u0434\u0438\u0442\u0441\u044F \u0432 \u0438\u043C\u044F \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430 run-log'\u043E\u0432: ${JSON.stringify(runLogKey)}`);
|
|
6906
6940
|
return /^\d+$/.test(safe) ? `k-${safe}` : safe;
|
|
6907
6941
|
}
|
|
6908
6942
|
function runLogDirPath(runLogsDir, runLogKey) {
|
|
6909
|
-
return
|
|
6943
|
+
return join7(runLogsDir, runLogDirName(runLogKey));
|
|
6910
6944
|
}
|
|
6911
6945
|
function findRunLogDir(runLogsDir, keys) {
|
|
6912
6946
|
for (const key of keys) {
|
|
@@ -6934,6 +6968,14 @@ function toolErrorText(res) {
|
|
|
6934
6968
|
if (!text) return "\u043F\u0440\u043E\u0434 \u043E\u0442\u0432\u0435\u0440\u0433 \u0432\u044B\u0437\u043E\u0432 \u0431\u0435\u0437 \u043E\u0431\u044A\u044F\u0441\u043D\u0435\u043D\u0438\u044F";
|
|
6935
6969
|
return text.length > 200 ? `${text.slice(0, 200)}\u2026` : text;
|
|
6936
6970
|
}
|
|
6971
|
+
var DEPS_UNRETRIABLE_CODES = /* @__PURE__ */ new Set([
|
|
6972
|
+
TOOL_FAILURE.FORBIDDEN,
|
|
6973
|
+
TOOL_FAILURE.UNAUTHORIZED,
|
|
6974
|
+
TOOL_FAILURE.WORKSPACE_MISMATCH
|
|
6975
|
+
]);
|
|
6976
|
+
function depsFailureRetriable(code) {
|
|
6977
|
+
return !DEPS_UNRETRIABLE_CODES.has(code);
|
|
6978
|
+
}
|
|
6937
6979
|
function runRef(runKey) {
|
|
6938
6980
|
return { __runRef: runKey };
|
|
6939
6981
|
}
|
|
@@ -7030,6 +7072,13 @@ var SwarmClient = class _SwarmClient {
|
|
|
7030
7072
|
// дальше удвоением до потолка TTL: устойчиво отвергающий прод (старее демона, скоуп
|
|
7031
7073
|
// токена) не должен получать вопрос каждые четыре секунды до конца своей жизни.
|
|
7032
7074
|
static DEPS_RETRY_BASE_MS = 5e3;
|
|
7075
|
+
// TASWM-517: сколько раз спрашиваем НЕСАМОЛЕЧАЩИЙСЯ отказ, прежде чем перестать вовсе.
|
|
7076
|
+
// `agent_forbidden` («тул недоступен аккаунту-агенту роя») повтором не чинится: 26.08
|
|
7077
|
+
// два прогона получили его по 53 раза каждый и остались в очереди навсегда. Три захода
|
|
7078
|
+
// при backoff 5с/10с — это ~15 секунд: запас на случай, если классификатор ошибся и
|
|
7079
|
+
// отказ на деле транзиентный, но не час молчаливого простоя. По исчерпании прогон
|
|
7080
|
+
// роняет гейт спавна (DEPS_UNRETRIABLE_ATTEMPTS читается и там).
|
|
7081
|
+
static DEPS_UNRETRIABLE_ATTEMPTS = 3;
|
|
7033
7082
|
// TASWM-327: прод старее демона и не знает аргумент `level` у swarm_notify. Выясняется
|
|
7034
7083
|
// ровно один раз (первой отвергнутой нотификацией) и держится всю жизнь процесса — иначе
|
|
7035
7084
|
// каждый сигнал платил бы за это лишним отвергнутым вызовом. См. notifyAnomaly.
|
|
@@ -7101,13 +7150,13 @@ var SwarmClient = class _SwarmClient {
|
|
|
7101
7150
|
recover() {
|
|
7102
7151
|
if (existsSync6(this.mapPath)) {
|
|
7103
7152
|
try {
|
|
7104
|
-
const obj = JSON.parse(
|
|
7153
|
+
const obj = JSON.parse(readFileSync5(this.mapPath, "utf8"));
|
|
7105
7154
|
for (const [k, v] of Object.entries(obj)) this.prodIdByKey.set(k, v);
|
|
7106
7155
|
} catch {
|
|
7107
7156
|
}
|
|
7108
7157
|
}
|
|
7109
7158
|
if (existsSync6(this.outboxPath)) {
|
|
7110
|
-
const txt =
|
|
7159
|
+
const txt = readFileSync5(this.outboxPath, "utf8");
|
|
7111
7160
|
for (const line of txt.split("\n")) {
|
|
7112
7161
|
const s = line.trim();
|
|
7113
7162
|
if (!s) continue;
|
|
@@ -7130,7 +7179,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
7130
7179
|
if (!existsSync6(this.stormPath)) return;
|
|
7131
7180
|
let txt;
|
|
7132
7181
|
try {
|
|
7133
|
-
txt =
|
|
7182
|
+
txt = readFileSync5(this.stormPath, "utf8");
|
|
7134
7183
|
} catch {
|
|
7135
7184
|
return;
|
|
7136
7185
|
}
|
|
@@ -7635,11 +7684,12 @@ var SwarmClient = class _SwarmClient {
|
|
|
7635
7684
|
res = await this.call("swarm_deps_status", { refs: ask });
|
|
7636
7685
|
} catch (e) {
|
|
7637
7686
|
for (const ref of ask) this.depsAskedAt.delete(ref);
|
|
7638
|
-
this.noteDepsUnanswered(ask, nowMs, String(e));
|
|
7687
|
+
this.noteDepsUnanswered(ask, nowMs, String(e), depsFailureRetriable(classifyThrown(e).code));
|
|
7639
7688
|
return;
|
|
7640
7689
|
}
|
|
7641
7690
|
if (res?.isError) {
|
|
7642
|
-
|
|
7691
|
+
const text = toolErrorText(res);
|
|
7692
|
+
this.noteDepsUnanswered(ask, nowMs, text, depsFailureRetriable(classifyToolFailure(text)));
|
|
7643
7693
|
return;
|
|
7644
7694
|
}
|
|
7645
7695
|
const deps = res.structuredContent?.deps ?? [];
|
|
@@ -7679,6 +7729,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
7679
7729
|
depsRetryDelay(ref) {
|
|
7680
7730
|
const fail = this.depsUnanswered.get(ref);
|
|
7681
7731
|
if (fail) {
|
|
7732
|
+
if (!fail.retriable && fail.attempts >= _SwarmClient.DEPS_UNRETRIABLE_ATTEMPTS) return _SwarmClient.DEPS_TTL_MS;
|
|
7682
7733
|
const backoff = _SwarmClient.DEPS_RETRY_BASE_MS * 2 ** Math.min(fail.attempts - 1, 10);
|
|
7683
7734
|
return Math.min(backoff, _SwarmClient.DEPS_TTL_MS);
|
|
7684
7735
|
}
|
|
@@ -7687,17 +7738,19 @@ var SwarmClient = class _SwarmClient {
|
|
|
7687
7738
|
return _SwarmClient.DEPS_TTL_MS;
|
|
7688
7739
|
}
|
|
7689
7740
|
/** Запомнить, что прод по этим ссылкам не ответил (см. depsUnanswered / TASWM-486). */
|
|
7690
|
-
noteDepsUnanswered(refs, nowMs, error) {
|
|
7741
|
+
noteDepsUnanswered(refs, nowMs, error, retriable = true) {
|
|
7691
7742
|
for (const ref of refs) {
|
|
7692
7743
|
const prev = this.depsUnanswered.get(ref);
|
|
7744
|
+
const sameKind = prev != null && prev.retriable === retriable;
|
|
7693
7745
|
this.depsUnanswered.set(ref, {
|
|
7694
7746
|
ref,
|
|
7695
7747
|
slug: this.depSlug.get(ref) ?? ref,
|
|
7696
7748
|
// `since` — начало ЭПИЗОДА слепоты, а не последняя попытка: именно возраст эпизода
|
|
7697
7749
|
// отвечает на вопрос «сколько прогон уже стоит вслепую».
|
|
7698
7750
|
since: prev?.since ?? nowMs,
|
|
7699
|
-
attempts:
|
|
7700
|
-
error
|
|
7751
|
+
attempts: sameKind ? prev.attempts + 1 : 1,
|
|
7752
|
+
error,
|
|
7753
|
+
retriable
|
|
7701
7754
|
});
|
|
7702
7755
|
}
|
|
7703
7756
|
}
|
|
@@ -9241,13 +9294,13 @@ var SwarmClient = class _SwarmClient {
|
|
|
9241
9294
|
}
|
|
9242
9295
|
persistPending() {
|
|
9243
9296
|
try {
|
|
9244
|
-
|
|
9297
|
+
writeFileSync5(this.outboxPath, this.pending.map((e) => JSON.stringify(e)).join("\n") + (this.pending.length ? "\n" : ""));
|
|
9245
9298
|
} catch {
|
|
9246
9299
|
}
|
|
9247
9300
|
}
|
|
9248
9301
|
persistMap() {
|
|
9249
9302
|
try {
|
|
9250
|
-
|
|
9303
|
+
writeFileSync5(this.mapPath, JSON.stringify(Object.fromEntries(this.prodIdByKey)));
|
|
9251
9304
|
} catch {
|
|
9252
9305
|
}
|
|
9253
9306
|
}
|
|
@@ -9380,7 +9433,7 @@ init_config();
|
|
|
9380
9433
|
import { $ as $6 } from "execa";
|
|
9381
9434
|
|
|
9382
9435
|
// src/daemonLaunch.ts
|
|
9383
|
-
import { openSync as openSync2, closeSync as closeSync2, readFileSync as
|
|
9436
|
+
import { openSync as openSync2, closeSync as closeSync2, readFileSync as readFileSync6 } from "fs";
|
|
9384
9437
|
import { spawn } from "child_process";
|
|
9385
9438
|
var FOREGROUND_FLAGS = /* @__PURE__ */ new Set(["--foreground", "-f", "--attach"]);
|
|
9386
9439
|
function parseStartMode(argv) {
|
|
@@ -9440,14 +9493,14 @@ function launchDetached(opts) {
|
|
|
9440
9493
|
}
|
|
9441
9494
|
function tailFile(path, lines) {
|
|
9442
9495
|
try {
|
|
9443
|
-
return tailText(
|
|
9496
|
+
return tailText(readFileSync6(path, "utf8"), lines);
|
|
9444
9497
|
} catch {
|
|
9445
9498
|
return "";
|
|
9446
9499
|
}
|
|
9447
9500
|
}
|
|
9448
9501
|
|
|
9449
9502
|
// src/logRotate.ts
|
|
9450
|
-
import { copyFileSync as copyFileSync2, renameSync as renameSync3, statSync as statSync5, fstatSync, truncateSync, unlinkSync, existsSync as existsSync7 } from "fs";
|
|
9503
|
+
import { copyFileSync as copyFileSync2, renameSync as renameSync3, statSync as statSync5, fstatSync, truncateSync, unlinkSync as unlinkSync2, existsSync as existsSync7 } from "fs";
|
|
9451
9504
|
var realIo = {
|
|
9452
9505
|
size: (path) => {
|
|
9453
9506
|
try {
|
|
@@ -9459,7 +9512,7 @@ var realIo = {
|
|
|
9459
9512
|
copy: copyFileSync2,
|
|
9460
9513
|
rename: renameSync3,
|
|
9461
9514
|
truncate: (path) => truncateSync(path, 0),
|
|
9462
|
-
unlink:
|
|
9515
|
+
unlink: unlinkSync2,
|
|
9463
9516
|
exists: existsSync7
|
|
9464
9517
|
};
|
|
9465
9518
|
var STRAY_GENERATIONS = 8;
|
|
@@ -9488,17 +9541,17 @@ init_repoRegistry();
|
|
|
9488
9541
|
|
|
9489
9542
|
// src/hostIdentity.ts
|
|
9490
9543
|
import { hostname } from "os";
|
|
9491
|
-
import { existsSync as existsSync8, readFileSync as
|
|
9492
|
-
import { join as
|
|
9544
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7, renameSync as renameSync4, writeFileSync as writeFileSync6 } from "fs";
|
|
9545
|
+
import { join as join9 } from "path";
|
|
9493
9546
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
9494
9547
|
function hostIdentityPath(home) {
|
|
9495
|
-
return
|
|
9548
|
+
return join9(home, "host.json");
|
|
9496
9549
|
}
|
|
9497
9550
|
function loadHostIdentity(deps) {
|
|
9498
9551
|
const exists = deps.exists ?? existsSync8;
|
|
9499
|
-
const readFile2 = deps.readFile ?? ((p) =>
|
|
9552
|
+
const readFile2 = deps.readFile ?? ((p) => readFileSync7(p, "utf8"));
|
|
9500
9553
|
const writeFile3 = deps.writeFile ?? ((p, data) => {
|
|
9501
|
-
|
|
9554
|
+
writeFileSync6(`${p}.tmp`, data);
|
|
9502
9555
|
renameSync4(`${p}.tmp`, p);
|
|
9503
9556
|
});
|
|
9504
9557
|
const host = deps.hostname ?? hostname;
|
|
@@ -9614,7 +9667,7 @@ import { serve as serve2 } from "@hono/node-server";
|
|
|
9614
9667
|
|
|
9615
9668
|
// src/diff.ts
|
|
9616
9669
|
import { statSync as statSync6 } from "fs";
|
|
9617
|
-
import { join as
|
|
9670
|
+
import { join as join10 } from "path";
|
|
9618
9671
|
import { $ as $3 } from "execa";
|
|
9619
9672
|
var MAX_FILES = 200;
|
|
9620
9673
|
var MAX_LINES_PER_FILE = 1500;
|
|
@@ -9782,7 +9835,7 @@ async function collectRunDiff(worktree, opts = {}) {
|
|
|
9782
9835
|
files.push(pf);
|
|
9783
9836
|
}
|
|
9784
9837
|
}
|
|
9785
|
-
files.sort((a, b) => mtime(
|
|
9838
|
+
files.sort((a, b) => mtime(join10(worktree, b.path)) - mtime(join10(worktree, a.path)));
|
|
9786
9839
|
const truncatedFiles = files.length > MAX_FILES;
|
|
9787
9840
|
if (truncatedFiles) files = files.slice(0, MAX_FILES);
|
|
9788
9841
|
const added = files.reduce((s, f) => s + f.added, 0);
|