@moda-ai/cli 1.17.0 → 1.17.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,7 +1,7 @@
1
1
  import { authFetch, clearAuthSession, isAuthSessionValid, loadAuthSession, login, revokeCliSessionToken, } from './auth.js';
2
2
  import { EXIT_AUTH_REQUIRED, EXIT_OK } from './output.js';
3
3
  import { KEY_LIFECYCLE_ENDPOINT_REQUIREMENTS, createLocalKeyDescriptor, loadLocalKeyDescriptors, upsertLocalKeyDescriptor, writeLocalApiKeySecret, } from './keys.js';
4
- import { DEFAULT_INGEST_URL, PROD_BASE_URL, ensureSecretFilePermissions, getActiveProfile, getModaPaths, loadProfileConfig, resolveApiKeyFromActiveProfile, resolveSecretReference, secretFilePathFromRef, setActiveProfile, upsertProfile, } from './profiles.js';
4
+ import { DEFAULT_INGEST_URL, PROD_BASE_URL, clearStoredApiKeyCredentials, ensureSecretFilePermissions, getActiveProfile, getModaPaths, loadProfileConfig, resolveApiKeyFromActiveProfile, resolveSecretReference, secretFilePathFromRef, setActiveProfile, upsertProfile, } from './profiles.js';
5
5
  import { CliInputError } from './errors.js';
6
6
  // Bound the `auth status --online` probe: without a signal, undici's defaults
7
7
  // can stall a status check for minutes on a black-holed connection.
@@ -77,9 +77,21 @@ export async function runAuthCommand(context) {
77
77
  // auth0-kind sessions have nothing to revoke.
78
78
  await revokeCliSessionToken({ profile: context.profile, env: context.env });
79
79
  clearAuthSession({ profile: context.profile, env: context.env });
80
+ // The session token is only the control-plane credential. Data commands
81
+ // authenticate with the moda_sk_ API key written by `moda init`
82
+ // (~/.moda/config.json + profile api_key_ref), so logout must clear it
83
+ // too or the next login keeps acting as the old tenant.
84
+ const apiKeyCleared = clearStoredApiKeyCredentials({
85
+ profile: context.profile,
86
+ env: context.env,
87
+ });
88
+ if (context.env.MODA_API_KEY) {
89
+ console.error('Note: MODA_API_KEY is set in the environment and still takes precedence. Unset it to fully sign out.');
90
+ }
80
91
  context.output.writeData({
81
92
  status: 'logged_out',
82
93
  profile: context.profile ?? process.env.MODA_PROFILE ?? null,
94
+ api_key_cleared: apiKeyCleared,
83
95
  });
84
96
  return { exitCode: 0 };
85
97
  }
@@ -80,6 +80,20 @@ export declare function getActiveProfile(options?: ProfileIOOptions): ModaProfil
80
80
  export declare function setActiveProfile(name: string, options?: ProfileIOOptions): ProfileConfig;
81
81
  export declare function upsertProfile(profile: ModaProfile, options?: ProfileIOOptions): ProfileConfig;
82
82
  export declare function syncLegacyConfigToProfile(legacy: LegacyCliConfig, options?: ProfileIOOptions): ProfileConfig;
83
+ /**
84
+ * Forget the stored data-plane credentials (`moda_sk_` API key + tenant
85
+ * binding written by `moda init`): the legacy config's api_key/tenant_id and
86
+ * the target profile's api_key_ref/tenant_id, deleting `file:` key secrets
87
+ * under ~/.moda. Called by `moda auth logout` — without this, analytics
88
+ * commands keep authenticating as the old tenant after a re-login, because
89
+ * they use the x-api-key plane, not the session token. MODA_API_KEY in the
90
+ * environment still wins at resolve time.
91
+ *
92
+ * The legacy config must be cleared BEFORE reading the profile config:
93
+ * applyLegacyAwareness re-derives api_key_ref onto the prod profile on every
94
+ * load while config.json still holds an api_key.
95
+ */
96
+ export declare function clearStoredApiKeyCredentials(options?: ProfileIOOptions): boolean;
83
97
  export declare function secretFilePathFromRef(reference: SecretReference, options?: ProfileIOOptions): string | undefined;
84
98
  export declare function resolveSecretReference(reference: SecretReference | undefined, options?: ProfileIOOptions): string | undefined;
85
99
  export declare function resolveApiKeyFromActiveProfile(options?: ProfileIOOptions): string | undefined;
package/dist/profiles.js CHANGED
@@ -1,4 +1,4 @@
1
- import { chmodSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync, } from 'node:fs';
1
+ import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync, } from 'node:fs';
2
2
  import { homedir } from 'node:os';
3
3
  import { dirname, isAbsolute, join, normalize, relative } from 'node:path';
4
4
  export const PROFILE_CONFIG_VERSION = 1;
@@ -347,6 +347,44 @@ export function syncLegacyConfigToProfile(legacy, options = {}) {
347
347
  },
348
348
  }, options);
349
349
  }
350
+ /**
351
+ * Forget the stored data-plane credentials (`moda_sk_` API key + tenant
352
+ * binding written by `moda init`): the legacy config's api_key/tenant_id and
353
+ * the target profile's api_key_ref/tenant_id, deleting `file:` key secrets
354
+ * under ~/.moda. Called by `moda auth logout` — without this, analytics
355
+ * commands keep authenticating as the old tenant after a re-login, because
356
+ * they use the x-api-key plane, not the session token. MODA_API_KEY in the
357
+ * environment still wins at resolve time.
358
+ *
359
+ * The legacy config must be cleared BEFORE reading the profile config:
360
+ * applyLegacyAwareness re-derives api_key_ref onto the prod profile on every
361
+ * load while config.json still holds an api_key.
362
+ */
363
+ export function clearStoredApiKeyCredentials(options = {}) {
364
+ let cleared = false;
365
+ const paths = getModaPaths(options);
366
+ const legacy = loadLegacyCliConfig(options);
367
+ if (legacy.api_key || legacy.tenant_id) {
368
+ writeSecretJsonFile(paths.legacyConfigPath, legacy.ingest_url ? { ingest_url: legacy.ingest_url } : {});
369
+ cleared = true;
370
+ }
371
+ const config = loadProfileConfig(options);
372
+ const profileName = options.profile ||
373
+ optionalString(getEnv(options).MODA_PROFILE) ||
374
+ config.active_profile;
375
+ const profile = config.profiles[profileName];
376
+ if (profile && (profile.api_key_ref || profile.tenant_id)) {
377
+ if (profile.api_key_ref?.startsWith('file:')) {
378
+ const secretPath = resolveFileSecretPath(profile.api_key_ref, options);
379
+ if (secretPath)
380
+ rmSync(secretPath, { force: true });
381
+ }
382
+ const { api_key_ref: _key, tenant_id: _tenant, ...rest } = profile;
383
+ saveProfileConfig({ ...config, profiles: { ...config.profiles, [profileName]: rest } }, options);
384
+ cleared = true;
385
+ }
386
+ return cleared;
387
+ }
350
388
  function resolveFileSecretPath(reference, options) {
351
389
  if (!reference.startsWith('file:'))
352
390
  return undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moda-ai/cli",
3
- "version": "1.17.0",
3
+ "version": "1.17.1",
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-07-08T22:14:49.889Z",
4
- "cli_version": "1.17.0",
3
+ "bundled_at": "2026-07-08T22:22:27.468Z",
4
+ "cli_version": "1.17.1",
5
5
  "skills": [
6
6
  {
7
7
  "id": "integration-node-anthropic",