@staff0rd/assist 0.488.0 → 0.488.2
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/README.md +1 -1
- package/dist/commands/sessions/web/bundle.js +228 -228
- package/dist/index.js +248 -75
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Command } from "commander";
|
|
|
6
6
|
// package.json
|
|
7
7
|
var package_default = {
|
|
8
8
|
name: "@staff0rd/assist",
|
|
9
|
-
version: "0.488.
|
|
9
|
+
version: "0.488.2",
|
|
10
10
|
type: "module",
|
|
11
11
|
main: "dist/index.js",
|
|
12
12
|
bin: {
|
|
@@ -10891,6 +10891,18 @@ function resolveNamedRepoWriteLabel(globalRaw, name) {
|
|
|
10891
10891
|
return matches[0];
|
|
10892
10892
|
}
|
|
10893
10893
|
|
|
10894
|
+
// src/commands/config/resolveRepoConfigBlock.ts
|
|
10895
|
+
function resolveRepoConfigBlock(repoName, cwd) {
|
|
10896
|
+
const globalRaw = loadGlobalConfigRaw();
|
|
10897
|
+
const label2 = repoName === void 0 ? resolveRepoWriteLabel(globalRaw, getCurrentOrigin(cwd)) : resolveNamedRepoWriteLabel(globalRaw, repoName);
|
|
10898
|
+
const repos2 = isPlainObject3(globalRaw.repos) ? { ...globalRaw.repos } : {};
|
|
10899
|
+
const block = isPlainObject3(repos2[label2]) ? repos2[label2] : {};
|
|
10900
|
+
return { globalRaw, repos: repos2, label: label2, block };
|
|
10901
|
+
}
|
|
10902
|
+
function isPlainObject3(value) {
|
|
10903
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
10904
|
+
}
|
|
10905
|
+
|
|
10894
10906
|
// src/commands/config/applyRepoConfigSet.ts
|
|
10895
10907
|
function applyRepoConfigSet(key, coerced, repoName, cwd = process.cwd()) {
|
|
10896
10908
|
if (isGlobalOnlyConfigKey(key)) {
|
|
@@ -10901,20 +10913,17 @@ function applyRepoConfigSet(key, coerced, repoName, cwd = process.cwd()) {
|
|
|
10901
10913
|
]
|
|
10902
10914
|
};
|
|
10903
10915
|
}
|
|
10904
|
-
const globalRaw =
|
|
10905
|
-
|
|
10906
|
-
|
|
10907
|
-
|
|
10908
|
-
const updatedBlock = setNestedValue(
|
|
10916
|
+
const { globalRaw, repos: repos2, label: label2, block } = resolveRepoConfigBlock(
|
|
10917
|
+
repoName,
|
|
10918
|
+
cwd
|
|
10919
|
+
);
|
|
10920
|
+
const updatedBlock = setNestedValue(block, key, coerced);
|
|
10909
10921
|
const validation = validateConfig(updatedBlock, key, repoConfigSchema);
|
|
10910
10922
|
if (!validation.ok) return validation;
|
|
10911
10923
|
repos2[label2] = updatedBlock;
|
|
10912
10924
|
saveGlobalConfig({ ...globalRaw, repos: repos2 });
|
|
10913
10925
|
return { ok: true, target: "repo", label: label2 };
|
|
10914
10926
|
}
|
|
10915
|
-
function isPlainObject3(value) {
|
|
10916
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
10917
|
-
}
|
|
10918
10927
|
|
|
10919
10928
|
// src/commands/sessions/web/applyScopedConfigSet.ts
|
|
10920
10929
|
function applyScopedConfigSet(key, value, cwd, scope) {
|
|
@@ -10994,6 +11003,11 @@ function setConfig(req, res) {
|
|
|
10994
11003
|
});
|
|
10995
11004
|
}
|
|
10996
11005
|
|
|
11006
|
+
// src/commands/config/isKnownConfigKey.ts
|
|
11007
|
+
function isKnownConfigKey(key) {
|
|
11008
|
+
return enumerateConfigLeafKeys(assistConfigSchema).includes(key);
|
|
11009
|
+
}
|
|
11010
|
+
|
|
10997
11011
|
// src/commands/config/unsetNestedValue.ts
|
|
10998
11012
|
function isPlainObject4(val) {
|
|
10999
11013
|
return val !== null && typeof val === "object" && !Array.isArray(val);
|
|
@@ -11084,9 +11098,48 @@ function applyConfigUnset(key, global, cwd = process.cwd()) {
|
|
|
11084
11098
|
return { ok: true, target, removed: true };
|
|
11085
11099
|
}
|
|
11086
11100
|
|
|
11087
|
-
// src/commands/config/
|
|
11088
|
-
function
|
|
11089
|
-
|
|
11101
|
+
// src/commands/config/applyRepoConfigUnset.ts
|
|
11102
|
+
function applyRepoConfigUnset(key, repoName, cwd = process.cwd()) {
|
|
11103
|
+
if (isGlobalOnlyConfigKey(key)) {
|
|
11104
|
+
return {
|
|
11105
|
+
ok: false,
|
|
11106
|
+
errors: [
|
|
11107
|
+
`"${key}" is a global-only key. Unset it in ~/.assist.yml rather than under repos:`
|
|
11108
|
+
]
|
|
11109
|
+
};
|
|
11110
|
+
}
|
|
11111
|
+
const { globalRaw, repos: repos2, label: label2, block } = resolveRepoConfigBlock(
|
|
11112
|
+
repoName,
|
|
11113
|
+
cwd
|
|
11114
|
+
);
|
|
11115
|
+
const { config: updatedBlock, removed } = unsetNestedValue(block, key);
|
|
11116
|
+
if (!removed) return { ok: true, target: "repo", label: label2, removed: false };
|
|
11117
|
+
const validation = validateConfig(updatedBlock, key, repoConfigSchema);
|
|
11118
|
+
if (!validation.ok) return validation;
|
|
11119
|
+
if (Object.keys(updatedBlock).length === 0) delete repos2[label2];
|
|
11120
|
+
else repos2[label2] = updatedBlock;
|
|
11121
|
+
const next3 = { ...globalRaw };
|
|
11122
|
+
if (Object.keys(repos2).length === 0) delete next3.repos;
|
|
11123
|
+
else next3.repos = repos2;
|
|
11124
|
+
saveGlobalConfig(next3);
|
|
11125
|
+
return { ok: true, target: "repo", label: label2, removed: true };
|
|
11126
|
+
}
|
|
11127
|
+
|
|
11128
|
+
// src/commands/sessions/web/applyScopedConfigUnset.ts
|
|
11129
|
+
function applyScopedConfigUnset(key, cwd, scope) {
|
|
11130
|
+
if (scope === "repo") {
|
|
11131
|
+
const result2 = applyRepoConfigUnset(key, void 0, cwd);
|
|
11132
|
+
return result2.ok ? {
|
|
11133
|
+
ok: true,
|
|
11134
|
+
payload: {
|
|
11135
|
+
target: result2.target,
|
|
11136
|
+
repoKey: result2.label,
|
|
11137
|
+
removed: result2.removed
|
|
11138
|
+
}
|
|
11139
|
+
} : result2;
|
|
11140
|
+
}
|
|
11141
|
+
const result = applyConfigUnset(key, scope === "global", cwd);
|
|
11142
|
+
return result.ok ? { ok: true, payload: { target: result.target, removed: result.removed } } : result;
|
|
11090
11143
|
}
|
|
11091
11144
|
|
|
11092
11145
|
// src/commands/sessions/web/unsetConfig.ts
|
|
@@ -11094,22 +11147,7 @@ function unsetConfig(req, res) {
|
|
|
11094
11147
|
return handleConfigWrite(req, res, (request) => {
|
|
11095
11148
|
if (!isKnownConfigKey(request.key))
|
|
11096
11149
|
return { ok: false, errors: [`Unknown config key "${request.key}"`] };
|
|
11097
|
-
|
|
11098
|
-
return {
|
|
11099
|
-
ok: false,
|
|
11100
|
-
errors: [
|
|
11101
|
-
"Clearing a repos override is not supported \u2014 remove the key from repos: in ~/.assist.yml"
|
|
11102
|
-
]
|
|
11103
|
-
};
|
|
11104
|
-
const result = applyConfigUnset(
|
|
11105
|
-
request.key,
|
|
11106
|
-
request.scope === "global",
|
|
11107
|
-
request.cwd
|
|
11108
|
-
);
|
|
11109
|
-
return result.ok ? {
|
|
11110
|
-
ok: true,
|
|
11111
|
-
payload: { target: result.target, removed: result.removed }
|
|
11112
|
-
} : result;
|
|
11150
|
+
return applyScopedConfigUnset(request.key, request.cwd, request.scope);
|
|
11113
11151
|
});
|
|
11114
11152
|
}
|
|
11115
11153
|
|
|
@@ -16566,16 +16604,48 @@ function applyRepoOrExit(key, coerced, repoName) {
|
|
|
16566
16604
|
|
|
16567
16605
|
// src/commands/config/configUnset.ts
|
|
16568
16606
|
import chalk132 from "chalk";
|
|
16607
|
+
|
|
16608
|
+
// src/commands/config/resolveRepoUnsetTarget.ts
|
|
16609
|
+
function resolveRepoUnsetTarget(key, repo) {
|
|
16610
|
+
if (typeof repo === "string") {
|
|
16611
|
+
if (key === void 0)
|
|
16612
|
+
return { key: repo, useRepo: true, repoName: void 0 };
|
|
16613
|
+
return { key, useRepo: true, repoName: repo };
|
|
16614
|
+
}
|
|
16615
|
+
return { key, useRepo: repo === true, repoName: void 0 };
|
|
16616
|
+
}
|
|
16617
|
+
|
|
16618
|
+
// src/commands/config/configUnset.ts
|
|
16569
16619
|
function configUnset(key, options2 = {}) {
|
|
16570
|
-
|
|
16620
|
+
if (options2.repo !== void 0 && !options2.global) {
|
|
16621
|
+
console.error(
|
|
16622
|
+
chalk132.red(
|
|
16623
|
+
"--repo removes from the global config; add -g (e.g. -g --repo)"
|
|
16624
|
+
)
|
|
16625
|
+
);
|
|
16626
|
+
process.exit(1);
|
|
16627
|
+
}
|
|
16628
|
+
const resolved = resolveRepoUnsetTarget(key, options2.repo);
|
|
16629
|
+
if (resolved.key === void 0) {
|
|
16630
|
+
console.error(chalk132.red("Missing required argument 'key'"));
|
|
16631
|
+
process.exit(1);
|
|
16632
|
+
return;
|
|
16633
|
+
}
|
|
16634
|
+
const result = resolved.useRepo ? applyRepoConfigUnset(resolved.key, resolved.repoName) : applyConfigUnset(resolved.key, options2.global ?? false);
|
|
16571
16635
|
if (!result.ok) exitWithConfigErrors(result.errors);
|
|
16572
16636
|
if (!result.removed) {
|
|
16573
16637
|
console.log(
|
|
16574
|
-
chalk132.yellow(`${key} is not set in
|
|
16638
|
+
chalk132.yellow(`${resolved.key} is not set in ${whereLabel(result)}`)
|
|
16575
16639
|
);
|
|
16576
16640
|
return;
|
|
16577
16641
|
}
|
|
16578
|
-
console.log(chalk132.green(`Unset ${key} (${result
|
|
16642
|
+
console.log(chalk132.green(`Unset ${resolved.key} (${targetLabel(result)})`));
|
|
16643
|
+
}
|
|
16644
|
+
function whereLabel(result) {
|
|
16645
|
+
return result.target === "repo" ? `repos.${result.label}` : `the ${result.target} config`;
|
|
16646
|
+
}
|
|
16647
|
+
function targetLabel(result) {
|
|
16648
|
+
return result.target === "repo" ? `repo: ${result.label}` : result.target;
|
|
16579
16649
|
}
|
|
16580
16650
|
|
|
16581
16651
|
// src/commands/registerConfig.ts
|
|
@@ -16585,7 +16655,10 @@ function registerConfig(program2) {
|
|
|
16585
16655
|
"-r, --repo [name]",
|
|
16586
16656
|
"Requires -g: scope the global write to a repo's identity (defaults to the current repo)"
|
|
16587
16657
|
).action((key, value, options2) => configSet(key, value, options2));
|
|
16588
|
-
configCommand.command("unset
|
|
16658
|
+
configCommand.command("unset [key]").description("Remove a config value (e.g. commit.push)").option("-g, --global", "Remove from global ~/.assist.yml").option(
|
|
16659
|
+
"-r, --repo [name]",
|
|
16660
|
+
"Requires -g: remove the key from a repo's identity block (defaults to the current repo)"
|
|
16661
|
+
).action((key, options2) => configUnset(key, options2));
|
|
16589
16662
|
configCommand.command("get <key>").description("Get a config value").action(configGet);
|
|
16590
16663
|
configCommand.command("list").description("List all config values").action(configList);
|
|
16591
16664
|
}
|
|
@@ -18970,6 +19043,7 @@ function createWorktree(clone, strategy, boundTreeRoots2) {
|
|
|
18970
19043
|
|
|
18971
19044
|
// src/commands/sessions/daemon/worktree/treeDurability.ts
|
|
18972
19045
|
import { existsSync as existsSync45 } from "fs";
|
|
19046
|
+
var treeIsGone = { durable: true, gone: true };
|
|
18973
19047
|
function treeDurability(state) {
|
|
18974
19048
|
if (state.dirty) return { durable: false, reason: "uncommitted changes" };
|
|
18975
19049
|
if (state.localOnlyCommits)
|
|
@@ -18999,14 +19073,14 @@ function* durabilityProbes() {
|
|
|
18999
19073
|
});
|
|
19000
19074
|
}
|
|
19001
19075
|
async function checkDurability(cwd) {
|
|
19002
|
-
if (!existsSync45(cwd)) return
|
|
19076
|
+
if (!existsSync45(cwd)) return treeIsGone;
|
|
19003
19077
|
const probes = durabilityProbes();
|
|
19004
19078
|
let step2 = probes.next();
|
|
19005
19079
|
while (!step2.done) step2 = probes.next(await gitResult(cwd, step2.value));
|
|
19006
19080
|
return step2.value;
|
|
19007
19081
|
}
|
|
19008
19082
|
function checkDurabilitySync(cwd) {
|
|
19009
|
-
if (!existsSync45(cwd)) return
|
|
19083
|
+
if (!existsSync45(cwd)) return treeIsGone;
|
|
19010
19084
|
const probes = durabilityProbes();
|
|
19011
19085
|
let step2 = probes.next();
|
|
19012
19086
|
while (!step2.done) step2 = probes.next(gitSyncResult(cwd, step2.value));
|
|
@@ -28400,6 +28474,7 @@ function readDesignSystemPrompt() {
|
|
|
28400
28474
|
}
|
|
28401
28475
|
|
|
28402
28476
|
// src/commands/sessions/daemon/spawnPty.ts
|
|
28477
|
+
import { existsSync as existsSync62 } from "fs";
|
|
28403
28478
|
import * as pty from "node-pty";
|
|
28404
28479
|
|
|
28405
28480
|
// src/commands/sessions/daemon/ensureSpawnHelperExecutable.ts
|
|
@@ -28424,7 +28499,15 @@ function ensureSpawnHelperExecutable() {
|
|
|
28424
28499
|
}
|
|
28425
28500
|
|
|
28426
28501
|
// src/commands/sessions/daemon/spawnPty.ts
|
|
28502
|
+
var MissingCwdError = class extends Error {
|
|
28503
|
+
constructor(cwd) {
|
|
28504
|
+
super(`working directory no longer exists: ${cwd}`);
|
|
28505
|
+
this.cwd = cwd;
|
|
28506
|
+
this.name = "MissingCwdError";
|
|
28507
|
+
}
|
|
28508
|
+
};
|
|
28427
28509
|
function spawnPty(args, cwd, sessionId, extraEnv) {
|
|
28510
|
+
refuseMissingCwd(cwd, sessionId);
|
|
28428
28511
|
ensureSpawnHelperExecutable();
|
|
28429
28512
|
const shell = process.platform === "win32" ? "cmd.exe" : process.env.SHELL ?? "bash";
|
|
28430
28513
|
const shellArgs = process.platform === "win32" ? ["/c", ...args] : ["-l", "-c", `exec ${args.map(shellEscape).join(" ")}`];
|
|
@@ -28445,6 +28528,13 @@ function spawnPty(args, cwd, sessionId, extraEnv) {
|
|
|
28445
28528
|
}
|
|
28446
28529
|
});
|
|
28447
28530
|
}
|
|
28531
|
+
function refuseMissingCwd(cwd, sessionId) {
|
|
28532
|
+
if (!cwd || existsSync62(cwd)) return;
|
|
28533
|
+
daemonLog(
|
|
28534
|
+
`${sessionId ? `session ${sessionId}` : "pty"} not spawned: working directory ${cwd} no longer exists`
|
|
28535
|
+
);
|
|
28536
|
+
throw new MissingCwdError(cwd);
|
|
28537
|
+
}
|
|
28448
28538
|
function shellEscape(s) {
|
|
28449
28539
|
return `'${s.replace(/'/g, String.raw`'\''`)}'`;
|
|
28450
28540
|
}
|
|
@@ -28578,11 +28668,11 @@ function otherTreeHolders(sessions, session) {
|
|
|
28578
28668
|
}
|
|
28579
28669
|
|
|
28580
28670
|
// src/commands/sessions/daemon/worktree/reapWorktree.ts
|
|
28581
|
-
import { existsSync as
|
|
28671
|
+
import { existsSync as existsSync64 } from "fs";
|
|
28582
28672
|
import { basename as basename18 } from "path";
|
|
28583
28673
|
|
|
28584
28674
|
// src/commands/sessions/daemon/worktree/deleteStrandedTree.ts
|
|
28585
|
-
import { existsSync as
|
|
28675
|
+
import { existsSync as existsSync63 } from "fs";
|
|
28586
28676
|
import { join as join74 } from "path";
|
|
28587
28677
|
|
|
28588
28678
|
// src/commands/sessions/daemon/worktree/deleteTreeDirectly.ts
|
|
@@ -28651,7 +28741,7 @@ async function deleteStrandedTree(clone, worktreePath, cause) {
|
|
|
28651
28741
|
);
|
|
28652
28742
|
}
|
|
28653
28743
|
function strandedReason(worktreePath, cause) {
|
|
28654
|
-
if (!
|
|
28744
|
+
if (!existsSync63(join74(worktreePath, ".git")))
|
|
28655
28745
|
return "its .git link is already gone";
|
|
28656
28746
|
if (/not a working tree|not a git repository/i.test(reason2(cause)))
|
|
28657
28747
|
return "git no longer recognises it as a working tree";
|
|
@@ -28703,7 +28793,7 @@ function reason3(error) {
|
|
|
28703
28793
|
|
|
28704
28794
|
// src/commands/sessions/daemon/worktree/reapWorktree.ts
|
|
28705
28795
|
async function reapWorktree(worktreePath, force = false) {
|
|
28706
|
-
if (!
|
|
28796
|
+
if (!existsSync64(worktreePath)) {
|
|
28707
28797
|
daemonLog(`worktree ${worktreePath} already gone; skipping reap`);
|
|
28708
28798
|
return false;
|
|
28709
28799
|
}
|
|
@@ -28724,7 +28814,7 @@ async function reapWorktree(worktreePath, force = false) {
|
|
|
28724
28814
|
}
|
|
28725
28815
|
function owningClone(worktreePath) {
|
|
28726
28816
|
const recorded = worktreeAttributionIncludingReaped(worktreePath)?.clone;
|
|
28727
|
-
if (recorded &&
|
|
28817
|
+
if (recorded && existsSync64(recorded)) return recorded;
|
|
28728
28818
|
const detected = mainWorktree(worktreePath);
|
|
28729
28819
|
if (detected) return detected;
|
|
28730
28820
|
daemonLog(
|
|
@@ -28811,12 +28901,12 @@ function setStatus2(session, newStatus) {
|
|
|
28811
28901
|
}
|
|
28812
28902
|
|
|
28813
28903
|
// src/commands/sessions/daemon/worktree/watchGitState.ts
|
|
28814
|
-
import { existsSync as
|
|
28904
|
+
import { existsSync as existsSync65, watch } from "fs";
|
|
28815
28905
|
var DEBOUNCE_MS = 500;
|
|
28816
28906
|
var POLL_MS = 3e4;
|
|
28817
28907
|
function watchGitState(cwd, onChange) {
|
|
28818
28908
|
const common = gitCommonDir(cwd);
|
|
28819
|
-
if (!common || !
|
|
28909
|
+
if (!common || !existsSync65(common)) return void 0;
|
|
28820
28910
|
const watchers = [
|
|
28821
28911
|
watchGitDir(common, onChange),
|
|
28822
28912
|
pollGitState(cwd, onChange)
|
|
@@ -28862,6 +28952,10 @@ async function resolveCloseDurability(session, finalize, notify2) {
|
|
|
28862
28952
|
holdStopped(session, tree, durability.reason, finalize, notify2);
|
|
28863
28953
|
return;
|
|
28864
28954
|
}
|
|
28955
|
+
if (durability.gone)
|
|
28956
|
+
daemonLog(
|
|
28957
|
+
`session ${session.id} closing: worktree ${tree.path} is gone from disk \u2014 released with nothing to land, not landed work`
|
|
28958
|
+
);
|
|
28865
28959
|
if (tree.removable) await reapWorktree(tree.path);
|
|
28866
28960
|
session.worktree = void 0;
|
|
28867
28961
|
session.undurable = void 0;
|
|
@@ -29220,10 +29314,10 @@ function emitSessionOutput(session, clients, data) {
|
|
|
29220
29314
|
}
|
|
29221
29315
|
|
|
29222
29316
|
// src/commands/sessions/daemon/exitReason.ts
|
|
29223
|
-
import { existsSync as
|
|
29317
|
+
import { existsSync as existsSync66 } from "fs";
|
|
29224
29318
|
function exitReason(session, exitCode) {
|
|
29225
29319
|
const base = `process exited with code ${exitCode}`;
|
|
29226
|
-
if (session.cwd && !
|
|
29320
|
+
if (session.cwd && !existsSync66(session.cwd))
|
|
29227
29321
|
return `${base}: working directory ${session.cwd} no longer exists`;
|
|
29228
29322
|
return base;
|
|
29229
29323
|
}
|
|
@@ -29241,7 +29335,7 @@ function handleFailedResume(session, exitCode, onStatusChange) {
|
|
|
29241
29335
|
}
|
|
29242
29336
|
|
|
29243
29337
|
// src/commands/sessions/daemon/watchActivity.ts
|
|
29244
|
-
import { existsSync as
|
|
29338
|
+
import { existsSync as existsSync67, mkdirSync as mkdirSync26, watch as watch2 } from "fs";
|
|
29245
29339
|
import { dirname as dirname33 } from "path";
|
|
29246
29340
|
|
|
29247
29341
|
// src/commands/sessions/daemon/applyReviewPause.ts
|
|
@@ -29322,7 +29416,7 @@ function watchActivity(session, notify2, onClaudeSessionId) {
|
|
|
29322
29416
|
if (timer) clearTimeout(timer);
|
|
29323
29417
|
timer = setTimeout(read, DEBOUNCE_MS2);
|
|
29324
29418
|
});
|
|
29325
|
-
if (
|
|
29419
|
+
if (existsSync67(path71)) read();
|
|
29326
29420
|
}
|
|
29327
29421
|
function refreshActivity(session) {
|
|
29328
29422
|
if (session.commandType !== "assist" || !session.cwd) return;
|
|
@@ -30034,6 +30128,26 @@ function makeStatusChangeHandler(sessions, dismiss, notify2, reuseForRun) {
|
|
|
30034
30128
|
);
|
|
30035
30129
|
}
|
|
30036
30130
|
|
|
30131
|
+
// src/commands/sessions/daemon/refuseSpawn.ts
|
|
30132
|
+
function refuseSpawn(session, error, clients, onStatusChange, stage = "respawned") {
|
|
30133
|
+
const reason4 = error instanceof Error ? error.message : String(error);
|
|
30134
|
+
session.pty = null;
|
|
30135
|
+
session.pendingStart = void 0;
|
|
30136
|
+
session.error = reason4;
|
|
30137
|
+
daemonLog(
|
|
30138
|
+
`session ${session.id} ("${session.name}") not ${stage}: ${reason4}`
|
|
30139
|
+
);
|
|
30140
|
+
broadcast(clients, { type: "clear", sessionId: session.id });
|
|
30141
|
+
emitSessionOutput(
|
|
30142
|
+
session,
|
|
30143
|
+
clients,
|
|
30144
|
+
`\r
|
|
30145
|
+
\x1B[31mCannot start this session: ${reason4}\x1B[0m\r
|
|
30146
|
+
`
|
|
30147
|
+
);
|
|
30148
|
+
onStatusChange(session, "error");
|
|
30149
|
+
}
|
|
30150
|
+
|
|
30037
30151
|
// src/commands/sessions/daemon/respawnSession.ts
|
|
30038
30152
|
function respawnSession(session, respawn, status3, clients, onStatusChange) {
|
|
30039
30153
|
session.gitWatcher?.close();
|
|
@@ -30047,7 +30161,12 @@ function respawnSession(session, respawn, status3, clients, onStatusChange) {
|
|
|
30047
30161
|
session.runningSince = null;
|
|
30048
30162
|
setStatus2(session, status3);
|
|
30049
30163
|
session.restored = void 0;
|
|
30050
|
-
|
|
30164
|
+
try {
|
|
30165
|
+
session.pty = respawn();
|
|
30166
|
+
} catch (error) {
|
|
30167
|
+
refuseSpawn(session, error, clients, onStatusChange);
|
|
30168
|
+
return;
|
|
30169
|
+
}
|
|
30051
30170
|
if (session.cols && session.rows)
|
|
30052
30171
|
try {
|
|
30053
30172
|
session.pty?.resize(session.cols, session.rows);
|
|
@@ -30182,6 +30301,9 @@ function restoreBase(id, persisted) {
|
|
|
30182
30301
|
function errorSession(id, persisted, error) {
|
|
30183
30302
|
return {
|
|
30184
30303
|
...restoreBase(id, persisted),
|
|
30304
|
+
scrollback: `\r
|
|
30305
|
+
\x1B[31m${error}\x1B[0m\r
|
|
30306
|
+
`,
|
|
30185
30307
|
status: "error",
|
|
30186
30308
|
startedAt: persisted.startedAt,
|
|
30187
30309
|
runningMs: persisted.runningMs ?? 0,
|
|
@@ -30411,7 +30533,7 @@ function rearmStoppedSessions(sessions, notify2) {
|
|
|
30411
30533
|
}
|
|
30412
30534
|
|
|
30413
30535
|
// src/commands/sessions/daemon/worktree/reconcileWorktreesOnRestore.ts
|
|
30414
|
-
import { existsSync as
|
|
30536
|
+
import { existsSync as existsSync70 } from "fs";
|
|
30415
30537
|
import { basename as basename20 } from "path";
|
|
30416
30538
|
|
|
30417
30539
|
// src/commands/sessions/daemon/worktree/accountedTrees.ts
|
|
@@ -30501,9 +30623,9 @@ function capped(lines) {
|
|
|
30501
30623
|
}
|
|
30502
30624
|
|
|
30503
30625
|
// src/commands/sessions/daemon/worktree/reclaimVanishedWorktrees.ts
|
|
30504
|
-
import { existsSync as
|
|
30626
|
+
import { existsSync as existsSync69 } from "fs";
|
|
30505
30627
|
async function reclaimVanishedWorktrees(clone, paths) {
|
|
30506
|
-
if (!
|
|
30628
|
+
if (!existsSync69(clone)) {
|
|
30507
30629
|
for (const { path: path71 } of paths) forgetWorktree(path71);
|
|
30508
30630
|
daemonLog(
|
|
30509
30631
|
`clone ${clone} is gone; forgot ${paths.length} worktree record(s) it owned`
|
|
@@ -30586,6 +30708,21 @@ function recoveryNotice({ orphan, held }) {
|
|
|
30586
30708
|
].join("");
|
|
30587
30709
|
}
|
|
30588
30710
|
|
|
30711
|
+
// src/commands/sessions/daemon/worktree/logVanishedTree.ts
|
|
30712
|
+
function logVanishedTree(sessions, path71) {
|
|
30713
|
+
const claimants = [...sessions.values()].filter(
|
|
30714
|
+
(s) => s.cwd === path71 || s.worktree?.path === path71
|
|
30715
|
+
);
|
|
30716
|
+
if (claimants.length === 0) {
|
|
30717
|
+
daemonLog(`worktree ${path71} gone from disk and unclaimed; reclaiming it`);
|
|
30718
|
+
return;
|
|
30719
|
+
}
|
|
30720
|
+
for (const s of claimants)
|
|
30721
|
+
daemonLog(
|
|
30722
|
+
`session ${s.id} ("${s.name}") claims worktree ${path71}, which is gone from disk; reclaiming it`
|
|
30723
|
+
);
|
|
30724
|
+
}
|
|
30725
|
+
|
|
30589
30726
|
// src/commands/sessions/daemon/worktree/reconcileWorktreesOnRestore.ts
|
|
30590
30727
|
function reconcileWorktreesOnRestore(sessions, spawnWith, notify2) {
|
|
30591
30728
|
bindRestoredWorktrees(sessions);
|
|
@@ -30595,14 +30732,15 @@ async function recoverOrphanedWorktrees(sessions, spawnWith, notify2) {
|
|
|
30595
30732
|
const accounted = accountedTrees(sessions);
|
|
30596
30733
|
const vanished = /* @__PURE__ */ new Map();
|
|
30597
30734
|
for (const { path: path71, clone } of readWorktreeRegistry()) {
|
|
30598
|
-
if (
|
|
30599
|
-
|
|
30735
|
+
if (!existsSync70(path71)) {
|
|
30736
|
+
logVanishedTree(sessions, path71);
|
|
30600
30737
|
vanished.set(clone, [
|
|
30601
30738
|
...vanished.get(clone) ?? [],
|
|
30602
30739
|
{ path: path71, branch: basename20(path71) }
|
|
30603
30740
|
]);
|
|
30604
30741
|
continue;
|
|
30605
30742
|
}
|
|
30743
|
+
if (accounted.has(path71)) continue;
|
|
30606
30744
|
await recoverOrphan(sessions, spawnWith, { path: path71, clone }, notify2);
|
|
30607
30745
|
}
|
|
30608
30746
|
for (const [clone, paths] of vanished)
|
|
@@ -30742,11 +30880,16 @@ function holdsTree(session, root) {
|
|
|
30742
30880
|
if (session.status !== "done" && session.status !== "error") return true;
|
|
30743
30881
|
if (!worktreeConfigFor(root).enabled) return true;
|
|
30744
30882
|
const durability = checkDurabilitySync(root);
|
|
30745
|
-
daemonLog(
|
|
30746
|
-
durability.durable ? `tree ${root} free for allocation: session ${session.id} finished and its work is landed` : `tree ${root} still held by finished session ${session.id}: ${durability.reason}`
|
|
30747
|
-
);
|
|
30883
|
+
daemonLog(`tree ${root} ${releaseReason(durability, session)}`);
|
|
30748
30884
|
return !durability.durable;
|
|
30749
30885
|
}
|
|
30886
|
+
function releaseReason(durability, session) {
|
|
30887
|
+
if (!durability.durable)
|
|
30888
|
+
return `still held by finished session ${session.id}: ${durability.reason}`;
|
|
30889
|
+
if (durability.gone)
|
|
30890
|
+
return `free for allocation: session ${session.id}'s directory is gone from disk \u2014 nothing to land, not landed work`;
|
|
30891
|
+
return `free for allocation: session ${session.id} finished and its work is landed`;
|
|
30892
|
+
}
|
|
30750
30893
|
|
|
30751
30894
|
// src/commands/sessions/daemon/worktree/planReuseTree.ts
|
|
30752
30895
|
function planReuseTree(session, ctx) {
|
|
@@ -30768,13 +30911,24 @@ function reuseSessionForRun(session, itemId2, clients, onStatusChange, tree) {
|
|
|
30768
30911
|
resetCardForRun(session, assistArgs);
|
|
30769
30912
|
const alloc = planReuseTree(session, tree);
|
|
30770
30913
|
if (alloc) session.cwd = alloc.cwd;
|
|
30771
|
-
|
|
30772
|
-
|
|
30773
|
-
|
|
30774
|
-
(
|
|
30775
|
-
|
|
30776
|
-
|
|
30777
|
-
|
|
30914
|
+
try {
|
|
30915
|
+
Object.assign(
|
|
30916
|
+
session,
|
|
30917
|
+
startOrHoldPty(
|
|
30918
|
+
() => spawnPty(["assist", ...assistArgs], session.cwd, session.id),
|
|
30919
|
+
alloc !== void 0
|
|
30920
|
+
)
|
|
30921
|
+
);
|
|
30922
|
+
} catch (error) {
|
|
30923
|
+
refuseSpawn(
|
|
30924
|
+
session,
|
|
30925
|
+
error,
|
|
30926
|
+
clients,
|
|
30927
|
+
onStatusChange,
|
|
30928
|
+
`reused for backlog run ${itemId2}`
|
|
30929
|
+
);
|
|
30930
|
+
return;
|
|
30931
|
+
}
|
|
30778
30932
|
broadcast(clients, { type: "clear", sessionId: session.id });
|
|
30779
30933
|
if (alloc)
|
|
30780
30934
|
bindNewWorktree(
|
|
@@ -30798,26 +30952,45 @@ function shutdownSessions(sessions) {
|
|
|
30798
30952
|
}
|
|
30799
30953
|
}
|
|
30800
30954
|
|
|
30801
|
-
// src/commands/sessions/daemon/
|
|
30802
|
-
function
|
|
30803
|
-
const
|
|
30804
|
-
if (!start3) return;
|
|
30805
|
-
session.pendingStart = void 0;
|
|
30955
|
+
// src/commands/sessions/daemon/heldStartBlocked.ts
|
|
30956
|
+
function heldStartBlocked(session, sessions) {
|
|
30957
|
+
const where = session.worktree?.path ?? "no worktree";
|
|
30806
30958
|
if (sessions.get(session.id) !== session) {
|
|
30807
30959
|
daemonLog(
|
|
30808
|
-
`session ${session.id} not started after seeding: card is gone (${
|
|
30960
|
+
`session ${session.id} not started after seeding: card is gone (${where})`
|
|
30809
30961
|
);
|
|
30810
|
-
return;
|
|
30962
|
+
return true;
|
|
30811
30963
|
}
|
|
30812
30964
|
if (session.closing) {
|
|
30813
30965
|
daemonLog(
|
|
30814
|
-
`session ${session.id} not started after seeding: card is closing (${
|
|
30966
|
+
`session ${session.id} not started after seeding: card is closing (${where})`
|
|
30815
30967
|
);
|
|
30816
|
-
return;
|
|
30968
|
+
return true;
|
|
30817
30969
|
}
|
|
30970
|
+
return false;
|
|
30971
|
+
}
|
|
30972
|
+
|
|
30973
|
+
// src/commands/sessions/daemon/startHeldSession.ts
|
|
30974
|
+
function startHeldSession(session, sessions, clients, onStatusChange, notify2) {
|
|
30975
|
+
const start3 = session.pendingStart;
|
|
30976
|
+
if (!start3) return;
|
|
30977
|
+
session.pendingStart = void 0;
|
|
30978
|
+
if (heldStartBlocked(session, sessions)) return;
|
|
30818
30979
|
session.startedAt = Date.now();
|
|
30819
30980
|
if (session.status === "running") session.runningSince = session.startedAt;
|
|
30820
|
-
|
|
30981
|
+
try {
|
|
30982
|
+
session.pty = start3();
|
|
30983
|
+
} catch (error) {
|
|
30984
|
+
refuseSpawn(
|
|
30985
|
+
session,
|
|
30986
|
+
error,
|
|
30987
|
+
clients,
|
|
30988
|
+
onStatusChange,
|
|
30989
|
+
"started after seeding"
|
|
30990
|
+
);
|
|
30991
|
+
notify2();
|
|
30992
|
+
return;
|
|
30993
|
+
}
|
|
30821
30994
|
if (session.cols && session.rows)
|
|
30822
30995
|
try {
|
|
30823
30996
|
session.pty?.resize(session.cols, session.rows);
|
|
@@ -30964,13 +31137,13 @@ async function defaultConnect() {
|
|
|
30964
31137
|
}
|
|
30965
31138
|
|
|
30966
31139
|
// src/commands/sessions/daemon/hasPersistedWindowsSessions.ts
|
|
30967
|
-
import { existsSync as
|
|
31140
|
+
import { existsSync as existsSync71, readFileSync as readFileSync51 } from "fs";
|
|
30968
31141
|
import { posix } from "path";
|
|
30969
31142
|
function hasPersistedWindowsSessions() {
|
|
30970
31143
|
const sessionsFile = windowsSessionsFileFromWsl();
|
|
30971
31144
|
if (!sessionsFile) return false;
|
|
30972
31145
|
try {
|
|
30973
|
-
if (!
|
|
31146
|
+
if (!existsSync71(sessionsFile)) return false;
|
|
30974
31147
|
const data = JSON.parse(readFileSync51(sessionsFile, "utf8"));
|
|
30975
31148
|
return Array.isArray(data) && data.length > 0;
|
|
30976
31149
|
} catch (error) {
|
|
@@ -32005,9 +32178,9 @@ function safeParse2(line) {
|
|
|
32005
32178
|
}
|
|
32006
32179
|
|
|
32007
32180
|
// src/commands/sessions/daemon/repoDirExists.ts
|
|
32008
|
-
import { existsSync as
|
|
32181
|
+
import { existsSync as existsSync72 } from "fs";
|
|
32009
32182
|
function repoDirExists(cwd) {
|
|
32010
|
-
return
|
|
32183
|
+
return existsSync72(toGitCwd(cwd));
|
|
32011
32184
|
}
|
|
32012
32185
|
|
|
32013
32186
|
// src/commands/sessions/daemon/withRepoGroups.ts
|