omnara 1.0.5 → 1.0.7

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.
Files changed (2) hide show
  1. package/dist/bin.js +311 -247
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -8,11 +8,6 @@ var __export = (target, all) => {
8
8
  // src/bin.ts
9
9
  import { Command } from "commander";
10
10
 
11
- // src/config.ts
12
- import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
13
- import { homedir } from "node:os";
14
- import { dirname, join } from "node:path";
15
-
16
11
  // ../sdk/src/generated/sdk.gen.ts
17
12
  var sdk_gen_exports = {};
18
13
  __export(sdk_gen_exports, {
@@ -1248,7 +1243,7 @@ var zConfiguredModel = z.object({
1248
1243
  context_window_tokens: z.int(),
1249
1244
  max_output_tokens: z.int(),
1250
1245
  default_max_output_tokens: z.int().nullish(),
1251
- default_cache_retention: zModelCacheRetention,
1246
+ default_cache_retention: zModelCacheRetention.optional(),
1252
1247
  supports_tools: z.boolean(),
1253
1248
  supports_reasoning: z.boolean(),
1254
1249
  default_reasoning_effort: z.string(),
@@ -6810,12 +6805,12 @@ async function pollDeviceAuthToken(options) {
6810
6805
  }
6811
6806
  }
6812
6807
 
6813
- // src/config.ts
6808
+ // src/config-file.ts
6809
+ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
6810
+ import { homedir } from "node:os";
6811
+ import { dirname, join } from "node:path";
6814
6812
  import * as z5 from "zod";
6815
6813
 
6816
- // src/interactive.ts
6817
- import { isCancel, select } from "@clack/prompts";
6818
-
6819
6814
  // src/output.ts
6820
6815
  import * as z4 from "zod";
6821
6816
  var zRenderValue = z4.json();
@@ -7003,54 +6998,7 @@ ${z4.prettifyError(error)}`);
7003
6998
  }
7004
6999
  }
7005
7000
 
7006
- // src/interactive.ts
7007
- function canPromptInteractively() {
7008
- return process.stdin.isTTY && process.stdout.isTTY;
7009
- }
7010
- async function selectFrom(message, choices) {
7011
- const [only] = choices;
7012
- if (choices.length === 1 && only !== void 0) {
7013
- console.log(`${message}: ${only.label} (only option)`);
7014
- return only.id;
7015
- }
7016
- const picked = await select({
7017
- message,
7018
- options: choices.map((choice) => ({ value: choice.id, label: choice.label, hint: choice.id }))
7019
- });
7020
- if (isCancel(picked)) throw new CliInputError("selection cancelled");
7021
- return picked;
7022
- }
7023
- async function promptOrgSelection(client2) {
7024
- const { data } = await sdk_gen_exports.getCurrentUser({ client: client2 });
7025
- if (data.orgs.length === 0) {
7026
- throw new CliInputError("this account has no organizations");
7027
- }
7028
- return selectFrom(
7029
- "Select an organization",
7030
- data.orgs.map((org) => ({ id: org.id, label: org.name }))
7031
- );
7032
- }
7033
- async function promptProjectSelection(client2, orgId) {
7034
- const projects = [];
7035
- let cursor;
7036
- do {
7037
- const { data } = await sdk_gen_exports.listVisibleProjects({
7038
- client: client2,
7039
- path: { orgID: orgId },
7040
- query: cursor === void 0 ? {} : { cursor }
7041
- });
7042
- projects.push(...data.data.map((project) => ({ id: project.id, label: project.name })));
7043
- cursor = data.next_cursor ?? void 0;
7044
- } while (cursor !== void 0);
7045
- if (projects.length === 0) {
7046
- throw new CliInputError(`no projects visible in organization ${orgId}`);
7047
- }
7048
- return selectFrom("Select a project", projects);
7049
- }
7050
-
7051
- // src/config.ts
7052
- var DEFAULT_API_URL = "https://api.omnara.com/v1";
7053
- var DEFAULT_ISSUER_URL = "https://app.omnara.com";
7001
+ // src/config-file.ts
7054
7002
  var zConfigFile = z5.looseObject({
7055
7003
  api_url: z5.url().optional(),
7056
7004
  issuer_url: z5.url().optional(),
@@ -7113,6 +7061,238 @@ ${z5.prettifyError(result.error)}`);
7113
7061
  chmodSync(configFilePath(), 384);
7114
7062
  return result.data;
7115
7063
  }
7064
+
7065
+ // src/device-login.ts
7066
+ import { hostname } from "node:os";
7067
+ import { intro, log, note, outro, spinner } from "@clack/prompts";
7068
+ import * as z6 from "zod";
7069
+
7070
+ // src/browser.ts
7071
+ import { spawn } from "node:child_process";
7072
+ function browserCommand(url4) {
7073
+ if (process.platform === "darwin") {
7074
+ return { command: "open", args: [url4], windowsVerbatimArguments: false };
7075
+ }
7076
+ if (process.platform === "win32") {
7077
+ return {
7078
+ command: "cmd",
7079
+ args: ["/c", "start", '""', `"${url4}"`],
7080
+ windowsVerbatimArguments: true
7081
+ };
7082
+ }
7083
+ return { command: "xdg-open", args: [url4], windowsVerbatimArguments: false };
7084
+ }
7085
+ function openInBrowser(url4, onError) {
7086
+ const { command, args, windowsVerbatimArguments } = browserCommand(url4);
7087
+ const child = spawn(command, args, { stdio: "ignore", detached: true, windowsVerbatimArguments });
7088
+ child.once("error", () => {
7089
+ onError("could not open a browser; use the URL above");
7090
+ });
7091
+ child.unref();
7092
+ }
7093
+
7094
+ // src/interactive.ts
7095
+ import { isCancel, select } from "@clack/prompts";
7096
+ function canPromptInteractively() {
7097
+ return process.stdin.isTTY && process.stdout.isTTY;
7098
+ }
7099
+ async function selectFrom(message, choices) {
7100
+ const [only] = choices;
7101
+ if (choices.length === 1 && only !== void 0) {
7102
+ console.log(`${message}: ${only.label} (only option)`);
7103
+ return only.id;
7104
+ }
7105
+ const picked = await select({
7106
+ message,
7107
+ options: choices.map((choice) => ({ value: choice.id, label: choice.label, hint: choice.id }))
7108
+ });
7109
+ if (isCancel(picked)) throw new CliInputError("selection cancelled");
7110
+ return picked;
7111
+ }
7112
+ async function promptOrgSelection(client2, issuerUrl) {
7113
+ const { data } = await sdk_gen_exports.getCurrentUser({ client: client2 });
7114
+ if (data.orgs.length === 0) {
7115
+ throw new CliInputError(
7116
+ `this account has no organizations; create one at ${new URL("/onboarding", issuerUrl).toString()}`
7117
+ );
7118
+ }
7119
+ return selectFrom(
7120
+ "Select an organization",
7121
+ data.orgs.map((org) => ({ id: org.id, label: org.name }))
7122
+ );
7123
+ }
7124
+ async function promptProjectSelection(client2, orgId) {
7125
+ const projects = [];
7126
+ let cursor;
7127
+ do {
7128
+ const { data } = await sdk_gen_exports.listVisibleProjects({
7129
+ client: client2,
7130
+ path: { orgID: orgId },
7131
+ query: cursor === void 0 ? {} : { cursor }
7132
+ });
7133
+ projects.push(...data.data.map((project) => ({ id: project.id, label: project.name })));
7134
+ cursor = data.next_cursor ?? void 0;
7135
+ } while (cursor !== void 0);
7136
+ if (projects.length === 0) {
7137
+ throw new CliInputError(`no projects visible in organization ${orgId}`);
7138
+ }
7139
+ return selectFrom("Select a project", projects);
7140
+ }
7141
+
7142
+ // src/device-login.ts
7143
+ async function isProjectVisible(client2, orgId, projectId) {
7144
+ let cursor;
7145
+ do {
7146
+ const { data } = await sdk_gen_exports.listVisibleProjects({
7147
+ client: client2,
7148
+ path: { orgID: orgId },
7149
+ query: cursor === void 0 ? {} : { cursor }
7150
+ });
7151
+ if (data.data.some((project) => project.id === projectId)) return true;
7152
+ cursor = data.next_cursor ?? void 0;
7153
+ } while (cursor !== void 0);
7154
+ return false;
7155
+ }
7156
+ function interactiveReporter(title) {
7157
+ intro(title);
7158
+ const spin = spinner();
7159
+ return {
7160
+ showCode(userCode, approvalUrl) {
7161
+ note(`Code: ${userCode}
7162
+ ${approvalUrl}`, "Confirm this code in your browser");
7163
+ },
7164
+ startWaiting() {
7165
+ spin.start("Waiting for approval in your browser");
7166
+ },
7167
+ stopWaiting(approved) {
7168
+ if (approved) {
7169
+ spin.stop("Approved");
7170
+ } else {
7171
+ spin.error("Approval failed");
7172
+ }
7173
+ },
7174
+ success(message) {
7175
+ log.success(message);
7176
+ },
7177
+ info(message) {
7178
+ log.info(message);
7179
+ },
7180
+ warn(message) {
7181
+ log.warn(message);
7182
+ },
7183
+ finish(message) {
7184
+ outro(message ?? "Done");
7185
+ }
7186
+ };
7187
+ }
7188
+ function plainReporter(title) {
7189
+ console.log(title);
7190
+ return {
7191
+ showCode(userCode, approvalUrl) {
7192
+ console.log(`Confirm this code in your browser: ${userCode}`);
7193
+ console.log(`Approval page: ${approvalUrl}`);
7194
+ },
7195
+ startWaiting() {
7196
+ console.log("Waiting for approval...");
7197
+ },
7198
+ stopWaiting(approved) {
7199
+ if (approved) console.log("Approved.");
7200
+ },
7201
+ success(message) {
7202
+ console.log(message);
7203
+ },
7204
+ info(message) {
7205
+ console.log(message);
7206
+ },
7207
+ warn(message) {
7208
+ console.error(`warning: ${message}`);
7209
+ },
7210
+ finish(message) {
7211
+ if (message !== void 0) console.log(message);
7212
+ }
7213
+ };
7214
+ }
7215
+ function createLoginReporter(title) {
7216
+ return canPromptInteractively() ? interactiveReporter(title) : plainReporter(title);
7217
+ }
7218
+ function loginTokenName(explicit, hostName) {
7219
+ const candidate = explicit ?? cliLoginTokenName(hostName);
7220
+ const result = zResourceName.safeParse(candidate);
7221
+ if (result.success) return result.data;
7222
+ if (explicit === void 0) return cliLoginTokenName();
7223
+ throw new CliInputError(`invalid token name:
7224
+ ${z6.prettifyError(result.error)}`);
7225
+ }
7226
+ async function loginWithDevice(options) {
7227
+ const { report } = options;
7228
+ updateConfigFile({});
7229
+ const start = await startDeviceAuth({
7230
+ issuerUrl: options.issuerUrl,
7231
+ clientId: OMNARA_CLI_OAUTH_CLIENT_ID,
7232
+ tokenName: loginTokenName(options.tokenName, hostname())
7233
+ });
7234
+ report.showCode(start.userCode, start.verificationUriComplete);
7235
+ if (options.browser) {
7236
+ openInBrowser(start.verificationUriComplete, (message) => {
7237
+ report.warn(message);
7238
+ });
7239
+ }
7240
+ report.startWaiting();
7241
+ let token;
7242
+ try {
7243
+ token = await pollDeviceAuthToken({
7244
+ tokenEndpoint: start.tokenEndpoint,
7245
+ clientId: start.clientId,
7246
+ deviceCode: start.deviceCode,
7247
+ intervalSeconds: start.intervalSeconds
7248
+ });
7249
+ } catch (error) {
7250
+ report.stopWaiting(false);
7251
+ throw error;
7252
+ }
7253
+ report.stopWaiting(true);
7254
+ const saved = updateConfigFile({
7255
+ token,
7256
+ api_url: options.apiUrl,
7257
+ issuer_url: options.issuerUrl
7258
+ });
7259
+ const client2 = createOmnaraClient({ baseUrl: options.apiUrl, auth: bearerToken(token) });
7260
+ let orgId = saved.org_id;
7261
+ let projectId = saved.project_id;
7262
+ let hasOrganizations;
7263
+ let loginMessage = "Logged in";
7264
+ let validationWarning;
7265
+ try {
7266
+ const { data: me } = await sdk_gen_exports.getCurrentUser({ client: client2 });
7267
+ loginMessage = `Logged in as ${me.user.email || me.user.display_name}`;
7268
+ hasOrganizations = me.orgs.length > 0;
7269
+ if (orgId !== void 0 && !me.orgs.some((org) => org.id === orgId)) {
7270
+ updateConfigFile({ org_id: void 0, project_id: void 0 });
7271
+ orgId = void 0;
7272
+ projectId = void 0;
7273
+ report.warn(
7274
+ "Cleared the saved default organization and project: this account cannot access them."
7275
+ );
7276
+ } else if (orgId !== void 0 && projectId !== void 0 && !await isProjectVisible(client2, orgId, projectId)) {
7277
+ updateConfigFile({ project_id: void 0 });
7278
+ projectId = void 0;
7279
+ report.warn("Cleared the saved default project: this account cannot access it.");
7280
+ }
7281
+ } catch (error) {
7282
+ validationWarning = `could not verify the account or saved organization and project defaults: ${error instanceof Error ? error.message : String(error)}`;
7283
+ }
7284
+ report.success(loginMessage);
7285
+ report.info(`Token saved to ${configFilePath()}`);
7286
+ if (validationWarning !== void 0) report.warn(validationWarning);
7287
+ if (process.env.OMNARA_API_KEY !== void 0) {
7288
+ report.warn("OMNARA_API_KEY is set and takes precedence over the saved token");
7289
+ }
7290
+ return { token, orgId, projectId, hasOrganizations };
7291
+ }
7292
+
7293
+ // src/config.ts
7294
+ var DEFAULT_API_URL = "https://api.omnara.com/v1";
7295
+ var DEFAULT_ISSUER_URL = "https://app.omnara.com";
7116
7296
  function resolveUrls(file, env) {
7117
7297
  return {
7118
7298
  apiUrl: env.OMNARA_API_URL ?? file.api_url ?? DEFAULT_API_URL,
@@ -7124,23 +7304,58 @@ function migrateLegacyBaseUrl(file) {
7124
7304
  if (baseUrl === void 0) return void 0;
7125
7305
  return rest;
7126
7306
  }
7307
+ function createTokenResolver(options) {
7308
+ let token = options.savedToken;
7309
+ let pending;
7310
+ return () => {
7311
+ if (token !== void 0) return Promise.resolve(token);
7312
+ if (!options.canPrompt()) {
7313
+ return Promise.reject(
7314
+ new CliInputError("not logged in: run 'omnara login' or set OMNARA_API_KEY")
7315
+ );
7316
+ }
7317
+ pending ??= options.login().then((result) => {
7318
+ token = result;
7319
+ return result;
7320
+ });
7321
+ return pending;
7322
+ };
7323
+ }
7127
7324
  function loadConfig() {
7128
7325
  const loaded = readConfigFile();
7129
7326
  const migrated = migrateLegacyBaseUrl(loaded);
7130
7327
  const file = migrated === void 0 ? loaded : updateConfigFile({ ...migrated, base_url: void 0 });
7131
7328
  const { apiUrl, issuerUrl } = resolveUrls(file, process.env);
7132
- const token = process.env.OMNARA_API_KEY ?? file.token;
7329
+ let saved = { orgId: file.org_id, projectId: file.project_id };
7330
+ const resolveToken = createTokenResolver({
7331
+ savedToken: process.env.OMNARA_API_KEY ?? file.token,
7332
+ canPrompt: canPromptInteractively,
7333
+ login: async () => {
7334
+ const report = createLoginReporter(`Not logged in yet. Log in to ${issuerUrl}`);
7335
+ const login = await loginWithDevice({ apiUrl, issuerUrl, browser: true, report });
7336
+ saved = { orgId: login.orgId, projectId: login.projectId };
7337
+ report.finish("Continuing");
7338
+ return login.token;
7339
+ }
7340
+ });
7133
7341
  const client2 = createOmnaraClient({
7134
7342
  baseUrl: apiUrl,
7135
- auth: token ? bearerToken(token) : void 0,
7343
+ auth: bearerToken(resolveToken),
7136
7344
  headers: { "User-Agent": "omnara-cli" }
7137
7345
  });
7138
7346
  return {
7139
7347
  client: client2,
7140
7348
  apiUrl,
7141
7349
  issuerUrl,
7142
- defaultOrgId: process.env.OMNARA_ORG_ID ?? file.org_id,
7143
- defaultProjectId: process.env.OMNARA_PROJECT_ID ?? file.project_id
7350
+ get defaultOrgId() {
7351
+ return process.env.OMNARA_ORG_ID ?? saved.orgId;
7352
+ },
7353
+ get defaultProjectId() {
7354
+ return process.env.OMNARA_PROJECT_ID ?? saved.projectId;
7355
+ },
7356
+ ensureLoggedIn: async () => {
7357
+ await resolveToken();
7358
+ }
7144
7359
  };
7145
7360
  }
7146
7361
  function describeValue(envName, fileValue, defaultValue) {
@@ -7185,7 +7400,8 @@ function registerConfigCommand(program2, cli) {
7185
7400
  if (!canPromptInteractively()) {
7186
7401
  throw new CliInputError("interactive selection needs a terminal");
7187
7402
  }
7188
- const orgId = await promptOrgSelection(cli.client);
7403
+ await cli.ensureLoggedIn();
7404
+ const orgId = await promptOrgSelection(cli.client, cli.issuerUrl);
7189
7405
  const projectId = await promptProjectSelection(cli.client, orgId);
7190
7406
  updateConfigFile({ org_id: orgId, project_id: projectId });
7191
7407
  printConfig();
@@ -7195,10 +7411,10 @@ function registerConfigCommand(program2, cli) {
7195
7411
 
7196
7412
  // src/factory.ts
7197
7413
  import { InvalidArgumentError } from "commander";
7198
- import * as z7 from "zod";
7414
+ import * as z8 from "zod";
7199
7415
 
7200
7416
  // src/flags.ts
7201
- import * as z6 from "zod";
7417
+ import * as z7 from "zod";
7202
7418
  function kebabCase(name) {
7203
7419
  return name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replaceAll("_", "-").toLowerCase();
7204
7420
  }
@@ -7315,21 +7531,21 @@ function finalizeFlag(draft) {
7315
7531
  };
7316
7532
  }
7317
7533
  function deriveFlags(schema) {
7318
- const jsonSchema = z6.toJSONSchema(schema, { io: "input" });
7534
+ const jsonSchema = z7.toJSONSchema(schema, { io: "input" });
7319
7535
  const drafts = /* @__PURE__ */ new Map();
7320
7536
  collectDrafts(drafts, jsonSchema, [], false);
7321
7537
  return [...drafts.values()].map(finalizeFlag);
7322
7538
  }
7323
7539
 
7324
7540
  // src/reporter.ts
7325
- import { intro, log, note, outro, spinner } from "@clack/prompts";
7326
- function interactiveReporter(title) {
7327
- intro(title);
7328
- const spin = spinner();
7541
+ import { intro as intro2, log as log2, note as note2, outro as outro2, spinner as spinner2 } from "@clack/prompts";
7542
+ function interactiveReporter2(title) {
7543
+ intro2(title);
7544
+ const spin = spinner2();
7329
7545
  let spinning = false;
7330
7546
  return {
7331
7547
  url(label, href) {
7332
- note(href, label);
7548
+ note2(href, label);
7333
7549
  },
7334
7550
  start(message) {
7335
7551
  spin.start(message);
@@ -7346,17 +7562,17 @@ function interactiveReporter(title) {
7346
7562
  spinning = false;
7347
7563
  },
7348
7564
  warn(message) {
7349
- log.warn(message);
7565
+ log2.warn(message);
7350
7566
  },
7351
7567
  info(message) {
7352
- log.info(message);
7568
+ log2.info(message);
7353
7569
  },
7354
7570
  done() {
7355
- outro("Done");
7571
+ outro2("Done");
7356
7572
  }
7357
7573
  };
7358
7574
  }
7359
- function plainReporter() {
7575
+ function plainReporter2() {
7360
7576
  return {
7361
7577
  url(label, href) {
7362
7578
  console.log(`${label}: ${href}`);
@@ -7382,7 +7598,7 @@ function plainReporter() {
7382
7598
  };
7383
7599
  }
7384
7600
  function createFlowReporter(title) {
7385
- return canPromptInteractively() ? interactiveReporter(title) : plainReporter();
7601
+ return canPromptInteractively() ? interactiveReporter2(title) : plainReporter2();
7386
7602
  }
7387
7603
 
7388
7604
  // src/factory.ts
@@ -7411,7 +7627,7 @@ var CONFIG_PARAMS = [
7411
7627
  configKey: "org_id",
7412
7628
  describe: "pass --org or set OMNARA_ORG_ID",
7413
7629
  resolve: (config2) => config2.defaultOrgId,
7414
- prompt: (client2) => promptOrgSelection(client2)
7630
+ prompt: (config2) => promptOrgSelection(config2.client, config2.issuerUrl)
7415
7631
  },
7416
7632
  {
7417
7633
  key: "projectID",
@@ -7420,12 +7636,12 @@ var CONFIG_PARAMS = [
7420
7636
  configKey: "project_id",
7421
7637
  describe: "pass --project or set OMNARA_PROJECT_ID",
7422
7638
  resolve: (config2, path) => path.orgID === void 0 || path.orgID === config2.defaultOrgId ? config2.defaultProjectId : void 0,
7423
- prompt: (client2, path) => {
7639
+ prompt: (config2, path) => {
7424
7640
  const orgId = path.orgID;
7425
7641
  if (typeof orgId !== "string") {
7426
7642
  throw new CliInputError("cannot select a project before an organization is set");
7427
7643
  }
7428
- return promptProjectSelection(client2, orgId);
7644
+ return promptProjectSelection(config2.client, orgId);
7429
7645
  }
7430
7646
  }
7431
7647
  ];
@@ -7496,7 +7712,7 @@ function parseWithSchema(schema, value, label) {
7496
7712
  const result = schema.safeParse(value);
7497
7713
  if (!result.success) {
7498
7714
  throw new CliInputError(`invalid ${label}:
7499
- ${z7.prettifyError(result.error)}`);
7715
+ ${z8.prettifyError(result.error)}`);
7500
7716
  }
7501
7717
  return result.data;
7502
7718
  }
@@ -7504,7 +7720,7 @@ async function callOperation(spec, input) {
7504
7720
  const result = await spec.fn(input);
7505
7721
  return result.data;
7506
7722
  }
7507
- var zBodyObject = z7.looseObject({});
7723
+ var zBodyObject = z8.looseObject({});
7508
7724
  function deepMerge(base, patch) {
7509
7725
  const merged = { ...base };
7510
7726
  for (const [key, value] of Object.entries(patch)) {
@@ -7555,7 +7771,7 @@ async function resolvePathValues(plan, args, options, config2) {
7555
7771
  usedExplicitOverride = usedExplicitOverride || explicit !== void 0;
7556
7772
  let value = explicit ?? param.resolve(config2, path);
7557
7773
  if (value === void 0 && canPromptInteractively()) {
7558
- value = await param.prompt(config2.client, path);
7774
+ value = await param.prompt(config2, path);
7559
7775
  if (!usedExplicitOverride) saveConfigDefault(param.configKey, value);
7560
7776
  }
7561
7777
  if (value === void 0) {
@@ -7582,6 +7798,7 @@ function registerOperation(parent, config2, spec) {
7582
7798
  command.option("--json", "print the raw JSON response");
7583
7799
  command.action(async (...args) => {
7584
7800
  await runCliAction(async () => {
7801
+ await config2.ensureLoggedIn();
7585
7802
  const options = command.opts();
7586
7803
  const input = { client: config2.client };
7587
7804
  if (spec.path) {
@@ -7621,6 +7838,7 @@ function registerFlow(parent, config2, spec) {
7621
7838
  for (const flag of bodyFlags) registerFlag(command, flag);
7622
7839
  command.action(async (...args) => {
7623
7840
  await runCliAction(async () => {
7841
+ await config2.ensureLoggedIn();
7624
7842
  const options = command.opts();
7625
7843
  await spec.execute({
7626
7844
  client: config2.client,
@@ -7641,176 +7859,22 @@ function registerGroup(program2, config2, group) {
7641
7859
  }
7642
7860
 
7643
7861
  // src/login.ts
7644
- import { hostname } from "node:os";
7645
- import { intro as intro2, log as log2, note as note2, outro as outro2, spinner as spinner2 } from "@clack/prompts";
7646
- import * as z8 from "zod";
7647
-
7648
- // src/browser.ts
7649
- import { spawn } from "node:child_process";
7650
- function browserCommand(url4) {
7651
- if (process.platform === "darwin") {
7652
- return { command: "open", args: [url4], windowsVerbatimArguments: false };
7653
- }
7654
- if (process.platform === "win32") {
7655
- return {
7656
- command: "cmd",
7657
- args: ["/c", "start", '""', `"${url4}"`],
7658
- windowsVerbatimArguments: true
7659
- };
7660
- }
7661
- return { command: "xdg-open", args: [url4], windowsVerbatimArguments: false };
7662
- }
7663
- function openInBrowser(url4, onError) {
7664
- const { command, args, windowsVerbatimArguments } = browserCommand(url4);
7665
- const child = spawn(command, args, { stdio: "ignore", detached: true, windowsVerbatimArguments });
7666
- child.once("error", () => {
7667
- onError("could not open a browser; use the URL above");
7668
- });
7669
- child.unref();
7670
- }
7671
-
7672
- // src/login.ts
7673
- async function isProjectVisible(client2, orgId, projectId) {
7674
- let cursor;
7675
- do {
7676
- const { data } = await sdk_gen_exports.listVisibleProjects({
7677
- client: client2,
7678
- path: { orgID: orgId },
7679
- query: cursor === void 0 ? {} : { cursor }
7680
- });
7681
- if (data.data.some((project) => project.id === projectId)) return true;
7682
- cursor = data.next_cursor ?? void 0;
7683
- } while (cursor !== void 0);
7684
- return false;
7685
- }
7686
- function interactiveReporter2(issuerUrl) {
7687
- intro2(`Log in to ${issuerUrl}`);
7688
- const spin = spinner2();
7689
- return {
7690
- showCode(userCode, approvalUrl) {
7691
- note2(`Code: ${userCode}
7692
- ${approvalUrl}`, "Confirm this code in your browser");
7693
- },
7694
- startWaiting() {
7695
- spin.start("Waiting for approval in your browser");
7696
- },
7697
- stopWaiting(approved) {
7698
- if (approved) {
7699
- spin.stop("Approved");
7700
- } else {
7701
- spin.error("Approval failed");
7702
- }
7703
- },
7704
- success(message) {
7705
- log2.success(message);
7706
- },
7707
- info(message) {
7708
- log2.info(message);
7709
- },
7710
- warn(message) {
7711
- log2.warn(message);
7712
- },
7713
- finish(message) {
7714
- outro2(message ?? "Done");
7715
- }
7716
- };
7717
- }
7718
- function plainReporter2() {
7719
- return {
7720
- showCode(userCode, approvalUrl) {
7721
- console.log(`Confirm this code in your browser: ${userCode}`);
7722
- console.log(`Approval page: ${approvalUrl}`);
7723
- },
7724
- startWaiting() {
7725
- console.log("Waiting for approval...");
7726
- },
7727
- stopWaiting(approved) {
7728
- if (approved) console.log("Approved.");
7729
- },
7730
- success(message) {
7731
- console.log(message);
7732
- },
7733
- info(message) {
7734
- console.log(message);
7735
- },
7736
- warn(message) {
7737
- console.error(`warning: ${message}`);
7738
- },
7739
- finish(message) {
7740
- if (message !== void 0) console.log(message);
7741
- }
7742
- };
7743
- }
7744
- function loginTokenName(explicit, hostName) {
7745
- const candidate = explicit ?? cliLoginTokenName(hostName);
7746
- const result = zResourceName.safeParse(candidate);
7747
- if (result.success) return result.data;
7748
- if (explicit === void 0) return cliLoginTokenName();
7749
- throw new CliInputError(`invalid token name:
7750
- ${z8.prettifyError(result.error)}`);
7751
- }
7752
7862
  function registerLoginCommand(program2, cli) {
7753
7863
  program2.command("login").description("Log in via the browser and save an API token").option("--no-browser", "print the approval URL instead of opening a browser").option("--token-name <name>", "name for the created API token").action(async (options) => {
7754
7864
  await runCliAction(async () => {
7755
- updateConfigFile({});
7756
- const report = canPromptInteractively() ? interactiveReporter2(cli.issuerUrl) : plainReporter2();
7757
- const start = await startDeviceAuth({
7865
+ const report = createLoginReporter(`Log in to ${cli.issuerUrl}`);
7866
+ const { orgId, hasOrganizations } = await loginWithDevice({
7867
+ apiUrl: cli.apiUrl,
7758
7868
  issuerUrl: cli.issuerUrl,
7759
- clientId: OMNARA_CLI_OAUTH_CLIENT_ID,
7760
- tokenName: loginTokenName(options.tokenName, hostname())
7869
+ browser: options.browser,
7870
+ tokenName: options.tokenName,
7871
+ report
7761
7872
  });
7762
- report.showCode(start.userCode, start.verificationUriComplete);
7763
- if (options.browser) {
7764
- openInBrowser(start.verificationUriComplete, (message) => {
7765
- report.warn(message);
7766
- });
7767
- }
7768
- report.startWaiting();
7769
- let token;
7770
- try {
7771
- token = await pollDeviceAuthToken({
7772
- tokenEndpoint: start.tokenEndpoint,
7773
- clientId: start.clientId,
7774
- deviceCode: start.deviceCode,
7775
- intervalSeconds: start.intervalSeconds
7776
- });
7777
- } catch (error) {
7778
- report.stopWaiting(false);
7779
- throw error;
7780
- }
7781
- report.stopWaiting(true);
7782
- const saved = updateConfigFile({
7783
- token,
7784
- api_url: cli.apiUrl,
7785
- issuer_url: cli.issuerUrl
7786
- });
7787
- const client2 = createOmnaraClient({ baseUrl: cli.apiUrl, auth: bearerToken(token) });
7788
- let orgId = saved.org_id;
7789
- let loginMessage = "Logged in";
7790
- let validationWarning;
7791
- try {
7792
- const { data: me } = await sdk_gen_exports.getCurrentUser({ client: client2 });
7793
- loginMessage = `Logged in as ${me.user.email || me.user.display_name}`;
7794
- if (orgId !== void 0 && !me.orgs.some((org) => org.id === orgId)) {
7795
- updateConfigFile({ org_id: void 0, project_id: void 0 });
7796
- orgId = void 0;
7797
- report.warn(
7798
- "Cleared the saved default organization and project: this account cannot access them."
7799
- );
7800
- } else if (orgId !== void 0 && saved.project_id !== void 0 && !await isProjectVisible(client2, orgId, saved.project_id)) {
7801
- updateConfigFile({ project_id: void 0 });
7802
- report.warn("Cleared the saved default project: this account cannot access it.");
7803
- }
7804
- } catch (error) {
7805
- validationWarning = `could not verify the account or saved organization and project defaults: ${error instanceof Error ? error.message : String(error)}`;
7806
- }
7807
- report.success(loginMessage);
7808
- report.info(`Token saved to ${configFilePath()}`);
7809
- if (validationWarning !== void 0) report.warn(validationWarning);
7810
- if (process.env.OMNARA_API_KEY !== void 0) {
7811
- report.warn("OMNARA_API_KEY is set and takes precedence over the saved token");
7812
- }
7813
- if ((process.env.OMNARA_ORG_ID ?? orgId) === void 0) {
7873
+ if (hasOrganizations === false) {
7874
+ report.finish(
7875
+ `This account has no organization yet. Create one at ${new URL("/onboarding", cli.issuerUrl).toString()}, then run 'omnara config select'.`
7876
+ );
7877
+ } else if ((process.env.OMNARA_ORG_ID ?? orgId) === void 0) {
7814
7878
  report.finish("Run 'omnara config select' to choose a default organization and project.");
7815
7879
  } else {
7816
7880
  report.finish();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnara",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Command-line interface for Omnara: launch and manage agents, organizations, integrations, and MCP servers",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://omnara.com",