@dashai/cli 0.2.2 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -92,6 +92,94 @@ function isPlausibleConfig(v) {
92
92
  if (typeof u.email !== "string") return false;
93
93
  return true;
94
94
  }
95
+
96
+ // src/lib/api-client.ts
97
+ var ApiError = class extends Error {
98
+ status;
99
+ code;
100
+ context;
101
+ retriable;
102
+ constructor(status, envelope, fallbackMessage) {
103
+ const code2 = envelope?.code ?? codeFromStatus(status);
104
+ const message = envelope?.message ?? fallbackMessage;
105
+ super(message);
106
+ this.name = "ApiError";
107
+ this.status = status;
108
+ this.code = code2;
109
+ this.context = envelope?.context;
110
+ this.retriable = envelope?.retriable ?? status >= 500;
111
+ }
112
+ };
113
+ async function apiRequest(method, path, body, options = {}) {
114
+ const baseUrl = (options.baseUrl ?? resolveApiUrl()).replace(/\/+$/, "");
115
+ const url = `${baseUrl}${path.startsWith("/") ? path : `/${path}`}`;
116
+ const headers = {
117
+ Accept: "application/json",
118
+ ...options.headers ?? {}
119
+ };
120
+ if (body !== void 0) {
121
+ headers["Content-Type"] = "application/json";
122
+ }
123
+ const authMode = options.auth ?? "auto";
124
+ if (authMode === "auto") {
125
+ const saved = readSavedToken();
126
+ if (saved) headers["Authorization"] = `Bearer ${saved}`;
127
+ } else if (typeof authMode === "object" && authMode.token) {
128
+ headers["Authorization"] = `Bearer ${authMode.token}`;
129
+ }
130
+ let res;
131
+ try {
132
+ res = await fetch(url, {
133
+ method,
134
+ headers,
135
+ body: body === void 0 ? void 0 : JSON.stringify(body)
136
+ });
137
+ } catch (err) {
138
+ throw new ApiError(0, { code: "NETWORK_ERROR", message: err.message }, "Network error");
139
+ }
140
+ if (res.status === 204) {
141
+ return void 0;
142
+ }
143
+ const contentType = res.headers.get("content-type") ?? "";
144
+ let parsed = null;
145
+ if (contentType.includes("application/json")) {
146
+ try {
147
+ parsed = await res.json();
148
+ } catch {
149
+ parsed = null;
150
+ }
151
+ } else if (res.status >= 400) {
152
+ try {
153
+ parsed = { message: await res.text() };
154
+ } catch {
155
+ parsed = null;
156
+ }
157
+ }
158
+ if (!res.ok) {
159
+ throw new ApiError(res.status, asEnvelope(parsed), `HTTP ${res.status}`);
160
+ }
161
+ return parsed;
162
+ }
163
+ function asEnvelope(v) {
164
+ if (!v || typeof v !== "object") return null;
165
+ const env = v;
166
+ const out2 = {};
167
+ if (typeof env.code === "string") out2.code = env.code;
168
+ if (typeof env.message === "string") out2.message = env.message;
169
+ if (typeof env.retriable === "boolean") out2.retriable = env.retriable;
170
+ if (env.context && typeof env.context === "object") {
171
+ out2.context = env.context;
172
+ }
173
+ return out2;
174
+ }
175
+ function codeFromStatus(status) {
176
+ if (status === 401) return "UNAUTHENTICATED";
177
+ if (status === 403) return "FORBIDDEN";
178
+ if (status === 404) return "NOT_FOUND";
179
+ if (status === 0) return "NETWORK_ERROR";
180
+ if (status >= 500) return "INTERNAL_ERROR";
181
+ return "HTTP_ERROR";
182
+ }
95
183
  function success(msg) {
96
184
  process.stderr.write(`${pc.green("\u2713")} ${msg}
97
185
  `);
@@ -125,6 +213,7 @@ function dim(s) {
125
213
  }
126
214
 
127
215
  // src/commands/dev.ts
216
+ var DEFAULT_NEXT_PORT = 3e3;
128
217
  var PREFIX_SDK = `\x1B[36m[sdk]\x1B[0m `;
129
218
  var PREFIX_NEXT = `\x1B[35m[next]\x1B[0m `;
130
219
  async function devCommand(options) {
@@ -157,17 +246,60 @@ async function devCommand(options) {
157
246
  return 1;
158
247
  }
159
248
  const apiUrl = options.apiUrl ?? process.env.DASHWISE_API_URL ?? savedConfig?.apiUrl ?? "http://localhost:3000";
160
- const installationId = options.installationId ?? process.env.DASHWISE_INSTALLATION_ID ?? "local";
249
+ const port = options.port ?? DEFAULT_NEXT_PORT;
250
+ const explicitInstallationId = options.installationId ?? process.env.DASHWISE_INSTALLATION_ID;
251
+ const reuseDevSessionId = process.env.DASHWISE_DEV_SESSION_ID;
252
+ let installationIdEnv;
253
+ let devSessionIdEnv;
254
+ let revokeDevSession;
255
+ if (explicitInstallationId && explicitInstallationId !== "local") {
256
+ installationIdEnv = explicitInstallationId;
257
+ } else if (reuseDevSessionId) {
258
+ devSessionIdEnv = reuseDevSessionId;
259
+ } else {
260
+ let workspaceContext;
261
+ try {
262
+ workspaceContext = await resolveTargetWorkspace(apiUrl, apiToken, options.workspace);
263
+ } catch (err) {
264
+ fail(err.message);
265
+ return 1;
266
+ }
267
+ const callbackOrigins = computeCallbackOrigins(port, options.callbackOrigin ?? []);
268
+ try {
269
+ const session = await registerDevSession(apiUrl, apiToken, {
270
+ target_workspace_id: workspaceContext.id,
271
+ callback_origins: callbackOrigins
272
+ });
273
+ devSessionIdEnv = session.id;
274
+ revokeDevSession = () => revokeDevSessionBestEffort(apiUrl, apiToken, session.id);
275
+ info(` ${dim("dev session")} ${session.id}`);
276
+ info(` ${dim("workspace")} ${workspaceContext.name} (id=${workspaceContext.id}, slug=${workspaceContext.slug})`);
277
+ info(` ${dim("callbacks")} ${callbackOrigins.join(", ")}`);
278
+ info(` ${dim("expires")} ${session.expires_at}`);
279
+ } catch (err) {
280
+ if (err instanceof ApiError) {
281
+ fail(`Failed to register dev session: ${err.code} \u2014 ${err.message}`);
282
+ } else {
283
+ fail(`Failed to register dev session: ${err.message}`);
284
+ }
285
+ return 1;
286
+ }
287
+ }
161
288
  const childEnv = {
162
289
  ...process.env,
163
290
  DASHWISE_API_URL: apiUrl,
164
291
  DASHWISE_API_TOKEN: apiToken,
165
- DASHWISE_INSTALLATION_ID: installationId
292
+ DASHWISE_INSTALLATION_ID: installationIdEnv ?? "",
293
+ ...devSessionIdEnv !== void 0 ? { DASHWISE_DEV_SESSION_ID: devSessionIdEnv } : {}
166
294
  };
167
295
  info(`Dev loop starting \u2014 Ctrl-C to stop.`);
168
- info(` ${dim("api")} ${apiUrl}`);
169
- info(` ${dim("installation")} ${installationId}`);
170
- info(` ${dim("token")} ${maskedToken(apiToken)} ${dim(`(from ${apiToken === savedConfig?.token ? "~/.config/dashwise/auth.json" : "$DASHWISE_API_TOKEN"})`)}`);
296
+ info(` ${dim("api")} ${apiUrl}`);
297
+ if (installationIdEnv !== void 0) {
298
+ info(` ${dim("installation")} ${installationIdEnv} (prod-mode SSO)`);
299
+ } else {
300
+ info(` ${dim("mode")} dev (DASHWISE_DEV_SESSION_ID injected)`);
301
+ }
302
+ info(` ${dim("token")} ${maskedToken(apiToken)} ${dim(`(from ${apiToken === savedConfig?.token ? "~/.config/dashwise/auth.json" : "$DASHWISE_API_TOKEN"})`)}`);
171
303
  info("");
172
304
  const children = [];
173
305
  if (!options.nextOnly) {
@@ -175,7 +307,7 @@ async function devCommand(options) {
175
307
  children.push({ name: "sdk", child: sdkChild, prefix: PREFIX_SDK });
176
308
  }
177
309
  if (!options.sdkOnly) {
178
- const nextChild = spawnNextDev(projectRoot, childEnv, options.port);
310
+ const nextChild = spawnNextDev(projectRoot, childEnv, port);
179
311
  children.push({ name: "next", child: nextChild, prefix: PREFIX_NEXT });
180
312
  }
181
313
  for (const { child, prefix } of children) {
@@ -193,6 +325,10 @@ Received ${sig}, stopping children\u2026`);
193
325
  child.kill(sig);
194
326
  }
195
327
  }
328
+ if (revokeDevSession) {
329
+ revokeDevSession().catch(() => {
330
+ });
331
+ }
196
332
  };
197
333
  process.on("SIGINT", () => forwardSignal("SIGINT"));
198
334
  process.on("SIGTERM", () => forwardSignal("SIGTERM"));
@@ -286,96 +422,60 @@ function maskedToken(token) {
286
422
  const tail = token.slice(-4);
287
423
  return `${prefix}\u2026${tail}`;
288
424
  }
289
-
290
- // src/lib/api-client.ts
291
- var ApiError = class extends Error {
292
- status;
293
- code;
294
- context;
295
- retriable;
296
- constructor(status, envelope, fallbackMessage) {
297
- const code2 = envelope?.code ?? codeFromStatus(status);
298
- const message = envelope?.message ?? fallbackMessage;
299
- super(message);
300
- this.name = "ApiError";
301
- this.status = status;
302
- this.code = code2;
303
- this.context = envelope?.context;
304
- this.retriable = envelope?.retriable ?? status >= 500;
305
- }
306
- };
307
- async function apiRequest(method, path, body, options = {}) {
308
- const baseUrl = (options.baseUrl ?? resolveApiUrl()).replace(/\/+$/, "");
309
- const url = `${baseUrl}${path.startsWith("/") ? path : `/${path}`}`;
310
- const headers = {
311
- Accept: "application/json",
312
- ...options.headers ?? {}
313
- };
314
- if (body !== void 0) {
315
- headers["Content-Type"] = "application/json";
316
- }
317
- const authMode = options.auth ?? "auto";
318
- if (authMode === "auto") {
319
- const saved = readSavedToken();
320
- if (saved) headers["Authorization"] = `Bearer ${saved}`;
321
- } else if (typeof authMode === "object" && authMode.token) {
322
- headers["Authorization"] = `Bearer ${authMode.token}`;
323
- }
324
- let res;
325
- try {
326
- res = await fetch(url, {
327
- method,
328
- headers,
329
- body: body === void 0 ? void 0 : JSON.stringify(body)
330
- });
331
- } catch (err) {
332
- throw new ApiError(0, { code: "NETWORK_ERROR", message: err.message }, "Network error");
333
- }
334
- if (res.status === 204) {
335
- return void 0;
425
+ function computeCallbackOrigins(port, extra) {
426
+ const def = `http://localhost:${port}`;
427
+ return Array.from(/* @__PURE__ */ new Set([def, ...extra]));
428
+ }
429
+ async function resolveTargetWorkspace(apiUrl, apiToken, flag) {
430
+ const workspaces = await apiRequest(
431
+ "GET",
432
+ "/api/workspaces",
433
+ void 0,
434
+ { baseUrl: apiUrl, auth: { token: apiToken } }
435
+ );
436
+ if (workspaces.length === 0) {
437
+ throw new Error(
438
+ "No workspaces found for your account. Create a workspace at the DashWise dashboard first."
439
+ );
336
440
  }
337
- const contentType = res.headers.get("content-type") ?? "";
338
- let parsed = null;
339
- if (contentType.includes("application/json")) {
340
- try {
341
- parsed = await res.json();
342
- } catch {
343
- parsed = null;
344
- }
345
- } else if (res.status >= 400) {
346
- try {
347
- parsed = { message: await res.text() };
348
- } catch {
349
- parsed = null;
441
+ if (flag === void 0) {
442
+ if (workspaces.length === 1) {
443
+ return workspaces[0];
350
444
  }
445
+ const list = workspaces.map((w) => ` - ${w.slug} (id=${w.id}, name="${w.name}")`).join("\n");
446
+ throw new Error(
447
+ `You have ${workspaces.length} workspaces. Re-run with --workspace <slug-or-id> to pick one:
448
+ ${list}`
449
+ );
351
450
  }
352
- if (!res.ok) {
353
- throw new ApiError(res.status, asEnvelope(parsed), `HTTP ${res.status}`);
451
+ const asNumber = Number.parseInt(flag, 10);
452
+ const isNumeric = !Number.isNaN(asNumber) && String(asNumber) === flag;
453
+ const match = isNumeric ? workspaces.find((w) => w.id === asNumber) : workspaces.find((w) => w.slug === flag);
454
+ if (!match) {
455
+ const list = workspaces.map((w) => ` - ${w.slug} (id=${w.id})`).join("\n");
456
+ throw new Error(
457
+ `No workspace matching --workspace=${flag}. Available:
458
+ ${list}`
459
+ );
354
460
  }
355
- return parsed;
461
+ return match;
356
462
  }
357
- function asEnvelope(v) {
358
- if (!v || typeof v !== "object") return null;
359
- const env = v;
360
- const out2 = {};
361
- if (typeof env.code === "string") out2.code = env.code;
362
- if (typeof env.message === "string") out2.message = env.message;
363
- if (typeof env.retriable === "boolean") out2.retriable = env.retriable;
364
- if (env.context && typeof env.context === "object") {
365
- out2.context = env.context;
366
- }
367
- return out2;
463
+ async function registerDevSession(apiUrl, apiToken, body) {
464
+ return apiRequest(
465
+ "POST",
466
+ "/api/auth/dev/sessions",
467
+ body,
468
+ { baseUrl: apiUrl, auth: { token: apiToken } }
469
+ );
368
470
  }
369
- function codeFromStatus(status) {
370
- if (status === 401) return "UNAUTHENTICATED";
371
- if (status === 403) return "FORBIDDEN";
372
- if (status === 404) return "NOT_FOUND";
373
- if (status === 0) return "NETWORK_ERROR";
374
- if (status >= 500) return "INTERNAL_ERROR";
375
- return "HTTP_ERROR";
471
+ async function revokeDevSessionBestEffort(apiUrl, apiToken, id) {
472
+ await apiRequest(
473
+ "DELETE",
474
+ `/api/auth/dev/sessions/${encodeURIComponent(id)}`,
475
+ void 0,
476
+ { baseUrl: apiUrl, auth: { token: apiToken } }
477
+ );
376
478
  }
377
-
378
- // src/commands/login.ts
379
479
  var DEFAULT_TOKEN_NAME = "dashwise-cli";
380
480
  async function loginCommand(options) {
381
481
  const apiUrl = (options.apiUrl ?? resolveApiUrl()).replace(/\/+$/, "");
@@ -910,6 +1010,24 @@ function envLocalExample() {
910
1010
  ""
911
1011
  ].join("\n");
912
1012
  }
1013
+ function envLocal() {
1014
+ return [
1015
+ "# Local dev config \u2014 auto-generated by `dashwise module init`.",
1016
+ "# Gitignored; safe to edit. `dashwise dev` (the unified loop) auto-",
1017
+ "# injects DASHWISE_API_TOKEN from ~/.config/dashwise/auth.json AND",
1018
+ "# DASHWISE_DEV_SESSION_ID from the dev session it registers \u2014 so",
1019
+ "# you typically don't need to set either here. Change DASHWISE_API_URL",
1020
+ "# to point at a non-default backend (e.g. https://api.dashwise.net).",
1021
+ "#",
1022
+ "# DASHWISE_INSTALLATION_ID is only needed AFTER you publish + install",
1023
+ "# the module in a workspace; until then dev mode (DASHWISE_DEV_SESSION_ID,",
1024
+ "# auto-set by `dashwise dev`) is the auth path.",
1025
+ "DASHWISE_API_URL=http://localhost:3000",
1026
+ "DASHWISE_API_TOKEN=",
1027
+ "DASHWISE_INSTALLATION_ID=",
1028
+ ""
1029
+ ].join("\n");
1030
+ }
913
1031
  function appLayoutTsx(opts) {
914
1032
  return `export const metadata = {
915
1033
  title: ${JSON.stringify(opts.name)},
@@ -1344,11 +1462,23 @@ function Row({ label, value }: { label: string; value: string }) {
1344
1462
  function appApiAuthSignInTs() {
1345
1463
  return `// /api/auth/sign-in \u2014 kick off the SSO flow.
1346
1464
  //
1347
- // Reads \`?return_to=\` from the caller (defaults to '/'), looks up
1348
- // the module's installation id from env, redirects the browser to
1349
- // the DashWise backend's \`/api/auth/sso/start\`. The backend mints
1350
- // an exchange code + 302s back to \`/api/auth/callback?code=...\`
1351
- // where we set the session cookie.
1465
+ // Reads \`?return_to=\` from the caller (defaults to '/'). Branches
1466
+ // on env:
1467
+ //
1468
+ // - DASHWISE_DEV_SESSION_ID set (set automatically by
1469
+ // \`dashwise dev\`): hits /api/auth/dev/sso/start with the dev
1470
+ // session id. No installation_id required \u2014 the module isn't
1471
+ // installed in a workspace yet.
1472
+ //
1473
+ // - DASHWISE_INSTALLATION_ID set (production / installed-module
1474
+ // path): hits /api/auth/sso/start with the installation id.
1475
+ //
1476
+ // In both cases the backend mints an exchange code + 302s back to
1477
+ // \`/api/auth/callback?code=...\` where we set the session cookie.
1478
+ //
1479
+ // Unauthenticated browser hits to either backend endpoint 302 to
1480
+ // the DashWise FE \`/login?return_to=...\` automatically \u2014 you don't
1481
+ // need to handle the "user not logged in" case here.
1352
1482
  import { NextResponse } from 'next/server';
1353
1483
 
1354
1484
  export function GET(request: Request) {
@@ -1356,19 +1486,43 @@ export function GET(request: Request) {
1356
1486
  const returnTo = url.searchParams.get('return_to') ?? '/';
1357
1487
  const apiBaseUrl = process.env.DASHWISE_API_URL;
1358
1488
  const installationId = process.env.DASHWISE_INSTALLATION_ID;
1359
- if (!apiBaseUrl || !installationId) {
1489
+ const devSessionId = process.env.DASHWISE_DEV_SESSION_ID;
1490
+
1491
+ if (!apiBaseUrl) {
1360
1492
  return new NextResponse(
1361
- 'DASHWISE_API_URL and DASHWISE_INSTALLATION_ID must be set in .env.local',
1493
+ 'DASHWISE_API_URL must be set in .env.local',
1362
1494
  { status: 500 },
1363
1495
  );
1364
1496
  }
1497
+ if (!installationId && !devSessionId) {
1498
+ return new NextResponse(
1499
+ 'Either DASHWISE_INSTALLATION_ID (installed module) or DASHWISE_DEV_SESSION_ID ' +
1500
+ '(local dev via \`dashwise dev\`) must be set. Run \`dashwise dev\` to register ' +
1501
+ 'a dev session, or set DASHWISE_INSTALLATION_ID after publishing your module.',
1502
+ { status: 500 },
1503
+ );
1504
+ }
1505
+
1365
1506
  // Absolute URL for return_to so the backend knows which host to
1366
1507
  // bounce back to (the callback path is fixed at /api/auth/callback
1367
1508
  // by SDK convention).
1368
1509
  const moduleOrigin = url.origin;
1369
- const ssoStart = new URL('/api/auth/sso/start', apiBaseUrl);
1370
- ssoStart.searchParams.set('return_to', \`\${moduleOrigin}\${returnTo}\`);
1371
- ssoStart.searchParams.set('client_id', installationId);
1510
+ const absoluteReturnTo = \`\${moduleOrigin}\${returnTo}\`;
1511
+
1512
+ let ssoStart;
1513
+ if (devSessionId) {
1514
+ // Dev mode wins when both are set \u2014 \`dashwise dev\` injects
1515
+ // DASHWISE_DEV_SESSION_ID into the child env, which overrides
1516
+ // whatever was in .env.local. The dev session id keys back to
1517
+ // the dev's workspace + callback origin allowlist.
1518
+ ssoStart = new URL('/api/auth/dev/sso/start', apiBaseUrl);
1519
+ ssoStart.searchParams.set('return_to', absoluteReturnTo);
1520
+ ssoStart.searchParams.set('dev_session_id', devSessionId);
1521
+ } else {
1522
+ ssoStart = new URL('/api/auth/sso/start', apiBaseUrl);
1523
+ ssoStart.searchParams.set('return_to', absoluteReturnTo);
1524
+ ssoStart.searchParams.set('client_id', installationId);
1525
+ }
1372
1526
  return NextResponse.redirect(ssoStart);
1373
1527
  }
1374
1528
  `;
@@ -2375,6 +2529,7 @@ async function moduleInitCommand(slugArg, options) {
2375
2529
  ["next-env.d.ts", nextEnvDts()],
2376
2530
  [".gitignore", gitignore()],
2377
2531
  [".env.local.example", envLocalExample()],
2532
+ [".env.local", envLocal()],
2378
2533
  ["README.md", readme(scaffoldOpts)]
2379
2534
  ];
2380
2535
  if (kind === "custom") {
@@ -4100,18 +4255,28 @@ workspace.command("use <slug>").description("Set the default workspace for subse
4100
4255
  process.exitCode = await workspaceUseCommand(slug);
4101
4256
  });
4102
4257
  program.command("dev").description(
4103
- "One-command local dev loop: runs the manifest watcher + Next.js dev server together, with `DASHWISE_API_URL`/`DASHWISE_API_TOKEN`/`DASHWISE_INSTALLATION_ID` auto-injected from your saved CLI config. Run `dashwise login` first."
4258
+ "One-command local dev loop: runs the manifest watcher + Next.js dev server together. Registers a dev-mode SSO session with the backend so workspace members can sign into your locally-running module via DashWise SSO (no installation_id required). Auth + dev-session env auto-injected into both child processes from your saved CLI config. Run `dashwise login` first."
4104
4259
  ).option("--dir <path>", "Project root containing `module.json` (default: current directory).").option(
4105
4260
  "--installation-id <id>",
4106
- 'Override the `DASHWISE_INSTALLATION_ID` env var passed to children (default: "local").'
4261
+ "Use prod-mode SSO with this installation id instead of registering a dev session. The installation must already exist in the target workspace."
4262
+ ).option(
4263
+ "--workspace <slug-or-id>",
4264
+ "Workspace to scope the dev session to (slug or numeric id). Required if your account belongs to more than one workspace; auto-picked if you have just one."
4265
+ ).option(
4266
+ "--callback-origin <url>",
4267
+ "Additional callback origin to allowlist on the dev session (repeatable). The dev server URL (http://localhost:<port>) is always allowlisted; pass this for custom-domain local dev (e.g. https://myapp.local).",
4268
+ collectRepeated,
4269
+ []
4107
4270
  ).option(
4108
4271
  "--api-url <url>",
4109
4272
  "Override the `DASHWISE_API_URL` env var passed to children (default: saved CLI config)."
4110
- ).option("--port <number>", "Port for `next dev` (default: Next.js picks 3000 / next free).", parseIntOption).option("--sdk-only", "Only run the manifest watcher; skip `next dev`.").option("--next-only", "Only run `next dev`; skip the manifest watcher.").action(
4273
+ ).option("--port <number>", "Port for `next dev` (default: 3000).", parseIntOption).option("--sdk-only", "Only run the manifest watcher; skip `next dev`.").option("--next-only", "Only run `next dev`; skip the manifest watcher.").action(
4111
4274
  async (cmdOpts) => {
4112
4275
  process.exitCode = await devCommand({
4113
4276
  ...cmdOpts.dir !== void 0 ? { dir: cmdOpts.dir } : {},
4114
4277
  ...cmdOpts.installationId !== void 0 ? { installationId: cmdOpts.installationId } : {},
4278
+ ...cmdOpts.workspace !== void 0 ? { workspace: cmdOpts.workspace } : {},
4279
+ ...cmdOpts.callbackOrigin !== void 0 && cmdOpts.callbackOrigin.length > 0 ? { callbackOrigin: cmdOpts.callbackOrigin } : {},
4115
4280
  ...cmdOpts.apiUrl !== void 0 ? { apiUrl: cmdOpts.apiUrl } : {},
4116
4281
  ...cmdOpts.port !== void 0 ? { port: cmdOpts.port } : {},
4117
4282
  ...cmdOpts.sdkOnly !== void 0 ? { sdkOnly: cmdOpts.sdkOnly } : {},
@@ -4330,7 +4495,7 @@ moduleCmd.command("action-log").description(
4330
4495
  }
4331
4496
  })();
4332
4497
  function getVersion() {
4333
- return "0.1.0";
4498
+ return "0.2.4";
4334
4499
  }
4335
4500
  function parseIntOption(value) {
4336
4501
  const n = Number.parseInt(value, 10);
@@ -4339,5 +4504,8 @@ function parseIntOption(value) {
4339
4504
  }
4340
4505
  return n;
4341
4506
  }
4507
+ function collectRepeated(value, previous) {
4508
+ return [...previous, value];
4509
+ }
4342
4510
  //# sourceMappingURL=bin.js.map
4343
4511
  //# sourceMappingURL=bin.js.map