@vincentt-xr/cli 0.7.0 → 0.9.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/dist/cli/args.d.ts +18 -1
- package/dist/cli/args.js +40 -1
- package/dist/cli/index.js +208 -10
- package/dist/dx/command.d.ts +59 -2
- package/dist/dx/command.js +150 -11
- package/dist/dx/enablement.d.ts +51 -0
- package/dist/dx/enablement.js +97 -0
- package/dist/dx/qa.f7.fixtures.d.ts +73 -0
- package/dist/dx/qa.f7.fixtures.js +58 -0
- package/dist/dx/skeleton.js +1 -0
- package/dist/lib/initCommand.d.ts +28 -1
- package/dist/lib/initCommand.js +91 -16
- package/dist/lib/initFrom.d.ts +48 -0
- package/dist/lib/initFrom.js +198 -0
- package/dist/lib/sourceRef.d.ts +46 -0
- package/dist/lib/sourceRef.js +107 -0
- package/dist/lib/sourceRefCopy.d.ts +11 -0
- package/dist/lib/sourceRefCopy.js +80 -0
- package/dist/lib/version.d.ts +20 -1
- package/dist/lib/version.js +41 -5
- package/dist/library/assetsCommand.d.ts +32 -0
- package/dist/library/assetsCommand.js +264 -0
- package/dist/library/client.d.ts +42 -0
- package/dist/library/client.js +137 -0
- package/dist/library/download.d.ts +46 -0
- package/dist/library/download.js +98 -0
- package/dist/library/parse.d.ts +43 -0
- package/dist/library/parse.js +109 -0
- package/dist/library/qa.f5-b32.spec.d.ts +1 -0
- package/dist/library/qa.f5-b32.spec.js +202 -0
- package/dist/library/qa.f5-cli.spec.d.ts +1 -0
- package/dist/library/qa.f5-cli.spec.js +433 -0
- package/dist/library/qa.f5-contract.spec.d.ts +1 -0
- package/dist/library/qa.f5-contract.spec.js +206 -0
- package/dist/library/render.d.ts +39 -0
- package/dist/library/render.js +305 -0
- package/dist/library/wire.d.ts +96 -0
- package/dist/library/wire.js +167 -0
- package/dist/publish/api.d.ts +20 -0
- package/dist/publish/command.d.ts +3 -0
- package/dist/publish/command.js +39 -4
- package/dist/publish/copy.d.ts +29 -0
- package/dist/publish/copy.js +86 -0
- package/dist/publish/prompt.js +17 -2
- package/dist/scaffold/index.d.ts +9 -0
- package/dist/scaffold/index.js +13 -1
- package/dist/shared/config.d.ts +27 -0
- package/dist/shared/config.js +64 -9
- package/package.json +1 -1
package/dist/cli/args.d.ts
CHANGED
|
@@ -13,8 +13,25 @@ export declare function isHelpInvocation(args: string[]): boolean;
|
|
|
13
13
|
* skip a publish the creator asked for. Anywhere else it stays an unknown flag.
|
|
14
14
|
*/
|
|
15
15
|
export declare function isVersionInvocation(args: string[]): boolean;
|
|
16
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Flags that stand alone; every other `--flag` consumes the next argument.
|
|
18
|
+
*
|
|
19
|
+
* A boolean flag missing from this set silently eats the argument after it. For
|
|
20
|
+
* `vincentt assets add --download lib_7f3a…` that would swallow the asset id and
|
|
21
|
+
* leave the command with nothing to resolve, so every new boolean belongs here at
|
|
22
|
+
* the moment it is introduced.
|
|
23
|
+
*/
|
|
17
24
|
export declare const BOOLEAN_FLAGS: Set<string>;
|
|
25
|
+
export interface ArgConflict {
|
|
26
|
+
/** The two flags that cannot appear together, in the order the copy names them. */
|
|
27
|
+
flags: [string, string];
|
|
28
|
+
kind: "source" | "install";
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The `--from` exclusivity rules, checked during argument parsing before any
|
|
32
|
+
* work begins. Returns the conflict to report, or undefined.
|
|
33
|
+
*/
|
|
34
|
+
export declare function fromFlagConflict(args: string[]): ArgConflict | undefined;
|
|
18
35
|
export declare function refusedFlag(args: string[]): string | undefined;
|
|
19
36
|
/**
|
|
20
37
|
* The bare arguments, with flag VALUES removed. Without this, `vincentt link --dir
|
package/dist/cli/args.js
CHANGED
|
@@ -17,7 +17,14 @@ export function isHelpInvocation(args) {
|
|
|
17
17
|
export function isVersionInvocation(args) {
|
|
18
18
|
return args[0] === "--version" || args[0] === "-v";
|
|
19
19
|
}
|
|
20
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Flags that stand alone; every other `--flag` consumes the next argument.
|
|
22
|
+
*
|
|
23
|
+
* A boolean flag missing from this set silently eats the argument after it. For
|
|
24
|
+
* `vincentt assets add --download lib_7f3a…` that would swallow the asset id and
|
|
25
|
+
* leave the command with nothing to resolve, so every new boolean belongs here at
|
|
26
|
+
* the moment it is introduced.
|
|
27
|
+
*/
|
|
21
28
|
export const BOOLEAN_FLAGS = new Set([
|
|
22
29
|
"--force",
|
|
23
30
|
"--preview",
|
|
@@ -33,9 +40,41 @@ export const BOOLEAN_FLAGS = new Set([
|
|
|
33
40
|
"--no-install",
|
|
34
41
|
"--allow-custom-api",
|
|
35
42
|
"--yes",
|
|
43
|
+
"--download",
|
|
36
44
|
"--help",
|
|
37
45
|
"-h",
|
|
38
46
|
]);
|
|
47
|
+
/**
|
|
48
|
+
* Flags that would turn the dependency install back ON. `--from` is mutually
|
|
49
|
+
* exclusive with every one of them, and the refusal lives HERE rather than in a
|
|
50
|
+
* code path (Q-1).
|
|
51
|
+
*
|
|
52
|
+
* Install is opt-out (`if (!opts.skipInstall)`), so the natural implementation of
|
|
53
|
+
* `--from` INHERITS the install — a developer has to actively write code to
|
|
54
|
+
* prevent it, which is the wrong direction for a security control. Making the
|
|
55
|
+
* refusal structural means the rule cannot be lost by forgetting something, only
|
|
56
|
+
* by deleting an explicit refusal, which is a reviewable act. S-2's accepted
|
|
57
|
+
* moved-tag risk is conditional on S-1 holding, so a single flag must not be able
|
|
58
|
+
* to defeat it.
|
|
59
|
+
*/
|
|
60
|
+
const INSTALL_ENABLING_FLAGS = new Set(["--install", "--with-install", "--deps"]);
|
|
61
|
+
/**
|
|
62
|
+
* The `--from` exclusivity rules, checked during argument parsing before any
|
|
63
|
+
* work begins. Returns the conflict to report, or undefined.
|
|
64
|
+
*/
|
|
65
|
+
export function fromFlagConflict(args) {
|
|
66
|
+
const names = new Set(args.filter((a) => a.startsWith("-")).map((a) => a.split("=")[0]));
|
|
67
|
+
if (!names.has("--from"))
|
|
68
|
+
return undefined;
|
|
69
|
+
// Both choose which source to scaffold from; honoring both is undefined.
|
|
70
|
+
if (names.has("--template"))
|
|
71
|
+
return { flags: ["--from", "--template"], kind: "source" };
|
|
72
|
+
for (const f of names) {
|
|
73
|
+
if (INSTALL_ENABLING_FLAGS.has(f))
|
|
74
|
+
return { flags: ["--from", f], kind: "install" };
|
|
75
|
+
}
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
39
78
|
/**
|
|
40
79
|
* Flags the CLI deliberately does NOT have. The checklist reports and never
|
|
41
80
|
* writes: a suggested value is one keystroke from a tool that writes it, and
|
package/dist/cli/index.js
CHANGED
|
@@ -18,7 +18,7 @@ import { runPkceLogin } from "../login/pkceLogin.js";
|
|
|
18
18
|
import { httpAuthBackend } from "../login/authBackend.js";
|
|
19
19
|
import { loginCommand } from "../login/loginCommand.js";
|
|
20
20
|
import { ensureLoggedIn } from "../login/ensure.js";
|
|
21
|
-
import { clearMachineToken, loadMachineConfig, locateProjectBinding, resolveConfig, MACHINE_CONFIG_PATH, } from "../shared/config.js";
|
|
21
|
+
import { clearMachineToken, loadMachineConfig, locateProjectBinding, resolveConfig, writeDxEnablement, MACHINE_CONFIG_PATH, } from "../shared/config.js";
|
|
22
22
|
import { getMe, ProjectUnreachableError } from "../lib/backend.js";
|
|
23
23
|
import { initCommand } from "../lib/initCommand.js";
|
|
24
24
|
import { linkCommand, printUnreachable, unlinkCommand } from "../lib/projects.js";
|
|
@@ -36,12 +36,18 @@ import { renderTerminalQr, shouldRenderQr } from "../preview/qr.js";
|
|
|
36
36
|
import { PreviewConflictError, PreviewRateLimitedError } from "../preview/api.js";
|
|
37
37
|
import { fetchNext, readCursor, writeCursor } from "../lib/feedback.js";
|
|
38
38
|
import { record as recordInvocation } from "../dx/journal.js";
|
|
39
|
-
import { dxSendCommand, parseDxOptions, runDx } from "../dx/command.js";
|
|
39
|
+
import { dxDisableCommand, dxEnableCommand, dxSendCommand, parseDxOptions, runDx, } from "../dx/command.js";
|
|
40
|
+
import { DX_UNATTENDED_SUNSET, loadEnablement } from "../dx/enablement.js";
|
|
40
41
|
import { journalEnabled, journalPath, readJournal } from "../dx/journal.js";
|
|
41
42
|
import { postReport } from "../dx/send.js";
|
|
42
43
|
import { nudgeFor } from "../dx/nudge.js";
|
|
43
44
|
import { verbOf } from "../dx/skeleton.js";
|
|
44
|
-
import { isHelpInvocation, isVersionInvocation, positionalArgs, refusedFlag, } from "./args.js";
|
|
45
|
+
import { fromFlagConflict, isHelpInvocation, isVersionInvocation, positionalArgs, refusedFlag, } from "./args.js";
|
|
46
|
+
import { parseSourceRef } from "../lib/sourceRef.js";
|
|
47
|
+
import { printFlagConflict, printSourceRefFailure } from "../lib/sourceRefCopy.js";
|
|
48
|
+
import { registerAfterScaffold } from "../lib/initFrom.js";
|
|
49
|
+
import { createCommand } from "../lib/projects.js";
|
|
50
|
+
import { resolveOrgForCommand } from "../lib/lifecycle.js";
|
|
45
51
|
// The console origin serves /cli-auth (the browser target for `vincentt login`). VINCENTT_
|
|
46
52
|
// CONSOLE_URL overrides for dev/dogfood; VINCENTT_EDITOR_URL is honored as a legacy alias.
|
|
47
53
|
// This is the prod console; vincentt.app is the published-PROJECT apex, not the console, so a
|
|
@@ -163,17 +169,75 @@ async function init() {
|
|
|
163
169
|
// ABSENCE as "may not ask" — so a piped or CI invocation refuses rather than
|
|
164
170
|
// blocking forever on a question nobody can see.
|
|
165
171
|
const interactive = Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY);
|
|
172
|
+
const print = (l) => console.log(l);
|
|
173
|
+
const dir = positional(1);
|
|
174
|
+
const dirLabel = dir ?? "<dir>";
|
|
175
|
+
// Refused during ARGUMENT PARSING, before anything is written (Q-1).
|
|
176
|
+
const conflict = fromFlagConflict(argv);
|
|
177
|
+
if (conflict) {
|
|
178
|
+
printFlagConflict(print, conflict, { dir: dirLabel });
|
|
179
|
+
process.exitCode = 1;
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const rawFrom = flag("--from");
|
|
183
|
+
let from;
|
|
184
|
+
if (rawFrom !== undefined) {
|
|
185
|
+
const parsed = parseSourceRef(rawFrom);
|
|
186
|
+
if (!parsed.ok) {
|
|
187
|
+
printSourceRefFailure(print, parsed.failure, { dir: dirLabel });
|
|
188
|
+
process.exitCode = 1;
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
from = parsed.ref;
|
|
192
|
+
}
|
|
166
193
|
process.exitCode = await initCommand({
|
|
167
|
-
print
|
|
194
|
+
print,
|
|
168
195
|
cwd: process.cwd(),
|
|
169
196
|
ask: interactive ? stdinAsk() : undefined,
|
|
170
197
|
confirm: interactive ? stdinConfirm() : undefined,
|
|
198
|
+
register: from
|
|
199
|
+
? (rd, o) => registerAfterScaffold({
|
|
200
|
+
...rd,
|
|
201
|
+
currentToken,
|
|
202
|
+
runLogin: async () => {
|
|
203
|
+
await login();
|
|
204
|
+
},
|
|
205
|
+
resolveOrg: async () => {
|
|
206
|
+
const cfg = await resolveConfig(rd.targetDir, { warn: print });
|
|
207
|
+
const resolved = await resolveOrgForCommand(cfg, { flag: flag("--org") });
|
|
208
|
+
if (!resolved.ok)
|
|
209
|
+
return resolved;
|
|
210
|
+
return {
|
|
211
|
+
ok: true,
|
|
212
|
+
name: resolved.org.name,
|
|
213
|
+
source: orgSource(),
|
|
214
|
+
};
|
|
215
|
+
},
|
|
216
|
+
create: async ({ name, slug }) => createCommand({
|
|
217
|
+
print,
|
|
218
|
+
cwd: rd.targetDir,
|
|
219
|
+
config: () => resolveConfig(rd.targetDir, { warn: print }),
|
|
220
|
+
org: async () => resolveOrgForCommand(await resolveConfig(rd.targetDir, { warn: print }), { flag: flag("--org") }),
|
|
221
|
+
}, { name, slug }),
|
|
222
|
+
}, o)
|
|
223
|
+
: undefined,
|
|
171
224
|
}, {
|
|
172
|
-
dir
|
|
225
|
+
dir,
|
|
173
226
|
skipInstall: has("--no-install"),
|
|
174
227
|
template: flag("--template"),
|
|
228
|
+
from,
|
|
229
|
+
name: flag("--name"),
|
|
230
|
+
slug: flag("--slug"),
|
|
175
231
|
});
|
|
176
232
|
}
|
|
233
|
+
/** Where the resolved org came from, so a surprising value is diagnosable (AF-3). */
|
|
234
|
+
function orgSource() {
|
|
235
|
+
if (flag("--org"))
|
|
236
|
+
return "flag";
|
|
237
|
+
if (process.env.VINCENTT_ORG)
|
|
238
|
+
return "env";
|
|
239
|
+
return "active";
|
|
240
|
+
}
|
|
177
241
|
async function create() {
|
|
178
242
|
await requireLogin();
|
|
179
243
|
const result = await projectCreate(projectDir(), {
|
|
@@ -253,6 +317,8 @@ async function publish() {
|
|
|
253
317
|
now: () => Date.now(),
|
|
254
318
|
sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
|
|
255
319
|
orgName: activeOrgName,
|
|
320
|
+
orgSlug: async () => (await activeOrgName()) || undefined,
|
|
321
|
+
consoleUrl: flag("--console") ?? process.env.VINCENTT_CONSOLE_URL,
|
|
256
322
|
}, { distDir: flag("--dist"), note: flag("--note"), yes: has("--yes") });
|
|
257
323
|
}
|
|
258
324
|
/** Flow L30 — the six verbs that are console actions, answered rather than rejected. */
|
|
@@ -583,8 +649,35 @@ async function version() {
|
|
|
583
649
|
// ---- dx ---------------------------------------------------------------------
|
|
584
650
|
async function dx() {
|
|
585
651
|
const options = parseDxOptions(argv);
|
|
652
|
+
const now = Date.now();
|
|
653
|
+
// Both descriptors, matching publish and init. stdin alone let
|
|
654
|
+
// `vincentt dx --send --yes > out.txt` through — a redirected stdout is
|
|
655
|
+
// precisely the case where nobody reads what was disclosed.
|
|
656
|
+
const interactive = () => Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
657
|
+
if (options.enable || options.disable) {
|
|
658
|
+
const deps = {
|
|
659
|
+
print: (l) => console.log(l),
|
|
660
|
+
now: () => now,
|
|
661
|
+
interactive,
|
|
662
|
+
confirm: (q) => stdinConfirm()(` ${q} [y/N] `, false),
|
|
663
|
+
// `enable` does NOT require being signed in. --send refuses without a PAT
|
|
664
|
+
// regardless, so a config holding only enablement keys is inert; and
|
|
665
|
+
// ordering the two verbs ("you must log in before you may state a
|
|
666
|
+
// preference") would be a rule with no safety behind it.
|
|
667
|
+
save: async (next) => {
|
|
668
|
+
await writeDxEnablement(next);
|
|
669
|
+
},
|
|
670
|
+
};
|
|
671
|
+
process.exitCode = options.enable
|
|
672
|
+
? await dxEnableCommand(deps, DX_UNATTENDED_SUNSET)
|
|
673
|
+
: await dxDisableCommand(deps);
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
// Read strictly and independently of auth: a truncated config must disable the
|
|
677
|
+
// relaxation, not throw through it.
|
|
678
|
+
const enablement = await loadEnablement(now);
|
|
586
679
|
if (!options.send) {
|
|
587
|
-
process.exitCode = runDx(argv, (l) => console.log(l));
|
|
680
|
+
process.exitCode = runDx(argv, (l) => console.log(l), () => enablement);
|
|
588
681
|
return;
|
|
589
682
|
}
|
|
590
683
|
// `--send` is the one path out of this machine, so it resolves auth the way
|
|
@@ -592,7 +685,7 @@ async function dx() {
|
|
|
592
685
|
process.exitCode = await dxSendCommand({
|
|
593
686
|
entries: readJournal,
|
|
594
687
|
print: (l) => console.log(l),
|
|
595
|
-
now: () =>
|
|
688
|
+
now: () => now,
|
|
596
689
|
enabled: () => journalEnabled(),
|
|
597
690
|
path: journalPath,
|
|
598
691
|
cliVersion,
|
|
@@ -613,9 +706,91 @@ async function dx() {
|
|
|
613
706
|
},
|
|
614
707
|
post: (apiUrl, pat, report) => postReport(apiUrl, pat, report),
|
|
615
708
|
confirm: (q) => stdinConfirm()(`${q} [y/N] `, false),
|
|
616
|
-
interactive
|
|
709
|
+
interactive,
|
|
710
|
+
enablement: () => enablement,
|
|
617
711
|
}, options);
|
|
618
712
|
}
|
|
713
|
+
// ---- assets (f5) ------------------------------------------------------------
|
|
714
|
+
/**
|
|
715
|
+
* `vincentt assets <search|show|add>` — the CLI's FIRST nested subcommand group,
|
|
716
|
+
* so this is one `case` with an inner switch rather than a new flat verb.
|
|
717
|
+
*
|
|
718
|
+
* The whole group is gated on being signed in, and every screen it prints goes to
|
|
719
|
+
* STDERR: under `--json` stdout must carry only the JSON document, so an agent
|
|
720
|
+
* piping stdout into a parser never gets a mixed stream.
|
|
721
|
+
*/
|
|
722
|
+
async function assets() {
|
|
723
|
+
const sub = positional(1);
|
|
724
|
+
if (sub === undefined) {
|
|
725
|
+
console.error("Usage: vincentt assets <search|show|add> [options]");
|
|
726
|
+
process.exitCode = 1;
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
if (sub !== "search" && sub !== "show" && sub !== "add") {
|
|
730
|
+
console.error(`Unknown assets command: ${sub}`);
|
|
731
|
+
console.error("Usage: vincentt assets <search|show|add> [options]");
|
|
732
|
+
process.exitCode = 1;
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
const { httpLibraryClient } = await import("../library/client.js");
|
|
736
|
+
const { assetsAdd, assetsSearch, assetsShow } = await import("../library/assetsCommand.js");
|
|
737
|
+
const cwd = projectDir();
|
|
738
|
+
let cfg;
|
|
739
|
+
try {
|
|
740
|
+
cfg = await resolveConfig(cwd, {
|
|
741
|
+
allowCustomApi: has("--allow-custom-api"),
|
|
742
|
+
warn: (l) => console.error(l),
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
catch {
|
|
746
|
+
// Not signed in — the exit-4 screen, because we did not answer. The library is
|
|
747
|
+
// a platform call, and the creator's PROJECT is unaffected either way.
|
|
748
|
+
const { unreachableLines } = await import("../library/render.js");
|
|
749
|
+
for (const l of unreachableLines("auth", "vincentt assets search \"<what you want>\"")) {
|
|
750
|
+
console.error(l);
|
|
751
|
+
}
|
|
752
|
+
process.exitCode = 4;
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
const deps = {
|
|
756
|
+
out: (l) => console.log(l),
|
|
757
|
+
err: (l) => console.error(l),
|
|
758
|
+
client: httpLibraryClient(cfg),
|
|
759
|
+
cwd,
|
|
760
|
+
};
|
|
761
|
+
const json = has("--json");
|
|
762
|
+
if (sub === "search") {
|
|
763
|
+
// The query is the bare argument after `search`; the remaining positionals are
|
|
764
|
+
// ignored rather than concatenated, so a quoting mistake fails loudly.
|
|
765
|
+
const query = positional(2);
|
|
766
|
+
if (query === undefined) {
|
|
767
|
+
console.error('Usage: vincentt assets search "<query>" [--kind prop|frame] [--limit N]');
|
|
768
|
+
process.exitCode = 1;
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
process.exitCode = await assetsSearch(deps, {
|
|
772
|
+
query,
|
|
773
|
+
kind: flag("--kind"),
|
|
774
|
+
render: flag("--render"),
|
|
775
|
+
palette: flag("--palette"),
|
|
776
|
+
limit: flag("--limit"),
|
|
777
|
+
json,
|
|
778
|
+
});
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
const ids = positionalArgs(argv).slice(2);
|
|
782
|
+
if (sub === "show") {
|
|
783
|
+
process.exitCode = await assetsShow(deps, { ids, json });
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
process.exitCode = await assetsAdd(deps, {
|
|
787
|
+
ids,
|
|
788
|
+
json,
|
|
789
|
+
download: has("--download"),
|
|
790
|
+
force: has("--force"),
|
|
791
|
+
dir: flag("--dir"),
|
|
792
|
+
});
|
|
793
|
+
}
|
|
619
794
|
// ---- servers ----------------------------------------------------------------
|
|
620
795
|
async function relay() {
|
|
621
796
|
const { startRelay } = await import("../relay/server.js");
|
|
@@ -646,6 +821,8 @@ function usage() {
|
|
|
646
821
|
`Project\n` +
|
|
647
822
|
` init [dir] Scaffold a Vincentt XR app — no account needed\n` +
|
|
648
823
|
` [--no-install] [--template <version>]\n` +
|
|
824
|
+
` [--from <owner>/<repo>@<tag>] Clone a gallery template\n` +
|
|
825
|
+
` and register it [--name <s>] [--slug <s>]\n` +
|
|
649
826
|
` create Register a project and reserve its address\n` +
|
|
650
827
|
` [--name <s>] [--slug <s>] [--org <s>] [--preview] [--force]\n` +
|
|
651
828
|
` link <project-id> Link this folder to an existing project\n` +
|
|
@@ -663,6 +840,15 @@ function usage() {
|
|
|
663
840
|
` --tester t1 filters to one tester (one tab, across\n` +
|
|
664
841
|
` its refreshes). --viewer <label> filters to a single\n` +
|
|
665
842
|
` connection; one tab opens several.\n\n` +
|
|
843
|
+
`Asset library\n` +
|
|
844
|
+
` assets search "<query>" Find curated assets [--kind prop|frame]\n` +
|
|
845
|
+
` [--render 3d-clay] [--palette sage] [--limit 20] [--json]\n` +
|
|
846
|
+
` assets show <id> [<id>…] Everything known about an asset [--json]\n` +
|
|
847
|
+
` assets add <id> [<id>…] Print the URLs to reference from your source\n` +
|
|
848
|
+
` Writes nothing. [--download [--dir <path>] [--force]]\n` +
|
|
849
|
+
` [--json]\n` +
|
|
850
|
+
` Ids look like lib_7f3a2c67d4e5b6a7; search returns\n` +
|
|
851
|
+
` them. Reference only ids the tool returned.\n\n` +
|
|
666
852
|
`Reverse channel\n` +
|
|
667
853
|
` feedback --wait Block for the next phone annotation, print it, exit\n` +
|
|
668
854
|
` feedback --watch Stream annotations as they arrive\n\n` +
|
|
@@ -673,7 +859,12 @@ function usage() {
|
|
|
673
859
|
` dx Where this dev loop fought you [--days <n>] [--json]\n` +
|
|
674
860
|
` Reads a local journal of your own runs. Local by default.\n` +
|
|
675
861
|
` --send reports the findings to the Vincentt team; it\n` +
|
|
676
|
-
` shows the exact payload and asks first. [--note <text>]\n
|
|
862
|
+
` shows the exact payload and asks first. [--note <text>]\n` +
|
|
863
|
+
` dx enable Allow reports to be sent from this machine without asking.\n` +
|
|
864
|
+
` Asks you to confirm in your terminal. Stays on until you\n` +
|
|
865
|
+
` disable it, and is shown on \`vincentt dx\` and\n` +
|
|
866
|
+
` \`vincentt version\` while on.\n` +
|
|
867
|
+
` dx disable Turn that off again.\n\n` +
|
|
677
868
|
`Common options: --dir <path> (project dir, default cwd)\n` +
|
|
678
869
|
` --allow-custom-api honor a binding's apiUrl override\n`);
|
|
679
870
|
}
|
|
@@ -721,6 +912,8 @@ async function main() {
|
|
|
721
912
|
return feedback();
|
|
722
913
|
case "relay":
|
|
723
914
|
return relay();
|
|
915
|
+
case "assets":
|
|
916
|
+
return assets();
|
|
724
917
|
case "dx":
|
|
725
918
|
return dx();
|
|
726
919
|
default:
|
|
@@ -756,8 +949,13 @@ function journal(exitCode, error) {
|
|
|
756
949
|
if (verbOf(argv) === "dx")
|
|
757
950
|
return;
|
|
758
951
|
try {
|
|
952
|
+
// STDERR, not stdout. This is human narration appended after EVERY verb, so on
|
|
953
|
+
// any `--json` invocation it would land inside the document an agent is piping
|
|
954
|
+
// into a parser and corrupt it. Whether that happened was down to the nudge's
|
|
955
|
+
// own timing heuristic, which makes it the worst kind of intermittent: green in
|
|
956
|
+
// a test run, broken on a creator's machine a week later.
|
|
759
957
|
for (const l of nudgeFor(readJournal(), Date.now()))
|
|
760
|
-
console.
|
|
958
|
+
console.error(l);
|
|
761
959
|
}
|
|
762
960
|
catch {
|
|
763
961
|
// A hint that can break a publish is not worth having.
|
package/dist/dx/command.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Enablement } from "./enablement.js";
|
|
1
2
|
import type { JournalEntry } from "./journal.js";
|
|
2
3
|
import type { WireReport } from "./send.js";
|
|
3
4
|
export interface DxDeps {
|
|
@@ -6,6 +7,11 @@ export interface DxDeps {
|
|
|
6
7
|
now: () => number;
|
|
7
8
|
enabled: () => boolean;
|
|
8
9
|
path: () => string;
|
|
10
|
+
/**
|
|
11
|
+
* Durable unattended-send state. Optional so the many callers that only read
|
|
12
|
+
* the journal need not resolve it; absent reads as not enabled.
|
|
13
|
+
*/
|
|
14
|
+
enablement?: () => Enablement;
|
|
9
15
|
}
|
|
10
16
|
export interface DxOptions {
|
|
11
17
|
/** Only consider runs from the last N days. */
|
|
@@ -17,11 +23,55 @@ export interface DxOptions {
|
|
|
17
23
|
yes: boolean;
|
|
18
24
|
/** The creator's own words about the friction, carried with the report. */
|
|
19
25
|
note?: string;
|
|
26
|
+
/** `vincentt dx enable` — a positional subcommand, not a flag. */
|
|
27
|
+
enable: boolean;
|
|
28
|
+
/** `vincentt dx disable` — a positional subcommand, not a flag. */
|
|
29
|
+
disable: boolean;
|
|
20
30
|
}
|
|
21
31
|
export declare function parseDxOptions(args: string[]): DxOptions;
|
|
22
32
|
export declare function dxCommand(deps: DxDeps, options: DxOptions): number;
|
|
23
33
|
/** Wire the command to the real journal. */
|
|
24
|
-
export declare function runDx(args: string[], print: (line: string) => void): number;
|
|
34
|
+
export declare function runDx(args: string[], print: (line: string) => void, enablement?: () => Enablement): number;
|
|
35
|
+
export interface DxEnableDeps {
|
|
36
|
+
print: (line: string) => void;
|
|
37
|
+
now: () => number;
|
|
38
|
+
/** stdin.isTTY && stdout.isTTY. Evidence of an interactive session, not proof of a human. */
|
|
39
|
+
interactive: () => boolean;
|
|
40
|
+
/** Ask once. Returns false to abort. */
|
|
41
|
+
confirm: (question: string) => Promise<boolean>;
|
|
42
|
+
/**
|
|
43
|
+
* Persist the grant, or clear it when given undefined. NEVER called on a
|
|
44
|
+
* refusal path — `enable` that refuses must not touch the file at all.
|
|
45
|
+
*/
|
|
46
|
+
save: (next: {
|
|
47
|
+
at: string;
|
|
48
|
+
} | undefined) => Promise<void>;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* `vincentt dx enable` — grant this machine durable permission to send DX reports
|
|
52
|
+
* without asking first.
|
|
53
|
+
*
|
|
54
|
+
* The TTY requirement is a SPEED BUMP, NOT A BOUNDARY, and is written down as
|
|
55
|
+
* such so a later reader does not build on a guarantee that is not there. It
|
|
56
|
+
* stops the reflex — an agent that meets a prompt, infers there must be a way
|
|
57
|
+
* past it, and tries the obvious thing. It does not stop a caller that allocates
|
|
58
|
+
* a PTY or writes config.json directly.
|
|
59
|
+
*
|
|
60
|
+
* The refusal path writes NOTHING. Not "writes the same bytes back" — nothing at
|
|
61
|
+
* all: this file holds the PAT, and a refusal that round-trips the config through
|
|
62
|
+
* a write can lose the credential if it dies mid-write.
|
|
63
|
+
*/
|
|
64
|
+
export declare function dxEnableCommand(deps: DxEnableDeps, sunset: number): Promise<number>;
|
|
65
|
+
/**
|
|
66
|
+
* `vincentt dx disable` — the obvious opposite command.
|
|
67
|
+
*
|
|
68
|
+
* Clears BOTH keys. Leaving the timestamp behind would make a later re-enable
|
|
69
|
+
* report its age from the first grant, which is the kind of thing that ships
|
|
70
|
+
* wrong and is never noticed. Needs no terminal and no confirmation: turning a
|
|
71
|
+
* capability OFF is always safe to do, and requiring a TTY here would strand a
|
|
72
|
+
* creator who wanted to revoke from a script.
|
|
73
|
+
*/
|
|
74
|
+
export declare function dxDisableCommand(deps: DxEnableDeps): Promise<number>;
|
|
25
75
|
export interface DxSendDeps extends DxDeps {
|
|
26
76
|
/** Resolved API host + PAT. Absent when the creator is not signed in. */
|
|
27
77
|
auth: () => Promise<{
|
|
@@ -36,8 +86,15 @@ export interface DxSendDeps extends DxDeps {
|
|
|
36
86
|
/** Ask before sending. Returns false to abort. */
|
|
37
87
|
confirm: (question: string) => Promise<boolean>;
|
|
38
88
|
cliVersion: () => string;
|
|
39
|
-
/**
|
|
89
|
+
/**
|
|
90
|
+
* stdin.isTTY && stdout.isTTY. Evidence of an INTERACTIVE SESSION, not proof of
|
|
91
|
+
* a human: an agent harness that allocates a PTY passes this with nobody
|
|
92
|
+
* present. The enabled path below is the honest one; this arm gates
|
|
93
|
+
* convenience, not authorization.
|
|
94
|
+
*/
|
|
40
95
|
interactive: () => boolean;
|
|
96
|
+
/** Durable unattended-send state, read strictly. Absent reads as not enabled. */
|
|
97
|
+
enablement?: () => Enablement;
|
|
41
98
|
}
|
|
42
99
|
/**
|
|
43
100
|
* `vincentt dx --send`.
|