peachsim 0.1.571 → 0.1.573
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/LICENSE +1 -1
- package/README.md +106 -106
- package/cli/app-config.ts +1 -1
- package/cli/app-fonts.ts +4 -4
- package/cli/auth.ts +11 -11
- package/cli/bin.ts +4 -4
- package/cli/bridge-flow-runner.ts +13 -13
- package/cli/browser-evals.ts +2 -2
- package/cli/cli-update.ts +6 -6
- package/cli/cloud-client.ts +16 -16
- package/cli/cloud-dispatch.ts +2 -2
- package/cli/cloud-session.ts +12 -12
- package/cli/cloud-test-tunnel.ts +4 -4
- package/cli/command-registry.ts +7 -7
- package/cli/commands/agent-wrapper.ts +7 -7
- package/cli/commands/agent.ts +16 -16
- package/cli/commands/app-fonts.ts +3 -3
- package/cli/commands/assert.ts +10 -10
- package/cli/commands/auth.ts +5 -5
- package/cli/commands/box/checkout-plane.ts +1 -1
- package/cli/commands/box/plane-share.ts +2 -2
- package/cli/commands/box/rnx-command.ts +5 -5
- package/cli/commands/box.ts +38 -38
- package/cli/commands/camera.ts +13 -13
- package/cli/commands/cleanup.ts +13 -13
- package/cli/commands/compat.ts +7 -7
- package/cli/commands/config.ts +5 -5
- package/cli/commands/control.ts +63 -63
- package/cli/commands/cpu-profile.ts +11 -11
- package/cli/commands/daemon-mac-app.ts +10 -10
- package/cli/commands/daemon.ts +29 -27
- package/cli/commands/debug.ts +35 -35
- package/cli/commands/desktop.ts +3 -3
- package/cli/commands/detox.ts +33 -33
- package/cli/commands/device.ts +9 -9
- package/cli/commands/diagnose.ts +17 -17
- package/cli/commands/do-chain.ts +4 -4
- package/cli/commands/electron.ts +9 -9
- package/cli/commands/film.ts +9 -9
- package/cli/commands/flow.ts +49 -49
- package/cli/commands/inspect/core.ts +12 -12
- package/cli/commands/inspect/count.ts +1 -1
- package/cli/commands/inspect/describe.ts +1 -1
- package/cli/commands/inspect/env.ts +2 -2
- package/cli/commands/inspect/find.ts +2 -2
- package/cli/commands/inspect/get-layout.ts +2 -2
- package/cli/commands/inspect/keyboard.ts +1 -1
- package/cli/commands/inspect/list.ts +4 -4
- package/cli/commands/inspect/memory.ts +1 -1
- package/cli/commands/inspect/resolve-target.ts +1 -1
- package/cli/commands/inspect/screens.ts +1 -1
- package/cli/commands/inspect/shared.ts +1 -1
- package/cli/commands/inspect/tree.ts +1 -1
- package/cli/commands/inspect/url.ts +1 -1
- package/cli/commands/inspect/wait-event.ts +5 -5
- package/cli/commands/inspect.ts +82 -82
- package/cli/commands/install-cli.ts +16 -16
- package/cli/commands/install-desktop.ts +26 -26
- package/cli/commands/login.ts +11 -11
- package/cli/commands/maestro-generate.ts +10 -10
- package/cli/commands/maestro.ts +77 -67
- package/cli/commands/mode.ts +4 -4
- package/cli/commands/no-bridge-hint.ts +12 -12
- package/cli/commands/perf.ts +11 -11
- package/cli/commands/permissions.ts +5 -5
- package/cli/commands/platform.ts +21 -21
- package/cli/commands/preview.ts +5 -5
- package/cli/commands/profile.ts +12 -12
- package/cli/commands/react.ts +15 -15
- package/cli/commands/record.ts +31 -31
- package/cli/commands/report-issue.ts +4 -4
- package/cli/commands/reset.ts +2 -2
- package/cli/commands/runtime.ts +23 -23
- package/cli/commands/screenshot-command.ts +2 -2
- package/cli/commands/screenshot-layers.ts +5 -5
- package/cli/commands/screenshot-mode.ts +7 -7
- package/cli/commands/screenshot.ts +24 -24
- package/cli/commands/screenshots-capture.ts +13 -13
- package/cli/commands/screenshots.ts +6 -6
- package/cli/commands/serve.ts +12 -12
- package/cli/commands/setup.ts +25 -25
- package/cli/commands/skills.ts +30 -23
- package/cli/commands/slides.ts +20 -20
- package/cli/commands/state.ts +2 -2
- package/cli/commands/storage.ts +3 -3
- package/cli/commands/telemetry.ts +2 -2
- package/cli/commands/test.ts +15 -15
- package/cli/commands/three-mode.ts +17 -17
- package/cli/commands/timeline.ts +12 -12
- package/cli/commands/upgrade.ts +11 -11
- package/cli/commands/upload.ts +26 -26
- package/cli/commands/version.ts +7 -7
- package/cli/commands/what-happened.ts +20 -20
- package/cli/current-sim.ts +3 -3
- package/cli/desktop-companion.ts +16 -16
- package/cli/drivers/electron.ts +4 -4
- package/cli/drivers/playwright-sim-host.ts +7 -7
- package/cli/drivers/playwright.ts +4 -4
- package/cli/drivers/registry.ts +1 -1
- package/cli/drivers/types.ts +5 -5
- package/cli/flow-export.ts +1 -1
- package/cli/flow-file.ts +5 -5
- package/cli/flow-session.ts +1 -1
- package/cli/help-core.ts +13 -13
- package/cli/hints.ts +14 -14
- package/cli/inspect-notice-state.ts +1 -1
- package/cli/internal-child.ts +2 -2
- package/cli/maestro-js.ts +4 -4
- package/cli/maestro-report.ts +1 -1
- package/cli/main.ts +14 -14
- package/cli/outbound-endpoints.ts +30 -30
- package/cli/parse-args.ts +7 -7
- package/cli/privacy.ts +1 -1
- package/cli/recording-access.ts +10 -10
- package/cli/resolve-assets.ts +2 -2
- package/cli/rnx-in-process.ts +5 -5
- package/cli/run-registry.ts +1 -1
- package/cli/run-rnx-core.ts +3 -3
- package/cli/run-rnx.ts +1 -1
- package/cli/runtime-notes.ts +1 -1
- package/cli/self-invocation.ts +1 -1
- package/cli/send-to-box.ts +1 -1
- package/cli/shell-init.ts +40 -40
- package/cli/ui.ts +1 -1
- package/cli/ws-bridge.ts +22 -22
- package/detox/config-loader.ts +1 -1
- package/detox/index.ts +28 -28
- package/detox/navigation.ts +2 -2
- package/dist-lib/agent-daemon-client.cjs +12 -12
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/agent-identity.cjs +2 -2
- package/dist-lib/agent-sessions.cjs +12 -12
- package/dist-lib/attached-projects.cjs +4 -4
- package/dist-lib/auth/shared-session.cjs +4 -4
- package/dist-lib/backend-origin.cjs +1 -1
- package/dist-lib/beta.cjs +1 -1
- package/dist-lib/beta.mjs +1 -1
- package/dist-lib/bridge-constants.cjs +4 -4
- package/dist-lib/bridge-contract-input.cjs +1 -1
- package/dist-lib/bridge-contract-input.mjs +1 -1
- package/dist-lib/bridge-contract.cjs +2 -2
- package/dist-lib/bridge-contract.mjs +2 -2
- package/dist-lib/capture-contract.cjs +1 -1
- package/dist-lib/capture-contract.mjs +1 -1
- package/dist-lib/cli-constants.cjs +1 -1
- package/dist-lib/cloud-contract.cjs +1 -1
- package/dist-lib/cloud-contract.mjs +1 -1
- package/dist-lib/cloud-test-args.cjs +1 -1
- package/dist-lib/cloud-test-args.mjs +1 -1
- package/dist-lib/cloud.cjs +42 -42
- package/dist-lib/cloud.mjs +42 -42
- package/dist-lib/config.cjs +2 -2
- package/dist-lib/detox/index.cjs +29 -29
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/home-paths.cjs +12 -12
- package/dist-lib/host/bridge-host.cjs +72 -71
- package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
- package/dist-lib/host/replacement-module-handler.cjs +6 -6
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +48 -48
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/jump-to-source-native.cjs +1 -1
- package/dist-lib/menu.cjs +5 -5
- package/dist-lib/menu.mjs +5 -5
- package/dist-lib/metro-fingerprint-registry.cjs +1 -1
- package/dist-lib/metro-fingerprint-registry.mjs +1 -1
- package/dist-lib/metro-production-bundle.cjs +1 -1
- package/dist-lib/metro-production-bundle.mjs +1 -1
- package/dist-lib/metro.cjs +15 -15
- package/dist-lib/profiles.cjs +4 -4
- package/dist-lib/public-brand.cjs +3 -3
- package/dist-lib/react-native-host-modules.cjs +1 -1
- package/dist-lib/react-native-host-modules.mjs +1 -1
- package/dist-lib/render-mode.cjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +2 -1
- package/dist-lib/sdk.cjs +9 -9
- package/dist-lib/sdk.mjs +9 -9
- package/dist-lib/skills.cjs +416 -415
- package/dist-lib/swift-compile.cjs +1 -1
- package/dist-lib/swift-compile.mjs +1 -1
- package/dist-lib/swift-package.cjs +1 -1
- package/dist-lib/swift-package.mjs +1 -1
- package/dist-lib/swift-submit.cjs +1 -1
- package/dist-lib/swift-submit.mjs +1 -1
- package/dist-lib/swift.cjs +1 -1
- package/dist-lib/vite.cjs +24 -24
- package/package.json +1 -1
- package/scripts/dev-server-scanner.ts +4 -3
- package/skills/contrast/SKILL.md +75 -75
- package/skills/{rnx-debug → peach-debug}/SKILL.md +90 -90
- package/skills/{rnx-perf → peach-perf}/SKILL.md +13 -13
- package/skills/{rnx-setup → peach-setup}/SKILL.md +60 -60
- package/skills/{rnx-test → peach-test}/SKILL.md +44 -44
- package/skills/{rnx-visual → peach-visual}/SKILL.md +26 -26
- package/src/agent-daemon-client.ts +5 -5
- package/src/agent-events.ts +1 -1
- package/src/agent-identity.ts +1 -1
- package/src/agent-sessions.ts +13 -13
- package/src/attached-projects.ts +1 -1
- package/src/auth/shared-session.ts +1 -1
- package/src/bridge-constants.ts +3 -3
- package/src/bridge-contract-input.ts +1 -1
- package/src/bridge-contract.ts +3 -3
- package/src/cli-version.ts +6 -6
- package/src/cloud.ts +40 -40
- package/src/config.ts +1 -1
- package/src/connect.ts +6 -6
- package/src/dev-server-open.ts +8 -8
- package/src/disk-cleanup.ts +3 -3
- package/src/home-paths.ts +26 -26
- package/src/host/agent-host.ts +3 -3
- package/src/host/bridge-host.ts +33 -33
- package/src/host/fetch-proxy-handler.ts +1 -1
- package/src/host/plane-host.ts +2 -2
- package/src/host/replacement-module-handler.ts +2 -2
- package/src/menu.ts +2 -2
- package/src/metro-plugin.ts +7 -7
- package/src/metro-production-bundle.ts +2 -2
- package/src/native-dev-bundle-url.ts +2 -2
- package/src/playwright-host-log.ts +1 -1
- package/src/public-brand.ts +2 -2
- package/src/react-native-host-modules.ts +1 -1
- package/src/rolldown-plugin-swift.ts +3 -3
- package/src/runtime-assets.ts +11 -11
- package/src/runtime-delivery.ts +6 -6
- package/src/screenshots/orchestrate.ts +1 -1
- package/src/screenshots/org-deck.ts +1 -1
- package/src/screenshots/schema.ts +2 -2
- package/src/sim-client.ts +1 -1
- package/src/skills/builtin/compat-check.ts +2 -2
- package/src/vite-plugin-one.ts +8 -8
- package/src/vite-plugin-swift.ts +3 -3
- package/src/vite-plugin.ts +6 -19
package/cli/commands/agent.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// user-facing `
|
|
1
|
+
// user-facing `peach agent` command tree.
|
|
2
2
|
//
|
|
3
3
|
// session-lifecycle commands (start, prompt, end, watch) route through
|
|
4
4
|
// the bridge daemon — the daemon owns the single FIFO reader per
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
//
|
|
9
9
|
// disk-only inspection commands (projects, sessions, paths, transcript,
|
|
10
10
|
// attach, project show/remove) read the attached-projects JSON directly —
|
|
11
|
-
// no daemon needed, so `
|
|
11
|
+
// no daemon needed, so `peach agent projects` stays fast when no
|
|
12
12
|
// wrapper is live.
|
|
13
13
|
|
|
14
14
|
import fs from 'node:fs'
|
|
@@ -109,7 +109,7 @@ async function withDaemon<T>(fn: (client: AgentDaemonClient) => Promise<T>): Pro
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
function printHelp(): void {
|
|
112
|
-
// delegate to the meta-driven renderer so bare `
|
|
112
|
+
// delegate to the meta-driven renderer so bare `peach agent`, `--help`,
|
|
113
113
|
// and `unknown subcommand` paths all show the same rich help (header,
|
|
114
114
|
// options, examples) instead of a sparse hand-coded summary.
|
|
115
115
|
printCommandHelp('agent')
|
|
@@ -153,7 +153,7 @@ async function cmdAttach(argv: string[]): Promise<number> {
|
|
|
153
153
|
const dir = positional[0]
|
|
154
154
|
if (!dir) {
|
|
155
155
|
process.stderr.write(
|
|
156
|
-
'usage:
|
|
156
|
+
'usage: peach agent attach <dir> [--name X] [--provider codex|claude]\n',
|
|
157
157
|
)
|
|
158
158
|
return 2
|
|
159
159
|
}
|
|
@@ -180,7 +180,7 @@ async function cmdAttach(argv: string[]): Promise<number> {
|
|
|
180
180
|
async function cmdProjects(): Promise<number> {
|
|
181
181
|
const projects = [...listProjects()].sort((a, b) => b.updatedAt - a.updatedAt)
|
|
182
182
|
if (projects.length === 0) {
|
|
183
|
-
process.stdout.write('no attached projects yet. try `
|
|
183
|
+
process.stdout.write('no attached projects yet. try `peach agent attach <dir>`.\n')
|
|
184
184
|
return 0
|
|
185
185
|
}
|
|
186
186
|
const header = `${'ID'.padEnd(18)} ${'NAME'.padEnd(20)} ${'FRAMEWORK'.padEnd(10)} ${'PROVIDER'.padEnd(8)} ${'7D COST'.padEnd(10)} CWD\n`
|
|
@@ -199,7 +199,7 @@ async function cmdProject(argv: string[]): Promise<number> {
|
|
|
199
199
|
const [action, id] = argv
|
|
200
200
|
if (action === 'show') {
|
|
201
201
|
if (!id) {
|
|
202
|
-
process.stderr.write('usage:
|
|
202
|
+
process.stderr.write('usage: peach agent project show <id>\n')
|
|
203
203
|
return 2
|
|
204
204
|
}
|
|
205
205
|
const project = findProjectById(id)
|
|
@@ -212,14 +212,14 @@ async function cmdProject(argv: string[]): Promise<number> {
|
|
|
212
212
|
}
|
|
213
213
|
if (action === 'remove') {
|
|
214
214
|
if (!id) {
|
|
215
|
-
process.stderr.write('usage:
|
|
215
|
+
process.stderr.write('usage: peach agent project remove <id>\n')
|
|
216
216
|
return 2
|
|
217
217
|
}
|
|
218
218
|
deleteProject(id)
|
|
219
219
|
process.stdout.write(`✓ removed project ${id}\n`)
|
|
220
220
|
return 0
|
|
221
221
|
}
|
|
222
|
-
process.stderr.write('usage:
|
|
222
|
+
process.stderr.write('usage: peach agent project [show|remove] <id>\n')
|
|
223
223
|
return 2
|
|
224
224
|
}
|
|
225
225
|
|
|
@@ -260,7 +260,7 @@ async function cmdStart(argv: string[]): Promise<number> {
|
|
|
260
260
|
const projectId = positional[0]
|
|
261
261
|
if (!projectId) {
|
|
262
262
|
process.stderr.write(
|
|
263
|
-
'usage:
|
|
263
|
+
'usage: peach agent start <projectId> [--provider codex|claude] [--fresh-thread]\n',
|
|
264
264
|
)
|
|
265
265
|
return 2
|
|
266
266
|
}
|
|
@@ -281,8 +281,8 @@ async function cmdStart(argv: string[]): Promise<number> {
|
|
|
281
281
|
` project: ${project.name} (${project.id})\n` +
|
|
282
282
|
` provider: ${result.session.provider}\n` +
|
|
283
283
|
` pid: ${result.wrapperPid}\n` +
|
|
284
|
-
` prompt with:
|
|
285
|
-
` watch events:
|
|
284
|
+
` prompt with: peach agent prompt ${result.session.id} "<text>"\n` +
|
|
285
|
+
` watch events: peach agent watch ${result.session.id}\n`,
|
|
286
286
|
)
|
|
287
287
|
return 0
|
|
288
288
|
}
|
|
@@ -291,7 +291,7 @@ async function cmdPrompt(argv: string[]): Promise<number> {
|
|
|
291
291
|
const [sessionId, ...rest] = argv
|
|
292
292
|
const text = rest.join(' ')
|
|
293
293
|
if (!sessionId || !text) {
|
|
294
|
-
process.stderr.write('usage:
|
|
294
|
+
process.stderr.write('usage: peach agent prompt <sessionId> "<text>"\n')
|
|
295
295
|
return 2
|
|
296
296
|
}
|
|
297
297
|
await withDaemon((client) => client.sendPrompt(sessionId, { text }))
|
|
@@ -302,7 +302,7 @@ async function cmdPrompt(argv: string[]): Promise<number> {
|
|
|
302
302
|
async function cmdWatch(argv: string[]): Promise<number> {
|
|
303
303
|
const sessionId = argv[0]
|
|
304
304
|
if (!sessionId) {
|
|
305
|
-
process.stderr.write('usage:
|
|
305
|
+
process.stderr.write('usage: peach agent watch <sessionId>\n')
|
|
306
306
|
return 2
|
|
307
307
|
}
|
|
308
308
|
process.stdout.write(`watching ${sessionId}\n---\n`)
|
|
@@ -321,7 +321,7 @@ async function cmdWatch(argv: string[]): Promise<number> {
|
|
|
321
321
|
process.stdout.write(formatEvent(event) + '\n')
|
|
322
322
|
})
|
|
323
323
|
const offDisconnect = client.onDisconnect(() => {
|
|
324
|
-
process.stderr.write('\n[
|
|
324
|
+
process.stderr.write('\n[peach] daemon disconnected; exiting watch.\n')
|
|
325
325
|
resolveExit(1)
|
|
326
326
|
})
|
|
327
327
|
try {
|
|
@@ -379,7 +379,7 @@ function formatEvent(ev: AgentEvent): string {
|
|
|
379
379
|
async function cmdTranscript(argv: string[]): Promise<number> {
|
|
380
380
|
const sessionId = argv[0]
|
|
381
381
|
if (!sessionId) {
|
|
382
|
-
process.stderr.write('usage:
|
|
382
|
+
process.stderr.write('usage: peach agent transcript <sessionId>\n')
|
|
383
383
|
return 2
|
|
384
384
|
}
|
|
385
385
|
const p = transcriptPath(sessionId)
|
|
@@ -394,7 +394,7 @@ async function cmdTranscript(argv: string[]): Promise<number> {
|
|
|
394
394
|
async function cmdEnd(argv: string[]): Promise<number> {
|
|
395
395
|
const sessionId = argv[0]
|
|
396
396
|
if (!sessionId) {
|
|
397
|
-
process.stderr.write('usage:
|
|
397
|
+
process.stderr.write('usage: peach agent end <sessionId>\n')
|
|
398
398
|
return 2
|
|
399
399
|
}
|
|
400
400
|
await withDaemon((client) => client.endSession(sessionId))
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//
|
|
1
|
+
// peach app-fonts stage — fetch the config-plugin fonts declared on an
|
|
2
2
|
// engine `?appFonts=` wire, gzip them, and merge asset descriptors into a
|
|
3
3
|
// build's asset manifest.
|
|
4
4
|
//
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// after the asset capture; the descriptors land in the same manifest the
|
|
10
10
|
// upload step already PUTs, keyed by sha256(url) so the rendered build resolves
|
|
11
11
|
// each font through /api/preview/share/fetch?url=<fontUrl> identically to the
|
|
12
|
-
// preview path (`
|
|
12
|
+
// preview path (`peach record upload`). both pipelines share fetchAppFontFiles.
|
|
13
13
|
|
|
14
14
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
15
15
|
import { join } from 'node:path'
|
|
@@ -39,7 +39,7 @@ async function stage(args: string[]): Promise<void> {
|
|
|
39
39
|
const manifestPath = flag(args, 'manifest')
|
|
40
40
|
if (!outDir || !manifestPath) {
|
|
41
41
|
console.error(
|
|
42
|
-
' usage:
|
|
42
|
+
' usage: peach app-fonts stage --wire <wire> --out-dir <dir> --manifest <path>',
|
|
43
43
|
)
|
|
44
44
|
rnxExit(1)
|
|
45
45
|
}
|
package/cli/commands/assert.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// `
|
|
1
|
+
// `peach assert <verb> <verb-args...> <assertion-flags...>`
|
|
2
2
|
//
|
|
3
3
|
// thin wrapper that runs any read verb with --json forced on, captures the
|
|
4
4
|
// JSON payload, and converts predicate flags into a bisect-friendly exit
|
|
@@ -18,7 +18,7 @@ import { rnxExit } from '../run-rnx'
|
|
|
18
18
|
import { rnxSelfInvocation } from '../self-invocation'
|
|
19
19
|
|
|
20
20
|
// READ_VERBS lists every command whose stdout we know is valid JSON under
|
|
21
|
-
// --json. keeping this explicit means `
|
|
21
|
+
// --json. keeping this explicit means `peach assert tap 100 200` fails
|
|
22
22
|
// fast with a clear message instead of silently running a write verb and
|
|
23
23
|
// trying to parse " tapped 100,200" as JSON.
|
|
24
24
|
const READ_VERBS = new Set([
|
|
@@ -47,7 +47,7 @@ const READ_VERBS = new Set([
|
|
|
47
47
|
|
|
48
48
|
// assertion flags that assert recognizes. everything else is passed through
|
|
49
49
|
// to the inner verb untouched. keeping this as a set (rather than positional
|
|
50
|
-
// parsing) means `
|
|
50
|
+
// parsing) means `peach assert find --testid X --count 4` reads naturally.
|
|
51
51
|
const BOOL_ASSERT_FLAGS = new Set([
|
|
52
52
|
'--exists',
|
|
53
53
|
'--empty',
|
|
@@ -502,10 +502,10 @@ function sleep(ms: number): Promise<void> {
|
|
|
502
502
|
|
|
503
503
|
function printHelp(): void {
|
|
504
504
|
process.stdout.write(`
|
|
505
|
-
|
|
505
|
+
peach assert — run any read verb and convert its output into a bisect-friendly exit code.
|
|
506
506
|
|
|
507
507
|
usage:
|
|
508
|
-
|
|
508
|
+
peach assert <verb> <verb-args...> <assertion-flags...>
|
|
509
509
|
|
|
510
510
|
exit codes:
|
|
511
511
|
0 assertion passed (git bisect: good)
|
|
@@ -533,10 +533,10 @@ assertion flags (AND'd when multiple are given):
|
|
|
533
533
|
--quiet suppress the one-line pass/fail summary
|
|
534
534
|
|
|
535
535
|
examples:
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
536
|
+
peach assert find --testid set-max-input --count 4
|
|
537
|
+
peach assert describe --contains "swap-form-header"
|
|
538
|
+
peach assert get errors --count 0
|
|
539
|
+
peach assert find --testid submit --exists --within 3000
|
|
540
|
+
peach assert describe --jq '.tree | contains("swap-form-header")'
|
|
541
541
|
`)
|
|
542
542
|
}
|
package/cli/commands/auth.ts
CHANGED
|
@@ -7,13 +7,13 @@ import { rnxExit } from '../run-rnx'
|
|
|
7
7
|
|
|
8
8
|
function printHelp(): void {
|
|
9
9
|
console.log(`
|
|
10
|
-
|
|
10
|
+
peach auth — inspect CLI auth
|
|
11
11
|
|
|
12
12
|
usage:
|
|
13
|
-
|
|
13
|
+
peach auth
|
|
14
14
|
|
|
15
|
-
API keys are read from
|
|
16
|
-
Use \`
|
|
15
|
+
API keys are read from PEACH_API_KEY.
|
|
16
|
+
Use \`peach login\` to sign in and \`peach logout\` to clear the desktop session.
|
|
17
17
|
`)
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -21,7 +21,7 @@ async function printStatus(): Promise<number> {
|
|
|
21
21
|
const auth = resolveCliAuth()
|
|
22
22
|
if (!auth) {
|
|
23
23
|
console.log(' not signed in')
|
|
24
|
-
console.log(' set
|
|
24
|
+
console.log(' set PEACH_API_KEY=sk_rnx_... or run `peach login`')
|
|
25
25
|
return 1
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -62,7 +62,7 @@ const LIST_FILES_TIMEOUT_MS = 10_000
|
|
|
62
62
|
// `touch created.txt` working and the box telling the user it has no file set
|
|
63
63
|
// to serve: measured here as exit 1 in 103ms, which is what took the box
|
|
64
64
|
// conformance suite red under a load average of 47. the conditions the two
|
|
65
|
-
// tests in test/
|
|
65
|
+
// tests in test/peachBoxCheckoutPlane.test.ts hold (a git that never answers, a
|
|
66
66
|
// git that always answers with nothing) are permanent, so they still fail, and
|
|
67
67
|
// they fail naming the same thing.
|
|
68
68
|
const LIST_FILES_ATTEMPTS = 3
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
// resolveBridgeWorld reports source 'default' and hands back
|
|
18
18
|
// DEFAULT_SOOTSIM_BRIDGE_PORT, which on a machine running a real daemon is
|
|
19
19
|
// that daemon. registering there would attach a test's or a benchmark's box to
|
|
20
|
-
// another session's daemon, which is exactly what a private
|
|
20
|
+
// another session's daemon, which is exactly what a private PEACH_HOME
|
|
21
21
|
// exists to prevent. so a guessed port is refused by name.
|
|
22
22
|
|
|
23
23
|
import { WebSocket } from 'ws'
|
|
@@ -99,7 +99,7 @@ export function sharePlane(root: string, opts: { port?: number } = {}): PlaneSha
|
|
|
99
99
|
socket.on('open', () => {
|
|
100
100
|
// node sends no Origin, so this socket is already cli-role. a browser
|
|
101
101
|
// client has to send bridge:hello to be corrected off the provisional
|
|
102
|
-
// sim role; see plans/
|
|
102
|
+
// sim role; see plans/peach-cloud/transport-decision.md.
|
|
103
103
|
socket?.send(JSON.stringify({ type: 'plane:register', id: 'register', root }))
|
|
104
104
|
})
|
|
105
105
|
socket.on('message', (data) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// step 4: RNX operations registered as just-bash commands.
|
|
2
2
|
//
|
|
3
|
-
// `
|
|
3
|
+
// `peach` inside a box shell is the same CLI, re-invoked as a child process with
|
|
4
4
|
// the shell's own cwd. that is deliberate — spelling the verbs a second time
|
|
5
5
|
// in shell-command form would be a second implementation of every command, and
|
|
6
6
|
// the spec is explicit that the CLI and the in-shell commands are clients of
|
|
@@ -12,10 +12,10 @@ import { defineCommand, type CustomCommand } from '@peach/box/shell'
|
|
|
12
12
|
import { PROJECT_MOUNT } from '@peach/box/shell'
|
|
13
13
|
import { rnxPublicBrand } from '../../../src/public-brand'
|
|
14
14
|
import { rnxSelfInvocation } from '../../self-invocation'
|
|
15
|
-
import {
|
|
15
|
+
import { PEACH_PLACEMENT_ENV } from '../../ws-bridge'
|
|
16
16
|
|
|
17
17
|
// map a shell path under /project back onto the real checkout, so a child
|
|
18
|
-
// process started from `cd src &&
|
|
18
|
+
// process started from `cd src && peach describe` sees the directory the user
|
|
19
19
|
// is actually standing in.
|
|
20
20
|
function hostCwd(root: string, shellCwd: string): string {
|
|
21
21
|
if (shellCwd === PROJECT_MOUNT) return root
|
|
@@ -33,8 +33,8 @@ export function createRnxCommand(options: {
|
|
|
33
33
|
cwd: hostCwd(options.root, options.shellCwd()),
|
|
34
34
|
// mark the child as running inside a local box. this is the only thing
|
|
35
35
|
// that sets a placement — nothing infers one from cwd — and it is what
|
|
36
|
-
// lets `
|
|
37
|
-
env: { ...process.env, [
|
|
36
|
+
// lets `peach box` refuse to nest inside itself.
|
|
37
|
+
env: { ...process.env, [PEACH_PLACEMENT_ENV]: 'local-box' },
|
|
38
38
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
39
39
|
})
|
|
40
40
|
|
package/cli/commands/box.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
// `
|
|
1
|
+
// `peach box` — the box environment.
|
|
2
2
|
//
|
|
3
|
-
// bare `
|
|
3
|
+
// bare `peach box` is LOCAL and mounts the current checkout at /project. cloud
|
|
4
4
|
// create provisions through the shared client and enters the same shell unless
|
|
5
5
|
// --json asks for a noninteractive result. connect asks the account service to
|
|
6
6
|
// turn a Box name into an authorized reference and enters that same shell, so
|
|
7
7
|
// this process never stores a Box token.
|
|
8
8
|
//
|
|
9
9
|
// every verb that acts on an existing Box takes it the same way: the id or
|
|
10
|
-
// name on the command line, else the one `
|
|
10
|
+
// name on the command line, else the one `peach box use` remembered. see
|
|
11
11
|
// boxTarget below.
|
|
12
12
|
|
|
13
13
|
import { spawn } from 'node:child_process'
|
|
@@ -52,8 +52,8 @@ interface BoxCommandOptions {
|
|
|
52
52
|
// no default. a cloud box service has to be deployed before one can exist, and
|
|
53
53
|
// defaulting to an origin that answers nothing would advertise a capability
|
|
54
54
|
// that always fails.
|
|
55
|
-
const ENDPOINT_ENV = '
|
|
56
|
-
const API_ORIGIN_ENV = '
|
|
55
|
+
const ENDPOINT_ENV = 'PEACH_BOX_ENDPOINT'
|
|
56
|
+
const API_ORIGIN_ENV = 'PEACH_API_ORIGIN'
|
|
57
57
|
const BOX_TEMPLATES = new BoxTemplateRegistry([
|
|
58
58
|
terminalBoxTemplate,
|
|
59
59
|
sourceControlBoxTemplate,
|
|
@@ -135,7 +135,7 @@ function namedBox(args: string[]): string | undefined {
|
|
|
135
135
|
* which Box a verb acts on.
|
|
136
136
|
*
|
|
137
137
|
* one order for every verb that acts on a Box that already exists: the id or
|
|
138
|
-
* name on the command line, else the one `
|
|
138
|
+
* name on the command line, else the one `peach box use` remembered, else
|
|
139
139
|
* nothing and the reason printed here.
|
|
140
140
|
*/
|
|
141
141
|
function boxTarget(args: string[], label: string): string | null {
|
|
@@ -143,16 +143,16 @@ function boxTarget(args: string[], label: string): string | null {
|
|
|
143
143
|
if (named) return named
|
|
144
144
|
const stored = readDefaultBox()
|
|
145
145
|
if (stored) return stored.boxId
|
|
146
|
-
const
|
|
146
|
+
const peach = rnxPublicBrand.commandName
|
|
147
147
|
console.error(
|
|
148
|
-
` ${
|
|
149
|
-
` or set one for every box command with \`${
|
|
148
|
+
` ${peach} box ${label}: which box? pass its ID or name, as \`${peach} box ls\` prints it,\n` +
|
|
149
|
+
` or set one for every box command with \`${peach} box use <id-or-name>\`.`,
|
|
150
150
|
)
|
|
151
151
|
return null
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
/**
|
|
155
|
-
* `
|
|
155
|
+
* `peach box use` — the Box every other verb acts on when none is named.
|
|
156
156
|
*
|
|
157
157
|
* it resolves through the account service exactly as connect does, because
|
|
158
158
|
* that is the only thing that knows which account owns a Box, and a default
|
|
@@ -161,13 +161,13 @@ function boxTarget(args: string[], label: string): string | null {
|
|
|
161
161
|
* token is resolved again per command and never reaches disk.
|
|
162
162
|
*/
|
|
163
163
|
async function runCloudBoxUse(args: string[]): Promise<number> {
|
|
164
|
-
const
|
|
164
|
+
const peach = rnxPublicBrand.commandName
|
|
165
165
|
const named = namedBox(args)
|
|
166
166
|
if (!named) {
|
|
167
167
|
const stored = readDefaultBox()
|
|
168
168
|
if (!stored) {
|
|
169
169
|
console.log(
|
|
170
|
-
` no default box. \`${
|
|
170
|
+
` no default box. \`${peach} box use <id-or-name>\` targets one, and every\n` +
|
|
171
171
|
` other box command then acts on it when you name none.`,
|
|
172
172
|
)
|
|
173
173
|
return 0
|
|
@@ -201,7 +201,7 @@ async function runCloudBoxUse(args: string[]): Promise<number> {
|
|
|
201
201
|
return 0
|
|
202
202
|
} catch (error) {
|
|
203
203
|
if (error instanceof BoxClientError) {
|
|
204
|
-
console.error(` ${
|
|
204
|
+
console.error(` ${peach} box use: ${error.message}`)
|
|
205
205
|
return 1
|
|
206
206
|
}
|
|
207
207
|
throw error
|
|
@@ -214,7 +214,7 @@ function requireEndpoint(args: string[], label: string): string | null {
|
|
|
214
214
|
|
|
215
215
|
/**
|
|
216
216
|
* the cloud box service this command talks to, or null with the reason
|
|
217
|
-
* printed. exported because `
|
|
217
|
+
* printed. exported because `peach box send` creates a box too, and a second
|
|
218
218
|
* spelling of where boxes live is a second place to configure.
|
|
219
219
|
*/
|
|
220
220
|
export function cloudBoxEndpoint(args: string[], command: string): string | null {
|
|
@@ -232,11 +232,11 @@ export function cloudBoxApiOrigin(args: string[], auth: CliAuth): string {
|
|
|
232
232
|
return apiOrigin(args, auth)
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
-
// `
|
|
235
|
+
// `peach box send` — the CLI half of "send to box". the shell's rail button runs
|
|
236
236
|
// the same function inside the daemon; this is the same send for an agent or a
|
|
237
237
|
// terminal, and it prints the id and URL either way.
|
|
238
238
|
async function runBoxSend(args: string[], opts: BoxCommandOptions): Promise<number> {
|
|
239
|
-
const
|
|
239
|
+
const peach = rnxPublicBrand.commandName
|
|
240
240
|
const { auth, header } = authHeaderOrExit('box send')
|
|
241
241
|
const accountId = cloudAccountIdOrExit(auth)
|
|
242
242
|
const { createBridgeFromParsed, parseBridgeCliArgs } = await import('../ws-bridge')
|
|
@@ -263,7 +263,7 @@ async function runBoxSend(args: string[], opts: BoxCommandOptions): Promise<numb
|
|
|
263
263
|
return 0
|
|
264
264
|
} catch (error) {
|
|
265
265
|
console.error(
|
|
266
|
-
` ${
|
|
266
|
+
` ${peach} box send: ${error instanceof Error ? error.message : String(error)}`,
|
|
267
267
|
)
|
|
268
268
|
return 1
|
|
269
269
|
} finally {
|
|
@@ -272,7 +272,7 @@ async function runBoxSend(args: string[], opts: BoxCommandOptions): Promise<numb
|
|
|
272
272
|
}
|
|
273
273
|
|
|
274
274
|
async function runCloudBoxStop(args: string[], purge: boolean): Promise<number> {
|
|
275
|
-
const
|
|
275
|
+
const peach = rnxPublicBrand.commandName
|
|
276
276
|
const label = purge ? 'delete' : 'stop'
|
|
277
277
|
const endpoint = requireEndpoint(args, label)
|
|
278
278
|
if (!endpoint) return 1
|
|
@@ -303,7 +303,7 @@ async function runCloudBoxStop(args: string[], purge: boolean): Promise<number>
|
|
|
303
303
|
return 0
|
|
304
304
|
} catch (error) {
|
|
305
305
|
if (error instanceof BoxClientError) {
|
|
306
|
-
console.error(` ${
|
|
306
|
+
console.error(` ${peach} box ${label}: ${error.message}`)
|
|
307
307
|
return 1
|
|
308
308
|
}
|
|
309
309
|
throw error
|
|
@@ -311,7 +311,7 @@ async function runCloudBoxStop(args: string[], purge: boolean): Promise<number>
|
|
|
311
311
|
}
|
|
312
312
|
|
|
313
313
|
async function runCloudBoxList(args: string[]): Promise<number> {
|
|
314
|
-
const
|
|
314
|
+
const peach = rnxPublicBrand.commandName
|
|
315
315
|
const { auth, header } = authHeaderOrExit('box ls')
|
|
316
316
|
const apiKey = header.replace(/^Bearer /, '')
|
|
317
317
|
const accountId = cloudAccountIdOrExit(auth)
|
|
@@ -336,7 +336,7 @@ async function runCloudBoxList(args: string[]): Promise<number> {
|
|
|
336
336
|
return 0
|
|
337
337
|
} catch (error) {
|
|
338
338
|
if (error instanceof BoxClientError) {
|
|
339
|
-
console.error(` ${
|
|
339
|
+
console.error(` ${peach} box ls: ${error.message}`)
|
|
340
340
|
return 1
|
|
341
341
|
}
|
|
342
342
|
throw error
|
|
@@ -344,7 +344,7 @@ async function runCloudBoxList(args: string[]): Promise<number> {
|
|
|
344
344
|
}
|
|
345
345
|
|
|
346
346
|
async function runCloudBoxConnect(args: string[]): Promise<number> {
|
|
347
|
-
const
|
|
347
|
+
const peach = rnxPublicBrand.commandName
|
|
348
348
|
const json = args.includes('--json')
|
|
349
349
|
const boxId = boxTarget(args, 'connect')
|
|
350
350
|
if (!boxId) return 1
|
|
@@ -392,7 +392,7 @@ async function runCloudBoxConnect(args: string[]): Promise<number> {
|
|
|
392
392
|
return await repl(session, '')
|
|
393
393
|
} catch (error) {
|
|
394
394
|
if (error instanceof BoxClientError) {
|
|
395
|
-
console.error(` ${
|
|
395
|
+
console.error(` ${peach} box connect: ${error.message}`)
|
|
396
396
|
return 1
|
|
397
397
|
}
|
|
398
398
|
throw error
|
|
@@ -415,7 +415,7 @@ async function runCloudBoxConnect(args: string[]): Promise<number> {
|
|
|
415
415
|
* makes it a link somebody can keep, paste, or reload.
|
|
416
416
|
*/
|
|
417
417
|
async function runCloudBoxOpen(args: string[]): Promise<number> {
|
|
418
|
-
const
|
|
418
|
+
const peach = rnxPublicBrand.commandName
|
|
419
419
|
const json = args.includes('--json')
|
|
420
420
|
const print = args.includes('--print')
|
|
421
421
|
const boxId = boxTarget(args, 'open')
|
|
@@ -458,7 +458,7 @@ async function runCloudBoxOpen(args: string[]): Promise<number> {
|
|
|
458
458
|
const url = box.description.pageUrl
|
|
459
459
|
if (!url) {
|
|
460
460
|
console.error(
|
|
461
|
-
` ${
|
|
461
|
+
` ${peach} box open: this box service has no page origin configured, so it has no page to open.`,
|
|
462
462
|
)
|
|
463
463
|
return 1
|
|
464
464
|
}
|
|
@@ -473,7 +473,7 @@ async function runCloudBoxOpen(args: string[]): Promise<number> {
|
|
|
473
473
|
return await openInBrowser(url)
|
|
474
474
|
} catch (error) {
|
|
475
475
|
if (error instanceof BoxClientError) {
|
|
476
|
-
console.error(` ${
|
|
476
|
+
console.error(` ${peach} box open: ${error.message}`)
|
|
477
477
|
return 1
|
|
478
478
|
}
|
|
479
479
|
throw error
|
|
@@ -520,7 +520,7 @@ function cloudBoxClient(options: {
|
|
|
520
520
|
/**
|
|
521
521
|
* create a Box that runs an app somebody already built.
|
|
522
522
|
*
|
|
523
|
-
* `box create --bundle` reaches this with a file from disk and `
|
|
523
|
+
* `box create --bundle` reaches this with a file from disk and `peach box send`
|
|
524
524
|
* with the artifact it just produced. both are the same box: the bundle is
|
|
525
525
|
* the whole project, the provenance says so, and the page that opens it boots
|
|
526
526
|
* that bundle instead of building anything.
|
|
@@ -545,13 +545,13 @@ async function runCloudBoxCreate(
|
|
|
545
545
|
args: string[],
|
|
546
546
|
opts: BoxCommandOptions,
|
|
547
547
|
): Promise<number> {
|
|
548
|
-
const
|
|
548
|
+
const peach = rnxPublicBrand.commandName
|
|
549
549
|
const json = args.includes('--json')
|
|
550
550
|
const endpoint = flag(args, 'endpoint') ?? process.env[ENDPOINT_ENV]?.trim()
|
|
551
551
|
if (!endpoint) {
|
|
552
552
|
console.error(
|
|
553
|
-
` ${
|
|
554
|
-
` set ${ENDPOINT_ENV} or pass --endpoint=<url>. \`${
|
|
553
|
+
` ${peach} box create: no cloud box service to create a box in.\n` +
|
|
554
|
+
` set ${ENDPOINT_ENV} or pass --endpoint=<url>. \`${peach} box\` runs the\n` +
|
|
555
555
|
` same shell locally over this checkout.`,
|
|
556
556
|
)
|
|
557
557
|
return 1
|
|
@@ -563,7 +563,7 @@ async function runCloudBoxCreate(
|
|
|
563
563
|
const namedTemplate = flag(args, 'template')
|
|
564
564
|
if (bundlePath && namedTemplate) {
|
|
565
565
|
console.error(
|
|
566
|
-
` ${
|
|
566
|
+
` ${peach} box create: --bundle creates a ${prebuiltBundleBoxTemplate.name} box,\n` +
|
|
567
567
|
` so it cannot be combined with --template ${namedTemplate}.`,
|
|
568
568
|
)
|
|
569
569
|
return 1
|
|
@@ -576,7 +576,7 @@ async function runCloudBoxCreate(
|
|
|
576
576
|
template = BOX_TEMPLATES.resolve({ name: templateName })
|
|
577
577
|
} catch (error) {
|
|
578
578
|
console.error(
|
|
579
|
-
` ${
|
|
579
|
+
` ${peach} box create: ${error instanceof Error ? error.message : String(error)}.`,
|
|
580
580
|
)
|
|
581
581
|
return 1
|
|
582
582
|
}
|
|
@@ -597,14 +597,14 @@ async function runCloudBoxCreate(
|
|
|
597
597
|
artifact = await produceCloudBoxArtifact(resolved)
|
|
598
598
|
} catch (error) {
|
|
599
599
|
console.error(
|
|
600
|
-
` ${
|
|
600
|
+
` ${peach} box create: could not prepare the bundle at ${resolved}: ${
|
|
601
601
|
error instanceof Error ? error.message : String(error)
|
|
602
602
|
}`,
|
|
603
603
|
)
|
|
604
604
|
return 1
|
|
605
605
|
}
|
|
606
606
|
if (artifact.bytes.byteLength === 0) {
|
|
607
|
-
console.error(` ${
|
|
607
|
+
console.error(` ${peach} box create: the bundle at ${resolved} is empty.`)
|
|
608
608
|
return 1
|
|
609
609
|
}
|
|
610
610
|
for (const warning of artifact.warnings) console.error(` ${warning}`)
|
|
@@ -614,7 +614,7 @@ async function runCloudBoxCreate(
|
|
|
614
614
|
const root = findRepositoryPackageRoot(process.cwd())
|
|
615
615
|
if (!root) {
|
|
616
616
|
console.error(
|
|
617
|
-
` ${
|
|
617
|
+
` ${peach} box create: no project here. a box is created from a checkout,\n` +
|
|
618
618
|
` so run this inside a git repository that has a package.json.`,
|
|
619
619
|
)
|
|
620
620
|
return 1
|
|
@@ -624,7 +624,7 @@ async function runCloudBoxCreate(
|
|
|
624
624
|
await checkout.refresh()
|
|
625
625
|
} catch (error) {
|
|
626
626
|
if (error instanceof CheckoutRequiresGitError) {
|
|
627
|
-
console.error(` ${
|
|
627
|
+
console.error(` ${peach} box create: ${error.message}`)
|
|
628
628
|
return 1
|
|
629
629
|
}
|
|
630
630
|
throw error
|
|
@@ -703,7 +703,7 @@ async function runCloudBoxCreate(
|
|
|
703
703
|
return await repl(session, '')
|
|
704
704
|
} catch (error) {
|
|
705
705
|
if (error instanceof BoxClientError) {
|
|
706
|
-
console.error(` ${
|
|
706
|
+
console.error(` ${peach} box create: ${error.message}`)
|
|
707
707
|
return 1
|
|
708
708
|
}
|
|
709
709
|
throw error
|
|
@@ -830,7 +830,7 @@ async function repl(
|
|
|
830
830
|
): Promise<number> {
|
|
831
831
|
const rl = createInterface({ input: process.stdin, output: process.stdout })
|
|
832
832
|
|
|
833
|
-
// enter where the user was standing, so `
|
|
833
|
+
// enter where the user was standing, so `peach box` from a subdirectory does
|
|
834
834
|
// not silently teleport them to the project root.
|
|
835
835
|
if (startRelative && !startRelative.startsWith('..')) {
|
|
836
836
|
await session.exec(`cd ${JSON.stringify(startRelative)}`)
|
package/cli/commands/camera.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//
|
|
1
|
+
// peach camera drives the host-owned camera source. fixture video uses the
|
|
2
2
|
// same hidden video, canvas frame pump, ImageBitmap transfer, and
|
|
3
3
|
// BarcodeDetector loop as a real device camera. `do scan` remains the narrow
|
|
4
4
|
// synthetic special case for callers that have a payload string but no media.
|
|
@@ -22,13 +22,13 @@ const VIDEO_EXTENSIONS = new Set(['.m4v', '.mov', '.mp4', '.webm'])
|
|
|
22
22
|
|
|
23
23
|
function printHelp() {
|
|
24
24
|
console.log(`
|
|
25
|
-
|
|
25
|
+
peach camera — control the simulator camera source
|
|
26
26
|
|
|
27
27
|
usage:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
peach camera play <file> [--loop] [--rate <number>]
|
|
29
|
+
peach camera stop
|
|
30
|
+
peach camera status [--json]
|
|
31
|
+
peach do scan <text> [--format <web-format>]
|
|
32
32
|
|
|
33
33
|
subcommands:
|
|
34
34
|
play <file> replace the active camera source with a local video fixture
|
|
@@ -45,11 +45,11 @@ options:
|
|
|
45
45
|
--port <p> WS bridge port (default: 7668)
|
|
46
46
|
|
|
47
47
|
examples:
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
peach camera play ./fixtures/qr.mp4 --loop
|
|
49
|
+
peach camera play ./fixtures/document.webm --rate 2
|
|
50
|
+
peach camera status
|
|
51
|
+
peach camera stop
|
|
52
|
+
peach do scan "https://example.com" --format qr_code
|
|
53
53
|
`)
|
|
54
54
|
}
|
|
55
55
|
|
|
@@ -126,7 +126,7 @@ export async function runCamera(
|
|
|
126
126
|
if (subcommand === 'scan') {
|
|
127
127
|
const text = positional[1]
|
|
128
128
|
if (!text) {
|
|
129
|
-
console.error(' usage:
|
|
129
|
+
console.error(' usage: peach do scan <text>')
|
|
130
130
|
return 1
|
|
131
131
|
}
|
|
132
132
|
const formatFlag = args.indexOf('--format')
|
|
@@ -164,7 +164,7 @@ export async function runCamera(
|
|
|
164
164
|
if (subcommand === 'play') {
|
|
165
165
|
const input = positional[1]
|
|
166
166
|
if (!input) {
|
|
167
|
-
console.error(' usage:
|
|
167
|
+
console.error(' usage: peach camera play <file> [--loop] [--rate <number>]')
|
|
168
168
|
return 1
|
|
169
169
|
}
|
|
170
170
|
const sourcePath = path.resolve(input)
|