omnara 1.0.0-beta.1 → 1.0.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.
Files changed (3) hide show
  1. package/README.md +26 -0
  2. package/dist/bin.js +380 -232
  3. package/package.json +12 -3
package/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # Omnara CLI
2
+
3
+ Command-line interface for [Omnara](https://omnara.com): launch and manage agents, organizations, integrations, and MCP servers.
4
+
5
+ ## Installation
6
+
7
+ ```sh
8
+ npx omnara
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```sh
14
+ omnara login
15
+ omnara --help
16
+ ```
17
+
18
+ Requires Node.js 22 or later.
19
+
20
+ ## Documentation
21
+
22
+ See the [Omnara docs](https://docs.omnara.com) and the [GitHub repository](https://github.com/omnara-ai/omnara).
23
+
24
+ ## License
25
+
26
+ Apache-2.0
package/dist/bin.js CHANGED
@@ -210,7 +210,7 @@ function createSseClient({
210
210
  ...options
211
211
  }) {
212
212
  let lastEventId;
213
- const sleep = sseSleepFn ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
213
+ const sleep2 = sseSleepFn ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
214
214
  const createStream = async function* () {
215
215
  let retryDelay = sseDefaultRetryDelay ?? 3e3;
216
216
  let attempt = 0;
@@ -314,7 +314,7 @@ function createSseClient({
314
314
  break;
315
315
  }
316
316
  const backoff = Math.min(retryDelay * 2 ** (attempt - 1), sseMaxRetryDelay ?? 3e4);
317
- await sleep(backoff);
317
+ await sleep2(backoff);
318
318
  }
319
319
  }
320
320
  };
@@ -1012,6 +1012,10 @@ var zError = z.object({
1012
1012
  "authentication_unavailable"
1013
1013
  ])
1014
1014
  });
1015
+ var zWarning = z.object({
1016
+ message: z.string(),
1017
+ code: z.enum(["missing_recommended_machine_tools"])
1018
+ });
1015
1019
  var zClientErrorCode = z.enum([
1016
1020
  "invalid_request",
1017
1021
  "validation_failed",
@@ -1043,6 +1047,8 @@ var zOrgInvitationId = z.string().regex(/^oinv_[a-z2-7]{26}$/);
1043
1047
  var zPersonalAccessTokenId = z.string().regex(/^pat_[a-z2-7]{26}$/);
1044
1048
  var zOrgApiKeyId = z.string().regex(/^oak_[a-z2-7]{26}$/);
1045
1049
  var zProjectId = z.string().regex(/^proj_[a-z2-7]{26}$/);
1050
+ var zMcpoAuthFlowId = z.string().regex(/^moaf_[a-z2-7]{26}$/);
1051
+ var zIntegrationOAuthFlowId = z.string().regex(/^ioaf_[a-z2-7]{26}$/);
1046
1052
  var zActorId = z.string().regex(/^actr_[a-z2-7]{26}$/);
1047
1053
  var zAgentId = z.string().regex(/^agt_[a-z2-7]{26}$/);
1048
1054
  var zAgentConfigId = z.string().regex(/^acfg_[a-z2-7]{26}$/);
@@ -1220,6 +1226,7 @@ var zConfiguredModel = z.object({
1220
1226
  id: zConfiguredModelId,
1221
1227
  org_id: zOrganizationId,
1222
1228
  model_provider_config_id: zModelProviderConfigId,
1229
+ management_kind: zManagementKind,
1223
1230
  name: z.string(),
1224
1231
  current_revision_id: zConfiguredModelRevisionId,
1225
1232
  provider_model_slug: z.string(),
@@ -1363,6 +1370,7 @@ var zSkillGrant = z.object({
1363
1370
  });
1364
1371
  var zMcpoAuthStartMetadata = z.record(z.string(), z.string().max(512));
1365
1372
  var zMcpoAuthStartResponse = z.object({
1373
+ flow_id: zMcpoAuthFlowId,
1366
1374
  authorization_url: z.url(),
1367
1375
  expires_at: zTimestamp
1368
1376
  });
@@ -1395,6 +1403,7 @@ var zListIntegrationInstallsResponse = z.object({
1395
1403
  });
1396
1404
  var zIntegrationOAuthSetup = z.object({
1397
1405
  provider: z.string(),
1406
+ flow_id: zIntegrationOAuthFlowId,
1398
1407
  oauth_url: z.url(),
1399
1408
  redirect_uri: z.url(),
1400
1409
  events_url: z.url(),
@@ -1413,6 +1422,7 @@ var zCreateSlackSetupRequest = z.object({
1413
1422
  });
1414
1423
  var zSlackSetup = z.object({
1415
1424
  provider: z.string(),
1425
+ flow_id: zIntegrationOAuthFlowId,
1416
1426
  slack_app_id: z.string(),
1417
1427
  oauth_url: z.url(),
1418
1428
  redirect_uri: z.url(),
@@ -1483,6 +1493,7 @@ var zAgentConfig = z.object({
1483
1493
  effective_definition_hash: z.string(),
1484
1494
  model: zAgentConfigModel,
1485
1495
  instruction_hash: z.string().optional(),
1496
+ warnings: z.array(zWarning).min(1).optional(),
1486
1497
  created_at: zTimestamp
1487
1498
  });
1488
1499
  var zCreateAgentProfileRequest = z.object({
@@ -2441,6 +2452,11 @@ var zUpdateMachinePoolRequest = z.object({
2441
2452
  max_machine_memory_mb: z.int().gte(1).lte(2147483647).nullish(),
2442
2453
  metadata: zMachineMetadata.optional()
2443
2454
  });
2455
+ var zMachinePoolUsage = z.object({
2456
+ machines: z.int().gte(0).max(2147483647, { error: "Invalid value: Expected int32 to be <= 2147483647" }),
2457
+ cpu: z.coerce.bigint().gte(BigInt(0)).max(BigInt("9223372036854775807"), { error: "Invalid value: Expected int64 to be <= 9223372036854775807" }),
2458
+ memory_mb: z.coerce.bigint().gte(BigInt(0)).max(BigInt("9223372036854775807"), { error: "Invalid value: Expected int64 to be <= 9223372036854775807" })
2459
+ });
2444
2460
  var zMachinePool = z.object({
2445
2461
  id: zMachinePoolId,
2446
2462
  org_id: zOrganizationId,
@@ -2466,7 +2482,8 @@ var zMachinePool = z.object({
2466
2482
  max_machine_memory_mb: z.int().gte(1).lte(2147483647).nullable(),
2467
2483
  metadata: zMetadata,
2468
2484
  created_at: zTimestamp,
2469
- updated_at: zTimestamp
2485
+ updated_at: zTimestamp,
2486
+ usage: zMachinePoolUsage.optional()
2470
2487
  });
2471
2488
  var zListMachinePoolsResponse = z.object({
2472
2489
  data: z.array(zMachinePool),
@@ -3114,6 +3131,7 @@ var zListSkillGrantsQuery = z.object({
3114
3131
  cursor: z.string().max(1024).optional()
3115
3132
  });
3116
3133
  var zListSkillGrantsResponse2 = zListSkillGrantsResponse;
3134
+ var zCreateSkillGrantBody = zSkillGrantCreateRequest;
3117
3135
  var zCreateSkillGrantPath = z.object({
3118
3136
  orgID: z.string().regex(/^org_[a-z2-7]{26}$/),
3119
3137
  skillID: zSkillId
@@ -3154,6 +3172,7 @@ var zListSecretsQuery = z.object({
3154
3172
  "user"
3155
3173
  ]).optional(),
3156
3174
  owner_project_id: zProjectId.optional(),
3175
+ mcp_oauth_flow_id: zMcpoAuthFlowId.optional(),
3157
3176
  metadata: z.record(z.string(), z.string()).optional(),
3158
3177
  sort: zResourceListSort.optional(),
3159
3178
  limit: z.int().gte(1).lte(100).optional().default(50),
@@ -3201,6 +3220,7 @@ var zListSecretGrantsQuery = z.object({
3201
3220
  cursor: z.string().max(1024).optional()
3202
3221
  });
3203
3222
  var zListSecretGrantsResponse2 = zListSecretGrantsResponse;
3223
+ var zCreateSecretGrantBody = zSecretGrantCreateRequest;
3204
3224
  var zCreateSecretGrantPath = z.object({
3205
3225
  orgID: z.string().regex(/^org_[a-z2-7]{26}$/),
3206
3226
  secretID: z.string().regex(/^sec_[a-z2-7]{26}$/)
@@ -3219,6 +3239,7 @@ var zListIntegrationInstallsPath = z.object({
3219
3239
  var zListIntegrationInstallsQuery = z.object({
3220
3240
  name: z.string().min(1).max(200).optional(),
3221
3241
  agent_profile_id: zAgentProfileId.optional(),
3242
+ oauth_flow_id: zIntegrationOAuthFlowId.optional(),
3222
3243
  sort: zResourceListSort.optional(),
3223
3244
  limit: z.int().gte(1).lte(100).optional().default(50),
3224
3245
  cursor: z.string().max(1024).optional()
@@ -3561,6 +3582,7 @@ var zListProjectMachineGrantsQuery = z.object({
3561
3582
  cursor: z.string().max(1024).optional()
3562
3583
  });
3563
3584
  var zListProjectMachineGrantsResponse2 = zListProjectMachineGrantsResponse;
3585
+ var zCreateProjectMachineGrantBody = zCreateProjectMachineGrantRequest;
3564
3586
  var zCreateProjectMachineGrantHeaders = z.object({
3565
3587
  "Idempotency-Key": z.string().min(1).max(255).optional()
3566
3588
  });
@@ -3737,6 +3759,7 @@ var zListProjectModelGrantsQuery = z.object({
3737
3759
  cursor: z.string().max(1024).optional()
3738
3760
  });
3739
3761
  var zListProjectModelGrantsResponse2 = zListProjectModelGrantsResponse;
3762
+ var zCreateProjectModelGrantBody = zCreateProjectModelGrantRequest;
3740
3763
  var zCreateProjectModelGrantPath = z.object({
3741
3764
  orgID: z.string().regex(/^org_[a-z2-7]{26}$/),
3742
3765
  projectID: z.string().regex(/^proj_[a-z2-7]{26}$/)
@@ -3748,6 +3771,7 @@ var zDeleteProjectModelGrantPath = z.object({
3748
3771
  modelGrantID: z.string().regex(/^pmog_[a-z2-7]{26}$/)
3749
3772
  });
3750
3773
  var zDeleteProjectModelGrantResponse = z.void();
3774
+ var zUpdateProjectModelGrantBody = zUpdateProjectModelGrantRequest;
3751
3775
  var zUpdateProjectModelGrantPath = z.object({
3752
3776
  orgID: z.string().regex(/^org_[a-z2-7]{26}$/),
3753
3777
  projectID: z.string().regex(/^proj_[a-z2-7]{26}$/),
@@ -3796,6 +3820,7 @@ var zListProjectMachinePoolGrantsQuery = z.object({
3796
3820
  cursor: z.string().max(1024).optional()
3797
3821
  });
3798
3822
  var zListProjectMachinePoolGrantsResponse2 = zListProjectMachinePoolGrantsResponse;
3823
+ var zCreateProjectMachinePoolGrantBody = zCreateProjectMachinePoolGrantRequest;
3799
3824
  var zCreateProjectMachinePoolGrantHeaders = z.object({
3800
3825
  "Idempotency-Key": z.string().min(1).max(255).optional()
3801
3826
  });
@@ -3816,6 +3841,7 @@ var zGetProjectMachinePoolGrantPath = z.object({
3816
3841
  poolGrantID: z.string().regex(/^pmpg_[a-z2-7]{26}$/)
3817
3842
  });
3818
3843
  var zGetProjectMachinePoolGrantResponse = zProjectMachinePoolGrant;
3844
+ var zUpdateProjectMachinePoolGrantBody = zUpdateProjectMachinePoolGrantRequest;
3819
3845
  var zUpdateProjectMachinePoolGrantPath = z.object({
3820
3846
  orgID: z.string().regex(/^org_[a-z2-7]{26}$/),
3821
3847
  projectID: z.string().regex(/^proj_[a-z2-7]{26}$/),
@@ -6312,7 +6338,8 @@ function createOmnaraClient(options = {}) {
6312
6338
  );
6313
6339
  client2.setConfig({
6314
6340
  baseUrl: options.baseUrl ?? "",
6315
- credentials: options.credentials
6341
+ credentials: options.credentials,
6342
+ headers: options.headers
6316
6343
  });
6317
6344
  const { auth } = options;
6318
6345
  if (auth) {
@@ -6390,10 +6417,10 @@ function defaultSleep(seconds) {
6390
6417
  }
6391
6418
  async function pollDeviceAuthToken(options) {
6392
6419
  const fetchImpl = options.fetch ?? fetch;
6393
- const sleep = options.sleep ?? defaultSleep;
6420
+ const sleep2 = options.sleep ?? defaultSleep;
6394
6421
  let interval = options.intervalSeconds;
6395
6422
  while (true) {
6396
- await sleep(interval);
6423
+ await sleep2(interval);
6397
6424
  const response = await postDeviceAuthJSON(
6398
6425
  fetchImpl,
6399
6426
  options.baseUrl,
@@ -6725,7 +6752,8 @@ function loadConfig() {
6725
6752
  const token = process.env.OMNARA_API_KEY ?? file.token;
6726
6753
  const client2 = createOmnaraClient({
6727
6754
  baseUrl,
6728
- auth: token ? bearerToken(token) : void 0
6755
+ auth: token ? bearerToken(token) : void 0,
6756
+ headers: { "User-Agent": "omnara-cli" }
6729
6757
  });
6730
6758
  return {
6731
6759
  client: client2,
@@ -6952,7 +6980,8 @@ function plainReporter() {
6952
6980
  stop(message) {
6953
6981
  console.log(`${message}.`);
6954
6982
  },
6955
- fail() {
6983
+ fail(message) {
6984
+ console.error(message);
6956
6985
  },
6957
6986
  warn(message) {
6958
6987
  console.error(`warning: ${message}`);
@@ -6961,6 +6990,7 @@ function plainReporter() {
6961
6990
  console.log(message);
6962
6991
  },
6963
6992
  done() {
6993
+ console.log("Done");
6964
6994
  }
6965
6995
  };
6966
6996
  }
@@ -6981,7 +7011,7 @@ function flowOp(spec) {
6981
7011
  client: input.client,
6982
7012
  path: parseWithSchema(spec.path, input.path, "arguments"),
6983
7013
  body: parseWithSchema(spec.body, input.body, "flags"),
6984
- report: input.report
7014
+ report: createFlowReporter(spec.summary)
6985
7015
  })
6986
7016
  };
6987
7017
  }
@@ -7113,7 +7143,9 @@ function planPathParams(path, positional) {
7113
7143
  (param) => pathParams.includes(param.key) && !positional.includes(param.key)
7114
7144
  );
7115
7145
  return {
7116
- positionalParams: pathParams.filter((name) => !configParams.some((param) => param.key === name)),
7146
+ positionalParams: pathParams.filter(
7147
+ (name) => !configParams.some((param) => param.key === name)
7148
+ ),
7117
7149
  configParams
7118
7150
  };
7119
7151
  }
@@ -7200,8 +7232,7 @@ function registerFlow(parent, config2, spec) {
7200
7232
  await spec.execute({
7201
7233
  client: config2.client,
7202
7234
  path: await resolvePathValues(plan, args, options, config2),
7203
- body: collectFlagValues(bodyFlags, options),
7204
- report: createFlowReporter(spec.summary)
7235
+ body: collectFlagValues(bodyFlags, options)
7205
7236
  });
7206
7237
  });
7207
7238
  });
@@ -7222,13 +7253,21 @@ import { intro as intro2, log as log2, note as note2, outro as outro2, spinner a
7222
7253
  // src/browser.ts
7223
7254
  import { spawn } from "node:child_process";
7224
7255
  function browserCommand(url3) {
7225
- if (process.platform === "darwin") return { command: "open", args: [url3] };
7226
- if (process.platform === "win32") return { command: "cmd", args: ["/c", "start", "", url3] };
7227
- return { command: "xdg-open", args: [url3] };
7256
+ if (process.platform === "darwin") {
7257
+ return { command: "open", args: [url3], windowsVerbatimArguments: false };
7258
+ }
7259
+ if (process.platform === "win32") {
7260
+ return {
7261
+ command: "cmd",
7262
+ args: ["/c", "start", '""', `"${url3}"`],
7263
+ windowsVerbatimArguments: true
7264
+ };
7265
+ }
7266
+ return { command: "xdg-open", args: [url3], windowsVerbatimArguments: false };
7228
7267
  }
7229
7268
  function openInBrowser(url3, onError) {
7230
- const { command, args } = browserCommand(url3);
7231
- const child = spawn(command, args, { stdio: "ignore", detached: true });
7269
+ const { command, args, windowsVerbatimArguments } = browserCommand(url3);
7270
+ const child = spawn(command, args, { stdio: "ignore", detached: true, windowsVerbatimArguments });
7232
7271
  child.once("error", () => {
7233
7272
  onError("could not open a browser; use the URL above");
7234
7273
  });
@@ -7413,84 +7452,56 @@ function formatVoid(message = "ok") {
7413
7452
  }
7414
7453
 
7415
7454
  // src/mcp-add.ts
7455
+ import { isMap, isScalar as isScalar2, parseDocument } from "yaml";
7416
7456
  import * as z8 from "zod";
7417
7457
 
7418
7458
  // src/mcp-oauth.ts
7419
7459
  import * as z7 from "zod";
7460
+
7461
+ // src/poll.ts
7420
7462
  var POLL_INTERVAL_SECONDS = 2;
7421
- function exactGlob(value) {
7422
- return value.replace(/[\\*?]/g, "\\$&");
7423
- }
7424
- function sameOwner(secret, owner) {
7425
- if (secret.owner.kind !== owner.kind) return false;
7426
- if (owner.kind !== "project") return true;
7427
- return secret.owner.kind === "project" && secret.owner.project_id === owner.project_id;
7428
- }
7429
- async function matchingSecrets(client2, orgId, owner, name) {
7430
- const { data } = await sdk_gen_exports.listSecrets({
7431
- client: client2,
7432
- path: { orgID: orgId },
7433
- query: {
7434
- name: exactGlob(name),
7435
- kind: "oauth_token_set",
7436
- owner_kind: owner.kind,
7437
- ...owner.kind === "project" ? { owner_project_id: owner.project_id } : {},
7438
- limit: 100
7439
- }
7440
- });
7441
- return data.data.filter((secret) => secret.name === name && sameOwner(secret, owner));
7442
- }
7443
- async function snapshotSecrets(client2, orgId, owner, name) {
7444
- return new Map(
7445
- (await matchingSecrets(client2, orgId, owner, name)).map((secret) => [
7446
- secret.id,
7447
- { version: secret.current_version_number, updatedAt: secret.updated_at }
7448
- ])
7449
- );
7450
- }
7451
- function changedSecret(secrets, before) {
7452
- return secrets.find((secret) => {
7453
- const previous = before.get(secret.id);
7454
- return previous === void 0 || secret.current_version_number > previous.version || secret.updated_at !== previous.updatedAt;
7455
- });
7456
- }
7457
- function defaultSleep2(seconds) {
7463
+ var MAX_CONSECUTIVE_POLL_FAILURES = 3;
7464
+ function sleep(seconds) {
7458
7465
  return new Promise((resolve2) => setTimeout(resolve2, seconds * 1e3));
7459
7466
  }
7460
- async function pollForMcpOAuthSecret(options) {
7461
- const sleep = options.sleep ?? defaultSleep2;
7462
- const now = options.now ?? Date.now;
7467
+ async function pollUntilDeadline(options) {
7463
7468
  const deadline = Date.parse(options.expiresAt);
7464
- while (now() < deadline) {
7465
- const created = changedSecret(
7466
- await matchingSecrets(options.client, options.orgId, options.owner, options.name),
7467
- options.before
7468
- );
7469
- if (created !== void 0) return created;
7470
- await sleep(Math.min(POLL_INTERVAL_SECONDS, Math.max(0, (deadline - now()) / 1e3)));
7469
+ let consecutiveFailures = 0;
7470
+ while (true) {
7471
+ try {
7472
+ const found = await options.fetchOnce();
7473
+ consecutiveFailures = 0;
7474
+ if (found !== void 0) return found;
7475
+ } catch (error) {
7476
+ consecutiveFailures += 1;
7477
+ if (consecutiveFailures >= MAX_CONSECUTIVE_POLL_FAILURES) throw error;
7478
+ }
7479
+ const remainingSeconds = (deadline - Date.now()) / 1e3;
7480
+ if (remainingSeconds <= 0) break;
7481
+ await sleep(Math.min(POLL_INTERVAL_SECONDS, remainingSeconds));
7471
7482
  }
7472
- throw new CliInputError("MCP OAuth authorization expired before the secret was created");
7483
+ throw new CliInputError(options.expiredMessage);
7473
7484
  }
7485
+
7486
+ // src/mcp-oauth.ts
7474
7487
  async function authorizeMcpOAuthSecret(options) {
7475
- const before = await snapshotSecrets(
7476
- options.client,
7477
- options.orgId,
7478
- options.request.owner,
7479
- options.request.name
7480
- );
7481
7488
  const { data: start } = await sdk_gen_exports.startSecretMcpoAuth({
7482
7489
  client: options.client,
7483
7490
  path: { orgID: options.orgId },
7484
7491
  body: options.request
7485
7492
  });
7486
7493
  options.onAuthorization(start.authorization_url);
7487
- return pollForMcpOAuthSecret({
7488
- client: options.client,
7489
- orgId: options.orgId,
7490
- owner: options.request.owner,
7491
- name: options.request.name,
7492
- before,
7493
- expiresAt: start.expires_at
7494
+ return pollUntilDeadline({
7495
+ expiresAt: start.expires_at,
7496
+ expiredMessage: "MCP OAuth authorization expired before the secret was created",
7497
+ async fetchOnce() {
7498
+ const { data } = await sdk_gen_exports.listSecrets({
7499
+ client: options.client,
7500
+ path: { orgID: options.orgId },
7501
+ query: { mcp_oauth_flow_id: start.flow_id, limit: 1 }
7502
+ });
7503
+ return data.data[0];
7504
+ }
7494
7505
  });
7495
7506
  }
7496
7507
  var zBrowserFlag = z7.boolean().default(true).describe("open the authorization URL in a browser");
@@ -7539,98 +7550,63 @@ var zMcpAddBody = zMcpoAuthStartRequest.omit({ owner: true, name: true }).extend
7539
7550
  function targetLabel(target) {
7540
7551
  return target === "agent" ? "agent" : "agent profile";
7541
7552
  }
7542
- function isRecord(value) {
7543
- return typeof value === "object" && value !== null && !Array.isArray(value);
7544
- }
7545
7553
  function serverEntry(url3, secretId) {
7546
7554
  return { url: url3, auth: { type: "oauth", secret_id: secretId } };
7547
7555
  }
7556
+ var zJsonRecord = z8.record(z8.string(), z8.json());
7548
7557
  function prepareJsonConfig(config2, serverName, mcpUrl) {
7549
- let parsed;
7558
+ let parsedJson;
7550
7559
  try {
7551
- parsed = JSON.parse(config2.source ?? "");
7560
+ parsedJson = JSON.parse(config2.source ?? "");
7552
7561
  } catch {
7553
7562
  throw new CliInputError("the current agent config contains invalid JSON");
7554
7563
  }
7555
- if (!isRecord(parsed)) throw new CliInputError("the current agent config must be an object");
7556
- const existingMcp = parsed.mcp;
7557
- if (existingMcp !== void 0 && !isRecord(existingMcp)) {
7564
+ const parsed = zJsonRecord.safeParse(parsedJson);
7565
+ if (!parsed.success) throw new CliInputError("the current agent config must be an object");
7566
+ const mcp = zJsonRecord.optional().safeParse(parsed.data.mcp);
7567
+ if (!mcp.success) {
7558
7568
  throw new CliInputError("the current agent config has an invalid mcp section");
7559
7569
  }
7560
- if (existingMcp !== void 0 && Object.hasOwn(existingMcp, serverName)) {
7570
+ if (mcp.data !== void 0 && Object.hasOwn(mcp.data, serverName)) {
7561
7571
  throw new CliInputError(`MCP server ${serverName} already exists in the current config`);
7562
7572
  }
7563
7573
  return {
7564
7574
  currentConfigId: config2.id,
7565
7575
  render(secretId) {
7566
- const mcp = existingMcp ?? {};
7567
- mcp[serverName] = serverEntry(mcpUrl, secretId);
7568
- parsed.mcp = mcp;
7569
- return { source: `${JSON.stringify(parsed, null, 2)}
7576
+ const updated = {
7577
+ ...parsed.data,
7578
+ mcp: { ...mcp.data, [serverName]: serverEntry(mcpUrl, secretId) }
7579
+ };
7580
+ return { source: `${JSON.stringify(updated, null, 2)}
7570
7581
  `, sourceFormat: "json" };
7571
7582
  }
7572
7583
  };
7573
7584
  }
7574
7585
  function prepareYamlConfig(config2, serverName, mcpUrl) {
7575
- const source = config2.source ?? "";
7576
- const lines = source.split(/\r?\n/);
7577
- const mcpIndex = lines.findIndex((line) => /^mcp\s*:/.test(line));
7578
- const quotedServerName = serverName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7579
- const entry = (indent, secretId) => {
7580
- const outer = " ".repeat(indent);
7581
- const inner = " ".repeat(indent + 2);
7582
- const auth = " ".repeat(indent + 4);
7583
- return [
7584
- `${outer}${serverName}:`,
7585
- `${inner}url: ${JSON.stringify(mcpUrl)}`,
7586
- `${inner}auth:`,
7587
- `${auth}type: oauth`,
7588
- `${auth}secret_id: ${JSON.stringify(secretId)}`
7589
- ];
7590
- };
7591
- let sectionEnd = lines.length;
7592
- let childIndent = 2;
7593
- if (mcpIndex !== -1) {
7594
- const declaration = lines[mcpIndex] ?? "";
7595
- const remainder = declaration.replace(/^mcp\s*:\s*/, "");
7596
- if (!/^(?:#.*)?$/.test(remainder) && !/^\{\s*\}(?:\s*#.*)?$/.test(remainder)) {
7597
- throw new CliInputError("mcp-add cannot edit an inline YAML mcp mapping");
7598
- }
7599
- for (let index = mcpIndex + 1; index < lines.length; index += 1) {
7600
- const line = lines[index] ?? "";
7601
- if (/^[^\s#]/.test(line)) {
7602
- sectionEnd = index;
7603
- break;
7604
- }
7605
- }
7606
- const firstEntry = lines.slice(mcpIndex + 1, sectionEnd).find((line) => /^\s+\S/.test(line) && !/^\s*#/.test(line));
7607
- const indent = firstEntry?.match(/^(\s+)/)?.[1]?.length;
7608
- if (indent !== void 0) childIndent = indent;
7609
- const existingPattern = new RegExp(`^\\s{${childIndent}}["']?${quotedServerName}["']?\\s*:`);
7610
- if (lines.slice(mcpIndex + 1, sectionEnd).some((line) => existingPattern.test(line))) {
7611
- throw new CliInputError(`MCP server ${serverName} already exists in the current config`);
7612
- }
7586
+ const doc = parseDocument(config2.source ?? "");
7587
+ if (doc.errors.length > 0) {
7588
+ throw new CliInputError("the current agent config contains invalid YAML");
7589
+ }
7590
+ if (doc.contents !== null && !isMap(doc.contents)) {
7591
+ throw new CliInputError("the current agent config must be an object");
7592
+ }
7593
+ const mcp = doc.get("mcp", true);
7594
+ const emptyMcp = isScalar2(mcp) && mcp.value === null;
7595
+ if (mcp !== void 0 && !emptyMcp && !isMap(mcp)) {
7596
+ throw new CliInputError("the current agent config has an invalid mcp section");
7597
+ }
7598
+ if (isMap(mcp) && doc.hasIn(["mcp", serverName])) {
7599
+ throw new CliInputError(`MCP server ${serverName} already exists in the current config`);
7613
7600
  }
7614
7601
  return {
7615
7602
  currentConfigId: config2.id,
7616
7603
  render(secretId) {
7617
- if (mcpIndex === -1) {
7618
- const base = source.trimEnd();
7619
- const separator = base === "" ? "" : "\n\n";
7620
- return {
7621
- source: `${base}${separator}mcp:
7622
- ${entry(2, secretId).join("\n")}
7623
- `,
7624
- sourceFormat: "yaml"
7625
- };
7626
- }
7627
- const updated = [...lines];
7628
- if (/^\{\s*\}/.test((updated[mcpIndex] ?? "").replace(/^mcp\s*:\s*/, ""))) {
7629
- updated[mcpIndex] = "mcp:";
7604
+ if (isMap(mcp)) {
7605
+ doc.setIn(["mcp", serverName], serverEntry(mcpUrl, secretId));
7606
+ } else {
7607
+ doc.set("mcp", { [serverName]: serverEntry(mcpUrl, secretId) });
7630
7608
  }
7631
- updated.splice(sectionEnd, 0, ...entry(childIndent, secretId));
7632
- return { source: `${updated.join("\n").trimEnd()}
7633
- `, sourceFormat: "yaml" };
7609
+ return { source: doc.toString(), sourceFormat: "yaml" };
7634
7610
  }
7635
7611
  };
7636
7612
  }
@@ -7655,19 +7631,7 @@ async function loadConfig2(client2, scope, target, targetId) {
7655
7631
  if (configId === void 0) throw new CliInputError("the agent has no current config");
7656
7632
  return (await sdk_gen_exports.getAgentConfig({ client: client2, path: { ...path, agentConfigID: configId } })).data;
7657
7633
  }
7658
- function oauthRequest(body, projectId) {
7659
- return {
7660
- owner: { kind: "project", project_id: projectId },
7661
- name: body.secret_name ?? `${body.server_name}-mcp`,
7662
- mcp_url: body.mcp_url,
7663
- ...body.return_to !== void 0 ? { return_to: body.return_to } : {},
7664
- ...body.client_id !== void 0 ? { client_id: body.client_id } : {},
7665
- ...body.client_secret !== void 0 ? { client_secret: body.client_secret } : {},
7666
- ...body.scopes !== void 0 ? { scopes: body.scopes } : {},
7667
- ...body.metadata !== void 0 ? { metadata: body.metadata } : {}
7668
- };
7669
- }
7670
- async function applyConfig(client2, scope, target, targetId, prepared, secret) {
7634
+ async function applyConfig(client2, scope, target, targetId, prepared, secret, report) {
7671
7635
  const rendered = prepared.render(secret.id);
7672
7636
  const path = { orgID: scope.orgId, projectID: scope.projectId };
7673
7637
  if (target === "agent") {
@@ -7687,11 +7651,16 @@ async function applyConfig(client2, scope, target, targetId, prepared, secret) {
7687
7651
  path,
7688
7652
  body: { source: rendered.source, source_format: rendered.sourceFormat }
7689
7653
  });
7690
- await sdk_gen_exports.updateAgentProfile({
7691
- client: client2,
7692
- path: { ...path, agentProfileID: targetId },
7693
- body: { config: config2.id, expected_current_config_id: prepared.currentConfigId }
7694
- });
7654
+ try {
7655
+ await sdk_gen_exports.updateAgentProfile({
7656
+ client: client2,
7657
+ path: { ...path, agentProfileID: targetId },
7658
+ body: { config: config2.id, expected_current_config_id: prepared.currentConfigId }
7659
+ });
7660
+ } catch (error) {
7661
+ report.warn(`agent config ${config2.id} was created, but the agent profile was not updated`);
7662
+ throw error;
7663
+ }
7695
7664
  return config2.id;
7696
7665
  }
7697
7666
  async function runMcpAdd(context, target, targetId) {
@@ -7700,9 +7669,14 @@ async function runMcpAdd(context, target, targetId) {
7700
7669
  orgId: context.path.orgID,
7701
7670
  projectId: context.path.projectID
7702
7671
  };
7703
- const request = oauthRequest(body, scope.projectId);
7672
+ const { server_name, secret_name, browser, ...oauthFields } = body;
7673
+ const request = {
7674
+ ...oauthFields,
7675
+ owner: { kind: "project", project_id: scope.projectId },
7676
+ name: secret_name ?? `${server_name}-mcp`
7677
+ };
7704
7678
  const currentConfig = await loadConfig2(client2, scope, target, targetId);
7705
- const prepared = prepareConfig(currentConfig, body.server_name, request.mcp_url);
7679
+ const prepared = prepareConfig(currentConfig, server_name, request.mcp_url);
7706
7680
  let secret;
7707
7681
  try {
7708
7682
  secret = await authorizeMcpOAuthSecret({
@@ -7710,7 +7684,7 @@ async function runMcpAdd(context, target, targetId) {
7710
7684
  orgId: scope.orgId,
7711
7685
  request,
7712
7686
  onAuthorization(url3) {
7713
- openAuthorizationUrl(report, "Authorize this MCP server in your browser", url3, body.browser);
7687
+ openAuthorizationUrl(report, "Authorize this MCP server in your browser", url3, browser);
7714
7688
  report.start("Waiting for the MCP OAuth secret to be created");
7715
7689
  }
7716
7690
  });
@@ -7722,10 +7696,12 @@ async function runMcpAdd(context, target, targetId) {
7722
7696
  report.start(`Updating the ${targetLabel(target)} config`);
7723
7697
  let configId;
7724
7698
  try {
7725
- configId = await applyConfig(client2, scope, target, targetId, prepared, secret);
7699
+ configId = await applyConfig(client2, scope, target, targetId, prepared, secret, report);
7726
7700
  } catch (error) {
7727
7701
  report.fail("Config update failed");
7728
- report.warn(`OAuth secret ${secret.id} was created, but the config was not updated`);
7702
+ report.warn(
7703
+ `OAuth secret ${secret.id} was created, but the ${targetLabel(target)} was not updated`
7704
+ );
7729
7705
  throw error;
7730
7706
  }
7731
7707
  report.stop("Config updated");
@@ -7790,7 +7766,6 @@ async function loadSkillArchive(archivePath) {
7790
7766
 
7791
7767
  // src/slack-integration.ts
7792
7768
  import * as z10 from "zod";
7793
- var POLL_INTERVAL_SECONDS2 = 2;
7794
7769
  var zSlackBody = z10.object({
7795
7770
  app_name: z10.string().optional().describe("name for a new Slack app"),
7796
7771
  app_configuration_token: z10.string().optional().describe("Slack app configuration token for a new app"),
@@ -7856,88 +7831,54 @@ Pass --app-name and --app-configuration-token, or existing app credentials.`
7856
7831
  }
7857
7832
  return { kind: "create-app", body: result.data };
7858
7833
  }
7859
- async function profileSlackIntegrations(client2, orgId, projectId, profileId) {
7860
- const { data } = await sdk_gen_exports.listIntegrationInstalls({
7861
- client: client2,
7862
- path: { orgID: orgId, projectID: projectId },
7863
- query: { agent_profile_id: profileId, limit: 100 }
7864
- });
7865
- return data.data.filter(
7866
- (install) => install.provider === "slack" && install.agent_profile_id === profileId
7867
- );
7868
- }
7869
- async function snapshotIntegrations(client2, orgId, projectId, profileId) {
7870
- return new Map(
7871
- (await profileSlackIntegrations(client2, orgId, projectId, profileId)).map((install) => [
7872
- install.id,
7873
- { state: install.state, updatedAt: install.updated_at }
7874
- ])
7875
- );
7876
- }
7877
- function changedIntegration(integrations, before, expectedProviderAccountRef) {
7878
- return integrations.find((integration) => {
7879
- if (expectedProviderAccountRef !== void 0 && integration.provider_account_ref !== expectedProviderAccountRef) {
7880
- return false;
7881
- }
7882
- const previous = before.get(integration.id);
7883
- if (previous === void 0) return true;
7884
- return integration.state !== previous.state || integration.updated_at !== previous.updatedAt;
7885
- });
7886
- }
7887
- function defaultSleep3(seconds) {
7888
- return new Promise((resolve2) => setTimeout(resolve2, seconds * 1e3));
7889
- }
7890
- async function pollForSlackIntegration(options) {
7891
- const sleep = options.sleep ?? defaultSleep3;
7892
- const now = options.now ?? Date.now;
7893
- const deadline = Date.parse(options.expiresAt);
7894
- while (now() < deadline) {
7895
- const created = changedIntegration(
7896
- await profileSlackIntegrations(
7897
- options.client,
7898
- options.orgId,
7899
- options.projectId,
7900
- options.profileId
7901
- ),
7902
- options.before,
7903
- options.expectedProviderAccountRef
7904
- );
7905
- if (created !== void 0) return created;
7906
- await sleep(Math.min(POLL_INTERVAL_SECONDS2, Math.max(0, (deadline - now()) / 1e3)));
7907
- }
7908
- throw new CliInputError("Slack authorization expired before the integration was created");
7909
- }
7910
7834
  async function runSlackIntegration(context) {
7911
7835
  const { client: client2, path, body, report } = context;
7912
7836
  const request = parseRequest(body);
7913
- const before = await snapshotIntegrations(client2, path.orgID, path.projectID, path.agentProfileID);
7914
7837
  const setupPath = {
7915
7838
  orgID: path.orgID,
7916
7839
  projectID: path.projectID,
7917
7840
  agentProfileID: path.agentProfileID
7918
7841
  };
7919
- const start = request.kind === "create-app" ? await sdk_gen_exports.createSlackSetup({ client: client2, path: setupPath, body: request.body }) : await sdk_gen_exports.createIntegrationOAuthSetup({ client: client2, path: setupPath, body: request.body });
7920
- const providerAccountRef = "slack_app_id" in start.data ? start.data.slack_app_id : void 0;
7842
+ let start;
7843
+ let slackAppId;
7844
+ if (request.kind === "create-app") {
7845
+ const { data } = await sdk_gen_exports.createSlackSetup({ client: client2, path: setupPath, body: request.body });
7846
+ start = data;
7847
+ slackAppId = data.slack_app_id;
7848
+ } else {
7849
+ const { data } = await sdk_gen_exports.createIntegrationOAuthSetup({
7850
+ client: client2,
7851
+ path: setupPath,
7852
+ body: request.body
7853
+ });
7854
+ start = data;
7855
+ }
7921
7856
  openAuthorizationUrl(
7922
7857
  report,
7923
7858
  "Approve the Slack integration in your browser",
7924
- start.data.oauth_url,
7859
+ start.oauth_url,
7925
7860
  body.browser
7926
7861
  );
7927
7862
  report.start("Waiting for the Slack integration to be created");
7928
7863
  let integration;
7929
7864
  try {
7930
- integration = await pollForSlackIntegration({
7931
- client: client2,
7932
- orgId: path.orgID,
7933
- projectId: path.projectID,
7934
- profileId: path.agentProfileID,
7935
- before,
7936
- expiresAt: start.data.expires_at,
7937
- ...typeof providerAccountRef === "string" ? { expectedProviderAccountRef: providerAccountRef } : {}
7865
+ integration = await pollUntilDeadline({
7866
+ expiresAt: start.expires_at,
7867
+ expiredMessage: "Slack authorization expired before the integration was created",
7868
+ async fetchOnce() {
7869
+ const { data } = await sdk_gen_exports.listIntegrationInstalls({
7870
+ client: client2,
7871
+ path: { orgID: path.orgID, projectID: path.projectID },
7872
+ query: { oauth_flow_id: start.flow_id, limit: 1 }
7873
+ });
7874
+ return data.data[0];
7875
+ }
7938
7876
  });
7939
7877
  } catch (error) {
7940
7878
  report.fail("Slack authorization failed");
7879
+ if (slackAppId !== void 0) {
7880
+ report.warn(`Slack app ${slackAppId} was created, but the integration was not completed`);
7881
+ }
7941
7882
  throw error;
7942
7883
  }
7943
7884
  report.stop("Slack integration created");
@@ -8625,6 +8566,213 @@ var commandGroups = [
8625
8566
  path: zDeleteCronTriggerPath
8626
8567
  })
8627
8568
  ]
8569
+ },
8570
+ {
8571
+ name: "grant",
8572
+ aliases: ["grants"],
8573
+ summary: "Manage resource grants to projects",
8574
+ groups: [
8575
+ {
8576
+ name: "skills",
8577
+ aliases: ["skill"],
8578
+ summary: "Manage skill grants",
8579
+ operations: [
8580
+ op({
8581
+ verb: "list",
8582
+ summary: "List a skill's project grants",
8583
+ fn: sdk_gen_exports.listSkillGrants,
8584
+ format: (response) => formatTable(["id", "target_project", "target_project_id", "created_at"])({
8585
+ data: response.data.map((item) => ({
8586
+ ...item.grant,
8587
+ target_project: item.target_project.name
8588
+ })),
8589
+ next_cursor: response.next_cursor
8590
+ }),
8591
+ path: zListSkillGrantsPath,
8592
+ query: zListSkillGrantsQuery
8593
+ }),
8594
+ op({
8595
+ verb: "add",
8596
+ summary: "Grant a skill to a project",
8597
+ fn: sdk_gen_exports.createSkillGrant,
8598
+ format: formatRecord(),
8599
+ path: zCreateSkillGrantPath,
8600
+ body: zCreateSkillGrantBody
8601
+ }),
8602
+ op({
8603
+ verb: "delete",
8604
+ summary: "Remove a skill grant",
8605
+ fn: sdk_gen_exports.deleteSkillGrant,
8606
+ format: formatVoid("deleted"),
8607
+ path: zDeleteSkillGrantPath
8608
+ })
8609
+ ]
8610
+ },
8611
+ {
8612
+ name: "secrets",
8613
+ aliases: ["secret"],
8614
+ summary: "Manage secret grants",
8615
+ operations: [
8616
+ op({
8617
+ verb: "list",
8618
+ summary: "List a secret's project grants",
8619
+ fn: sdk_gen_exports.listSecretGrants,
8620
+ format: (response) => formatTable(["id", "target_project", "target_project_id", "created_at"])({
8621
+ data: response.data.map((item) => ({
8622
+ ...item.grant,
8623
+ target_project: item.target_project.name
8624
+ })),
8625
+ next_cursor: response.next_cursor
8626
+ }),
8627
+ path: zListSecretGrantsPath,
8628
+ query: zListSecretGrantsQuery
8629
+ }),
8630
+ op({
8631
+ verb: "add",
8632
+ summary: "Grant a secret to a project",
8633
+ fn: sdk_gen_exports.createSecretGrant,
8634
+ format: formatRecord(),
8635
+ path: zCreateSecretGrantPath,
8636
+ body: zCreateSecretGrantBody
8637
+ }),
8638
+ op({
8639
+ verb: "delete",
8640
+ summary: "Remove a secret grant",
8641
+ fn: sdk_gen_exports.deleteSecretGrant,
8642
+ format: formatVoid("deleted"),
8643
+ path: zDeleteSecretGrantPath
8644
+ })
8645
+ ]
8646
+ },
8647
+ {
8648
+ name: "machines",
8649
+ aliases: ["machine"],
8650
+ summary: "Manage machine grants in a project",
8651
+ operations: [
8652
+ op({
8653
+ verb: "list",
8654
+ summary: "List a project's explicit machine grants",
8655
+ fn: sdk_gen_exports.listProjectMachineGrants,
8656
+ format: (response) => formatTable(["id", "machine", "machine_id", "source_kind", "created_at"])({
8657
+ data: response.data.map((item) => ({
8658
+ ...item.grant,
8659
+ machine: item.machine.display_name
8660
+ })),
8661
+ next_cursor: response.next_cursor
8662
+ }),
8663
+ path: zListProjectMachineGrantsPath,
8664
+ query: zListProjectMachineGrantsQuery
8665
+ }),
8666
+ op({
8667
+ verb: "add",
8668
+ summary: "Grant a machine to a project",
8669
+ fn: sdk_gen_exports.createProjectMachineGrant,
8670
+ format: (response) => formatRecord()(response.grant),
8671
+ path: zCreateProjectMachineGrantPath,
8672
+ body: zCreateProjectMachineGrantBody
8673
+ }),
8674
+ op({
8675
+ verb: "delete",
8676
+ summary: "Remove a machine grant",
8677
+ fn: sdk_gen_exports.deleteProjectMachineGrant,
8678
+ format: formatVoid("deleted"),
8679
+ path: zDeleteProjectMachineGrantPath
8680
+ })
8681
+ ]
8682
+ },
8683
+ {
8684
+ name: "pools",
8685
+ aliases: ["pool"],
8686
+ summary: "Manage machine pool grants in a project",
8687
+ operations: [
8688
+ op({
8689
+ verb: "list",
8690
+ summary: "List a project's machine pool grants",
8691
+ fn: sdk_gen_exports.listProjectMachinePoolGrants,
8692
+ format: (response) => formatTable(["id", "machine_pool", "machine_pool_id", "description", "created_at"])({
8693
+ data: response.data.map((item) => ({
8694
+ ...item.grant,
8695
+ machine_pool: item.machine_pool.name
8696
+ })),
8697
+ next_cursor: response.next_cursor
8698
+ }),
8699
+ path: zListProjectMachinePoolGrantsPath,
8700
+ query: zListProjectMachinePoolGrantsQuery
8701
+ }),
8702
+ op({
8703
+ verb: "get",
8704
+ summary: "Fetch a machine pool grant",
8705
+ fn: sdk_gen_exports.getProjectMachinePoolGrant,
8706
+ format: formatRecord(),
8707
+ path: zGetProjectMachinePoolGrantPath
8708
+ }),
8709
+ op({
8710
+ verb: "add",
8711
+ summary: "Grant a machine pool to a project",
8712
+ fn: sdk_gen_exports.createProjectMachinePoolGrant,
8713
+ format: formatRecord(),
8714
+ path: zCreateProjectMachinePoolGrantPath,
8715
+ body: zCreateProjectMachinePoolGrantBody
8716
+ }),
8717
+ op({
8718
+ verb: "edit",
8719
+ summary: "Edit a machine pool grant's overrides",
8720
+ fn: sdk_gen_exports.updateProjectMachinePoolGrant,
8721
+ format: formatRecord(),
8722
+ path: zUpdateProjectMachinePoolGrantPath,
8723
+ body: zUpdateProjectMachinePoolGrantBody
8724
+ }),
8725
+ op({
8726
+ verb: "delete",
8727
+ summary: "Remove a machine pool grant",
8728
+ fn: sdk_gen_exports.deleteProjectMachinePoolGrant,
8729
+ format: formatVoid("deleted"),
8730
+ path: zDeleteProjectMachinePoolGrantPath
8731
+ })
8732
+ ]
8733
+ },
8734
+ {
8735
+ name: "models",
8736
+ aliases: ["model"],
8737
+ summary: "Manage model grants in a project",
8738
+ operations: [
8739
+ op({
8740
+ verb: "list",
8741
+ summary: "List a project's model grants",
8742
+ fn: sdk_gen_exports.listProjectModelGrants,
8743
+ format: (response) => formatTable(["id", "model", "configured_model_id", "created_at"])({
8744
+ data: response.data.map((item) => ({ ...item.grant, model: item.model.name })),
8745
+ next_cursor: response.next_cursor
8746
+ }),
8747
+ path: zListProjectModelGrantsPath,
8748
+ query: zListProjectModelGrantsQuery
8749
+ }),
8750
+ op({
8751
+ verb: "add",
8752
+ summary: "Grant a configured model to a project",
8753
+ fn: sdk_gen_exports.createProjectModelGrant,
8754
+ format: (response) => formatRecord()(response.grant),
8755
+ path: zCreateProjectModelGrantPath,
8756
+ body: zCreateProjectModelGrantBody
8757
+ }),
8758
+ op({
8759
+ verb: "edit",
8760
+ summary: "Edit a model grant's overrides",
8761
+ fn: sdk_gen_exports.updateProjectModelGrant,
8762
+ format: (response) => formatRecord()(response.grant),
8763
+ path: zUpdateProjectModelGrantPath,
8764
+ body: zUpdateProjectModelGrantBody
8765
+ }),
8766
+ op({
8767
+ verb: "delete",
8768
+ summary: "Remove a model grant",
8769
+ fn: sdk_gen_exports.deleteProjectModelGrant,
8770
+ format: formatVoid("deleted"),
8771
+ path: zDeleteProjectModelGrantPath
8772
+ })
8773
+ ]
8774
+ }
8775
+ ]
8628
8776
  }
8629
8777
  ];
8630
8778
  var topLevelOperations = [
package/package.json CHANGED
@@ -1,9 +1,17 @@
1
1
  {
2
2
  "name": "omnara",
3
- "version": "1.0.0-beta.1",
3
+ "version": "1.0.0",
4
+ "description": "Command-line interface for Omnara: launch and manage agents, organizations, integrations, and MCP servers",
5
+ "license": "Apache-2.0",
6
+ "homepage": "https://omnara.com",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/omnara-ai/omnara.git",
10
+ "directory": "frontend/packages/cli"
11
+ },
4
12
  "type": "module",
5
13
  "engines": {
6
- "node": ">=20"
14
+ "node": ">=22"
7
15
  },
8
16
  "bin": {
9
17
  "omnara": "./dist/bin.js"
@@ -15,6 +23,7 @@
15
23
  "@clack/prompts": "^1.7.0",
16
24
  "commander": "^14.0.0",
17
25
  "tar": "^7.5.22",
26
+ "yaml": "^2.9.0",
18
27
  "zod": "^4.4.3"
19
28
  },
20
29
  "devDependencies": {
@@ -26,7 +35,7 @@
26
35
  "@omnara/sdk": "0.0.0"
27
36
  },
28
37
  "scripts": {
29
- "build": "esbuild src/bin.ts --bundle --platform=node --format=esm --target=node20 --outfile=dist/bin.js --banner:js='#!/usr/bin/env node' --external:@clack/prompts --external:commander --external:tar --external:zod",
38
+ "build": "esbuild src/bin.ts --bundle --platform=node --format=esm --target=node22 --outfile=dist/bin.js --banner:js='#!/usr/bin/env node' --external:@clack/prompts --external:commander --external:tar --external:yaml --external:zod",
30
39
  "omnara": "tsx src/bin.ts",
31
40
  "test": "vitest run",
32
41
  "typecheck": "tsc --noEmit -p tsconfig.json"