@vgai/live 0.5.22 → 0.5.24
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/.tsbuildinfo +1 -1
- package/dist/{editor.d.ts → vgai-live/src/editor.d.ts} +5 -8
- package/dist/{editor.js → vgai-live/src/editor.js} +5 -15
- package/dist/{game-client → vgai-live/src/game-client}/bridge-transport.d.ts +10 -1
- package/dist/{game-client → vgai-live/src/game-client}/client.d.ts +36 -6
- package/dist/{game-client → vgai-live/src/game-client}/client.js +80 -9
- package/dist/{game-client → vgai-live/src/game-client}/relay-transport.d.ts +15 -2
- package/dist/{game-client → vgai-live/src/game-client}/relay-transport.js +38 -3
- package/dist/{game-client → vgai-live/src/game-client}/types.d.ts +5 -5
- package/dist/{game-client → vgai-live/src/game-client}/wait-for.js +2 -2
- package/dist/{game.d.ts → vgai-live/src/game.d.ts} +1 -1
- package/dist/{index.d.ts → vgai-live/src/index.d.ts} +2 -2
- package/dist/{index.js → vgai-live/src/index.js} +2 -2
- package/dist/{recording.js → vgai-live/src/recording.js} +3 -10
- package/dist/{session.d.ts → vgai-live/src/session.d.ts} +0 -7
- package/dist/{session.js → vgai-live/src/session.js} +56 -19
- package/dist/{tools.d.ts → vgai-live/src/tools.d.ts} +1 -1
- package/dist/{tools.js → vgai-live/src/tools.js} +1 -1
- package/dist/vgai-sdk/src/editor/session-registry-format.d.ts +81 -0
- package/dist/vgai-sdk/src/editor/session-registry-format.js +158 -0
- package/package.json +3 -3
- package/src/editor.ts +5 -16
- package/src/game-client/bridge-transport.ts +14 -1
- package/src/game-client/client.ts +98 -9
- package/src/game-client/relay-transport.ts +46 -3
- package/src/game-client/types.ts +5 -5
- package/src/game-client/wait-for.ts +2 -2
- package/src/game.ts +1 -1
- package/src/index.ts +2 -2
- package/src/recording.ts +3 -10
- package/src/session.ts +67 -24
- package/src/tools.ts +1 -1
- /package/dist/{editor-document.d.ts → vgai-live/src/editor-document.d.ts} +0 -0
- /package/dist/{editor-document.js → vgai-live/src/editor-document.js} +0 -0
- /package/dist/{game-client → vgai-live/src/game-client}/bridge-heartbeat.d.ts +0 -0
- /package/dist/{game-client → vgai-live/src/game-client}/bridge-heartbeat.js +0 -0
- /package/dist/{game-client → vgai-live/src/game-client}/bridge-transport.js +0 -0
- /package/dist/{game-client → vgai-live/src/game-client}/capture-notes.d.ts +0 -0
- /package/dist/{game-client → vgai-live/src/game-client}/capture-notes.js +0 -0
- /package/dist/{game-client → vgai-live/src/game-client}/errors.d.ts +0 -0
- /package/dist/{game-client → vgai-live/src/game-client}/errors.js +0 -0
- /package/dist/{game-client → vgai-live/src/game-client}/events-matcher.d.ts +0 -0
- /package/dist/{game-client → vgai-live/src/game-client}/events-matcher.js +0 -0
- /package/dist/{game-client → vgai-live/src/game-client}/failure-block.d.ts +0 -0
- /package/dist/{game-client → vgai-live/src/game-client}/failure-block.js +0 -0
- /package/dist/{game-client → vgai-live/src/game-client}/fast-forward.d.ts +0 -0
- /package/dist/{game-client → vgai-live/src/game-client}/fast-forward.js +0 -0
- /package/dist/{game-client → vgai-live/src/game-client}/hidden-recovery.d.ts +0 -0
- /package/dist/{game-client → vgai-live/src/game-client}/hidden-recovery.js +0 -0
- /package/dist/{game-client → vgai-live/src/game-client}/index.d.ts +0 -0
- /package/dist/{game-client → vgai-live/src/game-client}/index.js +0 -0
- /package/dist/{game-client → vgai-live/src/game-client}/perf-sampling.d.ts +0 -0
- /package/dist/{game-client → vgai-live/src/game-client}/perf-sampling.js +0 -0
- /package/dist/{game-client → vgai-live/src/game-client}/screenshot-target.d.ts +0 -0
- /package/dist/{game-client → vgai-live/src/game-client}/screenshot-target.js +0 -0
- /package/dist/{game-client → vgai-live/src/game-client}/state-cap.d.ts +0 -0
- /package/dist/{game-client → vgai-live/src/game-client}/state-cap.js +0 -0
- /package/dist/{game-client → vgai-live/src/game-client}/types.js +0 -0
- /package/dist/{game-client → vgai-live/src/game-client}/wait-for.d.ts +0 -0
- /package/dist/{game.js → vgai-live/src/game.js} +0 -0
- /package/dist/{lazy-proxy.d.ts → vgai-live/src/lazy-proxy.d.ts} +0 -0
- /package/dist/{lazy-proxy.js → vgai-live/src/lazy-proxy.js} +0 -0
- /package/dist/{recording.d.ts → vgai-live/src/recording.d.ts} +0 -0
- /package/dist/{singleton.d.ts → vgai-live/src/singleton.d.ts} +0 -0
- /package/dist/{singleton.js → vgai-live/src/singleton.js} +0 -0
|
@@ -102,6 +102,17 @@ export class RelayTransport implements BridgeTransport {
|
|
|
102
102
|
this.instance = opts.instance;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Deliberately NOT guarded against a page-fallback answer the way the browser
|
|
107
|
+
* (`packages/editor/src/editor-server-response.ts`) and the CLI client
|
|
108
|
+
* (`@vgai/editor-sdk`'s `EditorClient.readJson`) are. Those two can be pointed
|
|
109
|
+
* at an arbitrary URL — a share tunnel, a static host — where a `200
|
|
110
|
+
* text/html` for an unserved route is real. This `baseUrl` is
|
|
111
|
+
* `http://127.0.0.1:<port>` and nothing else, and `session.ts` has already
|
|
112
|
+
* PROVED that port is this project's editor server (`probeServedProject`, whose
|
|
113
|
+
* own unparseable-answer path is exactly the refusal): a foreign occupant is
|
|
114
|
+
* refused there, before any command reaches here.
|
|
115
|
+
*/
|
|
105
116
|
private async postCommand(
|
|
106
117
|
body: Record<string, unknown>,
|
|
107
118
|
timeoutMs: number,
|
|
@@ -176,7 +187,7 @@ export class RelayTransport implements BridgeTransport {
|
|
|
176
187
|
*
|
|
177
188
|
* It runs on every leg, not just `call('snapshot')`, because that is where
|
|
178
189
|
* the measured gap was: a bot that drives the game with `hold`/`command` and
|
|
179
|
-
* reads through them
|
|
190
|
+
* reads through them could run
|
|
180
191
|
* its entire session against a backgrounded tab and never be told, so a
|
|
181
192
|
* later failure read as a generic relay timeout instead of "your tab is
|
|
182
193
|
* hidden". Returns whether the tab is hidden so `call` can decide whether to
|
|
@@ -189,7 +200,14 @@ export class RelayTransport implements BridgeTransport {
|
|
|
189
200
|
// P21: what was measured, and what this run is doing about it — not a
|
|
190
201
|
// claim about where the tab is. The reading is a `presence` snapshot
|
|
191
202
|
// (`isHidden` below), which ages between the tab's own reports.
|
|
192
|
-
|
|
203
|
+
// STDERR, not stdout. `vgai status` and every other `--json`-shaped verb
|
|
204
|
+
// put their PAYLOAD on stdout and every banner on stderr; this notice went
|
|
205
|
+
// to stdout and prepended a prose sentence to the JSON, so any machine
|
|
206
|
+
// consumer piping `vgai status` into a parser got a SyntaxError the moment
|
|
207
|
+
// the tab happened to be hidden — measured while reading the coverage
|
|
208
|
+
// table on a backgrounded session. A diagnostic that breaks the payload it
|
|
209
|
+
// annotates is worse than no diagnostic.
|
|
210
|
+
process.stderr.write(
|
|
193
211
|
'vgai: the editor page last REPORTED document.visibilityState "hidden" — the engine ' +
|
|
194
212
|
'stops its loop while the page reports itself hidden, so this run drives ' +
|
|
195
213
|
'deterministic runTicks through the session relay instead of wall clock. Sim time ' +
|
|
@@ -302,7 +320,7 @@ export class RelayTransport implements BridgeTransport {
|
|
|
302
320
|
}
|
|
303
321
|
|
|
304
322
|
/**
|
|
305
|
-
*
|
|
323
|
+
* Ships `src` (`step.toString()`) to the editor dev server's
|
|
306
324
|
* `page-script` op — a STANDALONE relay command (like `bridge-screenshot`
|
|
307
325
|
* above), not a `bridge-call` method (see `command-listener.ts`'s
|
|
308
326
|
* `handlePageScript` doc comment for why). `step` itself is unused on this
|
|
@@ -327,6 +345,31 @@ export class RelayTransport implements BridgeTransport {
|
|
|
327
345
|
}
|
|
328
346
|
}
|
|
329
347
|
|
|
348
|
+
/** THE MODULE LANE over the relay — same wire shape as `page-script`. */
|
|
349
|
+
async runGameScript(
|
|
350
|
+
src: string,
|
|
351
|
+
_step: (scope: unknown) => unknown,
|
|
352
|
+
instance?: string,
|
|
353
|
+
): Promise<BridgeCallOutcome> {
|
|
354
|
+
try {
|
|
355
|
+
const body = await this.postCommand(
|
|
356
|
+
{ type: 'game-eval', src, ...(instance === undefined ? {} : { instance }) },
|
|
357
|
+
PAGE_SCRIPT_TIMEOUT_MS,
|
|
358
|
+
);
|
|
359
|
+
return this.toBridgeOutcome(body);
|
|
360
|
+
} catch (err) {
|
|
361
|
+
return {
|
|
362
|
+
ok: false,
|
|
363
|
+
error: {
|
|
364
|
+
code: 'RELAY_UNREACHABLE',
|
|
365
|
+
message:
|
|
366
|
+
`vgai: could not reach the editor dev server relay at ${this.baseUrl} — ` +
|
|
367
|
+
`${err instanceof Error ? err.message : String(err)}`,
|
|
368
|
+
},
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
330
373
|
/**
|
|
331
374
|
* P20 — order the tab to reload, then wait for EVIDENCE that it came back.
|
|
332
375
|
*
|
package/src/game-client/types.ts
CHANGED
|
@@ -45,22 +45,22 @@ export interface DebugBridgeInput {
|
|
|
45
45
|
setVirtualAction(action: string, value: VirtualActionValue): VirtualActionResult;
|
|
46
46
|
tapVirtualAction(action: string): VirtualActionResult;
|
|
47
47
|
clearVirtualActions(): void;
|
|
48
|
-
/**
|
|
49
|
-
* at the start of that tick's input phase. Declared here
|
|
50
|
-
* bridge↔wire coverage-parity gate
|
|
48
|
+
/** Schedule a virtual actuation for a specific tick, applied
|
|
49
|
+
* at the start of that tick's input phase. Declared here for the
|
|
50
|
+
* bridge↔wire coverage-parity gate — type-shape completeness with
|
|
51
51
|
* `runtime/debug-bridge.ts`'s `VgaiDebugInputHandle` — this package still
|
|
52
52
|
* exposes no client-side convenience wrapper around it (deliberately
|
|
53
53
|
* parked; see `GameInput` in `client.ts`), this is pure type-shape
|
|
54
54
|
* mirroring. */
|
|
55
55
|
scheduleActionAtTick(tick: number, action: string, value: VirtualActionValue): void;
|
|
56
|
-
/**
|
|
56
|
+
/** Pointer-dispatch op — mirrors `runtime/debug-bridge.ts`'s
|
|
57
57
|
* `VgaiDebugInputHandle.injectPointerDelta`: accumulates a synthetic
|
|
58
58
|
* pointer delta for a named test source (sums within a frame, clears each
|
|
59
59
|
* frame). Declared here for type-shape completeness with the bridge, same
|
|
60
60
|
* precedent as `scheduleActionAtTick` above — no client-side convenience
|
|
61
61
|
* wrapper in `client.ts` (deliberately parked). */
|
|
62
62
|
injectPointerDelta(sourceId: string, delta: { x: number; y: number }): void;
|
|
63
|
-
/**
|
|
63
|
+
/** Pointer-dispatch op — mirrors `runtime/debug-bridge.ts`'s
|
|
64
64
|
* `VgaiDebugInputHandle.injectPointerPosition`: sets a synthetic absolute
|
|
65
65
|
* pointer position for a named test source (last-write-wins, persists
|
|
66
66
|
* until changed). Same type-shape-only precedent as `scheduleActionAtTick`. */
|
|
@@ -168,7 +168,7 @@ export interface WaitForClock {
|
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
// ---------------------------------------------------------------------------
|
|
171
|
-
// Fixture heartbeat (
|
|
171
|
+
// Fixture heartbeat ("Watchdog sim-awareness / fixture
|
|
172
172
|
// heartbeat for long silent tests" — Session C hit exit-5 on the runner's
|
|
173
173
|
// 2x90s stdout-liveness watchdog during a legitimately silent 5-minute test).
|
|
174
174
|
//
|
|
@@ -179,7 +179,7 @@ export interface WaitForClock {
|
|
|
179
179
|
// advancing test can never false-wedge regardless of duration, no matter how
|
|
180
180
|
// long a single `simSeconds` budget runs.
|
|
181
181
|
//
|
|
182
|
-
// INVARIANT (tested below
|
|
182
|
+
// INVARIANT (tested below): a heartbeat requires BOTH
|
|
183
183
|
// (a) >= HEARTBEAT_INTERVAL_MS of wall time since the last heartbeat, AND
|
|
184
184
|
// (b) the tick has ADVANCED since the last heartbeat (not merely since the
|
|
185
185
|
// last poll). Without (b), the poll loop itself — which keeps running
|
package/src/game.ts
CHANGED
|
@@ -22,7 +22,7 @@ import { GameClient, RelayTransport } from './game-client/index.js';
|
|
|
22
22
|
/** A `GameClient` addressing ONE mounted instance, tagged with the mount `id`
|
|
23
23
|
* it drives. `id` is the whole point of the handle to a CALLER: it is what
|
|
24
24
|
* `tools.run(name, args, { instance: handle.id })` passes to scope a
|
|
25
|
-
* node-hosted tool
|
|
25
|
+
* node-hosted tool to this seat — the two-seat
|
|
26
26
|
* multiplayer verification pattern. Without it the caller has a client it can
|
|
27
27
|
* drive but no id to hand a tool, so the seat-scoped path silently degrades to
|
|
28
28
|
* the sole-instance one. */
|
package/src/index.ts
CHANGED
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
* and driven through the product. It is scoped to the ACTIVE document and
|
|
35
35
|
* refuses anything outside it by name (`editor-document.ts`).
|
|
36
36
|
*
|
|
37
|
-
* `page(step)` = `GameClient.page(step)
|
|
38
|
-
*
|
|
37
|
+
* `page(step)` = `GameClient.page(step)`, the playwright-shim surface —
|
|
38
|
+
* write `step` as a literal `async (page) => {...}` and inline
|
|
39
39
|
* every value it needs. KNOWN WIRE LIMITATION: under the relay transport this
|
|
40
40
|
* binds to, `step` is shipped to the editor dev server as `step.toString()`
|
|
41
41
|
* and reconstructed there — closures over outer variables do NOT survive that
|
package/src/recording.ts
CHANGED
|
@@ -54,17 +54,10 @@ export class LiveGameplayRecording {
|
|
|
54
54
|
await copyFile(capture.path, path);
|
|
55
55
|
}
|
|
56
56
|
return {
|
|
57
|
+
...capture,
|
|
58
|
+
// The caller's `destination` copy, when there was one — otherwise the
|
|
59
|
+
// project path the recorder wrote. Spread first so this wins.
|
|
57
60
|
path,
|
|
58
|
-
startedAt: capture.startedAt,
|
|
59
|
-
mimeType: capture.mimeType,
|
|
60
|
-
width: capture.width,
|
|
61
|
-
height: capture.height,
|
|
62
|
-
fps: capture.fps,
|
|
63
|
-
audio: capture.audio,
|
|
64
|
-
durationMs: capture.durationMs,
|
|
65
|
-
droppedFrames: capture.droppedFrames,
|
|
66
|
-
frameErrors: capture.frameErrors,
|
|
67
|
-
layers: capture.layers,
|
|
68
61
|
};
|
|
69
62
|
}
|
|
70
63
|
}
|
package/src/session.ts
CHANGED
|
@@ -54,6 +54,7 @@ import {
|
|
|
54
54
|
HttpEditorTransport,
|
|
55
55
|
withTimeout,
|
|
56
56
|
} from '@vgai/sdk';
|
|
57
|
+
import { servedProjectAnswer } from '../../vgai-sdk/src/editor/session-registry-format';
|
|
57
58
|
|
|
58
59
|
/**
|
|
59
60
|
* Lifted verbatim from `packages/vgai-cli/src/project-root.ts` (see this
|
|
@@ -144,16 +145,6 @@ function readProjectSession(projectRoot: string): ProjectSessionHint | null {
|
|
|
144
145
|
* but none open this project)" — sending the operator to start an editor that
|
|
145
146
|
* was already running, with the real defect (their own manifest) never named.
|
|
146
147
|
*/
|
|
147
|
-
function servedProject(body: unknown): { path: string | null; manifestError: string | null } {
|
|
148
|
-
const b = body as {
|
|
149
|
-
project?: { path?: string } | null;
|
|
150
|
-
serving?: { path?: string; error?: string } | null;
|
|
151
|
-
};
|
|
152
|
-
return {
|
|
153
|
-
path: b.project?.path ?? b.serving?.path ?? null,
|
|
154
|
-
manifestError: b.project ? null : (b.serving?.error ?? null),
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
148
|
|
|
158
149
|
/**
|
|
159
150
|
* What the server on `port` says it serves — `undefined` when it did not
|
|
@@ -168,7 +159,7 @@ async function probeServedProject(
|
|
|
168
159
|
signal: AbortSignal.timeout(EDITOR_SESSION_DISCOVERY_TIMEOUT_MS),
|
|
169
160
|
});
|
|
170
161
|
if (!response.ok) return undefined;
|
|
171
|
-
return
|
|
162
|
+
return servedProjectAnswer(await response.json());
|
|
172
163
|
} catch {
|
|
173
164
|
return undefined;
|
|
174
165
|
}
|
|
@@ -200,6 +191,62 @@ function manifestRefusal(projectRoot: string, manifestError: string): Error {
|
|
|
200
191
|
* silently attaches to an unrelated project's session — see the module doc
|
|
201
192
|
* above) when no vgai.project.json is found, or no live session covers it.
|
|
202
193
|
*/
|
|
194
|
+
/**
|
|
195
|
+
* THE REFUSAL WHEN NOTHING MATCHED — and it says WHICH nothing.
|
|
196
|
+
*
|
|
197
|
+
* "No live editor session found … run `vgai edit`" used to be the answer to
|
|
198
|
+
* four different states, only one of which it described. The other three sent
|
|
199
|
+
* the operator to start an editor that was already running:
|
|
200
|
+
*
|
|
201
|
+
* - discovery FAILED (a probe timeout under load) — nothing was learned, so
|
|
202
|
+
* "no session is running" is not a fact anyone established;
|
|
203
|
+
* - the registry was read and is genuinely empty — the one case the old text
|
|
204
|
+
* was right about;
|
|
205
|
+
* - sessions exist, but every one resolves to a different canonical path. In
|
|
206
|
+
* a repo worked through git worktrees this is the ORDINARY miss: two
|
|
207
|
+
* checkouts of the same project differ only in a path prefix, and a
|
|
208
|
+
* symlinked worktree's `realpath` diverges from the path the caller typed.
|
|
209
|
+
* Naming both sides is what makes it a two-second diagnosis instead of a
|
|
210
|
+
* hunt.
|
|
211
|
+
*
|
|
212
|
+
* Each branch prescribes only what its own state supports.
|
|
213
|
+
*/
|
|
214
|
+
function noMatchingSessionRefusal(
|
|
215
|
+
projectRoot: string,
|
|
216
|
+
canon: string,
|
|
217
|
+
sessions: readonly EditorSessionInfo[],
|
|
218
|
+
discoveryFailure: string | null,
|
|
219
|
+
): Error {
|
|
220
|
+
if (discoveryFailure !== null) {
|
|
221
|
+
return new Error(
|
|
222
|
+
`@vgai/live: could not READ the editor session registry while looking for ${projectRoot} — ` +
|
|
223
|
+
`${discoveryFailure}. This is not the answer "no editor is running": the question went ` +
|
|
224
|
+
'unanswered, so nothing is known about what is live. Retry (a probe can time out while ' +
|
|
225
|
+
'the box is loaded); if it keeps failing, `vgai sessions` asks the same question directly.',
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
if (sessions.length === 0) {
|
|
229
|
+
return new Error(
|
|
230
|
+
`@vgai/live: the editor session registry is readable and lists NO live sessions, so none ` +
|
|
231
|
+
`covers ${projectRoot}. @vgai/live only attaches to an already-running session — it ` +
|
|
232
|
+
'never starts one — so run `vgai edit` in that project first, then retry.',
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
const listed = sessions
|
|
236
|
+
.map((s) => ` port ${s.port} → ${s.project === null ? '(no project)' : s.project}`)
|
|
237
|
+
.join('\n');
|
|
238
|
+
return new Error(
|
|
239
|
+
`@vgai/live: ${sessions.length} live editor session(s) are running, but none of them opens ` +
|
|
240
|
+
`${projectRoot}. @vgai/live never silently attaches to a different project.\n` +
|
|
241
|
+
` looking for (resolved): ${canon}\n` +
|
|
242
|
+
` live sessions:\n${listed}\n` +
|
|
243
|
+
' If one of those is meant to be this project, the two paths differ after resolution — ' +
|
|
244
|
+
'the usual cause is a git worktree or a symlink, where the session was opened through a ' +
|
|
245
|
+
'different path to the same files. Run `vgai edit` from THIS path, or use the path the ' +
|
|
246
|
+
'session lists.',
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
203
250
|
export async function resolveSession(
|
|
204
251
|
projectDir: string = process.cwd(),
|
|
205
252
|
deps: SessionResolutionDeps = {},
|
|
@@ -239,30 +286,26 @@ export async function resolveSession(
|
|
|
239
286
|
}
|
|
240
287
|
|
|
241
288
|
let sessions: EditorSessionInfo[];
|
|
289
|
+
// A FAILED discovery is not an empty one. Collapsing the two into `[]` is
|
|
290
|
+
// what made this door answer "no live editor session found" — and prescribe
|
|
291
|
+
// `vgai edit` — for a probe that merely timed out under load, sending the
|
|
292
|
+
// operator to start an editor that was already running while the real defect
|
|
293
|
+
// went unnamed. The same collapse the manifest refusal above was added for.
|
|
294
|
+
let discoveryFailure: string | null = null;
|
|
242
295
|
try {
|
|
243
296
|
sessions = await withTimeout(
|
|
244
297
|
transport.listSessions(EDITOR_SESSION_DISCOVERY_TIMEOUT_MS),
|
|
245
298
|
EDITOR_SESSION_DISCOVERY_TIMEOUT_MS,
|
|
246
299
|
'editor session discovery',
|
|
247
300
|
);
|
|
248
|
-
} catch {
|
|
301
|
+
} catch (err) {
|
|
249
302
|
sessions = [];
|
|
303
|
+
discoveryFailure = err instanceof Error ? err.message : String(err);
|
|
250
304
|
}
|
|
251
305
|
|
|
252
306
|
const canon = canonicalPath(projectRoot);
|
|
253
307
|
const match = sessions.find((s) => s.project !== null && canonicalPath(s.project) === canon);
|
|
254
|
-
if (!match)
|
|
255
|
-
const otherCount = sessions.length;
|
|
256
|
-
throw new Error(
|
|
257
|
-
`@vgai/live: no live editor session found for ${projectRoot}. @vgai/live only attaches to ` +
|
|
258
|
-
'an already-running session — it never starts one — so run `vgai edit` in that project ' +
|
|
259
|
-
'first, then retry.' +
|
|
260
|
-
(otherCount > 0
|
|
261
|
-
? ` (${otherCount} other live session(s) found, but none open this project — @vgai/live ` +
|
|
262
|
-
'never silently attaches to a different project.)'
|
|
263
|
-
: ''),
|
|
264
|
-
);
|
|
265
|
-
}
|
|
308
|
+
if (!match) throw noMatchingSessionRefusal(projectRoot, canon, sessions, discoveryFailure);
|
|
266
309
|
|
|
267
310
|
if (match.manifestError != null) throw manifestRefusal(projectRoot, match.manifestError);
|
|
268
311
|
|
package/src/tools.ts
CHANGED
|
@@ -31,7 +31,7 @@ export class LiveTools {
|
|
|
31
31
|
*
|
|
32
32
|
* `instance` names WHICH mounted instance the tool should drive when several
|
|
33
33
|
* are live (multiplayer authoring) — it reaches the tool as `ctx.instance`,
|
|
34
|
-
* and a tool that drives the game
|
|
34
|
+
* and a tool that drives the game binds
|
|
35
35
|
* `game.instance(ctx.instance)` from it. Omitted is the single-instance case;
|
|
36
36
|
* the tool then targets the sole live instance, exactly as before.
|
|
37
37
|
*/
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|