flowviant 0.102.2 → 0.104.0
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/bin/cli.mjs +87 -474
- package/bin/lib/admission.mjs +19 -3
- package/bin/lib/agentPlan.mjs +2 -2
- package/bin/lib/agentTaskKinds.mjs +93 -0
- package/bin/lib/agySessions.mjs +158 -0
- package/bin/lib/artifactPolicy.mjs +44 -0
- package/bin/lib/artifactPreview.mjs +52 -0
- package/bin/lib/artifactReporter.mjs +163 -0
- package/bin/lib/artifacts.mjs +47 -227
- package/bin/lib/authReject.mjs +16 -4
- package/bin/lib/authproxy.mjs +47 -304
- package/bin/lib/boxIdentity.mjs +245 -0
- package/bin/lib/childEnv.mjs +11 -9
- package/bin/lib/claude.mjs +14 -851
- package/bin/lib/claudeAuth.mjs +3 -3
- package/bin/lib/claudePosture.mjs +367 -0
- package/bin/lib/claudeSessions.mjs +411 -0
- package/bin/lib/claudeStream.mjs +207 -0
- package/bin/lib/cliLimit.mjs +34 -0
- package/bin/lib/commandSpecs.mjs +173 -0
- package/bin/lib/config.mjs +11 -42
- package/bin/lib/credentialDate.mjs +22 -0
- package/bin/lib/credentials.mjs +21 -26
- package/bin/lib/daemonLogging.mjs +55 -0
- package/bin/lib/daemonState.mjs +64 -0
- package/bin/lib/deploy.mjs +148 -647
- package/bin/lib/deployCheckout.mjs +265 -0
- package/bin/lib/deployConfig.mjs +142 -0
- package/bin/lib/deployRunner.mjs +311 -0
- package/bin/lib/deployWire.mjs +41 -0
- package/bin/lib/desktopContract.mjs +56 -105
- package/bin/lib/env.mjs +49 -484
- package/bin/lib/fleet.mjs +184 -2396
- package/bin/lib/fleetJobs.mjs +120 -0
- package/bin/lib/fleetPost.mjs +40 -0
- package/bin/lib/fleetReports.mjs +392 -0
- package/bin/lib/fleetRoster.mjs +470 -0
- package/bin/lib/fleetStartup.mjs +196 -0
- package/bin/lib/fleetWake.mjs +45 -0
- package/bin/lib/fleetWire.mjs +46 -0
- package/bin/lib/git.mjs +34 -3
- package/bin/lib/gitMergeIdentity.mjs +51 -0
- package/bin/lib/grant.mjs +5 -3
- package/bin/lib/help.mjs +3 -125
- package/bin/lib/holder.mjs +482 -0
- package/bin/lib/instance.mjs +124 -46
- package/bin/lib/knowledge.mjs +16 -799
- package/bin/lib/knowledgeFetch.mjs +38 -0
- package/bin/lib/knowledgeFileSync.mjs +112 -0
- package/bin/lib/knowledgeFiles.mjs +487 -0
- package/bin/lib/knowledgeLibrary.mjs +249 -0
- package/bin/lib/landed.mjs +2 -1
- package/bin/lib/listeners.mjs +99 -141
- package/bin/lib/localSessions.mjs +30 -555
- package/bin/lib/login.mjs +3 -2
- package/bin/lib/loginPath.mjs +3 -5
- package/bin/lib/machineDisconnect.mjs +203 -0
- package/bin/lib/machineEnv.mjs +22 -0
- package/bin/lib/machineLimits.mjs +61 -0
- package/bin/lib/machineListing.mjs +338 -0
- package/bin/lib/machines.mjs +78 -455
- package/bin/lib/machinesCommand.mjs +214 -0
- package/bin/lib/mcp-cli.mjs +2 -1
- package/bin/lib/patchRevert.mjs +60 -0
- package/bin/lib/prWorkflow.mjs +157 -0
- package/bin/lib/preflight.mjs +2 -1
- package/bin/lib/pressurePolicy.mjs +104 -0
- package/bin/lib/preview.mjs +2 -272
- package/bin/lib/previewFramePolicy.mjs +112 -0
- package/bin/lib/previewGatePolicy.mjs +236 -0
- package/bin/lib/previewRegistry.mjs +235 -0
- package/bin/lib/printable.mjs +2 -2
- package/bin/lib/procRegistry.mjs +28 -18
- package/bin/lib/procScan.mjs +146 -0
- package/bin/lib/processes.mjs +29 -37
- package/bin/lib/projectToolRuntimes.mjs +138 -0
- package/bin/lib/projectTools.mjs +13 -88
- package/bin/lib/prompts.mjs +28 -42
- package/bin/lib/repoPath.mjs +26 -0
- package/bin/lib/resources.mjs +7 -69
- package/bin/lib/resumeLost.mjs +107 -0
- package/bin/lib/runTurn.mjs +324 -0
- package/bin/lib/runtimeAntigravity.mjs +230 -0
- package/bin/lib/runtimeCapabilities.mjs +334 -0
- package/bin/lib/runtimeClaude.mjs +126 -0
- package/bin/lib/runtimeCodex.mjs +217 -0
- package/bin/lib/runtimeDetection.mjs +121 -0
- package/bin/lib/runtimeEvents.mjs +482 -0
- package/bin/lib/runtimeOfferings.mjs +4 -1
- package/bin/lib/runtimes.mjs +42 -1402
- package/bin/lib/safeFileName.mjs +74 -0
- package/bin/lib/shipMerge.mjs +33 -2
- package/bin/lib/standDownExit.mjs +181 -0
- package/bin/lib/standDownGate.mjs +48 -0
- package/bin/lib/startProject.mjs +279 -0
- package/bin/lib/trace.mjs +3 -3
- package/bin/lib/turnLock.mjs +68 -0
- package/bin/lib/turnProfile.mjs +77 -0
- package/bin/lib/uninstall.mjs +55 -45
- package/bin/lib/uninstallCopies.mjs +112 -0
- package/bin/lib/update.mjs +30 -17
- package/bin/lib/uplinkScrub.mjs +265 -0
- package/bin/lib/vault.mjs +22 -151
- package/bin/lib/vaultDiff.mjs +183 -0
- package/bin/lib/versionOrder.mjs +42 -0
- package/bin/lib/views.mjs +35 -12
- package/bin/lib/wikiRunner.mjs +630 -0
- package/bin/lib/work.mjs +188 -3037
- package/bin/lib/workAdoptCarry.mjs +94 -0
- package/bin/lib/workAgentCheck.mjs +278 -0
- package/bin/lib/workAgentMerges.mjs +54 -142
- package/bin/lib/workAgentPlans.mjs +14 -12
- package/bin/lib/workAgentPrecheck.mjs +393 -0
- package/bin/lib/workAgentPublish.mjs +177 -0
- package/bin/lib/workAgentReview.mjs +8 -666
- package/bin/lib/workAgentTurnBegun.mjs +109 -0
- package/bin/lib/workAgentTurnExecution.mjs +604 -0
- package/bin/lib/workAgentTurnOutcome.mjs +207 -0
- package/bin/lib/workAgentTurnReports.mjs +189 -0
- package/bin/lib/workAgentTurns.mjs +92 -897
- package/bin/lib/workAttachments.mjs +93 -0
- package/bin/lib/workBrain.mjs +53 -0
- package/bin/lib/workChildren.mjs +110 -0
- package/bin/lib/workCommandAudit.mjs +62 -0
- package/bin/lib/workDiffs.mjs +2 -1
- package/bin/lib/workNarration.mjs +152 -0
- package/bin/lib/workPlaceReports.mjs +426 -0
- package/bin/lib/workPlaces.mjs +228 -0
- package/bin/lib/workPreviews.mjs +31 -9
- package/bin/lib/workProcesses.mjs +4 -5
- package/bin/lib/workPullRequests.mjs +52 -126
- package/bin/lib/workReportQueue.mjs +254 -0
- package/bin/lib/workRetire.mjs +185 -0
- package/bin/lib/workSessionGroups.mjs +166 -0
- package/bin/lib/workSessionRuntime.mjs +164 -0
- package/bin/lib/workSessionTokens.mjs +68 -0
- package/bin/lib/workSessionTurns.mjs +892 -0
- package/bin/lib/workShip.mjs +9 -29
- package/bin/lib/workToolLog.mjs +76 -0
- package/package.json +3 -2
- package/bin/lib/patch.mjs +0 -363
package/bin/lib/instance.mjs
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* AFTER the work token is minted and the attachments are fetched — a window
|
|
25
25
|
* containing a network round trip — so both daemons clear the check and both
|
|
26
26
|
* spawn a CLI into one held conversation. It was built for a RESTARTED daemon
|
|
27
|
-
* (its own comment says so,
|
|
27
|
+
* (its own comment says so, turnLock.mjs), where the holder is already live when
|
|
28
28
|
* the successor looks; it was never a concurrency primitive.
|
|
29
29
|
*
|
|
30
30
|
* What a duplicate run cost before the session lease, all of it invisible in
|
|
@@ -89,7 +89,7 @@ import {
|
|
|
89
89
|
openSync,
|
|
90
90
|
readdirSync,
|
|
91
91
|
readFileSync,
|
|
92
|
-
|
|
92
|
+
renameSync,
|
|
93
93
|
rmSync,
|
|
94
94
|
writeFileSync,
|
|
95
95
|
writeSync,
|
|
@@ -98,6 +98,8 @@ import { homedir, platform } from 'node:os';
|
|
|
98
98
|
import { join } from 'node:path';
|
|
99
99
|
import { createHash } from 'node:crypto';
|
|
100
100
|
import { VERSION, PROCESS_STARTED_AT } from './config.mjs';
|
|
101
|
+
import { compareVersions } from './versionOrder.mjs';
|
|
102
|
+
import { samePath } from './repoPath.mjs';
|
|
101
103
|
|
|
102
104
|
/** Deliberately a HASH: a credential must never become a filename. */
|
|
103
105
|
export function instanceLockPath(fleetToken) {
|
|
@@ -105,20 +107,6 @@ export function instanceLockPath(fleetToken) {
|
|
|
105
107
|
return join(homedir(), '.flowviant', `daemon-${key}.lock`);
|
|
106
108
|
}
|
|
107
109
|
|
|
108
|
-
/** Numeric dotted compare, -1/0/1. Unparsable compares EQUAL, so a version we
|
|
109
|
-
* cannot read never silently authorises a downgrade. */
|
|
110
|
-
function cmpVersion(a, b) {
|
|
111
|
-
const x = String(a).split('.').map((n) => Number.parseInt(n, 10));
|
|
112
|
-
const y = String(b).split('.').map((n) => Number.parseInt(n, 10));
|
|
113
|
-
for (let i = 0; i < Math.max(x.length, y.length); i++) {
|
|
114
|
-
const p = x[i] ?? 0;
|
|
115
|
-
const q = y[i] ?? 0;
|
|
116
|
-
if (Number.isNaN(p) || Number.isNaN(q)) return 0;
|
|
117
|
-
if (p !== q) return p > q ? 1 : -1;
|
|
118
|
-
}
|
|
119
|
-
return 0;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
110
|
/** Signal 0 — a liveness probe, not a kill. EPERM means alive and not ours. */
|
|
123
111
|
function alive(pid) {
|
|
124
112
|
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
@@ -139,6 +127,19 @@ function readHolder(path) {
|
|
|
139
127
|
}
|
|
140
128
|
}
|
|
141
129
|
|
|
130
|
+
/**
|
|
131
|
+
* WHAT THE DAEMON HOLDING THIS CREDENTIAL'S LOCK IS FINISHING — the `target →
|
|
132
|
+
* env` words its draining mark carries (`markLockDraining`), or null. Read-only,
|
|
133
|
+
* for `status --json` (desktopContract.mjs), so the tray can say the box is
|
|
134
|
+
* finishing a deploy rather than idle. Null covers no lock, no mark, and an
|
|
135
|
+
* older daemon that never writes one; the caller asks only of a daemon it has
|
|
136
|
+
* already measured running.
|
|
137
|
+
*/
|
|
138
|
+
export function drainingFor(fleetToken) {
|
|
139
|
+
const what = readHolder(instanceLockPath(fleetToken))?.draining?.what;
|
|
140
|
+
return typeof what === 'string' && what ? what : null;
|
|
141
|
+
}
|
|
142
|
+
|
|
142
143
|
/** Read-only desktop measurement. No lock is unknown because an unguarded or
|
|
143
144
|
* older daemon may still run; a stale lock with a dead pid measures empty. */
|
|
144
145
|
export function daemonRunningFor(fleetToken) {
|
|
@@ -258,20 +259,6 @@ function isOurReexecParent(pid) {
|
|
|
258
259
|
return false;
|
|
259
260
|
}
|
|
260
261
|
|
|
261
|
-
/** Same directory, whatever it is spelled as — symlinks and trailing slashes
|
|
262
|
-
* included. A repo compared by string would let `/repo` and `/repo/` past. */
|
|
263
|
-
function samePath(a, b) {
|
|
264
|
-
if (!a || !b) return false;
|
|
265
|
-
const norm = (v) => {
|
|
266
|
-
try {
|
|
267
|
-
return realpathSync(v);
|
|
268
|
-
} catch {
|
|
269
|
-
return String(v).replace(/\/+$/, '');
|
|
270
|
-
}
|
|
271
|
-
};
|
|
272
|
-
return norm(a) === norm(b);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
262
|
/**
|
|
276
263
|
* A LIVE daemon in this same checkout, under a DIFFERENT credential.
|
|
277
264
|
*
|
|
@@ -636,10 +623,21 @@ const TAKEOVER_GRACE_MS = 20_000;
|
|
|
636
623
|
* this same lock through the ppid branch. Treating that as free steals a live
|
|
637
624
|
* daemon's lock and leaves it running unguarded — measured doing exactly that.
|
|
638
625
|
*
|
|
626
|
+
* A DRAINING HOLDER IS NEVER SIGNALLED (ruling 2026-09-26, "a stand-down lets
|
|
627
|
+
* a deploy finish and report"). It is already standing down and leaves by
|
|
628
|
+
* itself once its deploy's outcome is posted, and a signal can only cost that
|
|
629
|
+
* outcome. So the mark is read BEFORE the SIGTERM, and again on every look
|
|
630
|
+
* after it (the holder writes it the moment our SIGTERM finds a deploy in
|
|
631
|
+
* flight). What happens then is the caller's: `flowviant stop` is told so and
|
|
632
|
+
* returns (`draining`), a takeover waits for the lock to clear
|
|
633
|
+
* (`waitOutDrain`) — one line said once, no deadline, and Ctrl+C on the
|
|
634
|
+
* waiting process abandons only the wait (no handler is installed yet, so the
|
|
635
|
+
* default action ends the process; the draining daemon is untouched).
|
|
636
|
+
*
|
|
639
637
|
* Returns null when the holder is stopped and its lock file is cleared, or
|
|
640
638
|
* `{ failed }` with a sentence the caller can print as-is.
|
|
641
639
|
*/
|
|
642
|
-
function standDown(holder, path, log) {
|
|
640
|
+
function standDown(holder, path, log, { waitOutDrain = false } = {}) {
|
|
643
641
|
// NEVER SIGNAL OURSELVES, and this is not a theoretical guard — it was
|
|
644
642
|
// reproduced end to end. A 0.54.0+ lock records `entry` = the daemon's
|
|
645
643
|
// argv[1], i.e. `…/bin/cli.mjs`. Run `flowviant stop` and OUR cmdline is
|
|
@@ -653,19 +651,23 @@ function standDown(holder, path, log) {
|
|
|
653
651
|
// identity check cannot catch it and the guard belongs here, at the one place
|
|
654
652
|
// that signals, rather than in each caller.
|
|
655
653
|
if (holder.pid === process.pid) return { failed: 'refusing to signal this very process' };
|
|
656
|
-
log?.(`asking daemon pid ${holder.pid} to stand down…`);
|
|
657
|
-
try {
|
|
658
|
-
process.kill(holder.pid, 'SIGTERM');
|
|
659
|
-
} catch {
|
|
660
|
-
return { failed: `could not signal pid ${holder.pid}` };
|
|
661
|
-
}
|
|
662
|
-
|
|
663
654
|
const standing = () => {
|
|
664
655
|
const now = readHolder(path);
|
|
665
656
|
if (!now || !alive(now.pid)) return null;
|
|
666
657
|
return now;
|
|
667
658
|
};
|
|
659
|
+
const before = standing();
|
|
660
|
+
if (!(before?.pid === holder.pid && before.draining)) {
|
|
661
|
+
log?.(`asking daemon pid ${holder.pid} to stand down…`);
|
|
662
|
+
try {
|
|
663
|
+
process.kill(holder.pid, 'SIGTERM');
|
|
664
|
+
} catch {
|
|
665
|
+
return { failed: `could not signal pid ${holder.pid}` };
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
668
669
|
const deadline = Date.now() + TAKEOVER_GRACE_MS;
|
|
670
|
+
let saidWaiting = false;
|
|
669
671
|
for (;;) {
|
|
670
672
|
const now = standing();
|
|
671
673
|
if (!now) break;
|
|
@@ -674,6 +676,28 @@ function standDown(holder, path, log) {
|
|
|
674
676
|
failed: `the daemon handed over to pid ${now.pid}${now.version ? ` (${now.version})` : ''} while we waited — it is mid-update, so try again in a moment`,
|
|
675
677
|
};
|
|
676
678
|
}
|
|
679
|
+
/**
|
|
680
|
+
* IT IS FINISHING A DEPLOY — never forced (see above). The lock's mark
|
|
681
|
+
* (`markLockDraining`) says it has stopped taking work and leaves by
|
|
682
|
+
* itself once the outcome is posted; a SIGKILL here would lose exactly
|
|
683
|
+
* that outcome, so the server would hand the same irreversible job out
|
|
684
|
+
* again.
|
|
685
|
+
*/
|
|
686
|
+
if (now.draining) {
|
|
687
|
+
const what = typeof now.draining.what === 'string' && now.draining.what ? ` (${now.draining.what})` : '';
|
|
688
|
+
if (!waitOutDrain) {
|
|
689
|
+
return {
|
|
690
|
+
draining: true,
|
|
691
|
+
failed: `pid ${holder.pid} is finishing a deploy${what} and stops by itself once its outcome is reported — it was not forced`,
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
if (!saidWaiting) {
|
|
695
|
+
saidWaiting = true;
|
|
696
|
+
log?.(`pid ${holder.pid} is finishing a deploy${what} — waiting…`);
|
|
697
|
+
}
|
|
698
|
+
sleep(1000);
|
|
699
|
+
continue;
|
|
700
|
+
}
|
|
677
701
|
if (Date.now() >= deadline) {
|
|
678
702
|
log?.(`pid ${holder.pid} did not stand down within ${TAKEOVER_GRACE_MS / 1000}s — forcing it.`);
|
|
679
703
|
try {
|
|
@@ -734,13 +758,23 @@ function takeOverFrom(holder, path, log, { allowDowngrade = false } = {}) {
|
|
|
734
758
|
// clears the corpse the same way it clears a dead pid's.
|
|
735
759
|
return { stale: true };
|
|
736
760
|
}
|
|
737
|
-
|
|
761
|
+
/**
|
|
762
|
+
* THE DOWNGRADE GUARD refuses only a MEASURED downgrade. A holder version
|
|
763
|
+
* this daemon cannot read (versionOrder.mjs answers unknown) gates nothing,
|
|
764
|
+
* the same as before the one comparator existed: ignorance never refuses
|
|
765
|
+
* (CLAUDE.md, "What Flowviant is"). The old comment here claimed the
|
|
766
|
+
* opposite while its code let the takeover through; the code was the rule.
|
|
767
|
+
* A lock with NO version (an older daemon wrote it) is not compared either.
|
|
768
|
+
*/
|
|
769
|
+
if (!allowDowngrade && holder.version && compareVersions(VERSION, holder.version) === -1) {
|
|
738
770
|
return {
|
|
739
771
|
failed: `the running daemon is ${holder.version} and this one is ${VERSION} — refusing to replace a newer daemon with an older one (--takeover-downgrade if you mean it)`,
|
|
740
772
|
};
|
|
741
773
|
}
|
|
742
774
|
|
|
743
|
-
|
|
775
|
+
// A takeover WAITS OUT a drain (ruling 2026-09-26): the new start is what
|
|
776
|
+
// was asked for, and it arrives once the deploy has reported.
|
|
777
|
+
const bad = standDown(holder, path, log, { waitOutDrain: true });
|
|
744
778
|
if (bad) return bad;
|
|
745
779
|
log?.(`daemon pid ${holder.pid} stopped — taking over.`);
|
|
746
780
|
return null;
|
|
@@ -875,6 +909,37 @@ export function acquireInstanceLock(fleetToken, repoRoot, opts = {}) {
|
|
|
875
909
|
return { ok: false, holder: readHolder(path) };
|
|
876
910
|
}
|
|
877
911
|
|
|
912
|
+
/**
|
|
913
|
+
* MARK THIS PROCESS'S LOCK AS DRAINING — it is standing down and waits only
|
|
914
|
+
* for its in-flight deploys to report (ruling 2026-09-26; standDownExit.mjs).
|
|
915
|
+
* The lock stays held, so `status --json` still shows the daemon running and
|
|
916
|
+
* busy until the outcome lands; the mark is what tells a takeover (which then
|
|
917
|
+
* waits) or a `flowviant stop` (which then says so) never to signal it
|
|
918
|
+
* (see standDown).
|
|
919
|
+
*
|
|
920
|
+
* Written only while the lock still names this pid, and by rename, so a
|
|
921
|
+
* reader never sees half a record. Returns whether it was written; an
|
|
922
|
+
* unguarded daemon (no lock) has nothing to mark.
|
|
923
|
+
*/
|
|
924
|
+
export function markLockDraining(fleetToken, what) {
|
|
925
|
+
const path = instanceLockPath(fleetToken);
|
|
926
|
+
const holder = readHolder(path);
|
|
927
|
+
if (!holder || holder.pid !== process.pid) return false;
|
|
928
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
929
|
+
try {
|
|
930
|
+
writeFileSync(tmp, JSON.stringify({ ...holder, draining: { what: String(what ?? ''), since: new Date().toISOString() } }));
|
|
931
|
+
renameSync(tmp, path);
|
|
932
|
+
return true;
|
|
933
|
+
} catch {
|
|
934
|
+
try {
|
|
935
|
+
rmSync(tmp, { force: true });
|
|
936
|
+
} catch {
|
|
937
|
+
/* best-effort */
|
|
938
|
+
}
|
|
939
|
+
return false;
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
|
|
878
943
|
/** Release ONLY what we still own: a successor that adopted the lock (see the
|
|
879
944
|
* ppid branch) must not have it deleted out from under it when we exit. */
|
|
880
945
|
function makeRelease(path) {
|
|
@@ -890,7 +955,8 @@ function makeRelease(path) {
|
|
|
890
955
|
/* best-effort; a stale file is cleared by the next acquire */
|
|
891
956
|
}
|
|
892
957
|
};
|
|
893
|
-
// 'exit' covers the SIGINT/SIGTERM handlers too — both
|
|
958
|
+
// 'exit' covers the SIGINT/SIGTERM handlers too — both leave through
|
|
959
|
+
// standDownExit.mjs's `leave`, which ends in process.exit().
|
|
894
960
|
process.on('exit', release);
|
|
895
961
|
return release;
|
|
896
962
|
}
|
|
@@ -906,7 +972,9 @@ function makeRelease(path) {
|
|
|
906
972
|
* for the reason `standDown` gives about SIGKILLs.
|
|
907
973
|
*
|
|
908
974
|
* `tally` is mutated in place: `running` counts locks naming a process we
|
|
909
|
-
* believe is, or might be, a daemon;
|
|
975
|
+
* believe is, or might be, a daemon; `draining` counts the ones that were
|
|
976
|
+
* obeyed but are still alive, finishing a deploy (also counted `stopped`); the
|
|
977
|
+
* rest are what the caller reports.
|
|
910
978
|
*/
|
|
911
979
|
function stopLock(path, log, tally) {
|
|
912
980
|
const holder = readHolder(path);
|
|
@@ -960,6 +1028,16 @@ function stopLock(path, log, tally) {
|
|
|
960
1028
|
}
|
|
961
1029
|
|
|
962
1030
|
const bad = standDown(holder, path, log);
|
|
1031
|
+
if (bad?.draining) {
|
|
1032
|
+
// Asked, heard, and leaving once its deploy reports: the stop was obeyed,
|
|
1033
|
+
// so this is not a failure — and nothing is forced (see standDown). It is
|
|
1034
|
+
// counted apart too, because it is still ALIVE: a disconnect must not go
|
|
1035
|
+
// on to leave and forget under it (machineDisconnect.mjs).
|
|
1036
|
+
tally.stopped++;
|
|
1037
|
+
tally.draining++;
|
|
1038
|
+
log(`daemon pid ${holder.pid}${where}: ${bad.failed}.`);
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
963
1041
|
if (bad) {
|
|
964
1042
|
tally.failed++;
|
|
965
1043
|
log(`could not stop daemon pid ${holder.pid}${where}: ${bad.failed}`);
|
|
@@ -985,7 +1063,7 @@ function stopLock(path, log, tally) {
|
|
|
985
1063
|
* running" rather than failing. Returns the same tally `stopAllDaemons` does.
|
|
986
1064
|
*/
|
|
987
1065
|
export function stopDaemonFor(fleetToken, { log = (m) => console.log(m) } = {}) {
|
|
988
|
-
const tally = { stopped: 0, unconfirmed: 0, failed: 0, running: 0 };
|
|
1066
|
+
const tally = { stopped: 0, unconfirmed: 0, failed: 0, running: 0, draining: 0 };
|
|
989
1067
|
stopLock(instanceLockPath(fleetToken), log, tally);
|
|
990
1068
|
return tally;
|
|
991
1069
|
}
|
|
@@ -1038,7 +1116,7 @@ export function stopDaemonFor(fleetToken, { log = (m) => console.log(m) } = {})
|
|
|
1038
1116
|
* exit code.
|
|
1039
1117
|
*/
|
|
1040
1118
|
export function stopAllDaemons({ log = (m) => console.log(m) } = {}) {
|
|
1041
|
-
const tally = { stopped: 0, unconfirmed: 0, failed: 0, running: 0 };
|
|
1119
|
+
const tally = { stopped: 0, unconfirmed: 0, failed: 0, running: 0, draining: 0 };
|
|
1042
1120
|
for (const path of lockFiles()) stopLock(path, log, tally);
|
|
1043
1121
|
const { stopped, unconfirmed, failed, running } = tally;
|
|
1044
1122
|
|
|
@@ -1046,8 +1124,8 @@ export function stopAllDaemons({ log = (m) => console.log(m) } = {}) {
|
|
|
1046
1124
|
// sentence — "no flowviant daemon is running on this machine" — was asserted
|
|
1047
1125
|
// from lock files alone, and there are several ways to run a daemon that
|
|
1048
1126
|
// holds no readable lock: FLOWVIANT_ALLOW_MULTI=1 returns before the
|
|
1049
|
-
// filesystem is touched (and
|
|
1050
|
-
// "already running" refusal, so a stuck user is steered
|
|
1127
|
+
// filesystem is touched (and fleetStartup.mjs PRINTS that flag as the way out
|
|
1128
|
+
// of an "already running" refusal, so a stuck user is steered onto it), an
|
|
1051
1129
|
// unwritable ~/.flowviant runs `unguarded`, and every daemon before 0.51.2
|
|
1052
1130
|
// predates the lock entirely. Telling somebody "nothing is running" while
|
|
1053
1131
|
// something is, is this product's cardinal sin: it turns ignorance into a
|