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
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE RECONCILE WAIT — idle until the next poll deadline OR a push wake,
|
|
3
|
+
* whichever comes first.
|
|
4
|
+
*
|
|
5
|
+
* Split out of fleet.mjs (SOLID audit 2026-09-26, F038): a small piece of
|
|
6
|
+
* timing state (the pending resolver, and a wake that landed mid-reconcile)
|
|
7
|
+
* that the loop only ever touches through these two functions. The socket
|
|
8
|
+
* itself is stream.mjs's; this is what a wake DOES to the loop.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { RECONCILE_SECONDS } from './config.mjs';
|
|
12
|
+
|
|
13
|
+
export function createReconcileWait() {
|
|
14
|
+
// ── Push channel: a server wake short-circuits the reconcile sleep so a job is
|
|
15
|
+
// picked up in ~a round trip instead of on the next poll. The socket only
|
|
16
|
+
// nudges — we still fetch the roster below — so it's pure latency, and the
|
|
17
|
+
// poll stays the fallback whenever the socket is down. `waitReconcile()`
|
|
18
|
+
// resolves on either a wake or the RECONCILE_SECONDS timeout, whichever first.
|
|
19
|
+
let wakeSignal = null; // { resolve, timer } while the loop is idling
|
|
20
|
+
let pendingWake = false; // a wake that landed mid-reconcile — honored next wait
|
|
21
|
+
const fireWake = () => {
|
|
22
|
+
if (wakeSignal) {
|
|
23
|
+
clearTimeout(wakeSignal.timer);
|
|
24
|
+
const { resolve } = wakeSignal;
|
|
25
|
+
wakeSignal = null;
|
|
26
|
+
resolve();
|
|
27
|
+
} else {
|
|
28
|
+
pendingWake = true; // not idling right now; don't lose the wake
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const waitReconcile = () => {
|
|
32
|
+
if (pendingWake) {
|
|
33
|
+
pendingWake = false;
|
|
34
|
+
return Promise.resolve();
|
|
35
|
+
}
|
|
36
|
+
return new Promise((resolve) => {
|
|
37
|
+
const timer = setTimeout(() => {
|
|
38
|
+
wakeSignal = null;
|
|
39
|
+
resolve();
|
|
40
|
+
}, RECONCILE_SECONDS * 1000);
|
|
41
|
+
wakeSignal = { resolve, timer };
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
return { fireWake, waitReconcile };
|
|
45
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EVERY `/fleet/*` ENDPOINT, derived from the one roster URL.
|
|
3
|
+
*
|
|
4
|
+
* One place configures the API base (`FLEET_URL`, overridable by
|
|
5
|
+
* `FLOWVIANT_FLEET_URL`, ending in `/fleet/agents`) and every other daemon
|
|
6
|
+
* endpoint is that URL with its final `agents` segment swapped — so a
|
|
7
|
+
* self-hosted server, or the `/api` → `/api/v2` alias, is honoured by every
|
|
8
|
+
* lane at once.
|
|
9
|
+
*
|
|
10
|
+
* ONE HOME (SOLID audit 2026-09-26, F166). The swap was spelled
|
|
11
|
+
* `FLEET_URL.replace(/\/agents\/?$/, '/…')` forty-nine times across eighteen
|
|
12
|
+
* files; a change to the roster URL's shape, or to what counts as a valid
|
|
13
|
+
* endpoint name, was forty-nine edits.
|
|
14
|
+
*
|
|
15
|
+
* WHAT IT VALIDATES, AND WHAT IT DELIBERATELY DOES NOT. An endpoint NAME that
|
|
16
|
+
* is not a plain lowercase path is a programming error and throws. A ROSTER
|
|
17
|
+
* URL that does not end in `/agents` is NOT refused here: endpoints are
|
|
18
|
+
* derived while modules load (fleetReports.mjs, landed.mjs, login.mjs), and a throw
|
|
19
|
+
* there took down every command — `help` and `projects` included — over a
|
|
20
|
+
* setting only the daemon's lanes use (measured while making this change). Such
|
|
21
|
+
* a URL comes back unchanged, exactly as the forty-nine copies answered, and
|
|
22
|
+
* the roster poll against it is the first thing to fail and say so.
|
|
23
|
+
*
|
|
24
|
+
* The push channel (`STREAM_URL` in config.mjs) is the one derivation kept
|
|
25
|
+
* apart: it switches the scheme to ws and keeps a trailing slash.
|
|
26
|
+
*
|
|
27
|
+
* A leaf on purpose: it takes the roster URL as an argument rather than
|
|
28
|
+
* importing `config.mjs`, so modules that are handed a `fleetUrl` (the
|
|
29
|
+
* artifact reporter, the knowledge fetcher, the tray's remote status) do not
|
|
30
|
+
* gain a config import to name a path.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
const ROSTER_TAIL = /\/agents\/?$/;
|
|
34
|
+
const NAME = /^[a-z0-9-]+(\/[a-z0-9-]+)*$/;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The `/fleet/<name>` endpoint beside the roster URL `rosterUrl`
|
|
38
|
+
* (`https://api.flowviant.com/api/fleet/agents` → `…/api/fleet/<name>`). A
|
|
39
|
+
* trailing slash on the roster URL is accepted and not carried over.
|
|
40
|
+
*/
|
|
41
|
+
export function fleetEndpoint(name, rosterUrl) {
|
|
42
|
+
if (typeof name !== 'string' || !NAME.test(name)) {
|
|
43
|
+
throw new TypeError(`fleetEndpoint: "${name}" is not a /fleet endpoint name`);
|
|
44
|
+
}
|
|
45
|
+
return String(rosterUrl).replace(ROSTER_TAIL, `/${name}`);
|
|
46
|
+
}
|
package/bin/lib/git.mjs
CHANGED
|
@@ -250,12 +250,43 @@ export function detectBaseRef(repoRoot) {
|
|
|
250
250
|
}
|
|
251
251
|
}
|
|
252
252
|
try {
|
|
253
|
-
return `origin/${git(['rev-parse', '--abbrev-ref', 'HEAD'], repoRoot)}
|
|
253
|
+
return usableBaseRef(repoRoot, `origin/${git(['rev-parse', '--abbrev-ref', 'HEAD'], repoRoot)}`);
|
|
254
254
|
} catch {
|
|
255
255
|
return 'HEAD';
|
|
256
256
|
}
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
+
/**
|
|
260
|
+
* A base ref an agent's worktree can actually be cut from.
|
|
261
|
+
*
|
|
262
|
+
* `origin/<branch>` is the right base when the repo HAS an origin. A repo with
|
|
263
|
+
* no remote (a local-only project — the owner's "Death Note - Roblox",
|
|
264
|
+
* 2026-09-25) has no `origin/*` refs at all, and cutting a worktree from one
|
|
265
|
+
* fails; every agent turn then ended "nothing" in a second. So: the ref itself
|
|
266
|
+
* when it resolves to a commit, else the LOCAL branch of the same name, else
|
|
267
|
+
* the ref unchanged (and the worktree step says why in git's words).
|
|
268
|
+
*/
|
|
269
|
+
export function usableBaseRef(repoRoot, ref) {
|
|
270
|
+
const resolves = (r) => {
|
|
271
|
+
try { git(['rev-parse', '--verify', '--quiet', `${r}^{commit}`], repoRoot); return true; } catch { return false; }
|
|
272
|
+
};
|
|
273
|
+
if (resolves(ref)) return ref;
|
|
274
|
+
const local = String(ref).replace(/^origin\//, '');
|
|
275
|
+
if (local !== ref && resolves(local)) return local;
|
|
276
|
+
return ref;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/** True when the repository has at least one commit (HEAD resolves). */
|
|
280
|
+
export function hasCommits(repoRoot) {
|
|
281
|
+
try { git(['rev-parse', '--verify', '--quiet', 'HEAD^{commit}'], repoRoot); return true; } catch { return false; }
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/** The last line git wrote to stderr for a failed call, or its message. */
|
|
285
|
+
export function gitFailure(error) {
|
|
286
|
+
const stderr = String(error?.stderr ?? '').trim().split('\n').filter(Boolean);
|
|
287
|
+
return stderr.at(-1) ?? String(error?.message ?? error).split('\n')[0];
|
|
288
|
+
}
|
|
289
|
+
|
|
259
290
|
/**
|
|
260
291
|
* The BRANCH NAME behind a base ref.
|
|
261
292
|
*
|
|
@@ -292,8 +323,8 @@ export function resetWorktree(wt, baseRef) {
|
|
|
292
323
|
* the vault is what first needed it — materialized `.env` files had to be
|
|
293
324
|
* untracked AND unstageable — but it is a plain `git info/exclude` helper with
|
|
294
325
|
* no crypto, no bundle and no opinion about secrets, and the vault is deleted.
|
|
295
|
-
* Its
|
|
296
|
-
*
|
|
326
|
+
* Its callers are the work manager (`work.mjs`, before a turn's artifacts)
|
|
327
|
+
* and `workAttachments.mjs`, which hide a tab's `.flowviant/` paths with it.
|
|
297
328
|
*
|
|
298
329
|
* WHY IT IS NOT `.git/worktrees/<name>/info/exclude`: it used to resolve the
|
|
299
330
|
* worktree's own gitdir and write there, on the belief that the file "applies
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE IDENTITY A DAEMON MERGE COMMIT IS WRITTEN UNDER (2026-09-26, SOLID F165).
|
|
3
|
+
*
|
|
4
|
+
* A merge COMMIT needs a git identity and the machine may have none. Prefer the
|
|
5
|
+
* operator's own config; fall back to the daemon's, so a bare machine does not
|
|
6
|
+
* fail the fold with "Please tell me who you are".
|
|
7
|
+
*
|
|
8
|
+
* Split out because the session ship lane (workShip.mjs) and the agent merge
|
|
9
|
+
* lane (workAgentMerges.mjs) each carried a copy of the probe and the four
|
|
10
|
+
* fallback variables — an identity-policy change needed two edits. Both lanes
|
|
11
|
+
* now take their `gitMerge` from `gitMergeIn(repoRoot)`; their merge decisions
|
|
12
|
+
* are their own.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { execFileSync } from 'node:child_process';
|
|
16
|
+
import { git } from './git.mjs';
|
|
17
|
+
|
|
18
|
+
export const DAEMON_GIT_IDENTITY = Object.freeze({
|
|
19
|
+
GIT_AUTHOR_NAME: 'Flowviant',
|
|
20
|
+
GIT_AUTHOR_EMAIL: 'daemon@flowviant.com',
|
|
21
|
+
GIT_COMMITTER_NAME: 'Flowviant',
|
|
22
|
+
GIT_COMMITTER_EMAIL: 'daemon@flowviant.com',
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
/** The env a merge in `repoRoot` needs: null when the operator's config names a
|
|
26
|
+
* user.email (git uses theirs), the daemon's identity when it names none. */
|
|
27
|
+
export function mergeIdentityEnv(repoRoot) {
|
|
28
|
+
try {
|
|
29
|
+
git(['config', 'user.email'], repoRoot);
|
|
30
|
+
return null;
|
|
31
|
+
} catch {
|
|
32
|
+
return DAEMON_GIT_IDENTITY;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* A `gitMerge(args, cwd)` for a checkout rooted at `repoRoot`: runs `git args`
|
|
38
|
+
* in `cwd` (a throwaway worktree, usually) under the identity the probe finds.
|
|
39
|
+
* The probe runs per call, so an identity configured mid-job is heard.
|
|
40
|
+
*/
|
|
41
|
+
export function gitMergeIn(repoRoot) {
|
|
42
|
+
return (args, cwd) => {
|
|
43
|
+
const idEnv = mergeIdentityEnv(repoRoot);
|
|
44
|
+
return execFileSync('git', args, {
|
|
45
|
+
cwd,
|
|
46
|
+
encoding: 'utf8',
|
|
47
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
48
|
+
...(idEnv ? { env: { ...process.env, ...idEnv } } : {}),
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
}
|
package/bin/lib/grant.mjs
CHANGED
|
@@ -46,8 +46,10 @@ export const GRANT_SKEW_MS = 60_000;
|
|
|
46
46
|
const b64url = (buf) => buf.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
47
47
|
|
|
48
48
|
/** Constant-time over sha256 digests, so length never leaks and a missing
|
|
49
|
-
* value costs the same as a wrong one. Never `===` on a signature
|
|
50
|
-
|
|
49
|
+
* value costs the same as a wrong one. Never `===` on a signature or a
|
|
50
|
+
* password. The daemon's ONE secret comparison: the grant signature below and
|
|
51
|
+
* the gate's Basic password (`previewGatePolicy.mjs`) both ask it. */
|
|
52
|
+
export function sameSecret(a, b) {
|
|
51
53
|
try {
|
|
52
54
|
const da = createHash('sha256').update(String(a ?? '')).digest();
|
|
53
55
|
const db = createHash('sha256').update(String(b ?? '')).digest();
|
|
@@ -80,7 +82,7 @@ export function verifyGrant(raw, { secret, shareId, nowMs, skewMs } = {}) {
|
|
|
80
82
|
}
|
|
81
83
|
|
|
82
84
|
const expected = b64url(createHmac('sha256', String(secret)).update(DOMAIN + body).digest());
|
|
83
|
-
if (!
|
|
85
|
+
if (!sameSecret(sig, expected)) return { ok: false, reason: 'sig' };
|
|
84
86
|
|
|
85
87
|
let payload;
|
|
86
88
|
try {
|
package/bin/lib/help.mjs
CHANGED
|
@@ -9,127 +9,12 @@
|
|
|
9
9
|
* and `<command> --help` print one entry, and an unknown word is refused with
|
|
10
10
|
* the nearest command instead of being served.
|
|
11
11
|
*
|
|
12
|
-
* THE TABLE
|
|
13
|
-
*
|
|
14
|
-
* (or listed here and not dispatched). `hidden` rows are real commands a
|
|
15
|
-
* person rarely types (an agent calls `shot`; `mcp` manages connectors from a
|
|
16
|
-
* script); they answer `help <name>` but stay off the main screen.
|
|
12
|
+
* THE TABLE is commandSpecs.mjs (one list for help, the --dir checkout policy
|
|
13
|
+
* and cli.mjs's dispatch); this module renders it and answers a slip.
|
|
17
14
|
*/
|
|
18
15
|
|
|
19
16
|
import { c } from './ui.mjs';
|
|
20
|
-
|
|
21
|
-
export const COMMANDS = [
|
|
22
|
-
{
|
|
23
|
-
name: 'start',
|
|
24
|
-
usage: 'flowviant [start] [--project <name|id>]',
|
|
25
|
-
summary: "Serve this repo's project: run agents and Workbench tabs here. Plain `flowviant` does the same.",
|
|
26
|
-
details: [
|
|
27
|
-
'Run it inside the repository you connected. The first run in a new repo asks you to approve this computer.',
|
|
28
|
-
'--project <name|id> serve that stored project instead of the one this repo is bound to',
|
|
29
|
-
],
|
|
30
|
-
group: 'run',
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
name: 'login',
|
|
34
|
-
usage: 'flowviant login [--no-start]',
|
|
35
|
-
summary: 'Connect this repo to a project: approve the code in your browser, then start serving.',
|
|
36
|
-
details: ['--no-start save the connection without starting the daemon'],
|
|
37
|
-
group: 'run',
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
name: 'stop',
|
|
41
|
-
usage: 'flowviant stop [--project <name|id>]',
|
|
42
|
-
summary: 'Stop every flowviant daemon on this computer (or just one project’s).',
|
|
43
|
-
group: 'run',
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
name: 'status',
|
|
47
|
-
usage: 'flowviant status',
|
|
48
|
-
summary: 'Each project connected here: its repo, whether it is running and serving, and its live agents.',
|
|
49
|
-
details: ['--json the machine-readable form the Windows tray reads'],
|
|
50
|
-
group: 'look',
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
name: 'logs',
|
|
54
|
-
usage: 'flowviant logs [-f] [--project <name|id>]',
|
|
55
|
-
summary: "The daemon's log for this repo's project.",
|
|
56
|
-
details: ['-f, --follow keep printing new lines as they are written', '-n <lines> how many lines to show first (default 80)'],
|
|
57
|
-
group: 'look',
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
name: 'open',
|
|
61
|
-
usage: 'flowviant open [--project <name|id>]',
|
|
62
|
-
summary: "Open this project's board in your browser (prints the link too).",
|
|
63
|
-
group: 'look',
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
name: 'projects',
|
|
67
|
-
usage: 'flowviant projects',
|
|
68
|
-
summary: 'Every project this computer has a login for, and the repo each one serves.',
|
|
69
|
-
group: 'look',
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
name: 'machines',
|
|
73
|
-
usage: 'flowviant machines [--remove <id> | --forget <id>] [--yes]',
|
|
74
|
-
summary: 'Every computer that serves your projects. On a terminal, a menu to disconnect or forget one here.',
|
|
75
|
-
group: 'look',
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
name: 'doctor',
|
|
79
|
-
usage: 'flowviant doctor',
|
|
80
|
-
summary: 'Check what this computer needs: git, a signed-in CLI, a connection to Flowviant, a running daemon.',
|
|
81
|
-
group: 'look',
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
name: 'update',
|
|
85
|
-
usage: 'flowviant update',
|
|
86
|
-
summary: 'Install the newest flowviant now. It also updates itself when idle.',
|
|
87
|
-
group: 'manage',
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
name: 'clean',
|
|
91
|
-
usage: 'flowviant clean',
|
|
92
|
-
summary: 'Delete the kept agent worktrees under ~/.flowviant/worktrees. Stop the daemon first.',
|
|
93
|
-
group: 'manage',
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
name: 'uninstall',
|
|
97
|
-
usage: 'flowviant uninstall [--purge] [--yes]',
|
|
98
|
-
summary: 'Remove every flowviant copy on this computer. Logins are kept unless you add --purge.',
|
|
99
|
-
details: ['--purge also disconnect this computer from its projects and delete ~/.flowviant', '--yes do not ask first'],
|
|
100
|
-
group: 'manage',
|
|
101
|
-
},
|
|
102
|
-
{
|
|
103
|
-
name: 'gh-auth',
|
|
104
|
-
usage: 'flowviant gh-auth',
|
|
105
|
-
summary: 'Sign in the GitHub CLI flowviant uses to open pull requests.',
|
|
106
|
-
group: 'manage',
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
name: 'version',
|
|
110
|
-
usage: 'flowviant --version',
|
|
111
|
-
summary: 'Print the installed version.',
|
|
112
|
-
group: 'manage',
|
|
113
|
-
},
|
|
114
|
-
{
|
|
115
|
-
name: 'help',
|
|
116
|
-
usage: 'flowviant help [command]',
|
|
117
|
-
summary: 'This list, or one command in detail.',
|
|
118
|
-
group: 'manage',
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
name: 'shot',
|
|
122
|
-
usage: 'flowviant shot <url> [--out <file>]',
|
|
123
|
-
summary: 'Screenshot a running page with a headless browser (agents use it to see their change).',
|
|
124
|
-
hidden: true,
|
|
125
|
-
},
|
|
126
|
-
{
|
|
127
|
-
name: 'mcp',
|
|
128
|
-
usage: 'flowviant mcp <subcommand>',
|
|
129
|
-
summary: "Manage the project's MCP connectors from a script.",
|
|
130
|
-
hidden: true,
|
|
131
|
-
},
|
|
132
|
-
];
|
|
17
|
+
import { COMMANDS } from './commandSpecs.mjs';
|
|
133
18
|
|
|
134
19
|
const GROUPS = [
|
|
135
20
|
['run', 'Run'],
|
|
@@ -137,13 +22,6 @@ const GROUPS = [
|
|
|
137
22
|
['manage', 'Manage'],
|
|
138
23
|
];
|
|
139
24
|
|
|
140
|
-
const ALIASES = { '--version': 'version', '-v': 'version', '--help': 'help', '-h': 'help' };
|
|
141
|
-
|
|
142
|
-
export function findCommand(name) {
|
|
143
|
-
const key = ALIASES[name] ?? name;
|
|
144
|
-
return COMMANDS.find((cmd) => cmd.name === key) ?? null;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
25
|
/** The main screen: every visible command, grouped, one line each. */
|
|
148
26
|
export function renderHelp(version) {
|
|
149
27
|
const width = Math.max(...COMMANDS.filter((cmd) => !cmd.hidden).map((cmd) => cmd.name.length));
|