@vincentt-xr/cli 0.21.0 → 0.23.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/MIGRATION.md +26 -0
- package/dist/cli/args.js +2 -0
- package/dist/cli/index.js +50 -8
- package/dist/cli/usage-text.js +10 -1
- package/dist/cli/verb-surface.d.ts +2 -1
- package/dist/cli/verb-surface.js +5 -0
- package/dist/dx/command.d.ts +74 -18
- package/dist/dx/command.js +137 -86
- package/dist/dx/consent.d.ts +25 -6
- package/dist/dx/consent.js +58 -25
- package/dist/dx/enablement.d.ts +63 -11
- package/dist/dx/enablement.js +93 -21
- package/dist/dx/journal.d.ts +3 -0
- package/dist/dx/journal.js +6 -1
- package/dist/dx/report.d.ts +4 -24
- package/dist/dx/report.js +32 -25
- package/dist/dx/skeleton.js +3 -0
- package/dist/editor/app.d.ts +61 -0
- package/dist/editor/app.js +180 -0
- package/dist/editor/copy.d.ts +22 -0
- package/dist/editor/copy.js +95 -0
- package/dist/editor/handoffs.d.ts +12 -0
- package/dist/editor/handoffs.js +93 -0
- package/dist/editor/launcher.d.ts +86 -0
- package/dist/editor/launcher.js +255 -0
- package/dist/editor/localFiles.d.ts +31 -0
- package/dist/editor/localFiles.js +100 -0
- package/dist/editor/reads.d.ts +47 -0
- package/dist/editor/reads.js +269 -0
- package/dist/editor/text.d.ts +7 -0
- package/dist/editor/text.js +58 -0
- package/dist/editor/tools.d.ts +5 -0
- package/dist/editor/tools.js +20 -0
- package/dist/lib/version.d.ts +28 -2
- package/dist/lib/version.js +30 -3
- package/dist/preview/command.js +2 -0
- package/dist/preview/copy.d.ts +5 -0
- package/dist/preview/copy.js +15 -0
- package/dist/preview/runner.d.ts +6 -0
- package/dist/preview/runner.js +1 -0
- package/dist/publish/command.js +5 -2
- package/dist/publish/copy.d.ts +6 -0
- package/dist/publish/copy.js +27 -6
- package/dist/publish/manifest.d.ts +11 -2
- package/dist/publish/manifest.js +45 -9
- package/dist/publish/upload.d.ts +10 -0
- package/dist/publish/upload.js +23 -0
- package/dist/shared/config.d.ts +6 -6
- package/dist/tunnel/client.d.ts +11 -3
- package/dist/tunnel/client.js +11 -8
- package/package.json +2 -2
package/MIGRATION.md
CHANGED
|
@@ -3,6 +3,32 @@
|
|
|
3
3
|
Per version, newest first. Read by the platform's own version surface and served to
|
|
4
4
|
creators' agents. Accurate or absent, never speculative (B-F5-2).
|
|
5
5
|
|
|
6
|
+
## 0.23.0
|
|
7
|
+
|
|
8
|
+
required: false
|
|
9
|
+
No action required. New verb `vincentt editor`, which hands the project to the Vincentt Editor desktop app.
|
|
10
|
+
- `vincentt editor` (macOS 14 or later, no account) finds `Vincentt Editor.app` in `/Applications`, then `~/Applications`, checks it is signed by Vincentt, hands it the project folder and exits 0 with `✓ Sent <name> to Vincentt Editor.` It does not wait for the app. The app is a separate download from https://vincentt.studio/editor; the CLI installs no editor binary and has no port option for it.
|
|
11
|
+
- It exits 1, last line `Nothing was opened.`, when the app is not installed, the installed copy is not signed by Vincentt, the Mac is not on macOS 14 or later, or another copy of the app is running from a different folder.
|
|
12
|
+
- `vincentt editor --json` prints one `vincentt.editor-open/1` object: `{"schema","handedOff":true,"project","app","appVersion"}`, or `handedOff: false` with a `reason` (`not_installed`, `bad_signature`, `unsupported_os`, `other_copy_running`, `open_failed`) and, where known, `app` and `detail`. The sentence goes to stderr.
|
|
13
|
+
- `vincentt editor --selection [--json]` prints what is selected in the editor (`vincentt.editor-selection/1`), and `vincentt editor --status --json` the editor's state for the folder (`vincentt.editor-state/1`, including `app.lastOpen`). Both work on any OS and exit 1, with one JSON object still on stdout, when no editor is running for the folder.
|
|
14
|
+
- In `--status --json`, `app` is always `{"version","lastOpen"}`; when no editor is running, `lastOpen` is reported only for a folder this machine handed over with `vincentt editor`, from a state file written after that hand-off, and is `null` otherwise.
|
|
15
|
+
- The CLI keeps those hand-offs in `~/.vincentt/editor-handoffs.json`, as a hash of each folder's path and a time, never the path itself. A file written in a newer format than this CLI reads is refused with an upgrade line on stderr and nothing on stdout.
|
|
16
|
+
|
|
17
|
+
## 0.22.0
|
|
18
|
+
|
|
19
|
+
required: false
|
|
20
|
+
No action required. Behaviour that changed:
|
|
21
|
+
- `vincentt dx enable` at a terminal now asks `Allow unattended reports on your account? [y/N]` and, on `y`, records the grant on your Vincentt account first, then sets this machine. Success reads `Enabled for your account. Reports may now be sent without a prompt from any machine signed in to it.`
|
|
22
|
+
- If the account does not record the grant, `vincentt dx enable` changes nothing and exits 1 with `✗ Vincentt did not record your consent.`, `✗ Too many consent changes. Wait a minute and try again.`, or `✗ Could not reach Vincentt to record your consent.` A refused sign-in exits 4 and a too-old CLI exits 5, as on other verbs.
|
|
23
|
+
- Unattended `vincentt dx --send` and `vincentt report` now check the account first. An account grant sends; an account that is not granted refuses (exit 1) and clears this machine's setting, so turning reports off in the console or on another machine reaches this one. This machine's setting is used on its own only when the account cannot be reached. An interactive session still asks and makes no account check.
|
|
24
|
+
- The `Unattended send: ENABLED since <day> (<n> days)` line now ends with where the answer came from: `· granted from the CLI`, `· granted in the console`, `· on your account`, or `· this machine (account not checked)`. `vincentt dx` and `vincentt version` check the account for up to 3 seconds first; `vincentt version` never fails because of that check.
|
|
25
|
+
- `vincentt dx --json` adds `dxConsent`: `{"state": "on" | "off" | "unknown", "checked": "account" | "machine", "at"?, "via"?}`. `dxSendEnabled` and `dxSendEnabledAt` still describe this machine's setting.
|
|
26
|
+
- `vincentt preview` has a new ending: when the preview server answers the tunnel with a reply this CLI cannot accept, the preview ends once with `✗ Preview stopped: the preview server sent an invalid reply. Update the CLI or try again later.` (exit 1) instead of reconnecting every few seconds indefinitely. The screen names the upgrade command and `vincentt preview`.
|
|
27
|
+
- `vincentt publish` refuses the whole publish, listing the files, when a folder in the build output turns into a link to somewhere outside it while publish reads it. Let the build finish, then publish again.
|
|
28
|
+
- `vincentt publish` refuses to upload when the platform's upload link does not carry the checksum publish computed for that file; nothing is sent, and the screen names the file.
|
|
29
|
+
- A folder or file in the build output that publish cannot read now ends the publish with the "changed while publish was reading it" screen naming the path inside the build output, instead of a raw error message holding the full path.
|
|
30
|
+
- File names printed on publish refusal screens show bidirectional and direction-mark characters as visible `\uXXXX` escapes.
|
|
31
|
+
|
|
6
32
|
## 0.21.0
|
|
7
33
|
|
|
8
34
|
required: false
|
package/dist/cli/args.js
CHANGED
package/dist/cli/index.js
CHANGED
|
@@ -29,7 +29,7 @@ import { listProjectsCommand } from "../lib/listProjects.js";
|
|
|
29
29
|
import { initCommand } from "../lib/initCommand.js";
|
|
30
30
|
import { linkCommand, printUnreachable, unlinkCommand } from "../lib/projects.js";
|
|
31
31
|
import { whoamiCommand } from "../lib/whoami.js";
|
|
32
|
-
import { renderVersion, versionReport } from "../lib/version.js";
|
|
32
|
+
import { renderVersion, VERSION_ACCOUNT_TIMEOUT_MS, versionReport, } from "../lib/version.js";
|
|
33
33
|
import { diagQuery, diagRead, previewStart, previewStop, previewStopSignal, projectCreate, } from "../lib/lifecycle.js";
|
|
34
34
|
import { publishCommand } from "../publish/command.js";
|
|
35
35
|
import { stdinAsk, stdinConfirm } from "../publish/prompt.js";
|
|
@@ -53,7 +53,7 @@ import * as feedbackCopy from "../lib/feedbackCopy.js";
|
|
|
53
53
|
import { record as recordInvocation } from "../dx/journal.js";
|
|
54
54
|
import { dxDisableCommand, dxEnableCommand, dxSendCommand, parseDxOptions, runDx, sinceUsageLines, } from "../dx/command.js";
|
|
55
55
|
import { httpConsentBackend, runConsentInBrowser } from "../dx/consent.js";
|
|
56
|
-
import { DX_UNATTENDED_SUNSET, loadEnablement } from "../dx/enablement.js";
|
|
56
|
+
import { DX_UNATTENDED_SUNSET, loadEnablement, resolveConsentView, } from "../dx/enablement.js";
|
|
57
57
|
import { journalEnabled, journalPath, readJournal } from "../dx/journal.js";
|
|
58
58
|
import { postReport } from "../dx/send.js";
|
|
59
59
|
import { nudgeFor } from "../dx/nudge.js";
|
|
@@ -1139,14 +1139,22 @@ function cliVersion() {
|
|
|
1139
1139
|
}
|
|
1140
1140
|
}
|
|
1141
1141
|
async function version() {
|
|
1142
|
+
const now = Date.now();
|
|
1142
1143
|
const report = await versionReport(projectDir(), {
|
|
1143
1144
|
cliVersion: cliVersion(),
|
|
1144
1145
|
allowCustomApi: has("--allow-custom-api"),
|
|
1146
|
+
consent: {
|
|
1147
|
+
auth: dxAuth,
|
|
1148
|
+
readAccount: (auth) => httpConsentBackend().read(auth.apiUrl, auth.pat, now, VERSION_ACCOUNT_TIMEOUT_MS),
|
|
1149
|
+
clearLocal: () => writeDxEnablement(undefined),
|
|
1150
|
+
},
|
|
1145
1151
|
});
|
|
1146
1152
|
for (const line of renderVersion(report))
|
|
1147
1153
|
console.log(line);
|
|
1148
1154
|
}
|
|
1149
1155
|
// ---- dx ---------------------------------------------------------------------
|
|
1156
|
+
/** The account bound on `vincentt dx` (f7 Scenario 3c); the send gate uses the default. */
|
|
1157
|
+
const DX_ACCOUNT_TIMEOUT_MS = 3_000;
|
|
1150
1158
|
/**
|
|
1151
1159
|
* The API host + PAT for a dx call, or undefined when signed out.
|
|
1152
1160
|
*
|
|
@@ -1197,6 +1205,7 @@ async function dx() {
|
|
|
1197
1205
|
onProgress: (l) => console.log(l),
|
|
1198
1206
|
});
|
|
1199
1207
|
},
|
|
1208
|
+
grantAccountConsent: (auth) => consent.grant(auth.apiUrl, auth.pat, now),
|
|
1200
1209
|
revokeAccountConsent: (auth) => consent.revoke(auth.apiUrl, auth.pat),
|
|
1201
1210
|
};
|
|
1202
1211
|
process.exitCode = options.enable
|
|
@@ -1214,7 +1223,15 @@ async function dx() {
|
|
|
1214
1223
|
// relaxation, not throw through it.
|
|
1215
1224
|
const enablement = await loadEnablement(now);
|
|
1216
1225
|
if (!options.send) {
|
|
1217
|
-
|
|
1226
|
+
// The report tail and `dxConsent` read the account first, bounded at 3 s.
|
|
1227
|
+
const view = await resolveConsentView({
|
|
1228
|
+
auth: await dxAuth(),
|
|
1229
|
+
readAccount: (auth) => httpConsentBackend().read(auth.apiUrl, auth.pat, now, DX_ACCOUNT_TIMEOUT_MS),
|
|
1230
|
+
local: () => enablement,
|
|
1231
|
+
clearLocal: () => writeDxEnablement(undefined),
|
|
1232
|
+
timeoutMs: DX_ACCOUNT_TIMEOUT_MS,
|
|
1233
|
+
});
|
|
1234
|
+
process.exitCode = runDx(argv, (l) => console.log(l), () => view.local, () => view);
|
|
1218
1235
|
return;
|
|
1219
1236
|
}
|
|
1220
1237
|
// `--send` is the one path out of this machine, so it resolves auth the way
|
|
@@ -1231,10 +1248,9 @@ async function dx() {
|
|
|
1231
1248
|
confirm: (q) => stdinConfirm()(`${q} [y/N] `, false),
|
|
1232
1249
|
interactive,
|
|
1233
1250
|
enablement: () => enablement,
|
|
1234
|
-
// The
|
|
1235
|
-
// first two have not already allowed the send, and allowed to throw — a /me that
|
|
1236
|
-
// is unreachable falls back to the other two arms rather than blocking.
|
|
1251
|
+
// The account decides when it answers; never read on an interactive session.
|
|
1237
1252
|
accountConsent: (auth) => httpConsentBackend().read(auth.apiUrl, auth.pat, now),
|
|
1253
|
+
clearEnablement: () => writeDxEnablement(undefined),
|
|
1238
1254
|
// `--dir`-or-cwd, the same resolution every other verb uses, so `--dir`
|
|
1239
1255
|
// works on a send as it does everywhere else.
|
|
1240
1256
|
cwd: projectDir(),
|
|
@@ -1276,6 +1292,7 @@ async function report() {
|
|
|
1276
1292
|
interactive,
|
|
1277
1293
|
enablement: () => enablement,
|
|
1278
1294
|
accountConsent: (auth) => httpConsentBackend().read(auth.apiUrl, auth.pat, now),
|
|
1295
|
+
clearEnablement: () => writeDxEnablement(undefined),
|
|
1279
1296
|
// `--dir`-or-cwd, the same resolution every other verb uses, so `--dir`
|
|
1280
1297
|
// works on a report as it does everywhere else.
|
|
1281
1298
|
cwd: projectDir(),
|
|
@@ -1429,6 +1446,25 @@ async function domain() {
|
|
|
1429
1446
|
yes: has("--yes"),
|
|
1430
1447
|
});
|
|
1431
1448
|
}
|
|
1449
|
+
// ---- Vincentt Editor (f21) --------------------------------------------------
|
|
1450
|
+
/** Why `editor` exited 1, for the journal (one word from a closed set, B-F7-1). */
|
|
1451
|
+
let editorReason;
|
|
1452
|
+
/**
|
|
1453
|
+
* `vincentt editor` hands the project folder to the installed Vincentt Editor app and exits.
|
|
1454
|
+
* Needs no account and makes no network call.
|
|
1455
|
+
*/
|
|
1456
|
+
async function editor() {
|
|
1457
|
+
const { editorVerb } = await import("../editor/launcher.js");
|
|
1458
|
+
// The launcher's parser accepts `--dir=<path>`, so the folder it names is the one handed over.
|
|
1459
|
+
const dir = flag("--dir") ?? valueOfEqualsFlag(argv, "--dir");
|
|
1460
|
+
const run = await editorVerb(argv.slice(1), dir ? path.resolve(dir) : process.cwd());
|
|
1461
|
+
for (const l of run.stdout)
|
|
1462
|
+
console.log(l);
|
|
1463
|
+
for (const l of run.stderr)
|
|
1464
|
+
console.error(l);
|
|
1465
|
+
editorReason = run.reason;
|
|
1466
|
+
process.exitCode = run.exitCode;
|
|
1467
|
+
}
|
|
1432
1468
|
// ---- servers ----------------------------------------------------------------
|
|
1433
1469
|
async function relay() {
|
|
1434
1470
|
const { startRelay } = await import("../relay/server.js");
|
|
@@ -1532,6 +1568,8 @@ async function main() {
|
|
|
1532
1568
|
return outdated();
|
|
1533
1569
|
case "domain":
|
|
1534
1570
|
return domain();
|
|
1571
|
+
case "editor":
|
|
1572
|
+
return editor();
|
|
1535
1573
|
}
|
|
1536
1574
|
}
|
|
1537
1575
|
// Every invocation is journaled here, at the one site that sees all of them —
|
|
@@ -1539,6 +1577,8 @@ async function main() {
|
|
|
1539
1577
|
// command. Recording is best-effort and local-only (see dx/journal.ts); it never
|
|
1540
1578
|
// changes what the CLI does or what it exits with.
|
|
1541
1579
|
const startedAt = Date.now();
|
|
1580
|
+
/** Verbs that make no network request, footnotes included: their footnotes never refresh. */
|
|
1581
|
+
const NO_NETWORK_VERBS = new Set(["editor"]);
|
|
1542
1582
|
function journal(exitCode, error) {
|
|
1543
1583
|
recordInvocation({
|
|
1544
1584
|
argv,
|
|
@@ -1548,6 +1588,7 @@ function journal(exitCode, error) {
|
|
|
1548
1588
|
endedAt: Date.now(),
|
|
1549
1589
|
exitCode,
|
|
1550
1590
|
error,
|
|
1591
|
+
reason: editorReason,
|
|
1551
1592
|
});
|
|
1552
1593
|
// The hint goes out AFTER the run it is about, so it reads as a footnote to
|
|
1553
1594
|
// what just happened rather than a warning about what is about to. Never on
|
|
@@ -1580,7 +1621,7 @@ function journal(exitCode, error) {
|
|
|
1580
1621
|
for (const l of updateNotice(cliVersion(), cached?.latest))
|
|
1581
1622
|
console.error(l);
|
|
1582
1623
|
}
|
|
1583
|
-
if (!isFresh(cached, Date.now()))
|
|
1624
|
+
if (!isFresh(cached, Date.now()) && !NO_NETWORK_VERBS.has(verbOf(argv)))
|
|
1584
1625
|
refreshUpdateCache();
|
|
1585
1626
|
}
|
|
1586
1627
|
catch {
|
|
@@ -1625,7 +1666,8 @@ async function announceBehind() {
|
|
|
1625
1666
|
console.error(l);
|
|
1626
1667
|
}
|
|
1627
1668
|
}
|
|
1628
|
-
if (needsRefresh(loadReleaseCache(), Date.now())
|
|
1669
|
+
if (needsRefresh(loadReleaseCache(), Date.now()) &&
|
|
1670
|
+
!NO_NETWORK_VERBS.has(verbOf(argv))) {
|
|
1629
1671
|
refreshReleaseCache({
|
|
1630
1672
|
apiUrl: machineApiUrl(await loadMachineConfig()),
|
|
1631
1673
|
now: () => Date.now(),
|
package/dist/cli/usage-text.js
CHANGED
|
@@ -60,7 +60,16 @@ export const USAGE_TEXT = `Vincentt CLI: the local-first AR dev loop\n\n` +
|
|
|
60
60
|
` [--no-qr] [--stop]\n` +
|
|
61
61
|
` Runs dev.command and forwards dev.port from vincentt.json,\n` +
|
|
62
62
|
` else npm run dev on a free port (passed as PORT).\n` +
|
|
63
|
-
` Re-running prints the live link; --restart mints a new one.\n
|
|
63
|
+
` Re-running prints the live link; --restart mints a new one.\n` +
|
|
64
|
+
` editor Hand this project to the Vincentt Editor app (macOS 14 or\n` +
|
|
65
|
+
` later) and exit. Opens only the app in /Applications or\n` +
|
|
66
|
+
` ~/Applications, signed by Vincentt. No account needed.\n` +
|
|
67
|
+
` [--json] one vincentt.editor-open/1 object; handedOff says\n` +
|
|
68
|
+
` the folder reached the app, not that it opened.\n` +
|
|
69
|
+
` editor --selection The object selected in the editor, with its source\n` +
|
|
70
|
+
` location and values [--json]\n` +
|
|
71
|
+
` editor --status --json The editor's state for this folder, including\n` +
|
|
72
|
+
` app.lastOpen. Both reads exit 1 when no editor runs here.\n\n` +
|
|
64
73
|
// f16. HELP TEACHES THE CANONICAL VERBS ONLY: `add` and `remove` are accepted
|
|
65
74
|
// as silent aliases and are deliberately ABSENT here, because help, docs and
|
|
66
75
|
// every error string are where the vocabulary is actually taught. Listing the
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const PINNED_VERBS: readonly ["version", "login", "logout", "whoami", "init", "create", "link", "unlink", "publish", "preview", "logs", "network", "trace", "feedback", "relay", "assets", "dx", "projects", "report", "outdated", "domain"];
|
|
1
|
+
export declare const PINNED_VERBS: readonly ["version", "login", "logout", "whoami", "init", "create", "link", "unlink", "publish", "preview", "logs", "network", "trace", "feedback", "relay", "assets", "dx", "projects", "report", "outdated", "domain", "editor"];
|
|
2
2
|
export type PinnedVerb = (typeof PINNED_VERBS)[number];
|
|
3
3
|
export declare const VERB_HANDLERS: {
|
|
4
4
|
readonly version: true;
|
|
@@ -22,6 +22,7 @@ export declare const VERB_HANDLERS: {
|
|
|
22
22
|
readonly report: true;
|
|
23
23
|
readonly outdated: true;
|
|
24
24
|
readonly domain: true;
|
|
25
|
+
readonly editor: true;
|
|
25
26
|
};
|
|
26
27
|
export type DispatchVerb = keyof typeof VERB_HANDLERS;
|
|
27
28
|
export declare function resolveVerb(cmd: string | undefined): DispatchVerb | undefined;
|
package/dist/cli/verb-surface.js
CHANGED
|
@@ -26,6 +26,10 @@ export const PINNED_VERBS = [
|
|
|
26
26
|
// collection verb. `domains` is routed to an answer, not accepted as a verb —
|
|
27
27
|
// see domainVerbRedirect. An addition, so B-F3-8 permits it.
|
|
28
28
|
"domain",
|
|
29
|
+
// Appended (f21): the Vincentt Editor launcher. `editor`, not `edit`: an agent
|
|
30
|
+
// reading "vincentt edit" would take it as a command that changes files. An
|
|
31
|
+
// addition, so B-F3-8 permits it.
|
|
32
|
+
"editor",
|
|
29
33
|
];
|
|
30
34
|
// The dispatch's verb set, as data. `main()` switches over this, so the two can
|
|
31
35
|
// never disagree: a verb added to the CLI is a key added here, and QA-AHG-01
|
|
@@ -52,6 +56,7 @@ export const VERB_HANDLERS = {
|
|
|
52
56
|
report: true,
|
|
53
57
|
outdated: true,
|
|
54
58
|
domain: true,
|
|
59
|
+
editor: true,
|
|
55
60
|
};
|
|
56
61
|
// Answers "is this a verb this CLI accepts" without running it. The
|
|
57
62
|
// console-redirect strings are NOT accepted here: they reach their explanation
|
package/dist/dx/command.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { ConsentDecision } from "./consent.js";
|
|
2
|
-
import type { Enablement } from "./enablement.js";
|
|
1
|
+
import type { ConsentDecision, GrantResult } from "./consent.js";
|
|
2
|
+
import type { AccountRead, ConsentView, Enablement } from "./enablement.js";
|
|
3
3
|
import type { JournalEntry, JournalWindow } from "./journal.js";
|
|
4
4
|
import type { WireReport } from "./send.js";
|
|
5
5
|
import { type BuiltWith } from "../platform/resolve.js";
|
|
@@ -11,10 +11,15 @@ export interface DxDeps {
|
|
|
11
11
|
enabled: () => boolean;
|
|
12
12
|
path: () => string;
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
15
|
-
* the journal need not resolve it; absent reads as not enabled.
|
|
14
|
+
* This machine's copy of the grant (the local flag). Optional so the many callers that
|
|
15
|
+
* only read the journal need not resolve it; absent reads as not enabled.
|
|
16
16
|
*/
|
|
17
17
|
enablement?: () => Enablement;
|
|
18
|
+
/**
|
|
19
|
+
* The account-first answer the report tail and `dxConsent` show. Absent falls back to
|
|
20
|
+
* the local flag, reported as checked on this machine only.
|
|
21
|
+
*/
|
|
22
|
+
consentView?: () => ConsentView;
|
|
18
23
|
}
|
|
19
24
|
/** How far back a report looks. */
|
|
20
25
|
export interface DxWindow {
|
|
@@ -53,7 +58,7 @@ export declare function sinceUsageLines(raw: string): string[];
|
|
|
53
58
|
export declare function parseDxOptions(args: string[]): DxOptions;
|
|
54
59
|
export declare function dxCommand(deps: DxDeps, options: DxOptions): number;
|
|
55
60
|
/** Wire the command to the real journal. */
|
|
56
|
-
export declare function runDx(args: string[], print: (line: string) => void, enablement?: () => Enablement): number;
|
|
61
|
+
export declare function runDx(args: string[], print: (line: string) => void, enablement?: () => Enablement, consentView?: () => ConsentView): number;
|
|
57
62
|
export interface DxEnableDeps {
|
|
58
63
|
print: (line: string) => void;
|
|
59
64
|
now: () => number;
|
|
@@ -89,6 +94,14 @@ export interface DxEnableDeps {
|
|
|
89
94
|
* no socket, no browser, and no clock to test.
|
|
90
95
|
*/
|
|
91
96
|
consentInBrowser?: () => Promise<ConsentDecision>;
|
|
97
|
+
/**
|
|
98
|
+
* POST /me/dx-consent with the PAT, on the terminal path. Rejects only with the version
|
|
99
|
+
* refusal or the sign-in error; every other outcome is a GrantResult.
|
|
100
|
+
*/
|
|
101
|
+
grantAccountConsent?: (auth: {
|
|
102
|
+
apiUrl: string;
|
|
103
|
+
pat: string;
|
|
104
|
+
}) => Promise<GrantResult>;
|
|
92
105
|
/**
|
|
93
106
|
* DELETE /me/dx-consent. Used only by `disable`, and allowed to throw — the caller
|
|
94
107
|
* treats a network failure as "local revoke still stands", never as a reason to leave
|
|
@@ -103,11 +116,13 @@ export interface DxEnableDeps {
|
|
|
103
116
|
* `vincentt dx enable` — grant durable permission to send DX reports without asking
|
|
104
117
|
* first.
|
|
105
118
|
*
|
|
106
|
-
* TWO PATHS, and which one runs is decided by whether there is a
|
|
119
|
+
* TWO PATHS to ONE account grant, and which one runs is decided by whether there is a
|
|
120
|
+
* terminal to ask in:
|
|
107
121
|
*
|
|
108
|
-
* - At a TTY: the
|
|
109
|
-
*
|
|
110
|
-
*
|
|
122
|
+
* - At a TTY: the terminal prompt. A person is already here and can read the
|
|
123
|
+
* disclosure where they stand; sending them to a browser would be ceremony. On `y`
|
|
124
|
+
* the CLI POSTs /me/dx-consent with the PAT, and writes the local flag only after
|
|
125
|
+
* the account recorded the grant.
|
|
111
126
|
* - With no TTY: the BROWSER path. This is the case the feature exists for — a
|
|
112
127
|
* creator driving the CLI through a coding agent has no terminal, so the old code
|
|
113
128
|
* refused and the flow dead-ended. It now opens the console, where a human can
|
|
@@ -130,8 +145,8 @@ export declare function dxEnableCommand(deps: DxEnableDeps, sunset: number): Pro
|
|
|
130
145
|
* Clears BOTH local keys AND revokes the account grant when there is a PAT to do it
|
|
131
146
|
* with. Both halves are needed: a creator who runs `disable` means "stop sending", and
|
|
132
147
|
* clearing only the local flag would leave the account grant standing, so the very next
|
|
133
|
-
* `--send` would be authorized by
|
|
134
|
-
* worked.
|
|
148
|
+
* `--send` would be authorized by the account and the command would appear not to
|
|
149
|
+
* have worked.
|
|
135
150
|
*
|
|
136
151
|
* ORDER IS DELIBERATE: local first, network second. The local clear is the one that
|
|
137
152
|
* cannot fail and the one that stops THIS machine, which is the machine the creator is
|
|
@@ -168,18 +183,19 @@ export interface DxSendDeps extends DxDeps {
|
|
|
168
183
|
* convenience, not authorization.
|
|
169
184
|
*/
|
|
170
185
|
interactive: () => boolean;
|
|
171
|
-
/**
|
|
186
|
+
/** This machine's flag, read strictly. Absent reads as not enabled. */
|
|
172
187
|
enablement?: () => Enablement;
|
|
173
188
|
/**
|
|
174
|
-
* The ACCOUNT's consent, read from GET /me.
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
* not wired at all) reads as not consented, never as "unknown, so allow".
|
|
189
|
+
* The ACCOUNT's consent, read from GET /me. Allowed to throw; a throw reads as
|
|
190
|
+
* unreadable. Absent reads as unreadable too: it can only let a valid local flag stand,
|
|
191
|
+
* never authorize on its own.
|
|
178
192
|
*/
|
|
179
193
|
accountConsent?: (auth: {
|
|
180
194
|
apiUrl: string;
|
|
181
195
|
pat: string;
|
|
182
|
-
}) => Promise<
|
|
196
|
+
}) => Promise<AccountRead>;
|
|
197
|
+
/** Clears this machine's flag when the account answers "not granted". */
|
|
198
|
+
clearEnablement?: () => Promise<unknown>;
|
|
183
199
|
/**
|
|
184
200
|
* The version set. Injectable so a fixed set — or a REJECTING promise, the arm
|
|
185
201
|
* proving a fault omits — can be driven with no filesystem. Defaults to the one
|
|
@@ -188,6 +204,46 @@ export interface DxSendDeps extends DxDeps {
|
|
|
188
204
|
builtWith?: () => Promise<BuiltWith | undefined>;
|
|
189
205
|
cwd?: string;
|
|
190
206
|
}
|
|
207
|
+
/** What the send gate needs. `dx --send` and `report` both satisfy it. */
|
|
208
|
+
export interface SendGateDeps {
|
|
209
|
+
interactive: () => boolean;
|
|
210
|
+
enablement?: () => Enablement;
|
|
211
|
+
accountConsent?: (auth: {
|
|
212
|
+
apiUrl: string;
|
|
213
|
+
pat: string;
|
|
214
|
+
}) => Promise<AccountRead>;
|
|
215
|
+
clearEnablement?: () => Promise<unknown>;
|
|
216
|
+
}
|
|
217
|
+
export type SendGate = {
|
|
218
|
+
arm: "interactive";
|
|
219
|
+
} | {
|
|
220
|
+
arm: "unattended";
|
|
221
|
+
enablement: Enablement;
|
|
222
|
+
} | {
|
|
223
|
+
arm: "refused";
|
|
224
|
+
};
|
|
225
|
+
/**
|
|
226
|
+
* The one consent gate both send paths call (f7 Scenario 3), checked in order:
|
|
227
|
+
*
|
|
228
|
+
* 1. an interactive session → ask, then send. No /me read at all, so a creator at their
|
|
229
|
+
* own terminal is never blocked by an API being slow or down.
|
|
230
|
+
* 2. signed in and GET /me answers: a strict grant sends (source: account); any other
|
|
231
|
+
* 200 clears this machine's flag and refuses.
|
|
232
|
+
* 3. /me cannot be read and this machine's flag is valid → send (source: machine).
|
|
233
|
+
*
|
|
234
|
+
* An unreadable /me never authorizes by itself; it only lets an existing local flag stand.
|
|
235
|
+
*/
|
|
236
|
+
export declare function gateSend(deps: SendGateDeps, auth: {
|
|
237
|
+
apiUrl: string;
|
|
238
|
+
pat: string;
|
|
239
|
+
} | undefined): Promise<SendGate>;
|
|
240
|
+
/**
|
|
241
|
+
* The gate's refusal. It NAMES the enabling command: a refusal is an answer to a
|
|
242
|
+
* command the caller typed, which makes it SOLICITED output — the prohibition is on
|
|
243
|
+
* UNBIDDEN output instructing a caller. A refusal naming nothing leaves a human at
|
|
244
|
+
* exit 1 with no path forward, for no security gain.
|
|
245
|
+
*/
|
|
246
|
+
export declare const SEND_REFUSAL_LINES: readonly string[];
|
|
191
247
|
/**
|
|
192
248
|
* `vincentt dx --send`.
|
|
193
249
|
*
|
|
@@ -195,6 +251,6 @@ export interface DxSendDeps extends DxDeps {
|
|
|
195
251
|
* who cannot see what leaves their machine before it leaves has not agreed to
|
|
196
252
|
* anything:
|
|
197
253
|
*
|
|
198
|
-
* friction →
|
|
254
|
+
* friction → auth → GATE → RESOLVE → build → SHOW → ask → post
|
|
199
255
|
*/
|
|
200
256
|
export declare function dxSendCommand(deps: DxSendDeps, options: DxOptions): Promise<number>;
|