@remotedraw/cli 0.2.1 → 0.3.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.
@@ -15,7 +15,6 @@ export declare const LOCALES: {
15
15
  aliases: string[];
16
16
  catalog: {
17
17
  readonly "language.word": "Language";
18
- readonly "language.prompt.helper": "Sets the language of the RemoteDraw CLI. Generated code and files stay in English.";
19
18
  readonly "language.saved": "Language set to {language}. Change it any time with: remotedraw language";
20
19
  readonly "language.current": "Language: {language} ({locale})";
21
20
  readonly "language.source.flag": "Set for this run by --language.";
@@ -51,6 +50,24 @@ export declare const LOCALES: {
51
50
  readonly "help.command.createInput": "Create or print a test input request payload.";
52
51
  readonly "help.command.examples": "List or install example projects.";
53
52
  readonly "help.command.agent": "Print or install the RemoteDraw agent skill.";
53
+ readonly "help.command.scan": "Read a codebase and propose where RemoteDraw fits.";
54
+ readonly "help.command.project": "Create a dashboard project and API key without scaffolding.";
55
+ readonly "project.help.usage": " remotedraw project create --name <name> [--key-name <name>] [--env <dir>] [--format json]";
56
+ readonly "project.help.body": "Creates a dashboard project and a project-scoped development API key. Nothing is scaffolded; use it when the integration already has its files.";
57
+ readonly "project.help.env": "Pass --env <dir> to write REMOTEDRAW_API_BASE_URL, REMOTEDRAW_PROJECT_ID, and REMOTEDRAW_API_KEY into <dir>/.env.local instead of printing the key.";
58
+ readonly "project.help.auth": "Needs a signed-in CLI (remotedraw login) or REMOTEDRAW_CLI_TOKEN.";
59
+ readonly "project.error.unknownSubcommand": "Unknown project subcommand: {subcommand}. Expected create.";
60
+ readonly "project.error.nameRequired": "--name is required for project create.";
61
+ readonly "project.created": "Created project {name} ({id}).";
62
+ readonly "project.keyWritten": "Wrote the API key to {path}.";
63
+ readonly "project.keyOnce": "API key (shown once): {key}";
64
+ readonly "project.keyRule": "Keep rd_sk_... keys in backend secrets only; never in browser bundles, mobile apps, or source control.";
65
+ readonly "cloud.warning.clientTree": "src/remotedraw/createRemoteDrawSession.ts reads REMOTEDRAW_API_KEY and must run server-side. Move it to your API route, server action, Convex action, or serverless function before use; a browser bundle must never import it.";
66
+ readonly "scan.help.usage": " remotedraw scan [--path <dir>] [--format json]";
67
+ readonly "scan.help.body": "Detects web, server, and iOS projects, existing RemoteDraw wiring, and where an rd_sk_ key may live; then lists integration options and the product questions to ask the user before building.";
68
+ readonly "scan.help.privacy": "Reads manifests and file names only. Reports env variable names, never values.";
69
+ readonly "scan.help.json": "Pass --format json for the machine-readable report agents should read.";
70
+ readonly "scan.error.missingPath": "Path does not exist: {path}";
54
71
  readonly "help.startHere": "Start here:";
55
72
  readonly "guide.title": "RemoteDraw integration guide";
56
73
  readonly "guide.intro": "Choose the surface your customer already has:";
@@ -152,22 +169,22 @@ export declare const LOCALES: {
152
169
  readonly "choice.preset.sketch.summary": "Normalized input for your own receiver surface.";
153
170
  readonly "choice.preset.sketch.explanation": "Choose this when your product owns the photo, map, PDF, canvas, or other receiver UI. RemoteDraw supplies the input primitives without prescribing a component design.";
154
171
  readonly "choice.preset.sketch.docs": "Open sketch docs";
155
- readonly "choice.preset.photoMarkup.summary": "Annotations on top of a photo.";
156
- readonly "choice.preset.photoMarkup.explanation": "Use this for inspections, feedback, or instructions on imagery. Your app supplies the photo and keeps the link with the drawings.";
172
+ readonly "choice.preset.photoMarkup.summary": "Draw on a photo your app is already showing on a screen.";
173
+ readonly "choice.preset.photoMarkup.explanation": "Your receiver renders the photo — on the desktop, laptop, or display someone is looking at — and a phone marks it up from across the room. The phone never takes or uploads the picture; your app supplies it and keeps the link with the drawings. Use this for reviewing an inspection photo at a desk, giving feedback on imagery, or annotating a screenshot.";
157
174
  readonly "choice.preset.photoMarkup.docs": "Open photo markup docs";
158
- readonly "choice.preset.pdfMarkup.summary": "Annotations on a PDF page.";
159
- readonly "choice.preset.pdfMarkup.explanation": "Use this for document review. Your app renders the right page and maps RemoteDraw coordinates onto that fixed view.";
175
+ readonly "choice.preset.pdfMarkup.summary": "Draw on a PDF page your app is already showing on a screen.";
176
+ readonly "choice.preset.pdfMarkup.explanation": "Use this for document review at a desk: your receiver renders the right page and a phone marks it. RemoteDraw renders no PDFs — your app owns the page and maps RemoteDraw coordinates onto that fixed view.";
160
177
  readonly "choice.preset.pdfMarkup.docs": "Open PDF markup docs";
161
- readonly "choice.preset.mapMarkup.summary": "Drawings and directions on a map.";
162
- readonly "choice.preset.mapMarkup.explanation": "Use this for routes, locations, or spatial feedback. Your app owns the map and viewport; RemoteDraw syncs the input.";
178
+ readonly "choice.preset.mapMarkup.summary": "Draw routes and directions on the map your app is showing.";
179
+ readonly "choice.preset.mapMarkup.explanation": "Use this for routes, locations, or spatial feedback marked from a phone onto a map on a desktop or wall display. Your app owns the map and the camera; RemoteDraw carries the input in geographic board space. The board's coordinateSpace.bounds is a fixed fence — size it larger than the camera you open on.";
163
180
  readonly "choice.preset.mapMarkup.docs": "Open map markup docs";
164
181
  readonly "choice.preset.screenMarkup.label": "Screen annotation";
165
182
  readonly "choice.preset.screenMarkup.hint": "Mark up a screen or window your receiver shares.";
166
183
  readonly "choice.preset.screenMarkup.summary": "Annotations on a screen or application view.";
167
- readonly "choice.preset.screenMarkup.explanation": "Use this for support, demos, and UI feedback. Your receiver supplies the screen image the phone marks are projected onto.";
184
+ readonly "choice.preset.screenMarkup.explanation": "Use this for support, demos, and UI feedback: the screen being marked is the receiver's, and the phone is the pen. Your receiver supplies the screen image the phone marks are projected onto.";
168
185
  readonly "choice.preset.screenMarkup.docs": "Open screen markup docs";
169
186
  readonly "choice.preset.designReview.summary": "Focused visual feedback on a design.";
170
- readonly "choice.preset.designReview.explanation": "Use this for design reviews with arrows, shapes, and freehand lines. Your product owns comments, versions, and decisions.";
187
+ readonly "choice.preset.designReview.explanation": "Use this for design reviews with arrows, shapes, and freehand lines drawn from a phone onto the design open on a reviewer's screen. Your product owns comments, versions, and decisions.";
171
188
  readonly "choice.preset.designReview.docs": "Open design review docs";
172
189
  readonly "choice.preset.pointer.summary": "Live pointing without persistent ink.";
173
190
  readonly "choice.preset.pointer.explanation": "Use this for presentations and guidance where the phone acts as a pointer and movement matters more than stored ink.";
@@ -200,7 +217,6 @@ export declare const LOCALES: {
200
217
  readonly "wizard.control.back": "Esc back";
201
218
  readonly "wizard.control.quitCtrlC": "Ctrl+C quit";
202
219
  readonly "wizard.control.quitQ": "q quit";
203
- readonly "wizard.control.move": "↑/↓ move";
204
220
  readonly "wizard.control.steps": "←/→ steps";
205
221
  readonly "wizard.control.choose": "↑/↓ choose";
206
222
  readonly "wizard.control.docs": "d open docs";
@@ -218,7 +234,6 @@ export declare const LOCALES: {
218
234
  readonly "wizard.tagline.setup": "phone → canvas · project setup";
219
235
  readonly "wizard.tagline": "phone → canvas";
220
236
  readonly "wizard.docs.openFailed": "Could not open the documentation. Use {url}";
221
- readonly "wizard.docs.button": "[ {label} ↗ ]";
222
237
  readonly "new.help.usage.bare": " remotedraw new";
223
238
  readonly "new.help.usage.named": " remotedraw new --app-name <name> [options]";
224
239
  readonly "new.help.interactive": "Run without options for the interactive setup flow.";
@@ -286,7 +301,7 @@ export declare const LOCALES: {
286
301
  readonly "doctor.sdk.ok": "{package} is listed in package.json dependencies.";
287
302
  readonly "doctor.sdk.missing": "Add {package}@{range} to dependencies or rerun remotedraw init.";
288
303
  readonly "doctor.apiKey.ok": "Server-side API key shape looks correct.";
289
- readonly "doctor.apiKey.missing": "Keep an rd_sk_... key in backend secrets only.";
304
+ readonly "doctor.apiKey.missing": "No rd_sk_... key found in this project's environment (REMOTEDRAW_API_KEY). When you add one, keep it in backend secrets only.";
290
305
  readonly "doctor.label.deployment": "deployment";
291
306
  readonly "doctor.label.deploymentReachable": "deployment reachable";
292
307
  readonly "doctor.label.apiKeyAccepted": "API key accepted";
@@ -419,7 +434,7 @@ export declare const LOCALES: {
419
434
  endonym: string;
420
435
  tags: string[];
421
436
  aliases: string[];
422
- catalog: Partial<Record<"language.word" | "language.prompt.helper" | "language.saved" | "language.current" | "language.source.flag" | "language.source.env" | "language.source.config" | "language.source.default" | "language.available" | "language.unknown" | "language.saveFailed" | "language.help.title" | "language.help.usage.list" | "language.help.usage.set" | "language.help.body" | "language.help.override" | "word.usage" | "word.options" | "word.commands" | "word.next" | "word.examples" | "help.title" | "help.usage.main" | "help.command.guide" | "help.command.options" | "help.command.language" | "help.command.login" | "help.command.logout" | "help.command.whoami" | "help.command.new" | "help.command.init" | "help.command.dev" | "help.command.doctor" | "help.command.update" | "help.command.createInput" | "help.command.examples" | "help.command.agent" | "help.startHere" | "guide.title" | "guide.intro" | "guide.path.webIos" | "guide.path.webOwned" | "guide.path.svelteIos" | "guide.path.desktop" | "guide.path.ownIos" | "guide.note.keys" | "guide.note.provisioning" | "guide.note.offline" | "options.title" | "options.machineReadable" | "options.help.body" | "field.appName" | "field.target" | "field.sender" | "field.sdk" | "field.preset" | "field.packageManager" | "field.apiBaseUrl" | "field.project" | "field.folder" | "field.sdkShort" | "field.sender.helper" | "field.sdk.helper" | "field.appName.error" | "field.appName.preview" | "choice.target.web.label" | "choice.target.web.summary" | "choice.target.web.explanation" | "choice.target.web.docs" | "choice.target.desktop.label" | "choice.target.desktop.summary" | "choice.target.desktop.explanation" | "choice.target.desktop.docs" | "choice.target.ios.label" | "choice.target.ios.summary" | "choice.target.ios.explanation" | "choice.target.ios.docs" | "choice.target.headless.label" | "choice.target.headless.summary" | "choice.target.headless.explanation" | "choice.target.headless.docs" | "choice.sender.remotedrawIos.label" | "choice.sender.remotedrawIos.hint.scan" | "choice.sender.remotedrawIos.hint.joinUrl" | "choice.sender.remotedrawIos.summary" | "choice.sender.remotedrawIos.explanation" | "choice.sender.remotedrawIos.docs" | "choice.sender.embeddedWeb.label" | "choice.sender.embeddedWeb.hint" | "choice.sender.embeddedWeb.summary" | "choice.sender.embeddedWeb.explanation" | "choice.sender.embeddedWeb.docs" | "choice.sender.ownIos.label" | "choice.sender.ownIos.hint" | "choice.sender.ownIos.summary" | "choice.sender.ownIos.explanation" | "choice.sender.ownIos.docs" | "choice.sender.headless.label" | "choice.sender.headless.hint.noUi" | "choice.sender.headless.hint.httpRoutes" | "choice.sender.headless.summary" | "choice.sender.headless.explanation" | "choice.sender.headless.docs" | "choice.sdk.react.label" | "choice.sdk.react.hint" | "choice.sdk.react.summary" | "choice.sdk.react.explanation" | "choice.sdk.react.docs" | "choice.sdk.svelte.label" | "choice.sdk.svelte.hint" | "choice.sdk.svelte.summary" | "choice.sdk.svelte.explanation" | "choice.sdk.svelte.docs" | "choice.sdk.js.label.client" | "choice.sdk.js.label.noFramework" | "choice.sdk.js.hint.client" | "choice.sdk.js.hint.noFramework" | "choice.sdk.js.summary" | "choice.sdk.js.explanation" | "choice.sdk.js.docs" | "choice.sdk.swift.label" | "choice.sdk.swift.hint" | "choice.sdk.swift.summary" | "choice.sdk.swift.explanation" | "choice.sdk.swift.docs" | "choice.sdk.headless.label" | "choice.sdk.headless.hint" | "choice.sdk.headless.summary" | "choice.sdk.headless.explanation" | "choice.sdk.headless.docs" | "choice.preset.approval.summary" | "choice.preset.approval.explanation" | "choice.preset.approval.docs" | "choice.preset.sketch.label" | "choice.preset.sketch.hint" | "choice.preset.sketch.summary" | "choice.preset.sketch.explanation" | "choice.preset.sketch.docs" | "choice.preset.photoMarkup.summary" | "choice.preset.photoMarkup.explanation" | "choice.preset.photoMarkup.docs" | "choice.preset.pdfMarkup.summary" | "choice.preset.pdfMarkup.explanation" | "choice.preset.pdfMarkup.docs" | "choice.preset.mapMarkup.summary" | "choice.preset.mapMarkup.explanation" | "choice.preset.mapMarkup.docs" | "choice.preset.screenMarkup.label" | "choice.preset.screenMarkup.hint" | "choice.preset.screenMarkup.summary" | "choice.preset.screenMarkup.explanation" | "choice.preset.screenMarkup.docs" | "choice.preset.designReview.summary" | "choice.preset.designReview.explanation" | "choice.preset.designReview.docs" | "choice.preset.pointer.summary" | "choice.preset.pointer.explanation" | "choice.preset.pointer.docs" | "choice.packageManager.npm.summary" | "choice.packageManager.npm.explanation" | "choice.packageManager.npm.docs" | "choice.packageManager.pnpm.summary" | "choice.packageManager.pnpm.explanation" | "choice.packageManager.pnpm.docs" | "choice.packageManager.yarn.summary" | "choice.packageManager.yarn.explanation" | "choice.packageManager.yarn.docs" | "choice.packageManager.bun.summary" | "choice.packageManager.bun.explanation" | "choice.packageManager.bun.docs" | "wizard.title.setup" | "wizard.title.review" | "wizard.title.confirm" | "wizard.title.executing" | "wizard.title.success" | "wizard.title.error" | "wizard.title.interrupted" | "wizard.title.cancelled" | "wizard.confirm.no" | "wizard.confirm.yes" | "wizard.hint.replaceDefault" | "wizard.hint.edit" | "wizard.control.next" | "wizard.control.back" | "wizard.control.quitCtrlC" | "wizard.control.quitQ" | "wizard.control.move" | "wizard.control.steps" | "wizard.control.choose" | "wizard.control.docs" | "wizard.control.forward" | "wizard.control.backArrow" | "wizard.control.confirm" | "wizard.progress.step" | "wizard.card.help" | "wizard.card.summary" | "wizard.value.default" | "wizard.executing.detail" | "wizard.created.at" | "wizard.next.command" | "wizard.docs.line" | "wizard.tagline.setup" | "wizard.tagline" | "wizard.docs.openFailed" | "wizard.docs.button" | "new.help.usage.bare" | "new.help.usage.named" | "new.help.interactive" | "new.help.option.path" | "new.help.option.target" | "new.help.option.sender" | "new.help.option.sdk" | "new.help.option.preset" | "new.help.option.packageManager" | "new.help.option.apiBaseUrl" | "new.help.option.force" | "new.help.option.offline" | "new.help.option.nonInteractive" | "new.help.option.format" | "new.help.option.dryRun" | "new.help.option.language" | "init.help.usage" | "init.help.body" | "prompt.control.move" | "prompt.control.select" | "prompt.control.quit" | "prompt.intro.setup" | "scaffold.verb.created" | "scaffold.verb.initialized" | "scaffold.verb.installedExample" | "scaffold.headline" | "scaffold.choices" | "scaffold.choice.app" | "scaffold.choice.sdk" | "scaffold.choice.sender" | "scaffold.choice.starter" | "scaffold.wrote" | "scaffold.wouldWrite" | "scaffold.next.install" | "scaffold.next.secrets" | "scaffold.next.createInput" | "scaffold.next.render" | "cloud.skipped" | "cloud.dashboardProject" | "cloud.devKey" | "cloud.projectId" | "cloud.warning.placeholder" | "cloud.warning.offline" | "cloud.warning.dryRun" | "login.already" | "login.complete" | "login.credential" | "login.help.title" | "login.help.usage" | "login.help.body" | "logout.done" | "logout.none" | "logout.help.title" | "logout.help.usage" | "whoami.notLoggedIn" | "whoami.api" | "whoami.anonymous" | "whoami.help.title" | "whoami.help.usage" | "doctor.title" | "doctor.config.ok" | "doctor.config.missing" | "doctor.package.ok" | "doctor.package.missing" | "doctor.sdk.ok" | "doctor.sdk.missing" | "doctor.apiKey.ok" | "doctor.apiKey.missing" | "doctor.label.deployment" | "doctor.label.deploymentReachable" | "doctor.label.apiKeyAccepted" | "doctor.label.cliVersion" | "doctor.network.offline" | "doctor.network.noOrigin" | "doctor.health.ok" | "doctor.health.failed" | "doctor.key.skippedNoKey" | "doctor.key.skippedNoHealth" | "doctor.key.liveOne" | "doctor.key.liveMany" | "doctor.version.offline" | "doctor.version.unknown" | "doctor.version.stale" | "doctor.version.latest" | "doctor.probe.noNetwork" | "doctor.probe.noNetworkKey" | "doctor.probe.nonJson" | "doctor.probe.rejected" | "doctor.baseUrl.placeholder" | "doctor.baseUrl.invalid" | "doctor.baseUrl.default" | "doctor.baseUrl.production" | "doctor.baseUrl.override" | "doctor.baseUrl.origin.flag" | "doctor.baseUrl.origin.project" | "doctor.help.usage" | "doctor.help.checks" | "doctor.help.baseUrl" | "doctor.help.network" | "doctor.help.version" | "doctor.help.offline" | "update.unreachable" | "update.alreadyLatest" | "update.available" | "update.ephemeral" | "update.runToInstall" | "update.availableRun" | "update.failed" | "update.done" | "update.help.usage" | "update.help.body" | "update.help.global" | "update.help.check" | "update.help.silence" | "dev.title" | "dev.apiBaseUrl" | "dev.hosted" | "dev.step1" | "dev.step1.detail" | "dev.step2" | "dev.step3" | "dev.step3.detail" | "dev.help.usage" | "dev.help.body" | "createInput.title" | "createInput.warning" | "createInput.footer" | "createInput.error.noFetch" | "createInput.error.noKey" | "createInput.error.apiStatus" | "createInput.help.usage" | "createInput.help.option.preset" | "createInput.help.option.label" | "createInput.help.option.externalId" | "createInput.help.option.apiBaseUrl" | "createInput.help.option.apiKey" | "createInput.help.option.expiresInMs" | "examples.title" | "examples.reactIos" | "examples.reactOwnedSender" | "examples.rawHttp" | "examples.iosOwnedSender" | "examples.installOne" | "examples.help.usage.list" | "examples.help.usage.install" | "agent.title" | "agent.intro" | "agent.printSkill" | "agent.installSkill" | "agent.wouldWrite" | "agent.installed" | "agent.next" | "agent.error.exists" | "agent.help.usage.bare" | "agent.help.usage.print" | "agent.help.usage.install" | "agent.help.paths" | "agent.help.body" | "cloud.authorize" | "cloud.browserOpened" | "cloud.browserFailed" | "cloud.error.notLoggedIn" | "cloud.error.authStatus" | "cloud.error.authExpired" | "cloud.error.noFetch" | "cloud.error.unreachable" | "cloud.error.endpointMissing" | "cloud.error.invalidResponse" | "cloud.error.requestFailed" | "cloud.error.badOrigin" | "cloud.error.configUnreadable" | "cloud.error.envConflict" | "notice.update.available" | "notice.update.ephemeral" | "notice.update.install" | "error.appNameRequired" | "error.plan.ownIosNeedsSwift" | "error.plan.swiftNeedsOwnIos" | "error.plan.iosNeedsOwnIos" | "error.plan.headlessSenderSdk" | "error.unknownChoice" | "error.refuseOverwrite" | "error.expectedHttpOrigin" | "error.expectedBareOrigin" | "error.noHome" | "error.unknownCommand" | "error.unsupportedOption" | "error.optionNoValue" | "error.optionNeedsValue" | "error.expiresInMs" | "error.interactiveNeedsTty" | "error.noChoices" | "error.httpsDocsOnly" | "error.httpUrlsOnly", string>>;
437
+ catalog: Partial<Record<"language.word" | "language.saved" | "language.current" | "language.source.flag" | "language.source.env" | "language.source.config" | "language.source.default" | "language.available" | "language.unknown" | "language.saveFailed" | "language.help.title" | "language.help.usage.list" | "language.help.usage.set" | "language.help.body" | "language.help.override" | "word.usage" | "word.options" | "word.commands" | "word.next" | "word.examples" | "help.title" | "help.usage.main" | "help.command.guide" | "help.command.options" | "help.command.language" | "help.command.login" | "help.command.logout" | "help.command.whoami" | "help.command.new" | "help.command.init" | "help.command.dev" | "help.command.doctor" | "help.command.update" | "help.command.createInput" | "help.command.examples" | "help.command.agent" | "help.command.scan" | "help.command.project" | "project.help.usage" | "project.help.body" | "project.help.env" | "project.help.auth" | "project.error.unknownSubcommand" | "project.error.nameRequired" | "project.created" | "project.keyWritten" | "project.keyOnce" | "project.keyRule" | "cloud.warning.clientTree" | "scan.help.usage" | "scan.help.body" | "scan.help.privacy" | "scan.help.json" | "scan.error.missingPath" | "help.startHere" | "guide.title" | "guide.intro" | "guide.path.webIos" | "guide.path.webOwned" | "guide.path.svelteIos" | "guide.path.desktop" | "guide.path.ownIos" | "guide.note.keys" | "guide.note.provisioning" | "guide.note.offline" | "options.title" | "options.machineReadable" | "options.help.body" | "field.appName" | "field.target" | "field.sender" | "field.sdk" | "field.preset" | "field.packageManager" | "field.apiBaseUrl" | "field.project" | "field.folder" | "field.sdkShort" | "field.sender.helper" | "field.sdk.helper" | "field.appName.error" | "field.appName.preview" | "choice.target.web.label" | "choice.target.web.summary" | "choice.target.web.explanation" | "choice.target.web.docs" | "choice.target.desktop.label" | "choice.target.desktop.summary" | "choice.target.desktop.explanation" | "choice.target.desktop.docs" | "choice.target.ios.label" | "choice.target.ios.summary" | "choice.target.ios.explanation" | "choice.target.ios.docs" | "choice.target.headless.label" | "choice.target.headless.summary" | "choice.target.headless.explanation" | "choice.target.headless.docs" | "choice.sender.remotedrawIos.label" | "choice.sender.remotedrawIos.hint.scan" | "choice.sender.remotedrawIos.hint.joinUrl" | "choice.sender.remotedrawIos.summary" | "choice.sender.remotedrawIos.explanation" | "choice.sender.remotedrawIos.docs" | "choice.sender.embeddedWeb.label" | "choice.sender.embeddedWeb.hint" | "choice.sender.embeddedWeb.summary" | "choice.sender.embeddedWeb.explanation" | "choice.sender.embeddedWeb.docs" | "choice.sender.ownIos.label" | "choice.sender.ownIos.hint" | "choice.sender.ownIos.summary" | "choice.sender.ownIos.explanation" | "choice.sender.ownIos.docs" | "choice.sender.headless.label" | "choice.sender.headless.hint.noUi" | "choice.sender.headless.hint.httpRoutes" | "choice.sender.headless.summary" | "choice.sender.headless.explanation" | "choice.sender.headless.docs" | "choice.sdk.react.label" | "choice.sdk.react.hint" | "choice.sdk.react.summary" | "choice.sdk.react.explanation" | "choice.sdk.react.docs" | "choice.sdk.svelte.label" | "choice.sdk.svelte.hint" | "choice.sdk.svelte.summary" | "choice.sdk.svelte.explanation" | "choice.sdk.svelte.docs" | "choice.sdk.js.label.client" | "choice.sdk.js.label.noFramework" | "choice.sdk.js.hint.client" | "choice.sdk.js.hint.noFramework" | "choice.sdk.js.summary" | "choice.sdk.js.explanation" | "choice.sdk.js.docs" | "choice.sdk.swift.label" | "choice.sdk.swift.hint" | "choice.sdk.swift.summary" | "choice.sdk.swift.explanation" | "choice.sdk.swift.docs" | "choice.sdk.headless.label" | "choice.sdk.headless.hint" | "choice.sdk.headless.summary" | "choice.sdk.headless.explanation" | "choice.sdk.headless.docs" | "choice.preset.approval.summary" | "choice.preset.approval.explanation" | "choice.preset.approval.docs" | "choice.preset.sketch.label" | "choice.preset.sketch.hint" | "choice.preset.sketch.summary" | "choice.preset.sketch.explanation" | "choice.preset.sketch.docs" | "choice.preset.photoMarkup.summary" | "choice.preset.photoMarkup.explanation" | "choice.preset.photoMarkup.docs" | "choice.preset.pdfMarkup.summary" | "choice.preset.pdfMarkup.explanation" | "choice.preset.pdfMarkup.docs" | "choice.preset.mapMarkup.summary" | "choice.preset.mapMarkup.explanation" | "choice.preset.mapMarkup.docs" | "choice.preset.screenMarkup.label" | "choice.preset.screenMarkup.hint" | "choice.preset.screenMarkup.summary" | "choice.preset.screenMarkup.explanation" | "choice.preset.screenMarkup.docs" | "choice.preset.designReview.summary" | "choice.preset.designReview.explanation" | "choice.preset.designReview.docs" | "choice.preset.pointer.summary" | "choice.preset.pointer.explanation" | "choice.preset.pointer.docs" | "choice.packageManager.npm.summary" | "choice.packageManager.npm.explanation" | "choice.packageManager.npm.docs" | "choice.packageManager.pnpm.summary" | "choice.packageManager.pnpm.explanation" | "choice.packageManager.pnpm.docs" | "choice.packageManager.yarn.summary" | "choice.packageManager.yarn.explanation" | "choice.packageManager.yarn.docs" | "choice.packageManager.bun.summary" | "choice.packageManager.bun.explanation" | "choice.packageManager.bun.docs" | "wizard.title.setup" | "wizard.title.review" | "wizard.title.confirm" | "wizard.title.executing" | "wizard.title.success" | "wizard.title.error" | "wizard.title.interrupted" | "wizard.title.cancelled" | "wizard.confirm.no" | "wizard.confirm.yes" | "wizard.hint.replaceDefault" | "wizard.hint.edit" | "wizard.control.next" | "wizard.control.back" | "wizard.control.quitCtrlC" | "wizard.control.quitQ" | "wizard.control.steps" | "wizard.control.choose" | "wizard.control.docs" | "wizard.control.forward" | "wizard.control.backArrow" | "wizard.control.confirm" | "wizard.progress.step" | "wizard.card.help" | "wizard.card.summary" | "wizard.value.default" | "wizard.executing.detail" | "wizard.created.at" | "wizard.next.command" | "wizard.docs.line" | "wizard.tagline.setup" | "wizard.tagline" | "wizard.docs.openFailed" | "new.help.usage.bare" | "new.help.usage.named" | "new.help.interactive" | "new.help.option.path" | "new.help.option.target" | "new.help.option.sender" | "new.help.option.sdk" | "new.help.option.preset" | "new.help.option.packageManager" | "new.help.option.apiBaseUrl" | "new.help.option.force" | "new.help.option.offline" | "new.help.option.nonInteractive" | "new.help.option.format" | "new.help.option.dryRun" | "new.help.option.language" | "init.help.usage" | "init.help.body" | "prompt.control.move" | "prompt.control.select" | "prompt.control.quit" | "prompt.intro.setup" | "scaffold.verb.created" | "scaffold.verb.initialized" | "scaffold.verb.installedExample" | "scaffold.headline" | "scaffold.choices" | "scaffold.choice.app" | "scaffold.choice.sdk" | "scaffold.choice.sender" | "scaffold.choice.starter" | "scaffold.wrote" | "scaffold.wouldWrite" | "scaffold.next.install" | "scaffold.next.secrets" | "scaffold.next.createInput" | "scaffold.next.render" | "cloud.skipped" | "cloud.dashboardProject" | "cloud.devKey" | "cloud.projectId" | "cloud.warning.placeholder" | "cloud.warning.offline" | "cloud.warning.dryRun" | "login.already" | "login.complete" | "login.credential" | "login.help.title" | "login.help.usage" | "login.help.body" | "logout.done" | "logout.none" | "logout.help.title" | "logout.help.usage" | "whoami.notLoggedIn" | "whoami.api" | "whoami.anonymous" | "whoami.help.title" | "whoami.help.usage" | "doctor.title" | "doctor.config.ok" | "doctor.config.missing" | "doctor.package.ok" | "doctor.package.missing" | "doctor.sdk.ok" | "doctor.sdk.missing" | "doctor.apiKey.ok" | "doctor.apiKey.missing" | "doctor.label.deployment" | "doctor.label.deploymentReachable" | "doctor.label.apiKeyAccepted" | "doctor.label.cliVersion" | "doctor.network.offline" | "doctor.network.noOrigin" | "doctor.health.ok" | "doctor.health.failed" | "doctor.key.skippedNoKey" | "doctor.key.skippedNoHealth" | "doctor.key.liveOne" | "doctor.key.liveMany" | "doctor.version.offline" | "doctor.version.unknown" | "doctor.version.stale" | "doctor.version.latest" | "doctor.probe.noNetwork" | "doctor.probe.noNetworkKey" | "doctor.probe.nonJson" | "doctor.probe.rejected" | "doctor.baseUrl.placeholder" | "doctor.baseUrl.invalid" | "doctor.baseUrl.default" | "doctor.baseUrl.production" | "doctor.baseUrl.override" | "doctor.baseUrl.origin.flag" | "doctor.baseUrl.origin.project" | "doctor.help.usage" | "doctor.help.checks" | "doctor.help.baseUrl" | "doctor.help.network" | "doctor.help.version" | "doctor.help.offline" | "update.unreachable" | "update.alreadyLatest" | "update.available" | "update.ephemeral" | "update.runToInstall" | "update.availableRun" | "update.failed" | "update.done" | "update.help.usage" | "update.help.body" | "update.help.global" | "update.help.check" | "update.help.silence" | "dev.title" | "dev.apiBaseUrl" | "dev.hosted" | "dev.step1" | "dev.step1.detail" | "dev.step2" | "dev.step3" | "dev.step3.detail" | "dev.help.usage" | "dev.help.body" | "createInput.title" | "createInput.warning" | "createInput.footer" | "createInput.error.noFetch" | "createInput.error.noKey" | "createInput.error.apiStatus" | "createInput.help.usage" | "createInput.help.option.preset" | "createInput.help.option.label" | "createInput.help.option.externalId" | "createInput.help.option.apiBaseUrl" | "createInput.help.option.apiKey" | "createInput.help.option.expiresInMs" | "examples.title" | "examples.reactIos" | "examples.reactOwnedSender" | "examples.rawHttp" | "examples.iosOwnedSender" | "examples.installOne" | "examples.help.usage.list" | "examples.help.usage.install" | "agent.title" | "agent.intro" | "agent.printSkill" | "agent.installSkill" | "agent.wouldWrite" | "agent.installed" | "agent.next" | "agent.error.exists" | "agent.help.usage.bare" | "agent.help.usage.print" | "agent.help.usage.install" | "agent.help.paths" | "agent.help.body" | "cloud.authorize" | "cloud.browserOpened" | "cloud.browserFailed" | "cloud.error.notLoggedIn" | "cloud.error.authStatus" | "cloud.error.authExpired" | "cloud.error.noFetch" | "cloud.error.unreachable" | "cloud.error.endpointMissing" | "cloud.error.invalidResponse" | "cloud.error.requestFailed" | "cloud.error.badOrigin" | "cloud.error.configUnreadable" | "cloud.error.envConflict" | "notice.update.available" | "notice.update.ephemeral" | "notice.update.install" | "error.appNameRequired" | "error.plan.ownIosNeedsSwift" | "error.plan.swiftNeedsOwnIos" | "error.plan.iosNeedsOwnIos" | "error.plan.headlessSenderSdk" | "error.unknownChoice" | "error.refuseOverwrite" | "error.expectedHttpOrigin" | "error.expectedBareOrigin" | "error.noHome" | "error.unknownCommand" | "error.unsupportedOption" | "error.optionNoValue" | "error.optionNeedsValue" | "error.expiresInMs" | "error.interactiveNeedsTty" | "error.noChoices" | "error.httpsDocsOnly" | "error.httpUrlsOnly", string>>;
423
438
  };
424
439
  };
425
440
  export type Locale = keyof typeof LOCALES;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/locales/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAG7B,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE/E;;;;;;GAMG;AACH,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAawB,CAAC;AAE7C,MAAM,MAAM,MAAM,GAAG,MAAM,OAAO,OAAO,CAAC;AAE1C,6EAA6E;AAC7E,eAAO,MAAM,aAAa,EAAE,MAAa,CAAC;AAE1C,eAAO,MAAM,iBAAiB,EAA2B,MAAM,EAAE,CAAC;AAElE,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/locales/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAG7B,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE/E;;;;;;GAMG;AACH,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAawB,CAAC;AAE7C,MAAM,MAAM,MAAM,GAAG,MAAM,OAAO,OAAO,CAAC;AAE1C,6EAA6E;AAC7E,eAAO,MAAM,aAAa,EAAE,MAAa,CAAC;AAE1C,eAAO,MAAM,iBAAiB,EAA2B,MAAM,EAAE,CAAC;AAElE,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"nl.d.ts","sourceRoot":"","sources":["../../src/locales/nl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD;;;;;;GAMG;AACH,eAAO,MAAM,EAAE,EAAE,aA6lBhB,CAAC"}
1
+ {"version":3,"file":"nl.d.ts","sourceRoot":"","sources":["../../src/locales/nl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD;;;;;;GAMG;AACH,eAAO,MAAM,EAAE,EAAE,aA8mBhB,CAAC"}
@@ -8,7 +8,6 @@
8
8
  export const nl = {
9
9
  // ── Taalkeuze ─────────────────────────────────────────────────────────
10
10
  "language.word": "Taal",
11
- "language.prompt.helper": "Stelt de taal van de RemoteDraw-CLI in. Gegenereerde code en bestanden blijven Engels.",
12
11
  "language.saved": "Taal ingesteld op {language}. Wijzig dit wanneer je wilt met: remotedraw language",
13
12
  "language.current": "Taal: {language} ({locale})",
14
13
  "language.source.flag": "Voor deze opdracht ingesteld met --language.",
@@ -46,6 +45,24 @@ export const nl = {
46
45
  "help.command.createInput": "Maak of toon een testverzoek voor tekeninvoer.",
47
46
  "help.command.examples": "Toon of installeer voorbeeldprojecten.",
48
47
  "help.command.agent": "Toon of installeer de RemoteDraw-agentskill.",
48
+ "help.command.scan": "Lees een codebase en stel voor waar RemoteDraw past.",
49
+ "help.command.project": "Maak een dashboardproject en API-sleutel zonder starterscode.",
50
+ "project.help.usage": " remotedraw project create --name <naam> [--key-name <naam>] [--env <map>] [--format json]",
51
+ "project.help.body": "Maakt een dashboardproject en een projectgebonden ontwikkel-API-sleutel. Er wordt niets gescaffold; gebruik dit als de integratie haar bestanden al heeft.",
52
+ "project.help.env": "Geef --env <map> op om REMOTEDRAW_API_BASE_URL, REMOTEDRAW_PROJECT_ID en REMOTEDRAW_API_KEY naar <map>/.env.local te schrijven in plaats van de sleutel te tonen.",
53
+ "project.help.auth": "Vereist een ingelogde CLI (remotedraw login) of REMOTEDRAW_CLI_TOKEN.",
54
+ "project.error.unknownSubcommand": "Onbekend project-subcommando: {subcommand}. Verwacht: create.",
55
+ "project.error.nameRequired": "--name is verplicht voor project create.",
56
+ "project.created": "Project {name} ({id}) aangemaakt.",
57
+ "project.keyWritten": "API-sleutel geschreven naar {path}.",
58
+ "project.keyOnce": "API-sleutel (eenmalig getoond): {key}",
59
+ "project.keyRule": "Bewaar rd_sk_...-sleutels uitsluitend in backend-secrets; nooit in browserbundels, mobiele apps of versiebeheer.",
60
+ "cloud.warning.clientTree": "src/remotedraw/createRemoteDrawSession.ts leest REMOTEDRAW_API_KEY en moet server-side draaien. Verplaats het naar je API-route, server action, Convex action of serverless functie voordat je het gebruikt; een browserbundel mag het nooit importeren.",
61
+ "scan.help.usage": " remotedraw scan [--path <map>] [--format json]",
62
+ "scan.help.body": "Herkent web-, server- en iOS-projecten, bestaande RemoteDraw-koppelingen en waar een rd_sk_-sleutel mag staan; toont daarna integratieopties en de productvragen die je de gebruiker stelt voordat je bouwt.",
63
+ "scan.help.privacy": "Leest alleen manifesten en bestandsnamen. Toont namen van omgevingsvariabelen, nooit waarden.",
64
+ "scan.help.json": "Gebruik --format json voor het machineleesbare rapport dat agents moeten lezen.",
65
+ "scan.error.missingPath": "Pad bestaat niet: {path}",
49
66
  "help.startHere": "Begin hier:",
50
67
  // ── guide ─────────────────────────────────────────────────────────────
51
68
  "guide.title": "RemoteDraw-integratiegids",
@@ -154,19 +171,19 @@ export const nl = {
154
171
  "choice.preset.sketch.summary": "Genormaliseerde invoer voor een eigen ontvangeroppervlak.",
155
172
  "choice.preset.sketch.explanation": "Kies dit wanneer jouw product de foto, kaart, PDF, canvas of andere receiver-UI beheert. RemoteDraw levert de invoerprimitieven zonder een voorgeschreven componentontwerp.",
156
173
  "choice.preset.sketch.docs": "Schetsdocs openen",
157
- "choice.preset.photoMarkup.summary": "Aantekeningen bovenop een foto.",
158
- "choice.preset.photoMarkup.explanation": "Gebruik dit voor inspecties, feedback of aanwijzingen op beeldmateriaal. Je app levert de foto en bewaart de relatie met de tekeningen.",
174
+ "choice.preset.photoMarkup.summary": "Teken op een foto die je app al op een scherm toont.",
175
+ "choice.preset.photoMarkup.explanation": "Je ontvanger toont de foto — op de desktop, laptop of display waar iemand naar kijkt — en een telefoon zet er markeringen op. De telefoon maakt of uploadt de foto nooit; je app levert die en bewaart de relatie met de tekeningen. Gebruik dit voor het beoordelen van een inspectiefoto aan een bureau, feedback op beeldmateriaal of het annoteren van een schermafbeelding.",
159
176
  "choice.preset.photoMarkup.docs": "Fotoannotatiedocs openen",
160
- "choice.preset.pdfMarkup.summary": "Aantekeningen op een PDF-pagina.",
161
- "choice.preset.pdfMarkup.explanation": "Gebruik dit voor documentreview. Je app rendert de juiste pagina en koppelt RemoteDraw-coördinaten aan die vaste weergave.",
177
+ "choice.preset.pdfMarkup.summary": "Teken op een PDF-pagina die je app al op een scherm toont.",
178
+ "choice.preset.pdfMarkup.explanation": "Gebruik dit voor documentreview aan een bureau: je ontvanger rendert de juiste pagina en een telefoon markeert die. RemoteDraw rendert geen PDF's — je app beheert de pagina en koppelt RemoteDraw-coördinaten aan die vaste weergave.",
162
179
  "choice.preset.pdfMarkup.docs": "PDF-annotatiedocs openen",
163
- "choice.preset.mapMarkup.summary": "Tekeningen en aanwijzingen op een kaart.",
164
- "choice.preset.mapMarkup.explanation": "Gebruik dit voor routes, locaties of ruimtelijke feedback. Je app beheert de kaart en viewport; RemoteDraw synchroniseert de invoer.",
180
+ "choice.preset.mapMarkup.summary": "Teken routes en aanwijzingen op de kaart die je app toont.",
181
+ "choice.preset.mapMarkup.explanation": "Gebruik dit voor routes, locaties of ruimtelijke feedback die vanaf een telefoon op een kaart op een desktop of wanddisplay worden gezet. Je app beheert de kaart en de camera; RemoteDraw draagt de invoer in geografische board-ruimte. De coordinateSpace.bounds van het board is een vast hek — maak het groter dan de camera waarmee je opent.",
165
182
  "choice.preset.mapMarkup.docs": "Kaartannotatiedocs openen",
166
183
  "choice.preset.screenMarkup.label": "Schermannotatie",
167
184
  "choice.preset.screenMarkup.hint": "Teken over een scherm of venster dat je ontvanger deelt.",
168
185
  "choice.preset.screenMarkup.summary": "Aantekeningen op een scherm of applicatieweergave.",
169
- "choice.preset.screenMarkup.explanation": "Gebruik dit voor support, demo's en UI-feedback. Je ontvanger levert het schermbeeld waarop de telefoonmarkeringen worden geprojecteerd.",
186
+ "choice.preset.screenMarkup.explanation": "Gebruik dit voor support, demo's en UI-feedback: het scherm dat gemarkeerd wordt is dat van de ontvanger en de telefoon is de pen. Je ontvanger levert het schermbeeld waarop de telefoonmarkeringen worden geprojecteerd.",
170
187
  "choice.preset.screenMarkup.docs": "Schermaantekeningdocs openen",
171
188
  "choice.preset.designReview.summary": "Gerichte visuele feedback op een ontwerp.",
172
189
  "choice.preset.designReview.explanation": "Gebruik dit voor ontwerpbeoordelingen met pijlen, vormen en vrije lijnen. Je product beheert opmerkingen, versies en besluitvorming.",
@@ -204,7 +221,6 @@ export const nl = {
204
221
  "wizard.control.back": "Esc terug",
205
222
  "wizard.control.quitCtrlC": "Ctrl+C stoppen",
206
223
  "wizard.control.quitQ": "q stoppen",
207
- "wizard.control.move": "↑/↓ bewegen",
208
224
  "wizard.control.steps": "←/→ stappen",
209
225
  "wizard.control.choose": "↑/↓ kiezen",
210
226
  "wizard.control.docs": "d docs openen",
@@ -222,7 +238,6 @@ export const nl = {
222
238
  "wizard.tagline.setup": "telefoon → canvas · projectsetup",
223
239
  "wizard.tagline": "telefoon → canvas",
224
240
  "wizard.docs.openFailed": "Kon de documentatie niet openen. Gebruik {url}",
225
- "wizard.docs.button": "[ {label} ↗ ]",
226
241
  // ── new / init ────────────────────────────────────────────────────────
227
242
  "new.help.usage.bare": " remotedraw new",
228
243
  "new.help.usage.named": " remotedraw new --app-name <naam> [opties]",
@@ -294,7 +309,7 @@ export const nl = {
294
309
  "doctor.sdk.ok": "{package} staat in de dependencies van package.json.",
295
310
  "doctor.sdk.missing": "Voeg {package}@{range} toe aan de dependencies of voer remotedraw init opnieuw uit.",
296
311
  "doctor.apiKey.ok": "De vorm van de server-API-sleutel klopt.",
297
- "doctor.apiKey.missing": "Bewaar een rd_sk_...-sleutel uitsluitend in backend-secrets.",
312
+ "doctor.apiKey.missing": "Geen rd_sk_...-sleutel gevonden in de omgeving van dit project (REMOTEDRAW_API_KEY). Bewaar die, zodra je er een toevoegt, uitsluitend in backend-secrets.",
298
313
  "doctor.label.deployment": "deployment",
299
314
  "doctor.label.deploymentReachable": "deployment bereikbaar",
300
315
  "doctor.label.apiKeyAccepted": "API-sleutel geaccepteerd",
@@ -0,0 +1,75 @@
1
+ /**
2
+ * One table for the four package managers RemoteDraw supports, and the
3
+ * detection that picks the right one for a project.
4
+ *
5
+ * Two things go wrong without this. First, every surface that prints a command
6
+ * invents its own spelling, and they drift: `pnpm install` is not `pnpm add`,
7
+ * `npx` is not `pnpm dlx`, and Yarn Berry has no global install at all. Second,
8
+ * the CLI defaults to npm and tells a pnpm user to run `npm install` in a
9
+ * pnpm project - a command that works, and quietly leaves a `package-lock.json`
10
+ * next to their `pnpm-lock.yaml`.
11
+ *
12
+ * The table below is the single source of truth. `tests/api/docs-route-coverage`
13
+ * style parity tests keep the docs snippets generated from it rather than typed
14
+ * out beside it.
15
+ */
16
+ export declare const packageManagerIds: readonly ["npm", "pnpm", "yarn", "bun"];
17
+ export type PackageManagerId = (typeof packageManagerIds)[number];
18
+ export type PackageManagerCommands = {
19
+ /** Install everything the manifest already declares. */
20
+ install: string;
21
+ /** Add a dependency to the project. */
22
+ add: string;
23
+ /**
24
+ * Install a CLI globally. `null` where the manager has no such concept:
25
+ * Yarn Berry removed `yarn global`, and points people at `yarn dlx` instead.
26
+ */
27
+ addGlobal: string | null;
28
+ /** Run a published binary without installing it first. */
29
+ dlx: string;
30
+ /** Run a script from package.json. */
31
+ run: string;
32
+ /** The lockfiles this manager writes, most specific first. */
33
+ lockfiles: string[];
34
+ };
35
+ export declare const packageManagerCommands: Record<PackageManagerId, PackageManagerCommands>;
36
+ export declare function isPackageManagerId(value: string): value is PackageManagerId;
37
+ /**
38
+ * How a reader of the docs gets the CLI. A manager without a global install
39
+ * runs it straight from the registry instead, which is a real answer rather
40
+ * than a caveat.
41
+ */
42
+ export declare function cliInstallCommand(manager: PackageManagerId, packageName: string): string;
43
+ /** `npm install @remotedraw/react`, `pnpm add @remotedraw/react`, ... */
44
+ export declare function addPackageCommand(manager: PackageManagerId, packageName: string): string;
45
+ /**
46
+ * Every manager sets `npm_config_user_agent` when it runs a script or a binary
47
+ * it installed, and the name comes first: "pnpm/10.4.1 npm/? node/v24.0.0".
48
+ * This is the only signal that reports the manager actually in use rather than
49
+ * one inferred from files on disk.
50
+ */
51
+ export declare function packageManagerFromUserAgent(env: Record<string, string | undefined>): PackageManagerId | undefined;
52
+ type DetectRuntime = {
53
+ env: Record<string, string | undefined>;
54
+ exists: (filePath: string) => boolean | Promise<boolean>;
55
+ readFile: (filePath: string) => Promise<string>;
56
+ };
57
+ /**
58
+ * Walks up from `dir` looking for the nearest declaration, because the answer
59
+ * usually lives at the root of a monorepo rather than in the package being
60
+ * scaffolded into it.
61
+ *
62
+ * Order: the `packageManager` field (explicit), then a lockfile (evidence),
63
+ * then the running manager's user agent (ambient), then npm.
64
+ */
65
+ export declare function detectPackageManager(dir: string, runtime: DetectRuntime, options?: {
66
+ pathModule?: {
67
+ join: (...parts: string[]) => string;
68
+ dirname: (p: string) => string;
69
+ };
70
+ }): Promise<{
71
+ manager: PackageManagerId;
72
+ reason: string;
73
+ }>;
74
+ export {};
75
+ //# sourceMappingURL=packageManagers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"packageManagers.d.ts","sourceRoot":"","sources":["../src/packageManagers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,iBAAiB,yCAA0C,CAAC;AAEzE,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAElE,MAAM,MAAM,sBAAsB,GAAG;IACnC,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAC;IAChB,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,0DAA0D;IAC1D,GAAG,EAAE,MAAM,CAAC;IACZ,sCAAsC;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,8DAA8D;IAC9D,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,MAAM,CACzC,gBAAgB,EAChB,sBAAsB,CAqCvB,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,gBAAgB,CAE3E;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,gBAAgB,EACzB,WAAW,EAAE,MAAM,GAClB,MAAM,CAKR;AAED,yEAAyE;AACzE,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,gBAAgB,EACzB,WAAW,EAAE,MAAM,GAClB,MAAM,CAER;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GACtC,gBAAgB,GAAG,SAAS,CAK9B;AAED,KAAK,aAAa,GAAG;IACnB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACxC,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACzD,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACjD,CAAC;AAmBF;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,aAAa,EACtB,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE;QAAE,IAAI,EAAE,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,KAAK,MAAM,CAAC;QAAC,OAAO,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;KAAE,CAAA;CAAO,GACtG,OAAO,CAAC;IAAE,OAAO,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAsCxD"}
@@ -0,0 +1,146 @@
1
+ /**
2
+ * One table for the four package managers RemoteDraw supports, and the
3
+ * detection that picks the right one for a project.
4
+ *
5
+ * Two things go wrong without this. First, every surface that prints a command
6
+ * invents its own spelling, and they drift: `pnpm install` is not `pnpm add`,
7
+ * `npx` is not `pnpm dlx`, and Yarn Berry has no global install at all. Second,
8
+ * the CLI defaults to npm and tells a pnpm user to run `npm install` in a
9
+ * pnpm project - a command that works, and quietly leaves a `package-lock.json`
10
+ * next to their `pnpm-lock.yaml`.
11
+ *
12
+ * The table below is the single source of truth. `tests/api/docs-route-coverage`
13
+ * style parity tests keep the docs snippets generated from it rather than typed
14
+ * out beside it.
15
+ */
16
+ export const packageManagerIds = ["npm", "pnpm", "yarn", "bun"];
17
+ export const packageManagerCommands = {
18
+ npm: {
19
+ install: "npm install",
20
+ add: "npm install",
21
+ addGlobal: "npm install -g",
22
+ dlx: "npx",
23
+ run: "npm run",
24
+ lockfiles: ["package-lock.json", "npm-shrinkwrap.json"],
25
+ },
26
+ pnpm: {
27
+ install: "pnpm install",
28
+ add: "pnpm add",
29
+ addGlobal: "pnpm add -g",
30
+ dlx: "pnpm dlx",
31
+ run: "pnpm",
32
+ lockfiles: ["pnpm-lock.yaml"],
33
+ },
34
+ yarn: {
35
+ install: "yarn install",
36
+ add: "yarn add",
37
+ // Berry (Yarn 2+) dropped global installs entirely, and classic's
38
+ // `yarn global add` is deprecated. `yarn dlx` is the one answer that is
39
+ // correct on both.
40
+ addGlobal: null,
41
+ dlx: "yarn dlx",
42
+ run: "yarn",
43
+ lockfiles: ["yarn.lock"],
44
+ },
45
+ bun: {
46
+ install: "bun install",
47
+ add: "bun add",
48
+ addGlobal: "bun add -g",
49
+ dlx: "bunx",
50
+ run: "bun run",
51
+ lockfiles: ["bun.lock", "bun.lockb"],
52
+ },
53
+ };
54
+ export function isPackageManagerId(value) {
55
+ return packageManagerIds.includes(value);
56
+ }
57
+ /**
58
+ * How a reader of the docs gets the CLI. A manager without a global install
59
+ * runs it straight from the registry instead, which is a real answer rather
60
+ * than a caveat.
61
+ */
62
+ export function cliInstallCommand(manager, packageName) {
63
+ const commands = packageManagerCommands[manager];
64
+ return commands.addGlobal
65
+ ? `${commands.addGlobal} ${packageName}`
66
+ : `${commands.dlx} ${packageName}`;
67
+ }
68
+ /** `npm install @remotedraw/react`, `pnpm add @remotedraw/react`, ... */
69
+ export function addPackageCommand(manager, packageName) {
70
+ return `${packageManagerCommands[manager].add} ${packageName}`;
71
+ }
72
+ /**
73
+ * Every manager sets `npm_config_user_agent` when it runs a script or a binary
74
+ * it installed, and the name comes first: "pnpm/10.4.1 npm/? node/v24.0.0".
75
+ * This is the only signal that reports the manager actually in use rather than
76
+ * one inferred from files on disk.
77
+ */
78
+ export function packageManagerFromUserAgent(env) {
79
+ const agent = env.npm_config_user_agent?.trim();
80
+ if (!agent)
81
+ return undefined;
82
+ const name = agent.split("/")[0]?.trim().toLowerCase() ?? "";
83
+ return isPackageManagerId(name) ? name : undefined;
84
+ }
85
+ /**
86
+ * Corepack's `packageManager` field, e.g. "pnpm@10.4.1+sha512...". It is the
87
+ * project's own declaration, so it outranks anything inferred.
88
+ */
89
+ function managerFromManifest(contents) {
90
+ try {
91
+ const parsed = JSON.parse(contents);
92
+ if (typeof parsed.packageManager !== "string")
93
+ return undefined;
94
+ const name = parsed.packageManager.split("@")[0]?.trim().toLowerCase() ?? "";
95
+ return isPackageManagerId(name) ? name : undefined;
96
+ }
97
+ catch {
98
+ // A malformed package.json is the caller's problem, not a reason to fail
99
+ // a question that has a perfectly good fallback.
100
+ return undefined;
101
+ }
102
+ }
103
+ /**
104
+ * Walks up from `dir` looking for the nearest declaration, because the answer
105
+ * usually lives at the root of a monorepo rather than in the package being
106
+ * scaffolded into it.
107
+ *
108
+ * Order: the `packageManager` field (explicit), then a lockfile (evidence),
109
+ * then the running manager's user agent (ambient), then npm.
110
+ */
111
+ export async function detectPackageManager(dir, runtime, options = {}) {
112
+ const join = options.pathModule?.join ?? ((...parts) => parts.join("/"));
113
+ const dirname = options.pathModule?.dirname ??
114
+ ((value) => value.slice(0, Math.max(value.lastIndexOf("/"), 0)));
115
+ let current = dir;
116
+ // A repo root is a handful of levels up at most; the bound is only here so a
117
+ // symlink loop cannot turn detection into a hang.
118
+ for (let depth = 0; depth < 32; depth += 1) {
119
+ const manifest = join(current, "package.json");
120
+ if (await runtime.exists(manifest)) {
121
+ const declared = managerFromManifest(await runtime.readFile(manifest));
122
+ if (declared) {
123
+ return {
124
+ manager: declared,
125
+ reason: `${manifest} declares packageManager: ${declared}`,
126
+ };
127
+ }
128
+ }
129
+ for (const manager of packageManagerIds) {
130
+ for (const lockfile of packageManagerCommands[manager].lockfiles) {
131
+ if (await runtime.exists(join(current, lockfile))) {
132
+ return { manager, reason: `found ${join(current, lockfile)}` };
133
+ }
134
+ }
135
+ }
136
+ const parent = dirname(current);
137
+ if (!parent || parent === current)
138
+ break;
139
+ current = parent;
140
+ }
141
+ const ambient = packageManagerFromUserAgent(runtime.env);
142
+ if (ambient) {
143
+ return { manager: ambient, reason: `running under ${ambient}` };
144
+ }
145
+ return { manager: "npm", reason: "no lockfile or declaration found" };
146
+ }
package/dist/scan.d.ts ADDED
@@ -0,0 +1,77 @@
1
+ import { type PackageManagerId } from "./packageManagers.js";
2
+ export type ScanRuntime = {
3
+ cwd: string;
4
+ exists: (filePath: string) => boolean | Promise<boolean>;
5
+ readFile: (filePath: string) => Promise<string>;
6
+ readdir?: (dirPath: string) => Promise<string[]>;
7
+ /** Only read for `npm_config_user_agent`, when nothing on disk answers. */
8
+ env?: Record<string, string | undefined>;
9
+ };
10
+ export type ScanProjectKind = "web" | "server" | "ios" | "library" | "unknown";
11
+ export type ScanProject = {
12
+ /** Directory relative to the scan root; `.` for the root itself. */
13
+ path: string;
14
+ name?: string;
15
+ kind: ScanProjectKind;
16
+ /** Framework and runtime signals, deduplicated, stable order. */
17
+ frameworks: string[];
18
+ /** Human-readable proof for every claim above, so the agent can verify. */
19
+ evidence: string[];
20
+ /** For web projects: whether server-side code was found *inside* them. */
21
+ serverSide?: string[];
22
+ };
23
+ export type ScanRecommendation = {
24
+ id: string;
25
+ title: string;
26
+ fit: "strong" | "possible";
27
+ receiver: string;
28
+ sender: string;
29
+ keyLocation: string;
30
+ why: string;
31
+ commands: string[];
32
+ packages: string[];
33
+ };
34
+ export type ScanReport = {
35
+ ok: true;
36
+ command: "scan";
37
+ schemaVersion: 1;
38
+ root: string;
39
+ /**
40
+ * The manager this project already uses, and the evidence for it. An agent
41
+ * that installs with the wrong one leaves a second lockfile behind, which is
42
+ * a mess a human then has to notice and clean up.
43
+ */
44
+ packageManager: {
45
+ id: PackageManagerId;
46
+ reason: string;
47
+ /** Ready to run, so nothing downstream has to spell them itself. */
48
+ commands: {
49
+ install: string;
50
+ add: string;
51
+ dlx: string;
52
+ run: string;
53
+ };
54
+ };
55
+ projects: ScanProject[];
56
+ remotedraw: {
57
+ configured: boolean;
58
+ configFiles: string[];
59
+ packages: string[];
60
+ envKeys: string[];
61
+ swiftPackage: boolean;
62
+ };
63
+ keyBoundary: {
64
+ /** Directories where an `rd_sk_` key may live. Empty means "none found". */
65
+ serverSide: string[];
66
+ warning?: string;
67
+ };
68
+ recommendations: ScanRecommendation[];
69
+ /** Product decisions the agent must ask the user, not decide. */
70
+ questions: string[];
71
+ /** Hard rules for whatever the agent builds next. */
72
+ rules: string[];
73
+ };
74
+ export declare function scanProject(root: string, runtime: ScanRuntime): Promise<ScanReport>;
75
+ export declare const INTEGRATION_RULES: string[];
76
+ export declare function renderScanText(report: ScanReport): string;
77
+ //# sourceMappingURL=scan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../src/scan.ts"],"names":[],"mappings":"AAiBA,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,sBAAsB,CAAC;AAE9B,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACzD,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACjD,2EAA2E;IAC3E,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;CAC1C,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,QAAQ,GAAG,KAAK,GAAG,SAAS,GAAG,SAAS,CAAC;AAE/E,MAAM,MAAM,WAAW,GAAG;IACxB,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,eAAe,CAAC;IACtB,iEAAiE;IACjE,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,IAAI,CAAC;IACT,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,CAAC,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,cAAc,EAAE;QACd,EAAE,EAAE,gBAAgB,CAAC;QACrB,MAAM,EAAE,MAAM,CAAC;QACf,oEAAoE;QACpE,QAAQ,EAAE;YAAE,OAAO,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAA;SAAE,CAAC;KACtE,CAAC;IACF,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,UAAU,EAAE;QACV,UAAU,EAAE,OAAO,CAAC;QACpB,WAAW,EAAE,MAAM,EAAE,CAAC;QACtB,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,YAAY,EAAE,OAAO,CAAC;KACvB,CAAC;IACF,WAAW,EAAE;QACX,4EAA4E;QAC5E,UAAU,EAAE,MAAM,EAAE,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,eAAe,EAAE,kBAAkB,EAAE,CAAC;IACtC,iEAAiE;IACjE,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,qDAAqD;IACrD,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC;AAoPF,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,CAAC,CA6KrB;AA6HD,eAAO,MAAM,iBAAiB,UAa7B,CAAC;AAEF,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,UAiDhD"}