@vincentt-xr/cli 0.24.0 → 0.25.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 +11 -0
- package/README.md +2 -3
- package/dist/cli/index.js +15 -15
- package/dist/dx/consent.js +1 -1
- package/dist/lib/initCommand.js +10 -5
- package/dist/lib/initFrom.js +4 -4
- package/dist/lib/lifecycle.js +1 -0
- package/dist/lib/projects.js +4 -5
- package/dist/lib/relayInfo.d.ts +6 -0
- package/dist/login/pkceLogin.js +1 -1
- package/dist/preview/command.d.ts +2 -2
- package/dist/preview/command.js +1 -1
- package/dist/preview/copy.d.ts +26 -21
- package/dist/preview/copy.js +49 -40
- package/dist/relay/server.js +3 -1
- package/dist/shared/upgradeError.d.ts +0 -2
- package/dist/shared/upgradeError.js +2 -3
- package/package.json +1 -1
package/MIGRATION.md
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
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.25.0
|
|
7
|
+
|
|
8
|
+
required: false
|
|
9
|
+
No action required. `vincentt preview` leads with the console page and no creator-facing screen prints a localhost address.
|
|
10
|
+
- The start block prints `→ <project> · <workspace>` before the preview is created, then `✓ Preview is live`, then `Open the console:` with the project's console page, then `On a phone:` with the phone link and QR. The `→ Serving http://localhost:<port>` line is gone.
|
|
11
|
+
- Running `vincentt preview` while one is running in the folder prints the same two blocks: the console page, then the phone link.
|
|
12
|
+
- The not-signed-in screen ends with `Sign in: vincentt login` and `Then start it again: vincentt preview`. It no longer names the dev command or a local address. The preview's upgrade refusal likewise ends on the upgrade and `vincentt preview`.
|
|
13
|
+
- When the dev server is down, the advisory reads `! Your dev server isn't responding on port <n>.`
|
|
14
|
+
- `vincentt create` and `vincentt init` next steps no longer list the dev server; `vincentt preview` starts it. A non-interactive `create` that scaffolded without an account ends with `Signing in is what gives it an address a phone can open.`
|
|
15
|
+
- The browser page at the end of `vincentt login` reads `You're Signed In` and `Go back to your AI assistant to continue.` The problem-report consent page reads `Problem Reports Are On` or `Problem Reports Are Off` with the same line. Neither mentions the terminal.
|
|
16
|
+
|
|
6
17
|
## 0.24.0
|
|
7
18
|
|
|
8
19
|
required: false
|
package/README.md
CHANGED
|
@@ -108,9 +108,8 @@ later verbs run without prompting. `init` never reaches that gate at all.
|
|
|
108
108
|
|
|
109
109
|
## The loop, once a project exists
|
|
110
110
|
|
|
111
|
-
- `
|
|
112
|
-
|
|
113
|
-
URL (and a desktop QR page) to open on a phone. AR needs a secure context for
|
|
111
|
+
- `npx vincentt preview` — start the dev server and tunnel the app, then hand over
|
|
112
|
+
the project's console page, which carries the phone link and its QR. AR needs a secure context for
|
|
114
113
|
camera access, so this is how you test tracking and gestures on a device.
|
|
115
114
|
- `npx vincentt logs | network | trace` — read what the phone reported to the
|
|
116
115
|
preview relay. Debug on-device misbehavior instead of guessing.
|
package/dist/cli/index.js
CHANGED
|
@@ -39,7 +39,7 @@ import { USAGE_TEXT } from "./usage-text.js";
|
|
|
39
39
|
import { PreviewUi } from "../preview/command.js";
|
|
40
40
|
import { httpRelayClient } from "../lib/diagnostics.js";
|
|
41
41
|
import { readLivePreview, relayInfoDir } from "../lib/relayInfo.js";
|
|
42
|
-
import { attachedToRunning, mintRateLimited, noDiagnosticsStatic, notSignedIn, previewConflict, previewUserLimitRefused, settingsBanner, startBlock, } from "../preview/copy.js";
|
|
42
|
+
import { attachedToRunning, bindingLine, mintRateLimited, noDiagnosticsStatic, notSignedIn, previewConflict, previewUserLimitRefused, settingsBanner, startBlock, } from "../preview/copy.js";
|
|
43
43
|
import { chooseDevPort } from "../preview/devPort.js";
|
|
44
44
|
import { isPortListening } from "../preview/net.js";
|
|
45
45
|
import { readSettings, resolveSettings, SettingsError, TEMPLATE_DEFAULTS, validateField, } from "../lib/settings.js";
|
|
@@ -570,9 +570,6 @@ async function preview() {
|
|
|
570
570
|
}
|
|
571
571
|
const devCommand = settings.devCommand.value;
|
|
572
572
|
const { port: devPort, fresh: devPortFresh } = await chooseDevPort(settings);
|
|
573
|
-
// What the creator gets running the dev command by hand, without PORT.
|
|
574
|
-
const handRunUrl = `http://localhost:${settings.devPort.value}`;
|
|
575
|
-
const localUrl = `http://localhost:${devPort}`;
|
|
576
573
|
for (const l of settingsBanner({
|
|
577
574
|
devCommand,
|
|
578
575
|
devCommandSource: sourceWords(settings.devCommand),
|
|
@@ -589,7 +586,7 @@ async function preview() {
|
|
|
589
586
|
// The gate is LOCAL and NOTHING IS ATTEMPTED: no spinner, no partial mint, no
|
|
590
587
|
// session left in `pending`. The command stops before it touches the network.
|
|
591
588
|
if (!(await currentToken())) {
|
|
592
|
-
for (const l of notSignedIn(
|
|
589
|
+
for (const l of notSignedIn())
|
|
593
590
|
console.log(l);
|
|
594
591
|
process.exitCode = EXIT.NOT_AUTHENTICATED;
|
|
595
592
|
return;
|
|
@@ -600,7 +597,7 @@ async function preview() {
|
|
|
600
597
|
const ui = new PreviewUi({
|
|
601
598
|
print: (l) => console.log(l),
|
|
602
599
|
now: () => Date.now(),
|
|
603
|
-
|
|
600
|
+
devPort,
|
|
604
601
|
color,
|
|
605
602
|
filterTester,
|
|
606
603
|
filterViewer,
|
|
@@ -629,8 +626,10 @@ async function preview() {
|
|
|
629
626
|
// No filter means the caller wanted the preview, not a diagnostics stream:
|
|
630
627
|
// hand them the link and exit rather than blocking in the follow loop.
|
|
631
628
|
// Following a live preview is what `vincentt logs`/`network`/`trace` are.
|
|
632
|
-
|
|
629
|
+
const consoleUrl = await projectConsoleUrl(cwd, existing.sessionId);
|
|
630
|
+
for (const l of attachedToRunning(existing.url, consoleUrl, existing.expiresAt)) {
|
|
633
631
|
console.log(l);
|
|
632
|
+
}
|
|
634
633
|
return;
|
|
635
634
|
}
|
|
636
635
|
else {
|
|
@@ -642,11 +641,7 @@ async function preview() {
|
|
|
642
641
|
let endingRefusal;
|
|
643
642
|
let resolveDone;
|
|
644
643
|
const refusedAtStart = (facts) => {
|
|
645
|
-
for (const l of upgradeRefusalLines(facts, {
|
|
646
|
-
kind: "preview_start",
|
|
647
|
-
devCommand,
|
|
648
|
-
localUrl: handRunUrl,
|
|
649
|
-
})) {
|
|
644
|
+
for (const l of upgradeRefusalLines(facts, { kind: "preview_start" })) {
|
|
650
645
|
console.log(l);
|
|
651
646
|
}
|
|
652
647
|
process.exitCode = EXIT.UPGRADE_REQUIRED;
|
|
@@ -654,6 +649,14 @@ async function preview() {
|
|
|
654
649
|
const done = new Promise((r) => {
|
|
655
650
|
resolveDone = r;
|
|
656
651
|
});
|
|
652
|
+
// The binding line prints BEFORE the mint: a wrong binding is cheap to catch
|
|
653
|
+
// only while no session exists. An unlinked folder prints none — previewStart
|
|
654
|
+
// refuses it, and a guessed name would be a line about the wrong project.
|
|
655
|
+
if (await locateProjectBinding(cwd)) {
|
|
656
|
+
for (const l of bindingLine(await projectDisplayName(cwd), await activeOrgName())) {
|
|
657
|
+
console.log(l);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
657
660
|
let running;
|
|
658
661
|
try {
|
|
659
662
|
const started = await previewStart(cwd, (m) => console.error(`[preview] ${m}`), {
|
|
@@ -723,9 +726,6 @@ async function preview() {
|
|
|
723
726
|
? await renderTerminalQr(running.url).catch(() => undefined)
|
|
724
727
|
: undefined;
|
|
725
728
|
for (const l of startBlock({
|
|
726
|
-
projectName: await projectDisplayName(cwd),
|
|
727
|
-
orgName: await activeOrgName(),
|
|
728
|
-
targetUrl: `http://localhost:${running.appPort}`,
|
|
729
729
|
url: running.url,
|
|
730
730
|
consoleUrl: await projectConsoleUrl(cwd, running.sessionId),
|
|
731
731
|
feedbackOrigin: running.consoleOrigin !== undefined &&
|
package/dist/dx/consent.js
CHANGED
|
@@ -195,7 +195,7 @@ export async function runConsentInBrowser(opts) {
|
|
|
195
195
|
value: result.decision,
|
|
196
196
|
page: {
|
|
197
197
|
status: 200,
|
|
198
|
-
html: browserPage(granted, granted ? "Reports
|
|
198
|
+
html: browserPage(granted, granted ? "Problem Reports Are On" : "Problem Reports Are Off", "Go back to your AI assistant to continue."),
|
|
199
199
|
},
|
|
200
200
|
};
|
|
201
201
|
}
|
package/dist/lib/initCommand.js
CHANGED
|
@@ -134,11 +134,13 @@ export async function initCommand(deps, opts = {}) {
|
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
// The template ships a pnpm lockfile and a hoisted node-linker, so telling a
|
|
137
|
-
// creator to `npm
|
|
137
|
+
// creator to `npm install` in it produces a second lockfile and an unproven
|
|
138
138
|
// node_modules layout. Name the manager the scaffold is actually set up for.
|
|
139
139
|
const pm = await detectPackageManager(target).catch(() => "npm");
|
|
140
140
|
deps.print("");
|
|
141
141
|
deps.print(`✓ Created ${label}`);
|
|
142
|
+
// No dev-server step: `vincentt preview` starts it, and the product never
|
|
143
|
+
// points the creator at the local loop (B-F3-6).
|
|
142
144
|
const steps = [];
|
|
143
145
|
// Keyed off the RESOLVED dir, not the flag — a prompted name needs the cd too.
|
|
144
146
|
if (dir !== ".")
|
|
@@ -146,16 +148,18 @@ export async function initCommand(deps, opts = {}) {
|
|
|
146
148
|
if (!installed || opts.skipInstall) {
|
|
147
149
|
steps.push([`${pm} install`, "Install dependencies"]);
|
|
148
150
|
}
|
|
149
|
-
steps.push([`${pm} run dev`, "Start the local dev server"]);
|
|
150
151
|
if (!opts.from) {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
152
|
+
if (steps.length > 0) {
|
|
153
|
+
deps.print("");
|
|
154
|
+
for (const line of alignedSteps(steps))
|
|
155
|
+
deps.print(line);
|
|
156
|
+
}
|
|
154
157
|
deps.print("");
|
|
155
158
|
deps.print(" No account needed. When you're ready to put it online:");
|
|
156
159
|
for (const line of alignedSteps([
|
|
157
160
|
["vincentt login", "Connect this machine to Vincentt"],
|
|
158
161
|
["vincentt create", "Register the project and reserve its address"],
|
|
162
|
+
["vincentt preview", "Try it on your phone"],
|
|
159
163
|
])) {
|
|
160
164
|
deps.print(line);
|
|
161
165
|
}
|
|
@@ -181,6 +185,7 @@ export async function initCommand(deps, opts = {}) {
|
|
|
181
185
|
return code;
|
|
182
186
|
deps.print("");
|
|
183
187
|
deps.print(" Next:");
|
|
188
|
+
steps.push(["vincentt preview", "Try it on your phone"]);
|
|
184
189
|
for (const line of alignedSteps(steps))
|
|
185
190
|
deps.print(line);
|
|
186
191
|
return 0;
|
package/dist/lib/initFrom.js
CHANGED
|
@@ -86,10 +86,10 @@ export async function registerAfterScaffold(deps, opts = {}) {
|
|
|
86
86
|
resume(deps.print, deps.dirLabel, resumeCommand, needsCd);
|
|
87
87
|
if (scaffolded) {
|
|
88
88
|
deps.print("");
|
|
89
|
-
// B-
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
deps.print("
|
|
89
|
+
// B-F3-6 (amended): the product points only at the platform, so this
|
|
90
|
+
// ending names what signing in buys, never the local dev loop. The open
|
|
91
|
+
// route B-F2-9(a) requires stays named by the template's verb list.
|
|
92
|
+
deps.print(" Signing in is what gives it an address a phone can open.");
|
|
93
93
|
}
|
|
94
94
|
deps.print("");
|
|
95
95
|
deps.print(closing);
|
package/dist/lib/lifecycle.js
CHANGED
|
@@ -176,6 +176,7 @@ export async function previewStart(projectCwd, onLog, extra = {}) {
|
|
|
176
176
|
expiresAt: preview.expiresAt,
|
|
177
177
|
pidStartTime: await readProcessStartTime(process.pid),
|
|
178
178
|
token: preview.relayToken,
|
|
179
|
+
sessionId: preview.sessionId,
|
|
179
180
|
});
|
|
180
181
|
return { preview, started: true };
|
|
181
182
|
}
|
package/dist/lib/projects.js
CHANGED
|
@@ -203,16 +203,15 @@ export async function createCommand(deps, opts = {}) {
|
|
|
203
203
|
if (code !== 0 && code !== EXIT.DELIVERED_NOT_REGISTERED)
|
|
204
204
|
return code;
|
|
205
205
|
if (scaffolded) {
|
|
206
|
-
//
|
|
207
|
-
//
|
|
208
|
-
//
|
|
209
|
-
//
|
|
206
|
+
// Two aligned columns off the DETECTED package manager, never a hardcoded one
|
|
207
|
+
// (the template ships a pnpm lockfile and a hoisted node-linker). No dev-server
|
|
208
|
+
// step: `vincentt preview` starts it, and the product never points the creator
|
|
209
|
+
// at the local loop (B-F3-6).
|
|
210
210
|
const steps = [];
|
|
211
211
|
if (opts.dir)
|
|
212
212
|
steps.push([`cd ${opts.dir}`, ""]);
|
|
213
213
|
if (!installed || opts.skipInstall)
|
|
214
214
|
steps.push([`${pm} install`, "Install dependencies"]);
|
|
215
|
-
steps.push([`${pm} run dev`, "Start the local dev server"]);
|
|
216
215
|
steps.push(["vincentt preview", "Try it on your phone"]);
|
|
217
216
|
deps.print("");
|
|
218
217
|
for (const line of alignedSteps(steps))
|
package/dist/lib/relayInfo.d.ts
CHANGED
|
@@ -30,6 +30,12 @@ export interface RelayInfo {
|
|
|
30
30
|
* whose relay asks for none.
|
|
31
31
|
*/
|
|
32
32
|
token?: string;
|
|
33
|
+
/**
|
|
34
|
+
* The running session's id, so a re-run can hand over the console page that
|
|
35
|
+
* shows THIS preview rather than the bare project page. Never printed on its
|
|
36
|
+
* own; absent from markers written by an older CLI.
|
|
37
|
+
*/
|
|
38
|
+
sessionId?: string;
|
|
33
39
|
}
|
|
34
40
|
export declare function relayInfoPath(cwd: string): string;
|
|
35
41
|
/**
|
package/dist/login/pkceLogin.js
CHANGED
|
@@ -166,7 +166,7 @@ export async function runPkceLogin(opts) {
|
|
|
166
166
|
value: result.code,
|
|
167
167
|
page: {
|
|
168
168
|
status: 200,
|
|
169
|
-
html: browserPage(true, "You're
|
|
169
|
+
html: browserPage(true, "You're Signed In", "Go back to your AI assistant to continue."),
|
|
170
170
|
},
|
|
171
171
|
};
|
|
172
172
|
}
|
|
@@ -6,8 +6,8 @@ import { type UpgradeFacts } from "../shared/upgradeError.js";
|
|
|
6
6
|
export interface PreviewUiDeps {
|
|
7
7
|
print: (line: string) => void;
|
|
8
8
|
now: () => number;
|
|
9
|
-
/** The
|
|
10
|
-
|
|
9
|
+
/** The forwarded dev port, named identically in the settings banner and the 502. */
|
|
10
|
+
devPort: number;
|
|
11
11
|
color: boolean;
|
|
12
12
|
/** Filter the stream to one tester (`--tester`) — one tab, across refreshes. */
|
|
13
13
|
filterTester?: string;
|
package/dist/preview/command.js
CHANGED
|
@@ -238,7 +238,7 @@ export class PreviewUi {
|
|
|
238
238
|
const settings = this.deps.devCommand !== undefined && this.deps.portSource !== undefined
|
|
239
239
|
? { devCommand: this.deps.devCommand, portSource: this.deps.portSource }
|
|
240
240
|
: undefined;
|
|
241
|
-
this.lines(copy.originDown(this.deps.
|
|
241
|
+
this.lines(copy.originDown(this.deps.devPort, settings).map((l) => ` ${l}`));
|
|
242
242
|
}
|
|
243
243
|
originRecovered() {
|
|
244
244
|
this.originDown = false;
|
package/dist/preview/copy.d.ts
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flow L13 — the binding line, printed BEFORE the mint. The creator sees which
|
|
3
|
+
* project this folder is before anything is minted, the only moment a wrong
|
|
4
|
+
* binding is cheap to catch; the start block follows once the preview is live.
|
|
5
|
+
*/
|
|
6
|
+
export declare function bindingLine(projectName: string, orgName: string): string[];
|
|
7
|
+
/** Flow L13 — the start block, after the binding line. */
|
|
1
8
|
export interface StartBlockInput {
|
|
2
|
-
projectName: string;
|
|
3
|
-
orgName: string;
|
|
4
|
-
/** The forward target, printed BEFORE the URL so the 502 advisory matches it. */
|
|
5
|
-
targetUrl: string;
|
|
6
9
|
url: string;
|
|
7
10
|
/**
|
|
8
|
-
* The project's console page
|
|
9
|
-
* unlinked folder
|
|
10
|
-
*
|
|
11
|
-
*
|
|
11
|
+
* The project's console page. Always set on the start path, because a preview
|
|
12
|
+
* refuses an unlinked folder; kept optional so a missing binding prints NO
|
|
13
|
+
* console line rather than a guessed one — a link to the wrong project is worse
|
|
14
|
+
* than no link.
|
|
12
15
|
*/
|
|
13
16
|
consoleUrl?: string;
|
|
14
17
|
/**
|
|
@@ -24,17 +27,15 @@ export interface StartBlockInput {
|
|
|
24
27
|
}
|
|
25
28
|
export declare function startBlock(i: StartBlockInput): string[];
|
|
26
29
|
/**
|
|
27
|
-
* Flow L14 — not signed in.
|
|
28
|
-
* feel like a wall.
|
|
30
|
+
* Flow L14 — not signed in.
|
|
29
31
|
*
|
|
30
32
|
* Line 1 is f1's exact string, so a creator meeting this gate in a second
|
|
31
|
-
* command reads one product. The
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* consolation — no "unfortunately", no "you can still".
|
|
33
|
+
* command reads one product. The second line is the reason, not a rule. No local
|
|
34
|
+
* alternative is offered (B-F3-6 as amended: the local loop keeps working, but
|
|
35
|
+
* no product surface points at it) — an agent reading this screen relays what
|
|
36
|
+
* it says, and the only thing to relay is: sign in.
|
|
36
37
|
*/
|
|
37
|
-
export declare function notSignedIn(
|
|
38
|
+
export declare function notSignedIn(): string[];
|
|
38
39
|
/**
|
|
39
40
|
* Flow P1 — the two settings lines, printed right after resolution and before
|
|
40
41
|
* the sign-in check, the mint and the spawn. They are the ONLY disclosure of the
|
|
@@ -72,9 +73,10 @@ export declare function followingFooter(): string[];
|
|
|
72
73
|
* room over an `npm run dev` they forgot.
|
|
73
74
|
*
|
|
74
75
|
* The middle line splits the wire in two, because the creator's first instinct
|
|
75
|
-
* on a 502 is to suspect us.
|
|
76
|
+
* on a 502 is to suspect us. It names the PORT, matching the settings banner,
|
|
77
|
+
* never a loopback URL an agent could paste.
|
|
76
78
|
*/
|
|
77
|
-
export declare function originDown(
|
|
79
|
+
export declare function originDown(devPort: number, settings?: {
|
|
78
80
|
devCommand: string;
|
|
79
81
|
portSource: "flag" | "vincentt.json" | "default";
|
|
80
82
|
}): string[];
|
|
@@ -197,10 +199,13 @@ export declare function supersededLoser(): string[];
|
|
|
197
199
|
* each of those re-runs superseded the previous session: the link really did
|
|
198
200
|
* keep changing, because the attach never printed one.
|
|
199
201
|
*
|
|
200
|
-
* The
|
|
201
|
-
*
|
|
202
|
+
* The two addresses render in the start block's order and shape (Flow L16a),
|
|
203
|
+
* console page first, so an agent that re-runs preview to recover a link finds
|
|
204
|
+
* the console page exactly where the start block put it. `consoleUrl` carries
|
|
205
|
+
* the running session when the marker holds its id, and is undefined only for an
|
|
206
|
+
* unlinked folder, which prints no console line rather than a guessed one.
|
|
202
207
|
*/
|
|
203
|
-
export declare function attachedToRunning(url: string, expiresAt?: string): string[];
|
|
208
|
+
export declare function attachedToRunning(url: string, consoleUrl?: string, expiresAt?: string): string[];
|
|
204
209
|
/**
|
|
205
210
|
* c. Superseded — the terminal that WON. This sits ABOVE the `✓ Preview is live`
|
|
206
211
|
* block so the consequence is read before the URL is copied.
|
package/dist/preview/copy.js
CHANGED
|
@@ -16,38 +16,44 @@
|
|
|
16
16
|
// session id, the edge hostname, or the workingTreeKey. The grant is the
|
|
17
17
|
// sharpest — it transits an agent's tool-output transcript by default in this
|
|
18
18
|
// product, so f1's "a token is never printed" extends to it verbatim.
|
|
19
|
-
/** Flow L13 — the start block. */
|
|
20
19
|
import { escapeControl, terminalSafe } from "../lib/terminalText.js";
|
|
21
20
|
import { INSTALL_COMMAND } from "../shared/upgradeError.js";
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Flow L13 — the binding line, printed BEFORE the mint. The creator sees which
|
|
23
|
+
* project this folder is before anything is minted, the only moment a wrong
|
|
24
|
+
* binding is cheap to catch; the start block follows once the preview is live.
|
|
25
|
+
*/
|
|
26
|
+
export function bindingLine(projectName, orgName) {
|
|
27
|
+
return [
|
|
24
28
|
"",
|
|
25
29
|
// The org is dropped rather than rendered as a dangling separator when it
|
|
26
30
|
// is not known. `Museum AR Tour · ` reads as a truncated line — the one
|
|
27
31
|
// moment a wrong binding is cheap to catch is the one moment the line must
|
|
28
32
|
// not look broken.
|
|
29
|
-
|
|
30
|
-
`→ Serving ${i.targetUrl}`,
|
|
31
|
-
"",
|
|
32
|
-
"✓ Preview is live",
|
|
33
|
-
"",
|
|
34
|
-
// The URL is on its own line, indented, unwrapped and UNADORNED. No `URL:`
|
|
35
|
-
// label, no backticks, no surrounding punctuation — it is the single
|
|
36
|
-
// most-copied string in the product, and anything beside it ends up in
|
|
37
|
-
// someone's paste.
|
|
38
|
-
` ${i.url}`,
|
|
39
|
-
"",
|
|
33
|
+
orgName ? `→ ${projectName} · ${orgName}` : `→ ${projectName}`,
|
|
40
34
|
];
|
|
35
|
+
}
|
|
36
|
+
export function startBlock(i) {
|
|
37
|
+
// No loopback address: the settings banner already names the dev port, and a
|
|
38
|
+
// URL in this transcript is a URL an agent can paste. A loopback one reaches
|
|
39
|
+
// nobody but this machine (B-F3-6: the local loop works, no surface points at
|
|
40
|
+
// it).
|
|
41
|
+
const out = ["", "✓ Preview is live", ""];
|
|
42
|
+
// Each URL is on its own line, indented, unwrapped and UNADORNED. No `URL:`
|
|
43
|
+
// label, no backticks, no surrounding punctuation — anything beside it ends up
|
|
44
|
+
// in someone's paste.
|
|
45
|
+
//
|
|
46
|
+
// The console page comes FIRST: it carries the phone link, the QR, the live
|
|
47
|
+
// device view and Stop, so it is the one address an agent hands over, and the
|
|
48
|
+
// first one it reads.
|
|
49
|
+
if (i.consoleUrl) {
|
|
50
|
+
out.push(" Open the console:", "", ` ${i.consoleUrl}`, "");
|
|
51
|
+
}
|
|
52
|
+
out.push(" On a phone:", "", ` ${i.url}`, "");
|
|
41
53
|
if (i.qr && i.qr.length > 0) {
|
|
42
54
|
out.push(...i.qr.map((row) => ` ${row}`), "");
|
|
43
55
|
}
|
|
44
56
|
out.push(" Scan it, or open the link on any phone. No sign-in needed on the phone.", ` Expires in ${i.expiresInHours}h · Ctrl-C to stop`, "");
|
|
45
|
-
// BELOW the phone URL and its instruction, never above. The phone address is the
|
|
46
|
-
// line the creator is waiting on; a second URL printed first is a second thing to
|
|
47
|
-
// read before the one that matters. Dropped entirely when the folder is unlinked.
|
|
48
|
-
if (i.consoleUrl) {
|
|
49
|
-
out.push(" In the console:", "", ` ${i.consoleUrl}`, "");
|
|
50
|
-
}
|
|
51
57
|
if (i.feedbackOrigin) {
|
|
52
58
|
out.push(` Feedback is accepted from the console at ${terminalSafe(i.feedbackOrigin)}`, "");
|
|
53
59
|
}
|
|
@@ -55,17 +61,15 @@ export function startBlock(i) {
|
|
|
55
61
|
return out;
|
|
56
62
|
}
|
|
57
63
|
/**
|
|
58
|
-
* Flow L14 — not signed in.
|
|
59
|
-
* feel like a wall.
|
|
64
|
+
* Flow L14 — not signed in.
|
|
60
65
|
*
|
|
61
66
|
* Line 1 is f1's exact string, so a creator meeting this gate in a second
|
|
62
|
-
* command reads one product. The
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* consolation — no "unfortunately", no "you can still".
|
|
67
|
+
* command reads one product. The second line is the reason, not a rule. No local
|
|
68
|
+
* alternative is offered (B-F3-6 as amended: the local loop keeps working, but
|
|
69
|
+
* no product surface points at it) — an agent reading this screen relays what
|
|
70
|
+
* it says, and the only thing to relay is: sign in.
|
|
67
71
|
*/
|
|
68
|
-
export function notSignedIn(
|
|
72
|
+
export function notSignedIn() {
|
|
69
73
|
return [
|
|
70
74
|
"",
|
|
71
75
|
"✗ You're not signed in.",
|
|
@@ -73,10 +77,7 @@ export function notSignedIn(devCommand = "npm run dev", localUrl = "http://local
|
|
|
73
77
|
" A preview needs a hostname your phone can reach, and that needs an account.",
|
|
74
78
|
"",
|
|
75
79
|
" Sign in: vincentt login",
|
|
76
|
-
"",
|
|
77
|
-
` Your local dev loop does not need one. Run \`${devCommand}\` and open`,
|
|
78
|
-
` ${localUrl} on this machine. The camera works there.`,
|
|
79
|
-
" What signing in adds is the phone.",
|
|
80
|
+
" Then start it again: vincentt preview",
|
|
80
81
|
"",
|
|
81
82
|
];
|
|
82
83
|
}
|
|
@@ -156,12 +157,13 @@ export function followingFooter() {
|
|
|
156
157
|
* room over an `npm run dev` they forgot.
|
|
157
158
|
*
|
|
158
159
|
* The middle line splits the wire in two, because the creator's first instinct
|
|
159
|
-
* on a 502 is to suspect us.
|
|
160
|
+
* on a 502 is to suspect us. It names the PORT, matching the settings banner,
|
|
161
|
+
* never a loopback URL an agent could paste.
|
|
160
162
|
*/
|
|
161
|
-
export function originDown(
|
|
163
|
+
export function originDown(devPort, settings) {
|
|
162
164
|
if (settings === undefined) {
|
|
163
165
|
return [
|
|
164
|
-
`! Your dev server isn't responding on ${
|
|
166
|
+
`! Your dev server isn't responding on port ${devPort}.`,
|
|
165
167
|
"",
|
|
166
168
|
" The preview is still live. The phone is reaching us; we can't reach your app.",
|
|
167
169
|
" Start it and reload the phone: npm run dev",
|
|
@@ -179,7 +181,7 @@ export function originDown(targetUrl, settings) {
|
|
|
179
181
|
? " If it runs on another port, pass that port with --port."
|
|
180
182
|
: " If it runs on another port, pass --port or add vincentt.json.";
|
|
181
183
|
return [
|
|
182
|
-
`! Your dev server isn't responding on ${
|
|
184
|
+
`! Your dev server isn't responding on port ${devPort}${source}.`,
|
|
183
185
|
"",
|
|
184
186
|
" The preview is still live. The phone is reaching us; we can't reach your app.",
|
|
185
187
|
` Start it and reload the phone: ${settings.devCommand}`,
|
|
@@ -405,11 +407,18 @@ export function supersededLoser() {
|
|
|
405
407
|
* each of those re-runs superseded the previous session: the link really did
|
|
406
408
|
* keep changing, because the attach never printed one.
|
|
407
409
|
*
|
|
408
|
-
* The
|
|
409
|
-
*
|
|
410
|
+
* The two addresses render in the start block's order and shape (Flow L16a),
|
|
411
|
+
* console page first, so an agent that re-runs preview to recover a link finds
|
|
412
|
+
* the console page exactly where the start block put it. `consoleUrl` carries
|
|
413
|
+
* the running session when the marker holds its id, and is undefined only for an
|
|
414
|
+
* unlinked folder, which prints no console line rather than a guessed one.
|
|
410
415
|
*/
|
|
411
|
-
export function attachedToRunning(url, expiresAt) {
|
|
412
|
-
const lines = ["", "→ A preview is already running for this folder.", ""
|
|
416
|
+
export function attachedToRunning(url, consoleUrl, expiresAt) {
|
|
417
|
+
const lines = ["", "→ A preview is already running for this folder.", ""];
|
|
418
|
+
if (consoleUrl) {
|
|
419
|
+
lines.push(" Open the console:", "", ` ${consoleUrl}`, "");
|
|
420
|
+
}
|
|
421
|
+
lines.push(" On a phone:", "", ` ${url}`, "");
|
|
413
422
|
// Stated only when known: a marker written by an older CLI has no expiry, and
|
|
414
423
|
// inventing one would be the over-claim this copy exists to avoid.
|
|
415
424
|
if (expiresAt !== undefined) {
|
package/dist/relay/server.js
CHANGED
|
@@ -147,7 +147,9 @@ export function startRelay(opts = {}) {
|
|
|
147
147
|
});
|
|
148
148
|
ws.on("close", () => log("client disconnected"));
|
|
149
149
|
});
|
|
150
|
-
|
|
150
|
+
// Port only: this line reaches `vincentt preview`'s transcript, and a loopback
|
|
151
|
+
// address there is one an agent can paste and hand over.
|
|
152
|
+
http.listen(port, host, () => log(`relay listening on port ${port} (ws ${wsPath}, http /query)`));
|
|
151
153
|
return {
|
|
152
154
|
store,
|
|
153
155
|
annotations,
|
|
@@ -50,8 +50,6 @@ export type RefusalArm = {
|
|
|
50
50
|
stillServing?: number;
|
|
51
51
|
} | {
|
|
52
52
|
kind: "preview_start";
|
|
53
|
-
devCommand: string;
|
|
54
|
-
localUrl: string;
|
|
55
53
|
};
|
|
56
54
|
/** Flow L22: the version refusal on any verb, in the arm that knows where it landed. */
|
|
57
55
|
export declare function upgradeRefusalLines(facts: UpgradeFacts, arm?: RefusalArm, own?: string): string[];
|
|
@@ -171,11 +171,10 @@ export function upgradeRefusalLines(facts, arm = { kind: "generic" }, own = clie
|
|
|
171
171
|
"",
|
|
172
172
|
remedy(variant),
|
|
173
173
|
];
|
|
174
|
+
// No local-loop paragraph on preview_start (B-F3-6 as amended): the remedy is
|
|
175
|
+
// the upgrade, and the screen ends on the command that leads back to a preview.
|
|
174
176
|
if (variant !== "not_received")
|
|
175
177
|
out.push(thenLine(arm));
|
|
176
|
-
if (arm.kind === "preview_start") {
|
|
177
|
-
out.push("", ` Your local dev loop does not need an upgrade. Run \`${arm.devCommand}\` and open`, ` ${arm.localUrl} on this machine. The camera works there.`);
|
|
178
|
-
}
|
|
179
178
|
out.push("");
|
|
180
179
|
return out;
|
|
181
180
|
}
|
package/package.json
CHANGED