@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.
- package/README.md +36 -6
- package/dist/cli.d.ts +17 -5
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +261 -64
- package/dist/cloud.d.ts +1 -1
- package/dist/cloud.d.ts.map +1 -1
- package/dist/generated/agent-skill.d.ts +1 -1
- package/dist/generated/agent-skill.d.ts.map +1 -1
- package/dist/generated/agent-skill.js +1 -1
- package/dist/index.js +1 -1
- package/dist/locales/en.d.ts +27 -12
- package/dist/locales/en.d.ts.map +1 -1
- package/dist/locales/en.js +27 -12
- package/dist/locales/index.d.ts +28 -13
- package/dist/locales/index.d.ts.map +1 -1
- package/dist/locales/nl.d.ts.map +1 -1
- package/dist/locales/nl.js +26 -11
- package/dist/packageManagers.d.ts +75 -0
- package/dist/packageManagers.d.ts.map +1 -0
- package/dist/packageManagers.js +146 -0
- package/dist/scan.d.ts +77 -0
- package/dist/scan.d.ts.map +1 -0
- package/dist/scan.js +563 -0
- package/dist/telemetry.d.ts +1 -1
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/update.d.ts +11 -5
- package/dist/update.d.ts.map +1 -1
- package/dist/update.js +55 -28
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -5,11 +5,13 @@ import path from "node:path";
|
|
|
5
5
|
import process from "node:process";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { emitKeypressEvents } from "node:readline";
|
|
8
|
+
import { renderScanText, scanProject } from "./scan.js";
|
|
8
9
|
import { createInterface } from "node:readline/promises";
|
|
9
10
|
import { createWizardTerminal, runSetupWizard, } from "./setup-wizard.js";
|
|
10
|
-
import { assertEnvCanAcceptProvisioning, currentCliAccount, DEFAULT_REMOTEDRAW_API_BASE_URL, globalConfigPath, loginToRemoteDraw, logoutFromRemoteDraw, provisionRemoteDrawProject, readStoredLanguage, remoteDrawApiBaseUrl, writeStoredLanguage, writeProvisionedProjectSetup, } from "./cloud.js";
|
|
11
|
+
import { assertEnvCanAcceptProvisioning, currentCliAccount, DEFAULT_REMOTEDRAW_API_BASE_URL, globalConfigPath, loginToRemoteDraw, logoutFromRemoteDraw, provisionRemoteDrawProject, updateEnvContents, readStoredLanguage, remoteDrawApiBaseUrl, writeStoredLanguage, writeProvisionedProjectSetup, } from "./cloud.js";
|
|
11
12
|
import { LANGUAGE_ENV_VARS, SOURCE_LOCALE, extractLanguageFlag, languageChoices, languagePromptMessage, localeEndonym, normalizeLocale, resolveLocale, setActiveLocale, supportedLocaleList, SUPPORTED_LOCALES, systemLocaleHint, t, } from "./i18n.js";
|
|
12
13
|
import { CliError, cliError } from "./errors.js";
|
|
14
|
+
import { detectPackageManager, packageManagerCommands, packageManagerIds, } from "./packageManagers.js";
|
|
13
15
|
import { AGENT_SKILL_MARKDOWN } from "./generated/agent-skill.js";
|
|
14
16
|
import { reportCliCrash } from "./telemetry.js";
|
|
15
17
|
import { checkForUpdate, CLI_PACKAGE_NAME, detectInstallMethod, installMethodDisplay, runInstall, } from "./update.js";
|
|
@@ -23,8 +25,8 @@ export const CLI_VERSION = typeof packageMetadata.version === "string"
|
|
|
23
25
|
// private, and would silently accept a future breaking major once they were
|
|
24
26
|
// not. tests/generated-manifest.test.ts cross-checks every range below against
|
|
25
27
|
// the workspace manifests.
|
|
26
|
-
export const REMOTEDRAW_SDK_RANGE = "^0.
|
|
27
|
-
export const REMOTEDRAW_CLI_RANGE = "^0.
|
|
28
|
+
export const REMOTEDRAW_SDK_RANGE = "^0.3.0";
|
|
29
|
+
export const REMOTEDRAW_CLI_RANGE = "^0.3.0";
|
|
28
30
|
export const targetChoices = ["web", "desktop", "ios", "headless"];
|
|
29
31
|
export const senderChoices = [
|
|
30
32
|
"remotedraw-ios",
|
|
@@ -55,7 +57,7 @@ const exampleChoices = [
|
|
|
55
57
|
"raw-http",
|
|
56
58
|
"ios-owned-sender",
|
|
57
59
|
];
|
|
58
|
-
const packageManagerChoices =
|
|
60
|
+
const packageManagerChoices = packageManagerIds;
|
|
59
61
|
export function createNodeRuntime() {
|
|
60
62
|
const cwd = process.cwd();
|
|
61
63
|
const env = cliEnvironmentForWorkspace(cwd, process.env);
|
|
@@ -355,6 +357,10 @@ export async function runCli(args, runtime = createNodeRuntime()) {
|
|
|
355
357
|
return await examplesCommand(commandArgs, runtime);
|
|
356
358
|
case "agent":
|
|
357
359
|
return await agentCommand(commandArgs, runtime);
|
|
360
|
+
case "scan":
|
|
361
|
+
return await scanCommand(commandArgs, runtime);
|
|
362
|
+
case "project":
|
|
363
|
+
return await projectCommand(commandArgs, runtime);
|
|
358
364
|
default:
|
|
359
365
|
return requestedJsonOutput(args)
|
|
360
366
|
? jsonFailure(command, new Error(t("error.unknownCommand", { command })))
|
|
@@ -392,7 +398,9 @@ async function setupWizardCommand(runtime, askLanguage) {
|
|
|
392
398
|
await loginToRemoteDraw(runtime, apiBaseUrl);
|
|
393
399
|
}
|
|
394
400
|
const result = await runSetupWizard({
|
|
395
|
-
definition: projectSetupDefinition(
|
|
401
|
+
definition: projectSetupDefinition({
|
|
402
|
+
packageManager: await projectPackageManager(runtime.cwd, runtime),
|
|
403
|
+
}),
|
|
396
404
|
terminal,
|
|
397
405
|
execute: async (values) => {
|
|
398
406
|
const plan = planFromWizardValues(values);
|
|
@@ -603,6 +611,8 @@ function mainHelpText() {
|
|
|
603
611
|
command("create-input", t("help.command.createInput")),
|
|
604
612
|
command("examples", t("help.command.examples")),
|
|
605
613
|
command("agent", t("help.command.agent")),
|
|
614
|
+
command("scan", t("help.command.scan")),
|
|
615
|
+
command("project", t("help.command.project")),
|
|
606
616
|
"",
|
|
607
617
|
t("help.startHere"),
|
|
608
618
|
" remotedraw guide",
|
|
@@ -693,15 +703,33 @@ async function logoutCommand(args, runtime) {
|
|
|
693
703
|
}
|
|
694
704
|
async function whoamiCommand(args, runtime) {
|
|
695
705
|
const parsed = parseArgs(args, {
|
|
696
|
-
values: ["api-base-url"],
|
|
706
|
+
values: ["api-base-url", "format"],
|
|
697
707
|
booleans: ["help"],
|
|
698
708
|
});
|
|
699
709
|
if (parsed.booleans.has("help"))
|
|
700
710
|
return ok(whoamiHelpText());
|
|
711
|
+
const format = outputFormat(parsed);
|
|
701
712
|
const apiBaseUrl = remoteDrawApiBaseUrl(runtime.env, readString(parsed, "api-base-url"));
|
|
702
713
|
const account = await currentCliAccount(runtime, apiBaseUrl);
|
|
703
714
|
if (!account) {
|
|
704
|
-
|
|
715
|
+
const message = t("whoami.notLoggedIn", { url: apiBaseUrl });
|
|
716
|
+
return format === "json"
|
|
717
|
+
? jsonFailure("whoami", cliError("AUTH_REQUIRED", "whoami.notLoggedIn", { url: apiBaseUrl }))
|
|
718
|
+
: fail(message);
|
|
719
|
+
}
|
|
720
|
+
if (format === "json") {
|
|
721
|
+
return jsonResult({
|
|
722
|
+
ok: true,
|
|
723
|
+
command: "whoami",
|
|
724
|
+
apiBaseUrl,
|
|
725
|
+
account: {
|
|
726
|
+
userId: account.userId,
|
|
727
|
+
tenantId: account.tenantId,
|
|
728
|
+
tenantSlug: account.tenantSlug,
|
|
729
|
+
...(account.email ? { email: account.email } : {}),
|
|
730
|
+
...(account.name ? { name: account.name } : {}),
|
|
731
|
+
},
|
|
732
|
+
});
|
|
705
733
|
}
|
|
706
734
|
return ok([accountSummary(account), t("whoami.api", { url: apiBaseUrl })].join("\n"));
|
|
707
735
|
}
|
|
@@ -738,6 +766,9 @@ async function newCommand(args, runtime, askLanguage = false) {
|
|
|
738
766
|
: planFromArgs(parsed, {
|
|
739
767
|
appNameRequired: true,
|
|
740
768
|
defaultAppName: "RemoteDraw app",
|
|
769
|
+
defaults: {
|
|
770
|
+
packageManager: await projectPackageManager(runtime.cwd, runtime),
|
|
771
|
+
},
|
|
741
772
|
});
|
|
742
773
|
const outputDir = resolveOutputDir(runtime.cwd, parsed, plan.slug);
|
|
743
774
|
const dryRun = parsed.booleans.has("dry-run");
|
|
@@ -791,6 +822,9 @@ async function initCommand(args, runtime) {
|
|
|
791
822
|
const plan = planFromArgs(parsed, {
|
|
792
823
|
appNameRequired: false,
|
|
793
824
|
defaultAppName,
|
|
825
|
+
defaults: {
|
|
826
|
+
packageManager: await projectPackageManager(outputDir, runtime),
|
|
827
|
+
},
|
|
794
828
|
});
|
|
795
829
|
const dryRun = parsed.booleans.has("dry-run");
|
|
796
830
|
const force = parsed.booleans.has("force");
|
|
@@ -933,7 +967,7 @@ async function updateCommand(args, runtime) {
|
|
|
933
967
|
return ok(updateHelpText());
|
|
934
968
|
const format = outputFormat(parsed);
|
|
935
969
|
const checkOnly = parsed.booleans.has("check");
|
|
936
|
-
const method = detectInstallMethod(runtime.binPath ?? "");
|
|
970
|
+
const method = detectInstallMethod(runtime.binPath ?? "", runtime.env);
|
|
937
971
|
const command = installMethodDisplay(method);
|
|
938
972
|
// `remotedraw update` is an explicit request, so it ignores the cache and the
|
|
939
973
|
// ambient opt-outs that only silence the passive notice.
|
|
@@ -1243,7 +1277,10 @@ async function examplesCommand(args, runtime) {
|
|
|
1243
1277
|
const plan = planFromArgs(parsed, {
|
|
1244
1278
|
appNameRequired: false,
|
|
1245
1279
|
defaultAppName: readString(parsed, "app-name") ?? titleCase(installedExample),
|
|
1246
|
-
defaults
|
|
1280
|
+
defaults: {
|
|
1281
|
+
packageManager: await projectPackageManager(runtime.cwd, runtime),
|
|
1282
|
+
...defaults,
|
|
1283
|
+
},
|
|
1247
1284
|
});
|
|
1248
1285
|
const files = newProjectFiles(plan);
|
|
1249
1286
|
const dryRun = parsed.booleans.has("dry-run");
|
|
@@ -1253,6 +1290,125 @@ async function examplesCommand(args, runtime) {
|
|
|
1253
1290
|
});
|
|
1254
1291
|
return ok(scaffoldSummary(t("scaffold.verb.installedExample"), plan, outputDir, written, dryRun));
|
|
1255
1292
|
}
|
|
1293
|
+
async function projectCommand(args, runtime) {
|
|
1294
|
+
const parsed = parseArgs(args, {
|
|
1295
|
+
values: ["name", "key-name", "api-base-url", "env", "format"],
|
|
1296
|
+
booleans: ["help", "force"],
|
|
1297
|
+
});
|
|
1298
|
+
const subcommand = parsed.positionals[0];
|
|
1299
|
+
if (parsed.booleans.has("help") || subcommand == null) {
|
|
1300
|
+
return ok(projectHelpText());
|
|
1301
|
+
}
|
|
1302
|
+
if (subcommand !== "create") {
|
|
1303
|
+
throw cliError("INVALID_ARGUMENT", "project.error.unknownSubcommand", {
|
|
1304
|
+
subcommand,
|
|
1305
|
+
});
|
|
1306
|
+
}
|
|
1307
|
+
const format = outputFormat(parsed);
|
|
1308
|
+
const name = readString(parsed, "name");
|
|
1309
|
+
if (name == null) {
|
|
1310
|
+
throw cliError("MISSING_ARGUMENT", "project.error.nameRequired");
|
|
1311
|
+
}
|
|
1312
|
+
const apiBaseUrl = readString(parsed, "api-base-url") ??
|
|
1313
|
+
runtime.env.REMOTEDRAW_API_BASE_URL ??
|
|
1314
|
+
DEFAULT_REMOTEDRAW_API_BASE_URL;
|
|
1315
|
+
const keyName = readString(parsed, "key-name");
|
|
1316
|
+
// Provisioning is separate from scaffolding on purpose: an integration that
|
|
1317
|
+
// already has its files must not have to --force-overwrite them to get a key.
|
|
1318
|
+
const provisioning = await provisionRemoteDrawProject(runtime, apiBaseUrl, {
|
|
1319
|
+
name,
|
|
1320
|
+
...(keyName ? { keyName } : {}),
|
|
1321
|
+
});
|
|
1322
|
+
const envDir = readString(parsed, "env");
|
|
1323
|
+
let envPath;
|
|
1324
|
+
if (envDir != null) {
|
|
1325
|
+
const outputDir = path.resolve(runtime.cwd, envDir);
|
|
1326
|
+
const force = parsed.booleans.has("force");
|
|
1327
|
+
await assertEnvCanAcceptProvisioning(runtime, outputDir, apiBaseUrl, force);
|
|
1328
|
+
await runtime.mkdir(outputDir);
|
|
1329
|
+
envPath = path.join(outputDir, ".env.local");
|
|
1330
|
+
const existing = (await runtime.exists(envPath))
|
|
1331
|
+
? await runtime.readFile(envPath)
|
|
1332
|
+
: "# Generated by RemoteDraw CLI. Server-side secrets only.\n";
|
|
1333
|
+
const next = updateEnvContents(existing, {
|
|
1334
|
+
REMOTEDRAW_API_BASE_URL: provisioning.apiBaseUrl,
|
|
1335
|
+
REMOTEDRAW_PROJECT_ID: provisioning.project.id,
|
|
1336
|
+
REMOTEDRAW_API_KEY: provisioning.apiKey.value,
|
|
1337
|
+
}, force);
|
|
1338
|
+
if (runtime.writePrivateFile) {
|
|
1339
|
+
await runtime.writePrivateFile(envPath, next);
|
|
1340
|
+
}
|
|
1341
|
+
else {
|
|
1342
|
+
await runtime.writeFile(envPath, next);
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
if (format === "json") {
|
|
1346
|
+
return jsonResult({
|
|
1347
|
+
ok: true,
|
|
1348
|
+
command: "project",
|
|
1349
|
+
subcommand: "create",
|
|
1350
|
+
project: provisioning.project,
|
|
1351
|
+
apiKey: {
|
|
1352
|
+
id: provisioning.apiKey.id,
|
|
1353
|
+
name: provisioning.apiKey.name,
|
|
1354
|
+
// Shown exactly once, here. Written to .env.local only with --env.
|
|
1355
|
+
...(envPath ? {} : { value: provisioning.apiKey.value }),
|
|
1356
|
+
},
|
|
1357
|
+
apiBaseUrl: provisioning.apiBaseUrl,
|
|
1358
|
+
...(envPath ? { envPath } : {}),
|
|
1359
|
+
});
|
|
1360
|
+
}
|
|
1361
|
+
return ok([
|
|
1362
|
+
t("project.created", {
|
|
1363
|
+
name: provisioning.project.name,
|
|
1364
|
+
id: provisioning.project.id,
|
|
1365
|
+
}),
|
|
1366
|
+
` ${provisioning.project.dashboardUrl}`,
|
|
1367
|
+
"",
|
|
1368
|
+
envPath
|
|
1369
|
+
? t("project.keyWritten", { path: envPath })
|
|
1370
|
+
: t("project.keyOnce", { key: provisioning.apiKey.value }),
|
|
1371
|
+
"",
|
|
1372
|
+
t("project.keyRule"),
|
|
1373
|
+
].join("\n"));
|
|
1374
|
+
}
|
|
1375
|
+
function projectHelpText() {
|
|
1376
|
+
return [
|
|
1377
|
+
t("word.usage"),
|
|
1378
|
+
t("project.help.usage"),
|
|
1379
|
+
"",
|
|
1380
|
+
t("project.help.body"),
|
|
1381
|
+
t("project.help.env"),
|
|
1382
|
+
t("project.help.auth"),
|
|
1383
|
+
].join("\n");
|
|
1384
|
+
}
|
|
1385
|
+
async function scanCommand(args, runtime) {
|
|
1386
|
+
const parsed = parseArgs(args, {
|
|
1387
|
+
values: ["path", "format"],
|
|
1388
|
+
booleans: ["help"],
|
|
1389
|
+
});
|
|
1390
|
+
if (parsed.booleans.has("help"))
|
|
1391
|
+
return ok(scanHelpText());
|
|
1392
|
+
const format = outputFormat(parsed);
|
|
1393
|
+
const root = resolveOutputDir(runtime.cwd, parsed);
|
|
1394
|
+
if (!(await runtime.exists(root))) {
|
|
1395
|
+
throw cliError("INVALID_ARGUMENT", "scan.error.missingPath", { path: root });
|
|
1396
|
+
}
|
|
1397
|
+
const report = await scanProject(root, runtime);
|
|
1398
|
+
if (format === "json")
|
|
1399
|
+
return jsonResult(report);
|
|
1400
|
+
return ok(renderScanText(report));
|
|
1401
|
+
}
|
|
1402
|
+
function scanHelpText() {
|
|
1403
|
+
return [
|
|
1404
|
+
t("word.usage"),
|
|
1405
|
+
t("scan.help.usage"),
|
|
1406
|
+
"",
|
|
1407
|
+
t("scan.help.body"),
|
|
1408
|
+
t("scan.help.privacy"),
|
|
1409
|
+
t("scan.help.json"),
|
|
1410
|
+
].join("\n");
|
|
1411
|
+
}
|
|
1256
1412
|
async function agentCommand(args, runtime) {
|
|
1257
1413
|
const parsed = parseArgs(args, {
|
|
1258
1414
|
values: ["path"],
|
|
@@ -1496,10 +1652,12 @@ function shouldPromptForNewProject(parsed, runtime) {
|
|
|
1496
1652
|
}
|
|
1497
1653
|
async function promptForNewProject(parsed, runtime) {
|
|
1498
1654
|
const prompts = runtime.prompts;
|
|
1655
|
+
const detectedPackageManager = await projectPackageManager(runtime.cwd, runtime);
|
|
1499
1656
|
if (!prompts) {
|
|
1500
1657
|
return planFromArgs(parsed, {
|
|
1501
1658
|
appNameRequired: true,
|
|
1502
1659
|
defaultAppName: "RemoteDraw app",
|
|
1660
|
+
defaults: { packageManager: detectedPackageManager },
|
|
1503
1661
|
});
|
|
1504
1662
|
}
|
|
1505
1663
|
await prompts.intro?.(t("prompt.intro.setup"));
|
|
@@ -1532,7 +1690,7 @@ async function promptForNewProject(parsed, runtime) {
|
|
|
1532
1690
|
const packageManager = readString(parsed, "package-manager") == null
|
|
1533
1691
|
? await prompts.select({
|
|
1534
1692
|
message: t("field.packageManager"),
|
|
1535
|
-
defaultValue:
|
|
1693
|
+
defaultValue: detectedPackageManager,
|
|
1536
1694
|
choices: packageManagerPromptChoices(),
|
|
1537
1695
|
})
|
|
1538
1696
|
: choice(readString(parsed, "package-manager"), packageManagerChoices, "package-manager");
|
|
@@ -1987,7 +2145,7 @@ function agentOptionCatalog() {
|
|
|
1987
2145
|
},
|
|
1988
2146
|
};
|
|
1989
2147
|
}
|
|
1990
|
-
export function projectSetupDefinition() {
|
|
2148
|
+
export function projectSetupDefinition(detected = {}) {
|
|
1991
2149
|
return {
|
|
1992
2150
|
defaults: {
|
|
1993
2151
|
appName: "RemoteDraw App",
|
|
@@ -1995,7 +2153,8 @@ export function projectSetupDefinition() {
|
|
|
1995
2153
|
sender: "remotedraw-ios",
|
|
1996
2154
|
sdk: "react",
|
|
1997
2155
|
preset: "sketch",
|
|
1998
|
-
|
|
2156
|
+
// npm only when the project has not already answered this itself.
|
|
2157
|
+
packageManager: detected.packageManager ?? "npm",
|
|
1999
2158
|
},
|
|
2000
2159
|
fields: projectSetupFields(),
|
|
2001
2160
|
normalize(values, changed) {
|
|
@@ -2399,7 +2558,7 @@ function initPackageJson(plan) {
|
|
|
2399
2558
|
}
|
|
2400
2559
|
function configJson(plan) {
|
|
2401
2560
|
return {
|
|
2402
|
-
$schema: "https://
|
|
2561
|
+
$schema: "https://remotedraw.com/schemas/remotedraw.config.json",
|
|
2403
2562
|
appName: plan.appName,
|
|
2404
2563
|
environment: "dev",
|
|
2405
2564
|
target: plan.target,
|
|
@@ -2467,12 +2626,18 @@ function integrationReadme(plan) {
|
|
|
2467
2626
|
}
|
|
2468
2627
|
function reactBackendSessionTs(plan) {
|
|
2469
2628
|
return [
|
|
2629
|
+
"// SERVER-SIDE ONLY. This file reads REMOTEDRAW_API_KEY (an rd_sk_ secret).",
|
|
2630
|
+
"// It must run where secrets live — an API route, a server action, a Convex",
|
|
2631
|
+
"// action, a serverless function — never in browser or app code. If this",
|
|
2632
|
+
"// landed under a Vite/Next client tree, move it to your server before use.",
|
|
2633
|
+
"",
|
|
2470
2634
|
'import { createHttpRemoteDrawApiClient } from "@remotedraw/react";',
|
|
2471
2635
|
'import type { CreateSessionRequest } from "@remotedraw/react";',
|
|
2472
2636
|
"",
|
|
2473
2637
|
"const apiBaseUrl = process.env.REMOTEDRAW_API_BASE_URL;",
|
|
2474
2638
|
"const apiKey = process.env.REMOTEDRAW_API_KEY;",
|
|
2475
2639
|
"",
|
|
2640
|
+
...mapBoundsNote(plan.preset),
|
|
2476
2641
|
"export function createRemoteDrawSessionRequest(): CreateSessionRequest {",
|
|
2477
2642
|
" return " +
|
|
2478
2643
|
JSON.stringify(createSessionPayload({
|
|
@@ -2654,12 +2819,18 @@ function reactSenderTsx() {
|
|
|
2654
2819
|
}
|
|
2655
2820
|
function svelteBackendSessionTs(plan) {
|
|
2656
2821
|
return [
|
|
2822
|
+
"// SERVER-SIDE ONLY. This file reads REMOTEDRAW_API_KEY (an rd_sk_ secret).",
|
|
2823
|
+
"// It must run where secrets live — an API route, a server action, a Convex",
|
|
2824
|
+
"// action, a serverless function — never in browser or app code. If this",
|
|
2825
|
+
"// landed under a Vite/Next client tree, move it to your server before use.",
|
|
2826
|
+
"",
|
|
2657
2827
|
'import { createHttpRemoteDrawApiClient } from "@remotedraw/svelte";',
|
|
2658
2828
|
'import type { CreateSessionRequest } from "@remotedraw/svelte";',
|
|
2659
2829
|
"",
|
|
2660
2830
|
"const apiBaseUrl = process.env.REMOTEDRAW_API_BASE_URL;",
|
|
2661
2831
|
"const apiKey = process.env.REMOTEDRAW_API_KEY;",
|
|
2662
2832
|
"",
|
|
2833
|
+
...mapBoundsNote(plan.preset),
|
|
2663
2834
|
"export function createRemoteDrawSessionRequest(): CreateSessionRequest {",
|
|
2664
2835
|
" return " +
|
|
2665
2836
|
JSON.stringify(createSessionPayload({
|
|
@@ -2730,6 +2901,11 @@ function rawHttpSessionTs(plan) {
|
|
|
2730
2901
|
// declares nothing and stays import-free.
|
|
2731
2902
|
const typed = plan.sdk === "js";
|
|
2732
2903
|
return [
|
|
2904
|
+
"// SERVER-SIDE ONLY. This file reads REMOTEDRAW_API_KEY (an rd_sk_ secret).",
|
|
2905
|
+
"// It must run where secrets live — an API route, a server action, a Convex",
|
|
2906
|
+
"// action, a serverless function — never in browser or app code. If this",
|
|
2907
|
+
"// landed under a Vite/Next client tree, move it to your server before use.",
|
|
2908
|
+
"",
|
|
2733
2909
|
...(typed
|
|
2734
2910
|
? [
|
|
2735
2911
|
"import type {",
|
|
@@ -2742,6 +2918,7 @@ function rawHttpSessionTs(plan) {
|
|
|
2742
2918
|
"const apiBaseUrl = process.env.REMOTEDRAW_API_BASE_URL;",
|
|
2743
2919
|
"const apiKey = process.env.REMOTEDRAW_API_KEY;",
|
|
2744
2920
|
"",
|
|
2921
|
+
...mapBoundsNote(plan.preset),
|
|
2745
2922
|
`export function createRemoteDrawSessionRequest()${typed ? ": CreateSessionRequest" : ""} {`,
|
|
2746
2923
|
" return " +
|
|
2747
2924
|
JSON.stringify(createSessionPayload({
|
|
@@ -3029,14 +3206,53 @@ function reactViteConfig() {
|
|
|
3029
3206
|
"",
|
|
3030
3207
|
].join("\n");
|
|
3031
3208
|
}
|
|
3209
|
+
/**
|
|
3210
|
+
* The example fence a map preset ships with.
|
|
3211
|
+
*
|
|
3212
|
+
* A map session's `coordinateSpace.bounds` is what every point on the wire is
|
|
3213
|
+
* normalized against — `x` linear in longitude, `y` linear in Web Mercator — and
|
|
3214
|
+
* it cannot be changed once the session exists. Omitting it does not mean "the
|
|
3215
|
+
* customer's map": it means the server's own default region, which is how
|
|
3216
|
+
* `remotedraw init --preset mapMarkup` used to produce a phone showing New York
|
|
3217
|
+
* whatever the customer's map was pointed at.
|
|
3218
|
+
*
|
|
3219
|
+
* So the preset states one, explicitly and visibly, with the note above it in
|
|
3220
|
+
* the generated file. `width`/`height` are that region's Mercator aspect.
|
|
3221
|
+
*/
|
|
3222
|
+
const MAP_PRESET_COORDINATE_SPACE = {
|
|
3223
|
+
width: 1600,
|
|
3224
|
+
height: 1310,
|
|
3225
|
+
bounds: { minX: 4.85, minY: 52.35, maxX: 4.95, maxY: 52.4 },
|
|
3226
|
+
};
|
|
3227
|
+
/**
|
|
3228
|
+
* The comment that has to travel with the bounds. A map board's fence is the
|
|
3229
|
+
* one decision this scaffold cannot walk back for the customer.
|
|
3230
|
+
*/
|
|
3231
|
+
function mapBoundsNote(preset) {
|
|
3232
|
+
if (PRESETS[preset].kind !== "map")
|
|
3233
|
+
return [];
|
|
3234
|
+
return [
|
|
3235
|
+
"// `target.coordinateSpace.bounds` is this board's geographic fence: every",
|
|
3236
|
+
"// point on the wire is normalized against it (x linear in longitude, y",
|
|
3237
|
+
"// linear in Web Mercator). It is FIXED for the life of the session — no",
|
|
3238
|
+
"// route changes it — so size it LARGER than the camera you open on, or a",
|
|
3239
|
+
"// sender that pans is drawing at the edge of the world. Replace the example",
|
|
3240
|
+
"// region below with yours; padMapBounds(camera, 1) from @remotedraw/geometry",
|
|
3241
|
+
"// grows a camera into a fence. width/height are that fence's Mercator aspect.",
|
|
3242
|
+
"",
|
|
3243
|
+
];
|
|
3244
|
+
}
|
|
3032
3245
|
export function createSessionPayload(options) {
|
|
3033
3246
|
const descriptor = descriptorForPreset(options.preset, options.label);
|
|
3034
3247
|
return {
|
|
3035
3248
|
externalId: options.externalId,
|
|
3036
3249
|
markupPreset: options.preset,
|
|
3037
3250
|
target: {
|
|
3038
|
-
kind:
|
|
3251
|
+
kind: PRESETS[options.preset].kind,
|
|
3039
3252
|
label: options.label,
|
|
3253
|
+
...(PRESETS[options.preset].kind === "map"
|
|
3254
|
+
? { coordinateSpace: MAP_PRESET_COORDINATE_SPACE }
|
|
3255
|
+
: {}),
|
|
3040
3256
|
metadata: {
|
|
3041
3257
|
descriptor,
|
|
3042
3258
|
},
|
|
@@ -3064,7 +3280,7 @@ function descriptorForPreset(preset, label) {
|
|
|
3064
3280
|
return {
|
|
3065
3281
|
version: 1,
|
|
3066
3282
|
privacy: "metadata-only",
|
|
3067
|
-
surfaceRole:
|
|
3283
|
+
surfaceRole: PRESETS[preset].role,
|
|
3068
3284
|
inputIntent: preset === "pointer" ? "point" : "annotate",
|
|
3069
3285
|
label,
|
|
3070
3286
|
phoneHints: {
|
|
@@ -3076,42 +3292,18 @@ function descriptorForPreset(preset, label) {
|
|
|
3076
3292
|
},
|
|
3077
3293
|
};
|
|
3078
3294
|
}
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
return "screen";
|
|
3090
|
-
return "svg";
|
|
3091
|
-
}
|
|
3092
|
-
function surfaceRoleForPreset(preset) {
|
|
3093
|
-
if (preset === "photoMarkup" || preset === "designReview")
|
|
3094
|
-
return "image";
|
|
3095
|
-
if (preset === "mapMarkup")
|
|
3096
|
-
return "map";
|
|
3097
|
-
if (preset === "screenMarkup" || preset === "pointer")
|
|
3098
|
-
return "screen";
|
|
3099
|
-
if (preset === "sketch")
|
|
3100
|
-
return "canvas";
|
|
3101
|
-
return "document";
|
|
3102
|
-
}
|
|
3295
|
+
const PRESETS = {
|
|
3296
|
+
approval: { kind: "field", role: "document", label: "Quick approval" },
|
|
3297
|
+
sketch: { kind: "svg", role: "canvas", label: "Sketch input" },
|
|
3298
|
+
photoMarkup: { kind: "image", role: "image", label: "Photo annotation" },
|
|
3299
|
+
pdfMarkup: { kind: "pdf", role: "document", label: "PDF annotation" },
|
|
3300
|
+
mapMarkup: { kind: "map", role: "map", label: "Map annotation" },
|
|
3301
|
+
screenMarkup: { kind: "screen", role: "screen", label: "Screen annotation" },
|
|
3302
|
+
designReview: { kind: "image", role: "image", label: "Design review" },
|
|
3303
|
+
pointer: { kind: "screen", role: "screen", label: "Pointer input" },
|
|
3304
|
+
};
|
|
3103
3305
|
function labelForPreset(preset) {
|
|
3104
|
-
|
|
3105
|
-
approval: "Quick approval",
|
|
3106
|
-
sketch: "Sketch input",
|
|
3107
|
-
photoMarkup: "Photo annotation",
|
|
3108
|
-
pdfMarkup: "PDF annotation",
|
|
3109
|
-
mapMarkup: "Map annotation",
|
|
3110
|
-
screenMarkup: "Screen annotation",
|
|
3111
|
-
designReview: "Design review",
|
|
3112
|
-
pointer: "Pointer input",
|
|
3113
|
-
};
|
|
3114
|
-
return labels[preset];
|
|
3306
|
+
return PRESETS[preset].label;
|
|
3115
3307
|
}
|
|
3116
3308
|
function curlForCreateInput(apiBaseUrl, payload) {
|
|
3117
3309
|
return [
|
|
@@ -3302,6 +3494,12 @@ function scaffoldJsonResult(command, plan, outputDir, files, parsed, provisionin
|
|
|
3302
3494
|
...(plan.apiBaseUrl.includes("<deployment>")
|
|
3303
3495
|
? [t("cloud.warning.placeholder")]
|
|
3304
3496
|
: []),
|
|
3497
|
+
// The backend helper is written under src/ because the scaffold has no
|
|
3498
|
+
// idea where this project's server lives. Say so, every time, or a Vite
|
|
3499
|
+
// app ships its key.
|
|
3500
|
+
...(plan.target === "web" && plan.sdk !== "swift"
|
|
3501
|
+
? [t("cloud.warning.clientTree")]
|
|
3502
|
+
: []),
|
|
3305
3503
|
...(offline
|
|
3306
3504
|
? [t("cloud.warning.offline")]
|
|
3307
3505
|
: dryRun
|
|
@@ -3339,22 +3537,21 @@ function statusLine(state, label, message) {
|
|
|
3339
3537
|
return `[${state}] ${label}: ${message}`;
|
|
3340
3538
|
}
|
|
3341
3539
|
function installCommand(packageManager) {
|
|
3342
|
-
|
|
3343
|
-
return "npm install";
|
|
3344
|
-
if (packageManager === "pnpm")
|
|
3345
|
-
return "pnpm install";
|
|
3346
|
-
if (packageManager === "yarn")
|
|
3347
|
-
return "yarn install";
|
|
3348
|
-
return "bun install";
|
|
3540
|
+
return packageManagerCommands[packageManager].install;
|
|
3349
3541
|
}
|
|
3350
3542
|
function devCommandForPackageManager(packageManager) {
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3543
|
+
return `${packageManagerCommands[packageManager].run} dev`;
|
|
3544
|
+
}
|
|
3545
|
+
/**
|
|
3546
|
+
* The manager the project already uses, not the one we happen to prefer.
|
|
3547
|
+
* Scaffolding into a pnpm repo and then printing `npm install` is how a second
|
|
3548
|
+
* lockfile gets born.
|
|
3549
|
+
*/
|
|
3550
|
+
async function projectPackageManager(dir, runtime) {
|
|
3551
|
+
const detected = await detectPackageManager(dir, runtime, {
|
|
3552
|
+
pathModule: path,
|
|
3553
|
+
});
|
|
3554
|
+
return detected.manager;
|
|
3358
3555
|
}
|
|
3359
3556
|
function titleCase(value) {
|
|
3360
3557
|
return value
|
package/dist/cloud.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export declare const DEFAULT_REMOTEDRAW_API_BASE_URL = "https://api.remotedraw.c
|
|
|
2
2
|
export type CloudRuntime = {
|
|
3
3
|
env: Record<string, string | undefined>;
|
|
4
4
|
isInteractive?: boolean;
|
|
5
|
-
fetch?:
|
|
5
|
+
fetch?: (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
6
6
|
exists: (filePath: string) => boolean | Promise<boolean>;
|
|
7
7
|
readFile: (filePath: string) => Promise<string>;
|
|
8
8
|
writeFile: (filePath: string, contents: string) => Promise<void>;
|
package/dist/cloud.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloud.d.ts","sourceRoot":"","sources":["../src/cloud.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,+BAA+B,+BAA+B,CAAC;AAE5E,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACxC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"cloud.d.ts","sourceRoot":"","sources":["../src/cloud.ts"],"names":[],"mappings":"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"}
|