@cotal-ai/cli 0.154.0 → 0.155.1
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/commands/agents.js +3 -3
- package/dist/commands/agents.js.map +1 -1
- package/dist/commands/space-registration.d.ts.map +1 -1
- package/dist/commands/space-registration.js +17 -6
- package/dist/commands/space-registration.js.map +1 -1
- package/dist/commands/space.d.ts +2 -0
- package/dist/commands/space.d.ts.map +1 -1
- package/dist/commands/space.js +5 -1
- package/dist/commands/space.js.map +1 -1
- package/dist/commands/spawn-preview.d.ts.map +1 -1
- package/dist/commands/spawn-preview.js +4 -8
- package/dist/commands/spawn-preview.js.map +1 -1
- package/dist/commands/stack-down.d.ts.map +1 -1
- package/dist/commands/stack-down.js +13 -8
- package/dist/commands/stack-down.js.map +1 -1
- package/dist/commands/stack-lost-records.d.ts +7 -0
- package/dist/commands/stack-lost-records.d.ts.map +1 -0
- package/dist/commands/stack-lost-records.js +143 -0
- package/dist/commands/stack-lost-records.js.map +1 -0
- package/dist/commands/stack-up.d.ts +3 -3
- package/dist/commands/stack-up.d.ts.map +1 -1
- package/dist/commands/stack-up.js +20 -12
- package/dist/commands/stack-up.js.map +1 -1
- package/dist/commands/status.d.ts +4 -0
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +89 -37
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +8 -6
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/team-boot-controller.d.ts.map +1 -1
- package/dist/commands/team-boot-controller.js +3 -1
- package/dist/commands/team-boot-controller.js.map +1 -1
- package/dist/commands/team-channel-remove.d.ts +2 -2
- package/dist/commands/team.d.ts.map +1 -1
- package/dist/commands/team.js +6 -2
- package/dist/commands/team.js.map +1 -1
- package/dist/commands/whoami.d.ts.map +1 -1
- package/dist/commands/whoami.js +7 -11
- package/dist/commands/whoami.js.map +1 -1
- package/dist/help.d.ts.map +1 -1
- package/dist/help.js +11 -2
- package/dist/help.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/controller-resume.d.ts.map +1 -1
- package/dist/lib/controller-resume.js +2 -1
- package/dist/lib/controller-resume.js.map +1 -1
- package/dist/lib/persona-files.d.ts.map +1 -1
- package/dist/lib/persona-files.js +26 -11
- package/dist/lib/persona-files.js.map +1 -1
- package/dist/lib/restore.d.ts.map +1 -1
- package/dist/lib/restore.js +3 -1
- package/dist/lib/restore.js.map +1 -1
- package/dist/lib/self-exec.d.ts +2 -2
- package/dist/lib/self-exec.d.ts.map +1 -1
- package/dist/lib/self-exec.js +6 -3
- package/dist/lib/self-exec.js.map +1 -1
- package/dist/lib/service-presets.d.ts.map +1 -1
- package/dist/lib/service-presets.js +7 -1
- package/dist/lib/service-presets.js.map +1 -1
- package/dist/lib/service-systemd-files.d.ts +3 -0
- package/dist/lib/service-systemd-files.d.ts.map +1 -1
- package/dist/lib/service-systemd-files.js +23 -0
- package/dist/lib/service-systemd-files.js.map +1 -1
- package/dist/lib/stack-journal.d.ts +16 -0
- package/dist/lib/stack-journal.d.ts.map +1 -1
- package/dist/lib/stack-journal.js +39 -8
- package/dist/lib/stack-journal.js.map +1 -1
- package/dist/lib/store-identity.d.ts +15 -0
- package/dist/lib/store-identity.d.ts.map +1 -0
- package/dist/lib/store-identity.js +30 -0
- package/dist/lib/store-identity.js.map +1 -0
- package/package.json +4 -4
package/dist/commands/status.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { accessSync, constants, existsSync } from "node:fs";
|
|
1
|
+
import { accessSync, constants, existsSync, lstatSync } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { CotalEndpoint, agentFilePath, dialerFor, EpEnvelopeError, formatAge, isReachable, mintCreds, mintLifecycleUid, resolveService, realDirNoSymlink, invokeRepairingSplit, standaloneConnectOpts, newIdentity, idFromCreds, principalKey, DEV_OWNER, unansweredRequest, resolveAuthProvider, personaDir, presenceText, } from "@cotal-ai/core";
|
|
5
|
-
import { stoppedLoopbackListener, accountInventory, assessMaintenanceClaim, authDir, isCommandFailure, readMaintenanceJournal, recordsElsewhereError, recordsElsewhereText, restoreRolledBackRuns, targetErrorSpace, brokerAddressForDisplay, brokerCheck, canonicalRoot, CLI_USER_ACTOR, codeSpan, CommandFailure, deliveryCredsKey, DELIVERY_PIDFILE, extensionsDir, findCotalRoot, folderSpaceOverSelection, meshesForRoot, resolveReadsLiveness, stoppedStack, unsettledStackOperation, withdrawOperationNote, hasUserAuthState, homeCotalDir, cachedPreferences, loadLaunchPersonaFile, resolvePreference, isMeshRegistryError, isWorkspaceTargetError, jsonFlag, loadManagerInstanceIdentity, loadSoleSpaceAuth, localProcessPath, localProcessVisible, MANAGER_PIDFILE, oneLine, PREFERENCE_KEYS, parsePid, plainFlag, preflightTarget, presentationOf, principalFromBearer, probeLiveness, probeWebReadiness, readCurrent, readMeshRegistry, readPidfile, preparedRecordSkipped, readPreparedSpaces, readRenewalRecord, renderWorkspaceError, resolveMeshTarget, serverFlag, spaceFlag, userAuthStateDir, WEB_PIDFILE, WEB_READINESS_HEADER, workspaceSecretStore, writeResult, workspaceErrorParts, preferencesPath, preferencesFolderWarning, cotalDirLink, movedProject, spaceStreamsAbsent, spaceStreamsExpected, resolveControlTarget, } from "@cotal-ai/workspace";
|
|
5
|
+
import { stoppedLoopbackListener, accountInventory, assessMaintenanceClaim, authDir, isCommandFailure, readMaintenanceJournal, recordsElsewhereError, recordsElsewhereText, restoreRolledBackRuns, targetErrorSpace, brokerAddressForDisplay, brokerCheck, canonicalRoot, CLI_USER_ACTOR, codeSpan, CommandFailure, deliveryCredsKey, DELIVERY_PIDFILE, extensionsDir, findCotalRoot, folderSpaceOverSelection, meshesForRoot, resolveReadsLiveness, stoppedStack, unsettledStackOperation, withdrawOperationNote, hasUserAuthState, homeCotalDir, cachedPreferences, loadLaunchPersonaFile, resolvePreference, isMeshRegistryError, isWorkspaceTargetError, jsonFlag, loadManagerInstanceIdentity, loadSoleSpaceAuth, localProcessPath, localProcessVisible, MANAGER_PIDFILE, oneLine, PREFERENCE_KEYS, parsePid, plainFlag, preflightTarget, presentationOf, principalFromBearer, probeLiveness, probeWebReadiness, readCurrent, readMeshRegistry, readPidfile, preparedRecordSkipped, readPreparedSpaces, stackJournalPath, readRenewalRecord, renderWorkspaceError, resolveMeshTarget, serverFlag, spaceFlag, userAuthStateDir, WEB_PIDFILE, WEB_READINESS_HEADER, workspaceSecretStore, writeResult, workspaceErrorParts, preferencesPath, preferencesFolderWarning, cotalDirLink, movedProject, spaceStreamsAbsent, spaceStreamsExpected, resolveControlTarget, } from "@cotal-ai/workspace";
|
|
6
6
|
import { localProcessSurface } from "../ext-loader.js";
|
|
7
7
|
import { cliVersion, cliProvenance, readExtensionVersions } from "../lib/version.js";
|
|
8
8
|
import { agentSkillsSkew } from "../lib/agent-skills.js";
|
|
@@ -30,9 +30,10 @@ import { freshTeamAttemptSettled, listFreshTeamAttempts, listFreshTeamSummaries
|
|
|
30
30
|
import { additiveTeamRecordedState, listAdditiveTeamAttempts } from "../lib/team-additive-attempt.js";
|
|
31
31
|
import { maintenanceNextStep, preservedSourceState, restoreNextStep, rolledBackNotes } from "../lib/restore.js";
|
|
32
32
|
import { attemptProcessesGone } from "../lib/isolated-broker.js";
|
|
33
|
-
import { journalRecordedElsewhere, readStackJournal, serviceHeldRecords, STACK_DEFINITION, stackStartedWithoutManager, stackOperationBlock,
|
|
33
|
+
import { journalRecordedElsewhere, readStackJournal, serviceHeldRecords, STACK_DEFINITION, stackStartedWithoutManager, stackOperationBlock, stackRootGone, unfinishedStackOperation, } from "../lib/stack-journal.js";
|
|
34
34
|
import { cotalLinkHere, homeHoldsStack, linkedCotalRemedy } from "../lib/paths.js";
|
|
35
35
|
import { storeWriteProblem } from "../lib/store-dir.js";
|
|
36
|
+
import { stackStoreWarnings } from "../lib/store-identity.js";
|
|
36
37
|
import { natsServerLabel } from "../lib/nats-bin.js";
|
|
37
38
|
import { askManager } from "@cotal-ai/workspace/control";
|
|
38
39
|
/** `--components` is the fail-loud health pass with a machine-readable EXIT DISPOSITION. Bare
|
|
@@ -59,21 +60,20 @@ export const statusFlags = [
|
|
|
59
60
|
jsonFlag,
|
|
60
61
|
plainFlag,
|
|
61
62
|
];
|
|
62
|
-
/** A project root with no registration or prepared space
|
|
63
|
-
*
|
|
64
|
-
*
|
|
63
|
+
/** A project root with an empty `.cotal/` and no registration or prepared space. The resolver
|
|
64
|
+
* gives it the default broker address, which any broker may hold, so status selects no space for
|
|
65
|
+
* it and dials nothing. A root whose records name no space is refused by the resolver instead, and
|
|
66
|
+
* status uses that refusal. */
|
|
65
67
|
class UnregisteredRoot {
|
|
66
68
|
root;
|
|
67
|
-
|
|
68
|
-
constructor(root, namesNoSpace) {
|
|
69
|
+
constructor(root) {
|
|
69
70
|
this.root = root;
|
|
70
|
-
this.namesNoSpace = namesNoSpace;
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
function resolveSelected(cwd, values, offline) {
|
|
74
74
|
try {
|
|
75
75
|
const target = resolveMeshTarget(cwd, { server: values.server, space: values.space, offline });
|
|
76
|
-
return target.source === "local-space" ? { ok: false, error: new UnregisteredRoot(target.root
|
|
76
|
+
return target.source === "local-space" ? { ok: false, error: new UnregisteredRoot(target.root) } : { ok: true, target };
|
|
77
77
|
}
|
|
78
78
|
catch (e) {
|
|
79
79
|
return { ok: false, error: e };
|
|
@@ -166,7 +166,20 @@ export async function status(args) {
|
|
|
166
166
|
const responder = await readResponderAxis(selected, preflight);
|
|
167
167
|
const folder = blocked ? unreadFolder(root, blocked) : await readFolder(root, selected, values, responder.state, gaps, hints);
|
|
168
168
|
const meshes = await readMeshes(cwd, selected, Boolean(values.all), gaps, offline, preflight);
|
|
169
|
-
const
|
|
169
|
+
const maintenance = blocked ? { root, space: null, state: "unreadable", attemptId: null, next: null } : readMaintenance(root, gaps);
|
|
170
|
+
const recovery = backupRecovery(maintenance);
|
|
171
|
+
const selectedHints = [];
|
|
172
|
+
const target = await readSelected(selected, responder, folder, gaps, selectedHints, preflight);
|
|
173
|
+
// A held backup must be released before this root can start. Keep the selection's diagnostics,
|
|
174
|
+
// but replace its ordinary restart advice with the journal's recovery step.
|
|
175
|
+
if (recovery)
|
|
176
|
+
hints.unshift(recovery);
|
|
177
|
+
else
|
|
178
|
+
hints.push(...selectedHints);
|
|
179
|
+
if (!blocked)
|
|
180
|
+
readStackStoreWarnings(root, gaps, hints);
|
|
181
|
+
if (selected.ok && selected.target.origin === "up" && !sameDir(root, selected.target.root))
|
|
182
|
+
readStackStoreWarnings(selected.target.root, gaps, hints, selected.target.space);
|
|
170
183
|
// The note under the heading sends here for this folder's own stack cut off with no recorded
|
|
171
184
|
// outcome under another selection. A blocked one is named with its block under `This folder's
|
|
172
185
|
// space` and its step first in Next, so that note is left out here, with its --json warning. One
|
|
@@ -174,11 +187,10 @@ export async function status(args) {
|
|
|
174
187
|
const held = folderStack(folder, target);
|
|
175
188
|
if (held && folder.stackBlock)
|
|
176
189
|
withdrawOperationNote("FOLDER_SPACE_NOT_RUNNING", held.space);
|
|
177
|
-
if (held && !folder.stackBlock && !folder.stackUnfinished && unsettledStackOperation(held.root, held.space))
|
|
190
|
+
if (!recovery && held && !folder.stackBlock && !folder.stackUnfinished && unsettledStackOperation(held.root, held.space))
|
|
178
191
|
hints.push(`start it with \`cotal up\` in ${held.root}`);
|
|
179
192
|
const services = readOwnedServices(selected, gaps);
|
|
180
193
|
const bootAttempts = blocked ? { root, state: "partial", observedAt: new Date().toISOString(), unsettled: [], additive: [], unreadable: [], elsewhere: [] } : readBootAttempts(root, gaps);
|
|
181
|
-
const maintenance = blocked ? { root, space: null, state: "unreadable", attemptId: null, next: null } : readMaintenance(root, gaps);
|
|
182
194
|
const logged = selected.ok ? brokerStoreWrite(selected.target.root, selected.target.space) : undefined;
|
|
183
195
|
// A store write failure holds until the broker restarts, so it is named before a full store.
|
|
184
196
|
const full = selected.ok && target.resolved && target.storedBytes !== null && logged?.state !== "write-failed" ? storeFull(selected.target.root, selected.target.space, target.storedBytes) : undefined;
|
|
@@ -227,7 +239,7 @@ function folderReadProblem(root) {
|
|
|
227
239
|
/** The folder section for a folder this account cannot read: every part of it is unread. */
|
|
228
240
|
function unreadFolder(root, code) {
|
|
229
241
|
const error = `${root} cannot be read by this account (${code})`;
|
|
230
|
-
return { root, auth: { state: "unreadable", spaces: [], userAuth: false, error }, personas: { root: personaDir(root), default: "unreadable", demo: null, error }, launches: null, processes: null, processSpace: null, storedState: false, controllerStack: false, noManager: false, stackBlock: null };
|
|
242
|
+
return { root, auth: { state: "unreadable", spaces: [], userAuth: false, error }, personas: { root: personaDir(root), default: "unreadable", demo: null, error }, launches: null, processes: null, processSpace: null, storedState: false, controllerStack: false, stackJournal: "unreadable", noManager: false, stackBlock: null };
|
|
231
243
|
}
|
|
232
244
|
/** The folder's maintenance journal, read only. An unreadable one is reported, never taken for none. */
|
|
233
245
|
function readMaintenance(root, gaps) {
|
|
@@ -537,6 +549,13 @@ async function readResponderAxis(selected, preflight) {
|
|
|
537
549
|
}
|
|
538
550
|
async function readFolder(root, selected, values, responder, gaps, hints) {
|
|
539
551
|
const cmd = displayCmd();
|
|
552
|
+
let stackJournal;
|
|
553
|
+
try {
|
|
554
|
+
stackJournal = lstatSync(stackJournalPath(root), { throwIfNoEntry: false }) === undefined ? "absent" : "present";
|
|
555
|
+
}
|
|
556
|
+
catch {
|
|
557
|
+
stackJournal = "unreadable";
|
|
558
|
+
}
|
|
540
559
|
const folder = (auth, processes = null, storedState = false, processSpace = null, controllerStack = false, noManager = false, stackBlock = null, stackUnfinished = null) => ({
|
|
541
560
|
root,
|
|
542
561
|
auth,
|
|
@@ -545,6 +564,7 @@ async function readFolder(root, selected, values, responder, gaps, hints) {
|
|
|
545
564
|
processSpace,
|
|
546
565
|
storedState,
|
|
547
566
|
controllerStack,
|
|
567
|
+
stackJournal,
|
|
548
568
|
noManager,
|
|
549
569
|
stackBlock,
|
|
550
570
|
stackUnfinished,
|
|
@@ -646,9 +666,9 @@ async function readFolder(root, selected, values, responder, gaps, hints) {
|
|
|
646
666
|
controllerStack = false;
|
|
647
667
|
// A journal no command can use is named with its way out, in the warnings and under Next; the
|
|
648
668
|
// rest of the report still stands.
|
|
649
|
-
if (isCommandFailure(error) && error.code === "SCHEMA_INVALID") {
|
|
669
|
+
if (isCommandFailure(error) && (error.code === "SCHEMA_INVALID" || error.code === "PERMISSION_DENIED")) {
|
|
650
670
|
unusable = true;
|
|
651
|
-
gaps.add("STACK_JOURNAL_INVALID", error.remedy ? `${error.message}; ${error.remedy}` : error.message, { root });
|
|
671
|
+
gaps.add(error.code === "PERMISSION_DENIED" ? "STACK_JOURNAL_UNREADABLE" : "STACK_JOURNAL_INVALID", error.remedy ? `${error.message}; ${error.remedy}` : error.message, { root });
|
|
652
672
|
if (error.remedy)
|
|
653
673
|
hints.push(error.remedy);
|
|
654
674
|
}
|
|
@@ -997,9 +1017,10 @@ async function readMeshes(cwd, selected, all, gaps, offline, preflight) {
|
|
|
997
1017
|
catalog: m.origin === "catalog" ? { name: m.catalogName && m.catalogName !== m.space ? m.catalogName : null, fetchedAt: m.catalogFetchedAt ?? null, error: m.catalogError ?? null } : null,
|
|
998
1018
|
})));
|
|
999
1019
|
// The prepared spaces folder, or a record in it, that cannot be read is named the same way.
|
|
1000
|
-
let currentPrepared = false, currentStopped = false;
|
|
1020
|
+
let currentPrepared = false, currentStopped = false, hasPrepared = false;
|
|
1001
1021
|
try {
|
|
1002
1022
|
const prepared = readPreparedSpaces();
|
|
1023
|
+
hasPrepared = prepared.spaces.length > 0;
|
|
1003
1024
|
// Named with the warning a command that skips it gives, so a run that also skipped it says it once.
|
|
1004
1025
|
for (const e of prepared.unusable) {
|
|
1005
1026
|
const skipped = preparedRecordSkipped(e);
|
|
@@ -1019,6 +1040,7 @@ async function readMeshes(cwd, selected, all, gaps, offline, preflight) {
|
|
|
1019
1040
|
current,
|
|
1020
1041
|
currentRecorded: current === null || entries.some((m) => m.space === current),
|
|
1021
1042
|
currentPrepared,
|
|
1043
|
+
hasPrepared,
|
|
1022
1044
|
currentStopped,
|
|
1023
1045
|
folder,
|
|
1024
1046
|
unreadable,
|
|
@@ -1026,6 +1048,25 @@ async function readMeshes(cwd, selected, all, gaps, offline, preflight) {
|
|
|
1026
1048
|
catalogs: preparedCatalogDiagnostics().map((r) => ({ idp: r.account.idpUrl, account: r.account.sub, state: r.state, error: r.state === "failed" ? (r.error ?? "refresh failed") : null })),
|
|
1027
1049
|
};
|
|
1028
1050
|
}
|
|
1051
|
+
/** Diagnose the current folder's store and a selected local stack independently of folder trust. */
|
|
1052
|
+
function readStackStoreWarnings(root, gaps, hints, space) {
|
|
1053
|
+
try {
|
|
1054
|
+
const journal = readStackJournal(root)?.journal;
|
|
1055
|
+
if (journal && (space === undefined || journal.space === space))
|
|
1056
|
+
for (const warning of stackStoreWarnings(journal))
|
|
1057
|
+
gaps.add(warning.code, warning.message, warning.target);
|
|
1058
|
+
}
|
|
1059
|
+
catch (error) {
|
|
1060
|
+
if (isCommandFailure(error) && (error.code === "SCHEMA_INVALID" || error.code === "PERMISSION_DENIED")) {
|
|
1061
|
+
gaps.add(error.code === "PERMISSION_DENIED" ? "STACK_JOURNAL_UNREADABLE" : "STACK_JOURNAL_INVALID", error.remedy ? `${error.message}; ${error.remedy}` : error.message, { root });
|
|
1062
|
+
if (error.remedy)
|
|
1063
|
+
hints.push(error.remedy);
|
|
1064
|
+
}
|
|
1065
|
+
else if (!isCommandFailure(error) || error.code !== "CONFLICT")
|
|
1066
|
+
throw error;
|
|
1067
|
+
// A copied or moved journal is diagnosed by the folder and selection readers.
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1029
1070
|
/** The refusal of the stack journal at `root` when it is one no command can use, as `up` and `down`
|
|
1030
1071
|
* refuse it. Undefined when it reads or is absent. */
|
|
1031
1072
|
function unusableStackJournal(root) {
|
|
@@ -1034,7 +1075,7 @@ function unusableStackJournal(root) {
|
|
|
1034
1075
|
return undefined;
|
|
1035
1076
|
}
|
|
1036
1077
|
catch (error) {
|
|
1037
|
-
return isCommandFailure(error) && error.code === "SCHEMA_INVALID" ? error : undefined;
|
|
1078
|
+
return isCommandFailure(error) && (error.code === "SCHEMA_INVALID" || error.code === "PERMISSION_DENIED") ? error : undefined;
|
|
1038
1079
|
}
|
|
1039
1080
|
}
|
|
1040
1081
|
/** The refusal of `space`'s service instance records when one is a record no command can use, as
|
|
@@ -1086,11 +1127,7 @@ async function readSelected(selected, responder, folder, gaps, hints, preflight)
|
|
|
1086
1127
|
}
|
|
1087
1128
|
if (e instanceof UnregisteredRoot) {
|
|
1088
1129
|
const message = `no space is registered or prepared here for ${e.root}`;
|
|
1089
|
-
|
|
1090
|
-
// which then holds it, so the step that brings a forgotten space back is to prepare it again.
|
|
1091
|
-
hints.push(e.namesNoSpace
|
|
1092
|
-
? `prepare a space with \`${cmd} space create <name> --root ${shellWord(e.root)}\`, or select one with \`${cmd} use <name>\``
|
|
1093
|
-
: `run \`${cmd} up\` in ${e.root} to start one, or select a space with \`${cmd} use <name>\``);
|
|
1130
|
+
hints.push(`run \`${cmd} up\` in ${e.root} to start one, or select a space with \`${cmd} use <name>\``);
|
|
1094
1131
|
return { resolved: false, code: "unregistered-root", message };
|
|
1095
1132
|
}
|
|
1096
1133
|
if (!isWorkspaceTargetError(e))
|
|
@@ -1157,6 +1194,16 @@ async function readSelected(selected, responder, folder, gaps, hints, preflight)
|
|
|
1157
1194
|
spaceStreams: { state: "not-checked", root: null, stack: null },
|
|
1158
1195
|
storedBytes: null,
|
|
1159
1196
|
};
|
|
1197
|
+
// A moved project keeps its trust under its new path. Prefer that specific diagnosis when
|
|
1198
|
+
// this folder holds the records, otherwise report the missing stack root before any auth probe.
|
|
1199
|
+
const moved = target.mode === "auth" && !target.auth ? movedProject(target.space, target.root) : undefined;
|
|
1200
|
+
if (moved) {
|
|
1201
|
+
hints.push(moved.remedy);
|
|
1202
|
+
return { resolved: false, code: "moved-folder", message: moved.message };
|
|
1203
|
+
}
|
|
1204
|
+
const overridden = (target.source === "flag-server" || target.source === "flag-space-override") && target.recordedServer !== target.server;
|
|
1205
|
+
if (base.rootGone && !overridden)
|
|
1206
|
+
return { ...base, connection: "root-gone", snapshot: { state: "skipped", reason: "the stack's project folder was moved or deleted" } };
|
|
1160
1207
|
if (target.mode === "user") {
|
|
1161
1208
|
// Status never static-mints on a user-auth mesh (the flip). Everything here is offline
|
|
1162
1209
|
// introspection — the login cache + the locally-readable ledger — plus, only when this
|
|
@@ -1189,13 +1236,6 @@ async function readSelected(selected, responder, folder, gaps, hints, preflight)
|
|
|
1189
1236
|
return { ...result, snapshot: { state: "skipped", reason: "needs a signed-in, granted login" } };
|
|
1190
1237
|
return { ...result, ...(await readSnapshot(() => userSnapshotEndpoint(target, stateDir), target.space, true, gaps)) };
|
|
1191
1238
|
}
|
|
1192
|
-
// A project folder moved after its stack started there keeps the seed under its new path, where a
|
|
1193
|
-
// probe without it reads as authentication required. Name the move and the way back, as send does.
|
|
1194
|
-
const moved = target.mode === "auth" && !target.auth ? movedProject(target.space, target.root) : undefined;
|
|
1195
|
-
if (moved) {
|
|
1196
|
-
hints.push(moved.remedy);
|
|
1197
|
-
return { resolved: false, code: "moved-folder", message: moved.message };
|
|
1198
|
-
}
|
|
1199
1239
|
if (preflight === undefined)
|
|
1200
1240
|
throw new Error("status: selected non-user target has no preflight observation");
|
|
1201
1241
|
if (!preflight.ok) {
|
|
@@ -1579,7 +1619,8 @@ function extensionsCell(e) {
|
|
|
1579
1619
|
* does not have is what `cotal setup` adds, which the applications line already counts. So an
|
|
1580
1620
|
* application's own rows (the Claude plugin, Desktop and skills) appear only for an application that
|
|
1581
1621
|
* IS set up, and only when they are not current; the broker executable appears when it is missing or
|
|
1582
|
-
* refused; the dashboard, which is optional, appears
|
|
1622
|
+
* refused and local broker records make it relevant; the dashboard, which is optional, appears
|
|
1623
|
+
* when its record cannot be read. `--all` and
|
|
1583
1624
|
* `--wide` print every row, and `--verbose` adds the installation paths ({@link machineSection}).
|
|
1584
1625
|
*
|
|
1585
1626
|
* It used to print one row per known connector whether or not it was set up, every extension label
|
|
@@ -1602,7 +1643,15 @@ function machineOverview(r, wide) {
|
|
|
1602
1643
|
continue;
|
|
1603
1644
|
rows.push([connector.name, stateCell(connector.missing.length ? `${connector.missing.join(", ")} not on PATH` : connector.requires.length ? "available on PATH" : "in-process", connector.missing.length ? "waiting" : "ok")]);
|
|
1604
1645
|
}
|
|
1605
|
-
|
|
1646
|
+
// A remote registration uses the broker elsewhere. Keep executable readiness in the full
|
|
1647
|
+
// diagnostics, without making a hosted client repair a binary it does not need to run.
|
|
1648
|
+
const localBroker = r.meshes.hasPrepared || r.meshes.currentPrepared || r.meshes.unreadable.length > 0 ||
|
|
1649
|
+
r.meshes.items.some((entry) => entry.origin !== "manual" && entry.origin !== "catalog") ||
|
|
1650
|
+
r.folder.controllerStack || r.folder.storedState || r.folder.auth.state !== "none" ||
|
|
1651
|
+
(r.folder.stackJournal !== undefined && r.folder.stackJournal !== "absent") ||
|
|
1652
|
+
r.folder.processes?.some((row) => row.name === "nats" && (row.pid !== null || row.state === "unreadable")) ||
|
|
1653
|
+
(!r.selected.resolved && (r.selected.code === "not-started" || r.selected.code === "unregistered-root" || r.selected.code === "unreadable-auth"));
|
|
1654
|
+
if (wide || (localBroker && (m.nats === "missing" || m.natsProblem)))
|
|
1606
1655
|
rows.push(["Broker executable", stateCell(m.nats === "missing" ? "not installed" : brokerExecutable(m), m.nats === "missing" ? "waiting" : m.natsProblem ? "failed" : "ok")]);
|
|
1607
1656
|
if (wide || (m.skills.state !== "current" && setUp.length))
|
|
1608
1657
|
rows.push(["Agent skills", skillsCell(m.skills)]);
|
|
@@ -1621,15 +1670,11 @@ function machineOverview(r, wide) {
|
|
|
1621
1670
|
],
|
|
1622
1671
|
};
|
|
1623
1672
|
}
|
|
1624
|
-
/** Whether `root` is the gone folder of the stack this machine still records for `space`. */
|
|
1625
|
-
function stackRootGone(space, root) {
|
|
1626
|
-
const gone = goneStackRoot(space);
|
|
1627
|
-
return gone !== undefined && canonicalRoot(gone) === canonicalRoot(root);
|
|
1628
|
-
}
|
|
1629
1673
|
function connectionCell(s) {
|
|
1630
1674
|
if (s.connection === "ok" || s.connection === "reachable")
|
|
1631
1675
|
return stateCell("reachable", "ok");
|
|
1632
1676
|
const states = {
|
|
1677
|
+
"root-gone": "project folder was moved or deleted",
|
|
1633
1678
|
unreachable: "unreachable",
|
|
1634
1679
|
"slow-link": "not answering",
|
|
1635
1680
|
"broker-stopped": `broker process ${s.brokerPid} is stopped`,
|
|
@@ -1831,7 +1876,14 @@ function stackLines(folder) {
|
|
|
1831
1876
|
function roleList(roles) {
|
|
1832
1877
|
return roles.length > 1 ? `${roles.slice(0, -1).join(", ")} and ${roles.at(-1)}` : roles[0] ?? "";
|
|
1833
1878
|
}
|
|
1879
|
+
/** Pre-commit backup claims block ordinary startup; ready cuts and committed restores do not. */
|
|
1880
|
+
function backupRecovery(maintenance) {
|
|
1881
|
+
return maintenance?.state === "claimed" || maintenance?.state === "restore-ready" ? maintenance.next : null;
|
|
1882
|
+
}
|
|
1834
1883
|
function nextActions(r) {
|
|
1884
|
+
const recovery = backupRecovery(r.maintenance);
|
|
1885
|
+
if (recovery)
|
|
1886
|
+
return { title: "Next", blocks: [...new Set([recovery, ...r.hints])].map((hint) => ({ kind: "lines", lines: [cell(hint, "hint")] })) };
|
|
1835
1887
|
const cmd = displayCmd();
|
|
1836
1888
|
const items = [];
|
|
1837
1889
|
const extra = [];
|