@moda-ai/cli 1.37.0 → 1.38.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.
@@ -24,7 +24,7 @@ import {
24
24
  import {
25
25
  isAuthSessionValid,
26
26
  loadAuthSession
27
- } from "./cli-strngwn3.js";
27
+ } from "./cli-kegd748v.js";
28
28
  import {
29
29
  CliInputError,
30
30
  createCommandContext,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  authFetch,
3
3
  getCachedLoginContext
4
- } from "./cli-strngwn3.js";
4
+ } from "./cli-kegd748v.js";
5
5
  import {
6
6
  CliAccessError,
7
7
  CliInputError,
@@ -328,6 +328,71 @@ function getCachedLoginContext(options = {}) {
328
328
  return null;
329
329
  return cachedLoginContext.context;
330
330
  }
331
+ var MISMATCH_MESSAGE = "The sign-in code is for a different workspace than --tenant-id.";
332
+ var CONNECT_CODE_RE = /^mcc_[A-Za-z0-9_-]{43}$/;
333
+
334
+ class ConnectCodeError extends Error {
335
+ reason;
336
+ constructor(message, reason) {
337
+ super(message);
338
+ this.reason = reason;
339
+ this.name = "ConnectCodeError";
340
+ }
341
+ }
342
+ async function connectWithCode(code, options = {}) {
343
+ if (!CONNECT_CODE_RE.test(code)) {
344
+ throw new ConnectCodeError("That is not a Moda sign-in code. Copy the command from the Moda onboarding page again.", "rejected");
345
+ }
346
+ let response;
347
+ try {
348
+ response = await fetch(`${resolveModaBaseUrl(options)}/api/cli/auth/connect`, {
349
+ method: "POST",
350
+ headers: { "Content-Type": "application/json" },
351
+ body: JSON.stringify({ code, label: hostname(), ...options.tenantId ? { tenant_id: options.tenantId } : {} }),
352
+ redirect: "manual",
353
+ signal: AbortSignal.timeout(EXCHANGE_TIMEOUT_MS)
354
+ });
355
+ } catch {
356
+ throw new ConnectCodeError("Could not reach Moda to use the sign-in code.", "unreachable");
357
+ }
358
+ if (response.status === 400) {
359
+ const body = await response.json().catch(() => ({}));
360
+ if (body.error === "tenant_mismatch") {
361
+ throw new ConnectCodeError(MISMATCH_MESSAGE, "workspace_mismatch");
362
+ }
363
+ }
364
+ if (response.status === 401 || response.status === 400) {
365
+ throw new ConnectCodeError("The sign-in code was rejected: it expired (10 minutes) or was already used.", "rejected");
366
+ }
367
+ if (!response.ok || response.status >= 300 && response.status < 400) {
368
+ throw new ConnectCodeError(`Moda could not use the sign-in code (HTTP ${response.status}).`, "failed");
369
+ }
370
+ const payload = await response.json().catch(() => ({}));
371
+ const cliToken = typeof payload.cli_token === "string" ? payload.cli_token : undefined;
372
+ const expiresAtMs = typeof payload.expires_at === "string" ? Date.parse(payload.expires_at) : NaN;
373
+ const tenantId = typeof payload.tenant_id === "string" ? payload.tenant_id : undefined;
374
+ if (!cliToken || !Number.isFinite(expiresAtMs) || !tenantId) {
375
+ throw new ConnectCodeError("Moda returned an incomplete response for the sign-in code.", "failed");
376
+ }
377
+ if (options.tenantId && tenantId !== options.tenantId) {
378
+ throw new ConnectCodeError(MISMATCH_MESSAGE, "workspace_mismatch");
379
+ }
380
+ const userEmail = typeof payload.user?.email === "string" ? payload.user.email : undefined;
381
+ persistAuthSession({
382
+ access_token: cliToken,
383
+ expires_at: Math.floor(expiresAtMs / 1000),
384
+ profile: resolveStorageProfile(options),
385
+ token_type: "bearer",
386
+ token_kind: "moda_ct",
387
+ ...userEmail ? { user_email: userEmail } : {}
388
+ }, options);
389
+ const context = {
390
+ user: { email: userEmail ?? "", name: typeof payload.user?.name === "string" ? payload.user.name : null },
391
+ tenants: Array.isArray(payload.tenants) ? payload.tenants : []
392
+ };
393
+ cachedLoginContext = { profile: resolveStorageProfile(options), context };
394
+ return { ...context, tenantId };
395
+ }
331
396
  var SUCCESS_HTML = `<!DOCTYPE html>
332
397
  <html><body style="font-family:system-ui;display:flex;justify-content:center;align-items:center;height:100vh;margin:0;background:#fafafa">
333
398
  <div style="text-align:center">
@@ -761,4 +826,4 @@ function safeResolveUrl(location, base) {
761
826
  }
762
827
  }
763
828
 
764
- export { isAuthSessionValid, loadToken, loadAuthSession, clearAuthSession, revokeCliSessionToken, getCachedLoginContext, login, authFetch };
829
+ export { isAuthSessionValid, loadToken, loadAuthSession, clearAuthSession, revokeCliSessionToken, getCachedLoginContext, ConnectCodeError, connectWithCode, login, authFetch };
package/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  runPromptsCommand,
9
9
  runSkillsCommand,
10
10
  runStatusCommand
11
- } from "./cli-ert1sbbt.js";
11
+ } from "./cli-3kcy61fr.js";
12
12
  import {
13
13
  ApiError,
14
14
  HARNESS_REPORT_APPROVAL_PATH,
@@ -38,7 +38,7 @@ import {
38
38
  loadAuthSession,
39
39
  login,
40
40
  revokeCliSessionToken
41
- } from "./cli-strngwn3.js";
41
+ } from "./cli-kegd748v.js";
42
42
  import {
43
43
  AGENT_ERROR_CODES,
44
44
  CliAccessError,
@@ -5721,6 +5721,8 @@ Init flags:
5721
5721
  --no-sdk-integrate Skip SDK integration (alias: --no-sdk-guide)
5722
5722
  --verification-id=ID Tag the SDK integration's test trace with this id
5723
5723
  (the onb_ id from the Moda onboarding page)
5724
+ --connect-code=CODE Sign in with the one-time mcc_ code from the Moda
5725
+ onboarding page instead of the browser (10 min, single use)
5724
5726
  --ci-cd Create GitHub Actions Moda validation workflow
5725
5727
  --no-ci-cd Skip CI/CD workflow creation
5726
5728
  --autosync Create GitHub Actions workflow to sync prompts + skills to Moda on push to main
@@ -7084,7 +7086,7 @@ var commandRegistry = createCommandRegistry([
7084
7086
  resetApiRequestCountBeforeRun: true,
7085
7087
  telemetry: "result",
7086
7088
  handler: async (context) => {
7087
- const { runInit } = await import("./index-vmmfya6b.js");
7089
+ const { runInit } = await import("./index-hwfe7gs8.js");
7088
7090
  if (context.outputMode === "agent-stream") {
7089
7091
  context.output.writeEvent({
7090
7092
  event: "started",
@@ -7125,7 +7127,7 @@ var commandRegistry = createCommandRegistry([
7125
7127
  resetApiRequestCountBeforeRun: true,
7126
7128
  telemetry: "result-and-error",
7127
7129
  handler: async (context) => {
7128
- const { runProvision } = await import("./provision-spwn5t41.js");
7130
+ const { runProvision } = await import("./provision-64c2n9aw.js");
7129
7131
  return runProvision(context, {
7130
7132
  resumeCommand: buildResumeCommand("provision", context.rawFlags)
7131
7133
  });
@@ -3,7 +3,7 @@ import {
3
3
  initPrompts,
4
4
  runPromptSync,
5
5
  runSkillSync
6
- } from "./cli-ert1sbbt.js";
6
+ } from "./cli-3kcy61fr.js";
7
7
  import {
8
8
  codingAgentDisplayName,
9
9
  describeCodingAgentEvent,
@@ -34,11 +34,14 @@ import {
34
34
  } from "./cli-ssfq84k5.js";
35
35
  import {
36
36
  selectTenantAndCreateKey
37
- } from "./cli-qgf2cz2v.js";
37
+ } from "./cli-k38j2fpq.js";
38
38
  import {
39
+ ConnectCodeError,
40
+ connectWithCode,
41
+ loadAuthSession,
39
42
  loadToken,
40
43
  login
41
- } from "./cli-strngwn3.js";
44
+ } from "./cli-kegd748v.js";
42
45
  import {
43
46
  CliAuthError,
44
47
  CliInputError,
@@ -1643,6 +1646,32 @@ async function runInit(flags, options = {}) {
1643
1646
  const profile = stringFlag(flags.profile);
1644
1647
  log(renderInitStep(1, 5, "Sign in", "No environment variable files are written. File changes are listed before completion."));
1645
1648
  let token = loadToken({ profile });
1649
+ const connectCode = stringFlag(flagValue(flags, "connect-code", "connect_code"));
1650
+ let codeTenantId;
1651
+ if (connectCode) {
1652
+ const previousEmail = token ? loadAuthSession({ profile })?.user_email : undefined;
1653
+ try {
1654
+ const connected = await connectWithCode(connectCode, {
1655
+ profile,
1656
+ tenantId: stringFlag(flagValue(flags, "tenant-id", "tenant_id"))
1657
+ });
1658
+ token = loadToken({ profile });
1659
+ codeTenantId = connected.tenantId;
1660
+ const email = connected.user.email || "your Moda account";
1661
+ const replaced = previousEmail && previousEmail !== connected.user.email ? ` (replacing the CLI session for ${previousEmail})` : "";
1662
+ log(renderInitNote(`Signed in as ${email} with the one-time code${replaced}`));
1663
+ emit("auth", `Signed in as ${email} with the one-time code${replaced}.`);
1664
+ } catch (error) {
1665
+ if (!(error instanceof ConnectCodeError))
1666
+ throw error;
1667
+ if (error.reason === "workspace_mismatch") {
1668
+ throw new CliInputError(error.message, "Copy the command again from the Moda onboarding page, or drop --tenant-id.");
1669
+ }
1670
+ const next = token ? `Continuing with your existing CLI session${previousEmail ? ` (${previousEmail})` : ""}.` : "Signing in through the browser instead.";
1671
+ warn(renderInitNote(`${error.message} ${next}`, "warning"));
1672
+ emit("auth", `${error.message} ${next}`, "warning");
1673
+ }
1674
+ }
1646
1675
  if (!token) {
1647
1676
  if (json || agent || noPrompts) {
1648
1677
  throw new CliAuthError("Browser authentication is required before non-interactive init can continue.", {
@@ -1652,7 +1681,8 @@ async function runInit(flags, options = {}) {
1652
1681
  }, "Run `moda auth login`, then re-run `moda init`.");
1653
1682
  }
1654
1683
  let action = "login";
1655
- if (!yes) {
1684
+ const fromDashboard = Boolean(connectCode || stringFlag(flagValue(flags, "tenant-id", "tenant_id")) || stringFlag(flagValue(flags, "verification-id", "verification_id")));
1685
+ if (!yes && !fromDashboard) {
1656
1686
  action = await select({
1657
1687
  message: "Get started",
1658
1688
  choices: [
@@ -1673,14 +1703,17 @@ async function runInit(flags, options = {}) {
1673
1703
  log(renderInitStep(2, 5, "Workspace"));
1674
1704
  emit("workspace", "Selecting workspace and creating API key.");
1675
1705
  const tenantIdFlag = stringFlag(flagValue(flags, "tenant-id", "tenant_id"));
1706
+ if (codeTenantId && tenantIdFlag && tenantIdFlag !== codeTenantId) {
1707
+ throw new CliInputError("The sign-in code is for a different workspace than --tenant-id.", "Copy the command again from the Moda onboarding page, or drop --tenant-id.");
1708
+ }
1676
1709
  const verificationId = stringFlag(flagValue(flags, "verification-id", "verification_id"));
1677
1710
  if (verificationId !== undefined && !isAcceptedVerificationId(verificationId)) {
1678
1711
  throw new CliInputError(`Invalid --verification-id ${JSON.stringify(verificationId).slice(0, 80)}`, "Pass the onb_ id shown in the Moda onboarding page, or omit the flag.");
1679
1712
  }
1680
1713
  const tenantIdEnv = stringFlag(process.env.MODA_TENANT_ID);
1681
1714
  const { apiKey, tenantId: selectedTenantId, tenantName } = await selectTenantAndCreateKey({
1682
- tenantId: tenantIdFlag ?? tenantIdEnv,
1683
- tenantIdSource: tenantIdFlag ? "flag" : tenantIdEnv ? "env" : undefined,
1715
+ tenantId: tenantIdFlag ?? codeTenantId ?? tenantIdEnv,
1716
+ tenantIdSource: tenantIdFlag || codeTenantId ? "flag" : tenantIdEnv ? "env" : undefined,
1684
1717
  nonInteractive: json || agent || noPrompts,
1685
1718
  silent: !human,
1686
1719
  resumeCommand: ["moda", "init", ...resumeFlagsForInit(flags)],
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  selectTenantAndCreateKey
3
- } from "./cli-qgf2cz2v.js";
3
+ } from "./cli-k38j2fpq.js";
4
4
  import {
5
5
  loadAuthSession
6
- } from "./cli-strngwn3.js";
6
+ } from "./cli-kegd748v.js";
7
7
  import {
8
8
  CliAuthError,
9
9
  resolveIngestUrl,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moda-ai/cli",
3
- "version": "1.37.0",
3
+ "version": "1.38.0",
4
4
  "description": "CLI for Moda - AI agent analytics and observability",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schema_version": "moda.skill_index.v1",
3
- "bundled_at": "2026-09-27T21:11:50.220Z",
4
- "cli_version": "1.37.0",
3
+ "bundled_at": "2026-09-28T05:22:37.701Z",
4
+ "cli_version": "1.38.0",
5
5
  "skills": [
6
6
  {
7
7
  "id": "integration-cloudflare-think",