flowviant 0.116.0 → 0.124.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/README.md +66 -12
- package/bin/cli.mjs +50 -17
- package/bin/lib/admission.mjs +7 -14
- package/bin/lib/agentAsk.mjs +123 -0
- package/bin/lib/agentBrief.mjs +36 -0
- package/bin/lib/agentFootprint.mjs +76 -0
- package/bin/lib/agentInternalWait.mjs +83 -0
- package/bin/lib/agentPlan.mjs +134 -31
- package/bin/lib/agentPrerequisiteContract.mjs +43 -0
- package/bin/lib/agentPublish.mjs +12 -13
- package/bin/lib/agentTaskKinds.mjs +5 -8
- package/bin/lib/agySessions.mjs +3 -3
- package/bin/lib/artifactContracts.mjs +91 -20
- package/bin/lib/artifactIteration.mjs +182 -0
- package/bin/lib/artifactPreview.mjs +92 -18
- package/bin/lib/artifactRecovery.mjs +36 -0
- package/bin/lib/artifactReporter.mjs +194 -22
- package/bin/lib/artifacts.mjs +70 -63
- package/bin/lib/authproxy.mjs +2 -1
- package/bin/lib/childEnv.mjs +9 -4
- package/bin/lib/claude.mjs +10 -20
- package/bin/lib/claudeAuth.mjs +15 -5
- package/bin/lib/claudePersonal.mjs +71 -6
- package/bin/lib/claudePosture.mjs +227 -54
- package/bin/lib/claudeSessions.mjs +7 -7
- package/bin/lib/claudeStream.mjs +27 -13
- package/bin/lib/cliLimit.mjs +2 -2
- package/bin/lib/codexHome.mjs +17 -0
- package/bin/lib/codexPersonal.mjs +44 -3
- package/bin/lib/codexProjectRead.mjs +63 -0
- package/bin/lib/codexRelay.mjs +21 -5
- package/bin/lib/codexResearch.mjs +193 -0
- package/bin/lib/commandSpecs.mjs +6 -0
- package/bin/lib/config.mjs +37 -24
- package/bin/lib/conversationIds.mjs +51 -0
- package/bin/lib/credentials.mjs +2 -7
- package/bin/lib/daemonLocks.mjs +90 -0
- package/bin/lib/daemonState.mjs +5 -5
- package/bin/lib/deploy.mjs +6 -9
- package/bin/lib/deployCheckout.mjs +26 -22
- package/bin/lib/deployRunner.mjs +54 -18
- package/bin/lib/deployWire.mjs +32 -23
- package/bin/lib/desktopContract.mjs +6 -5
- package/bin/lib/discoveryContract.mjs +19 -0
- package/bin/lib/env.mjs +104 -35
- package/bin/lib/fleet.mjs +53 -13
- package/bin/lib/fleetPost.mjs +172 -25
- package/bin/lib/fleetReports.mjs +172 -171
- package/bin/lib/fleetStartup.mjs +6 -6
- package/bin/lib/fleetWire.mjs +42 -0
- package/bin/lib/git.mjs +111 -61
- package/bin/lib/gitMergeIdentity.mjs +2 -8
- package/bin/lib/grant.mjs +23 -7
- package/bin/lib/holder.mjs +108 -81
- package/bin/lib/hooks/mcpAllow.mjs +60 -0
- package/bin/lib/hooks/readGuard.mjs +54 -11
- package/bin/lib/imageArtifact.mjs +74 -0
- package/bin/lib/install.mjs +80 -33
- package/bin/lib/instance.mjs +36 -83
- package/bin/lib/intakeDraft.mjs +4 -1
- package/bin/lib/knowledgeFileSync.mjs +31 -0
- package/bin/lib/knowledgeFiles.mjs +28 -83
- package/bin/lib/landed.mjs +22 -29
- package/bin/lib/launchCommand.mjs +91 -0
- package/bin/lib/listeners.mjs +0 -5
- package/bin/lib/localSessions.mjs +52 -39
- package/bin/lib/login.mjs +22 -12
- package/bin/lib/loginPath.mjs +22 -17
- package/bin/lib/machineListing.mjs +6 -3
- package/bin/lib/maintenance.mjs +47 -0
- package/bin/lib/maintenanceInventory.mjs +39 -0
- package/bin/lib/mcp-cli.mjs +9 -10
- package/bin/lib/mcpInstall.mjs +163 -0
- package/bin/lib/mcpInventory.mjs +119 -0
- package/bin/lib/noWindowEnv.mjs +23 -3
- package/bin/lib/ownedDirs.mjs +99 -0
- package/bin/lib/pinnedCapture.mjs +383 -0
- package/bin/lib/planDependencies.mjs +70 -0
- package/bin/lib/postureTools.mjs +85 -0
- package/bin/lib/prWorkflow.mjs +94 -17
- package/bin/lib/preflight.mjs +8 -25
- package/bin/lib/preview.mjs +19 -25
- package/bin/lib/previewGatePolicy.mjs +34 -9
- package/bin/lib/previewRegistry.mjs +4 -11
- package/bin/lib/printable.mjs +70 -7
- package/bin/lib/procRegistry.mjs +33 -13
- package/bin/lib/procScan.mjs +2 -3
- package/bin/lib/procStat.mjs +76 -0
- package/bin/lib/processGroup.mjs +48 -0
- package/bin/lib/processes.mjs +10 -24
- package/bin/lib/projectToolRuntimes.mjs +328 -19
- package/bin/lib/projectTools.mjs +109 -34
- package/bin/lib/prompts.mjs +205 -60
- package/bin/lib/repoPath.mjs +19 -0
- package/bin/lib/repoState.mjs +17 -19
- package/bin/lib/repositoryBaseline.mjs +68 -0
- package/bin/lib/runTurn.mjs +286 -209
- package/bin/lib/runtimeAntigravity.mjs +16 -5
- package/bin/lib/runtimeCapabilities.mjs +215 -21
- package/bin/lib/runtimeClaude.mjs +78 -10
- package/bin/lib/runtimeCodex.mjs +217 -209
- package/bin/lib/runtimeDetection.mjs +8 -2
- package/bin/lib/runtimeEvents.mjs +28 -11
- package/bin/lib/runtimeLimits.mjs +5 -6
- package/bin/lib/runtimeOfferings.mjs +99 -14
- package/bin/lib/runtimes.mjs +10 -0
- package/bin/lib/shipSweep.mjs +5 -3
- package/bin/lib/shot.mjs +91 -17
- package/bin/lib/sleep.mjs +11 -0
- package/bin/lib/startProject.mjs +5 -5
- package/bin/lib/statePaths.mjs +40 -0
- package/bin/lib/tty.mjs +25 -15
- package/bin/lib/turnDiagnostics.mjs +93 -0
- package/bin/lib/turnLock.mjs +7 -14
- package/bin/lib/turnProfile.mjs +16 -4
- package/bin/lib/ui.mjs +0 -3
- package/bin/lib/uninstall.mjs +30 -26
- package/bin/lib/update.mjs +79 -27
- package/bin/lib/uplinkScrub.mjs +296 -31
- package/bin/lib/vault.mjs +78 -10
- package/bin/lib/vaultDiff.mjs +5 -2
- package/bin/lib/views.mjs +37 -12
- package/bin/lib/wikiRunner.mjs +40 -61
- package/bin/lib/work.mjs +44 -13
- package/bin/lib/workAdoptCarry.mjs +2 -4
- package/bin/lib/workAgentBrief.mjs +92 -0
- package/bin/lib/workAgentBriefRunner.mjs +59 -0
- package/bin/lib/workAgentCheck.mjs +104 -57
- package/bin/lib/workAgentMerges.mjs +62 -57
- package/bin/lib/workAgentPlans.mjs +82 -199
- package/bin/lib/workAgentPrecheck.mjs +76 -138
- package/bin/lib/workAgentPublish.mjs +6 -8
- package/bin/lib/workAgentTurnBegun.mjs +15 -11
- package/bin/lib/workAgentTurnExecution.mjs +131 -67
- package/bin/lib/workAgentTurnOutcome.mjs +11 -2
- package/bin/lib/workAgentTurnReports.mjs +9 -37
- package/bin/lib/workAgentTurns.mjs +64 -7
- package/bin/lib/workArtifactReconcile.mjs +79 -0
- package/bin/lib/workAttachments.mjs +66 -11
- package/bin/lib/workBrain.mjs +9 -4
- package/bin/lib/workCappedTurn.mjs +119 -0
- package/bin/lib/workChildren.mjs +5 -5
- package/bin/lib/workCommandAudit.mjs +18 -2
- package/bin/lib/workDiffs.mjs +52 -6
- package/bin/lib/workIntake.mjs +35 -60
- package/bin/lib/workPlaceReports.mjs +7 -4
- package/bin/lib/workPlaces.mjs +113 -31
- package/bin/lib/workPreviews.mjs +47 -112
- package/bin/lib/workProcesses.mjs +8 -35
- package/bin/lib/workPullRequests.mjs +37 -56
- package/bin/lib/workReportQueue.mjs +100 -148
- package/bin/lib/workRetire.mjs +2 -1
- package/bin/lib/workSessionRuntime.mjs +38 -37
- package/bin/lib/workSessionTurns.mjs +256 -199
- package/bin/lib/workShip.mjs +43 -23
- package/bin/lib/workToolLog.mjs +70 -3
- package/bin/lib/workTurnShape.mjs +165 -0
- package/npm-shrinkwrap.json +69 -0
- package/package.json +9 -4
- package/bin/lib/fleetJobs.mjs +0 -120
- package/bin/lib/patchRevert.mjs +0 -60
package/bin/lib/turnLock.mjs
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* means is this module's.
|
|
11
11
|
*/
|
|
12
12
|
import { existsSync, readFileSync, rmSync } from 'node:fs';
|
|
13
|
-
import { processStartTime } from './procRegistry.mjs';
|
|
13
|
+
import { aliveAndOurs, processStartTime } from './procRegistry.mjs';
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* The spawn lock: the pid of the CLI currently live in this worktree. A
|
|
@@ -45,19 +45,12 @@ export const turnLockedByLivePid = (lockPath) => {
|
|
|
45
45
|
} catch {
|
|
46
46
|
/* unreadable — treat as stale */
|
|
47
47
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
/* ESRCH: gone. EPERM: another uid's process — never our CLI. Stale. */
|
|
55
|
-
}
|
|
56
|
-
if (alive) {
|
|
57
|
-
const now = start ? processStartTime(pid) : null;
|
|
58
|
-
// Unmeasurable start time is "cannot tell", which keeps the lock.
|
|
59
|
-
if (!start || !now || now === start) return true;
|
|
60
|
-
}
|
|
48
|
+
// Signal 0 delivered to a process of OUR uid; EPERM is another uid's process,
|
|
49
|
+
// never our CLI, so stale (procRegistry.aliveAndOurs).
|
|
50
|
+
if (aliveAndOurs(pid)) {
|
|
51
|
+
const now = start ? processStartTime(pid) : null;
|
|
52
|
+
// Unmeasurable start time is "cannot tell", which keeps the lock.
|
|
53
|
+
if (!start || !now || now === start) return true;
|
|
61
54
|
}
|
|
62
55
|
try {
|
|
63
56
|
rmSync(lockPath, { force: true }); // dead holder — clear the stale lock
|
package/bin/lib/turnProfile.mjs
CHANGED
|
@@ -22,10 +22,18 @@
|
|
|
22
22
|
* - design / research / image — a non-code card's posture
|
|
23
23
|
* (agentTaskKinds.mjs), refused on any runtime that does not
|
|
24
24
|
* DECLARE it, because every other adapter's fallback is its
|
|
25
|
-
* build branch. research is Claude Code's; design is
|
|
25
|
+
* build branch. research is Claude Code's and isolated Codex's; design is
|
|
26
26
|
* Claude Code's and, since 0.115.0, Codex's; image (0.114.0)
|
|
27
27
|
* is Codex's.
|
|
28
28
|
*
|
|
29
|
+
* `projectMcp` (2026-10-10, the owner): whether an agent's turn under this
|
|
30
|
+
* profile mounts MCP servers — the project's reviewed base `.mcp.json` copy
|
|
31
|
+
* and, since 2026-10-11 ("the server that is running flowviant's claude HAS
|
|
32
|
+
* these MCPs yet the agents are not using them"), this machine's own CLI
|
|
33
|
+
* servers and claude.ai connectors. Every card's posture: the code card's
|
|
34
|
+
* build, and the write-up's, mockup/3D-model/deck's and image's fences. The
|
|
35
|
+
* consult, plan, wiki and plan-mode turns are not a card's and mount none.
|
|
36
|
+
*
|
|
29
37
|
* Claude's permission list for each name lives beside the lists themselves
|
|
30
38
|
* (`claudePermFor`, claudePosture.mjs); this module owns the vocabulary and the
|
|
31
39
|
* refusals, and imports the card-kind table and the runtime registry (whose
|
|
@@ -44,10 +52,12 @@ const declaredBy = (name) =>
|
|
|
44
52
|
.join(' or ') || 'no CLI this daemon knows';
|
|
45
53
|
const article = (word) => (/^[aeiou]/i.test(word) ? 'an' : 'a');
|
|
46
54
|
|
|
47
|
-
const plain = (adapterProfile) => Object.freeze({ adapterProfile, onlyOn: null, strictMcp: false });
|
|
55
|
+
const plain = (adapterProfile) => Object.freeze({ adapterProfile, onlyOn: null, strictMcp: false, projectMcp: false });
|
|
56
|
+
/** The kind postures that mount MCP servers (2026-10-10; image 2026-10-11). */
|
|
57
|
+
const KIND_PROJECT_MCP = new Set(['design', 'research', 'image']);
|
|
48
58
|
|
|
49
59
|
export const TURN_PROFILES = Object.freeze({
|
|
50
|
-
build: plain('build'),
|
|
60
|
+
build: Object.freeze({ ...plain('build'), projectMcp: true }),
|
|
51
61
|
wiki: plain('wiki'),
|
|
52
62
|
consult: plain('consult'),
|
|
53
63
|
plan: plain('plan'),
|
|
@@ -58,6 +68,7 @@ export const TURN_PROFILES = Object.freeze({
|
|
|
58
68
|
// wearing the word "plan".
|
|
59
69
|
onlyOn: (rt) => (rt.id === 'claude' ? null : `plan mode runs on Claude Code only — not '${rt.label}'`),
|
|
60
70
|
strictMcp: true,
|
|
71
|
+
projectMcp: false,
|
|
61
72
|
}),
|
|
62
73
|
// The kind postures: fenced to "write only the artifacts directory", and a
|
|
63
74
|
// runtime that does not declare one would run its build branch instead.
|
|
@@ -73,6 +84,7 @@ export const TURN_PROFILES = Object.freeze({
|
|
|
73
84
|
? null
|
|
74
85
|
: `${article(name)} ${name} card runs on ${declaredBy(name)} only — not '${rt.label}'`,
|
|
75
86
|
strictMcp: false,
|
|
87
|
+
projectMcp: KIND_PROJECT_MCP.has(name),
|
|
76
88
|
}),
|
|
77
89
|
])
|
|
78
90
|
),
|
|
@@ -83,7 +95,7 @@ export const TURN_PROFILES = Object.freeze({
|
|
|
83
95
|
export const RETIRED_POSTURE_KEYS = Object.freeze(['wikiPerm', 'readOnly', 'planPerm', 'planMode', 'posture']);
|
|
84
96
|
|
|
85
97
|
/**
|
|
86
|
-
* `{ name, adapterProfile, onlyOn, strictMcp }` for a turn's options, or
|
|
98
|
+
* `{ name, adapterProfile, onlyOn, strictMcp, projectMcp }` for a turn's options, or
|
|
87
99
|
* `{ error }` in words. Absent `profile` is build.
|
|
88
100
|
*/
|
|
89
101
|
export function resolveTurnProfile(opts = {}) {
|
package/bin/lib/ui.mjs
CHANGED
|
@@ -9,9 +9,6 @@ export const c = {
|
|
|
9
9
|
blue: wrap(34), magenta: wrap(35), cyan: wrap(36), gray: wrap(90),
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
// Stable, cycled colours so each agent's label is easy to scan in a fleet.
|
|
13
|
-
export const LABEL_COLORS = [c.cyan, c.magenta, c.blue, c.yellow, c.green, c.red];
|
|
14
|
-
|
|
15
12
|
const stamp = () => {
|
|
16
13
|
const d = new Date();
|
|
17
14
|
const p = (n) => String(n).padStart(2, '0');
|
package/bin/lib/uninstall.mjs
CHANGED
|
@@ -3,7 +3,9 @@ import { execFile as nodeExecFile } from 'node:child_process';
|
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
import { basename, dirname, isAbsolute, join, resolve } from 'node:path';
|
|
5
5
|
import { promisify } from 'node:util';
|
|
6
|
-
import { runningCompiledBinary } from './
|
|
6
|
+
import { runningCompiledBinary } from './launchCommand.mjs';
|
|
7
|
+
import { listDaemonLocks } from './daemonLocks.mjs';
|
|
8
|
+
import { processAlive } from './procRegistry.mjs';
|
|
7
9
|
import { VERSION } from './config.mjs';
|
|
8
10
|
import { parseVersion } from './versionOrder.mjs';
|
|
9
11
|
import { copyKind, exactBlock, inside } from './uninstallCopies.mjs';
|
|
@@ -18,7 +20,9 @@ function defaults(options = {}) {
|
|
|
18
20
|
home: homedir(),
|
|
19
21
|
path: process.env.PATH || '',
|
|
20
22
|
execFile: (file, args, opts) => execFileAsync(file, args, opts),
|
|
21
|
-
|
|
23
|
+
// EPERM is alive (2026-10-10, F090/F123): a conservative alignment with the
|
|
24
|
+
// one probe — a pid we may not signal still protects the copy it may run.
|
|
25
|
+
pidLive: processAlive,
|
|
22
26
|
procReader: (pid, file) => file === 'exe' || file === 'cwd'
|
|
23
27
|
? nodeFs.readlinkSync(`/proc/${pid}/${file}`) : nodeFs.readFileSync(`/proc/${pid}/${file}`),
|
|
24
28
|
procAvailable: nodeFs.existsSync('/proc/self/cmdline'),
|
|
@@ -58,32 +62,32 @@ function compiledExecutable(fs, path) {
|
|
|
58
62
|
} catch { return false; }
|
|
59
63
|
}
|
|
60
64
|
|
|
65
|
+
// The lock files and their records come from the one scan instance.mjs uses
|
|
66
|
+
// (daemonLocks.mjs), through the injected fs and home; an unreadable lock is
|
|
67
|
+
// not evidence of a live daemon.
|
|
61
68
|
function liveDaemons(o) {
|
|
62
69
|
const found = [];
|
|
63
|
-
for (const
|
|
64
|
-
if (
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
found.push({ pid, paths });
|
|
86
|
-
} catch { /* An unreadable lock is not evidence of a live daemon. */ }
|
|
70
|
+
for (const { holder } of listDaemonLocks({ fs: o.fs, home: o.home })) {
|
|
71
|
+
if (!holder || !o.pidLive(holder.pid)) continue;
|
|
72
|
+
const pid = holder.pid;
|
|
73
|
+
let paths = [];
|
|
74
|
+
if (o.procAvailable) {
|
|
75
|
+
try {
|
|
76
|
+
const exe = o.procReader(pid, 'exe');
|
|
77
|
+
const command = o.procReader(pid, 'cmdline');
|
|
78
|
+
const args = Buffer.from(command).toString('utf8').split('\0').filter(Boolean);
|
|
79
|
+
let scripts = args.filter((arg) => isAbsolute(arg));
|
|
80
|
+
const relative = args.filter((arg) => !arg.startsWith('-') && arg.includes('/') && !isAbsolute(arg));
|
|
81
|
+
if (relative.length) {
|
|
82
|
+
const cwd = String(o.procReader(pid, 'cwd'));
|
|
83
|
+
scripts.push(...relative.map((arg) => resolve(cwd, arg)));
|
|
84
|
+
}
|
|
85
|
+
const executable = String(exe).replace(/ \(deleted\)$/, '');
|
|
86
|
+
paths = basename(executable).startsWith('node') && !scripts.length ? [] :
|
|
87
|
+
[executable, ...scripts].map((path) => real(o.fs, path));
|
|
88
|
+
} catch { /* The live pid is still protected when its command is hidden. */ }
|
|
89
|
+
}
|
|
90
|
+
found.push({ pid, paths });
|
|
87
91
|
}
|
|
88
92
|
return found;
|
|
89
93
|
}
|
package/bin/lib/update.mjs
CHANGED
|
@@ -33,11 +33,12 @@ import { execFileSync, spawn } from 'node:child_process';
|
|
|
33
33
|
import { createHash } from 'node:crypto';
|
|
34
34
|
import { createReadStream, createWriteStream } from 'node:fs';
|
|
35
35
|
import { chmod, mkdtemp, open, rename, rm } from 'node:fs/promises';
|
|
36
|
+
import { homedir } from 'node:os';
|
|
36
37
|
import { dirname, join } from 'node:path';
|
|
37
38
|
import { platform, arch } from 'node:process';
|
|
38
39
|
import { pipeline } from 'node:stream/promises';
|
|
39
40
|
import { Readable } from 'node:stream';
|
|
40
|
-
import { runningViaNpx, terminalCommand } from './launchCommand.mjs';
|
|
41
|
+
import { relaunchSelf, runningCompiledBinary, runningViaNpx, selfArgv, terminalCommand } from './launchCommand.mjs';
|
|
41
42
|
import { VERSION } from './config.mjs';
|
|
42
43
|
import { compareVersions, versionBelow } from './versionOrder.mjs';
|
|
43
44
|
import { note, ok, warn } from './ui.mjs';
|
|
@@ -48,9 +49,40 @@ import { note, ok, warn } from './ui.mjs';
|
|
|
48
49
|
* never installs, never restarts and never nags on a version it cannot read.
|
|
49
50
|
*/
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
export
|
|
53
|
-
|
|
52
|
+
// Channel detection lives in launchCommand.mjs; re-exported for this module's callers.
|
|
53
|
+
export { runningCompiledBinary };
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* NPM RUNS FROM OUTSIDE THE CHECKOUT (2026-10-10, F131). The daemon's working
|
|
57
|
+
* directory IS the repository (the person launched there, or `--dir` chdir'd
|
|
58
|
+
* to it), and npm loads a project's `.npmrc` from the nearest package root —
|
|
59
|
+
* its `registry=` and `node-options=` included — for `npm exec` and `npm view`.
|
|
60
|
+
* A change that reached base could then steer the unattended self-update to
|
|
61
|
+
* another registry or preload code into it, outside any agent posture. From
|
|
62
|
+
* the home directory npm reads only the operator's own user and global config,
|
|
63
|
+
* which is honoured as it is: no registry is forced.
|
|
64
|
+
*/
|
|
65
|
+
export function npmOutsideCheckout() {
|
|
66
|
+
return { cwd: homedir() };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The npx relaunch's argv: the same flags, with the checkout named explicitly
|
|
71
|
+
* and ABSOLUTELY, because the relaunch no longer starts inside it. Any `--dir`
|
|
72
|
+
* pair already there is replaced — a relative one would now resolve against
|
|
73
|
+
* the home directory. `cwd` is this process's, which cli.mjs already moved to
|
|
74
|
+
* the `--dir` it was given; a bare start honours `--dir` (commandSpecs.mjs).
|
|
75
|
+
*/
|
|
76
|
+
export function npxRelaunchArgs(args = process.argv.slice(2), cwd = process.cwd()) {
|
|
77
|
+
const kept = [];
|
|
78
|
+
for (let i = 0; i < args.length; i++) {
|
|
79
|
+
if (args[i] === '--dir') {
|
|
80
|
+
i++; // and its value
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
kept.push(args[i]);
|
|
84
|
+
}
|
|
85
|
+
return ['-y', 'flowviant@latest', ...kept, '--dir', cwd];
|
|
54
86
|
}
|
|
55
87
|
|
|
56
88
|
const RELEASE_ORIGIN = 'https://api.flowviant.com';
|
|
@@ -115,9 +147,15 @@ export async function installBinaryUpdate({
|
|
|
115
147
|
* `npm i -g` overwrote the global package in place, so re-running argv[1] loads
|
|
116
148
|
* the NEW code. We tear down first (idle-gated, so nothing's mid-task) and keep
|
|
117
149
|
* this process alive only as a thin proxy waiting on the child, so the user's
|
|
118
|
-
* shell stays attached to one foreground process
|
|
150
|
+
* shell stays attached to one foreground process — the one relaunch proxy
|
|
151
|
+
* (launchCommand.mjs `relaunchSelf`), which passes signals on and reads a
|
|
152
|
+
* successor killed by one (the OOM killer) as a failure, so `Restart=on-failure`
|
|
153
|
+
* relaunches the machine instead of leaving it dark.
|
|
154
|
+
*
|
|
155
|
+
* `deps` (spawn, exit, signals) are relaunchSelf's test seams; the daemon
|
|
156
|
+
* passes none.
|
|
119
157
|
*/
|
|
120
|
-
function reexec(teardown, { viaNpx = false,
|
|
158
|
+
export function reexec(teardown, { viaNpx = false, target = null } = {}, deps = {}) {
|
|
121
159
|
try {
|
|
122
160
|
teardown?.();
|
|
123
161
|
} catch {
|
|
@@ -128,12 +166,16 @@ function reexec(teardown, { viaNpx = false, viaBinary = false, target = null } =
|
|
|
128
166
|
// to leave, and re-running it would reload the stale copy forever. Going back
|
|
129
167
|
// through `npx -y flowviant@latest` is what makes npx resolve `latest` against
|
|
130
168
|
// the registry and fetch the new one. `-y` because a restart must never stop
|
|
131
|
-
// on npx's install prompt — the same rule FLOWVIANT_REEXEC keeps below.
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
169
|
+
// on npx's install prompt — the same rule FLOWVIANT_REEXEC keeps below. It
|
|
170
|
+
// runs from outside the checkout and names it with `--dir` (npmOutsideCheckout).
|
|
171
|
+
const [cmd, argv, where] = viaNpx
|
|
172
|
+
? ['npx', npxRelaunchArgs(), npmOutsideCheckout()]
|
|
173
|
+
: [process.execPath, selfArgv(process.argv.slice(2)), {}];
|
|
174
|
+
return relaunchSelf({
|
|
175
|
+
...deps,
|
|
176
|
+
cmd,
|
|
177
|
+
argv,
|
|
178
|
+
...where,
|
|
137
179
|
// MARK THE CHILD AS A RESTART, not as a person typing `flowviant`.
|
|
138
180
|
// stdio is inherited, so the child sees two TTYs and believes a human is
|
|
139
181
|
// watching — and 0.55.0 asks a one-time binding question on exactly that
|
|
@@ -163,7 +205,6 @@ function reexec(teardown, { viaNpx = false, viaBinary = false, target = null } =
|
|
|
163
205
|
FLOWVIANT_UPDATE_FROM: VERSION,
|
|
164
206
|
},
|
|
165
207
|
});
|
|
166
|
-
child.on('exit', (code) => process.exit(code ?? 0));
|
|
167
208
|
}
|
|
168
209
|
|
|
169
210
|
/**
|
|
@@ -184,7 +225,29 @@ export function updateRestartFailed(target) {
|
|
|
184
225
|
|
|
185
226
|
/** Install @latest globally. Throws on failure (EACCES without sudo, offline…). */
|
|
186
227
|
function installLatest() {
|
|
187
|
-
execFileSync('npm', ['install', '-g', 'flowviant@latest'], { stdio: 'inherit' });
|
|
228
|
+
execFileSync('npm', ['install', '-g', 'flowviant@latest'], { ...npmOutsideCheckout(), stdio: 'inherit' });
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* What npm says `flowviant@latest` is, or null when it cannot say (offline, an
|
|
233
|
+
* npm hiccup, a hung registry) — read from outside the checkout like every
|
|
234
|
+
* other npm call here. `exec` is the test seam.
|
|
235
|
+
*/
|
|
236
|
+
export function publishedVersion({ exec = execFileSync } = {}) {
|
|
237
|
+
try {
|
|
238
|
+
return String(
|
|
239
|
+
exec('npm', ['view', 'flowviant', 'version'], {
|
|
240
|
+
...npmOutsideCheckout(),
|
|
241
|
+
encoding: 'utf8',
|
|
242
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
243
|
+
// Synchronous, so it holds the event loop hostage for however long it
|
|
244
|
+
// runs — a hung registry must not become a hung daemon.
|
|
245
|
+
timeout: 10_000,
|
|
246
|
+
})
|
|
247
|
+
).trim();
|
|
248
|
+
} catch {
|
|
249
|
+
return null; // offline / npm hiccup — "can't confirm", skip this poll
|
|
250
|
+
}
|
|
188
251
|
}
|
|
189
252
|
|
|
190
253
|
/** `flowviant update` — explicit, manual update. Does not re-exec into a daemon
|
|
@@ -289,7 +352,7 @@ export async function handleVersionSignal({ latest, min, autoUpdate, safeToUpdat
|
|
|
289
352
|
try {
|
|
290
353
|
spawn(process.execPath, ['uninstall', '--others', '--yes', '--json'], { detached: true, stdio: 'ignore' }).unref();
|
|
291
354
|
} catch (e) { warn(`older-copy cleanup: ${e?.message ?? e}`); }
|
|
292
|
-
reexec(teardown, {
|
|
355
|
+
reexec(teardown, { target: installed });
|
|
293
356
|
return true;
|
|
294
357
|
} catch (e) {
|
|
295
358
|
lastInstallFailAt = Date.now();
|
|
@@ -339,18 +402,7 @@ export async function handleVersionSignal({ latest, min, autoUpdate, safeToUpdat
|
|
|
339
402
|
// the source of truth — only install if npm ACTUALLY has something newer than
|
|
340
403
|
// us, else `npm i -g @latest` reinstalls our own version and we'd re-exec
|
|
341
404
|
// forever.
|
|
342
|
-
|
|
343
|
-
try {
|
|
344
|
-
published = execFileSync('npm', ['view', 'flowviant', 'version'], {
|
|
345
|
-
encoding: 'utf8',
|
|
346
|
-
stdio: ['ignore', 'pipe', 'ignore'],
|
|
347
|
-
// Synchronous, so it holds the event loop hostage for however long it
|
|
348
|
-
// runs — a hung registry must not become a hung daemon.
|
|
349
|
-
timeout: 10_000,
|
|
350
|
-
}).trim();
|
|
351
|
-
} catch {
|
|
352
|
-
/* offline / npm hiccup — treat as "can't confirm", skip this poll */
|
|
353
|
-
}
|
|
405
|
+
const published = publishedVersion();
|
|
354
406
|
// An unreadable answer from npm is "can't confirm", like no answer at all.
|
|
355
407
|
const publishedOrder = published ? compareVersions(published, cur) : null;
|
|
356
408
|
if (publishedOrder === null || publishedOrder <= 0) {
|