@taskless-app/swarm 0.6.31 → 0.6.32
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 +82 -17
- package/dist/overmind.js +847 -474
- package/package.json +2 -2
package/dist/overmind.js
CHANGED
|
@@ -1447,7 +1447,7 @@ function buildZoneModel(entries, opts = {}) {
|
|
|
1447
1447
|
if (!m) labelVotes.set(z2, m = /* @__PURE__ */ new Map());
|
|
1448
1448
|
bump(m, manual);
|
|
1449
1449
|
}
|
|
1450
|
-
const
|
|
1450
|
+
const rename2 = /* @__PURE__ */ new Map();
|
|
1451
1451
|
const takenNames = /* @__PURE__ */ new Set([SPINE_ZONE]);
|
|
1452
1452
|
const clustersByEvidence = [...labelVotes.entries()].sort((a, b) => {
|
|
1453
1453
|
const sum = (m) => [...m.values()].reduce((x, y) => x + y, 0);
|
|
@@ -1456,11 +1456,11 @@ function buildZoneModel(entries, opts = {}) {
|
|
|
1456
1456
|
for (const [zone, votes] of clustersByEvidence) {
|
|
1457
1457
|
const top = topKey(votes);
|
|
1458
1458
|
if (!top || top.share < 0.6 || takenNames.has(top.key) || top.key === zone) continue;
|
|
1459
|
-
|
|
1459
|
+
rename2.set(zone, top.key);
|
|
1460
1460
|
takenNames.add(top.key);
|
|
1461
1461
|
}
|
|
1462
|
-
const renamed = (z2) =>
|
|
1463
|
-
if (
|
|
1462
|
+
const renamed = (z2) => rename2.get(z2) ?? z2;
|
|
1463
|
+
if (rename2.size) {
|
|
1464
1464
|
for (const [f, z2] of fileZone) fileZone.set(f, renamed(z2));
|
|
1465
1465
|
for (const [s, z2] of taskZone) taskZone.set(s, renamed(z2));
|
|
1466
1466
|
}
|
|
@@ -1566,8 +1566,8 @@ function predictFiles(model, opts) {
|
|
|
1566
1566
|
}
|
|
1567
1567
|
const zone = normalizeZoneName(opts.zone) ?? (opts.text ? suggestZonesFromText(model, opts.text)[0]?.zone ?? null : null);
|
|
1568
1568
|
if (zone) {
|
|
1569
|
-
const
|
|
1570
|
-
if (
|
|
1569
|
+
const stat2 = model.zones.find((z2) => z2.zone === zone);
|
|
1570
|
+
if (stat2) stat2.files.slice(0, limit).forEach((f, i) => put(f, Math.max(0.12, 0.45 - i * 0.02), `\u0437\u043E\u043D\u0430 ${zone}`));
|
|
1571
1571
|
}
|
|
1572
1572
|
return [...best.values()].sort((a, b) => b.weight - a.weight || a.path.localeCompare(b.path)).slice(0, limit);
|
|
1573
1573
|
}
|
|
@@ -2231,10 +2231,10 @@ function ensureCodexHome(worktree, realHome, env = process.env, swarmHome = SWAR
|
|
|
2231
2231
|
const real = (env.CODEX_HOME ?? "").trim() || join5(realHome, ".codex");
|
|
2232
2232
|
for (const file of ["auth.json", "config.toml"]) {
|
|
2233
2233
|
const target = join5(real, file);
|
|
2234
|
-
const
|
|
2235
|
-
if (!existsSync4(target) || existsSync4(
|
|
2234
|
+
const link2 = join5(home, file);
|
|
2235
|
+
if (!existsSync4(target) || existsSync4(link2)) continue;
|
|
2236
2236
|
try {
|
|
2237
|
-
symlinkSync(target,
|
|
2237
|
+
symlinkSync(target, link2);
|
|
2238
2238
|
} catch {
|
|
2239
2239
|
}
|
|
2240
2240
|
}
|
|
@@ -2433,6 +2433,9 @@ __export(config_exports, {
|
|
|
2433
2433
|
DRONE_HOME: () => DRONE_HOME,
|
|
2434
2434
|
DRONE_HOME_ISOLATION: () => DRONE_HOME_ISOLATION,
|
|
2435
2435
|
DRONE_ISOLATION: () => DRONE_ISOLATION,
|
|
2436
|
+
ENV_CACHE_DIR: () => ENV_CACHE_DIR,
|
|
2437
|
+
ENV_CACHE_ENABLED: () => ENV_CACHE_ENABLED,
|
|
2438
|
+
ENV_CACHE_KEEP: () => ENV_CACHE_KEEP,
|
|
2436
2439
|
EXECUTOR: () => EXECUTOR,
|
|
2437
2440
|
HEARTBEAT_PATH: () => HEARTBEAT_PATH,
|
|
2438
2441
|
HEARTBEAT_STALE_MS: () => HEARTBEAT_STALE_MS,
|
|
@@ -2602,7 +2605,7 @@ function reviewCutoffAt() {
|
|
|
2602
2605
|
}
|
|
2603
2606
|
return at;
|
|
2604
2607
|
}
|
|
2605
|
-
var DB_PATH, EXECUTOR, DRONE_HOME, DRONE_HOME_ISOLATION, DRONE_ISOLATION, WORKTREES_NOINDEX_DIR, WORKTREES_LEGACY_DIR, WORKTREES_DIR, RUN_LOGS_DIR, DAEMON_LOG_PATH, DAEMON_LOG_MAX_BYTES, DAEMON_LOG_KEEP, DAEMON_LOG_CHECK_MS, DIST_BUILD, SWARM_BACKEND, SWARM_OUTBOX, SWARM_MAX_IN_FLIGHT, SWARM_CLAIM_INTERVAL_MS, SWARM_MAX_LOAD_FACTOR, SWARM_MIN_FREE_GB, SWARM_MAX_FD_LOAD_FACTOR, SWARM_MAX_CPU_BUSY, SWARM_CPU_WINDOW_MS, SWARM_CPU_SANITY_BUSY, SWARM_CPU_SANITY_LOAD_FACTOR, SWARM_CPU_SANITY_LAG_MS, SWARM_RELIEF_LOAD_FACTOR, SWARM_RELIEF_MIN_FREE_GB, SWARM_RELIEF_MAX_FD_LOAD_FACTOR, SWARM_RELIEF_CPU_BUSY, SWARM_RELIEF_COST_WINDOW_MS, SWARM_RELIEF_ALERT_PAUSES, SWARM_RELIEF_ALERT_LOST_MS, SWARM_RELIEF_ALERT_MIN_STEPS, SWARM_RELIEF_ALERT_SUSTAIN_MS, SWARM_LOW_DISK_GB, SWARM_REHYDRATE_MS, SWARM_DRONE_BRIDGE_MS, SWARM_PROD_HEARTBEAT_MS, SWARM_VERSION_CHECK_MS, SWARM_SWEEP_INTERVAL_MS, SWARM_WORKTREE_REAP_INTERVAL_MS, DRONE_CHANNEL_ENABLED, DRONE_CHANNEL_PORT, DRONE_CHANNEL_URL, DRONE_CHANNEL_SECRET_PATH, DRONE_CHANNEL_GRACE_MS, HEARTBEAT_PATH, HEARTBEAT_STALE_MS, SWARM_REPO, SWARM_SCAN_DEPTH, SWARM_REPO_PATHS, SWARM_SCAN_ROOTS_RAW, REPO_MAP_PATH, SWARM_REPO_MAP_MS, SWARM_REPO_SCAN_TTL_MS, TASKLESS_API, SWARM_SHOT_TIMEOUT_MS, TASKLESS_MCP_PATH, TASKLESS_REST_PATH, TASKLESS_TOKEN, TASKLESS_WORKSPACE, TASKLESS_WORKSPACE_SLUG, TASKLESS_TASK_URL_BASE, SWARM_VERIFY, SWARM_AUTO_REVIEW, SWARM_AUTO_VERIFY, SWARM_MERGE_QUEUE, REVIEW_CUTOFF_PATH, SWARM_REVIEW_CAP, SWARM_STORM_URL, WEB_PORT, WEB_REDIRECT_URI, SWARM_ANOMALY_RENOTIFY_MS, SWARM_ANOMALY_MAX_REPEATS, SWARM_ANOMALY_RUN_DOWN_WINDOW_MS, SWARM_ANOMALY_FAILED_SPIKE_COUNT, SWARM_ANOMALY_FAILED_SPIKE_WINDOW_MS, SWARM_ANOMALY_QUEUE_STALLED_MS, SWARM_ANOMALY_PR_STUCK_MERGE_MS, SWARM_ANOMALY_PR_NO_REVIEWER_MS, SWARM_ANOMALY_QUESTION_UNANSWERED_MS, SWARM_ANOMALY_OUTBOX_STUCK_MS, SWARM_ANOMALY_RESOURCE_GATE_STALL_MS, SWARM_ANOMALY_DEP_MISSING_MS, SWARM_WATCHDOG_STALL_MS;
|
|
2608
|
+
var DB_PATH, EXECUTOR, DRONE_HOME, DRONE_HOME_ISOLATION, DRONE_ISOLATION, WORKTREES_NOINDEX_DIR, WORKTREES_LEGACY_DIR, WORKTREES_DIR, RUN_LOGS_DIR, DAEMON_LOG_PATH, DAEMON_LOG_MAX_BYTES, DAEMON_LOG_KEEP, DAEMON_LOG_CHECK_MS, DIST_BUILD, SWARM_BACKEND, SWARM_OUTBOX, SWARM_MAX_IN_FLIGHT, SWARM_CLAIM_INTERVAL_MS, ENV_CACHE_DIR, ENV_CACHE_ENABLED, ENV_CACHE_KEEP, SWARM_MAX_LOAD_FACTOR, SWARM_MIN_FREE_GB, SWARM_MAX_FD_LOAD_FACTOR, SWARM_MAX_CPU_BUSY, SWARM_CPU_WINDOW_MS, SWARM_CPU_SANITY_BUSY, SWARM_CPU_SANITY_LOAD_FACTOR, SWARM_CPU_SANITY_LAG_MS, SWARM_RELIEF_LOAD_FACTOR, SWARM_RELIEF_MIN_FREE_GB, SWARM_RELIEF_MAX_FD_LOAD_FACTOR, SWARM_RELIEF_CPU_BUSY, SWARM_RELIEF_COST_WINDOW_MS, SWARM_RELIEF_ALERT_PAUSES, SWARM_RELIEF_ALERT_LOST_MS, SWARM_RELIEF_ALERT_MIN_STEPS, SWARM_RELIEF_ALERT_SUSTAIN_MS, SWARM_LOW_DISK_GB, SWARM_REHYDRATE_MS, SWARM_DRONE_BRIDGE_MS, SWARM_PROD_HEARTBEAT_MS, SWARM_VERSION_CHECK_MS, SWARM_SWEEP_INTERVAL_MS, SWARM_WORKTREE_REAP_INTERVAL_MS, DRONE_CHANNEL_ENABLED, DRONE_CHANNEL_PORT, DRONE_CHANNEL_URL, DRONE_CHANNEL_SECRET_PATH, DRONE_CHANNEL_GRACE_MS, HEARTBEAT_PATH, HEARTBEAT_STALE_MS, SWARM_REPO, SWARM_SCAN_DEPTH, SWARM_REPO_PATHS, SWARM_SCAN_ROOTS_RAW, REPO_MAP_PATH, SWARM_REPO_MAP_MS, SWARM_REPO_SCAN_TTL_MS, TASKLESS_API, SWARM_SHOT_TIMEOUT_MS, TASKLESS_MCP_PATH, TASKLESS_REST_PATH, TASKLESS_TOKEN, TASKLESS_WORKSPACE, TASKLESS_WORKSPACE_SLUG, TASKLESS_TASK_URL_BASE, SWARM_VERIFY, SWARM_AUTO_REVIEW, SWARM_AUTO_VERIFY, SWARM_MERGE_QUEUE, REVIEW_CUTOFF_PATH, SWARM_REVIEW_CAP, SWARM_STORM_URL, WEB_PORT, WEB_REDIRECT_URI, SWARM_ANOMALY_RENOTIFY_MS, SWARM_ANOMALY_MAX_REPEATS, SWARM_ANOMALY_RUN_DOWN_WINDOW_MS, SWARM_ANOMALY_FAILED_SPIKE_COUNT, SWARM_ANOMALY_FAILED_SPIKE_WINDOW_MS, SWARM_ANOMALY_QUEUE_STALLED_MS, SWARM_ANOMALY_PR_STUCK_MERGE_MS, SWARM_ANOMALY_PR_NO_REVIEWER_MS, SWARM_ANOMALY_QUESTION_UNANSWERED_MS, SWARM_ANOMALY_OUTBOX_STUCK_MS, SWARM_ANOMALY_RESOURCE_GATE_STALL_MS, SWARM_ANOMALY_DEP_MISSING_MS, SWARM_WATCHDOG_STALL_MS;
|
|
2606
2609
|
var init_config = __esm({
|
|
2607
2610
|
"src/config.ts"() {
|
|
2608
2611
|
"use strict";
|
|
@@ -2628,6 +2631,9 @@ var init_config = __esm({
|
|
|
2628
2631
|
SWARM_OUTBOX = process.env.SWARM_OUTBOX || join6(SWARM_HOME, "outbox.jsonl");
|
|
2629
2632
|
SWARM_MAX_IN_FLIGHT = Math.trunc(envNum("SWARM_MAX_IN_FLIGHT", 3));
|
|
2630
2633
|
SWARM_CLAIM_INTERVAL_MS = envNum("SWARM_CLAIM_INTERVAL_MS", 6e4);
|
|
2634
|
+
ENV_CACHE_DIR = process.env.SWARM_ENV_CACHE_DIR || join6(SWARM_HOME, "env-cache");
|
|
2635
|
+
ENV_CACHE_ENABLED = process.env.SWARM_ENV_CACHE !== "0";
|
|
2636
|
+
ENV_CACHE_KEEP = Math.trunc(envNum("SWARM_ENV_CACHE_KEEP", 4));
|
|
2631
2637
|
SWARM_MAX_LOAD_FACTOR = envNum("SWARM_MAX_LOAD_FACTOR", 0.9);
|
|
2632
2638
|
SWARM_MIN_FREE_GB = envNum("SWARM_MIN_FREE_GB", 1.5);
|
|
2633
2639
|
SWARM_MAX_FD_LOAD_FACTOR = envNum("SWARM_MAX_FD_LOAD_FACTOR", 0.9);
|
|
@@ -3327,6 +3333,118 @@ var init_logNoise = __esm({
|
|
|
3327
3333
|
}
|
|
3328
3334
|
});
|
|
3329
3335
|
|
|
3336
|
+
// src/netTimeout.ts
|
|
3337
|
+
import { basename as basename2 } from "path";
|
|
3338
|
+
function isTimedOut(outcome) {
|
|
3339
|
+
return typeof outcome === "object" && outcome !== null && outcome.timedOut === true;
|
|
3340
|
+
}
|
|
3341
|
+
function noteTimedOut(scope, call, timeoutMs, outcome, warn = console.warn) {
|
|
3342
|
+
const line = `[${scope}] \u23F1 \u0432\u044B\u0437\u043E\u0432 \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u0437\u0430 ${humanMs(timeoutMs)} \u0438 \u043F\u0440\u0435\u0440\u0432\u0430\u043D: ${call} \u2014 \u0440\u0435\u043A\u043E\u043D\u0441\u0430\u0439\u043B \u0438\u0434\u0451\u0442 \u0434\u0430\u043B\u044C\u0448\u0435`;
|
|
3343
|
+
if (!isTimedOut(outcome)) {
|
|
3344
|
+
timeoutNoise.forget(noiseKey(scope, call));
|
|
3345
|
+
return false;
|
|
3346
|
+
}
|
|
3347
|
+
const say = timeoutNoise.say(noiseKey(scope, call), line, Date.now());
|
|
3348
|
+
if (say) warn(say);
|
|
3349
|
+
return true;
|
|
3350
|
+
}
|
|
3351
|
+
function noiseKey(scope, call) {
|
|
3352
|
+
return `timeout:${scope}:${call}`;
|
|
3353
|
+
}
|
|
3354
|
+
function callFailureWord(f) {
|
|
3355
|
+
switch (f.reason) {
|
|
3356
|
+
case "timeout":
|
|
3357
|
+
return "\u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u0437\u0430 \u043E\u0442\u0432\u0435\u0434\u0451\u043D\u043D\u043E\u0435 \u0432\u0440\u0435\u043C\u044F (\u043F\u0440\u0435\u0440\u0432\u0430\u043D)";
|
|
3358
|
+
case "unreadable":
|
|
3359
|
+
return "\u043E\u0442\u0432\u0435\u0442\u0438\u043B, \u043D\u043E \u0432\u044B\u0432\u043E\u0434 \u043D\u0435 \u0440\u0430\u0437\u043E\u0431\u0440\u0430\u043D";
|
|
3360
|
+
case "crashed":
|
|
3361
|
+
return "\u043D\u0435 \u0437\u0430\u043F\u0443\u0441\u0442\u0438\u043B\u0441\u044F";
|
|
3362
|
+
default:
|
|
3363
|
+
return "\u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0441\u044F \u0431\u0435\u0437 \u0432\u044B\u0432\u043E\u0434\u0430";
|
|
3364
|
+
}
|
|
3365
|
+
}
|
|
3366
|
+
function classifyCallFailure(outcome) {
|
|
3367
|
+
if (isTimedOut(outcome)) return { reason: "timeout", detail: firstLineOf(errText(outcome)) };
|
|
3368
|
+
const code = outcome?.code;
|
|
3369
|
+
if (code === "ENOENT") return { reason: "crashed", detail: firstLineOf(errText(outcome)) };
|
|
3370
|
+
return { reason: "empty", detail: firstLineOf(errText(outcome)) };
|
|
3371
|
+
}
|
|
3372
|
+
function errText(outcome) {
|
|
3373
|
+
const o = outcome;
|
|
3374
|
+
for (const v of [o?.stderr, o?.shortMessage, o?.message]) if (typeof v === "string" && v.trim()) return v;
|
|
3375
|
+
return "";
|
|
3376
|
+
}
|
|
3377
|
+
function firstLineOf(s) {
|
|
3378
|
+
return s.split("\n").map((l) => l.trim()).find((l) => l.length > 0) ?? "";
|
|
3379
|
+
}
|
|
3380
|
+
function noteCallFailure(scope, call, outcome, warn = console.warn) {
|
|
3381
|
+
const f = classifyCallFailure(outcome);
|
|
3382
|
+
if (f.reason === "timeout") return f;
|
|
3383
|
+
const line = `[${scope}] \u2716 \u0432\u044B\u0437\u043E\u0432 ${callFailureWord(f)}: ${call}${f.detail ? ` \u2014 ${f.detail}` : ""} \u2014 \u0440\u0435\u043A\u043E\u043D\u0441\u0430\u0439\u043B \u0438\u0434\u0451\u0442 \u0434\u0430\u043B\u044C\u0448\u0435`;
|
|
3384
|
+
const say = timeoutNoise.say(noiseKey(scope, call), line, Date.now());
|
|
3385
|
+
if (say) warn(say);
|
|
3386
|
+
return f;
|
|
3387
|
+
}
|
|
3388
|
+
function humanMs(ms) {
|
|
3389
|
+
return ms >= 1e3 ? `${Math.round(ms / 1e3)}\u0441` : `${ms}\u043C\u0441`;
|
|
3390
|
+
}
|
|
3391
|
+
function destroyPipes(child) {
|
|
3392
|
+
for (const pipe of [child.stdout, child.stderr, child.stdin]) {
|
|
3393
|
+
try {
|
|
3394
|
+
pipe?.destroy?.();
|
|
3395
|
+
} catch {
|
|
3396
|
+
}
|
|
3397
|
+
}
|
|
3398
|
+
}
|
|
3399
|
+
async function withHardTimeout(child, scope, call, timeoutMs, warn = console.warn) {
|
|
3400
|
+
let timer;
|
|
3401
|
+
const capped = new Promise((_, reject) => {
|
|
3402
|
+
timer = setTimeout(() => {
|
|
3403
|
+
child.kill("SIGKILL");
|
|
3404
|
+
destroyPipes(child);
|
|
3405
|
+
reject(new CallTimedOutError(call, timeoutMs));
|
|
3406
|
+
}, timeoutMs + HARD_CAP_GRACE_MS);
|
|
3407
|
+
timer.unref?.();
|
|
3408
|
+
});
|
|
3409
|
+
try {
|
|
3410
|
+
const res = await Promise.race([child, capped]);
|
|
3411
|
+
noteTimedOut(scope, call, timeoutMs, res, warn);
|
|
3412
|
+
return res;
|
|
3413
|
+
} catch (e) {
|
|
3414
|
+
noteTimedOut(scope, call, timeoutMs, e, warn);
|
|
3415
|
+
throw e;
|
|
3416
|
+
} finally {
|
|
3417
|
+
if (timer) clearTimeout(timer);
|
|
3418
|
+
}
|
|
3419
|
+
}
|
|
3420
|
+
function callLabel(cmd, args, head = 4) {
|
|
3421
|
+
const shown = args.slice(0, head).join(" ");
|
|
3422
|
+
return `${cmd} ${shown}${args.length > head ? " \u2026" : ""}`.trim();
|
|
3423
|
+
}
|
|
3424
|
+
function gitCallLabel(repo, args) {
|
|
3425
|
+
return `git -C ${basename2(repo) || repo} ${args.join(" ")}`;
|
|
3426
|
+
}
|
|
3427
|
+
var GH_CALL_TIMEOUT_MS, GIT_NET_TIMEOUT_MS, GIT_LOCAL_TIMEOUT_MS, GIT_HEAVY_NET_TIMEOUT_MS, HARD_CAP_GRACE_MS, CallTimedOutError, timeoutNoise;
|
|
3428
|
+
var init_netTimeout = __esm({
|
|
3429
|
+
"src/netTimeout.ts"() {
|
|
3430
|
+
"use strict";
|
|
3431
|
+
init_logNoise();
|
|
3432
|
+
GH_CALL_TIMEOUT_MS = 15e3;
|
|
3433
|
+
GIT_NET_TIMEOUT_MS = 15e3;
|
|
3434
|
+
GIT_LOCAL_TIMEOUT_MS = 5e3;
|
|
3435
|
+
GIT_HEAVY_NET_TIMEOUT_MS = 6e4;
|
|
3436
|
+
HARD_CAP_GRACE_MS = 2e3;
|
|
3437
|
+
CallTimedOutError = class extends Error {
|
|
3438
|
+
timedOut = true;
|
|
3439
|
+
constructor(call, timeoutMs) {
|
|
3440
|
+
super(`\u0432\u044B\u0437\u043E\u0432 \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u0437\u0430 ${timeoutMs}\u043C\u0441 \u0438 \u043F\u0440\u0435\u0440\u0432\u0430\u043D: ${call}`);
|
|
3441
|
+
this.name = "CallTimedOutError";
|
|
3442
|
+
}
|
|
3443
|
+
};
|
|
3444
|
+
timeoutNoise = makeRepeatFilter();
|
|
3445
|
+
}
|
|
3446
|
+
});
|
|
3447
|
+
|
|
3330
3448
|
// src/procList.ts
|
|
3331
3449
|
function parsePsRows(out) {
|
|
3332
3450
|
const rows = [];
|
|
@@ -3645,84 +3763,6 @@ var init_procCleanup = __esm({
|
|
|
3645
3763
|
}
|
|
3646
3764
|
});
|
|
3647
3765
|
|
|
3648
|
-
// src/netTimeout.ts
|
|
3649
|
-
import { basename as basename2 } from "path";
|
|
3650
|
-
function isTimedOut(outcome) {
|
|
3651
|
-
return typeof outcome === "object" && outcome !== null && outcome.timedOut === true;
|
|
3652
|
-
}
|
|
3653
|
-
function noteTimedOut(scope, call, timeoutMs, outcome, warn = console.warn) {
|
|
3654
|
-
const line = `[${scope}] \u23F1 \u0432\u044B\u0437\u043E\u0432 \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u0437\u0430 ${humanMs(timeoutMs)} \u0438 \u043F\u0440\u0435\u0440\u0432\u0430\u043D: ${call} \u2014 \u0440\u0435\u043A\u043E\u043D\u0441\u0430\u0439\u043B \u0438\u0434\u0451\u0442 \u0434\u0430\u043B\u044C\u0448\u0435`;
|
|
3655
|
-
if (!isTimedOut(outcome)) {
|
|
3656
|
-
timeoutNoise.forget(noiseKey(scope, call));
|
|
3657
|
-
return false;
|
|
3658
|
-
}
|
|
3659
|
-
const say = timeoutNoise.say(noiseKey(scope, call), line, Date.now());
|
|
3660
|
-
if (say) warn(say);
|
|
3661
|
-
return true;
|
|
3662
|
-
}
|
|
3663
|
-
function noiseKey(scope, call) {
|
|
3664
|
-
return `timeout:${scope}:${call}`;
|
|
3665
|
-
}
|
|
3666
|
-
function humanMs(ms) {
|
|
3667
|
-
return ms >= 1e3 ? `${Math.round(ms / 1e3)}\u0441` : `${ms}\u043C\u0441`;
|
|
3668
|
-
}
|
|
3669
|
-
function destroyPipes(child) {
|
|
3670
|
-
for (const pipe of [child.stdout, child.stderr, child.stdin]) {
|
|
3671
|
-
try {
|
|
3672
|
-
pipe?.destroy?.();
|
|
3673
|
-
} catch {
|
|
3674
|
-
}
|
|
3675
|
-
}
|
|
3676
|
-
}
|
|
3677
|
-
async function withHardTimeout(child, scope, call, timeoutMs, warn = console.warn) {
|
|
3678
|
-
let timer;
|
|
3679
|
-
const capped = new Promise((_, reject) => {
|
|
3680
|
-
timer = setTimeout(() => {
|
|
3681
|
-
child.kill("SIGKILL");
|
|
3682
|
-
destroyPipes(child);
|
|
3683
|
-
reject(new CallTimedOutError(call, timeoutMs));
|
|
3684
|
-
}, timeoutMs + HARD_CAP_GRACE_MS);
|
|
3685
|
-
timer.unref?.();
|
|
3686
|
-
});
|
|
3687
|
-
try {
|
|
3688
|
-
const res = await Promise.race([child, capped]);
|
|
3689
|
-
noteTimedOut(scope, call, timeoutMs, res, warn);
|
|
3690
|
-
return res;
|
|
3691
|
-
} catch (e) {
|
|
3692
|
-
noteTimedOut(scope, call, timeoutMs, e, warn);
|
|
3693
|
-
throw e;
|
|
3694
|
-
} finally {
|
|
3695
|
-
if (timer) clearTimeout(timer);
|
|
3696
|
-
}
|
|
3697
|
-
}
|
|
3698
|
-
function callLabel(cmd, args, head = 4) {
|
|
3699
|
-
const shown = args.slice(0, head).join(" ");
|
|
3700
|
-
return `${cmd} ${shown}${args.length > head ? " \u2026" : ""}`.trim();
|
|
3701
|
-
}
|
|
3702
|
-
function gitCallLabel(repo, args) {
|
|
3703
|
-
return `git -C ${basename2(repo) || repo} ${args.join(" ")}`;
|
|
3704
|
-
}
|
|
3705
|
-
var GH_CALL_TIMEOUT_MS, GIT_NET_TIMEOUT_MS, GIT_LOCAL_TIMEOUT_MS, GIT_HEAVY_NET_TIMEOUT_MS, HARD_CAP_GRACE_MS, CallTimedOutError, timeoutNoise;
|
|
3706
|
-
var init_netTimeout = __esm({
|
|
3707
|
-
"src/netTimeout.ts"() {
|
|
3708
|
-
"use strict";
|
|
3709
|
-
init_logNoise();
|
|
3710
|
-
GH_CALL_TIMEOUT_MS = 15e3;
|
|
3711
|
-
GIT_NET_TIMEOUT_MS = 15e3;
|
|
3712
|
-
GIT_LOCAL_TIMEOUT_MS = 5e3;
|
|
3713
|
-
GIT_HEAVY_NET_TIMEOUT_MS = 6e4;
|
|
3714
|
-
HARD_CAP_GRACE_MS = 2e3;
|
|
3715
|
-
CallTimedOutError = class extends Error {
|
|
3716
|
-
timedOut = true;
|
|
3717
|
-
constructor(call, timeoutMs) {
|
|
3718
|
-
super(`\u0432\u044B\u0437\u043E\u0432 \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u0437\u0430 ${timeoutMs}\u043C\u0441 \u0438 \u043F\u0440\u0435\u0440\u0432\u0430\u043D: ${call}`);
|
|
3719
|
-
this.name = "CallTimedOutError";
|
|
3720
|
-
}
|
|
3721
|
-
};
|
|
3722
|
-
timeoutNoise = makeRepeatFilter();
|
|
3723
|
-
}
|
|
3724
|
-
});
|
|
3725
|
-
|
|
3726
3766
|
// src/git.ts
|
|
3727
3767
|
import { existsSync as existsSync11, readdirSync as readdirSync11, statSync as statSync8 } from "fs";
|
|
3728
3768
|
import { appendFile, chmod, mkdir, readFile, rm, writeFile } from "fs/promises";
|
|
@@ -4068,12 +4108,12 @@ __export(droneChannel_exports, {
|
|
|
4068
4108
|
verifyLocalCliToken: () => verifyLocalCliToken
|
|
4069
4109
|
});
|
|
4070
4110
|
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
4071
|
-
import { chmodSync as chmodSync2, mkdirSync as mkdirSync9, readFileSync as
|
|
4111
|
+
import { chmodSync as chmodSync2, mkdirSync as mkdirSync9, readFileSync as readFileSync17, writeFileSync as writeFileSync11 } from "fs";
|
|
4072
4112
|
import { dirname as dirname4 } from "path";
|
|
4073
4113
|
import { serve } from "@hono/node-server";
|
|
4074
4114
|
function loadChannelSecret(path) {
|
|
4075
4115
|
try {
|
|
4076
|
-
const s =
|
|
4116
|
+
const s = readFileSync17(path, "utf8").trim();
|
|
4077
4117
|
if (s.length >= 32) return s;
|
|
4078
4118
|
} catch {
|
|
4079
4119
|
}
|
|
@@ -4436,8 +4476,8 @@ var init_droneChannel = __esm({
|
|
|
4436
4476
|
});
|
|
4437
4477
|
|
|
4438
4478
|
// src/attachments.ts
|
|
4439
|
-
import { mkdir as
|
|
4440
|
-
import { join as
|
|
4479
|
+
import { mkdir as mkdir3, rm as rm3, writeFile as writeFile3 } from "fs/promises";
|
|
4480
|
+
import { join as join22 } from "path";
|
|
4441
4481
|
function attachmentsFromDoc(doc, origin) {
|
|
4442
4482
|
const out = [];
|
|
4443
4483
|
const walk2 = (node) => {
|
|
@@ -4522,13 +4562,13 @@ async function materializeAttachments(opts) {
|
|
|
4522
4562
|
const maxTotal = opts.maxTotalBytes ?? ATTACH_MAX_TOTAL_BYTES;
|
|
4523
4563
|
const maxFile = opts.maxFileBytes ?? ATTACH_MAX_FILE_BYTES;
|
|
4524
4564
|
const io = opts.io ?? {
|
|
4525
|
-
removeDir: async (dir2) =>
|
|
4526
|
-
mkdir: async (dir2) => void await
|
|
4527
|
-
writeFile: async (path, bytes) =>
|
|
4565
|
+
removeDir: async (dir2) => rm3(dir2, { recursive: true, force: true }),
|
|
4566
|
+
mkdir: async (dir2) => void await mkdir3(dir2, { recursive: true }),
|
|
4567
|
+
writeFile: async (path, bytes) => writeFile3(path, bytes)
|
|
4528
4568
|
};
|
|
4529
4569
|
const result = { files: [], skipped: [] };
|
|
4530
4570
|
if (opts.refs.length === 0) return result;
|
|
4531
|
-
const dir =
|
|
4571
|
+
const dir = join22(opts.worktree, ATTACHMENTS_DIR);
|
|
4532
4572
|
await io.removeDir(dir);
|
|
4533
4573
|
let dirReady = false;
|
|
4534
4574
|
let total = 0;
|
|
@@ -4553,7 +4593,7 @@ async function materializeAttachments(opts) {
|
|
|
4553
4593
|
try {
|
|
4554
4594
|
file = await opts.fetchFile(ref.url);
|
|
4555
4595
|
} catch (e) {
|
|
4556
|
-
result.skipped.push(`${label2} \u2014 \u043D\u0435 \u0441\u043A\u0430\u0447\u0430\u043B\u043E\u0441\u044C (${
|
|
4596
|
+
result.skipped.push(`${label2} \u2014 \u043D\u0435 \u0441\u043A\u0430\u0447\u0430\u043B\u043E\u0441\u044C (${errText2(e)})`);
|
|
4557
4597
|
continue;
|
|
4558
4598
|
}
|
|
4559
4599
|
if (!file) {
|
|
@@ -4574,9 +4614,9 @@ async function materializeAttachments(opts) {
|
|
|
4574
4614
|
await io.mkdir(dir);
|
|
4575
4615
|
dirReady = true;
|
|
4576
4616
|
}
|
|
4577
|
-
await io.writeFile(
|
|
4617
|
+
await io.writeFile(join22(dir, name), file.bytes);
|
|
4578
4618
|
} catch (e) {
|
|
4579
|
-
result.skipped.push(`${label2} \u2014 \u043D\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043B\u043E\u0441\u044C \u043D\u0430 \u0434\u0438\u0441\u043A (${
|
|
4619
|
+
result.skipped.push(`${label2} \u2014 \u043D\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043B\u043E\u0441\u044C \u043D\u0430 \u0434\u0438\u0441\u043A (${errText2(e)})`);
|
|
4580
4620
|
continue;
|
|
4581
4621
|
}
|
|
4582
4622
|
total += file.bytes.byteLength;
|
|
@@ -4630,7 +4670,7 @@ async function prepareTaskAttachments(opts) {
|
|
|
4630
4670
|
}
|
|
4631
4671
|
return renderAttachmentsBlock(result);
|
|
4632
4672
|
} catch (e) {
|
|
4633
|
-
opts.onEvent?.(`\u0432\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u0437\u0430\u0434\u0430\u0447\u0438 \u0432\u044B\u043B\u043E\u0436\u0438\u0442\u044C \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C: ${
|
|
4673
|
+
opts.onEvent?.(`\u0432\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u0437\u0430\u0434\u0430\u0447\u0438 \u0432\u044B\u043B\u043E\u0436\u0438\u0442\u044C \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C: ${errText2(e)} \u2014 \u0434\u0440\u043E\u043D \u0441\u0442\u0430\u0440\u0442\u0443\u0435\u0442 \u0431\u0435\u0437 \u043D\u0438\u0445`);
|
|
4634
4674
|
return void 0;
|
|
4635
4675
|
}
|
|
4636
4676
|
}
|
|
@@ -4653,7 +4693,7 @@ function mb(bytes) {
|
|
|
4653
4693
|
if (bytes >= 1024 * 1024) return `${(bytes / 1024 / 1024).toFixed(1)} \u041C\u0411`;
|
|
4654
4694
|
return `${bytes === 0 ? 0 : Math.max(1, Math.round(bytes / 1024))} \u041A\u0411`;
|
|
4655
4695
|
}
|
|
4656
|
-
function
|
|
4696
|
+
function errText2(e) {
|
|
4657
4697
|
return e instanceof Error ? e.message : String(e);
|
|
4658
4698
|
}
|
|
4659
4699
|
var ATTACHMENTS_DIR, ATTACH_MAX_FILES, ATTACH_MAX_TOTAL_BYTES, ATTACH_MAX_FILE_BYTES, IMAGE_EXT, DOC_EXT, TEXT_EXT, KNOWN_EXT, TRANSLIT, EXT_BY_MIME;
|
|
@@ -4727,7 +4767,7 @@ var init_attachments = __esm({
|
|
|
4727
4767
|
|
|
4728
4768
|
// src/shotUpload.ts
|
|
4729
4769
|
import { readFile as fsReadFile } from "fs/promises";
|
|
4730
|
-
import { basename as basename4, isAbsolute as isAbsolute3, join as
|
|
4770
|
+
import { basename as basename4, isAbsolute as isAbsolute3, join as join23 } from "path";
|
|
4731
4771
|
function shotMimetype(path) {
|
|
4732
4772
|
const dot = path.lastIndexOf(".");
|
|
4733
4773
|
if (dot < 0) return null;
|
|
@@ -4753,7 +4793,7 @@ async function readShots(paths, opts) {
|
|
|
4753
4793
|
}
|
|
4754
4794
|
let bytes;
|
|
4755
4795
|
try {
|
|
4756
|
-
bytes = await read(isAbsolute3(path) ? path :
|
|
4796
|
+
bytes = await read(isAbsolute3(path) ? path : join23(opts.worktree, path));
|
|
4757
4797
|
} catch (e) {
|
|
4758
4798
|
skipped.push({ path, reason: `\u043D\u0435 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D: ${e instanceof Error ? e.message : String(e)}` });
|
|
4759
4799
|
continue;
|
|
@@ -4833,12 +4873,12 @@ var init_shotUpload = __esm({
|
|
|
4833
4873
|
});
|
|
4834
4874
|
|
|
4835
4875
|
// src/oauth.ts
|
|
4836
|
-
import { createHash as
|
|
4837
|
-
import { readFileSync as
|
|
4838
|
-
import { join as
|
|
4876
|
+
import { createHash as createHash3, randomBytes as randomBytes2 } from "crypto";
|
|
4877
|
+
import { readFileSync as readFileSync18, writeFileSync as writeFileSync12, unlinkSync as unlinkSync4 } from "fs";
|
|
4878
|
+
import { join as join24 } from "path";
|
|
4839
4879
|
function makePkce() {
|
|
4840
4880
|
const verifier = randomBytes2(48).toString("base64url");
|
|
4841
|
-
const challenge =
|
|
4881
|
+
const challenge = createHash3("sha256").update(verifier).digest("base64url");
|
|
4842
4882
|
return { verifier, challenge };
|
|
4843
4883
|
}
|
|
4844
4884
|
async function registerClient(args, fetchFn = fetch) {
|
|
@@ -4919,7 +4959,7 @@ function toStore(clientId, t, now) {
|
|
|
4919
4959
|
}
|
|
4920
4960
|
function loadStore() {
|
|
4921
4961
|
try {
|
|
4922
|
-
return JSON.parse(
|
|
4962
|
+
return JSON.parse(readFileSync18(STORE_PATH2, "utf8"));
|
|
4923
4963
|
} catch {
|
|
4924
4964
|
return null;
|
|
4925
4965
|
}
|
|
@@ -4935,7 +4975,7 @@ function clearStore() {
|
|
|
4935
4975
|
}
|
|
4936
4976
|
function loadClientId() {
|
|
4937
4977
|
try {
|
|
4938
|
-
return JSON.parse(
|
|
4978
|
+
return JSON.parse(readFileSync18(CLIENT_PATH, "utf8")).clientId;
|
|
4939
4979
|
} catch {
|
|
4940
4980
|
return null;
|
|
4941
4981
|
}
|
|
@@ -4970,8 +5010,8 @@ var init_oauth = __esm({
|
|
|
4970
5010
|
AUTHORIZE = "/api/oauth/authorize";
|
|
4971
5011
|
TOKEN = "/api/oauth/token";
|
|
4972
5012
|
SCOPE = "openid profile email taskless:full";
|
|
4973
|
-
STORE_PATH2 =
|
|
4974
|
-
CLIENT_PATH =
|
|
5013
|
+
STORE_PATH2 = join24(SWARM_HOME, "oauth.json");
|
|
5014
|
+
CLIENT_PATH = join24(SWARM_HOME, "oauth-client.json");
|
|
4975
5015
|
base2 = (api) => api.replace(/\/$/, "");
|
|
4976
5016
|
}
|
|
4977
5017
|
});
|
|
@@ -5017,11 +5057,11 @@ var init_authState = __esm({
|
|
|
5017
5057
|
});
|
|
5018
5058
|
|
|
5019
5059
|
// src/workspace.ts
|
|
5020
|
-
import { readFileSync as
|
|
5021
|
-
import { join as
|
|
5060
|
+
import { readFileSync as readFileSync19, writeFileSync as writeFileSync13, unlinkSync as unlinkSync5 } from "fs";
|
|
5061
|
+
import { join as join25 } from "path";
|
|
5022
5062
|
function loadWorkspace() {
|
|
5023
5063
|
try {
|
|
5024
|
-
const w = JSON.parse(
|
|
5064
|
+
const w = JSON.parse(readFileSync19(WORKSPACE_PATH, "utf8"));
|
|
5025
5065
|
return w && typeof w.id === "string" && w.id ? w : null;
|
|
5026
5066
|
} catch {
|
|
5027
5067
|
return null;
|
|
@@ -5035,7 +5075,7 @@ var init_workspace = __esm({
|
|
|
5035
5075
|
"src/workspace.ts"() {
|
|
5036
5076
|
"use strict";
|
|
5037
5077
|
init_config();
|
|
5038
|
-
WORKSPACE_PATH =
|
|
5078
|
+
WORKSPACE_PATH = join25(SWARM_HOME, "workspace.json");
|
|
5039
5079
|
}
|
|
5040
5080
|
});
|
|
5041
5081
|
|
|
@@ -5616,7 +5656,7 @@ __export(db_exports, {
|
|
|
5616
5656
|
});
|
|
5617
5657
|
import { createRequire as createRequire2 } from "module";
|
|
5618
5658
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
5619
|
-
import { existsSync as
|
|
5659
|
+
import { existsSync as existsSync14 } from "fs";
|
|
5620
5660
|
import { resolve as resolve3 } from "path";
|
|
5621
5661
|
function rowToQuestion(row) {
|
|
5622
5662
|
return {
|
|
@@ -6239,7 +6279,7 @@ CREATE TABLE IF NOT EXISTS imports (
|
|
|
6239
6279
|
const repoN = normalizeRepo(repo);
|
|
6240
6280
|
const already = this.db.prepare("SELECT run_count FROM imports WHERE source = ?").get(abs);
|
|
6241
6281
|
if (already) return { imported: false, runs: already.run_count };
|
|
6242
|
-
if (!
|
|
6282
|
+
if (!existsSync14(abs)) throw new Error(`import-db: \u0444\u0430\u0439\u043B\u0430 \u043D\u0435\u0442: ${abs}`);
|
|
6243
6283
|
const src = new this.databaseCtor(abs);
|
|
6244
6284
|
try {
|
|
6245
6285
|
const runs = src.prepare("SELECT * FROM runs ORDER BY id").all();
|
|
@@ -6608,10 +6648,10 @@ var init_droneStore = __esm({
|
|
|
6608
6648
|
|
|
6609
6649
|
// src/overmind.ts
|
|
6610
6650
|
import { homedir as homedir6 } from "os";
|
|
6611
|
-
import { existsSync as
|
|
6612
|
-
import { basename as basename6, dirname as dirname5, join as
|
|
6651
|
+
import { existsSync as existsSync19, readFileSync as readFileSync23, readdirSync as readdirSync13, renameSync as renameSync11 } from "fs";
|
|
6652
|
+
import { basename as basename6, dirname as dirname5, join as join32 } from "path";
|
|
6613
6653
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "url";
|
|
6614
|
-
import { spawn as
|
|
6654
|
+
import { spawn as spawn4 } from "child_process";
|
|
6615
6655
|
|
|
6616
6656
|
// src/restart.ts
|
|
6617
6657
|
init_src();
|
|
@@ -6654,11 +6694,11 @@ function savePauseState(home, state, io = {}) {
|
|
|
6654
6694
|
remove(path);
|
|
6655
6695
|
return;
|
|
6656
6696
|
}
|
|
6657
|
-
const
|
|
6697
|
+
const writeFile4 = io.writeFile ?? ((p, data) => {
|
|
6658
6698
|
writeFileSync(`${p}.tmp`, data);
|
|
6659
6699
|
renameSync(`${p}.tmp`, p);
|
|
6660
6700
|
});
|
|
6661
|
-
|
|
6701
|
+
writeFile4(path, JSON.stringify(state, null, 2));
|
|
6662
6702
|
}
|
|
6663
6703
|
function makePauseState(source, reason, now = Date.now()) {
|
|
6664
6704
|
return {
|
|
@@ -9415,6 +9455,26 @@ function rotateDaemonLog(path, opts, io = realIo) {
|
|
|
9415
9455
|
return "rotated";
|
|
9416
9456
|
}
|
|
9417
9457
|
|
|
9458
|
+
// src/daemonLaunch.ts
|
|
9459
|
+
import { openSync as openSync2, closeSync as closeSync2, readFileSync as readFileSync8 } from "fs";
|
|
9460
|
+
import { spawn } from "child_process";
|
|
9461
|
+
var realRespawnIo = {
|
|
9462
|
+
isTty: () => process.stdout.isTTY === true,
|
|
9463
|
+
open: (path) => openSync2(path, "a")
|
|
9464
|
+
};
|
|
9465
|
+
function respawnStdio(logPath, io = realRespawnIo) {
|
|
9466
|
+
if (io.isTty()) return "inherit";
|
|
9467
|
+
try {
|
|
9468
|
+
const fd = io.open(logPath);
|
|
9469
|
+
return ["ignore", fd, fd];
|
|
9470
|
+
} catch {
|
|
9471
|
+
return "inherit";
|
|
9472
|
+
}
|
|
9473
|
+
}
|
|
9474
|
+
|
|
9475
|
+
// src/overmind.ts
|
|
9476
|
+
init_netTimeout();
|
|
9477
|
+
|
|
9418
9478
|
// src/logStamp.ts
|
|
9419
9479
|
import { format } from "util";
|
|
9420
9480
|
function logStamp(at) {
|
|
@@ -9496,13 +9556,13 @@ async function buildRepoRegistry(paths) {
|
|
|
9496
9556
|
}
|
|
9497
9557
|
|
|
9498
9558
|
// src/repoMemory.ts
|
|
9499
|
-
import { readFileSync as
|
|
9559
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync8 } from "fs";
|
|
9500
9560
|
import { join as join10 } from "path";
|
|
9501
9561
|
var FILE = "repos.json";
|
|
9502
9562
|
function loadRememberedRepos(home) {
|
|
9503
9563
|
let raw;
|
|
9504
9564
|
try {
|
|
9505
|
-
raw =
|
|
9565
|
+
raw = readFileSync9(join10(home, FILE), "utf8");
|
|
9506
9566
|
} catch {
|
|
9507
9567
|
return [];
|
|
9508
9568
|
}
|
|
@@ -9532,11 +9592,11 @@ init_src();
|
|
|
9532
9592
|
init_config();
|
|
9533
9593
|
init_linkFailure();
|
|
9534
9594
|
import { homedir as homedir4 } from "os";
|
|
9535
|
-
import { readFileSync as
|
|
9595
|
+
import { readFileSync as readFileSync11, renameSync as renameSync6, writeFileSync as writeFileSync9 } from "fs";
|
|
9536
9596
|
import { isAbsolute, join as join12, resolve as resolve2 } from "path";
|
|
9537
9597
|
|
|
9538
9598
|
// src/repoScan.ts
|
|
9539
|
-
import { readdirSync as readdirSync6, readFileSync as
|
|
9599
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync10, statSync as statSync6 } from "fs";
|
|
9540
9600
|
import { join as join11 } from "path";
|
|
9541
9601
|
var SKIP = /* @__PURE__ */ new Set([
|
|
9542
9602
|
"node_modules",
|
|
@@ -9567,7 +9627,7 @@ function parseOriginFromGitConfig(text) {
|
|
|
9567
9627
|
}
|
|
9568
9628
|
function originOf(dir) {
|
|
9569
9629
|
try {
|
|
9570
|
-
return parseOriginFromGitConfig(
|
|
9630
|
+
return parseOriginFromGitConfig(readFileSync10(join11(dir, ".git", "config"), "utf8"));
|
|
9571
9631
|
} catch {
|
|
9572
9632
|
return null;
|
|
9573
9633
|
}
|
|
@@ -9852,7 +9912,7 @@ function repoMapRows(state) {
|
|
|
9852
9912
|
}
|
|
9853
9913
|
function loadRepoMapCache(path) {
|
|
9854
9914
|
try {
|
|
9855
|
-
const raw = JSON.parse(
|
|
9915
|
+
const raw = JSON.parse(readFileSync11(path, "utf8"));
|
|
9856
9916
|
if (!Array.isArray(raw?.entries)) return { scannedAt: null, entries: [] };
|
|
9857
9917
|
return {
|
|
9858
9918
|
scannedAt: typeof raw.scannedAt === "string" && raw.scannedAt ? raw.scannedAt : null,
|
|
@@ -9875,7 +9935,7 @@ function writeRepoMapCache(path, entries, scannedAt = stamp()) {
|
|
|
9875
9935
|
|
|
9876
9936
|
// src/hostIdentity.ts
|
|
9877
9937
|
import { hostname } from "os";
|
|
9878
|
-
import { existsSync as existsSync9, readFileSync as
|
|
9938
|
+
import { existsSync as existsSync9, readFileSync as readFileSync12, renameSync as renameSync7, writeFileSync as writeFileSync10 } from "fs";
|
|
9879
9939
|
import { join as join13 } from "path";
|
|
9880
9940
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
9881
9941
|
function hostIdentityPath(home) {
|
|
@@ -9883,8 +9943,8 @@ function hostIdentityPath(home) {
|
|
|
9883
9943
|
}
|
|
9884
9944
|
function loadHostIdentity(deps) {
|
|
9885
9945
|
const exists = deps.exists ?? existsSync9;
|
|
9886
|
-
const readFile2 = deps.readFile ?? ((p) =>
|
|
9887
|
-
const
|
|
9946
|
+
const readFile2 = deps.readFile ?? ((p) => readFileSync12(p, "utf8"));
|
|
9947
|
+
const writeFile4 = deps.writeFile ?? ((p, data) => {
|
|
9888
9948
|
writeFileSync10(`${p}.tmp`, data);
|
|
9889
9949
|
renameSync7(`${p}.tmp`, p);
|
|
9890
9950
|
});
|
|
@@ -9904,7 +9964,7 @@ function loadHostIdentity(deps) {
|
|
|
9904
9964
|
}
|
|
9905
9965
|
const hostId = newId();
|
|
9906
9966
|
try {
|
|
9907
|
-
|
|
9967
|
+
writeFile4(path, JSON.stringify({ hostId, hostName, createdAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2));
|
|
9908
9968
|
} catch {
|
|
9909
9969
|
console.warn(`[overmind] \u043D\u0435 \u0441\u043C\u043E\u0433 \u0437\u0430\u043F\u0438\u0441\u0430\u0442\u044C ${path} \u2014 hostId \u0436\u0438\u0432\u0451\u0442 \u0442\u043E\u043B\u044C\u043A\u043E \u0434\u043E \u0440\u0435\u0441\u0442\u0430\u0440\u0442\u0430 \u043F\u0440\u043E\u0446\u0435\u0441\u0441\u0430`);
|
|
9910
9970
|
}
|
|
@@ -10067,8 +10127,8 @@ function buildReliefCostCandidates(cost, t, sustainMs, build) {
|
|
|
10067
10127
|
}
|
|
10068
10128
|
|
|
10069
10129
|
// src/launcher.ts
|
|
10070
|
-
import { spawn } from "child_process";
|
|
10071
|
-
import { closeSync as
|
|
10130
|
+
import { spawn as spawn2 } from "child_process";
|
|
10131
|
+
import { closeSync as closeSync3, existsSync as existsSync10, mkdirSync as mkdirSync8, openSync as openSync3, readdirSync as readdirSync8 } from "fs";
|
|
10072
10132
|
import { createRequire } from "module";
|
|
10073
10133
|
import { homedir as homedir5 } from "os";
|
|
10074
10134
|
import { dirname as dirname3, join as join15 } from "path";
|
|
@@ -10107,9 +10167,9 @@ function buildDroneHome(opts) {
|
|
|
10107
10167
|
}
|
|
10108
10168
|
}
|
|
10109
10169
|
for (const name of wanted) {
|
|
10110
|
-
const
|
|
10170
|
+
const link2 = join14(droneHome, name);
|
|
10111
10171
|
try {
|
|
10112
|
-
lstatSync(
|
|
10172
|
+
lstatSync(link2);
|
|
10113
10173
|
continue;
|
|
10114
10174
|
} catch {
|
|
10115
10175
|
}
|
|
@@ -10121,7 +10181,7 @@ function buildDroneHome(opts) {
|
|
|
10121
10181
|
continue;
|
|
10122
10182
|
}
|
|
10123
10183
|
try {
|
|
10124
|
-
symlinkSync2(target,
|
|
10184
|
+
symlinkSync2(target, link2, isDir ? "junction" : "file");
|
|
10125
10185
|
} catch (e) {
|
|
10126
10186
|
if (e.code !== "EEXIST") throw e;
|
|
10127
10187
|
}
|
|
@@ -10303,14 +10363,14 @@ function openNextRunLog(runLogsDir, runLogKeys) {
|
|
|
10303
10363
|
const path = nextRunLogPath(runLogsDir, runLogKeys);
|
|
10304
10364
|
let out;
|
|
10305
10365
|
try {
|
|
10306
|
-
out =
|
|
10366
|
+
out = openSync3(path, "wx");
|
|
10307
10367
|
} catch (e) {
|
|
10308
10368
|
if (e.code !== "EEXIST") throw e;
|
|
10309
10369
|
continue;
|
|
10310
10370
|
}
|
|
10311
10371
|
let err = null;
|
|
10312
10372
|
try {
|
|
10313
|
-
err =
|
|
10373
|
+
err = openSync3(`${path.slice(0, -".json".length)}.err.log`, "w");
|
|
10314
10374
|
} catch {
|
|
10315
10375
|
err = null;
|
|
10316
10376
|
}
|
|
@@ -10419,7 +10479,7 @@ function makeLauncher(cfg) {
|
|
|
10419
10479
|
errFd = fds.err ?? void 0;
|
|
10420
10480
|
stdio = ["ignore", outFd, errFd ?? "ignore"];
|
|
10421
10481
|
}
|
|
10422
|
-
const child =
|
|
10482
|
+
const child = spawn2(cfg.bin ?? plan.bin, plan.args, {
|
|
10423
10483
|
cwd: worktree,
|
|
10424
10484
|
detached: true,
|
|
10425
10485
|
stdio,
|
|
@@ -10430,8 +10490,8 @@ function makeLauncher(cfg) {
|
|
|
10430
10490
|
// они про его собственное состояние, и перебивать их ройным нечем.
|
|
10431
10491
|
env: { ...droneEnv(worktree, process.env, home ? { droneHome: home, realHome } : void 0, cfg.droneCap?.() ?? null), ...plan.env }
|
|
10432
10492
|
});
|
|
10433
|
-
if (outFd !== void 0)
|
|
10434
|
-
if (errFd !== void 0)
|
|
10493
|
+
if (outFd !== void 0) closeSync3(outFd);
|
|
10494
|
+
if (errFd !== void 0) closeSync3(errFd);
|
|
10435
10495
|
child.unref();
|
|
10436
10496
|
if (child.pid == null) throw new Error("\u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u044C \u0434\u0440\u043E\u043D\u0430 (\u043D\u0435\u0442 pid)");
|
|
10437
10497
|
return child.pid;
|
|
@@ -10442,7 +10502,7 @@ function makeLauncher(cfg) {
|
|
|
10442
10502
|
init_procCleanup();
|
|
10443
10503
|
|
|
10444
10504
|
// src/cost.ts
|
|
10445
|
-
import { readFileSync as
|
|
10505
|
+
import { readFileSync as readFileSync13, readdirSync as readdirSync9, renameSync as renameSync8 } from "fs";
|
|
10446
10506
|
import { join as join16 } from "path";
|
|
10447
10507
|
init_executors();
|
|
10448
10508
|
var UNPROCESSED_LOG_RE = /^\d+\.json$/;
|
|
@@ -10468,7 +10528,7 @@ function accountRunCost(deps, run) {
|
|
|
10468
10528
|
const full = join16(dir, f);
|
|
10469
10529
|
let raw = null;
|
|
10470
10530
|
try {
|
|
10471
|
-
raw =
|
|
10531
|
+
raw = readFileSync13(full, "utf8");
|
|
10472
10532
|
} catch {
|
|
10473
10533
|
continue;
|
|
10474
10534
|
}
|
|
@@ -10517,7 +10577,7 @@ function makeCostSweeper(deps) {
|
|
|
10517
10577
|
}
|
|
10518
10578
|
|
|
10519
10579
|
// src/runOutcome.ts
|
|
10520
|
-
import { readdirSync as readdirSync10, readFileSync as
|
|
10580
|
+
import { readdirSync as readdirSync10, readFileSync as readFileSync14 } from "fs";
|
|
10521
10581
|
import { join as join17 } from "path";
|
|
10522
10582
|
init_executors();
|
|
10523
10583
|
function endedItself(outcome) {
|
|
@@ -10545,7 +10605,7 @@ function readLatestResultRaw(runLogsDir, runLogKeys) {
|
|
|
10545
10605
|
}
|
|
10546
10606
|
if (!latest) return null;
|
|
10547
10607
|
try {
|
|
10548
|
-
return
|
|
10608
|
+
return readFileSync14(join17(dir, latest.name), "utf8");
|
|
10549
10609
|
} catch {
|
|
10550
10610
|
return null;
|
|
10551
10611
|
}
|
|
@@ -10578,13 +10638,13 @@ init_src();
|
|
|
10578
10638
|
init_git();
|
|
10579
10639
|
|
|
10580
10640
|
// src/deps.ts
|
|
10581
|
-
import { existsSync as
|
|
10582
|
-
import { join as
|
|
10641
|
+
import { existsSync as existsSync13, readdirSync as readdirSync12, readFileSync as readFileSync16 } from "fs";
|
|
10642
|
+
import { join as join21 } from "path";
|
|
10583
10643
|
import { execa } from "execa";
|
|
10584
10644
|
|
|
10585
10645
|
// src/prepareLock.ts
|
|
10586
10646
|
import { createHash } from "crypto";
|
|
10587
|
-
import { closeSync as
|
|
10647
|
+
import { closeSync as closeSync4, openSync as openSync4, rmSync as rmSync2, statSync as statSync9, writeSync } from "fs";
|
|
10588
10648
|
import { join as join19 } from "path";
|
|
10589
10649
|
import { tmpdir } from "os";
|
|
10590
10650
|
var WAIT_TIMEOUT_MS = 5 * 6e4;
|
|
@@ -10595,11 +10655,11 @@ function machineLockPath(key, dir = tmpdir()) {
|
|
|
10595
10655
|
}
|
|
10596
10656
|
function defaultTryCreate(path, content) {
|
|
10597
10657
|
try {
|
|
10598
|
-
const fd =
|
|
10658
|
+
const fd = openSync4(path, "wx");
|
|
10599
10659
|
try {
|
|
10600
10660
|
writeSync(fd, content);
|
|
10601
10661
|
} finally {
|
|
10602
|
-
|
|
10662
|
+
closeSync4(fd);
|
|
10603
10663
|
}
|
|
10604
10664
|
return true;
|
|
10605
10665
|
} catch {
|
|
@@ -10645,6 +10705,153 @@ async function withMachineLock(key, fn, io = {}) {
|
|
|
10645
10705
|
}
|
|
10646
10706
|
}
|
|
10647
10707
|
|
|
10708
|
+
// src/deps.ts
|
|
10709
|
+
init_config();
|
|
10710
|
+
|
|
10711
|
+
// src/envCache.ts
|
|
10712
|
+
init_config();
|
|
10713
|
+
import { createHash as createHash2 } from "crypto";
|
|
10714
|
+
import { cp, link, lstat, mkdir as mkdir2, readdir, readlink, rename, rm as rm2, stat, symlink, utimes, writeFile as writeFile2 } from "fs/promises";
|
|
10715
|
+
import { existsSync as existsSync12, readFileSync as readFileSync15 } from "fs";
|
|
10716
|
+
import { join as join20 } from "path";
|
|
10717
|
+
function fingerprint(parts) {
|
|
10718
|
+
const h = createHash2("sha256");
|
|
10719
|
+
for (const p of parts) {
|
|
10720
|
+
h.update(p);
|
|
10721
|
+
h.update("\0");
|
|
10722
|
+
}
|
|
10723
|
+
return h.digest("hex").slice(0, 32);
|
|
10724
|
+
}
|
|
10725
|
+
async function cloneTree(src, dst) {
|
|
10726
|
+
await mkdir2(dst, { recursive: true });
|
|
10727
|
+
const entries = await readdir(src, { withFileTypes: true });
|
|
10728
|
+
await Promise.all(
|
|
10729
|
+
entries.map(async (e) => {
|
|
10730
|
+
const from = join20(src, e.name);
|
|
10731
|
+
const to = join20(dst, e.name);
|
|
10732
|
+
if (e.isDirectory()) return cloneTree(from, to);
|
|
10733
|
+
if (e.isSymbolicLink()) {
|
|
10734
|
+
return symlink(await readlink(from), to).catch(() => cp(from, to, { recursive: true, verbatimSymlinks: true }));
|
|
10735
|
+
}
|
|
10736
|
+
return link(from, to).catch(() => cp(from, to));
|
|
10737
|
+
})
|
|
10738
|
+
);
|
|
10739
|
+
}
|
|
10740
|
+
function cacheRoot(io) {
|
|
10741
|
+
return io.root ?? ENV_CACHE_DIR;
|
|
10742
|
+
}
|
|
10743
|
+
function snapshotDir(io, key) {
|
|
10744
|
+
return join20(cacheRoot(io), key);
|
|
10745
|
+
}
|
|
10746
|
+
function readManifest(dir) {
|
|
10747
|
+
try {
|
|
10748
|
+
const parsed = JSON.parse(readFileSync15(join20(dir, "manifest.json"), "utf8"));
|
|
10749
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
10750
|
+
const m = parsed;
|
|
10751
|
+
if (typeof m.key !== "string" || !Array.isArray(m.dirs)) return null;
|
|
10752
|
+
const dirs = m.dirs.filter((d) => typeof d === "string" && d.length > 0 && !d.startsWith("/") && !d.includes(".."));
|
|
10753
|
+
if (!dirs.length) return null;
|
|
10754
|
+
return { key: m.key, dirs, createdAt: typeof m.createdAt === "string" ? m.createdAt : "" };
|
|
10755
|
+
} catch {
|
|
10756
|
+
return null;
|
|
10757
|
+
}
|
|
10758
|
+
}
|
|
10759
|
+
function hasEnvSnapshot(key, io = {}) {
|
|
10760
|
+
const exists = io.exists ?? existsSync12;
|
|
10761
|
+
return exists(join20(snapshotDir(io, key), "manifest.json"));
|
|
10762
|
+
}
|
|
10763
|
+
async function restoreEnvSnapshot(worktree, key, io = {}) {
|
|
10764
|
+
const exists = io.exists ?? existsSync12;
|
|
10765
|
+
const clone = io.clone ?? cloneTree;
|
|
10766
|
+
const dir = snapshotDir(io, key);
|
|
10767
|
+
const manifest = readManifest(dir);
|
|
10768
|
+
if (!manifest) return null;
|
|
10769
|
+
const made = [];
|
|
10770
|
+
try {
|
|
10771
|
+
for (let i = 0; i < manifest.dirs.length; i++) {
|
|
10772
|
+
const rel = manifest.dirs[i] ?? "";
|
|
10773
|
+
const target = join20(worktree, rel);
|
|
10774
|
+
if (exists(target)) continue;
|
|
10775
|
+
await clone(join20(dir, "trees", String(i)), target);
|
|
10776
|
+
made.push(rel);
|
|
10777
|
+
}
|
|
10778
|
+
} catch {
|
|
10779
|
+
for (const rel of made) await rm2(join20(worktree, rel), { recursive: true, force: true }).catch(() => {
|
|
10780
|
+
});
|
|
10781
|
+
return null;
|
|
10782
|
+
}
|
|
10783
|
+
await touch(dir);
|
|
10784
|
+
return made;
|
|
10785
|
+
}
|
|
10786
|
+
async function touch(dir) {
|
|
10787
|
+
const now = /* @__PURE__ */ new Date();
|
|
10788
|
+
await utimes(dir, now, now).catch(() => {
|
|
10789
|
+
});
|
|
10790
|
+
}
|
|
10791
|
+
async function saveEnvSnapshot(worktree, key, dirs, io = {}) {
|
|
10792
|
+
const exists = io.exists ?? existsSync12;
|
|
10793
|
+
const clone = io.clone ?? cloneTree;
|
|
10794
|
+
if (hasEnvSnapshot(key, io)) return true;
|
|
10795
|
+
const present = dirs.filter((rel) => exists(join20(worktree, rel)));
|
|
10796
|
+
if (!present.length) return false;
|
|
10797
|
+
const root = cacheRoot(io);
|
|
10798
|
+
const tmp = join20(root, `.tmp-${key}-${process.pid}`);
|
|
10799
|
+
try {
|
|
10800
|
+
await rm2(tmp, { recursive: true, force: true });
|
|
10801
|
+
await mkdir2(join20(tmp, "trees"), { recursive: true });
|
|
10802
|
+
for (let i = 0; i < present.length; i++) await clone(join20(worktree, present[i] ?? ""), join20(tmp, "trees", String(i)));
|
|
10803
|
+
const manifest = { key, dirs: present, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
10804
|
+
await writeFile2(join20(tmp, "manifest.json"), JSON.stringify(manifest, null, 2));
|
|
10805
|
+
await rename(tmp, snapshotDir(io, key));
|
|
10806
|
+
return true;
|
|
10807
|
+
} catch {
|
|
10808
|
+
await rm2(tmp, { recursive: true, force: true }).catch(() => {
|
|
10809
|
+
});
|
|
10810
|
+
return hasEnvSnapshot(key, io);
|
|
10811
|
+
}
|
|
10812
|
+
}
|
|
10813
|
+
async function pruneEnvSnapshots(io = {}) {
|
|
10814
|
+
const root = cacheRoot(io);
|
|
10815
|
+
const keep = io.keep ?? ENV_CACHE_KEEP;
|
|
10816
|
+
let names;
|
|
10817
|
+
try {
|
|
10818
|
+
names = (await readdir(root, { withFileTypes: true })).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
|
|
10819
|
+
} catch {
|
|
10820
|
+
return 0;
|
|
10821
|
+
}
|
|
10822
|
+
const withTime = [];
|
|
10823
|
+
for (const name of names) {
|
|
10824
|
+
const s = await stat(join20(root, name)).catch(() => null);
|
|
10825
|
+
if (s) withTime.push({ name, mtime: s.mtimeMs });
|
|
10826
|
+
}
|
|
10827
|
+
withTime.sort((a, b) => b.mtime - a.mtime);
|
|
10828
|
+
let removed = 0;
|
|
10829
|
+
for (const { name } of withTime.slice(Math.max(0, keep))) {
|
|
10830
|
+
await rm2(join20(root, name), { recursive: true, force: true }).catch(() => {
|
|
10831
|
+
});
|
|
10832
|
+
removed++;
|
|
10833
|
+
}
|
|
10834
|
+
return removed;
|
|
10835
|
+
}
|
|
10836
|
+
async function sweepEnvCacheTemp(io = {}) {
|
|
10837
|
+
const root = cacheRoot(io);
|
|
10838
|
+
let names;
|
|
10839
|
+
try {
|
|
10840
|
+
names = (await readdir(root, { withFileTypes: true })).filter((e) => e.isDirectory() && e.name.startsWith(".tmp-")).map((e) => e.name);
|
|
10841
|
+
} catch {
|
|
10842
|
+
return 0;
|
|
10843
|
+
}
|
|
10844
|
+
let removed = 0;
|
|
10845
|
+
for (const name of names) {
|
|
10846
|
+
const s = await lstat(join20(root, name)).catch(() => null);
|
|
10847
|
+
if (!s || Date.now() - s.mtimeMs < 60 * 6e4) continue;
|
|
10848
|
+
await rm2(join20(root, name), { recursive: true, force: true }).catch(() => {
|
|
10849
|
+
});
|
|
10850
|
+
removed++;
|
|
10851
|
+
}
|
|
10852
|
+
return removed;
|
|
10853
|
+
}
|
|
10854
|
+
|
|
10648
10855
|
// src/deps.ts
|
|
10649
10856
|
var PREPARE_TIMEOUT_MS = 15 * 6e4;
|
|
10650
10857
|
var MAX_PLAN_COMMANDS = 12;
|
|
@@ -10665,7 +10872,7 @@ async function runCommand(cmd, worktree) {
|
|
|
10665
10872
|
}
|
|
10666
10873
|
function safeReadText(p) {
|
|
10667
10874
|
try {
|
|
10668
|
-
return
|
|
10875
|
+
return readFileSync16(p, "utf8");
|
|
10669
10876
|
} catch {
|
|
10670
10877
|
return null;
|
|
10671
10878
|
}
|
|
@@ -10705,21 +10912,21 @@ function overridePlan(override) {
|
|
|
10705
10912
|
};
|
|
10706
10913
|
}
|
|
10707
10914
|
function detectPackageManager(worktree, io = {}) {
|
|
10708
|
-
const exists = io.exists ??
|
|
10915
|
+
const exists = io.exists ?? existsSync13;
|
|
10709
10916
|
const readText = io.readText ?? safeReadText;
|
|
10710
|
-
const hasPkgJson = exists(
|
|
10917
|
+
const hasPkgJson = exists(join21(worktree, "package.json"));
|
|
10711
10918
|
if (!hasPkgJson) {
|
|
10712
|
-
if (exists(
|
|
10919
|
+
if (exists(join21(worktree, "deno.json")) || exists(join21(worktree, "deno.jsonc")) || exists(join21(worktree, "deno.lock"))) return "deno";
|
|
10713
10920
|
return null;
|
|
10714
10921
|
}
|
|
10715
|
-
const pkg = readJson(readText,
|
|
10922
|
+
const pkg = readJson(readText, join21(worktree, "package.json"));
|
|
10716
10923
|
const declared = typeof pkg?.packageManager === "string" ? pkg.packageManager.split("@")[0] : void 0;
|
|
10717
10924
|
if (declared === "pnpm" || declared === "bun" || declared === "npm" || declared === "yarn") return declared;
|
|
10718
|
-
if (exists(
|
|
10719
|
-
if (exists(
|
|
10720
|
-
if (exists(
|
|
10721
|
-
if (exists(
|
|
10722
|
-
if (exists(
|
|
10925
|
+
if (exists(join21(worktree, "bun.lock")) || exists(join21(worktree, "bun.lockb"))) return "bun";
|
|
10926
|
+
if (exists(join21(worktree, "pnpm-lock.yaml"))) return "pnpm";
|
|
10927
|
+
if (exists(join21(worktree, "yarn.lock"))) return "yarn";
|
|
10928
|
+
if (exists(join21(worktree, "package-lock.json"))) return "npm";
|
|
10929
|
+
if (exists(join21(worktree, "deno.lock"))) return "deno";
|
|
10723
10930
|
return "npm";
|
|
10724
10931
|
}
|
|
10725
10932
|
function jsInstallCommand(pm) {
|
|
@@ -10775,7 +10982,7 @@ var ECOSYSTEM_MARKERS = [
|
|
|
10775
10982
|
// конфигурация — дело сборки, не подготовки
|
|
10776
10983
|
];
|
|
10777
10984
|
function detectPlan(worktree, io) {
|
|
10778
|
-
const exists = io.exists ??
|
|
10985
|
+
const exists = io.exists ?? existsSync13;
|
|
10779
10986
|
const listFiles = io.listFiles ?? safeListFiles;
|
|
10780
10987
|
const pm = detectPackageManager(worktree, io);
|
|
10781
10988
|
if (pm) {
|
|
@@ -10783,7 +10990,7 @@ function detectPlan(worktree, io) {
|
|
|
10783
10990
|
// node_modules на месте — установку пропускаем (быстрый путь переиспользованного worktree).
|
|
10784
10991
|
// У deno каталога может не быть вовсе (кэш в DENO_DIR) — тогда `deno install` отработает
|
|
10785
10992
|
// заново, но он дешёвый: тянет из того же общего кэша.
|
|
10786
|
-
{ label: `${pm} install`, run: jsInstallCommand(pm), skipIfExists:
|
|
10993
|
+
{ label: `${pm} install`, run: jsInstallCommand(pm), skipIfExists: join21(worktree, "node_modules"), source: "detect" }
|
|
10787
10994
|
];
|
|
10788
10995
|
for (const target of workspaceBuildTargets(worktree, io)) {
|
|
10789
10996
|
commands.push({ label: `\u0441\u0431\u043E\u0440\u043A\u0430 ${target.name}`, run: jsRunScript(pm, "build"), cwd: target.dir, source: "detect" });
|
|
@@ -10792,12 +10999,12 @@ function detectPlan(worktree, io) {
|
|
|
10792
10999
|
return { source: "detect", ecosystem: `node/${pm}`, commands };
|
|
10793
11000
|
}
|
|
10794
11001
|
for (const marker of ECOSYSTEM_MARKERS) {
|
|
10795
|
-
const hit = marker.files.some((f) => exists(
|
|
11002
|
+
const hit = marker.files.some((f) => exists(join21(worktree, f))) || marker.ext !== void 0 && listFiles(worktree).some((n) => n.endsWith(marker.ext));
|
|
10796
11003
|
if (!hit) continue;
|
|
10797
11004
|
let ecosystem = marker.ecosystem;
|
|
10798
11005
|
let command = marker.command;
|
|
10799
11006
|
for (const refine of marker.refine ?? []) {
|
|
10800
|
-
if (!exists(
|
|
11007
|
+
if (!exists(join21(worktree, refine.file))) continue;
|
|
10801
11008
|
ecosystem = refine.ecosystem;
|
|
10802
11009
|
command = refine.command;
|
|
10803
11010
|
break;
|
|
@@ -10854,12 +11061,12 @@ var CI_INSTALL_PATTERNS = [
|
|
|
10854
11061
|
function ciWorkflowPlan(worktree, io) {
|
|
10855
11062
|
const readText = io.readText ?? safeReadText;
|
|
10856
11063
|
const listFiles = io.listFiles ?? safeListFiles;
|
|
10857
|
-
const dir =
|
|
11064
|
+
const dir = join21(worktree, ".github", "workflows");
|
|
10858
11065
|
const files = listFiles(dir).filter((n) => n.endsWith(".yml") || n.endsWith(".yaml"));
|
|
10859
11066
|
const seen = /* @__PURE__ */ new Set();
|
|
10860
11067
|
const commands = [];
|
|
10861
11068
|
for (const name of files.sort()) {
|
|
10862
|
-
const yaml = readText(
|
|
11069
|
+
const yaml = readText(join21(dir, name));
|
|
10863
11070
|
if (yaml === null) continue;
|
|
10864
11071
|
for (const raw of workflowRunLines(yaml)) {
|
|
10865
11072
|
const line = raw.replace(/\s+/g, " ").trim();
|
|
@@ -10874,7 +11081,7 @@ function ciWorkflowPlan(worktree, io) {
|
|
|
10874
11081
|
return commands.length ? { source: "ci-workflow", ecosystem: "ci-\u043A\u043E\u043D\u0444\u0438\u0433", commands } : null;
|
|
10875
11082
|
}
|
|
10876
11083
|
function workspaceGlobs(worktree, readText) {
|
|
10877
|
-
const yaml = readText(
|
|
11084
|
+
const yaml = readText(join21(worktree, "pnpm-workspace.yaml"));
|
|
10878
11085
|
if (yaml !== null) {
|
|
10879
11086
|
const globs = [];
|
|
10880
11087
|
let inPackages = false;
|
|
@@ -10894,7 +11101,7 @@ function workspaceGlobs(worktree, readText) {
|
|
|
10894
11101
|
}
|
|
10895
11102
|
if (globs.length) return globs;
|
|
10896
11103
|
}
|
|
10897
|
-
const pkg = readJson(readText,
|
|
11104
|
+
const pkg = readJson(readText, join21(worktree, "package.json"));
|
|
10898
11105
|
const ws = pkg?.workspaces;
|
|
10899
11106
|
const list = Array.isArray(ws) ? ws : Array.isArray(ws?.packages) ? ws.packages : [];
|
|
10900
11107
|
return list.filter((g) => typeof g === "string");
|
|
@@ -10905,14 +11112,14 @@ function workspaceDirs(worktree, globs, io) {
|
|
|
10905
11112
|
if (glob.startsWith("!")) continue;
|
|
10906
11113
|
const star = glob.indexOf("*");
|
|
10907
11114
|
if (star === -1) {
|
|
10908
|
-
const dir =
|
|
10909
|
-
if (io.exists(
|
|
11115
|
+
const dir = join21(worktree, glob);
|
|
11116
|
+
if (io.exists(join21(dir, "package.json"))) dirs.add(dir);
|
|
10910
11117
|
continue;
|
|
10911
11118
|
}
|
|
10912
|
-
const base3 =
|
|
11119
|
+
const base3 = join21(worktree, glob.slice(0, star).replace(/\/$/, ""));
|
|
10913
11120
|
for (const name of io.listDirs(base3)) {
|
|
10914
|
-
const dir =
|
|
10915
|
-
if (io.exists(
|
|
11121
|
+
const dir = join21(base3, name);
|
|
11122
|
+
if (io.exists(join21(dir, "package.json"))) dirs.add(dir);
|
|
10916
11123
|
}
|
|
10917
11124
|
}
|
|
10918
11125
|
return [...dirs];
|
|
@@ -10933,18 +11140,18 @@ function entryPaths(pkg) {
|
|
|
10933
11140
|
return out;
|
|
10934
11141
|
}
|
|
10935
11142
|
function workspaceBuildTargets(worktree, io = {}) {
|
|
10936
|
-
const exists = io.exists ??
|
|
11143
|
+
const exists = io.exists ?? existsSync13;
|
|
10937
11144
|
const readText = io.readText ?? safeReadText;
|
|
10938
11145
|
const listDirs = io.listDirs ?? safeListDirs;
|
|
10939
11146
|
const globs = workspaceGlobs(worktree, readText);
|
|
10940
11147
|
if (!globs.length) return [];
|
|
10941
11148
|
const pkgs = [];
|
|
10942
11149
|
for (const dir of workspaceDirs(worktree, globs, { exists, listDirs })) {
|
|
10943
|
-
const pkg = readJson(readText,
|
|
11150
|
+
const pkg = readJson(readText, join21(dir, "package.json"));
|
|
10944
11151
|
if (!pkg || typeof pkg.name !== "string") continue;
|
|
10945
11152
|
const scripts = pkg.scripts ?? {};
|
|
10946
11153
|
const entries = entryPaths(pkg);
|
|
10947
|
-
const needsBuild = typeof scripts.build === "string" && entries.length > 0 && entries.some((e) => !exists(
|
|
11154
|
+
const needsBuild = typeof scripts.build === "string" && entries.length > 0 && entries.some((e) => !exists(join21(dir, e)));
|
|
10948
11155
|
const deps = Object.keys({ ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} });
|
|
10949
11156
|
pkgs.push({ name: pkg.name, dir, deps, needsBuild });
|
|
10950
11157
|
}
|
|
@@ -10972,7 +11179,7 @@ function devDatabaseUrl(worktree, io = {}) {
|
|
|
10972
11179
|
const fromEnv = (io.env ?? process.env).DATABASE_URL;
|
|
10973
11180
|
if (fromEnv && fromEnv.trim()) return fromEnv.trim();
|
|
10974
11181
|
for (const file of [".env", ".env.example"]) {
|
|
10975
|
-
const raw = readText(
|
|
11182
|
+
const raw = readText(join21(worktree, file));
|
|
10976
11183
|
if (raw === null) continue;
|
|
10977
11184
|
const m = /^\s*(?:export\s+)?DATABASE_URL\s*=\s*(.*)$/m.exec(raw);
|
|
10978
11185
|
const value = (m?.[1] ?? "").trim().replace(/^['"]|['"]$/g, "");
|
|
@@ -10988,11 +11195,11 @@ function isLocalDatabase(url) {
|
|
|
10988
11195
|
}
|
|
10989
11196
|
}
|
|
10990
11197
|
function prismaPackages(worktree, io) {
|
|
10991
|
-
const exists = io.exists ??
|
|
11198
|
+
const exists = io.exists ?? existsSync13;
|
|
10992
11199
|
const readText = io.readText ?? safeReadText;
|
|
10993
11200
|
const listDirs = io.listDirs ?? safeListDirs;
|
|
10994
11201
|
const dirs = [worktree, ...workspaceDirs(worktree, workspaceGlobs(worktree, readText), { exists, listDirs })];
|
|
10995
|
-
return dirs.filter((dir) => exists(
|
|
11202
|
+
return dirs.filter((dir) => exists(join21(dir, "prisma", "schema.prisma")) && listDirs(join21(dir, "prisma", "migrations")).length > 0);
|
|
10996
11203
|
}
|
|
10997
11204
|
function jsExecBin(pm, command) {
|
|
10998
11205
|
switch (pm) {
|
|
@@ -11019,26 +11226,59 @@ function migrateCommands(worktree, pm, io) {
|
|
|
11019
11226
|
source: "detect"
|
|
11020
11227
|
}));
|
|
11021
11228
|
}
|
|
11229
|
+
var ENV_DEFINING_FILES = ["pnpm-lock.yaml", "package-lock.json", "yarn.lock", "bun.lock", "bun.lockb", "deno.lock", "pnpm-workspace.yaml"];
|
|
11230
|
+
function envCachePlan(worktree, io = {}) {
|
|
11231
|
+
if (!ENV_CACHE_ENABLED) return null;
|
|
11232
|
+
const exists = io.exists ?? existsSync13;
|
|
11233
|
+
const readText = io.readText ?? safeReadText;
|
|
11234
|
+
const listDirs = io.listDirs ?? safeListDirs;
|
|
11235
|
+
const pm = detectPackageManager(worktree, io);
|
|
11236
|
+
if (!pm) return null;
|
|
11237
|
+
const parts = [`pm:${pm}`];
|
|
11238
|
+
for (const name of ENV_DEFINING_FILES) {
|
|
11239
|
+
const raw = readText(join21(worktree, name));
|
|
11240
|
+
if (raw !== null) parts.push(`${name}
|
|
11241
|
+
${raw}`);
|
|
11242
|
+
}
|
|
11243
|
+
const pkgDirs = workspaceDirs(worktree, workspaceGlobs(worktree, readText), { exists, listDirs });
|
|
11244
|
+
const rels = pkgDirs.map((dir) => dir.slice(worktree.length + 1)).sort();
|
|
11245
|
+
for (const rel of ["", ...rels]) {
|
|
11246
|
+
const raw = readText(join21(worktree, rel, "package.json"));
|
|
11247
|
+
if (raw !== null) parts.push(`${rel}/package.json
|
|
11248
|
+
${raw}`);
|
|
11249
|
+
}
|
|
11250
|
+
return { key: fingerprint(parts), dirs: ["node_modules", ...rels.map((rel) => join21(rel, "node_modules"))] };
|
|
11251
|
+
}
|
|
11022
11252
|
function planPrepare(worktree, io = {}) {
|
|
11023
11253
|
const plan = overridePlan(io.override) ?? detectPlan(worktree, io) ?? ciWorkflowPlan(worktree, io);
|
|
11024
11254
|
if (!plan) return { source: "none", ecosystem: null, commands: [] };
|
|
11025
11255
|
return { ...plan, commands: plan.commands.slice(0, MAX_PLAN_COMMANDS) };
|
|
11026
11256
|
}
|
|
11027
11257
|
async function ensureWorktreeDeps(worktree, io = {}) {
|
|
11028
|
-
const exists = io.exists ??
|
|
11258
|
+
const exists = io.exists ?? existsSync13;
|
|
11029
11259
|
const exec = io.exec ?? ((cmd) => runCommand(cmd, worktree));
|
|
11030
11260
|
const approve = io.approve ?? (() => true);
|
|
11031
|
-
const
|
|
11261
|
+
const cachePlan = envCachePlan(worktree, io);
|
|
11262
|
+
const cacheIO = io.envCache ?? {};
|
|
11032
11263
|
const report = {
|
|
11033
|
-
ecosystem:
|
|
11034
|
-
source:
|
|
11035
|
-
outcome:
|
|
11264
|
+
ecosystem: null,
|
|
11265
|
+
source: "none",
|
|
11266
|
+
outcome: "not-detected",
|
|
11036
11267
|
ran: [],
|
|
11037
11268
|
failed: [],
|
|
11038
11269
|
skipped: [],
|
|
11039
11270
|
blocked: [],
|
|
11040
|
-
warned: []
|
|
11271
|
+
warned: [],
|
|
11272
|
+
restored: [],
|
|
11273
|
+
cached: false
|
|
11041
11274
|
};
|
|
11275
|
+
if (cachePlan && !exists(join21(worktree, "node_modules"))) {
|
|
11276
|
+
report.restored = await restoreEnvSnapshot(worktree, cachePlan.key, cacheIO) ?? [];
|
|
11277
|
+
}
|
|
11278
|
+
const plan = planPrepare(worktree, io);
|
|
11279
|
+
report.ecosystem = plan.ecosystem;
|
|
11280
|
+
report.source = plan.source;
|
|
11281
|
+
report.outcome = plan.source === "none" ? "not-detected" : "nothing-to-do";
|
|
11042
11282
|
for (const cmd of plan.commands) {
|
|
11043
11283
|
if (cmd.skipIfExists && exists(cmd.skipIfExists)) {
|
|
11044
11284
|
report.skipped.push(cmd.label);
|
|
@@ -11060,11 +11300,20 @@ async function ensureWorktreeDeps(worktree, io = {}) {
|
|
|
11060
11300
|
break;
|
|
11061
11301
|
}
|
|
11062
11302
|
}
|
|
11303
|
+
if (cachePlan && !report.failed.length && !report.blocked.length) {
|
|
11304
|
+
report.cached = await saveEnvSnapshot(worktree, cachePlan.key, cachePlan.dirs, cacheIO);
|
|
11305
|
+
if (report.cached) {
|
|
11306
|
+
await pruneEnvSnapshots(cacheIO);
|
|
11307
|
+
await sweepEnvCacheTemp(cacheIO);
|
|
11308
|
+
}
|
|
11309
|
+
}
|
|
11063
11310
|
return report;
|
|
11064
11311
|
}
|
|
11065
11312
|
function depsEvents(report) {
|
|
11066
11313
|
const events = [];
|
|
11067
11314
|
const where = report.source === "none" ? "" : ` (${[report.ecosystem, `\u0438\u0441\u0442\u043E\u0447\u043D\u0438\u043A: ${sourceLabel(report.source)}`].filter(Boolean).join(", ")})`;
|
|
11315
|
+
if (report.restored.length)
|
|
11316
|
+
events.push(`\u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u0432\u0437\u044F\u0442\u043E \u0438\u0437 \u043C\u0430\u0448\u0438\u043D\u043D\u043E\u0433\u043E \u043A\u044D\u0448\u0430, \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430 \u043D\u0435 \u043F\u043E\u0442\u0440\u0435\u0431\u043E\u0432\u0430\u043B\u0430\u0441\u044C: ${report.restored.length} \u043A\u0430\u0442. node_modules \u2014 TASWM-519`);
|
|
11068
11317
|
if (report.ran.length) events.push(`\u043F\u043E\u0434\u0433\u043E\u0442\u043E\u0432\u043A\u0430 worktree${where}: ${report.ran.join(", ")} \u2014 TASWM-312`);
|
|
11069
11318
|
if (report.failed.length)
|
|
11070
11319
|
events.push(
|
|
@@ -11267,46 +11516,266 @@ function newestFirst(a, b) {
|
|
|
11267
11516
|
if (Number.isFinite(at) && Number.isFinite(bt) && at !== bt) return bt - at;
|
|
11268
11517
|
return b.id - a.id;
|
|
11269
11518
|
}
|
|
11270
|
-
function preStartAttempts(runs, taskSlug, opts = {}) {
|
|
11271
|
-
const windowMs = opts.windowMs ?? SPAWN_ATTEMPT_WINDOW_MS;
|
|
11272
|
-
const now = opts.now ?? Date.now();
|
|
11273
|
-
const own = ownRuns(runs, taskSlug).sort(newestFirst);
|
|
11274
|
-
let n = 0;
|
|
11275
|
-
let newestCreatedAt = Number.NEGATIVE_INFINITY;
|
|
11276
|
-
let newestSeenId = Number.NEGATIVE_INFINITY;
|
|
11277
|
-
for (const run of own) {
|
|
11278
|
-
const applied = parkApplied(run);
|
|
11279
|
-
if (applied) {
|
|
11280
|
-
const returned = applied.upToRunId != null ? newestSeenId > applied.upToRunId : applied.at != null && newestCreatedAt > applied.at;
|
|
11281
|
-
if (returned) break;
|
|
11282
|
-
}
|
|
11283
|
-
newestSeenId = Math.max(newestSeenId, run.id);
|
|
11284
|
-
const startedAt = run.created_at ? stampMs(run.created_at) : Number.NaN;
|
|
11285
|
-
if (Number.isFinite(startedAt)) newestCreatedAt = Math.max(newestCreatedAt, startedAt);
|
|
11286
|
-
if (run.status === "queued" || run.status === "running") continue;
|
|
11287
|
-
if (isSpawnParked(run) && run.status !== "failed") continue;
|
|
11288
|
-
if (!isPreStartFailure(run)) break;
|
|
11289
|
-
if (Number.isFinite(startedAt) && now - startedAt >= windowMs) break;
|
|
11290
|
-
if (opts.since != null && Number.isFinite(startedAt) && startedAt < opts.since) break;
|
|
11291
|
-
n += 1;
|
|
11292
|
-
if (isUnretriableParked(run)) return Math.max(n, PRE_START_ATTEMPT_LIMIT);
|
|
11293
|
-
}
|
|
11294
|
-
return n;
|
|
11519
|
+
function preStartAttempts(runs, taskSlug, opts = {}) {
|
|
11520
|
+
const windowMs = opts.windowMs ?? SPAWN_ATTEMPT_WINDOW_MS;
|
|
11521
|
+
const now = opts.now ?? Date.now();
|
|
11522
|
+
const own = ownRuns(runs, taskSlug).sort(newestFirst);
|
|
11523
|
+
let n = 0;
|
|
11524
|
+
let newestCreatedAt = Number.NEGATIVE_INFINITY;
|
|
11525
|
+
let newestSeenId = Number.NEGATIVE_INFINITY;
|
|
11526
|
+
for (const run of own) {
|
|
11527
|
+
const applied = parkApplied(run);
|
|
11528
|
+
if (applied) {
|
|
11529
|
+
const returned = applied.upToRunId != null ? newestSeenId > applied.upToRunId : applied.at != null && newestCreatedAt > applied.at;
|
|
11530
|
+
if (returned) break;
|
|
11531
|
+
}
|
|
11532
|
+
newestSeenId = Math.max(newestSeenId, run.id);
|
|
11533
|
+
const startedAt = run.created_at ? stampMs(run.created_at) : Number.NaN;
|
|
11534
|
+
if (Number.isFinite(startedAt)) newestCreatedAt = Math.max(newestCreatedAt, startedAt);
|
|
11535
|
+
if (run.status === "queued" || run.status === "running") continue;
|
|
11536
|
+
if (isSpawnParked(run) && run.status !== "failed") continue;
|
|
11537
|
+
if (!isPreStartFailure(run)) break;
|
|
11538
|
+
if (Number.isFinite(startedAt) && now - startedAt >= windowMs) break;
|
|
11539
|
+
if (opts.since != null && Number.isFinite(startedAt) && startedAt < opts.since) break;
|
|
11540
|
+
n += 1;
|
|
11541
|
+
if (isUnretriableParked(run)) return Math.max(n, PRE_START_ATTEMPT_LIMIT);
|
|
11542
|
+
}
|
|
11543
|
+
return n;
|
|
11544
|
+
}
|
|
11545
|
+
function lastPreStartAttempt(runs, taskSlug) {
|
|
11546
|
+
return runs.filter((r) => r.task_slug === taskSlug && isPreStartFailure(r)).sort(newestFirst)[0] ?? null;
|
|
11547
|
+
}
|
|
11548
|
+
function unresolvedRepoReason(repo, served) {
|
|
11549
|
+
const raw = repo.trim();
|
|
11550
|
+
if (!raw || raw.startsWith("/") || raw.startsWith("~") || /^[a-z]:[\\/]/i.test(raw)) return null;
|
|
11551
|
+
const list = served.length ? served.join(", ") : "\u043D\u0438 \u043E\u0434\u043D\u043E\u0433\u043E (\u0440\u0435\u0435\u0441\u0442\u0440 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0435\u0432 \u043F\u0443\u0441\u0442)";
|
|
11552
|
+
return `\u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439 ${raw} \u043D\u0435 \u0440\u0430\u0437\u0432\u0435\u0440\u043D\u0443\u043B\u0441\u044F \u0432 \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u044B\u0439 \u043F\u0443\u0442\u044C: \u043D\u0430 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435 \u0435\u0433\u043E \u043A\u043B\u043E\u043D\u0430 \u043D\u0435\u0442. \u041C\u0430\u0448\u0438\u043D\u0430 \u043E\u0431\u0441\u043B\u0443\u0436\u0438\u0432\u0430\u0435\u0442: ${list}. \u041A\u043B\u043E\u043D\u0438\u0440\u0443\u0439 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439 \u0438 \u0434\u043E\u0431\u0430\u0432\u044C \u0435\u0433\u043E \u0432 SWARM_REPO_PATHS (\u0438\u043B\u0438 swarm start --repo <\u043F\u0443\u0442\u044C>), \u043B\u0438\u0431\u043E \u043E\u0442\u0434\u0430\u0439 \u0437\u0430\u0434\u0430\u0447\u0443 \u043C\u0430\u0448\u0438\u043D\u0435, \u0433\u0434\u0435 \u043E\u043D \u0435\u0441\u0442\u044C.`;
|
|
11553
|
+
}
|
|
11554
|
+
function parkReason(attempts, reason) {
|
|
11555
|
+
return `${PRE_START_MARK} ${attempts} \u0440\u0430\u0437\u0430 \u043F\u043E\u0434\u0440\u044F\u0434 \u2014 ${SPAWN_PARK_MARK}, \u0447\u0442\u043E\u0431\u044B \u043D\u0435 \u0441\u043F\u0430\u0432\u043D\u0438\u0442\u044C \u043F\u043E \u043A\u0440\u0443\u0433\u0443. \u0427\u0435\u0433\u043E \u043D\u0435 \u0445\u0432\u0430\u0442\u0438\u043B\u043E: ${reason}`;
|
|
11556
|
+
}
|
|
11557
|
+
function parkReasonUnretriable(reason) {
|
|
11558
|
+
return `${PRE_START_MARK}, \u0438 ${UNRETRIABLE_PARK_MARK} \u2014 ${SPAWN_PARK_MARK} \u0441\u0440\u0430\u0437\u0443, \u043D\u0435 \u0442\u0440\u0430\u0442\u044F ${PRE_START_ATTEMPT_LIMIT} \u043F\u043E\u043F\u044B\u0442\u043A\u0438. \u0427\u0435\u0433\u043E \u043D\u0435 \u0445\u0432\u0430\u0442\u0438\u043B\u043E: ${reason}`;
|
|
11559
|
+
}
|
|
11560
|
+
|
|
11561
|
+
// package.json
|
|
11562
|
+
var package_default = {
|
|
11563
|
+
name: "@taskless-app/swarm",
|
|
11564
|
+
version: "0.6.32",
|
|
11565
|
+
type: "module",
|
|
11566
|
+
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
11567
|
+
keywords: [
|
|
11568
|
+
"ai-agents",
|
|
11569
|
+
"coding-agents",
|
|
11570
|
+
"swarm",
|
|
11571
|
+
"orchestration",
|
|
11572
|
+
"autonomous-agents",
|
|
11573
|
+
"mcp",
|
|
11574
|
+
"taskless",
|
|
11575
|
+
"cli"
|
|
11576
|
+
],
|
|
11577
|
+
license: "SEE LICENSE IN LICENSE",
|
|
11578
|
+
author: "Ilya Smurov <ilya@smurov.com>",
|
|
11579
|
+
homepage: "https://github.com/ilyasmurov/taskless/tree/main/packages/swarm-daemon#readme",
|
|
11580
|
+
repository: {
|
|
11581
|
+
type: "git",
|
|
11582
|
+
url: "git+https://github.com/ilyasmurov/taskless.git",
|
|
11583
|
+
directory: "packages/swarm-daemon"
|
|
11584
|
+
},
|
|
11585
|
+
engines: {
|
|
11586
|
+
node: ">=22"
|
|
11587
|
+
},
|
|
11588
|
+
bin: {
|
|
11589
|
+
swarm: "dist/cli.js"
|
|
11590
|
+
},
|
|
11591
|
+
files: [
|
|
11592
|
+
"dist",
|
|
11593
|
+
"docs/HERO.md",
|
|
11594
|
+
"hooks",
|
|
11595
|
+
"roles"
|
|
11596
|
+
],
|
|
11597
|
+
scripts: {
|
|
11598
|
+
overmind: 'NODE_OPTIONS="${NODE_OPTIONS:-} --conditions=development" tsx src/overmind.ts',
|
|
11599
|
+
swarm: 'NODE_OPTIONS="${NODE_OPTIONS:-} --conditions=development" tsx src/cli.ts',
|
|
11600
|
+
build: "tsup",
|
|
11601
|
+
start: "node dist/overmind.js",
|
|
11602
|
+
prepublishOnly: "pnpm run build",
|
|
11603
|
+
test: "vitest run && SWARM_TEST_BACKEND=client vitest run src/overmind.test.ts src/overmind.storm.test.ts src/db.storm.test.ts",
|
|
11604
|
+
"test:client": "SWARM_TEST_BACKEND=client vitest run src/overmind.test.ts src/overmind.storm.test.ts src/db.storm.test.ts",
|
|
11605
|
+
"test:watch": "vitest",
|
|
11606
|
+
typecheck: "tsc -p tsconfig.json --noEmit",
|
|
11607
|
+
lint: "echo 'no lint in swarm-daemon yet'"
|
|
11608
|
+
},
|
|
11609
|
+
dependencies: {
|
|
11610
|
+
"@hono/node-server": "^2.0.12",
|
|
11611
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
11612
|
+
execa: "^10.0.0",
|
|
11613
|
+
zod: "3.25.76"
|
|
11614
|
+
},
|
|
11615
|
+
gitHead: "f40f0fb4d2f96809f88b7d6ae4f086c5f4f004da"
|
|
11616
|
+
};
|
|
11617
|
+
|
|
11618
|
+
// src/version.ts
|
|
11619
|
+
var PACKAGE_VERSION = package_default.version;
|
|
11620
|
+
var MIN_NODE_MAJOR = Number(/(\d+)/.exec(package_default.engines?.node ?? "")?.[1] ?? 0);
|
|
11621
|
+
|
|
11622
|
+
// src/release.ts
|
|
11623
|
+
init_src();
|
|
11624
|
+
init_claudeBin();
|
|
11625
|
+
var SWARM_PACKAGE = SWARM_PACKAGE_NAME;
|
|
11626
|
+
var LATEST_TIMEOUT_MS = 4e3;
|
|
11627
|
+
async function fetchLatestVersion(opts = {}) {
|
|
11628
|
+
const fetchFn = opts.fetchFn ?? fetch;
|
|
11629
|
+
const timeoutMs = opts.timeoutMs ?? LATEST_TIMEOUT_MS;
|
|
11630
|
+
const pkg = opts.pkg ?? SWARM_PACKAGE;
|
|
11631
|
+
const ctrl = new AbortController();
|
|
11632
|
+
const timer = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
11633
|
+
try {
|
|
11634
|
+
const res = await fetchFn(`https://registry.npmjs.org/${pkg}/latest`, {
|
|
11635
|
+
signal: ctrl.signal,
|
|
11636
|
+
headers: { accept: "application/vnd.npm.install-v1+json, application/json" }
|
|
11637
|
+
});
|
|
11638
|
+
if (!res.ok) return null;
|
|
11639
|
+
const body = await res.json();
|
|
11640
|
+
return typeof body.version === "string" && body.version ? body.version : null;
|
|
11641
|
+
} catch {
|
|
11642
|
+
return null;
|
|
11643
|
+
} finally {
|
|
11644
|
+
clearTimeout(timer);
|
|
11645
|
+
}
|
|
11646
|
+
}
|
|
11647
|
+
function isOutdated(current, latest) {
|
|
11648
|
+
if (!latest || !current) return false;
|
|
11649
|
+
return compareVersionDirs(latest, current) > 0;
|
|
11650
|
+
}
|
|
11651
|
+
function outdatedLine(current, latest, updateCommand = "swarm update") {
|
|
11652
|
+
if (!isOutdated(current, latest)) return null;
|
|
11653
|
+
return `\u26A0 \u0432\u0435\u0440\u0441\u0438\u044F \u0440\u043E\u044F \u0443\u0441\u0442\u0430\u0440\u0435\u043B\u0430: \u0443 \u0442\u0435\u0431\u044F v${current}, \u043E\u043F\u0443\u0431\u043B\u0438\u043A\u043E\u0432\u0430\u043D\u0430 v${latest} \u2014 \u043E\u0431\u043D\u043E\u0432\u0438 \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439: ${updateCommand}`;
|
|
11654
|
+
}
|
|
11655
|
+
|
|
11656
|
+
// src/daemonVersion.ts
|
|
11657
|
+
function captureDaemonStart(now = () => /* @__PURE__ */ new Date()) {
|
|
11658
|
+
return { startedAt: now().toISOString(), version: PACKAGE_VERSION };
|
|
11659
|
+
}
|
|
11660
|
+
var DAEMON_CODE_PATHS = ["packages/swarm-daemon", "packages/swarm-core"];
|
|
11661
|
+
async function checkDaemonVersion(start, opts = {}) {
|
|
11662
|
+
const now = opts.now ?? (() => /* @__PURE__ */ new Date());
|
|
11663
|
+
const fetchLatest = opts.latest ?? (() => fetchLatestVersion());
|
|
11664
|
+
const latestVersion = await fetchLatest().catch(() => null);
|
|
11665
|
+
return {
|
|
11666
|
+
startedAt: start.startedAt,
|
|
11667
|
+
version: start.version,
|
|
11668
|
+
latestVersion,
|
|
11669
|
+
stale: isOutdated(start.version, latestVersion),
|
|
11670
|
+
checkedAt: now().toISOString()
|
|
11671
|
+
};
|
|
11672
|
+
}
|
|
11673
|
+
|
|
11674
|
+
// src/escalations.ts
|
|
11675
|
+
var RESOLVE_ESCALATION_MARK = "\u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442 \u043D\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0451\u043D";
|
|
11676
|
+
var REVIEW_ESCALATION_MARK = "\u0440\u0435\u0432\u044C\u044E \u043D\u0435 \u0441\u043C\u043E\u0433\u043B\u043E \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044C\u0441\u044F";
|
|
11677
|
+
var REVIEW_UNRECOVERABLE_MARK = "\u0440\u0435\u0432\u044C\u044E \u043D\u0435 \u043F\u043E\u0434\u043D\u044F\u0442\u044C";
|
|
11678
|
+
function reviewUnrecoverableMark(branch) {
|
|
11679
|
+
return `${REVIEW_UNRECOVERABLE_MARK} (\u0432\u0435\u0442\u043A\u0430 ${branch})`;
|
|
11680
|
+
}
|
|
11681
|
+
var REVIEW_CYCLE_ESCALATION_MARK = "\u0440\u0435-\u0440\u0435\u0432\u044C\u044E \u0437\u0430\u0446\u0438\u043A\u043B\u0438\u043B\u043E\u0441\u044C";
|
|
11682
|
+
var CI_WAKE_ESCALATION_MARK = "CI-\u0430\u0432\u0442\u043E\u043F\u043E\u0447\u0438\u043D\u043A\u0430 \u043D\u0435 \u0441\u043F\u0440\u0430\u0432\u0438\u043B\u0430\u0441\u044C";
|
|
11683
|
+
var CHANGES_WAKE_ESCALATION_MARK = "\u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044F \u043D\u0435 \u043F\u043E\u0434\u043D\u044F\u043B\u0430\u0441\u044C";
|
|
11684
|
+
var REVIEW_BUDGET_MARK = "\u0440\u0435\u0432\u044C\u044E \u043D\u0435 \u0443\u043B\u043E\u0436\u0438\u043B\u043E\u0441\u044C \u0432 \u043F\u043E\u0442\u043E\u043B\u043E\u043A \u0442\u0440\u0430\u0442";
|
|
11685
|
+
function reviewBudgetMark(cycle) {
|
|
11686
|
+
return `${REVIEW_BUDGET_MARK} (\u043A\u0440\u0443\u0433 ${cycle})`;
|
|
11295
11687
|
}
|
|
11296
|
-
|
|
11297
|
-
|
|
11688
|
+
var SELF_REFERENCE_MARK = "PR \u0442\u0440\u043E\u0433\u0430\u0435\u0442 \u043A\u043E\u0434 \u0441\u0430\u043C\u043E\u0433\u043E \u0434\u0435\u043C\u043E\u043D\u0430";
|
|
11689
|
+
var VERIFY_ESCALATION_MARK = "\u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044F: \u043F\u043E\u043F\u044B\u0442\u043A\u0438 \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D\u044B";
|
|
11690
|
+
var CI_WAIT_ESCALATION_MARK = "\u043E\u0436\u0438\u0434\u0430\u043D\u0438\u0435 CI \u0437\u0430\u0442\u044F\u043D\u0443\u043B\u043E\u0441\u044C";
|
|
11691
|
+
var ESCALATIONS = [
|
|
11692
|
+
{
|
|
11693
|
+
mark: SELF_REFERENCE_MARK,
|
|
11694
|
+
// Надзора тут нет намеренно: у self-reference свой канал доставки (watchdog-нотификация
|
|
11695
|
+
// на месте записи), и вход в NEEDS_HUMAN_MARKS задвоил бы сигнал. Ровно на этом маркере
|
|
11696
|
+
// wakeRun и запирал дверь: под self-reference попадает ЛЮБАЯ задача роя, трогающая
|
|
11697
|
+
// packages/swarm-daemon или packages/swarm-core, — вердикта не будет никогда.
|
|
11698
|
+
sides: ["reviewer", "author"],
|
|
11699
|
+
why: "PR \u0442\u0440\u043E\u0433\u0430\u0435\u0442 \u043A\u043E\u0434 \u0441\u0430\u043C\u043E\u0433\u043E \u0434\u0435\u043C\u043E\u043D\u0430: \u0430\u0432\u0442\u043E-\u0440\u0435\u0432\u044C\u044E \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043E \u043D\u0430\u043C\u0435\u0440\u0435\u043D\u043D\u043E, \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u043D\u0435 \u0431\u0443\u0434\u0435\u0442",
|
|
11700
|
+
stopReason: () => `PR \u0442\u0440\u043E\u0433\u0430\u0435\u0442 \u043A\u043E\u0434 \u0441\u0430\u043C\u043E\u0433\u043E \u0434\u0435\u043C\u043E\u043D\u0430 (${DAEMON_CODE_PATHS.join(", ")}) \u2014 \u0430\u0432\u0442\u043E-\u0440\u0435\u0432\u044C\u044E \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043E \u043D\u0430\u043C\u0435\u0440\u0435\u043D\u043D\u043E (TASWM-270): \u0440\u0430\u0431\u043E\u0442\u0430\u044E\u0449\u0438\u0439 \u0434\u0435\u043C\u043E\u043D \u043A\u0440\u0443\u0442\u0438\u0442\u0441\u044F \u043D\u0430 \u0441\u0442\u0430\u0440\u043E\u043C \u043A\u043E\u0434\u0435 \u0438 \u0441\u0432\u043E\u044E \u0436\u0435 \u043F\u0440\u0430\u0432\u043A\u0443 \u043D\u0435 \u0430\u0442\u0442\u0435\u0441\u0442\u0443\u0435\u0442. \u041D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A`
|
|
11701
|
+
},
|
|
11702
|
+
{
|
|
11703
|
+
mark: REVIEW_ESCALATION_MARK,
|
|
11704
|
+
sides: ["reviewer", "author", "supervision"],
|
|
11705
|
+
why: "\u043F\u043E\u0442\u043E\u043B\u043E\u043A \u043F\u043E\u043F\u044B\u0442\u043E\u043A \u0430\u0432\u0442\u043E-\u0440\u0435\u0432\u044C\u044E \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D \u2014 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u044E\u0449\u0435\u0433\u043E \u0440\u043E\u0439 \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u0435\u0442",
|
|
11706
|
+
stopReason: (_run, limits) => `\u043F\u043E\u043F\u044B\u0442\u043A\u0438 \u0430\u0432\u0442\u043E-\u0440\u0435\u0432\u044C\u044E \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D\u044B (${limits.reviewSpawn}) \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A`
|
|
11707
|
+
},
|
|
11708
|
+
{
|
|
11709
|
+
mark: REVIEW_CYCLE_ESCALATION_MARK,
|
|
11710
|
+
sides: ["reviewer", "author", "supervision"],
|
|
11711
|
+
why: "\u043F\u043E\u0442\u043E\u043B\u043E\u043A \u0446\u0438\u043A\u043B\u043E\u0432 \u0440\u0435-\u0440\u0435\u0432\u044C\u044E \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D \u2014 \u0430\u0432\u0442\u043E\u0441\u043F\u0430\u0432\u043D\u043E\u0432 \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435\u0442",
|
|
11712
|
+
stopReason: (_run, limits) => `\u043F\u043E\u0442\u043E\u043B\u043E\u043A \u0446\u0438\u043A\u043B\u043E\u0432 \u0440\u0435-\u0440\u0435\u0432\u044C\u044E (${limits.reviewCycle}) \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A`
|
|
11713
|
+
},
|
|
11714
|
+
{
|
|
11715
|
+
mark: REVIEW_BUDGET_MARK,
|
|
11716
|
+
sides: ["reviewer", "author", "supervision"],
|
|
11717
|
+
why: "\u0440\u0435\u0432\u044C\u044E \u044D\u0442\u043E\u0433\u043E \u043A\u0440\u0443\u0433\u0430 \u0441\u0433\u043E\u0440\u0435\u043B\u043E \u043D\u0430 \u043F\u043E\u0442\u043E\u043B\u043A\u0435 \u0442\u0440\u0430\u0442 \u2014 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0430\u044F \u043F\u043E\u043F\u044B\u0442\u043A\u0430 \u0443\u043F\u0440\u0451\u0442\u0441\u044F \u0432 \u0442\u043E\u0442 \u0436\u0435",
|
|
11718
|
+
scoped: (run) => reviewBudgetMark(run.review_cycle ?? 0),
|
|
11719
|
+
stopReason: () => "\u0440\u0435\u0432\u044C\u044E \u044D\u0442\u043E\u0433\u043E \u043A\u0440\u0443\u0433\u0430 \u0441\u0433\u043E\u0440\u0435\u043B\u043E \u043D\u0430 \u043F\u043E\u0442\u043E\u043B\u043A\u0435 \u0442\u0440\u0430\u0442 \u2014 \u043F\u043E\u0432\u0442\u043E\u0440 \u0443\u043F\u0440\u0451\u0442\u0441\u044F \u0432 \u0442\u043E\u0442 \u0436\u0435, \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A"
|
|
11720
|
+
},
|
|
11721
|
+
{
|
|
11722
|
+
mark: REVIEW_UNRECOVERABLE_MARK,
|
|
11723
|
+
sides: ["reviewer", "author", "supervision"],
|
|
11724
|
+
why: "\u0432\u0435\u0442\u043A\u0438 PR \u043D\u0435\u0442 \u043D\u0430 origin \u2014 \u043F\u043E\u0432\u0442\u043E\u0440 \u0441\u0434\u0435\u043B\u0430\u0435\u0442 \u0442\u043E\u0442 \u0436\u0435 git-\u0432\u044B\u0437\u043E\u0432 \u0441 \u0442\u0435\u043C \u0436\u0435 \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u043E\u043C",
|
|
11725
|
+
scoped: (run) => reviewUnrecoverableMark(run.branch || run.task_slug),
|
|
11726
|
+
stopReason: () => "\u0432\u0435\u0442\u043A\u0438 PR \u043D\u0435\u0442 \u043D\u0430 origin \u2014 \u043F\u043E\u0432\u0442\u043E\u0440\u043E\u043C \u043D\u0435 \u043B\u0435\u0447\u0438\u0442\u0441\u044F, \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A"
|
|
11727
|
+
},
|
|
11728
|
+
{
|
|
11729
|
+
mark: CHANGES_WAKE_ESCALATION_MARK,
|
|
11730
|
+
// Проверяющего этот маркер не касается: вердикт-то есть, не поднимается АВТОР.
|
|
11731
|
+
sides: ["author", "supervision"],
|
|
11732
|
+
why: "\u043F\u043E\u0442\u043E\u043B\u043E\u043A \u0431\u0435\u0441\u043F\u043B\u043E\u0434\u043D\u044B\u0445 \u0444\u0438\u043A\u0441-\u043F\u043E\u0431\u0443\u0434\u043E\u043A \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D \u2014 \u0430\u0432\u0442\u043E\u0440\u0430 \u0440\u043E\u0439 \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u0431\u0443\u0434\u0438\u0442",
|
|
11733
|
+
scoped: (run) => `${CHANGES_WAKE_ESCALATION_MARK} (\u043A\u0440\u0443\u0433 ${run.review_cycle ?? 0})`
|
|
11734
|
+
},
|
|
11735
|
+
{
|
|
11736
|
+
mark: RESOLVE_ESCALATION_MARK,
|
|
11737
|
+
sides: ["author", "supervision"],
|
|
11738
|
+
why: "\u043F\u043E\u0442\u043E\u043B\u043E\u043A \u0430\u0432\u0442\u043E-\u0440\u0435\u0437\u043E\u043B\u0432\u0435\u0440\u043E\u0432 \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u0430 \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D \u2014 \u0434\u0430\u043B\u044C\u0448\u0435 \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442 \u0440\u0430\u0437\u0432\u043E\u0434\u0438\u0442 \u0447\u0435\u043B\u043E\u0432\u0435\u043A"
|
|
11739
|
+
},
|
|
11740
|
+
{
|
|
11741
|
+
mark: CI_WAKE_ESCALATION_MARK,
|
|
11742
|
+
sides: ["author", "supervision"],
|
|
11743
|
+
why: "\u043F\u043E\u0442\u043E\u043B\u043E\u043A \u043F\u043E\u0431\u0443\u0434\u043E\u043A \u043F\u043E \u043A\u0440\u0430\u0441\u043D\u043E\u043C\u0443 CI \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D \u2014 \u0440\u043E\u0439 \u043F\u0435\u0440\u0435\u0441\u0442\u0430\u043B \u0447\u0438\u043D\u0438\u0442\u044C CI \u0441\u0430\u043C"
|
|
11744
|
+
},
|
|
11745
|
+
{
|
|
11746
|
+
mark: VERIFY_ESCALATION_MARK,
|
|
11747
|
+
// Надзора нет: верификация — не ревью, её отсутствие не держит PR (TASWM-344), и в
|
|
11748
|
+
// NEEDS_HUMAN_MARKS этот маркер никогда не входил. Автору дверь открывает: вердикта
|
|
11749
|
+
// верификации у прогона не будет, поднять его может только оператор.
|
|
11750
|
+
sides: ["author"],
|
|
11751
|
+
why: "\u043F\u043E\u0442\u043E\u043B\u043E\u043A \u043F\u043E\u043F\u044B\u0442\u043E\u043A \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u0443 PR \u043D\u0435 \u0431\u0443\u0434\u0435\u0442"
|
|
11752
|
+
},
|
|
11753
|
+
{
|
|
11754
|
+
mark: CI_WAIT_ESCALATION_MARK,
|
|
11755
|
+
// Сторон НЕТ — и это решение, а не пропуск. Маркер лежит на прогоне РЕВЬЮЕРА (paused,
|
|
11756
|
+
// AWAITING_CI_REASON), а не на прогоне автора, и человека зовёт сам:
|
|
11757
|
+
// escalateStaleCiWaits пишет аномалию и дёргает watchdog.notify на месте. Вписать его в
|
|
11758
|
+
// author/supervision значило бы задвоить сигнал и поменять поведение — чего задача
|
|
11759
|
+
// прямо просила не делать.
|
|
11760
|
+
sides: [],
|
|
11761
|
+
why: "\u0440\u043E\u0439 \u0417\u041E\u0412\u0401\u0422 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0430, \u043D\u043E \u0441\u0430\u043C \u0435\u0449\u0451 \u0436\u0434\u0451\u0442: \u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u043D\u0435 \u0441\u0434\u0430\u043B\u0441\u044F, \u0430 \u043D\u043E\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044E \u0448\u043B\u0451\u0442 \u0441\u0432\u043E\u0438\u043C \u043A\u0430\u043D\u0430\u043B\u043E\u043C"
|
|
11762
|
+
}
|
|
11763
|
+
];
|
|
11764
|
+
function escalationText(entry, run, side) {
|
|
11765
|
+
return side === "supervision" ? entry.mark : entry.scoped?.(run) ?? entry.mark;
|
|
11298
11766
|
}
|
|
11299
|
-
function
|
|
11300
|
-
|
|
11301
|
-
if (!raw || raw.startsWith("/") || raw.startsWith("~") || /^[a-z]:[\\/]/i.test(raw)) return null;
|
|
11302
|
-
const list = served.length ? served.join(", ") : "\u043D\u0438 \u043E\u0434\u043D\u043E\u0433\u043E (\u0440\u0435\u0435\u0441\u0442\u0440 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0435\u0432 \u043F\u0443\u0441\u0442)";
|
|
11303
|
-
return `\u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439 ${raw} \u043D\u0435 \u0440\u0430\u0437\u0432\u0435\u0440\u043D\u0443\u043B\u0441\u044F \u0432 \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u044B\u0439 \u043F\u0443\u0442\u044C: \u043D\u0430 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435 \u0435\u0433\u043E \u043A\u043B\u043E\u043D\u0430 \u043D\u0435\u0442. \u041C\u0430\u0448\u0438\u043D\u0430 \u043E\u0431\u0441\u043B\u0443\u0436\u0438\u0432\u0430\u0435\u0442: ${list}. \u041A\u043B\u043E\u043D\u0438\u0440\u0443\u0439 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439 \u0438 \u0434\u043E\u0431\u0430\u0432\u044C \u0435\u0433\u043E \u0432 SWARM_REPO_PATHS (\u0438\u043B\u0438 swarm start --repo <\u043F\u0443\u0442\u044C>), \u043B\u0438\u0431\u043E \u043E\u0442\u0434\u0430\u0439 \u0437\u0430\u0434\u0430\u0447\u0443 \u043C\u0430\u0448\u0438\u043D\u0435, \u0433\u0434\u0435 \u043E\u043D \u0435\u0441\u0442\u044C.`;
|
|
11767
|
+
function escalationsFor(side) {
|
|
11768
|
+
return ESCALATIONS.filter((e) => e.sides.includes(side));
|
|
11304
11769
|
}
|
|
11305
|
-
function
|
|
11306
|
-
return
|
|
11770
|
+
function isEscalationText(text, run, side) {
|
|
11771
|
+
return escalationsFor(side).some((e) => text.includes(escalationText(e, run, side)));
|
|
11307
11772
|
}
|
|
11308
|
-
function
|
|
11309
|
-
|
|
11773
|
+
function findEscalation(texts, run, side) {
|
|
11774
|
+
for (const entry of escalationsFor(side)) {
|
|
11775
|
+
const needle = escalationText(entry, run, side);
|
|
11776
|
+
if (texts.some((t) => t.includes(needle))) return entry;
|
|
11777
|
+
}
|
|
11778
|
+
return null;
|
|
11310
11779
|
}
|
|
11311
11780
|
|
|
11312
11781
|
// src/worktreeReap.ts
|
|
@@ -11384,7 +11853,12 @@ import { basename as basename3 } from "path";
|
|
|
11384
11853
|
async function runGh(args, timeoutMs, cwd) {
|
|
11385
11854
|
const label2 = callLabel("gh", args) + (cwd ? ` (repo ${basename3(cwd)})` : "");
|
|
11386
11855
|
const child = cwd ? $3({ cwd, timeout: timeoutMs })`gh ${args}` : $3({ timeout: timeoutMs })`gh ${args}`;
|
|
11387
|
-
|
|
11856
|
+
try {
|
|
11857
|
+
return (await withHardTimeout(child, "gh", label2, timeoutMs)).stdout;
|
|
11858
|
+
} catch (e) {
|
|
11859
|
+
noteCallFailure("gh", label2, e);
|
|
11860
|
+
throw e;
|
|
11861
|
+
}
|
|
11388
11862
|
}
|
|
11389
11863
|
function ghRunner(timeoutMs = GH_CALL_TIMEOUT_MS) {
|
|
11390
11864
|
return (args) => runGh(args, timeoutMs);
|
|
@@ -11598,36 +12072,59 @@ var DENIED_STDERR = /(?:^|\D)403(?:\D|$)|not accessible by integration|must have
|
|
|
11598
12072
|
function firstLine(s) {
|
|
11599
12073
|
return s.split("\n").map((l) => l.trim()).find((l) => l.length > 0) ?? "gh \u0432\u0435\u0440\u043D\u0443\u043B 403";
|
|
11600
12074
|
}
|
|
12075
|
+
var GhCallFailedError = class extends Error {
|
|
12076
|
+
failure;
|
|
12077
|
+
constructor(call, failure) {
|
|
12078
|
+
super(`${call}: ${failure.reason}${failure.detail ? ` \u2014 ${failure.detail}` : ""}`);
|
|
12079
|
+
this.name = "GhCallFailedError";
|
|
12080
|
+
this.failure = failure;
|
|
12081
|
+
}
|
|
12082
|
+
};
|
|
11601
12083
|
var realChecksRunner = async (pr) => {
|
|
11602
|
-
const
|
|
11603
|
-
|
|
11604
|
-
"gh",
|
|
11605
|
-
callLabel("gh", ["pr", "checks", pr]),
|
|
11606
|
-
15e3
|
|
11607
|
-
);
|
|
12084
|
+
const label2 = callLabel("gh", ["pr", "checks", pr]);
|
|
12085
|
+
const r = await withHardTimeout($3({ reject: false, timeout: 15e3 })`gh pr checks ${pr} --json state,name,bucket,link`, "gh", label2, 15e3);
|
|
11608
12086
|
const stdout = r.stdout ?? "";
|
|
11609
12087
|
const stderr = r.stderr ?? "";
|
|
12088
|
+
if (isTimedOut(r)) throw new GhCallFailedError(label2, classifyCallFailure(r));
|
|
11610
12089
|
if (!stdout.trim() && NO_CHECKS_STDERR.test(stderr)) return "[]";
|
|
11611
12090
|
if (!stdout.trim() && DENIED_STDERR.test(stderr)) throw new CiAccessDeniedError(firstLine(stderr));
|
|
12091
|
+
if (!stdout.trim()) throw new GhCallFailedError(label2, noteCallFailure("gh", label2, r));
|
|
11612
12092
|
return stdout;
|
|
11613
12093
|
};
|
|
11614
12094
|
async function prCheckRows(pr, run) {
|
|
12095
|
+
let out;
|
|
11615
12096
|
try {
|
|
11616
|
-
|
|
11617
|
-
const rows = JSON.parse(out);
|
|
11618
|
-
if (!Array.isArray(rows)) return null;
|
|
11619
|
-
return rows.map((r) => ({ name: r.name ?? "?", bucket: (r.bucket ?? "").toLowerCase(), link: r.link ?? "" }));
|
|
12097
|
+
out = await run(pr);
|
|
11620
12098
|
} catch (e) {
|
|
11621
12099
|
if (e instanceof CiAccessDeniedError) return { denied: e.detail };
|
|
11622
|
-
return
|
|
12100
|
+
return { unavailable: e instanceof GhCallFailedError ? e.failure : classifyCallFailure(e) };
|
|
12101
|
+
}
|
|
12102
|
+
let parsed;
|
|
12103
|
+
try {
|
|
12104
|
+
parsed = JSON.parse(out);
|
|
12105
|
+
} catch {
|
|
12106
|
+
return { unavailable: { reason: "unreadable", detail: headOfOutput(out) } };
|
|
11623
12107
|
}
|
|
12108
|
+
if (!Array.isArray(parsed)) return { unavailable: { reason: "unreadable", detail: "\u043E\u0442\u0432\u0435\u0442 gh \u2014 \u043D\u0435 \u0441\u043F\u0438\u0441\u043E\u043A \u0447\u0435\u043A\u043E\u0432" } };
|
|
12109
|
+
return parsed.map((r) => ({
|
|
12110
|
+
name: r.name ?? "?",
|
|
12111
|
+
bucket: (r.bucket ?? "").toLowerCase(),
|
|
12112
|
+
link: r.link ?? ""
|
|
12113
|
+
}));
|
|
12114
|
+
}
|
|
12115
|
+
function headOfOutput(out) {
|
|
12116
|
+
const head = firstLine(out).slice(0, 120);
|
|
12117
|
+
return head === "gh \u0432\u0435\u0440\u043D\u0443\u043B 403" ? "\u043F\u0443\u0441\u0442\u043E" : head;
|
|
11624
12118
|
}
|
|
11625
12119
|
function isDenied(rows) {
|
|
11626
|
-
return
|
|
12120
|
+
return !Array.isArray(rows) && "denied" in rows;
|
|
12121
|
+
}
|
|
12122
|
+
function isUnavailable(rows) {
|
|
12123
|
+
return !Array.isArray(rows) && "unavailable" in rows;
|
|
11627
12124
|
}
|
|
11628
12125
|
async function ghChecks(pr, run = realChecksRunner) {
|
|
11629
12126
|
const rows = await prCheckRows(pr, run);
|
|
11630
|
-
if (
|
|
12127
|
+
if (isDenied(rows) || isUnavailable(rows) || rows.length === 0) return null;
|
|
11631
12128
|
let pass = 0, fail = 0, pending2 = 0;
|
|
11632
12129
|
for (const r of rows) {
|
|
11633
12130
|
if (r.bucket === "pass") pass++;
|
|
@@ -11651,13 +12148,16 @@ async function ghChecks(pr, run = realChecksRunner) {
|
|
|
11651
12148
|
}
|
|
11652
12149
|
return { bucket, text, tone, total, pass, fail, pending: pending2 };
|
|
11653
12150
|
}
|
|
11654
|
-
function runIdFromLink(
|
|
11655
|
-
const m =
|
|
12151
|
+
function runIdFromLink(link2) {
|
|
12152
|
+
const m = link2.match(/\/actions\/runs\/(\d+)/);
|
|
11656
12153
|
return m ? m[1] : null;
|
|
11657
12154
|
}
|
|
11658
12155
|
async function prCiStatus(pr, run = realChecksRunner) {
|
|
11659
12156
|
const rows = await prCheckRows(pr, run);
|
|
11660
|
-
if (
|
|
12157
|
+
if (isUnavailable(rows)) {
|
|
12158
|
+
const f = rows.unavailable;
|
|
12159
|
+
return { bucket: "unavailable", reason: f.reason, failing: [], failingRunIds: [], total: 0, pass: 0, pending: 0, ...f.detail ? { detail: f.detail } : {} };
|
|
12160
|
+
}
|
|
11661
12161
|
if (isDenied(rows)) return { bucket: "denied", failing: [], failingRunIds: [], total: 0, pass: 0, pending: 0, detail: rows.denied };
|
|
11662
12162
|
if (rows.length === 0) return { bucket: "none", failing: [], failingRunIds: [], total: 0, pass: 0, pending: 0 };
|
|
11663
12163
|
const total = rows.length;
|
|
@@ -11713,7 +12213,7 @@ init_git();
|
|
|
11713
12213
|
|
|
11714
12214
|
// src/diff.ts
|
|
11715
12215
|
import { statSync as statSync10 } from "fs";
|
|
11716
|
-
import { join as
|
|
12216
|
+
import { join as join26 } from "path";
|
|
11717
12217
|
import { $ as $4 } from "execa";
|
|
11718
12218
|
var MAX_FILES = 200;
|
|
11719
12219
|
var MAX_LINES_PER_FILE = 1500;
|
|
@@ -11881,7 +12381,7 @@ async function collectRunDiff(worktree, opts = {}) {
|
|
|
11881
12381
|
files.push(pf);
|
|
11882
12382
|
}
|
|
11883
12383
|
}
|
|
11884
|
-
files.sort((a, b) => mtime(
|
|
12384
|
+
files.sort((a, b) => mtime(join26(worktree, b.path)) - mtime(join26(worktree, a.path)));
|
|
11885
12385
|
const truncatedFiles = files.length > MAX_FILES;
|
|
11886
12386
|
if (truncatedFiles) files = files.slice(0, MAX_FILES);
|
|
11887
12387
|
const added = files.reduce((s, f) => s + f.added, 0);
|
|
@@ -12714,7 +13214,7 @@ ${report}${shots.warning}`,
|
|
|
12714
13214
|
const myKind = opts.runKind ?? (await store.myRun().catch(() => null))?.kind;
|
|
12715
13215
|
if (myKind === "hero") {
|
|
12716
13216
|
const taskless = opts.taskless ?? null;
|
|
12717
|
-
const
|
|
13217
|
+
const errText3 = (text) => ({ content: [{ type: "text", text }], isError: true });
|
|
12718
13218
|
let heroCache = null;
|
|
12719
13219
|
const heroRun = async () => heroCache ??= await store.myRun();
|
|
12720
13220
|
let epicCache;
|
|
@@ -12732,14 +13232,14 @@ ${report}${shots.warning}`,
|
|
|
12732
13232
|
estimate_hours: z.number().positive().describe("\u043E\u0446\u0435\u043D\u043A\u0430 \u0432 \u0447\u0430\u0441\u0430\u0445 (\u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E)")
|
|
12733
13233
|
},
|
|
12734
13234
|
guard(async ({ title, description_md, estimate_hours }) => {
|
|
12735
|
-
if (!taskless) return
|
|
13235
|
+
if (!taskless) return errText3("Taskless \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D (slug-only \u0440\u0435\u0436\u0438\u043C) \u2014 \u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0443 \u043D\u0435 \u0441\u043E\u0437\u0434\u0430\u0442\u044C.");
|
|
12736
13236
|
const hero = await heroRun();
|
|
12737
|
-
if (!hero) return
|
|
13237
|
+
if (!hero) return errText3("\u0434\u0435\u043C\u043E\u043D \u043D\u0435 \u043E\u0442\u0434\u0430\u043B \u0442\u0432\u043E\u0439 \u043F\u0440\u043E\u0433\u043E\u043D \u2014 \u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0443 \u043D\u0435 \u0441\u043E\u0437\u0434\u0430\u0442\u044C, \u043F\u043E\u0432\u0442\u043E\u0440\u0438 \u043F\u043E\u0437\u0436\u0435.");
|
|
12738
13238
|
const ep = await epic(hero.task_slug);
|
|
12739
|
-
if (!ep) return
|
|
12740
|
-
if (!ep.projectId) return
|
|
13239
|
+
if (!ep) return errText3(`\u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u043E\u043B\u0443\u0447\u0438\u0442\u044C \u044D\u043F\u0438\u043A ${hero.task_slug} \u0438\u0437 Taskless \u2014 \u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0430 \u043D\u0435 \u0441\u043E\u0437\u0434\u0430\u043D\u0430.`);
|
|
13240
|
+
if (!ep.projectId) return errText3(`\u044D\u043F\u0438\u043A ${hero.task_slug} \u0431\u0435\u0437 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u2014 \u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0443 \u0441\u043E\u0437\u0434\u0430\u0442\u044C \u043D\u0435\u043B\u044C\u0437\u044F (\u043D\u0443\u0436\u0435\u043D projectId).`);
|
|
12741
13241
|
const statusId = await taskless.backlogStatusId();
|
|
12742
|
-
if (!statusId) return
|
|
13242
|
+
if (!statusId) return errText3("\u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D \u0441\u0442\u0430\u0442\u0443\u0441 \u043A\u0430\u0442\u0435\u0433\u043E\u0440\u0438\u0438 backlog \u0432\u043E\u0440\u043A\u0441\u043F\u0435\u0439\u0441\u0430 \u2014 \u043D\u0435 \u0441\u043E\u0437\u0434\u0430\u044E (\u0440\u0438\u0441\u043A \u043F\u043E\u043F\u0430\u0441\u0442\u044C \u0441\u0440\u0430\u0437\u0443 \u0432 \xAB\u0421\u0434\u0435\u043B\u0430\u0442\u044C\xBB).");
|
|
12743
13243
|
const footer = `
|
|
12744
13244
|
|
|
12745
13245
|
---
|
|
@@ -12753,7 +13253,7 @@ ${report}${shots.warning}`,
|
|
|
12753
13253
|
projectId: ep.projectId
|
|
12754
13254
|
// TASWM-138: наследуем проект эпика — иначе сирота не claimable
|
|
12755
13255
|
});
|
|
12756
|
-
if (!created) return
|
|
13256
|
+
if (!created) return errText3("Taskless \u043D\u0435 \u0441\u043E\u0437\u0434\u0430\u043B \u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0443 (\u0441\u043C. \u043B\u043E\u0433 \u0434\u0435\u043C\u043E\u043D\u0430).");
|
|
12757
13257
|
await store.addEvent(null, "step", `\u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0430 \u0441\u043E\u0437\u0434\u0430\u043D\u0430: ${created.slug}`);
|
|
12758
13258
|
return { content: [{ type: "text", text: created.slug }] };
|
|
12759
13259
|
})
|
|
@@ -12767,18 +13267,18 @@ ${report}${shots.warning}`,
|
|
|
12767
13267
|
after: z.array(z.string()).optional().describe("\u0441\u043B\u0430\u0433\u0438 \u0437\u0430\u0434\u0430\u0447, \u043F\u043E\u0441\u043B\u0435 done \u043A\u043E\u0442\u043E\u0440\u044B\u0445 \u0441\u043F\u0430\u0432\u043D\u0438\u0442\u044C (\u043E\u043F\u0446\u0438\u043E\u043D\u0430\u043B\u044C\u043D\u043E)")
|
|
12768
13268
|
},
|
|
12769
13269
|
guard(async ({ slug, area, after }) => {
|
|
12770
|
-
if (!taskless) return
|
|
13270
|
+
if (!taskless) return errText3("Taskless \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D (slug-only \u0440\u0435\u0436\u0438\u043C) \u2014 \u0434\u0438\u0441\u043F\u0430\u0442\u0447 \u043D\u0435\u0432\u043E\u0437\u043C\u043E\u0436\u0435\u043D.");
|
|
12771
13271
|
const hero = await heroRun();
|
|
12772
|
-
if (!hero) return
|
|
13272
|
+
if (!hero) return errText3("\u0434\u0435\u043C\u043E\u043D \u043D\u0435 \u043E\u0442\u0434\u0430\u043B \u0442\u0432\u043E\u0439 \u043F\u0440\u043E\u0433\u043E\u043D \u2014 \u0434\u0438\u0441\u043F\u0430\u0442\u0447 \u043D\u0435\u0432\u043E\u0437\u043C\u043E\u0436\u0435\u043D, \u043F\u043E\u0432\u0442\u043E\u0440\u0438 \u043F\u043E\u0437\u0436\u0435.");
|
|
12773
13273
|
const read = await taskless.getTaskDetailed(slug);
|
|
12774
|
-
if (!read.ok) return
|
|
13274
|
+
if (!read.ok) return errText3(`\u043D\u0435 \u0441\u043C\u043E\u0433 \u043F\u043E\u0441\u043C\u043E\u0442\u0440\u0435\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443 ${slug}: ${describeToolFailure(read.failure)}.`);
|
|
12775
13275
|
const task = read.task;
|
|
12776
13276
|
const ep = await epic(hero.task_slug);
|
|
12777
13277
|
if (!ep || task.parentId !== ep.id) {
|
|
12778
|
-
return
|
|
13278
|
+
return errText3(`${slug} \u2014 \u043D\u0435 \u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0430 \u0442\u0432\u043E\u0435\u0433\u043E \u044D\u043F\u0438\u043A\u0430 ${hero.task_slug}. \u0414\u0438\u0441\u043F\u0430\u0442\u0447\u0438\u0442\u044C \u043C\u043E\u0436\u043D\u043E \u0442\u043E\u043B\u044C\u043A\u043E \u0441\u0432\u043E\u0438 \u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0438.`);
|
|
12779
13279
|
}
|
|
12780
13280
|
if (task.statusCategory !== "unstarted") {
|
|
12781
|
-
return
|
|
13281
|
+
return errText3(
|
|
12782
13282
|
`\u0436\u0434\u0438 \u0430\u043F\u043F\u0440\u0443\u0432\u0430 \u0432\u043B\u0430\u0434\u0435\u043B\u044C\u0446\u0430: \u043F\u0435\u0440\u0435\u043D\u043E\u0441 \u0432 \xAB\u0421\u0434\u0435\u043B\u0430\u0442\u044C\xBB. ${slug} \u0441\u0435\u0439\u0447\u0430\u0441 \u0432 \u043A\u0430\u0442\u0435\u0433\u043E\u0440\u0438\u0438 \u0441\u0442\u0430\u0442\u0443\u0441\u0430 \xAB${task.statusCategory ?? "?"}\xBB, \u0434\u0438\u0441\u043F\u0430\u0442\u0447 \u0442\u043E\u043B\u044C\u043A\u043E \u0434\u043B\u044F unstarted (\u043E\u0434\u043E\u0431\u0440\u0435\u043D\u043D\u044B\u0445).`
|
|
12783
13283
|
);
|
|
12784
13284
|
}
|
|
@@ -12800,7 +13300,7 @@ ${report}${shots.warning}`,
|
|
|
12800
13300
|
{},
|
|
12801
13301
|
guard(async () => {
|
|
12802
13302
|
const hero = await heroRun();
|
|
12803
|
-
if (!hero) return
|
|
13303
|
+
if (!hero) return errText3("\u0434\u0435\u043C\u043E\u043D \u043D\u0435 \u043E\u0442\u0434\u0430\u043B \u0442\u0432\u043E\u0439 \u043F\u0440\u043E\u0433\u043E\u043D \u2014 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435 \u043E\u0442\u0440\u044F\u0434\u0430 \u0441\u0435\u0439\u0447\u0430\u0441 \u043D\u0435 \u0441\u043E\u0431\u0440\u0430\u0442\u044C, \u043F\u043E\u0432\u0442\u043E\u0440\u0438 \u043F\u043E\u0437\u0436\u0435.");
|
|
12804
13304
|
const squad = await store.squadRuns();
|
|
12805
13305
|
const qByRun = /* @__PURE__ */ new Map();
|
|
12806
13306
|
for (const q of await store.openQuestionsFor()) {
|
|
@@ -12830,9 +13330,9 @@ ${report}${shots.warning}`,
|
|
|
12830
13330
|
},
|
|
12831
13331
|
guard(async ({ question_id, text }) => {
|
|
12832
13332
|
const mine = (await store.openQuestionsFor()).some((q) => q.id === question_id);
|
|
12833
|
-
if (!mine) return
|
|
13333
|
+
if (!mine) return errText3(`\u0432\u043E\u043F\u0440\u043E\u0441 #${question_id} \u043D\u0435 \u0438\u0437 \u043E\u0442\u043A\u0440\u044B\u0442\u044B\u0445 \u0432\u043E\u043F\u0440\u043E\u0441\u043E\u0432 \u0442\u0432\u043E\u0435\u0433\u043E \u043E\u0442\u0440\u044F\u0434\u0430 \u2014 \u043E\u0442\u0432\u0435\u0442\u0438\u0442\u044C \u043D\u0435\u043B\u044C\u0437\u044F.`);
|
|
12834
13334
|
const answered = await store.answer(question_id, text, "hero");
|
|
12835
|
-
if (!answered || answered.status !== "answered") return
|
|
13335
|
+
if (!answered || answered.status !== "answered") return errText3(`\u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043E\u0442\u0432\u0435\u0442\u0438\u0442\u044C \u043D\u0430 \u0432\u043E\u043F\u0440\u043E\u0441 #${question_id}.`);
|
|
12836
13336
|
return { content: [{ type: "text", text: `\u043E\u0442\u0432\u0435\u0442 \u0434\u0440\u043E\u043D\u0443 (\u0432\u043E\u043F\u0440\u043E\u0441 #${question_id}) \u0437\u0430\u043F\u0438\u0441\u0430\u043D, \u0434\u0440\u043E\u043D \u0440\u0430\u0437\u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u0430\u043D` }] };
|
|
12837
13337
|
})
|
|
12838
13338
|
);
|
|
@@ -12906,7 +13406,7 @@ async function runDroneServer(runId) {
|
|
|
12906
13406
|
|
|
12907
13407
|
// src/droneBridge.ts
|
|
12908
13408
|
init_src();
|
|
12909
|
-
import { createHash as
|
|
13409
|
+
import { createHash as createHash4 } from "crypto";
|
|
12910
13410
|
|
|
12911
13411
|
// src/anomalies.ts
|
|
12912
13412
|
init_src();
|
|
@@ -13437,7 +13937,7 @@ function questionKey(runId, text) {
|
|
|
13437
13937
|
return `${runId} ${text.trim().replace(/\s+/g, " ")}`;
|
|
13438
13938
|
}
|
|
13439
13939
|
function noteKey(runId, text) {
|
|
13440
|
-
return `note:${runId}:${
|
|
13940
|
+
return `note:${runId}:${createHash4("sha1").update(questionKey(runId, text)).digest("hex").slice(0, 16)}`;
|
|
13441
13941
|
}
|
|
13442
13942
|
async function syncDroneChannel(opts) {
|
|
13443
13943
|
const result = { imported: [], answered: [], notes: [] };
|
|
@@ -13663,42 +14163,6 @@ init_authState();
|
|
|
13663
14163
|
|
|
13664
14164
|
// src/swarmStatus.ts
|
|
13665
14165
|
init_linkFailure();
|
|
13666
|
-
|
|
13667
|
-
// src/release.ts
|
|
13668
|
-
init_src();
|
|
13669
|
-
init_claudeBin();
|
|
13670
|
-
var SWARM_PACKAGE = SWARM_PACKAGE_NAME;
|
|
13671
|
-
var LATEST_TIMEOUT_MS = 4e3;
|
|
13672
|
-
async function fetchLatestVersion(opts = {}) {
|
|
13673
|
-
const fetchFn = opts.fetchFn ?? fetch;
|
|
13674
|
-
const timeoutMs = opts.timeoutMs ?? LATEST_TIMEOUT_MS;
|
|
13675
|
-
const pkg = opts.pkg ?? SWARM_PACKAGE;
|
|
13676
|
-
const ctrl = new AbortController();
|
|
13677
|
-
const timer = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
13678
|
-
try {
|
|
13679
|
-
const res = await fetchFn(`https://registry.npmjs.org/${pkg}/latest`, {
|
|
13680
|
-
signal: ctrl.signal,
|
|
13681
|
-
headers: { accept: "application/vnd.npm.install-v1+json, application/json" }
|
|
13682
|
-
});
|
|
13683
|
-
if (!res.ok) return null;
|
|
13684
|
-
const body = await res.json();
|
|
13685
|
-
return typeof body.version === "string" && body.version ? body.version : null;
|
|
13686
|
-
} catch {
|
|
13687
|
-
return null;
|
|
13688
|
-
} finally {
|
|
13689
|
-
clearTimeout(timer);
|
|
13690
|
-
}
|
|
13691
|
-
}
|
|
13692
|
-
function isOutdated(current, latest) {
|
|
13693
|
-
if (!latest || !current) return false;
|
|
13694
|
-
return compareVersionDirs(latest, current) > 0;
|
|
13695
|
-
}
|
|
13696
|
-
function outdatedLine(current, latest, updateCommand = "swarm update") {
|
|
13697
|
-
if (!isOutdated(current, latest)) return null;
|
|
13698
|
-
return `\u26A0 \u0432\u0435\u0440\u0441\u0438\u044F \u0440\u043E\u044F \u0443\u0441\u0442\u0430\u0440\u0435\u043B\u0430: \u0443 \u0442\u0435\u0431\u044F v${current}, \u043E\u043F\u0443\u0431\u043B\u0438\u043A\u043E\u0432\u0430\u043D\u0430 v${latest} \u2014 \u043E\u0431\u043D\u043E\u0432\u0438 \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439: ${updateCommand}`;
|
|
13699
|
-
}
|
|
13700
|
-
|
|
13701
|
-
// src/swarmStatus.ts
|
|
13702
14166
|
init_src();
|
|
13703
14167
|
function classifyLinkError(e) {
|
|
13704
14168
|
const { kind, detail } = explainLinkFailure(e);
|
|
@@ -14000,22 +14464,22 @@ function makeMergeGateParts(deps) {
|
|
|
14000
14464
|
}
|
|
14001
14465
|
|
|
14002
14466
|
// src/envGate.ts
|
|
14003
|
-
import { existsSync as
|
|
14004
|
-
import { join as
|
|
14467
|
+
import { existsSync as existsSync16 } from "fs";
|
|
14468
|
+
import { join as join28 } from "path";
|
|
14005
14469
|
import { createConnection } from "net";
|
|
14006
14470
|
import { execa as execa2 } from "execa";
|
|
14007
14471
|
|
|
14008
14472
|
// src/platform.ts
|
|
14009
|
-
import { existsSync as
|
|
14010
|
-
import { delimiter, join as
|
|
14473
|
+
import { existsSync as existsSync15, readFileSync as readFileSync20 } from "fs";
|
|
14474
|
+
import { delimiter, join as join27 } from "path";
|
|
14011
14475
|
function resolveCommandPath(name, env = process.env, platform = process.platform) {
|
|
14012
|
-
if (name.includes("/") || name.includes("\\")) return
|
|
14476
|
+
if (name.includes("/") || name.includes("\\")) return existsSync15(name) ? name : null;
|
|
14013
14477
|
const dirs = (env.PATH ?? env.Path ?? "").split(delimiter).filter(Boolean);
|
|
14014
14478
|
const exts = platform === "win32" ? (env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";").filter(Boolean) : [""];
|
|
14015
14479
|
for (const dir of dirs) {
|
|
14016
14480
|
for (const ext of exts) {
|
|
14017
|
-
const candidate =
|
|
14018
|
-
if (
|
|
14481
|
+
const candidate = join27(dir, name + ext);
|
|
14482
|
+
if (existsSync15(candidate)) return candidate;
|
|
14019
14483
|
}
|
|
14020
14484
|
}
|
|
14021
14485
|
return null;
|
|
@@ -14098,8 +14562,8 @@ var COMPOSE_MARKERS = [
|
|
|
14098
14562
|
"compose.yaml"
|
|
14099
14563
|
];
|
|
14100
14564
|
function detectRequirements(repo, io = {}) {
|
|
14101
|
-
const exists = io.exists ??
|
|
14102
|
-
const marker = COMPOSE_MARKERS.find((f) => exists(
|
|
14565
|
+
const exists = io.exists ?? existsSync16;
|
|
14566
|
+
const marker = COMPOSE_MARKERS.find((f) => exists(join28(repo, f)));
|
|
14103
14567
|
return marker ? [{ kind: "docker", raw: `docker (\u0434\u0435\u0442\u0435\u043A\u0442: ${marker})` }] : [];
|
|
14104
14568
|
}
|
|
14105
14569
|
function resolveRequirements(repo, declared, io = {}) {
|
|
@@ -14233,69 +14697,6 @@ init_src();
|
|
|
14233
14697
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
14234
14698
|
import { basename as basename5 } from "path";
|
|
14235
14699
|
import { serve as serve2 } from "@hono/node-server";
|
|
14236
|
-
|
|
14237
|
-
// package.json
|
|
14238
|
-
var package_default = {
|
|
14239
|
-
name: "@taskless-app/swarm",
|
|
14240
|
-
version: "0.6.31",
|
|
14241
|
-
type: "module",
|
|
14242
|
-
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
14243
|
-
keywords: [
|
|
14244
|
-
"ai-agents",
|
|
14245
|
-
"coding-agents",
|
|
14246
|
-
"swarm",
|
|
14247
|
-
"orchestration",
|
|
14248
|
-
"autonomous-agents",
|
|
14249
|
-
"mcp",
|
|
14250
|
-
"taskless",
|
|
14251
|
-
"cli"
|
|
14252
|
-
],
|
|
14253
|
-
license: "SEE LICENSE IN LICENSE",
|
|
14254
|
-
author: "Ilya Smurov <ilya@smurov.com>",
|
|
14255
|
-
homepage: "https://github.com/ilyasmurov/taskless/tree/main/packages/swarm-daemon#readme",
|
|
14256
|
-
repository: {
|
|
14257
|
-
type: "git",
|
|
14258
|
-
url: "git+https://github.com/ilyasmurov/taskless.git",
|
|
14259
|
-
directory: "packages/swarm-daemon"
|
|
14260
|
-
},
|
|
14261
|
-
engines: {
|
|
14262
|
-
node: ">=22"
|
|
14263
|
-
},
|
|
14264
|
-
bin: {
|
|
14265
|
-
swarm: "dist/cli.js"
|
|
14266
|
-
},
|
|
14267
|
-
files: [
|
|
14268
|
-
"dist",
|
|
14269
|
-
"docs/HERO.md",
|
|
14270
|
-
"hooks",
|
|
14271
|
-
"roles"
|
|
14272
|
-
],
|
|
14273
|
-
scripts: {
|
|
14274
|
-
overmind: 'NODE_OPTIONS="${NODE_OPTIONS:-} --conditions=development" tsx src/overmind.ts',
|
|
14275
|
-
swarm: 'NODE_OPTIONS="${NODE_OPTIONS:-} --conditions=development" tsx src/cli.ts',
|
|
14276
|
-
build: "tsup",
|
|
14277
|
-
start: "node dist/overmind.js",
|
|
14278
|
-
prepublishOnly: "pnpm run build",
|
|
14279
|
-
test: "vitest run && SWARM_TEST_BACKEND=client vitest run src/overmind.test.ts src/overmind.storm.test.ts src/db.storm.test.ts",
|
|
14280
|
-
"test:client": "SWARM_TEST_BACKEND=client vitest run src/overmind.test.ts src/overmind.storm.test.ts src/db.storm.test.ts",
|
|
14281
|
-
"test:watch": "vitest",
|
|
14282
|
-
typecheck: "tsc -p tsconfig.json --noEmit",
|
|
14283
|
-
lint: "echo 'no lint in swarm-daemon yet'"
|
|
14284
|
-
},
|
|
14285
|
-
dependencies: {
|
|
14286
|
-
"@hono/node-server": "^2.0.12",
|
|
14287
|
-
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
14288
|
-
execa: "^10.0.0",
|
|
14289
|
-
zod: "3.25.76"
|
|
14290
|
-
},
|
|
14291
|
-
gitHead: "82950eae7a6ad09be4193708257798c811bb6fae"
|
|
14292
|
-
};
|
|
14293
|
-
|
|
14294
|
-
// src/version.ts
|
|
14295
|
-
var PACKAGE_VERSION = package_default.version;
|
|
14296
|
-
var MIN_NODE_MAJOR = Number(/(\d+)/.exec(package_default.engines?.node ?? "")?.[1] ?? 0);
|
|
14297
|
-
|
|
14298
|
-
// src/web.ts
|
|
14299
14700
|
init_storm();
|
|
14300
14701
|
init_resources();
|
|
14301
14702
|
init_config();
|
|
@@ -15903,34 +16304,16 @@ function closeWebServer(server) {
|
|
|
15903
16304
|
});
|
|
15904
16305
|
}
|
|
15905
16306
|
|
|
15906
|
-
// src/daemonVersion.ts
|
|
15907
|
-
function captureDaemonStart(now = () => /* @__PURE__ */ new Date()) {
|
|
15908
|
-
return { startedAt: now().toISOString(), version: PACKAGE_VERSION };
|
|
15909
|
-
}
|
|
15910
|
-
var DAEMON_CODE_PATHS = ["packages/swarm-daemon", "packages/swarm-core"];
|
|
15911
|
-
async function checkDaemonVersion(start, opts = {}) {
|
|
15912
|
-
const now = opts.now ?? (() => /* @__PURE__ */ new Date());
|
|
15913
|
-
const fetchLatest = opts.latest ?? (() => fetchLatestVersion());
|
|
15914
|
-
const latestVersion = await fetchLatest().catch(() => null);
|
|
15915
|
-
return {
|
|
15916
|
-
startedAt: start.startedAt,
|
|
15917
|
-
version: start.version,
|
|
15918
|
-
latestVersion,
|
|
15919
|
-
stale: isOutdated(start.version, latestVersion),
|
|
15920
|
-
checkedAt: now().toISOString()
|
|
15921
|
-
};
|
|
15922
|
-
}
|
|
15923
|
-
|
|
15924
16307
|
// src/hostCommands.ts
|
|
15925
16308
|
init_src();
|
|
15926
|
-
import { readFileSync as
|
|
15927
|
-
import { join as
|
|
16309
|
+
import { readFileSync as readFileSync21, renameSync as renameSync9, writeFileSync as writeFileSync14 } from "fs";
|
|
16310
|
+
import { join as join29 } from "path";
|
|
15928
16311
|
function hostCommandStatePath(home) {
|
|
15929
|
-
return
|
|
16312
|
+
return join29(home, "host-command.json");
|
|
15930
16313
|
}
|
|
15931
16314
|
function readState(path) {
|
|
15932
16315
|
try {
|
|
15933
|
-
return JSON.parse(
|
|
16316
|
+
return JSON.parse(readFileSync21(path, "utf8"));
|
|
15934
16317
|
} catch {
|
|
15935
16318
|
return {};
|
|
15936
16319
|
}
|
|
@@ -16100,8 +16483,8 @@ async function doRestart(cmd, opts, note) {
|
|
|
16100
16483
|
|
|
16101
16484
|
// src/selfUpdate.ts
|
|
16102
16485
|
init_git();
|
|
16103
|
-
import { existsSync as
|
|
16104
|
-
import { join as
|
|
16486
|
+
import { existsSync as existsSync17 } from "fs";
|
|
16487
|
+
import { join as join30 } from "path";
|
|
16105
16488
|
import { $ as $5 } from "execa";
|
|
16106
16489
|
var DAEMON_PACKAGE = "@taskless-app/swarm";
|
|
16107
16490
|
var defaultRun = async (cmd, args, cwd) => {
|
|
@@ -16114,7 +16497,7 @@ function tail(text, max = 400) {
|
|
|
16114
16497
|
}
|
|
16115
16498
|
async function runSelfUpdate(io) {
|
|
16116
16499
|
const run = io.run ?? defaultRun;
|
|
16117
|
-
const exists = io.exists ??
|
|
16500
|
+
const exists = io.exists ?? existsSync17;
|
|
16118
16501
|
const repo = io.repo;
|
|
16119
16502
|
await io.onStep("fetch", "\u0442\u044F\u043D\u0443 \u0441\u0432\u0435\u0436\u0438\u0439 \u043A\u043E\u0434");
|
|
16120
16503
|
const fetched = await run("git", ["-C", repo, "fetch", "--quiet", "origin"], repo);
|
|
@@ -16143,7 +16526,7 @@ async function runSelfUpdate(io) {
|
|
|
16143
16526
|
};
|
|
16144
16527
|
}
|
|
16145
16528
|
await io.onStep("install", "\u0441\u0442\u0430\u0432\u043B\u044E \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0438");
|
|
16146
|
-
if (!exists(
|
|
16529
|
+
if (!exists(join30(repo, "pnpm-lock.yaml"))) {
|
|
16147
16530
|
return { ok: false, step: "install", message: "\u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0434\u0435\u043C\u043E\u043D\u0430 \u043D\u0435\u0442 pnpm-lock.yaml \u2014 \u043E\u0431\u043D\u043E\u0432\u043B\u044F\u0442\u044C \u0435\u0433\u043E \u044D\u0442\u0438\u043C \u043F\u0443\u0442\u0451\u043C \u043D\u0435 \u0431\u0435\u0440\u0443\u0441\u044C" };
|
|
16148
16531
|
}
|
|
16149
16532
|
const installed2 = await run("pnpm", ["install", "--frozen-lockfile"], repo);
|
|
@@ -16244,7 +16627,7 @@ function orphanNoteText(run, q) {
|
|
|
16244
16627
|
|
|
16245
16628
|
// src/probe.ts
|
|
16246
16629
|
init_executors();
|
|
16247
|
-
import { spawn as
|
|
16630
|
+
import { spawn as spawn3 } from "child_process";
|
|
16248
16631
|
var RESET_RE = /resets[^\n]*/i;
|
|
16249
16632
|
function executorHealthy(opts = {}) {
|
|
16250
16633
|
const executor = opts.executor ?? resolveExecutor();
|
|
@@ -16263,7 +16646,7 @@ function executorHealthy(opts = {}) {
|
|
|
16263
16646
|
}
|
|
16264
16647
|
resolve4(r);
|
|
16265
16648
|
};
|
|
16266
|
-
const child =
|
|
16649
|
+
const child = spawn3(bin, [...executor.probe.args], { stdio: ["ignore", "pipe", "pipe"] });
|
|
16267
16650
|
child.stdout?.on("data", (d) => {
|
|
16268
16651
|
out += d.toString();
|
|
16269
16652
|
});
|
|
@@ -16302,19 +16685,19 @@ async function fetchExternalStorm(url, opts = {}) {
|
|
|
16302
16685
|
}
|
|
16303
16686
|
|
|
16304
16687
|
// src/anomalyState.ts
|
|
16305
|
-
import { existsSync as
|
|
16306
|
-
import { join as
|
|
16688
|
+
import { existsSync as existsSync18, readFileSync as readFileSync22, renameSync as renameSync10, writeFileSync as writeFileSync15 } from "fs";
|
|
16689
|
+
import { join as join31 } from "path";
|
|
16307
16690
|
var KEY_TTL_MS = 7 * 24 * 60 * 6e4;
|
|
16308
16691
|
var MAX_RESOLVED = 200;
|
|
16309
16692
|
function emptyState() {
|
|
16310
16693
|
return { since: {}, lastFiredAt: {}, fires: {}, resolved: [], stopQuiet: null };
|
|
16311
16694
|
}
|
|
16312
16695
|
function anomalyStatePath(home) {
|
|
16313
|
-
return
|
|
16696
|
+
return join31(home, "anomaly-state.json");
|
|
16314
16697
|
}
|
|
16315
16698
|
function loadAnomalyState(home, now = Date.now(), io = {}) {
|
|
16316
|
-
const exists = io.exists ??
|
|
16317
|
-
const readFile2 = io.readFile ?? ((p) =>
|
|
16699
|
+
const exists = io.exists ?? existsSync18;
|
|
16700
|
+
const readFile2 = io.readFile ?? ((p) => readFileSync22(p, "utf8"));
|
|
16318
16701
|
const path = anomalyStatePath(home);
|
|
16319
16702
|
if (!exists(path)) return emptyState();
|
|
16320
16703
|
try {
|
|
@@ -16343,11 +16726,11 @@ function loadAnomalyState(home, now = Date.now(), io = {}) {
|
|
|
16343
16726
|
}
|
|
16344
16727
|
function saveAnomalyState(home, state, io = {}) {
|
|
16345
16728
|
const path = anomalyStatePath(home);
|
|
16346
|
-
const
|
|
16729
|
+
const writeFile4 = io.writeFile ?? ((p, data) => {
|
|
16347
16730
|
writeFileSync15(`${p}.tmp`, data);
|
|
16348
16731
|
renameSync10(`${p}.tmp`, p);
|
|
16349
16732
|
});
|
|
16350
|
-
|
|
16733
|
+
writeFile4(path, JSON.stringify(state, null, 2));
|
|
16351
16734
|
}
|
|
16352
16735
|
var AnomalyStateStore = class {
|
|
16353
16736
|
constructor(home, now = Date.now(), io = {}) {
|
|
@@ -16814,19 +17197,11 @@ var EXITED_CONTINUE_NUDGE = "\u0422\u0432\u043E\u044F \u043F\u0440\u043E\u0448\u
|
|
|
16814
17197
|
var STORM_DEAD_LIMIT = Number(process.env.SWARM_STORM_DEAD_LIMIT) || 3;
|
|
16815
17198
|
var CONFLICT_RESOLVE_COOLDOWN_MS = Number(process.env.SWARM_CONFLICT_COOLDOWN_MS) || 3 * 60 * 1e3;
|
|
16816
17199
|
var RESOLVE_LIMIT = Number(process.env.SWARM_RESOLVE_LIMIT) || 3;
|
|
16817
|
-
var RESOLVE_ESCALATION_MARK = "\u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442 \u043D\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0451\u043D";
|
|
16818
17200
|
var REVIEW_SPAWN_LIMIT = Number(process.env.SWARM_REVIEW_SPAWN_LIMIT) || 3;
|
|
16819
|
-
var REVIEW_ESCALATION_MARK = "\u0440\u0435\u0432\u044C\u044E \u043D\u0435 \u0441\u043C\u043E\u0433\u043B\u043E \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044C\u0441\u044F";
|
|
16820
|
-
var REVIEW_UNRECOVERABLE_MARK = "\u0440\u0435\u0432\u044C\u044E \u043D\u0435 \u043F\u043E\u0434\u043D\u044F\u0442\u044C";
|
|
16821
|
-
function reviewUnrecoverableMark(branch) {
|
|
16822
|
-
return `${REVIEW_UNRECOVERABLE_MARK} (\u0432\u0435\u0442\u043A\u0430 ${branch})`;
|
|
16823
|
-
}
|
|
16824
17201
|
var REVIEW_CYCLE_LIMIT = Number(process.env.SWARM_REVIEW_CYCLE_LIMIT) || 3;
|
|
16825
|
-
var REVIEW_CYCLE_ESCALATION_MARK = "\u0440\u0435-\u0440\u0435\u0432\u044C\u044E \u0437\u0430\u0446\u0438\u043A\u043B\u0438\u043B\u043E\u0441\u044C";
|
|
16826
17202
|
var CI_WAKE_LIMIT = Number(process.env.SWARM_CI_WAKE_LIMIT) || 3;
|
|
16827
17203
|
var CI_WAKE_COOLDOWN_MS = Number(process.env.SWARM_CI_WAKE_COOLDOWN_MS) || 3 * 60 * 1e3;
|
|
16828
17204
|
var CI_CHECK_COOLDOWN_MS = Number(process.env.SWARM_CI_CHECK_COOLDOWN_MS) || 60 * 1e3;
|
|
16829
|
-
var CI_WAKE_ESCALATION_MARK = "CI-\u0430\u0432\u0442\u043E\u043F\u043E\u0447\u0438\u043D\u043A\u0430 \u043D\u0435 \u0441\u043F\u0440\u0430\u0432\u0438\u043B\u0430\u0441\u044C";
|
|
16830
17205
|
var CI_NO_CHECKS_MARK = "\u0443 PR \u043D\u0435\u0442 \u0447\u0435\u043A\u043E\u0432 \u0438 \u043D\u0435 \u0431\u0443\u0434\u0435\u0442";
|
|
16831
17206
|
var CI_WAIT_POLL_MARK = "\u0436\u0434\u0443 \u043F\u0440\u043E\u0432\u0435\u0440\u043E\u043A";
|
|
16832
17207
|
var CI_POLL_LIMIT_MARK = "\u043E\u043F\u0440\u043E\u0441 CI \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D \u043F\u043E \u043F\u043E\u0442\u043E\u043B\u043A\u0443";
|
|
@@ -16842,29 +17217,13 @@ var CI_POLL_WINDOW_MS = Math.max(
|
|
|
16842
17217
|
Number(process.env.SWARM_CI_POLL_WINDOW_MS) || 90 * 60 * 1e3,
|
|
16843
17218
|
CI_WAIT_LIMIT_MS + CI_CHECK_COOLDOWN_MS
|
|
16844
17219
|
);
|
|
16845
|
-
var CI_WAIT_ESCALATION_MARK = "\u043E\u0436\u0438\u0434\u0430\u043D\u0438\u0435 CI \u0437\u0430\u0442\u044F\u043D\u0443\u043B\u043E\u0441\u044C";
|
|
16846
17220
|
var CHANGES_WAKE_COOLDOWN_MS = Number(process.env.SWARM_CHANGES_WAKE_COOLDOWN_MS) || 3 * 60 * 1e3;
|
|
16847
17221
|
var CHANGES_WAKE_LIMIT = Number(process.env.SWARM_CHANGES_WAKE_LIMIT) || 2;
|
|
16848
17222
|
var CHANGES_FRESH_WAKE_AFTER_MS = Number(process.env.SWARM_CHANGES_FRESH_WAKE_AFTER_MS) || SWARM_ANOMALY_PR_NO_REVIEWER_MS;
|
|
16849
17223
|
var CHANGES_WAKE_MARK = "\u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044F \u043F\u043E \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0443 \xAB\u043D\u0443\u0436\u043D\u044B \u043F\u0440\u0430\u0432\u043A\u0438\xBB";
|
|
16850
|
-
var CHANGES_WAKE_ESCALATION_MARK = "\u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044F \u043D\u0435 \u043F\u043E\u0434\u043D\u044F\u043B\u0430\u0441\u044C";
|
|
16851
17224
|
var CHANGES_WAKE_FAILED_MARK = "\u043F\u043E\u0431\u0443\u0434\u043A\u0430 \u0441\u043E\u0440\u0432\u0430\u043B\u0430\u0441\u044C";
|
|
16852
17225
|
var BUDGET_STOP_REASON = "\u043D\u0435 \u0443\u043B\u043E\u0436\u0438\u043B\u0441\u044F \u0432 \u043F\u043E\u0442\u043E\u043B\u043E\u043A \u0442\u0440\u0430\u0442: \u0441\u0435\u0441\u0441\u0438\u044E \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u043B \u0431\u044E\u0434\u0436\u0435\u0442 \u043F\u0440\u043E\u0433\u043E\u043D\u0430";
|
|
16853
17226
|
var NO_AUTO_WAKE_REASONS = [EXITED_NO_DONE_REASON, BUDGET_STOP_REASON, ABORTED_STOP_REASON];
|
|
16854
|
-
var REVIEW_BUDGET_MARK = "\u0440\u0435\u0432\u044C\u044E \u043D\u0435 \u0443\u043B\u043E\u0436\u0438\u043B\u043E\u0441\u044C \u0432 \u043F\u043E\u0442\u043E\u043B\u043E\u043A \u0442\u0440\u0430\u0442";
|
|
16855
|
-
function reviewBudgetMark(cycle) {
|
|
16856
|
-
return `${REVIEW_BUDGET_MARK} (\u043A\u0440\u0443\u0433 ${cycle})`;
|
|
16857
|
-
}
|
|
16858
|
-
var NEEDS_HUMAN_MARKS = [
|
|
16859
|
-
RESOLVE_ESCALATION_MARK,
|
|
16860
|
-
REVIEW_ESCALATION_MARK,
|
|
16861
|
-
REVIEW_UNRECOVERABLE_MARK,
|
|
16862
|
-
REVIEW_CYCLE_ESCALATION_MARK,
|
|
16863
|
-
CI_WAKE_ESCALATION_MARK,
|
|
16864
|
-
CHANGES_WAKE_ESCALATION_MARK,
|
|
16865
|
-
REVIEW_BUDGET_MARK
|
|
16866
|
-
];
|
|
16867
|
-
var SELF_REFERENCE_MARK = "PR \u0442\u0440\u043E\u0433\u0430\u0435\u0442 \u043A\u043E\u0434 \u0441\u0430\u043C\u043E\u0433\u043E \u0434\u0435\u043C\u043E\u043D\u0430";
|
|
16868
17227
|
var REVIEW_NO_VERDICT_REASON = "\u0440\u0435\u0432\u044C\u044E \u0437\u0430\u043A\u043E\u043D\u0447\u0438\u043B\u043E\u0441\u044C \u0431\u0435\u0437 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430: \u043D\u0438 approve, \u043D\u0438 changes \u2014 \u0437\u0430\u0441\u0447\u0438\u0442\u044B\u0432\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E, \u043D\u0443\u0436\u0435\u043D \u043D\u043E\u0432\u044B\u0439 \u0437\u0430\u0445\u043E\u0434 (TASWM-375)";
|
|
16869
17228
|
function verdictCommentMark(createdAt) {
|
|
16870
17229
|
return `\u043A\u043E\u043C\u043C\u0435\u043D\u0442-\u0432\u0435\u0440\u0434\u0438\u043A\u0442@${createdAt}`;
|
|
@@ -17120,11 +17479,17 @@ async function prGateForReviewer(deps, run, prUrl, when) {
|
|
|
17120
17479
|
await finalizeClosedPrReview(deps, run, `PR \u0437\u0430\u043A\u0440\u044B\u0442 \u0431\u0435\u0437 \u043C\u0435\u0440\u0436\u0430 (${when}) \u2014 \u0440\u0435\u0432\u044C\u044E \u043E\u0442\u043C\u0435\u043D\u0435\u043D\u043E`);
|
|
17121
17480
|
narrate(deps, `PR ${droneLabel(run)} \u0437\u0430\u043A\u0440\u044B\u0442 \u0431\u0435\u0437 \u043C\u0435\u0440\u0436\u0430, ${when} \u2014 \u0440\u0435\u0432\u044C\u044E \u043D\u0435 \u043D\u0443\u0436\u043D\u043E, \u043F\u0440\u043E\u0433\u043E\u043D \u0437\u0430\u043A\u0440\u044B\u0442.`);
|
|
17122
17481
|
return { kind: "finalized" };
|
|
17123
|
-
case "unknown":
|
|
17482
|
+
case "unknown": {
|
|
17483
|
+
const notPr = !isGithubPrUrl(prUrl);
|
|
17124
17484
|
if (!deps.db.events(run.id).some((e) => (e.text ?? "").includes(REVIEW_PR_UNKNOWN_MARK))) {
|
|
17125
|
-
deps.db.addEvent(
|
|
17485
|
+
deps.db.addEvent(
|
|
17486
|
+
run.id,
|
|
17487
|
+
"step",
|
|
17488
|
+
notPr ? `${REVIEW_PR_UNKNOWN_MARK}: \u0432 pr_url \u043D\u0435 GitHub-PR (${prUrl}) \u2014 \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u043D\u0435 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u0435\u043C. \u0421\u0430\u043C\u043E \u043D\u0435 \u043F\u043E\u0447\u0438\u043D\u0438\u0442\u0441\u044F: \u0434\u0440\u043E\u043D \u0441\u0434\u0430\u043B \u0441\u0441\u044B\u043B\u043A\u043E\u0439 \u043D\u0435 \u043D\u0430 PR.` : `${REVIEW_PR_UNKNOWN_MARK}: gh \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u043F\u0440\u043E PR (\u0441\u0435\u0442\u044C/\u043B\u0438\u043C\u0438\u0442) \u2014 \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u043D\u0435 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u0435\u043C, \u0441\u043F\u0440\u043E\u0441\u0438\u043C \u043D\u0430 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u043C \u0442\u0438\u043A\u0435`
|
|
17489
|
+
);
|
|
17126
17490
|
}
|
|
17127
17491
|
return { kind: "wait" };
|
|
17492
|
+
}
|
|
17128
17493
|
default:
|
|
17129
17494
|
if (st.conflicting || st.isDraft) {
|
|
17130
17495
|
deps.db.addEvent(run.id, "step", `PR \u0441\u0442\u0430\u043B \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u0443\u044E\u0449\u0438\u043C/\u0447\u0435\u0440\u043D\u043E\u0432\u0438\u043A\u043E\u043C, ${when} \u2014 \u0441\u043F\u0430\u0432\u043D \u043E\u0442\u043B\u043E\u0436\u0435\u043D \u0434\u043E \u0441\u043B\u0435\u0434. \u0442\u0438\u043A\u0430`);
|
|
@@ -17238,17 +17603,11 @@ function relinkUnboundReviewers(deps) {
|
|
|
17238
17603
|
narrate(deps, `${droneLabel(rv)} \u0440\u0430\u0431\u043E\u0442\u0430\u043B \u0431\u0435\u0437 \u043F\u0440\u0438\u0432\u044F\u0437\u043A\u0438 \u043A \u043F\u0440\u043E\u0433\u043E\u043D\u0443 \u2014 \u0441\u0432\u044F\u0437\u0430\u043B \u043F\u043E PR-\u0441\u0441\u044B\u043B\u043A\u0435, \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0442\u0435\u043F\u0435\u0440\u044C \u043B\u044F\u0436\u0435\u0442 \u0432 ${parent.task_slug}, \u0430 \u043D\u0435 \u0442\u043E\u043B\u044C\u043A\u043E \u0432 \u043A\u043E\u043C\u043C\u0435\u043D\u0442 GitHub.`);
|
|
17239
17604
|
}
|
|
17240
17605
|
}
|
|
17606
|
+
var ESCALATION_LIMITS = { reviewSpawn: REVIEW_SPAWN_LIMIT, reviewCycle: REVIEW_CYCLE_LIMIT };
|
|
17241
17607
|
function deliberateReviewStop(db, run) {
|
|
17242
17608
|
const texts = db.events(run.id).map((e) => e.text ?? "");
|
|
17243
|
-
const
|
|
17244
|
-
|
|
17245
|
-
return `PR \u0442\u0440\u043E\u0433\u0430\u0435\u0442 \u043A\u043E\u0434 \u0441\u0430\u043C\u043E\u0433\u043E \u0434\u0435\u043C\u043E\u043D\u0430 (${DAEMON_CODE_PATHS.join(", ")}) \u2014 \u0430\u0432\u0442\u043E-\u0440\u0435\u0432\u044C\u044E \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043E \u043D\u0430\u043C\u0435\u0440\u0435\u043D\u043D\u043E (TASWM-270): \u0440\u0430\u0431\u043E\u0442\u0430\u044E\u0449\u0438\u0439 \u0434\u0435\u043C\u043E\u043D \u043A\u0440\u0443\u0442\u0438\u0442\u0441\u044F \u043D\u0430 \u0441\u0442\u0430\u0440\u043E\u043C \u043A\u043E\u0434\u0435 \u0438 \u0441\u0432\u043E\u044E \u0436\u0435 \u043F\u0440\u0430\u0432\u043A\u0443 \u043D\u0435 \u0430\u0442\u0442\u0435\u0441\u0442\u0443\u0435\u0442. \u041D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A`;
|
|
17246
|
-
}
|
|
17247
|
-
if (has(REVIEW_ESCALATION_MARK)) return `\u043F\u043E\u043F\u044B\u0442\u043A\u0438 \u0430\u0432\u0442\u043E-\u0440\u0435\u0432\u044C\u044E \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D\u044B (${REVIEW_SPAWN_LIMIT}) \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A`;
|
|
17248
|
-
if (has(REVIEW_CYCLE_ESCALATION_MARK)) return `\u043F\u043E\u0442\u043E\u043B\u043E\u043A \u0446\u0438\u043A\u043B\u043E\u0432 \u0440\u0435-\u0440\u0435\u0432\u044C\u044E (${REVIEW_CYCLE_LIMIT}) \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A`;
|
|
17249
|
-
if (has(reviewBudgetMark(run.review_cycle ?? 0))) return "\u0440\u0435\u0432\u044C\u044E \u044D\u0442\u043E\u0433\u043E \u043A\u0440\u0443\u0433\u0430 \u0441\u0433\u043E\u0440\u0435\u043B\u043E \u043D\u0430 \u043F\u043E\u0442\u043E\u043B\u043A\u0435 \u0442\u0440\u0430\u0442 \u2014 \u043F\u043E\u0432\u0442\u043E\u0440 \u0443\u043F\u0440\u0451\u0442\u0441\u044F \u0432 \u0442\u043E\u0442 \u0436\u0435, \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A";
|
|
17250
|
-
if (has(reviewUnrecoverableMark(run.branch || run.task_slug))) return "\u0432\u0435\u0442\u043A\u0438 PR \u043D\u0435\u0442 \u043D\u0430 origin \u2014 \u043F\u043E\u0432\u0442\u043E\u0440\u043E\u043C \u043D\u0435 \u043B\u0435\u0447\u0438\u0442\u0441\u044F, \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A";
|
|
17251
|
-
return null;
|
|
17609
|
+
const hit = findEscalation(texts, run, "reviewer");
|
|
17610
|
+
return hit?.stopReason?.(run, ESCALATION_LIMITS) ?? null;
|
|
17252
17611
|
}
|
|
17253
17612
|
function noteReviewBlocked(deps, run, reason) {
|
|
17254
17613
|
if (!shouldNoteReason(deps.db, run.id, reason)) return;
|
|
@@ -17322,7 +17681,6 @@ async function closeOrphanedReviewers(deps) {
|
|
|
17322
17681
|
}
|
|
17323
17682
|
}
|
|
17324
17683
|
var VERIFY_SPAWN_LIMIT = Number(process.env.SWARM_VERIFY_SPAWN_LIMIT) || 2;
|
|
17325
|
-
var VERIFY_ESCALATION_MARK = "\u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044F: \u043F\u043E\u043F\u044B\u0442\u043A\u0438 \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D\u044B";
|
|
17326
17684
|
function countActiveVerifiers(db) {
|
|
17327
17685
|
return db.listRuns().filter((r) => r.kind === "verifier" && ACTIVE_DRONE_STATUSES.has(r.status)).length;
|
|
17328
17686
|
}
|
|
@@ -17496,10 +17854,10 @@ ${task.description}` : `\u0417\u0430\u0434\u0430\u0447\u0430 ${run.task_slug} (\
|
|
|
17496
17854
|
}
|
|
17497
17855
|
return parts.join("\n");
|
|
17498
17856
|
}
|
|
17499
|
-
var HERO_CHARTER_PATH =
|
|
17857
|
+
var HERO_CHARTER_PATH = join32(dirname5(THIS_FILE), "..", "docs", "HERO.md");
|
|
17500
17858
|
function buildHeroPrompt(task, run, taskContext, attachmentsBlock) {
|
|
17501
17859
|
const name = run.name || `#${run.id}`;
|
|
17502
|
-
const charter =
|
|
17860
|
+
const charter = readFileSync23(HERO_CHARTER_PATH, "utf8");
|
|
17503
17861
|
const head = task ? `\u042D\u041F\u0418\u041A ${run.task_slug}: ${task.title}
|
|
17504
17862
|
|
|
17505
17863
|
${task.description}` : `\u042D\u041F\u0418\u041A ${run.task_slug} (\u0442\u0435\u043B\u043E \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u043E \u2014 \u0440\u0430\u0431\u043E\u0442\u0430\u0439 \u043F\u043E slug \u0438 \u0437\u0434\u0440\u0430\u0432\u043E\u043C\u0443 \u0441\u043C\u044B\u0441\u043B\u0443).`;
|
|
@@ -17803,17 +18161,11 @@ async function wakeRun(deps, runId) {
|
|
|
17803
18161
|
if (!run) return { ok: false, why: `\u043F\u0440\u043E\u0433\u043E\u043D #${runId} \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D \u0432 \u0437\u0435\u0440\u043A\u0430\u043B\u0435 \u0440\u043E\u044F` };
|
|
17804
18162
|
if (run.pid != null && deps.isAlive(run.pid)) return { ok: false, why: `\u043F\u0440\u043E\u0433\u043E\u043D #${runId} \u0443\u0436\u0435 \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 (pid ${run.pid}) \u2014 \u0431\u0443\u0434\u0438\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E` };
|
|
17805
18163
|
if (isTerminalStatus(run.status)) return { ok: false, why: `\u043F\u0440\u043E\u0433\u043E\u043D #${runId} \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B\u0435\u043D (${run.status}) \u2014 \u0431\u0443\u0434\u0438\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E` };
|
|
17806
|
-
const
|
|
17807
|
-
(e) => [RESOLVE_ESCALATION_MARK, CI_WAKE_ESCALATION_MARK, VERIFY_ESCALATION_MARK].some(
|
|
17808
|
-
(m) => (e.text ?? "").includes(m)
|
|
17809
|
-
)
|
|
17810
|
-
);
|
|
17811
|
-
const changesEscalated = deps.db.events(run.id).some((e) => (e.text ?? "").includes(`${CHANGES_WAKE_ESCALATION_MARK} (\u043A\u0440\u0443\u0433 ${run.review_cycle ?? 0})`));
|
|
17812
|
-
const stoppedForHuman = deliberateReviewStop(deps.db, run) != null || changesEscalated || authorSideStopped;
|
|
18164
|
+
const stoppedForHuman = deps.db.events(run.id).some((e) => isEscalationText(e.text ?? "", run, "author"));
|
|
17813
18165
|
if (run.status === "review" && run.pr_url && !needsFixIteration(run) && !stoppedForHuman) {
|
|
17814
18166
|
return { ok: false, why: `\u043F\u0440\u043E\u0433\u043E\u043D #${runId} \u0443\u0436\u0435 \u0441\u0434\u0430\u043D \u043D\u0430 \u0440\u0435\u0432\u044C\u044E (${run.pr_url}) \u2014 \u0431\u0443\u0434\u0438\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E; \u043F\u0440\u0430\u0432\u043A\u0438 \u043F\u043E\u0434\u043D\u0438\u043C\u0435\u0442 \u0440\u043E\u0439 \u043F\u043E \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0443` };
|
|
17815
18167
|
}
|
|
17816
|
-
const worktreeExists = deps.worktreeExists ??
|
|
18168
|
+
const worktreeExists = deps.worktreeExists ?? existsSync19;
|
|
17817
18169
|
const hasTranscript = deps.hasTranscript ?? executorHasSession;
|
|
17818
18170
|
const wt = run.worktree_path;
|
|
17819
18171
|
const canContinue = !!wt && worktreeExists(wt) && hasTranscript(wt);
|
|
@@ -17907,11 +18259,25 @@ async function handleCi(deps, run, now, worktreeExists, hasTranscript, droneCap)
|
|
|
17907
18259
|
}
|
|
17908
18260
|
if (ci?.bucket === "pass" && await wakeReviewerOnCiReady(deps, run, { kind: "pass", ci })) return;
|
|
17909
18261
|
const counts = ci && ci.total > 0 ? ` (${ci.pass}/${ci.total})` : "";
|
|
17910
|
-
const
|
|
17911
|
-
const
|
|
18262
|
+
const why = ci == null ? "\u043E\u043F\u0440\u043E\u0441 CI \u0441\u043E\u0440\u0432\u0430\u043B\u0441\u044F \u0446\u0435\u043B\u0438\u043A\u043E\u043C (checks \u0431\u0440\u043E\u0441\u0438\u043B) \u2014 \u0436\u0434\u0443 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0433\u043E \u043E\u043F\u0440\u043E\u0441\u0430" : ciUnavailableNote(ci);
|
|
18263
|
+
const note = ci == null || ci.bucket === "unavailable" ? why : ci.bucket === "pending" ? "\u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u0435\u0449\u0451 \u0438\u0434\u0443\u0442" : ci.bucket === "none" ? "\u0447\u0435\u043A-\u0440\u0430\u043D\u043E\u0432 \u0435\u0449\u0451 \u043D\u0435 \u0432\u0438\u0434\u043D\u043E, workflow \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0435\u0441\u0442\u044C \u2014 \u0436\u0434\u0443" : "\u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u0433\u043E\u0442\u043E\u0432\u044B, \u0431\u0443\u0434\u0438\u0442\u044C \u043D\u0435\u043A\u043E\u0433\u043E";
|
|
18264
|
+
const waiting = ci == null || ci.bucket === "unavailable" || ci.bucket === "pending" || ci.bucket === "none";
|
|
17912
18265
|
const text = `CI ${ci?.bucket ?? "\u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D"}${counts} \u2014 ${note}`;
|
|
17913
18266
|
deps.db.addEvent(run.id, "ci_checked", waiting ? `${text} [${CI_WAIT_POLL_MARK}]` : text);
|
|
17914
18267
|
}
|
|
18268
|
+
function ciUnavailableNote(ci) {
|
|
18269
|
+
const detail = ci.detail ? ` \u2014 ${ci.detail}` : "";
|
|
18270
|
+
switch (ci.reason) {
|
|
18271
|
+
case "timeout":
|
|
18272
|
+
return `\u0422\u0410\u0419\u041C\u0410\u0423\u0422: gh pr checks \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u0437\u0430 ${Math.round(GH_CALL_TIMEOUT_MS / 1e3)} \u0441 \u0438 \u043F\u0440\u0435\u0440\u0432\u0430\u043D${detail}. \u0421\u0435\u0442\u044C \u0443 \u043F\u0440\u043E\u0446\u0435\u0441\u0441\u0430 \u0434\u0435\u043C\u043E\u043D\u0430 \u043C\u0435\u0434\u043B\u0435\u043D\u043D\u0435\u0435, \u0447\u0435\u043C \u0443 \u0448\u0435\u043B\u043B\u0430 (\u043F\u0440\u043E\u043A\u0441\u0438/DNS) \u2014 \u0436\u0434\u0443 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0433\u043E \u043E\u043F\u0440\u043E\u0441\u0430`;
|
|
18273
|
+
case "crashed":
|
|
18274
|
+
return `gh \u041D\u0415 \u0417\u0410\u041F\u0423\u0421\u0422\u0418\u041B\u0421\u042F${detail}. \u042D\u0442\u043E \u043D\u0435 \u0441\u0435\u0442\u044C: \u0443 \u043F\u0440\u043E\u0446\u0435\u0441\u0441\u0430 \u0434\u0435\u043C\u043E\u043D\u0430 \u043D\u0435\u0442 gh \u0432 PATH \u2014 \u043E\u0436\u0438\u0434\u0430\u043D\u0438\u0435\u043C \u043D\u0435 \u043B\u0435\u0447\u0438\u0442\u0441\u044F, \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A`;
|
|
18275
|
+
case "unreadable":
|
|
18276
|
+
return `\u041D\u0415\u0427\u0418\u0422\u0410\u0415\u041C\u042B\u0419 \u041E\u0422\u0412\u0415\u0422 gh${detail}. \u0412\u044B\u0432\u043E\u0434 \u0435\u0441\u0442\u044C, \u043D\u043E \u044D\u0442\u043E \u043D\u0435 \u0441\u043F\u0438\u0441\u043E\u043A \u0447\u0435\u043A\u043E\u0432 \u2014 \u0441\u043C\u043E\u0442\u0440\u0435\u0442\u044C, \u0447\u0442\u043E \u0438\u043C\u0435\u043D\u043D\u043E \u043F\u0435\u0447\u0430\u0442\u0430\u0435\u0442 gh, \u0430 \u043D\u0435 \u0436\u0434\u0430\u0442\u044C`;
|
|
18277
|
+
default:
|
|
18278
|
+
return `\u041F\u0423\u0421\u0422\u041E\u0419 \u041E\u0422\u0412\u0415\u0422: gh \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0441\u044F, \u043D\u0435 \u0441\u043A\u0430\u0437\u0430\u0432 \u043D\u0438\u0447\u0435\u0433\u043E${detail}. \u0421\u043C\u043E\u0442\u0440\u0435\u0442\u044C stderr \u0432\u044B\u0437\u043E\u0432\u0430 \u0432 \u043B\u043E\u0433\u0435 \u0434\u0435\u043C\u043E\u043D\u0430 \u2014 \u0442\u0430\u043C DNS/\u043F\u0440\u043E\u043A\u0441\u0438/\u043B\u0438\u043C\u0438\u0442`;
|
|
18279
|
+
}
|
|
18280
|
+
}
|
|
17915
18281
|
function repoHasCi(deps, run) {
|
|
17916
18282
|
if (deps.repoHasCi) return deps.repoHasCi(run);
|
|
17917
18283
|
return repoHasCiWorkflows(run.worktree_path, run.repo);
|
|
@@ -17919,7 +18285,7 @@ function repoHasCi(deps, run) {
|
|
|
17919
18285
|
function repoHasCiWorkflows(worktreePath, repo) {
|
|
17920
18286
|
for (const base3 of [worktreePath, repo]) {
|
|
17921
18287
|
if (!base3) continue;
|
|
17922
|
-
const dir =
|
|
18288
|
+
const dir = join32(base3, ".github", "workflows");
|
|
17923
18289
|
try {
|
|
17924
18290
|
if (readdirSync13(dir).some((f) => f.endsWith(".yml") || f.endsWith(".yaml"))) return true;
|
|
17925
18291
|
} catch {
|
|
@@ -18105,7 +18471,7 @@ async function wakeReviewerOnCiReady(deps, run, over) {
|
|
|
18105
18471
|
return true;
|
|
18106
18472
|
}
|
|
18107
18473
|
function worktreeAndTranscriptAlive(deps, worktree) {
|
|
18108
|
-
const exists = deps.worktreeExists ??
|
|
18474
|
+
const exists = deps.worktreeExists ?? existsSync19;
|
|
18109
18475
|
const transcript = deps.hasTranscript ?? executorHasSession;
|
|
18110
18476
|
return exists(worktree) && transcript(worktree);
|
|
18111
18477
|
}
|
|
@@ -18614,7 +18980,7 @@ async function resumeWatchdogParked(deps, droneCap) {
|
|
|
18614
18980
|
}
|
|
18615
18981
|
if (!candidate) return;
|
|
18616
18982
|
if (candidate.pid != null && deps.isAlive(candidate.pid)) return;
|
|
18617
|
-
const worktreeExists = deps.worktreeExists ??
|
|
18983
|
+
const worktreeExists = deps.worktreeExists ?? existsSync19;
|
|
18618
18984
|
const hasTranscript = deps.hasTranscript ?? executorHasSession;
|
|
18619
18985
|
const wt = candidate.worktree_path;
|
|
18620
18986
|
const canContinue = !!wt && worktreeExists(wt) && hasTranscript(wt);
|
|
@@ -18783,7 +19149,7 @@ async function manageRelief(deps, now, droneCap, stormActive) {
|
|
|
18783
19149
|
if (resumeCandidate.pid != null && deps.isAlive(resumeCandidate.pid)) return;
|
|
18784
19150
|
if (!deps.respawn) return;
|
|
18785
19151
|
const wt = resumeCandidate.worktree_path;
|
|
18786
|
-
const worktreeExists = deps.worktreeExists ??
|
|
19152
|
+
const worktreeExists = deps.worktreeExists ?? existsSync19;
|
|
18787
19153
|
const hasTranscript = deps.hasTranscript ?? executorHasSession;
|
|
18788
19154
|
const canContinue = !!wt && worktreeExists(wt) && hasTranscript(wt);
|
|
18789
19155
|
const mode = canContinue ? "continue" : "fresh";
|
|
@@ -19043,7 +19409,7 @@ async function handleBudgetExhausted(deps, run, ctx) {
|
|
|
19043
19409
|
function buildEscalationCandidates(db) {
|
|
19044
19410
|
const candidates = [];
|
|
19045
19411
|
for (const run of db.listRuns("review")) {
|
|
19046
|
-
const hit = db.events(run.id).findLast((e) =>
|
|
19412
|
+
const hit = db.events(run.id).findLast((e) => isEscalationText(e.text ?? "", run, "supervision"));
|
|
19047
19413
|
if (!hit) continue;
|
|
19048
19414
|
candidates.push({
|
|
19049
19415
|
key: `needs_human:${run.id}`,
|
|
@@ -19061,7 +19427,7 @@ function buildEscalationCandidates(db) {
|
|
|
19061
19427
|
}
|
|
19062
19428
|
async function tickOnce(deps) {
|
|
19063
19429
|
const now = deps.now ? deps.now() : Date.now();
|
|
19064
|
-
const worktreeExists = deps.worktreeExists ??
|
|
19430
|
+
const worktreeExists = deps.worktreeExists ?? existsSync19;
|
|
19065
19431
|
const hasTranscript = deps.hasTranscript ?? executorHasSession;
|
|
19066
19432
|
deps.sampleHost?.();
|
|
19067
19433
|
if (deps.rehydrate) {
|
|
@@ -19107,12 +19473,12 @@ async function tickOnce(deps) {
|
|
|
19107
19473
|
}
|
|
19108
19474
|
}
|
|
19109
19475
|
if (deps.repoMap) {
|
|
19110
|
-
const
|
|
19111
|
-
const sinceLast =
|
|
19112
|
-
if (sinceLast >=
|
|
19113
|
-
|
|
19476
|
+
const rm4 = deps.repoMap;
|
|
19477
|
+
const sinceLast = rm4.lastAt == null ? Infinity : now - rm4.lastAt;
|
|
19478
|
+
if (sinceLast >= rm4.intervalMs) {
|
|
19479
|
+
rm4.lastAt = now;
|
|
19114
19480
|
try {
|
|
19115
|
-
await
|
|
19481
|
+
await rm4.run();
|
|
19116
19482
|
} catch (e) {
|
|
19117
19483
|
console.error("[overmind] \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0435 \u043A\u0430\u0440\u0442\u044B \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0435\u0432 \u0441\u043E\u0440\u0432\u0430\u043B\u043E\u0441\u044C:", e);
|
|
19118
19484
|
}
|
|
@@ -19578,8 +19944,13 @@ async function tickOnce(deps) {
|
|
|
19578
19944
|
const ciWatchOff = parent != null && ciWatchClosed(deps.db, parent);
|
|
19579
19945
|
const ci = ciWatchOff ? null : await deps.ci.checks(run.pr_url).catch(() => null);
|
|
19580
19946
|
const noCiRepo = ci?.bucket === "none" && !repoHasCi(deps, run);
|
|
19581
|
-
if (!ciWatchOff && !noCiRepo && (ci == null || ci.bucket === "pending" || ci.bucket === "none" || ci.bucket === "denied")) {
|
|
19582
|
-
const what = ci == null ? "gh \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u043F\u0440\u043E \u0447\u0435\u043A\u0438 \u2014 \u0436\u0434\u0443, \u043F\u043E\u043A\u0430 \u043F\u0440\u043E\u044F\u0441\u043D\u0438\u0442\u0441\u044F" : ci.bucket === "none" ? "\u0447\u0435\u043A\u043E\u0432 \u043D\u0430 PR \u043F\u043E\u043A\u0430 \u043D\u0435 \u0432\u0438\u0434\u043D\u043E \u2014 \u043D\u0438 \u043E\u0434\u043D\u043E\u0433\u043E \u0447\u0435\u043A-\u0440\u0430\u043D\u0430, \u043D\u043E workflow \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0435\u0441\u0442\u044C" : ci.bucket === "denied" ? `\u0447\u0435\u043A\u0438 \u0440\u043E\u044E \u043D\u0435 \u0432\u0438\u0434\u043D\u044B: GitHub \u043E\u0442\u043A\u0430\u0437\u0430\u043B \u043F\u043E \u043F\u0440\u0430\u0432\u0430\u043C (403)${ci.detail ? ` \u2014 ${ci.detail}` : ""}` :
|
|
19947
|
+
if (!ciWatchOff && !noCiRepo && (ci == null || ci.bucket === "pending" || ci.bucket === "none" || ci.bucket === "denied" || ci.bucket === "unavailable")) {
|
|
19948
|
+
const what = ci == null ? "gh \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u043F\u0440\u043E \u0447\u0435\u043A\u0438 \u2014 \u0436\u0434\u0443, \u043F\u043E\u043A\u0430 \u043F\u0440\u043E\u044F\u0441\u043D\u0438\u0442\u0441\u044F" : ci.bucket === "none" ? "\u0447\u0435\u043A\u043E\u0432 \u043D\u0430 PR \u043F\u043E\u043A\u0430 \u043D\u0435 \u0432\u0438\u0434\u043D\u043E \u2014 \u043D\u0438 \u043E\u0434\u043D\u043E\u0433\u043E \u0447\u0435\u043A-\u0440\u0430\u043D\u0430, \u043D\u043E workflow \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0435\u0441\u0442\u044C" : ci.bucket === "denied" ? `\u0447\u0435\u043A\u0438 \u0440\u043E\u044E \u043D\u0435 \u0432\u0438\u0434\u043D\u044B: GitHub \u043E\u0442\u043A\u0430\u0437\u0430\u043B \u043F\u043E \u043F\u0440\u0430\u0432\u0430\u043C (403)${ci.detail ? ` \u2014 ${ci.detail}` : ""}` : (
|
|
19949
|
+
// TASWM-497: и здесь причина СЛОВАМИ, а не общее «gh не ответил». Тот же словарь,
|
|
19950
|
+
// что в ленте опроса (callFailureWord), чтобы одно и то же состояние в журнале
|
|
19951
|
+
// прогона и в событии парковки не называлось двумя разными способами.
|
|
19952
|
+
ci.bucket === "unavailable" ? `\u0441\u043F\u0440\u043E\u0441\u0438\u0442\u044C \u0447\u0435\u043A\u0438 \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C: gh ${callFailureWord({ reason: ci.reason ?? "empty", detail: "" })}${ci.detail ? ` \u2014 ${ci.detail}` : ""}` : `CI ${ci.pass}/${ci.total}`
|
|
19953
|
+
);
|
|
19583
19954
|
deps.db.updateRun(run.id, { status: "paused", stop_reason: AWAITING_CI_REASON });
|
|
19584
19955
|
deps.db.addEvent(run.id, "paused", `${AWAITING_CI_REASON} (${what}) \u2014 \u043D\u0435 \u0441\u043C\u0435\u0440\u0442\u044C: \u0440\u0430\u0437\u0431\u0443\u0436\u0443 \u0441\u0430\u043C, \u043A\u043E\u0433\u0434\u0430 \u0447\u0435\u043A\u0438 \u0434\u043E\u0439\u0434\u0443\u0442 \u0434\u043E \u043A\u043E\u043D\u0446\u0430`);
|
|
19585
19956
|
narrate(deps, `${droneLabel(run)} \u0443\u0448\u0451\u043B \u0436\u0434\u0430\u0442\u044C CI (${what}) \u2014 \u043D\u0435 \u0445\u043E\u0440\u043E\u043D\u044E, \u0440\u0430\u0437\u0431\u0443\u0436\u0443 \u043F\u043E \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438 \u043F\u0440\u043E\u0432\u0435\u0440\u043E\u043A.`);
|
|
@@ -19852,7 +20223,7 @@ async function resolveConflict(deps, reviewRun) {
|
|
|
19852
20223
|
model: reviewRun.model,
|
|
19853
20224
|
review_model: reviewRun.review_model
|
|
19854
20225
|
});
|
|
19855
|
-
const wt =
|
|
20226
|
+
const wt = join32(deps.worktreesDir, basename6(repo), `${reviewRun.task_slug}-resolve-${resolver.id}`);
|
|
19856
20227
|
try {
|
|
19857
20228
|
await deps.git.prepareDetachedWorktree(repo, branch, wt);
|
|
19858
20229
|
await deps.git.setWorktreeIdentity(wt, resolver.name);
|
|
@@ -19931,7 +20302,7 @@ async function spawnReviewer(deps, reviewRun, files) {
|
|
|
19931
20302
|
// (своей у ревьюера нет, TASWM-204), и коммент с вердиктом должен уехать именно туда.
|
|
19932
20303
|
workspace_id: reviewRun.workspace_id
|
|
19933
20304
|
});
|
|
19934
|
-
const wt =
|
|
20305
|
+
const wt = join32(deps.worktreesDir, basename6(repo), `${reviewRun.task_slug}-review-${reviewer.id}`);
|
|
19935
20306
|
try {
|
|
19936
20307
|
await deps.git.prepareDetachedWorktree(repo, branch, wt);
|
|
19937
20308
|
await deps.git.setWorktreeIdentity(wt, reviewer.name);
|
|
@@ -20012,7 +20383,7 @@ async function spawnVerifier(deps, verifiedRun, files) {
|
|
|
20012
20383
|
// штамп цикла: фикс-итерацию проверяет свежий верификатор
|
|
20013
20384
|
workspace_id: verifiedRun.workspace_id
|
|
20014
20385
|
});
|
|
20015
|
-
const wt =
|
|
20386
|
+
const wt = join32(deps.worktreesDir, basename6(repo), `${verifiedRun.task_slug}-verify-${verifier.id}`);
|
|
20016
20387
|
try {
|
|
20017
20388
|
await deps.git.prepareDetachedWorktree(repo, branch, wt);
|
|
20018
20389
|
await deps.git.setWorktreeIdentity(wt, verifier.name);
|
|
@@ -20058,7 +20429,7 @@ async function respawnRun(deps, run, mode, nudge) {
|
|
|
20058
20429
|
]);
|
|
20059
20430
|
const base3 = effectiveBaseBranch(run.base_branch, projectBase);
|
|
20060
20431
|
const runToSpawn = base3 && run.base_branch !== base3 ? deps.db.updateRun(run.id, { base_branch: base3 }) ?? run : run;
|
|
20061
|
-
if (!
|
|
20432
|
+
if (!existsSync19(wt)) {
|
|
20062
20433
|
const branch = runToSpawn.branch || runToSpawn.task_slug;
|
|
20063
20434
|
const source = await addWorktree(repo, wt, branch, {
|
|
20064
20435
|
...base3 ? { base: await remoteBaseRef(repo, base3) } : {},
|
|
@@ -20085,7 +20456,7 @@ async function respawnRun(deps, run, mode, nudge) {
|
|
|
20085
20456
|
deps.db.updateRun(run.id, { status: "running", pid });
|
|
20086
20457
|
}
|
|
20087
20458
|
async function syncCrashedOnStartup(deps) {
|
|
20088
|
-
const worktreeExists = deps.worktreeExists ??
|
|
20459
|
+
const worktreeExists = deps.worktreeExists ?? existsSync19;
|
|
20089
20460
|
let orphaned = 0;
|
|
20090
20461
|
let merged = 0;
|
|
20091
20462
|
let requeued = 0;
|
|
@@ -20239,7 +20610,7 @@ async function migrateWorktreesAtStartup() {
|
|
|
20239
20610
|
legacyDir: WORKTREES_LEGACY_DIR,
|
|
20240
20611
|
targetDir: WORKTREES_DIR,
|
|
20241
20612
|
envOverride: process.env.SWARM_WORKTREES || "",
|
|
20242
|
-
exists: (d) =>
|
|
20613
|
+
exists: (d) => existsSync19(d),
|
|
20243
20614
|
isNonEmptyDir: (d) => {
|
|
20244
20615
|
try {
|
|
20245
20616
|
return readdirSync13(d).length > 0;
|
|
@@ -20254,7 +20625,7 @@ async function migrateWorktreesAtStartup() {
|
|
|
20254
20625
|
});
|
|
20255
20626
|
} catch (e) {
|
|
20256
20627
|
console.error("[migrate] \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u044F worktrees \u0441\u043E\u0440\u0432\u0430\u043B\u0430\u0441\u044C \u2014 \u0444\u043E\u043B\u0431\u044D\u043A:", e);
|
|
20257
|
-
return
|
|
20628
|
+
return existsSync19(WORKTREES_DIR) ? WORKTREES_DIR : WORKTREES_LEGACY_DIR;
|
|
20258
20629
|
}
|
|
20259
20630
|
}
|
|
20260
20631
|
async function reportWorkspaceAccess(tl, db) {
|
|
@@ -20727,7 +21098,7 @@ async function runDaemon(opts) {
|
|
|
20727
21098
|
baseSync: (url) => prBaseSync(url),
|
|
20728
21099
|
checks: async (url) => {
|
|
20729
21100
|
const ci = await prCiStatus(url);
|
|
20730
|
-
if (ci
|
|
21101
|
+
if (ci.bucket === "denied" || ci.bucket === "unavailable") return null;
|
|
20731
21102
|
return { bucket: ci.bucket };
|
|
20732
21103
|
}
|
|
20733
21104
|
} : {}
|
|
@@ -20907,7 +21278,7 @@ async function runDaemon(opts) {
|
|
|
20907
21278
|
isDirty: (p) => worktreeDirty(p),
|
|
20908
21279
|
releaseWorktree: (repo, p) => releaseWorktree(repo || opts.repo, p),
|
|
20909
21280
|
pruneWorktrees: (repo) => pruneWorktrees(repo || opts.repo),
|
|
20910
|
-
worktreeExists:
|
|
21281
|
+
worktreeExists: existsSync19,
|
|
20911
21282
|
liveWorktrees: () => new Set(listDroneProcesses().map((p) => p.worktree)),
|
|
20912
21283
|
narrate: narrator.narrate
|
|
20913
21284
|
});
|
|
@@ -21030,7 +21401,7 @@ async function runDaemon(opts) {
|
|
|
21030
21401
|
mode,
|
|
21031
21402
|
nudge
|
|
21032
21403
|
),
|
|
21033
|
-
worktreeExists: (p) =>
|
|
21404
|
+
worktreeExists: (p) => existsSync19(p),
|
|
21034
21405
|
// TASWM-514: с этого момента считаем попытки спавна — счётчик живёт ровно одну жизнь
|
|
21035
21406
|
// процесса демона и рестарт не переживает (см. preStartAttempts).
|
|
21036
21407
|
startedAt: Date.now(),
|
|
@@ -21105,9 +21476,11 @@ async function runDaemon(opts) {
|
|
|
21105
21476
|
setTimeout(() => {
|
|
21106
21477
|
Promise.all([closeWebServer(web.server), droneChannel ? closeDroneChannel(droneChannel.server) : Promise.resolve()]).then(() => {
|
|
21107
21478
|
if (!stopping) {
|
|
21108
|
-
|
|
21479
|
+
spawn4(process.execPath, respawnArgs, {
|
|
21109
21480
|
detached: true,
|
|
21110
|
-
stdio: "ignore"
|
|
21481
|
+
// TASWM-497: в ЛОГ, а не в /dev/null. Со `stdio: "ignore"` демон после каждого
|
|
21482
|
+
// `update`/`restart` замолкал навсегда, и разбирать инциденты по логу было нечем.
|
|
21483
|
+
stdio: respawnStdio(DAEMON_LOG_PATH),
|
|
21111
21484
|
cwd: process.cwd(),
|
|
21112
21485
|
env: { ...process.env, SWARM_REPO: opts.repo }
|
|
21113
21486
|
}).unref();
|