switchroom 0.20.7 → 0.20.9
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/agent-scheduler/index.js +111 -14
- package/dist/auth-broker/index.js +113 -30
- package/dist/cli/autoaccept-poll.js +5 -3
- package/dist/cli/drive-write-pretool.mjs +5 -3
- package/dist/cli/ms-365-write-pretool.mjs +5 -3
- package/dist/cli/notion-write-pretool.mjs +67 -6
- package/dist/cli/switchroom.js +389 -31
- package/dist/host-control/main.js +69 -8
- package/dist/vault/approvals/kernel-server.js +68 -7
- package/dist/vault/broker/server.js +68 -7
- package/package.json +1 -1
- package/profiles/default/CLAUDE.md.hbs +12 -13
- package/telegram-plugin/ask-user.ts +6 -7
- package/telegram-plugin/bridge/ipc-client.ts +17 -1
- package/telegram-plugin/dist/bridge/bridge.js +5 -2
- package/telegram-plugin/dist/gateway/gateway.js +410 -178
- package/telegram-plugin/dist/server.js +5 -2
- package/telegram-plugin/gateway/auth-broker-client.ts +1 -1
- package/telegram-plugin/gateway/auth-command.ts +4 -2
- package/telegram-plugin/gateway/boot-reason.ts +61 -0
- package/telegram-plugin/gateway/checklist-fallback.ts +8 -1
- package/telegram-plugin/gateway/cron-session.ts +66 -0
- package/telegram-plugin/gateway/gateway.ts +36 -34
- package/telegram-plugin/gateway/narrative-lane.ts +21 -1
- package/telegram-plugin/gateway/outbound-send-path.ts +9 -1
- package/telegram-plugin/gateway/represent-delivery-guard.ts +33 -2
- package/telegram-plugin/gateway/stream-render.ts +11 -2
- package/telegram-plugin/gateway/subagent-handback-inbound-builder.ts +21 -1
- package/telegram-plugin/gateway/subagent-handback-marker.ts +94 -0
- package/telegram-plugin/gateway/throttle-tier-wiring.ts +93 -18
- package/telegram-plugin/render/emphasis-guard.ts +92 -12
- package/telegram-plugin/render/line-start-guard.ts +27 -2
- package/telegram-plugin/sticker-aliases.ts +12 -14
- package/telegram-plugin/tests/ask-user.test.ts +15 -0
- package/telegram-plugin/tests/boot-card-reason.test.ts +88 -0
- package/telegram-plugin/tests/checklist-fallback.test.ts +21 -0
- package/telegram-plugin/tests/cron-bridge-drain-spool-ack.test.ts +150 -0
- package/telegram-plugin/tests/handback-tasknotif-dedup.test.ts +248 -0
- package/telegram-plugin/tests/ipc-client-reconnect-rejection.test.ts +70 -0
- package/telegram-plugin/tests/narrative-lane-golden.test.ts +86 -0
- package/telegram-plugin/tests/queued-card-surface.test.ts +66 -0
- package/telegram-plugin/tests/render/emphasis-guard.test.ts +105 -6
- package/telegram-plugin/tests/render/heading-guard-blockquote-glued-hash.test.ts +123 -36
- package/telegram-plugin/tests/reply-quote-wire.test.ts +47 -0
- package/telegram-plugin/tests/represent-guard.test.ts +45 -0
- package/telegram-plugin/tests/sticker-aliases.test.ts +43 -0
- package/telegram-plugin/tests/throttle-tier-probe-only.test.ts +216 -0
- package/telegram-plugin/tests/throttle-tier-route-429-wiring.test.ts +92 -0
- package/telegram-plugin/tests/throttle-tier-route-429.test.ts +71 -0
- package/telegram-plugin/tests/turn-flush-safety.test.ts +83 -0
- package/telegram-plugin/throttle-tier.ts +59 -0
- package/telegram-plugin/turn-flush-safety.ts +79 -0
package/dist/cli/switchroom.js
CHANGED
|
@@ -2120,7 +2120,7 @@ var init_esm = __esm(() => {
|
|
|
2120
2120
|
});
|
|
2121
2121
|
|
|
2122
2122
|
// src/build-info.ts
|
|
2123
|
-
var VERSION = "0.20.
|
|
2123
|
+
var VERSION = "0.20.9", COMMIT_SHA = "63c4c44b";
|
|
2124
2124
|
|
|
2125
2125
|
// src/cli/resolve-version.ts
|
|
2126
2126
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -14641,6 +14641,20 @@ var init_overlay_schema = __esm(() => {
|
|
|
14641
14641
|
// src/config/overlay-loader.ts
|
|
14642
14642
|
import { existsSync as existsSync3, readFileSync as readFileSync2, readdirSync, statSync as statSync2 } from "node:fs";
|
|
14643
14643
|
import { basename, resolve as resolve2 } from "node:path";
|
|
14644
|
+
function recordReadFailure(agentCfg, failure) {
|
|
14645
|
+
const node = agentCfg;
|
|
14646
|
+
const existing = node[OVERLAY_READ_FAILURES];
|
|
14647
|
+
if (Array.isArray(existing)) {
|
|
14648
|
+
existing.push(failure);
|
|
14649
|
+
return;
|
|
14650
|
+
}
|
|
14651
|
+
Object.defineProperty(agentCfg, OVERLAY_READ_FAILURES, {
|
|
14652
|
+
value: [failure],
|
|
14653
|
+
enumerable: false,
|
|
14654
|
+
configurable: true,
|
|
14655
|
+
writable: false
|
|
14656
|
+
});
|
|
14657
|
+
}
|
|
14644
14658
|
function deriveOverlayTitle(raw, fileName) {
|
|
14645
14659
|
const titleFromComment = raw.match(/^#[^\S\n]*name:[^\S\n]*(\S.*?)[^\S\n]*$/m)?.[1];
|
|
14646
14660
|
if (titleFromComment)
|
|
@@ -14650,17 +14664,39 @@ function deriveOverlayTitle(raw, fileName) {
|
|
|
14650
14664
|
return;
|
|
14651
14665
|
return base.length > 0 ? base : undefined;
|
|
14652
14666
|
}
|
|
14667
|
+
function readOverlayFile(agentName, file, agentCfg, warnings, source) {
|
|
14668
|
+
try {
|
|
14669
|
+
return readFileSync2(file, "utf-8");
|
|
14670
|
+
} catch (err) {
|
|
14671
|
+
const code = err.code;
|
|
14672
|
+
if (code === "ENOENT")
|
|
14673
|
+
return;
|
|
14674
|
+
const w = {
|
|
14675
|
+
agent: agentName,
|
|
14676
|
+
file,
|
|
14677
|
+
reason: `read error: ${err.message}`,
|
|
14678
|
+
code: code ?? "EUNKNOWN"
|
|
14679
|
+
};
|
|
14680
|
+
recordReadFailure(agentCfg, { file, code: w.code, source });
|
|
14681
|
+
warnings.push(w);
|
|
14682
|
+
console.warn(`[switchroom] overlay-loader: agent='${agentName}' file='${file}': ${w.reason}`);
|
|
14683
|
+
return;
|
|
14684
|
+
}
|
|
14685
|
+
}
|
|
14653
14686
|
function overlayDirFor(agentName, subdir) {
|
|
14654
14687
|
const base = resolveDualPath(`~/.switchroom/agents/${agentName}/${subdir}`);
|
|
14655
14688
|
return resolve2(base);
|
|
14656
14689
|
}
|
|
14657
|
-
function listYamlFiles(dir) {
|
|
14690
|
+
function listYamlFiles(dir, onUnreadableDir) {
|
|
14658
14691
|
if (!existsSync3(dir))
|
|
14659
14692
|
return [];
|
|
14660
14693
|
let entries;
|
|
14661
14694
|
try {
|
|
14662
14695
|
entries = readdirSync(dir);
|
|
14663
|
-
} catch {
|
|
14696
|
+
} catch (err) {
|
|
14697
|
+
const code = err.code;
|
|
14698
|
+
if (code !== "ENOENT")
|
|
14699
|
+
onUnreadableDir?.(code ?? "EUNKNOWN");
|
|
14664
14700
|
return [];
|
|
14665
14701
|
}
|
|
14666
14702
|
const out = [];
|
|
@@ -14698,12 +14734,24 @@ function applyAgentOverlays(config) {
|
|
|
14698
14734
|
for (const [agentName, agentCfg] of Object.entries(agents)) {
|
|
14699
14735
|
try {
|
|
14700
14736
|
const scheduleDir = overlayDirFor(agentName, "schedule.d");
|
|
14701
|
-
const files = listYamlFiles(scheduleDir)
|
|
14737
|
+
const files = listYamlFiles(scheduleDir, (code) => {
|
|
14738
|
+
const w = {
|
|
14739
|
+
agent: agentName,
|
|
14740
|
+
file: scheduleDir,
|
|
14741
|
+
reason: `read error: cannot list overlay directory (${code})`,
|
|
14742
|
+
code
|
|
14743
|
+
};
|
|
14744
|
+
recordReadFailure(agentCfg, { file: scheduleDir, code, source: "schedule" });
|
|
14745
|
+
warnings.push(w);
|
|
14746
|
+
console.warn(`[switchroom] overlay-loader: agent='${agentName}' file='${scheduleDir}': ${w.reason}`);
|
|
14747
|
+
});
|
|
14702
14748
|
if (files.length > 0) {
|
|
14703
14749
|
const merged = [...agentCfg.schedule ?? []];
|
|
14704
14750
|
for (const file of files) {
|
|
14751
|
+
const raw = readOverlayFile(agentName, file, agentCfg, warnings, "schedule");
|
|
14752
|
+
if (raw === undefined)
|
|
14753
|
+
continue;
|
|
14705
14754
|
try {
|
|
14706
|
-
const raw = readFileSync2(file, "utf-8");
|
|
14707
14755
|
const parsed = import_yaml.parse(raw);
|
|
14708
14756
|
const doc = OverlayDocSchema.parse(parsed);
|
|
14709
14757
|
const title = deriveOverlayTitle(raw, basename(file));
|
|
@@ -14738,13 +14786,25 @@ function applyAgentOverlays(config) {
|
|
|
14738
14786
|
}
|
|
14739
14787
|
try {
|
|
14740
14788
|
const skillsDir = overlayDirFor(agentName, "skills.d");
|
|
14741
|
-
const skillFiles = listYamlFiles(skillsDir)
|
|
14789
|
+
const skillFiles = listYamlFiles(skillsDir, (code) => {
|
|
14790
|
+
const w = {
|
|
14791
|
+
agent: agentName,
|
|
14792
|
+
file: skillsDir,
|
|
14793
|
+
reason: `read error: cannot list overlay directory (${code})`,
|
|
14794
|
+
code
|
|
14795
|
+
};
|
|
14796
|
+
recordReadFailure(agentCfg, { file: skillsDir, code, source: "skills" });
|
|
14797
|
+
warnings.push(w);
|
|
14798
|
+
console.warn(`[switchroom] overlay-loader: agent='${agentName}' file='${skillsDir}': ${w.reason}`);
|
|
14799
|
+
});
|
|
14742
14800
|
if (skillFiles.length === 0) {} else {
|
|
14743
14801
|
const merged = [...agentCfg.skills ?? []];
|
|
14744
14802
|
const seen = new Set(merged);
|
|
14745
14803
|
for (const file of skillFiles) {
|
|
14804
|
+
const raw = readOverlayFile(agentName, file, agentCfg, warnings, "skills");
|
|
14805
|
+
if (raw === undefined)
|
|
14806
|
+
continue;
|
|
14746
14807
|
try {
|
|
14747
|
-
const raw = readFileSync2(file, "utf-8");
|
|
14748
14808
|
const parsed = import_yaml.parse(raw);
|
|
14749
14809
|
const doc = OverlayDocSchema.parse(parsed);
|
|
14750
14810
|
for (const skillName of doc.skills ?? []) {
|
|
@@ -14772,7 +14832,7 @@ function applyAgentOverlays(config) {
|
|
|
14772
14832
|
}
|
|
14773
14833
|
return { config, warnings };
|
|
14774
14834
|
}
|
|
14775
|
-
var import_yaml, OVERLAY_SOURCE, OVERLAY_TITLE;
|
|
14835
|
+
var import_yaml, OVERLAY_SOURCE, OVERLAY_TITLE, OVERLAY_READ_FAILURES;
|
|
14776
14836
|
var init_overlay_loader = __esm(() => {
|
|
14777
14837
|
init_zod();
|
|
14778
14838
|
init_overlay_schema();
|
|
@@ -14780,6 +14840,7 @@ var init_overlay_loader = __esm(() => {
|
|
|
14780
14840
|
import_yaml = __toESM(require_dist(), 1);
|
|
14781
14841
|
OVERLAY_SOURCE = Symbol.for("switchroom.config.overlay-source");
|
|
14782
14842
|
OVERLAY_TITLE = Symbol.for("switchroom.config.overlay-title");
|
|
14843
|
+
OVERLAY_READ_FAILURES = Symbol.for("switchroom.config.overlay-read-failures");
|
|
14783
14844
|
});
|
|
14784
14845
|
|
|
14785
14846
|
// src/config/merge.ts
|
|
@@ -20448,6 +20509,15 @@ var init_agent_uid = __esm(() => {
|
|
|
20448
20509
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
20449
20510
|
import { existsSync as existsSync9, lstatSync as lstatSync2, readdirSync as readdirSync3, statSync as statSync6 } from "node:fs";
|
|
20450
20511
|
import { join as join5 } from "node:path";
|
|
20512
|
+
function isChownVanishedRaceOnly(err) {
|
|
20513
|
+
const e = err;
|
|
20514
|
+
const raw = e?.stderr == null ? "" : e.stderr.toString();
|
|
20515
|
+
const lines = raw.split(`
|
|
20516
|
+
`).map((l) => l.trim()).filter(Boolean);
|
|
20517
|
+
if (lines.length === 0)
|
|
20518
|
+
return false;
|
|
20519
|
+
return lines.every((l) => /: No such file or directory$/.test(l));
|
|
20520
|
+
}
|
|
20451
20521
|
function scopedAssertCandidates(agentDir) {
|
|
20452
20522
|
return [
|
|
20453
20523
|
join5(agentDir, ".claude", "settings.json"),
|
|
@@ -20518,7 +20588,9 @@ var init_agent_owned_tree = __esm(() => {
|
|
|
20518
20588
|
SCOPED_RECURSIVE_SUBDIRS = [
|
|
20519
20589
|
".claude",
|
|
20520
20590
|
".claude-cron",
|
|
20521
|
-
"telegram"
|
|
20591
|
+
"telegram",
|
|
20592
|
+
"schedule.d",
|
|
20593
|
+
"skills.d"
|
|
20522
20594
|
];
|
|
20523
20595
|
SCOPED_TOP_LEVEL_CRITICAL = [
|
|
20524
20596
|
"start.sh",
|
|
@@ -20530,15 +20602,26 @@ var init_agent_owned_tree = __esm(() => {
|
|
|
20530
20602
|
ownershipRuntime = {
|
|
20531
20603
|
geteuid: () => process.geteuid?.(),
|
|
20532
20604
|
chownTree: (uid, gid, rootDir) => {
|
|
20533
|
-
|
|
20534
|
-
|
|
20535
|
-
|
|
20605
|
+
try {
|
|
20606
|
+
execFileSync3("chown", ["-h", "-R", "--", `${uid}:${gid}`, rootDir], {
|
|
20607
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
20608
|
+
env: { ...process.env, LC_ALL: "C" },
|
|
20609
|
+
maxBuffer: 64 * 1024 * 1024
|
|
20610
|
+
});
|
|
20611
|
+
} catch (err) {
|
|
20612
|
+
const e = err;
|
|
20613
|
+
if (e?.signal != null || typeof e?.status !== "number")
|
|
20614
|
+
throw err;
|
|
20615
|
+
if (!isChownVanishedRaceOnly(err))
|
|
20616
|
+
throw err;
|
|
20617
|
+
}
|
|
20536
20618
|
},
|
|
20537
20619
|
chownShallow: (uid, gid, paths) => {
|
|
20538
20620
|
if (paths.length === 0)
|
|
20539
20621
|
return;
|
|
20540
|
-
execFileSync3("chown", ["-h", `${uid}:${gid}`, ...paths], {
|
|
20541
|
-
stdio: ["ignore", "ignore", "pipe"]
|
|
20622
|
+
execFileSync3("chown", ["-h", "--", `${uid}:${gid}`, ...paths], {
|
|
20623
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
20624
|
+
env: { ...process.env, LC_ALL: "C" }
|
|
20542
20625
|
});
|
|
20543
20626
|
},
|
|
20544
20627
|
scopedSweepEnabled: () => process.env[SCOPED_SWEEP_ENV] === "1",
|
|
@@ -35931,7 +36014,8 @@ var init_protocol2 = __esm(() => {
|
|
|
35931
36014
|
v: exports_external.literal(PROTOCOL_VERSION),
|
|
35932
36015
|
op: exports_external.literal("mark-throttled"),
|
|
35933
36016
|
id: exports_external.string().min(1),
|
|
35934
|
-
until: exports_external.number().int().positive()
|
|
36017
|
+
until: exports_external.number().int().positive(),
|
|
36018
|
+
probeOnly: exports_external.boolean().optional()
|
|
35935
36019
|
});
|
|
35936
36020
|
RefreshAccountRequestSchema = exports_external.object({
|
|
35937
36021
|
v: exports_external.literal(PROTOCOL_VERSION),
|
|
@@ -36327,12 +36411,13 @@ class AuthBrokerClient {
|
|
|
36327
36411
|
const data = await this.send(req);
|
|
36328
36412
|
return data;
|
|
36329
36413
|
}
|
|
36330
|
-
async markThrottled(until) {
|
|
36414
|
+
async markThrottled(until, probeOnly = false) {
|
|
36331
36415
|
const data = await this.send({
|
|
36332
36416
|
v: PROTOCOL_VERSION,
|
|
36333
36417
|
id: randomUUID(),
|
|
36334
36418
|
op: "mark-throttled",
|
|
36335
|
-
until
|
|
36419
|
+
until,
|
|
36420
|
+
...probeOnly ? { probeOnly: true } : {}
|
|
36336
36421
|
});
|
|
36337
36422
|
return data;
|
|
36338
36423
|
}
|
|
@@ -46933,7 +47018,6 @@ var ManifestSchema, WARN_ONLY_COMPONENTS;
|
|
|
46933
47018
|
var init_manifest = __esm(() => {
|
|
46934
47019
|
init_zod();
|
|
46935
47020
|
ManifestSchema = exports_external.object({
|
|
46936
|
-
switchroom_version: exports_external.string().min(1),
|
|
46937
47021
|
tested_at: exports_external.string().min(1),
|
|
46938
47022
|
runtime: exports_external.object({
|
|
46939
47023
|
bun: exports_external.string().min(1),
|
|
@@ -65426,7 +65510,7 @@ async function checkManifestDrift(probers) {
|
|
|
65426
65510
|
{
|
|
65427
65511
|
name: "dependency manifest",
|
|
65428
65512
|
status: "ok",
|
|
65429
|
-
detail: `all versions match (
|
|
65513
|
+
detail: `all versions match (switchroom ${SWITCHROOM_VERSION})`
|
|
65430
65514
|
}
|
|
65431
65515
|
];
|
|
65432
65516
|
}
|
|
@@ -65812,6 +65896,7 @@ var init_doctor = __esm(() => {
|
|
|
65812
65896
|
init_accounts();
|
|
65813
65897
|
init_schema();
|
|
65814
65898
|
init_manifest();
|
|
65899
|
+
init_resolve_version();
|
|
65815
65900
|
init_hindsight2();
|
|
65816
65901
|
init_hindsight();
|
|
65817
65902
|
init_host_capabilities();
|
|
@@ -85383,6 +85468,7 @@ Scaffolding agent: ${name}
|
|
|
85383
85468
|
}
|
|
85384
85469
|
} catch (err) {
|
|
85385
85470
|
console.error(source_default.red(`Failed to restart ${n}: ${err.message}`));
|
|
85471
|
+
process.exitCode = 1;
|
|
85386
85472
|
}
|
|
85387
85473
|
}
|
|
85388
85474
|
if (sawAbort) {
|
|
@@ -113945,6 +114031,16 @@ async function executeRollout(steps, target, deps, execOpts = {}) {
|
|
|
113945
114031
|
timedOut: true
|
|
113946
114032
|
});
|
|
113947
114033
|
}
|
|
114034
|
+
if (restartRes.status !== 0) {
|
|
114035
|
+
const gotAfterExit = deps.probeVersion(step.agent);
|
|
114036
|
+
deps.log(` \u2717 ${step.agent} \u2192 restart exited ${restartRes.status} ` + `(probe says ${gotAfterExit ?? "<unreachable>"}) \u2014 STOPPING`);
|
|
114037
|
+
emit(isCanary ? { phase: "canary-fail", target, agent: step.agent, n: agentIndex, m: totalAgents } : { phase: "agent-done", target, agent: step.agent, n: agentIndex, m: totalAgents });
|
|
114038
|
+
return fail4({
|
|
114039
|
+
failedStep: "restart-agent",
|
|
114040
|
+
failedAgent: step.agent,
|
|
114041
|
+
got: gotAfterExit
|
|
114042
|
+
});
|
|
114043
|
+
}
|
|
113948
114044
|
const got = deps.probeVersion(step.agent);
|
|
113949
114045
|
if (got === null || normalizeVersion(got) !== targetNorm) {
|
|
113950
114046
|
deps.log(` \u2717 ${step.agent} \u2192 ${got ?? "<unreachable>"} (expected ${target}) \u2014 STOPPING`);
|
|
@@ -114635,6 +114731,7 @@ function registerRestartCommand(program3) {
|
|
|
114635
114731
|
}
|
|
114636
114732
|
} catch (err) {
|
|
114637
114733
|
console.error(source_default.red(` ${name}: restart failed: ${err.message}`));
|
|
114734
|
+
process.exitCode = 1;
|
|
114638
114735
|
}
|
|
114639
114736
|
}
|
|
114640
114737
|
console.log();
|
|
@@ -114645,6 +114742,7 @@ function registerRestartCommand(program3) {
|
|
|
114645
114742
|
// src/cli/versions.ts
|
|
114646
114743
|
init_source();
|
|
114647
114744
|
init_manifest();
|
|
114745
|
+
init_resolve_version();
|
|
114648
114746
|
function formatRow(component, declared, installed, isDrift, warnOnly) {
|
|
114649
114747
|
const installedStr = installed ?? source_default.red("(not installed)");
|
|
114650
114748
|
if (!isDrift) {
|
|
@@ -114674,7 +114772,7 @@ function registerVersionsCommand(program3) {
|
|
|
114674
114772
|
const driftMap = new Map(report.drift.map((d) => [d.component, d]));
|
|
114675
114773
|
console.log(source_default.bold(`
|
|
114676
114774
|
Dependency manifest`));
|
|
114677
|
-
console.log(source_default.gray(` switchroom ${
|
|
114775
|
+
console.log(source_default.gray(` switchroom ${SWITCHROOM_VERSION} \u00b7 tested ${manifest.tested_at}`));
|
|
114678
114776
|
console.log();
|
|
114679
114777
|
const bunDrift = driftMap.get("bun");
|
|
114680
114778
|
console.log(formatRow("bun", manifest.runtime.bun, bunDrift ? bunDrift.installed : manifest.runtime.bun, !!bunDrift, false));
|
|
@@ -117367,6 +117465,40 @@ function isEphemeralPath(path10) {
|
|
|
117367
117465
|
}
|
|
117368
117466
|
return false;
|
|
117369
117467
|
}
|
|
117468
|
+
function pushStateFrom(opts) {
|
|
117469
|
+
if (opts.detached)
|
|
117470
|
+
return "detached";
|
|
117471
|
+
if (!opts.upstream)
|
|
117472
|
+
return "no-upstream";
|
|
117473
|
+
if (opts.aheadCount < 0)
|
|
117474
|
+
return "error";
|
|
117475
|
+
if (opts.aheadCount > 0)
|
|
117476
|
+
return "unpushed";
|
|
117477
|
+
return "pushed";
|
|
117478
|
+
}
|
|
117479
|
+
function isStablePerRepoBranch(branch) {
|
|
117480
|
+
return !!branch && /^agent\/[^/]+\/main$/.test(branch);
|
|
117481
|
+
}
|
|
117482
|
+
function classifyTaskTree(i2) {
|
|
117483
|
+
if (i2.isRegistryClaimed || i2.isStablePerRepoTree || i2.isEphemeralPath) {
|
|
117484
|
+
return "skip-protected";
|
|
117485
|
+
}
|
|
117486
|
+
if (i2.inUse === "unavailable")
|
|
117487
|
+
return "skip-probe-unavailable";
|
|
117488
|
+
if (i2.inUse === "in-use")
|
|
117489
|
+
return "skip-in-use";
|
|
117490
|
+
if (!i2.clean)
|
|
117491
|
+
return "skip-dirty";
|
|
117492
|
+
if (i2.pushed !== "pushed")
|
|
117493
|
+
return "skip-unpushed";
|
|
117494
|
+
if (!i2.idle)
|
|
117495
|
+
return "skip-active";
|
|
117496
|
+
if (i2.prSignal === "error")
|
|
117497
|
+
return "skip-unknown";
|
|
117498
|
+
if (i2.prSignal !== "merged" && i2.prSignal !== "closed")
|
|
117499
|
+
return "skip-unmerged";
|
|
117500
|
+
return "reap";
|
|
117501
|
+
}
|
|
117370
117502
|
var defaultExec3 = (file, args, cwd) => execFileSync31(file, args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).toString();
|
|
117371
117503
|
function defaultPrSignal(repo, branch, exec) {
|
|
117372
117504
|
try {
|
|
@@ -117399,10 +117531,28 @@ function defaultPrSignal(repo, branch, exec) {
|
|
|
117399
117531
|
return "error";
|
|
117400
117532
|
}
|
|
117401
117533
|
}
|
|
117534
|
+
function defaultNewestTrackedMtimeMs(dir, exec) {
|
|
117535
|
+
let out;
|
|
117536
|
+
try {
|
|
117537
|
+
out = exec("git", ["-C", dir, "ls-files", "-z"]);
|
|
117538
|
+
} catch {
|
|
117539
|
+
return Date.now();
|
|
117540
|
+
}
|
|
117541
|
+
const files = out.split("\x00").filter(Boolean);
|
|
117542
|
+
let newest = 0;
|
|
117543
|
+
for (const f of files) {
|
|
117544
|
+
try {
|
|
117545
|
+
const m = statSync52(join102(dir, f)).mtimeMs;
|
|
117546
|
+
if (m > newest)
|
|
117547
|
+
newest = m;
|
|
117548
|
+
} catch {}
|
|
117549
|
+
}
|
|
117550
|
+
return newest || Date.now();
|
|
117551
|
+
}
|
|
117402
117552
|
function trashRoot() {
|
|
117403
117553
|
return resolve58(process.env.SWITCHROOM_WORKTREE_TRASH ?? join102(homedir55(), ".switchroom", "worktree-gc-trash"));
|
|
117404
117554
|
}
|
|
117405
|
-
function planGc(roots, deps = {}) {
|
|
117555
|
+
function planGc(roots, deps = {}, taskTreeRoots = []) {
|
|
117406
117556
|
const exists = deps.existsSync ?? existsSync100;
|
|
117407
117557
|
const readDir = deps.readDir ?? ((p) => readdirSync33(p));
|
|
117408
117558
|
const readFile4 = deps.readFile ?? ((p) => readFileSync90(p, "utf8"));
|
|
@@ -117411,6 +117561,11 @@ function planGc(roots, deps = {}) {
|
|
|
117411
117561
|
const prSignal = deps.prSignal ?? ((repo, branch) => defaultPrSignal(repo, branch, exec));
|
|
117412
117562
|
const stamp = deps.dateStamp ?? "undated";
|
|
117413
117563
|
const trash = join102(trashRoot(), stamp);
|
|
117564
|
+
const probeInUse = deps.probeInUse ?? probePathInUse;
|
|
117565
|
+
const newestMtime = deps.newestTrackedMtimeMs ?? ((dir) => defaultNewestTrackedMtimeMs(dir, exec));
|
|
117566
|
+
const idleDays = deps.idleDays ?? 14;
|
|
117567
|
+
const nowMs = deps.nowMs ?? Date.now();
|
|
117568
|
+
const escapeHatch = deps.escapeHatch ?? false;
|
|
117414
117569
|
let claimed;
|
|
117415
117570
|
try {
|
|
117416
117571
|
claimed = new Set(listRecords().map((r) => resolve58(r.path)));
|
|
@@ -117522,11 +117677,137 @@ function planGc(roots, deps = {}) {
|
|
|
117522
117677
|
registered.push({ path: wt.path, branch: wt.branch, verdict, prSignal: sig, repo });
|
|
117523
117678
|
}
|
|
117524
117679
|
}
|
|
117680
|
+
const taskTrees = [];
|
|
117681
|
+
for (const root of taskTreeRoots) {
|
|
117682
|
+
if (!exists(root))
|
|
117683
|
+
continue;
|
|
117684
|
+
let entries;
|
|
117685
|
+
try {
|
|
117686
|
+
entries = readDir(root);
|
|
117687
|
+
} catch {
|
|
117688
|
+
continue;
|
|
117689
|
+
}
|
|
117690
|
+
for (const name of entries) {
|
|
117691
|
+
const dir = join102(root, name);
|
|
117692
|
+
if (isEphemeralPath(dir))
|
|
117693
|
+
continue;
|
|
117694
|
+
const dotGit = join102(dir, ".git");
|
|
117695
|
+
if (!exists(dotGit))
|
|
117696
|
+
continue;
|
|
117697
|
+
let shape;
|
|
117698
|
+
try {
|
|
117699
|
+
shape = stat3(dotGit).isDirectory() ? "clone" : "worktree";
|
|
117700
|
+
} catch {
|
|
117701
|
+
continue;
|
|
117702
|
+
}
|
|
117703
|
+
let remoteOk = false;
|
|
117704
|
+
try {
|
|
117705
|
+
remoteOk = isSwitchroomRemote(exec("git", ["-C", dir, "remote", "get-url", "origin"]));
|
|
117706
|
+
} catch {
|
|
117707
|
+
remoteOk = false;
|
|
117708
|
+
}
|
|
117709
|
+
if (!remoteOk) {
|
|
117710
|
+
skipped.push({ dir, reason: "not a switchroom task tree" });
|
|
117711
|
+
continue;
|
|
117712
|
+
}
|
|
117713
|
+
let ownerRepo = null;
|
|
117714
|
+
if (shape === "worktree") {
|
|
117715
|
+
try {
|
|
117716
|
+
const ptr = parseGitdirPointer(readFile4(dotGit));
|
|
117717
|
+
if (ptr)
|
|
117718
|
+
ownerRepo = repoRootFromWorktreeGitdir(ptr);
|
|
117719
|
+
} catch {
|
|
117720
|
+
ownerRepo = null;
|
|
117721
|
+
}
|
|
117722
|
+
}
|
|
117723
|
+
let branch = null;
|
|
117724
|
+
let detached = false;
|
|
117725
|
+
try {
|
|
117726
|
+
const b = exec("git", ["-C", dir, "rev-parse", "--abbrev-ref", "HEAD"]).trim();
|
|
117727
|
+
if (b === "HEAD")
|
|
117728
|
+
detached = true;
|
|
117729
|
+
else
|
|
117730
|
+
branch = b;
|
|
117731
|
+
} catch {
|
|
117732
|
+
detached = true;
|
|
117733
|
+
}
|
|
117734
|
+
const claimedWt = claimed.has(resolve58(dir));
|
|
117735
|
+
const stableTree = isStablePerRepoBranch(branch);
|
|
117736
|
+
const ephemeral = isEphemeralPath(dir);
|
|
117737
|
+
const protectedTree = claimedWt || stableTree || ephemeral;
|
|
117738
|
+
let clean3 = false;
|
|
117739
|
+
try {
|
|
117740
|
+
clean3 = isEffectivelyClean(exec("git", ["-C", dir, "status", "--porcelain"]).split(`
|
|
117741
|
+
`));
|
|
117742
|
+
} catch {
|
|
117743
|
+
clean3 = false;
|
|
117744
|
+
}
|
|
117745
|
+
let upstream = null;
|
|
117746
|
+
let aheadCount = 0;
|
|
117747
|
+
if (!detached) {
|
|
117748
|
+
try {
|
|
117749
|
+
upstream = exec("git", ["-C", dir, "rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{upstream}"]).trim() || null;
|
|
117750
|
+
} catch {
|
|
117751
|
+
upstream = null;
|
|
117752
|
+
}
|
|
117753
|
+
if (upstream) {
|
|
117754
|
+
try {
|
|
117755
|
+
aheadCount = parseInt(exec("git", ["-C", dir, "rev-list", "--count", "@{upstream}..HEAD"]).trim(), 10);
|
|
117756
|
+
if (!Number.isFinite(aheadCount))
|
|
117757
|
+
aheadCount = -1;
|
|
117758
|
+
} catch {
|
|
117759
|
+
aheadCount = -1;
|
|
117760
|
+
}
|
|
117761
|
+
}
|
|
117762
|
+
}
|
|
117763
|
+
const pushed = pushStateFrom({ detached, upstream, aheadCount });
|
|
117764
|
+
let idle = false;
|
|
117765
|
+
try {
|
|
117766
|
+
idle = (nowMs - newestMtime(dir)) / 86400000 >= idleDays;
|
|
117767
|
+
} catch {
|
|
117768
|
+
idle = false;
|
|
117769
|
+
}
|
|
117770
|
+
let inUse = "unavailable";
|
|
117771
|
+
let sig = "error";
|
|
117772
|
+
if (!protectedTree) {
|
|
117773
|
+
inUse = probeInUse(dir);
|
|
117774
|
+
if (inUse === "free" && clean3 && pushed === "pushed" && idle && branch) {
|
|
117775
|
+
sig = prSignal(dir, branch);
|
|
117776
|
+
}
|
|
117777
|
+
}
|
|
117778
|
+
const verdict = classifyTaskTree({
|
|
117779
|
+
isRegistryClaimed: claimedWt,
|
|
117780
|
+
isStablePerRepoTree: stableTree,
|
|
117781
|
+
isEphemeralPath: ephemeral,
|
|
117782
|
+
clean: clean3,
|
|
117783
|
+
pushed,
|
|
117784
|
+
prSignal: sig,
|
|
117785
|
+
idle,
|
|
117786
|
+
inUse
|
|
117787
|
+
});
|
|
117788
|
+
const willAct = verdict === "reap" || escapeHatch && idle && (verdict === "skip-dirty" || verdict === "skip-unpushed");
|
|
117789
|
+
if (willAct && ownerRepo)
|
|
117790
|
+
reposToPrune.add(ownerRepo);
|
|
117791
|
+
taskTrees.push({
|
|
117792
|
+
dir,
|
|
117793
|
+
shape,
|
|
117794
|
+
ownerRepo,
|
|
117795
|
+
branch,
|
|
117796
|
+
verdict,
|
|
117797
|
+
prSignal: sig,
|
|
117798
|
+
idle,
|
|
117799
|
+
dest: join102(trash, name),
|
|
117800
|
+
willAct
|
|
117801
|
+
});
|
|
117802
|
+
}
|
|
117803
|
+
}
|
|
117525
117804
|
return {
|
|
117526
117805
|
roots,
|
|
117806
|
+
taskTreeRoots,
|
|
117527
117807
|
trashDir: trash,
|
|
117528
117808
|
orphans,
|
|
117529
117809
|
registered,
|
|
117810
|
+
taskTrees,
|
|
117530
117811
|
reposToPrune: [...reposToPrune],
|
|
117531
117812
|
skipped
|
|
117532
117813
|
};
|
|
@@ -117542,7 +117823,8 @@ function applyGc(plan, deps = {}) {
|
|
|
117542
117823
|
}
|
|
117543
117824
|
});
|
|
117544
117825
|
const res = { quarantined: [], removed: [], branchesDeleted: [], pruned: [], errors: [] };
|
|
117545
|
-
|
|
117826
|
+
const taskTreeActions = (plan.taskTrees ?? []).filter((t) => t.willAct);
|
|
117827
|
+
if (plan.orphans.length || taskTreeActions.length)
|
|
117546
117828
|
mkdirp(plan.trashDir);
|
|
117547
117829
|
for (const o of plan.orphans) {
|
|
117548
117830
|
try {
|
|
@@ -117552,6 +117834,14 @@ function applyGc(plan, deps = {}) {
|
|
|
117552
117834
|
res.errors.push(`quarantine ${o.dir}: ${e.message}`);
|
|
117553
117835
|
}
|
|
117554
117836
|
}
|
|
117837
|
+
for (const t of taskTreeActions) {
|
|
117838
|
+
try {
|
|
117839
|
+
move(t.dir, t.dest);
|
|
117840
|
+
res.quarantined.push(t.dir);
|
|
117841
|
+
} catch (e) {
|
|
117842
|
+
res.errors.push(`quarantine ${t.dir}: ${e.message}`);
|
|
117843
|
+
}
|
|
117844
|
+
}
|
|
117555
117845
|
for (const r of plan.registered) {
|
|
117556
117846
|
if (r.verdict !== "remove")
|
|
117557
117847
|
continue;
|
|
@@ -117623,6 +117913,24 @@ function purgeTrash(paths) {
|
|
|
117623
117913
|
function defaultRoots() {
|
|
117624
117914
|
return [join102(homedir55(), "code")];
|
|
117625
117915
|
}
|
|
117916
|
+
function defaultTaskTreeRoots() {
|
|
117917
|
+
const agentsDir = join102(homedir55(), ".switchroom", "agents");
|
|
117918
|
+
let names;
|
|
117919
|
+
try {
|
|
117920
|
+
names = readdirSync33(agentsDir);
|
|
117921
|
+
} catch {
|
|
117922
|
+
return [];
|
|
117923
|
+
}
|
|
117924
|
+
const roots = [];
|
|
117925
|
+
for (const name of names.sort()) {
|
|
117926
|
+
for (const sub of ["home/work", "home/workspace"]) {
|
|
117927
|
+
const r = join102(agentsDir, name, sub);
|
|
117928
|
+
if (existsSync100(r))
|
|
117929
|
+
roots.push(r);
|
|
117930
|
+
}
|
|
117931
|
+
}
|
|
117932
|
+
return roots;
|
|
117933
|
+
}
|
|
117626
117934
|
|
|
117627
117935
|
// src/cli/worktree.ts
|
|
117628
117936
|
function registerWorktreeCommand(program3) {
|
|
@@ -117756,10 +118064,13 @@ Skipped ${result.skipped.length} stale worktree(s) (kept for safety):`));
|
|
|
117756
118064
|
}
|
|
117757
118065
|
}
|
|
117758
118066
|
});
|
|
117759
|
-
worktree.command("gc").description(`Reclaim dev worktrees that outlived their PR.
|
|
118067
|
+
worktree.command("gc").description(`Reclaim dev worktrees that outlived their PR, and per-agent task trees.
|
|
117760
118068
|
` + `Dry-run by default; pass --yes to act. Quarantines orphaned worktree
|
|
117761
118069
|
` + `directories (move, not delete) and removes registered worktrees whose
|
|
117762
|
-
` +
|
|
118070
|
+
` + `PR is MERGED and whose tree is clean. Also sweeps per-agent home/work
|
|
118071
|
+
` + `task trees (all shapes) that are clean + pushed + idle + merged/closed;
|
|
118072
|
+
` + `dead-but-dirty/unpushed trees are surfaced and reclaimed only with
|
|
118073
|
+
` + "--reclaim-dirty (reversible quarantine).").option("--root <dir>", "Dev-worktree scan root (repeatable). Default: ~/code", (val, acc) => [...acc, val], []).option("--task-root <dir>", "Task-tree scan root (repeatable). Default: every agent's home/work + home/workspace", (val, acc) => [...acc, val], []).option("--no-task-roots", "Skip the per-agent task-tree sweep entirely").option("--idle-days <days>", "Idle threshold for task trees (default 14)", "14").option("--reclaim-dirty", "Operator escape hatch: quarantine IDLE dirty/unpushed task trees (reversible)").option("--yes", "Actually act (default is dry-run)").option("--json", "Output raw JSON").option("--purge-trash", "Hard-delete quarantined dirs older than --older-than").option("--older-than <days>", "Age threshold for --purge-trash (default 14)", "14").action((opts) => {
|
|
117763
118074
|
if (opts.purgeTrash) {
|
|
117764
118075
|
const parsed = Number(opts.olderThan);
|
|
117765
118076
|
const olderThan = isNaN(parsed) ? 14 : parsed;
|
|
@@ -117790,9 +118101,14 @@ Re-run with --yes to delete.`));
|
|
|
117790
118101
|
return;
|
|
117791
118102
|
}
|
|
117792
118103
|
const roots = opts.root.length > 0 ? opts.root : defaultRoots();
|
|
118104
|
+
const taskTreeRoots = opts.taskRoots === false ? [] : opts.taskRoot.length > 0 ? opts.taskRoot : defaultTaskTreeRoots();
|
|
118105
|
+
const parsedIdle = Number(opts.idleDays);
|
|
118106
|
+
const idleDays = Number.isFinite(parsedIdle) && parsedIdle >= 0 ? parsedIdle : 14;
|
|
117793
118107
|
const dateStamp = new Date().toISOString().slice(0, 10);
|
|
117794
|
-
const plan = planGc(roots, { dateStamp });
|
|
118108
|
+
const plan = planGc(roots, { dateStamp, idleDays, escapeHatch: opts.reclaimDirty }, taskTreeRoots);
|
|
117795
118109
|
const toRemove = plan.registered.filter((r) => r.verdict === "remove");
|
|
118110
|
+
const taskReap = plan.taskTrees.filter((t) => t.willAct);
|
|
118111
|
+
const taskEscape = plan.taskTrees.filter((t) => !t.willAct && t.idle && (t.verdict === "skip-dirty" || t.verdict === "skip-unpushed"));
|
|
117796
118112
|
if (!opts.yes) {
|
|
117797
118113
|
if (opts.json) {
|
|
117798
118114
|
console.log(JSON.stringify({ dryRun: true, plan }));
|
|
@@ -117814,21 +118130,45 @@ Kept (${skips.length}):`));
|
|
|
117814
118130
|
for (const r of skips)
|
|
117815
118131
|
console.log(source_default.dim(` ${r.verdict.replace("skip-", "")}: ${r.path} [${r.branch}] (pr=${r.prSignal})`));
|
|
117816
118132
|
}
|
|
118133
|
+
if (taskTreeRoots.length) {
|
|
118134
|
+
const label = opts.reclaimDirty ? "reclaim (reap + escape-hatch)" : "reap (clean + pushed + idle + merged/closed)";
|
|
118135
|
+
console.log(`
|
|
118136
|
+
Task trees to ${label}: ${source_default.bold(taskReap.length)}`);
|
|
118137
|
+
for (const t of taskReap)
|
|
118138
|
+
console.log(` ${t.dir} ${source_default.dim(`[${t.branch ?? "detached"}] ${t.shape} ${t.verdict} \u2192 ${t.dest}`)}`);
|
|
118139
|
+
if (taskEscape.length) {
|
|
118140
|
+
console.log(source_default.yellow(`
|
|
118141
|
+
Dead-but-dirty/unpushed task trees kept (${taskEscape.length}) \u2014 reclaim with --reclaim-dirty:`));
|
|
118142
|
+
for (const t of taskEscape)
|
|
118143
|
+
console.log(source_default.dim(` ${t.verdict.replace("skip-", "")}: ${t.dir} [${t.branch ?? "detached"}] ${t.shape}`));
|
|
118144
|
+
}
|
|
118145
|
+
const taskKept = plan.taskTrees.filter((t) => !t.willAct && !(t.idle && (t.verdict === "skip-dirty" || t.verdict === "skip-unpushed")) && t.verdict !== "skip-protected");
|
|
118146
|
+
if (taskKept.length) {
|
|
118147
|
+
console.log(source_default.dim(`
|
|
118148
|
+
Other task trees kept (${taskKept.length}):`));
|
|
118149
|
+
for (const t of taskKept)
|
|
118150
|
+
console.log(source_default.dim(` ${t.verdict.replace("skip-", "")}: ${t.dir} [${t.branch ?? "detached"}] (pr=${t.prSignal})`));
|
|
118151
|
+
}
|
|
118152
|
+
}
|
|
117817
118153
|
if (plan.skipped.length) {
|
|
117818
118154
|
console.log(source_default.dim(`
|
|
117819
118155
|
Ignored non-switchroom dirs: ${plan.skipped.length}`));
|
|
117820
118156
|
}
|
|
117821
118157
|
console.log(source_default.dim(`
|
|
117822
|
-
Re-run with --yes to act.
|
|
118158
|
+
Re-run with --yes to act. Reclaimed trees are MOVED to the trash dir, not deleted.`));
|
|
117823
118159
|
return;
|
|
117824
118160
|
}
|
|
117825
118161
|
const result = applyGc(plan);
|
|
117826
118162
|
if (opts.json) {
|
|
117827
118163
|
console.log(JSON.stringify(result));
|
|
117828
118164
|
} else {
|
|
117829
|
-
console.log(source_default.green(`Quarantined ${result.quarantined.length}
|
|
118165
|
+
console.log(source_default.green(`Quarantined ${result.quarantined.length} dir(s) \u2192 ${plan.trashDir}`));
|
|
117830
118166
|
console.log(source_default.green(`Removed ${result.removed.length} merged worktree(s); deleted ${result.branchesDeleted.length} branch(es).`));
|
|
117831
118167
|
console.log(source_default.dim(`Pruned metadata in ${result.pruned.length} repo(s).`));
|
|
118168
|
+
if (taskEscape.length && !opts.reclaimDirty) {
|
|
118169
|
+
console.log(source_default.yellow(`
|
|
118170
|
+
${taskEscape.length} dead-but-dirty/unpushed task tree(s) were kept. Reclaim reversibly with --reclaim-dirty.`));
|
|
118171
|
+
}
|
|
117832
118172
|
for (const e of result.errors)
|
|
117833
118173
|
console.warn(source_default.yellow(e));
|
|
117834
118174
|
console.log(source_default.dim(`
|
|
@@ -120405,6 +120745,7 @@ var import_yaml22 = __toESM(require_dist(), 1);
|
|
|
120405
120745
|
// src/config/overlay-writer.ts
|
|
120406
120746
|
init_paths();
|
|
120407
120747
|
import {
|
|
120748
|
+
chownSync as chownSync11,
|
|
120408
120749
|
closeSync as closeSync18,
|
|
120409
120750
|
existsSync as existsSync103,
|
|
120410
120751
|
fsyncSync as fsyncSync8,
|
|
@@ -120481,6 +120822,21 @@ function withAgentLock(paths, fn) {
|
|
|
120481
120822
|
} catch {}
|
|
120482
120823
|
}
|
|
120483
120824
|
}
|
|
120825
|
+
var overlayWriterRuntime = {
|
|
120826
|
+
geteuid: () => process.geteuid?.(),
|
|
120827
|
+
chown: (path10, uid, gid) => chownSync11(path10, uid, gid)
|
|
120828
|
+
};
|
|
120829
|
+
function alignStagedOwnerToDir(stagingPath, targetDir) {
|
|
120830
|
+
try {
|
|
120831
|
+
const euid = overlayWriterRuntime.geteuid();
|
|
120832
|
+
if (euid === undefined)
|
|
120833
|
+
return;
|
|
120834
|
+
const dirStat = statSync54(targetDir);
|
|
120835
|
+
if (dirStat.uid === euid)
|
|
120836
|
+
return;
|
|
120837
|
+
overlayWriterRuntime.chown(stagingPath, dirStat.uid, dirStat.gid);
|
|
120838
|
+
} catch {}
|
|
120839
|
+
}
|
|
120484
120840
|
function writeOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
120485
120841
|
const paths = overlayPathsFor(agent, opts);
|
|
120486
120842
|
return withAgentLock(paths, () => {
|
|
@@ -120494,6 +120850,7 @@ function writeOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
120494
120850
|
} finally {
|
|
120495
120851
|
closeSync18(fd);
|
|
120496
120852
|
}
|
|
120853
|
+
alignStagedOwnerToDir(stagingPath, paths.scheduleDir);
|
|
120497
120854
|
renameSync27(stagingPath, finalPath);
|
|
120498
120855
|
return finalPath;
|
|
120499
120856
|
});
|
|
@@ -120511,6 +120868,7 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
120511
120868
|
} finally {
|
|
120512
120869
|
closeSync18(fd);
|
|
120513
120870
|
}
|
|
120871
|
+
alignStagedOwnerToDir(stagingPath, paths.skillsDir);
|
|
120514
120872
|
renameSync27(stagingPath, finalPath);
|
|
120515
120873
|
return finalPath;
|
|
120516
120874
|
});
|
|
@@ -123985,7 +124343,7 @@ init_source();
|
|
|
123985
124343
|
init_helpers();
|
|
123986
124344
|
init_operator_uid();
|
|
123987
124345
|
init_compose();
|
|
123988
|
-
import { chownSync as
|
|
124346
|
+
import { chownSync as chownSync12, existsSync as existsSync112, mkdirSync as mkdirSync65, writeFileSync as writeFileSync48, copyFileSync as copyFileSync15 } from "node:fs";
|
|
123989
124347
|
import { homedir as homedir62 } from "node:os";
|
|
123990
124348
|
import { join as join115 } from "node:path";
|
|
123991
124349
|
import { spawnSync as spawnSync27 } from "node:child_process";
|
|
@@ -124165,10 +124523,10 @@ async function doInstall2(opts, program3) {
|
|
|
124165
124523
|
writeFileSync48(composePath, yaml, "utf8");
|
|
124166
124524
|
try {
|
|
124167
124525
|
if (typeof process.geteuid === "function" && process.geteuid() === 0) {
|
|
124168
|
-
|
|
124169
|
-
|
|
124526
|
+
chownSync12(dir, operatorUid, operatorUid);
|
|
124527
|
+
chownSync12(composePath, operatorUid, operatorUid);
|
|
124170
124528
|
if (bak)
|
|
124171
|
-
|
|
124529
|
+
chownSync12(bak, operatorUid, operatorUid);
|
|
124172
124530
|
}
|
|
124173
124531
|
} catch {}
|
|
124174
124532
|
console.log(source_default.green(` \u2713 Wrote ${composePath}`));
|