flowviant 0.103.0 → 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 +183 -2383
- 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 +2 -2
- 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/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 +17 -17
- package/bin/lib/wikiRunner.mjs +630 -0
- package/bin/lib/work.mjs +188 -3034
- 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 +91 -898
- 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
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE BOX KEYPAIR — this box's durable identity, and the one thing in this file.
|
|
3
|
+
*
|
|
4
|
+
* A persistent X25519 keypair at `~/.flowviant/env-keypair.json` (0600). Its
|
|
5
|
+
* public half rides EVERY roster poll as `envpub`, and that is what tells two
|
|
6
|
+
* computers apart when the server decides which of them is this project's
|
|
7
|
+
* machine (holdership, 2026-09-14) and which rows belong to which box in the
|
|
8
|
+
* registry (`machine_box`, 0.91.0). It is an identity LABEL for arbitration,
|
|
9
|
+
* never an authorization: nothing is GRANTED by it — the credential grants, the
|
|
10
|
+
* pubkey only disambiguates boxes.
|
|
11
|
+
*
|
|
12
|
+
* SPLIT OUT OF `env.mjs` (2026-09-26, the SOLID pass). That module held three
|
|
13
|
+
* things with three unrelated reasons to change — this identity, the checkout's
|
|
14
|
+
* env comparison scan (still `env.mjs`), and the uplink scrubber
|
|
15
|
+
* (`uplinkScrub.mjs`) — and they shared a file only because all three were
|
|
16
|
+
* what was left standing when the secrets vault they once served was deleted
|
|
17
|
+
* (2026-09-21; `env.mjs` keeps that obituary). Nothing here reads a `.env`,
|
|
18
|
+
* and nothing there touches this file. The `env-` in the file name is a fossil
|
|
19
|
+
* of the vault and stays, because the PATH is the identity: see below.
|
|
20
|
+
*
|
|
21
|
+
* WHAT MUST NOT CHANGE: the keypair file, its path, and its 2026-09-14
|
|
22
|
+
* correctness rule (only ENOENT mints; every other read failure RETHROWS).
|
|
23
|
+
* Rotating this box's identity would make the same physical machine arrive at
|
|
24
|
+
* the roster as a stranger — a new row in the registry, and a standby of
|
|
25
|
+
* itself for the whole holder-claim window.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import {
|
|
29
|
+
readFileSync,
|
|
30
|
+
mkdirSync,
|
|
31
|
+
openSync,
|
|
32
|
+
writeSync,
|
|
33
|
+
fsyncSync,
|
|
34
|
+
closeSync,
|
|
35
|
+
linkSync,
|
|
36
|
+
unlinkSync,
|
|
37
|
+
} from 'node:fs';
|
|
38
|
+
import { randomBytes } from 'node:crypto';
|
|
39
|
+
import { homedir } from 'node:os';
|
|
40
|
+
import { join, dirname } from 'node:path';
|
|
41
|
+
// STILL SUMO, and deliberately not narrowed to the standard build in the same
|
|
42
|
+
// pass that deleted the vault. `crypto_box_keypair` exists in both, but the
|
|
43
|
+
// sumo package is what is installed, what every other daemon on npm resolves,
|
|
44
|
+
// and swapping the dependency is a separate change with its own install-time
|
|
45
|
+
// failure modes. Nothing here needs Argon2 any more; nothing here forbids the
|
|
46
|
+
// narrowing either.
|
|
47
|
+
import sodium from 'libsodium-wrappers-sumo';
|
|
48
|
+
|
|
49
|
+
const B64 = () => sodium.base64_variants.ORIGINAL;
|
|
50
|
+
const KEYPAIR_PATH = join(homedir(), '.flowviant', 'env-keypair.json');
|
|
51
|
+
|
|
52
|
+
let keypair = null; // { publicKey: Uint8Array, privateKey: Uint8Array }
|
|
53
|
+
|
|
54
|
+
/*
|
|
55
|
+
* `sodiumReady` IS DELETED (2026-09-26, the SOLID pass). It moved here out of
|
|
56
|
+
* `env.mjs` with no caller anywhere in the daemon: every door below awaits
|
|
57
|
+
* `sodium.ready` itself, so a separate warm-up export was a door nobody used.
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
/*
|
|
61
|
+
* `pubkeyEmoji` IS DELETED (2026-09-20), and so is its byte-identical twin in
|
|
62
|
+
* the web app.
|
|
63
|
+
*
|
|
64
|
+
* It existed for exactly one gesture: the terminal printed eight glyphs, the
|
|
65
|
+
* browser's approve card printed the same eight, and a human compared them
|
|
66
|
+
* before pressing Approve. Registering became enrolling, and then the whole
|
|
67
|
+
* enrolment went with the vault — so there is no approve card, no comparison,
|
|
68
|
+
* and nobody to make it. A fingerprint kept byte-identical across two repos
|
|
69
|
+
* for nobody to look at is worse than none: it reads like a live MITM defence
|
|
70
|
+
* and defends nothing.
|
|
71
|
+
*/
|
|
72
|
+
|
|
73
|
+
/** This machine's persistent keypair (created on first use, 0600). */
|
|
74
|
+
export async function ensureKeypair() {
|
|
75
|
+
await sodium.ready;
|
|
76
|
+
if (keypair) return keypair;
|
|
77
|
+
let raw = null;
|
|
78
|
+
try {
|
|
79
|
+
raw = readFileSync(KEYPAIR_PATH, 'utf8');
|
|
80
|
+
} catch (e) {
|
|
81
|
+
/**
|
|
82
|
+
* ONLY "THERE IS NO FILE" IS A FIRST RUN, and the bare catch that used to
|
|
83
|
+
* stand here said every failure was one.
|
|
84
|
+
*
|
|
85
|
+
* This keypair is the box's DURABLE IDENTITY — since 2026-09-14 it is what
|
|
86
|
+
* tells two computers apart when the server decides which of them is this
|
|
87
|
+
* project's machine, and since 0.91.0 it is the key of this box's row in
|
|
88
|
+
* the machine registry. Regenerating it on a truncated file or an
|
|
89
|
+
* unreadable one OVERWRITES that identity: the box arrives at the roster as
|
|
90
|
+
* a stranger and stands itself down as a standby of itself.
|
|
91
|
+
*
|
|
92
|
+
* (It was ALSO what the project's secret vault was sealed to, until the
|
|
93
|
+
* vault was deleted 2026-09-21. That was the loudest consequence and it is
|
|
94
|
+
* gone; the identity consequence is the one that survives, and it is on its
|
|
95
|
+
* own sufficient.)
|
|
96
|
+
*
|
|
97
|
+
* A file we cannot read is not a file we may replace. Rethrown, the caller
|
|
98
|
+
* that can survive it does: `envQueryParams` is wrapped, so the poll simply
|
|
99
|
+
* carries no `envpub`, and a poll with no envpub is EXEMPT from arbitration
|
|
100
|
+
* — the documented fail-open arm, reached honestly instead of by minting a
|
|
101
|
+
* new box every restart.
|
|
102
|
+
*/
|
|
103
|
+
if (e?.code !== 'ENOENT') throw e;
|
|
104
|
+
}
|
|
105
|
+
if (raw !== null) {
|
|
106
|
+
const stored = JSON.parse(raw);
|
|
107
|
+
keypair = {
|
|
108
|
+
publicKey: sodium.from_base64(stored.pub, B64()),
|
|
109
|
+
privateKey: sodium.from_base64(stored.priv, B64()),
|
|
110
|
+
};
|
|
111
|
+
return keypair;
|
|
112
|
+
}
|
|
113
|
+
const minted = sodium.crypto_box_keypair();
|
|
114
|
+
const winner = mintKeypairFile(
|
|
115
|
+
KEYPAIR_PATH,
|
|
116
|
+
JSON.stringify({
|
|
117
|
+
pub: sodium.to_base64(minted.publicKey, B64()),
|
|
118
|
+
priv: sodium.to_base64(minted.privateKey, B64()),
|
|
119
|
+
})
|
|
120
|
+
);
|
|
121
|
+
keypair = {
|
|
122
|
+
publicKey: sodium.from_base64(winner.pub, B64()),
|
|
123
|
+
privateKey: sodium.from_base64(winner.priv, B64()),
|
|
124
|
+
};
|
|
125
|
+
return keypair;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* WRITE THE BOX'S IDENTITY ONCE, ATOMICALLY, AND LET THE FIRST WRITER WIN
|
|
130
|
+
* (2026-09-24, the audit). Returns the `{ pub, priv }` that is ON DISK after
|
|
131
|
+
* the call — ours, or the one a concurrent first start got there with.
|
|
132
|
+
*
|
|
133
|
+
* It was a plain `writeFileSync` reached by every process that saw ENOENT, so
|
|
134
|
+
* two daemons starting together on a fresh box (two projects, two units at
|
|
135
|
+
* boot) each minted a key and the LAST writer won: the first kept a key in
|
|
136
|
+
* memory the disk no longer held and arrived, after its next re-exec, as a
|
|
137
|
+
* different box. And a crash inside the write left a torn file that every later
|
|
138
|
+
* start refuses to read — the only-ENOENT-mints rule doing its job over a file
|
|
139
|
+
* nothing can fix.
|
|
140
|
+
*
|
|
141
|
+
* So the key goes to a private temp file, is fsync'd, and is LINKED into place:
|
|
142
|
+
* `link` fails with EEXIST instead of replacing, so exactly one identity is ever
|
|
143
|
+
* published and every loser reads the winner's. The file at the real path is
|
|
144
|
+
* either absent or complete — never half of one.
|
|
145
|
+
*/
|
|
146
|
+
export function mintKeypairFile(path, body) {
|
|
147
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
148
|
+
const tmp = `${path}.${process.pid}.${randomBytes(6).toString('hex')}.tmp`;
|
|
149
|
+
try {
|
|
150
|
+
const fd = openSync(tmp, 'wx', 0o600);
|
|
151
|
+
try {
|
|
152
|
+
writeSync(fd, body);
|
|
153
|
+
fsyncSync(fd);
|
|
154
|
+
} finally {
|
|
155
|
+
closeSync(fd);
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
linkSync(tmp, path);
|
|
159
|
+
return JSON.parse(body);
|
|
160
|
+
} catch (e) {
|
|
161
|
+
if (e?.code === 'EEXIST') {
|
|
162
|
+
// Somebody else published first. Theirs is the identity; ours is dropped.
|
|
163
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
164
|
+
}
|
|
165
|
+
// A filesystem with no hard links (some network and FUSE mounts answer
|
|
166
|
+
// EPERM/ENOTSUP) must not cost the box its identity — the plain write
|
|
167
|
+
// this replaced worked there. Fall back to an EXCLUSIVE create: still
|
|
168
|
+
// first-writer-wins, only without the torn-write guarantee.
|
|
169
|
+
if (!['EPERM', 'ENOTSUP', 'EOPNOTSUPP', 'ENOSYS', 'EXDEV'].includes(e?.code)) throw e;
|
|
170
|
+
let fd2;
|
|
171
|
+
try {
|
|
172
|
+
fd2 = openSync(path, 'wx', 0o600);
|
|
173
|
+
} catch (e2) {
|
|
174
|
+
if (e2?.code !== 'EEXIST') throw e2;
|
|
175
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
writeSync(fd2, body);
|
|
179
|
+
fsyncSync(fd2);
|
|
180
|
+
} finally {
|
|
181
|
+
closeSync(fd2);
|
|
182
|
+
}
|
|
183
|
+
return JSON.parse(body);
|
|
184
|
+
}
|
|
185
|
+
} finally {
|
|
186
|
+
try {
|
|
187
|
+
unlinkSync(tmp);
|
|
188
|
+
} catch {
|
|
189
|
+
/* already gone */
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function myPubB64() {
|
|
195
|
+
return keypair ? sodium.to_base64(keypair.publicKey, B64()) : null;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* THIS BOX'S PUBLIC KEY, READ AND NEVER WRITTEN (2026-09-19, the review).
|
|
200
|
+
*
|
|
201
|
+
* `flowviant machines` is a VIEW-ONLY command — that narrowness is the whole
|
|
202
|
+
* reason a third terminal command was allowed to exist at all — and it was
|
|
203
|
+
* calling `ensureKeypair`, which on a box with no keypair CREATES ONE: a
|
|
204
|
+
* filesystem write, 0600, minting this machine's durable identity, from a
|
|
205
|
+
* command whose entire job is to print a list. Listing must not enrol.
|
|
206
|
+
*
|
|
207
|
+
* It reads the stored file and nothing else. No sodium, no parse of the private
|
|
208
|
+
* half, no creation. NULL for every failure — absent, unreadable, malformed —
|
|
209
|
+
* and null simply means no row gets the "← this box" mark, which is honest: a
|
|
210
|
+
* box that has never run a daemon has never polled, so it is not in the listing
|
|
211
|
+
* to be marked. Claiming a row is you on a guess would be worse than the mark
|
|
212
|
+
* being absent.
|
|
213
|
+
*/
|
|
214
|
+
export function readStoredPubB64() {
|
|
215
|
+
try {
|
|
216
|
+
const stored = JSON.parse(readFileSync(KEYPAIR_PATH, 'utf8'));
|
|
217
|
+
return typeof stored?.pub === 'string' && stored.pub ? stored.pub : null;
|
|
218
|
+
} catch {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* THE IDENTITY PARAMS THE ROSTER POLL CARRIES — one key, and it is the box.
|
|
225
|
+
*
|
|
226
|
+
* It used to carry two more: `envv` (the materialized bundle version, which fed
|
|
227
|
+
* the Settings "env vN" chip) and `envskip` (the target files the materializer
|
|
228
|
+
* REFUSED to write, with the empty string as a real "measured, refused nothing"
|
|
229
|
+
* report). Both were facts about the vault and both died with it; the server
|
|
230
|
+
* stopped reading them in the same change.
|
|
231
|
+
*
|
|
232
|
+
* WHAT DID NOT CHANGE IS `envpub`: the same base64 of the same public key out
|
|
233
|
+
* of the same file. Holdership arbitration, the displaced signal and the box
|
|
234
|
+
* registry all key on it, so a box that upgrades to this release must be the
|
|
235
|
+
* SAME box to the server it was before — no new row, no re-claim, no standby
|
|
236
|
+
* of itself. That continuity is the point of this function still existing at
|
|
237
|
+
* all.
|
|
238
|
+
*
|
|
239
|
+
* Absence keeps its reserved meaning: a poll with no `envpub` is EXEMPT from
|
|
240
|
+
* arbitration rather than treated as an unknown box.
|
|
241
|
+
*/
|
|
242
|
+
export async function envQueryParams() {
|
|
243
|
+
await ensureKeypair();
|
|
244
|
+
return { envpub: myPubB64() };
|
|
245
|
+
}
|
package/bin/lib/childEnv.mjs
CHANGED
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
* confinement is a separate uid or a namespace and is not in this product. No
|
|
28
28
|
* surface may describe anything here as "sandboxed" or "isolated".
|
|
29
29
|
*/
|
|
30
|
+
import { MACHINE_CREDENTIAL_ENV } from './machineEnv.mjs';
|
|
30
31
|
|
|
31
32
|
/**
|
|
32
33
|
* The complete kept set. Two groups, and both are here for a reason that bit
|
|
@@ -160,8 +161,7 @@ export function childEnv({ cwd, extra, deploy = false } = {}) {
|
|
|
160
161
|
* purpose: the default is what almost every caller gets, and the sample is
|
|
161
162
|
* about the default. */
|
|
162
163
|
export const DROPPED_SAMPLE = [
|
|
163
|
-
|
|
164
|
-
'FLOWVIANT_FLEET',
|
|
164
|
+
...MACHINE_CREDENTIAL_ENV,
|
|
165
165
|
'FLOWVIANT_FLEET_URL',
|
|
166
166
|
'FLOWVIANT_API_URL',
|
|
167
167
|
'FLOWVIANT_MCP_URL',
|
|
@@ -192,16 +192,16 @@ export const DEPLOY_KEEP_NAMES = [...DEPLOY_KEEP];
|
|
|
192
192
|
* deploy credential is almost never in one: it is exported in the shell they
|
|
193
193
|
* started the daemon in, which is exactly why `DEPLOY_KEEP` above exists. So
|
|
194
194
|
* the one lane that PASSES a secret into a command it then streams to the
|
|
195
|
-
* server had stopped redacting it. `deploy.mjs`'s own header
|
|
196
|
-
* still asserting the opposite — "strictly more of what a deploy log can
|
|
195
|
+
* server had stopped redacting it. `deploy.mjs`'s own header (that argument
|
|
196
|
+
* now heads deployRunner.mjs) was meanwhile still asserting the opposite — "strictly more of what a deploy log can
|
|
197
197
|
* contain than the vault ever delivered" — which is the defended-local-decision
|
|
198
198
|
* shape: a confident comment over a guarantee that had quietly lapsed.
|
|
199
199
|
*
|
|
200
|
-
* WHY IT LIVES HERE AND NOT IN `
|
|
200
|
+
* WHY IT LIVES HERE AND NOT IN `uplinkScrub.mjs`. This file is the single definition of
|
|
201
201
|
* what a spawned command may be handed; the redaction list must be the same set
|
|
202
202
|
* or it rots the moment somebody adds a name to `DEPLOY_KEEP` and forgets the
|
|
203
203
|
* other half. Deriving it FROM that array is what makes forgetting impossible,
|
|
204
|
-
* and it is why the merge happens inside `
|
|
204
|
+
* and it is why the merge happens inside `learnSecrets` rather than at a
|
|
205
205
|
* call site somebody has to remember. The dependency direction is the only one
|
|
206
206
|
* that is acyclic besides: this module imports nothing at all.
|
|
207
207
|
*
|
|
@@ -237,7 +237,7 @@ export const DEPLOY_KEEP_NAMES = [...DEPLOY_KEEP];
|
|
|
237
237
|
* nothing (the `CF_*` argument above), so they join the REDACTION list only;
|
|
238
238
|
* `DEPLOY_KEEP` is untouched and `childEnv` still hands none of them to anybody.
|
|
239
239
|
*
|
|
240
|
-
* The machine credential is ALSO caught by shape in `
|
|
240
|
+
* The machine credential is ALSO caught by shape in `uplinkScrub.mjs` (`fva_…`),
|
|
241
241
|
* because the copy that matters most is not in any environment variable at all:
|
|
242
242
|
* it is every project's `fleetToken` in `~/.flowviant/credentials.json`, plus
|
|
243
243
|
* the per-turn work/capture tokens the server mints. The names here are belt.
|
|
@@ -245,8 +245,10 @@ export const DEPLOY_KEEP_NAMES = [...DEPLOY_KEEP];
|
|
|
245
245
|
const REDACT_ONLY = [
|
|
246
246
|
'CF_API_TOKEN',
|
|
247
247
|
'CF_ACCOUNT_ID',
|
|
248
|
-
|
|
249
|
-
|
|
248
|
+
// Every machine credential name, from the one list children are stripped
|
|
249
|
+
// of (machineEnv.mjs): a name removed from children is redacted by
|
|
250
|
+
// construction, never by a second hand-kept copy.
|
|
251
|
+
...MACHINE_CREDENTIAL_ENV,
|
|
250
252
|
'ANTHROPIC_API_KEY',
|
|
251
253
|
'ANTHROPIC_AUTH_TOKEN',
|
|
252
254
|
'CLAUDE_CODE_OAUTH_TOKEN',
|