@remotedraw/cli 0.1.3 → 0.2.1

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.
@@ -1,5 +1,6 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { emitKeypressEvents } from "node:readline";
3
+ import { t } from "./i18n.js";
3
4
  export function initialWizardState(definition) {
4
5
  return {
5
6
  screen: "field",
@@ -136,7 +137,7 @@ export function wizardSemanticView(state, definition) {
136
137
  const selectedChoice = choices?.find((choice) => choice.value === state.values[field.key]);
137
138
  return {
138
139
  screen: state.screen,
139
- title: "Een RemoteDraw-project maken",
140
+ title: t("wizard.title.setup"),
140
141
  progress: {
141
142
  current: state.fieldIndex + 1,
142
143
  total: definition.fields.length,
@@ -153,55 +154,63 @@ export function wizardSemanticView(state, definition) {
153
154
  controls: field.kind === "text"
154
155
  ? [
155
156
  !state.editedFields.includes(field.key)
156
- ? "Typ om de standaardnaam te vervangen"
157
- : "Typ om te wijzigen",
158
- "←/→ stappen",
159
- "Enter volgende",
160
- "Esc terug",
161
- "Ctrl+C stoppen",
157
+ ? t("wizard.hint.replaceDefault")
158
+ : t("wizard.hint.edit"),
159
+ t("wizard.control.steps"),
160
+ t("wizard.control.next"),
161
+ t("wizard.control.back"),
162
+ t("wizard.control.quitCtrlC"),
162
163
  ]
163
164
  : [
164
- "↑/↓ kiezen",
165
- ...(selectedChoice?.details ? ["d docs openen"] : []),
166
- "←/→ stappen",
167
- "Enter volgende",
168
- "Esc terug",
169
- "q stoppen",
165
+ t("wizard.control.choose"),
166
+ ...(selectedChoice?.details ? [t("wizard.control.docs")] : []),
167
+ t("wizard.control.steps"),
168
+ t("wizard.control.next"),
169
+ t("wizard.control.back"),
170
+ t("wizard.control.quitQ"),
170
171
  ],
171
172
  };
172
173
  }
173
174
  if (state.screen === "review") {
174
175
  return {
175
176
  screen: state.screen,
176
- title: "Project controleren",
177
+ title: t("wizard.title.review"),
177
178
  review: definition.review(state.values),
178
- controls: ["→/Enter doorgaan", "←/Esc terug", "q stoppen"],
179
+ controls: [
180
+ t("wizard.control.forward"),
181
+ t("wizard.control.backArrow"),
182
+ t("wizard.control.quitQ"),
183
+ ],
179
184
  };
180
185
  }
181
186
  if (state.screen === "confirm") {
182
187
  return {
183
188
  screen: state.screen,
184
- title: "Projectbestanden maken?",
189
+ title: t("wizard.title.confirm"),
185
190
  review: definition.review(state.values),
186
191
  choices: [
187
- { value: "no", label: "Nee, annuleren" },
188
- { value: "yes", label: "Ja, project maken" },
192
+ { value: "no", label: t("wizard.confirm.no") },
193
+ { value: "yes", label: t("wizard.confirm.yes") },
189
194
  ],
190
195
  selectedChoice: state.confirmCreate ? "yes" : "no",
191
- controls: ["↑/↓ kiezen", "Enter bevestigen", "←/Esc terug"],
196
+ controls: [
197
+ t("wizard.control.choose"),
198
+ t("wizard.control.confirm"),
199
+ t("wizard.control.backArrow"),
200
+ ],
192
201
  };
193
202
  }
194
203
  return {
195
204
  screen: state.screen,
196
205
  title: state.screen === "executing"
197
- ? "Project wordt gemaakt…"
206
+ ? t("wizard.title.executing")
198
207
  : state.screen === "success"
199
- ? "RemoteDraw-project gemaakt"
208
+ ? t("wizard.title.success")
200
209
  : state.screen === "error"
201
- ? "Project maken mislukt"
210
+ ? t("wizard.title.error")
202
211
  : state.screen === "interrupted"
203
- ? "Installatie onderbroken"
204
- : "Installatie geannuleerd",
212
+ ? t("wizard.title.interrupted")
213
+ : t("wizard.title.cancelled"),
205
214
  controls: [],
206
215
  createdPath: state.createdPath,
207
216
  nextCommand: state.nextCommand,
@@ -305,7 +314,7 @@ export async function runSetupWizard(options) {
305
314
  catch {
306
315
  state = {
307
316
  ...state,
308
- error: `Kon de documentatie niet openen. Gebruik ${selectedDetails.docsUrl}`,
317
+ error: t("wizard.docs.openFailed", { url: selectedDetails.docsUrl }),
309
318
  };
310
319
  }
311
320
  continue;
@@ -378,7 +387,7 @@ function hyperlink(enabled, url, label) {
378
387
  async function openExternalUrl(url) {
379
388
  const parsed = new URL(url);
380
389
  if (parsed.protocol !== "https:") {
381
- throw new Error("Only HTTPS documentation links can be opened.");
390
+ throw new Error(t("error.httpsDocsOnly"));
382
391
  }
383
392
  const [command, args] = process.platform === "darwin"
384
393
  ? ["open", [url]]
@@ -465,7 +474,7 @@ function renderChoiceDetails(details, terminal) {
465
474
  rendered: ink(terminal, "muted", line),
466
475
  })));
467
476
  }
468
- return cardLines(terminal, "Keuzehulp", rows, width);
477
+ return cardLines(terminal, t("wizard.card.help"), rows, width);
469
478
  }
470
479
  const BANNER_COLORS = ["36", "96", "94", "34"];
471
480
  const BANNER_GRADIENT = [
@@ -779,12 +788,15 @@ function composeWizard(view, terminal, frame, narrow, rule, density) {
779
788
  const lines = [
780
789
  ...bannerLines(terminal, frame, density.banner),
781
790
  ink(terminal, "muted", density.banner > 0
782
- ? "telefoon → canvas · projectsetup"
783
- : "telefoon → canvas"),
791
+ ? t("wizard.tagline.setup")
792
+ : t("wizard.tagline")),
784
793
  "",
785
794
  ];
786
795
  if (view.progress) {
787
- const step = `Stap ${view.progress.current}/${view.progress.total}`;
796
+ const step = t("wizard.progress.step", {
797
+ current: view.progress.current,
798
+ total: view.progress.total,
799
+ });
788
800
  lines.push(narrow
789
801
  ? `${ink(terminal, "muted", step)} · ${strong(terminal, view.title)}`
790
802
  : `${stepRail(terminal, view.progress.current, view.progress.total, frame)} ${ink(terminal, "muted", step)} ${strong(terminal, view.title)}`, "");
@@ -805,7 +817,9 @@ function composeWizard(view, terminal, frame, narrow, rule, density) {
805
817
  lines.push(...choiceLines(terminal, view.choices, view.selectedChoice, density.hints));
806
818
  }
807
819
  else {
808
- lines.push(`${ink(terminal, "accent", "│")} ${ink(terminal, view.valueIsDefault ? "muted" : "glow", view.value || "_", !view.valueIsDefault)}${ink(terminal, "glow", caretGlyph(frame))}${view.valueIsDefault ? ` ${ink(terminal, "muted", "(standaard)")}` : ""}`);
820
+ lines.push(`${ink(terminal, "accent", "│")} ${ink(terminal, view.valueIsDefault ? "muted" : "glow", view.value || "_", !view.valueIsDefault)}${ink(terminal, "glow", caretGlyph(frame))}${view.valueIsDefault
821
+ ? ` ${ink(terminal, "muted", t("wizard.value.default"))}`
822
+ : ""}`);
809
823
  if (view.valuePreview) {
810
824
  lines.push(`${ink(terminal, "muted", "└")} ${ink(terminal, "muted", view.valuePreview)}`);
811
825
  }
@@ -831,23 +845,23 @@ function composeWizard(view, terminal, frame, narrow, rule, density) {
831
845
  rendered: `${ink(terminal, "muted", label.padEnd(column))} ${strong(terminal, value)}`,
832
846
  }));
833
847
  const width = Math.max(24, Math.min(Math.max(20, terminal.columns - 4), rows.reduce((widest, row) => Math.max(widest, row.text.length), 0)));
834
- lines.push(...cardLines(terminal, "Overzicht", rows, width));
848
+ lines.push(...cardLines(terminal, t("wizard.card.summary"), rows, width));
835
849
  }
836
850
  }
837
851
  if (view.choices && !view.activeLabel) {
838
852
  lines.push(...choiceLines(terminal, view.choices, view.selectedChoice, false));
839
853
  }
840
854
  if (view.screen === "executing") {
841
- lines.push(progressBar(terminal, frame, narrow ? 12 : 24), "", ink(terminal, "muted", "Bestanden schrijven, sleutels koppelen en configuratie opslaan…"));
855
+ lines.push(progressBar(terminal, frame, narrow ? 12 : 24), "", ink(terminal, "muted", t("wizard.executing.detail")));
842
856
  }
843
857
  if (view.createdPath) {
844
- lines.push(`${ink(terminal, "muted", "Gemaakt in:")} ${view.createdPath}`);
858
+ lines.push(`${ink(terminal, "muted", t("wizard.created.at"))} ${view.createdPath}`);
845
859
  }
846
860
  if (view.nextCommand) {
847
- lines.push("", ink(terminal, "muted", "Volgende opdracht:"), ` ${ink(terminal, "glow", view.nextCommand, true)}`);
861
+ lines.push("", ink(terminal, "muted", t("wizard.next.command")), ` ${ink(terminal, "glow", view.nextCommand, true)}`);
848
862
  }
849
863
  if (view.screen === "success") {
850
- lines.push("", `${ink(terminal, "success", SPARKLES[frame % SPARKLES.length])} ${ink(terminal, "muted", "Documentatie: https://docs.remotedraw.com")}`);
864
+ lines.push("", `${ink(terminal, "success", SPARKLES[frame % SPARKLES.length])} ${ink(terminal, "muted", t("wizard.docs.line"))}`);
851
865
  }
852
866
  if (view.error) {
853
867
  for (const line of view.error.split("\n")) {
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Crash reporting for the published CLI.
3
+ *
4
+ * Deliberately dependency-free: `remotedraw` is installed globally by customers,
5
+ * and pulling `@sentry/node` into it would add tens of megabytes and a native
6
+ * OpenTelemetry stack to what is otherwise a small package. Sentry's envelope
7
+ * endpoint is a plain POST, so the whole client is the function below.
8
+ *
9
+ * It only ever sends a crash — never a command's arguments, never file
10
+ * contents, never a project name. Anything that could carry a credential is
11
+ * scrubbed before it leaves the machine, and the whole path is opt-out.
12
+ */
13
+ export type TelemetryEnvironment = {
14
+ env: Record<string, string | undefined>;
15
+ version: string;
16
+ platform: string;
17
+ nodeVersion: string;
18
+ /** Injected by the test suite so no suite run can reach the network. */
19
+ fetch?: typeof fetch | undefined;
20
+ };
21
+ /**
22
+ * Off whenever the user says so, and off in CI, where a crash report has no
23
+ * human behind it and the environment is usually a fresh container.
24
+ * DO_NOT_TRACK is the cross-vendor convention; we honor it without a flag.
25
+ */
26
+ export declare function telemetryDisabled(env: Record<string, string | undefined>): boolean;
27
+ /**
28
+ * Removes the two things a CLI error message realistically leaks: credentials
29
+ * pasted into a flag or read from the environment, and the absolute path of the
30
+ * user's home directory, which usually contains their name.
31
+ */
32
+ export declare function scrubTelemetryText(value: string, env?: Record<string, string | undefined>): string;
33
+ /**
34
+ * Builds the Sentry event for a crash. Exported so tests can assert exactly
35
+ * what would go over the wire without one leaving the machine.
36
+ */
37
+ export declare function crashEvent(error: unknown, command: string, environment: TelemetryEnvironment): {
38
+ event_id: string;
39
+ timestamp: number;
40
+ platform: string;
41
+ level: string;
42
+ logger: string;
43
+ release: string;
44
+ environment: string;
45
+ transaction: string;
46
+ tags: {
47
+ command: string;
48
+ cli_version: string;
49
+ node_version: string;
50
+ os: string;
51
+ };
52
+ exception: {
53
+ values: {
54
+ type: string;
55
+ value: string;
56
+ mechanism: {
57
+ handled: boolean;
58
+ };
59
+ }[];
60
+ };
61
+ extra: {
62
+ stack: string;
63
+ } | undefined;
64
+ };
65
+ /**
66
+ * Reports a CLI crash. Never throws, never rejects, and never delays the exit
67
+ * by more than the timeout: a command's outcome must be identical whether or
68
+ * not Sentry is reachable.
69
+ */
70
+ export declare function reportCliCrash(error: unknown, command: string, environment: TelemetryEnvironment): Promise<void>;
71
+ /** The environment description used by the real CLI process. */
72
+ export declare function nodeTelemetryEnvironment(version: string): TelemetryEnvironment;
73
+ //# sourceMappingURL=telemetry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AASH,MAAM,MAAM,oBAAoB,GAAG;IACjC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,wEAAwE;IACxE,KAAK,CAAC,EAAE,OAAO,KAAK,GAAG,SAAS,CAAC;CAClC,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,WAOxE;AAMD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAM,UAW7C;AAmBD;;;GAGG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsClC;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,oBAAoB,iBA+BlC;AAED,gEAAgE;AAChE,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,GACd,oBAAoB,CAOtB"}
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Crash reporting for the published CLI.
3
+ *
4
+ * Deliberately dependency-free: `remotedraw` is installed globally by customers,
5
+ * and pulling `@sentry/node` into it would add tens of megabytes and a native
6
+ * OpenTelemetry stack to what is otherwise a small package. Sentry's envelope
7
+ * endpoint is a plain POST, so the whole client is the function below.
8
+ *
9
+ * It only ever sends a crash — never a command's arguments, never file
10
+ * contents, never a project name. Anything that could carry a credential is
11
+ * scrubbed before it leaves the machine, and the whole path is opt-out.
12
+ */
13
+ /**
14
+ * Filled in at release time. Sentry DSNs are public by design (browser bundles
15
+ * ship them), so a value here is not a secret; it only identifies the project
16
+ * that receives crashes. Override at runtime with REMOTEDRAW_SENTRY_DSN.
17
+ */
18
+ const RELEASE_DSN = "";
19
+ /**
20
+ * Off whenever the user says so, and off in CI, where a crash report has no
21
+ * human behind it and the environment is usually a fresh container.
22
+ * DO_NOT_TRACK is the cross-vendor convention; we honor it without a flag.
23
+ */
24
+ export function telemetryDisabled(env) {
25
+ return Boolean(env.REMOTEDRAW_TELEMETRY_DISABLED?.trim() ||
26
+ env.REMOTEDRAW_CLI_OFFLINE?.trim() ||
27
+ env.DO_NOT_TRACK?.trim() ||
28
+ env.CI?.trim());
29
+ }
30
+ function dsnFor(env) {
31
+ return env.REMOTEDRAW_SENTRY_DSN?.trim() || RELEASE_DSN;
32
+ }
33
+ /**
34
+ * Removes the two things a CLI error message realistically leaks: credentials
35
+ * pasted into a flag or read from the environment, and the absolute path of the
36
+ * user's home directory, which usually contains their name.
37
+ */
38
+ export function scrubTelemetryText(value, env = {}) {
39
+ let scrubbed = value
40
+ .replace(/\brd_[a-z]+_[A-Za-z0-9_-]+/g, "rd_***")
41
+ .replace(/\bsk_[A-Za-z0-9_-]{8,}/g, "sk_***")
42
+ .replace(/\b(Bearer)\s+\S+/gi, "$1 ***");
43
+ const home = env.HOME ?? env.USERPROFILE;
44
+ if (home && home.length > 1) {
45
+ scrubbed = scrubbed.split(home).join("~");
46
+ }
47
+ return scrubbed;
48
+ }
49
+ function parseDsn(value) {
50
+ try {
51
+ const url = new URL(value);
52
+ const projectId = url.pathname.replace(/^\//, "");
53
+ if (!url.username || !projectId)
54
+ return null;
55
+ return {
56
+ envelopeUrl: `${url.protocol}//${url.host}/api/${projectId}/envelope/`,
57
+ publicKey: url.username,
58
+ dsn: value,
59
+ };
60
+ }
61
+ catch {
62
+ return null;
63
+ }
64
+ }
65
+ /**
66
+ * Builds the Sentry event for a crash. Exported so tests can assert exactly
67
+ * what would go over the wire without one leaving the machine.
68
+ */
69
+ export function crashEvent(error, command, environment) {
70
+ const isError = error instanceof Error;
71
+ const eventId = crypto.randomUUID().replace(/-/g, "");
72
+ return {
73
+ event_id: eventId,
74
+ timestamp: Date.now() / 1000,
75
+ platform: "node",
76
+ level: "error",
77
+ logger: "remotedraw.cli",
78
+ release: `remotedraw-cli@${environment.version}`,
79
+ environment: "production",
80
+ // The command name only; flags and values never leave the machine because
81
+ // any of them may be a key, a path, or a customer's project name.
82
+ transaction: command,
83
+ tags: {
84
+ command,
85
+ cli_version: environment.version,
86
+ node_version: environment.nodeVersion,
87
+ os: environment.platform,
88
+ },
89
+ exception: {
90
+ values: [
91
+ {
92
+ type: isError ? error.name || "Error" : "UnknownError",
93
+ value: scrubTelemetryText(isError ? error.message : String(error), environment.env),
94
+ mechanism: { handled: true },
95
+ },
96
+ ],
97
+ },
98
+ extra: isError && error.stack
99
+ ? { stack: scrubTelemetryText(error.stack, environment.env) }
100
+ : undefined,
101
+ };
102
+ }
103
+ /**
104
+ * Reports a CLI crash. Never throws, never rejects, and never delays the exit
105
+ * by more than the timeout: a command's outcome must be identical whether or
106
+ * not Sentry is reachable.
107
+ */
108
+ export async function reportCliCrash(error, command, environment) {
109
+ if (telemetryDisabled(environment.env))
110
+ return;
111
+ const target = parseDsn(dsnFor(environment.env));
112
+ if (!target)
113
+ return;
114
+ const event = crashEvent(error, command, environment);
115
+ const envelope = [
116
+ JSON.stringify({
117
+ event_id: event.event_id,
118
+ sent_at: new Date().toISOString(),
119
+ dsn: target.dsn,
120
+ }),
121
+ JSON.stringify({ type: "event" }),
122
+ JSON.stringify(event),
123
+ ].join("\n");
124
+ const send = environment.fetch ?? fetch;
125
+ try {
126
+ await send(`${target.envelopeUrl}?sentry_key=${target.publicKey}&sentry_version=7`, {
127
+ method: "POST",
128
+ headers: { "Content-Type": "application/x-sentry-envelope" },
129
+ body: envelope,
130
+ signal: AbortSignal.timeout(2000),
131
+ });
132
+ }
133
+ catch {
134
+ // A crash report that fails to send is not itself worth reporting.
135
+ }
136
+ }
137
+ /** The environment description used by the real CLI process. */
138
+ export function nodeTelemetryEnvironment(version) {
139
+ return {
140
+ env: process.env,
141
+ version,
142
+ platform: process.platform,
143
+ nodeVersion: process.versions.node,
144
+ };
145
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../src/update.ts"],"names":[],"mappings":"AACA,OAAO,EAAoB,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAEjE,eAAO,MAAM,gBAAgB,oBAAoB,CAAC;AAOlD,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG;IACzC;;;;OAIG;IACH,YAAY,CAAC,EAAE,CACb,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,KACX,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,sEAAsE;IACtE,OAAO,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;IACzC,iFAAiF;IACjF,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,OAAO,CAAC;CAC1B,CAAC;AAiBF,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM;;;EAczC;AA2BD,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,cASnD;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,CAoClE;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,aAAa,UAEzD;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,aAAa,UAErD;AAgDD,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,aAAa,EACtB,SAAS,SAAmB,+BAoC7B;AAED,+EAA+E;AAC/E,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,WAO3E;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,aAAa,EACtB,cAAc,EAAE,MAAM,EACtB,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAC9C,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CA2BlC;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,UAOrE;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,aAAa;;;;;;GAM7E"}
1
+ {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../src/update.ts"],"names":[],"mappings":"AACA,OAAO,EAAoB,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAGjE,eAAO,MAAM,gBAAgB,oBAAoB,CAAC;AAOlD,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG;IACzC;;;;OAIG;IACH,YAAY,CAAC,EAAE,CACb,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,KACX,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,sEAAsE;IACtE,OAAO,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;IACzC,iFAAiF;IACjF,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,OAAO,CAAC;CAC1B,CAAC;AAiBF,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM;;;EAczC;AA2BD,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,cASnD;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,CAoClE;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,aAAa,UAEzD;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,aAAa,UAErD;AAgDD,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,aAAa,EACtB,SAAS,SAAmB,+BAoC7B;AAED,+EAA+E;AAC/E,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,WAO3E;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,aAAa,EACtB,cAAc,EAAE,MAAM,EACtB,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAC9C,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CA2BlC;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,UAWrE;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,aAAa;;;;;;GAM7E"}
package/dist/update.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import path from "node:path";
2
2
  import { globalConfigPath } from "./cloud.js";
3
+ import { t } from "./i18n.js";
3
4
  export const CLI_PACKAGE_NAME = "@remotedraw/cli";
4
5
  const DEFAULT_REGISTRY_URL = "https://registry.npmjs.org";
5
6
  const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
@@ -220,10 +221,14 @@ export async function checkForUpdate(runtime, currentVersion, options = {}) {
220
221
  }
221
222
  export function updateNotice(check, method) {
222
223
  return [
223
- `Update available: ${CLI_PACKAGE_NAME} ${check.current} -> ${check.latest}`,
224
+ t("notice.update.available", {
225
+ package: CLI_PACKAGE_NAME,
226
+ current: check.current,
227
+ latest: check.latest,
228
+ }),
224
229
  method.ephemeral
225
- ? `Run remotedraw with ${CLI_PACKAGE_NAME}@latest to pick it up.`
226
- : `Run remotedraw update (or ${installMethodDisplay(method)}).`,
230
+ ? t("notice.update.ephemeral", { package: CLI_PACKAGE_NAME })
231
+ : t("notice.update.install", { command: installMethodDisplay(method) }),
227
232
  ].join("\n");
228
233
  }
229
234
  export async function runInstall(runtime, method) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remotedraw/cli",
3
- "version": "0.1.3",
3
+ "version": "0.2.1",
4
4
  "description": "Command-line tools for creating and inspecting RemoteDraw integrations.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -13,6 +13,12 @@
13
13
  "engines": {
14
14
  "node": ">=20"
15
15
  },
16
+ "license": "MIT",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/AxioSOzo/RemoteDraw.git",
20
+ "directory": "packages/cli"
21
+ },
16
22
  "bugs": {
17
23
  "email": "support@remotedraw.com"
18
24
  },
@@ -27,12 +33,13 @@
27
33
  "registry": "https://registry.npmjs.org/"
28
34
  },
29
35
  "scripts": {
30
- "build": "tsc -p tsconfig.json",
36
+ "build": "bun run generate:skill && tsc -p tsconfig.json",
31
37
  "dev": "bun run src/index.ts",
32
38
  "test": "bun test tests/*.test.ts",
33
39
  "typecheck": "tsc -p tsconfig.json --noEmit",
34
- "verify:publish": "bun run build && bun run typecheck && bun run test && node dist/index.js --help && node dist/index.js --version",
35
- "prepack": "bun run verify:publish"
40
+ "verify:publish": "bun run generate:skill && bun run build && bun run typecheck && bun run test && node dist/index.js --help && node dist/index.js --version",
41
+ "prepack": "bun run verify:publish",
42
+ "generate:skill": "bun run scripts/generate-agent-skill.ts"
36
43
  },
37
44
  "devDependencies": {
38
45
  "@types/node": "25.6.2",