@overaihq/cli 0.15.0 → 0.16.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 (2) hide show
  1. package/dist/index.js +1468 -141
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -1591,7 +1591,7 @@ var require_xpath = __commonJS({
1591
1591
  function toString2(x) {
1592
1592
  return x.toString();
1593
1593
  }
1594
- var join8 = function(s, xs) {
1594
+ var join9 = function(s, xs) {
1595
1595
  return xs.join(s);
1596
1596
  };
1597
1597
  var wrap = function(pref, suf, str) {
@@ -3337,7 +3337,7 @@ var require_xpath = __commonJS({
3337
3337
  return wrap("[", "]", predicate.toString());
3338
3338
  };
3339
3339
  PathExpr.predicatesString = function(predicates) {
3340
- return join8(
3340
+ return join9(
3341
3341
  "",
3342
3342
  map3(PathExpr.predicateString, predicates)
3343
3343
  );
@@ -4692,7 +4692,7 @@ var require_xpath = __commonJS({
4692
4692
  }
4693
4693
  return acc;
4694
4694
  }, {}, from);
4695
- var t = join8(
4695
+ var t = join9(
4696
4696
  "",
4697
4697
  map3(function(ch) {
4698
4698
  return ch in cMap ? cMap[ch] : ch;
@@ -13529,7 +13529,7 @@ var require_jsonata = __commonJS({
13529
13529
  }
13530
13530
  return result;
13531
13531
  }
13532
- function join8(strs, separator) {
13532
+ function join9(strs, separator) {
13533
13533
  if (typeof strs === "undefined") {
13534
13534
  return void 0;
13535
13535
  }
@@ -14460,7 +14460,7 @@ var require_jsonata = __commonJS({
14460
14460
  contains: contains2,
14461
14461
  replace: replace2,
14462
14462
  split: split2,
14463
- join: join8,
14463
+ join: join9,
14464
14464
  formatNumber,
14465
14465
  formatBase,
14466
14466
  number: number4,
@@ -38122,6 +38122,1082 @@ var automationRunState = async (client, runId) => {
38122
38122
  if (data.automationRun == null) throw new Error(`Run ${runId} not found.`);
38123
38123
  return data.automationRun.state ?? {};
38124
38124
  };
38125
+ var automationRun = async (client, runId, options) => {
38126
+ const data = await client.graphql(
38127
+ `query SdkAutomationRun($id: String!) {
38128
+ automationRun(id: $id) {
38129
+ id startedAt endedAt errored isCancelled manuallyTriggered version { num }
38130
+ }
38131
+ }`,
38132
+ { id: runId },
38133
+ options
38134
+ );
38135
+ const run2 = data.automationRun;
38136
+ if (run2 == null)
38137
+ throw new Error(
38138
+ `Run ${runId} not found or not accessible in this organization.`
38139
+ );
38140
+ return {
38141
+ id: run2.id,
38142
+ version: run2.version?.num,
38143
+ startedAt: run2.startedAt,
38144
+ endedAt: run2.endedAt,
38145
+ status: run2.errored ? "errored" : run2.isCancelled ? "cancelled" : run2.endedAt == null ? "running" : "ok",
38146
+ manual: run2.manuallyTriggered
38147
+ };
38148
+ };
38149
+
38150
+ // ../common/src/llm/models.ts
38151
+ var DEFAULT_MODEL = "claude-sonnet-5-5";
38152
+ var DEFAULT_OPENAI_MODEL = "gpt-6.1-sol";
38153
+ var LARGE_SCHEMA_MODELS = [
38154
+ DEFAULT_OPENAI_MODEL,
38155
+ "gemini-4-argon"
38156
+ ];
38157
+ var stripAnthropicThinking = (opts) => {
38158
+ const anthropicOpts = opts?.anthropic;
38159
+ if (!opts || !anthropicOpts || !("thinking" in anthropicOpts)) return opts;
38160
+ const { thinking: _omit, ...rest } = anthropicOpts;
38161
+ return { ...opts, anthropic: rest };
38162
+ };
38163
+ var bothVertexRegions = (route) => [
38164
+ { ...route, subprocessor: "vertex-global" },
38165
+ { ...route, subprocessor: "vertex-us" }
38166
+ ];
38167
+ var HAIKU_5_5_PRICING = {
38168
+ input: 0.1,
38169
+ output: 0.5,
38170
+ cacheRead: 0.01,
38171
+ cacheWrite: 0.125,
38172
+ longContext: {
38173
+ inputTokenThreshold: 1e5,
38174
+ input: 0.5,
38175
+ output: 2.5,
38176
+ cacheRead: 0.05,
38177
+ cacheWrite: 0.625
38178
+ }
38179
+ };
38180
+ var GEMINI_FLASH_PROMO_PRICING = {
38181
+ input: 0.75,
38182
+ output: 3.75,
38183
+ cacheRead: 0.075,
38184
+ cacheWrite: 0.75
38185
+ };
38186
+ var SONNET_5_5_PRICING_PERIODS = [
38187
+ {
38188
+ from: "2026-09-28",
38189
+ through: "2026-10-06",
38190
+ pricing: { input: 2, output: 10, cacheRead: 0.2, cacheWrite: 2.5 }
38191
+ }
38192
+ ];
38193
+ var MODEL_ROUTES = [
38194
+ // Anthropic. Pricing cacheWrite = 1.25x input — the 5-minute-TTL write rate,
38195
+ // which is what applyAnthropicCacheBreakpoints buys (1h TTL would bill 2x).
38196
+ // The 5.5 generation: 1M context, 128K output, vision and tool use.
38197
+ // Current architect default (see DEFAULT_MODEL).
38198
+ {
38199
+ model: "claude-sonnet-5-5",
38200
+ id: "claude-sonnet-5-5",
38201
+ name: "Claude Sonnet 5.5",
38202
+ subprocessor: "anthropic",
38203
+ provider: "anthropic",
38204
+ active: true,
38205
+ pricing: { input: 2, output: 10, cacheRead: 0.1, cacheWrite: 2.5 },
38206
+ pricingPeriods: SONNET_5_5_PRICING_PERIODS
38207
+ },
38208
+ {
38209
+ model: "claude-opus-5-5",
38210
+ id: "claude-opus-5-5",
38211
+ name: "Claude Opus 5.5",
38212
+ subprocessor: "anthropic",
38213
+ provider: "anthropic",
38214
+ active: true,
38215
+ pricing: { input: 4, output: 20, cacheRead: 0.2, cacheWrite: 5 }
38216
+ },
38217
+ {
38218
+ model: "claude-fable-5-1",
38219
+ id: "claude-fable-5-1",
38220
+ name: "Claude Fable 5.1",
38221
+ subprocessor: "anthropic",
38222
+ provider: "anthropic",
38223
+ active: true,
38224
+ pricing: { input: 10, output: 50, cacheRead: 0.25, cacheWrite: 12.5 },
38225
+ providerOptionsTransform: stripAnthropicThinking
38226
+ },
38227
+ {
38228
+ model: "claude-haiku-5-5",
38229
+ id: "claude-haiku-5-5",
38230
+ name: "Claude Haiku 5.5",
38231
+ subprocessor: "anthropic",
38232
+ provider: "anthropic",
38233
+ active: true,
38234
+ pricing: HAIKU_5_5_PRICING
38235
+ },
38236
+ {
38237
+ model: "claude-sonnet-4-6",
38238
+ id: "claude-sonnet-4-6",
38239
+ name: "Claude Sonnet 4.6",
38240
+ subprocessor: "anthropic",
38241
+ provider: "anthropic",
38242
+ active: true,
38243
+ pricing: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 }
38244
+ },
38245
+ // Superseded by Sonnet 5.5: same input and output price, half the cache-read
38246
+ // rate. Launched 2026-06-30 at a $2/$10 launch rate that Anthropic made
38247
+ // permanent on 2026-08-10, so the announced $3/$15 was never billed and the
38248
+ // row needs no pricing window.
38249
+ {
38250
+ model: "claude-sonnet-5",
38251
+ id: "claude-sonnet-5",
38252
+ name: "Claude Sonnet 5",
38253
+ subprocessor: "anthropic",
38254
+ provider: "anthropic",
38255
+ active: false,
38256
+ pricing: { input: 2, output: 10, cacheRead: 0.2, cacheWrite: 2.5 }
38257
+ },
38258
+ // Superseded by Opus 5.5 (cheaper, newer).
38259
+ {
38260
+ model: "claude-opus-5",
38261
+ id: "claude-opus-5",
38262
+ name: "Claude Opus 5",
38263
+ subprocessor: "anthropic",
38264
+ provider: "anthropic",
38265
+ active: false,
38266
+ pricing: { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 }
38267
+ },
38268
+ // Superseded by Fable 5.1 (same price).
38269
+ {
38270
+ model: "claude-fable-5",
38271
+ id: "claude-fable-5",
38272
+ name: "Claude Fable 5",
38273
+ subprocessor: "anthropic",
38274
+ provider: "anthropic",
38275
+ active: false,
38276
+ pricing: { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 },
38277
+ providerOptionsTransform: stripAnthropicThinking
38278
+ },
38279
+ // Superseded by Haiku 5.5 at a tenth of the price. Its "not sooner than
38280
+ // 2026-10-15" retirement date has passed with no deprecation notice yet.
38281
+ {
38282
+ model: "claude-haiku-4-5",
38283
+ id: "claude-haiku-4-5",
38284
+ name: "Claude Haiku 4.5",
38285
+ subprocessor: "anthropic",
38286
+ provider: "anthropic",
38287
+ active: false,
38288
+ pricing: { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 }
38289
+ },
38290
+ // Superseded by Opus 5 (same price, newer); kept addressable for persisted
38291
+ // automations/agents but dropped from the dropdown.
38292
+ {
38293
+ model: "claude-opus-4-8",
38294
+ id: "claude-opus-4-8",
38295
+ name: "Claude Opus 4.8",
38296
+ subprocessor: "anthropic",
38297
+ provider: "anthropic",
38298
+ active: false,
38299
+ pricing: { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 }
38300
+ },
38301
+ // Opus 4.7 sat between 4.6 and 4.8; kept addressable for any persisted
38302
+ // automations but not surfaced in the dropdown now that 4.8 is out.
38303
+ {
38304
+ model: "claude-opus-4-7",
38305
+ id: "claude-opus-4-7",
38306
+ name: "Claude Opus 4.7",
38307
+ subprocessor: "anthropic",
38308
+ provider: "anthropic",
38309
+ active: false,
38310
+ pricing: { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 }
38311
+ },
38312
+ // $5/$25 since its 2026-02-05 launch (the $15/$75 to $5/$25 cut came with
38313
+ // Opus 4.5); keep in sync with platform.claude.com/docs/.../pricing.
38314
+ {
38315
+ model: "claude-opus-4-6",
38316
+ id: "claude-opus-4-6",
38317
+ name: "Claude Opus 4.6",
38318
+ subprocessor: "anthropic",
38319
+ provider: "anthropic",
38320
+ active: false,
38321
+ pricing: { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 }
38322
+ },
38323
+ // Deprecated 2026-09-30; retires on the direct API 2026-11-30, when
38324
+ // SUNSET_MODEL_REPLACEMENTS moves persisted names to Sonnet 5.5.
38325
+ {
38326
+ model: "claude-sonnet-4-5",
38327
+ id: "claude-sonnet-4-5",
38328
+ name: "Claude Sonnet 4.5",
38329
+ subprocessor: "anthropic",
38330
+ provider: "anthropic",
38331
+ active: false,
38332
+ pricing: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 }
38333
+ },
38334
+ // OpenAI (docs at developers.openai.com). The 6 line (Astra frontier / Sol
38335
+ // balanced / Luna efficient) supersedes 5.6; 5.6 and older stay addressable
38336
+ // for persisted configs but drop from the dropdown. From 5.6 on, cacheRead =
38337
+ // 10% of input (6.1 Sol: 5%) and cacheWrite = 1.25x input for the default
38338
+ // 30-minute cache; older models bill no cache write, so their cacheWrite
38339
+ // equals input. Above 272K input tokens OpenAI bills the full request at 2x
38340
+ // input and cache rates and 1.5x output.
38341
+ {
38342
+ model: "gpt-6.1-sol",
38343
+ id: "gpt-6.1-sol",
38344
+ name: "GPT 6.1 Sol",
38345
+ subprocessor: "openai",
38346
+ provider: "openai",
38347
+ active: true,
38348
+ pricing: {
38349
+ input: 2,
38350
+ output: 10,
38351
+ cacheRead: 0.1,
38352
+ cacheWrite: 2.5,
38353
+ longContext: {
38354
+ inputTokenThreshold: 272e3,
38355
+ input: 4,
38356
+ output: 15,
38357
+ cacheRead: 0.2,
38358
+ cacheWrite: 5
38359
+ }
38360
+ }
38361
+ },
38362
+ {
38363
+ model: "gpt-6-luna",
38364
+ id: "gpt-6-luna",
38365
+ name: "GPT 6 Luna",
38366
+ subprocessor: "openai",
38367
+ provider: "openai",
38368
+ active: true,
38369
+ pricing: {
38370
+ input: 0.1,
38371
+ output: 0.5,
38372
+ cacheRead: 0.01,
38373
+ cacheWrite: 0.125,
38374
+ longContext: {
38375
+ inputTokenThreshold: 272e3,
38376
+ input: 0.2,
38377
+ output: 0.75,
38378
+ cacheRead: 0.02,
38379
+ cacheWrite: 0.25
38380
+ }
38381
+ }
38382
+ },
38383
+ {
38384
+ model: "gpt-6-astra",
38385
+ id: "gpt-6-astra",
38386
+ name: "GPT 6 Astra",
38387
+ subprocessor: "openai",
38388
+ provider: "openai",
38389
+ active: true,
38390
+ pricing: {
38391
+ input: 10,
38392
+ output: 50,
38393
+ cacheRead: 1,
38394
+ cacheWrite: 12.5,
38395
+ longContext: {
38396
+ inputTokenThreshold: 272e3,
38397
+ input: 20,
38398
+ output: 75,
38399
+ cacheRead: 2,
38400
+ cacheWrite: 25
38401
+ }
38402
+ }
38403
+ },
38404
+ // Superseded a week later by 6.1 Sol at the same price with half the
38405
+ // cache-read rate. Chat Completions serves it only with
38406
+ // `reasoning_effort: none`.
38407
+ {
38408
+ model: "gpt-6-sol",
38409
+ id: "gpt-6-sol",
38410
+ name: "GPT 6 Sol",
38411
+ subprocessor: "openai",
38412
+ provider: "openai",
38413
+ active: false,
38414
+ pricing: {
38415
+ input: 2,
38416
+ output: 10,
38417
+ cacheRead: 0.2,
38418
+ cacheWrite: 2.5,
38419
+ longContext: {
38420
+ inputTokenThreshold: 272e3,
38421
+ input: 4,
38422
+ output: 15,
38423
+ cacheRead: 0.4,
38424
+ cacheWrite: 5
38425
+ }
38426
+ }
38427
+ },
38428
+ // The 5.6 line launched 2026-07-09. OpenAI cut Terra 20% and Luna 80% on
38429
+ // 2026-07-30; each row's window holds the launch rate for usage before the cut.
38430
+ {
38431
+ model: "gpt-5.6-terra",
38432
+ id: "gpt-5.6-terra",
38433
+ name: "GPT 5.6 Terra",
38434
+ subprocessor: "openai",
38435
+ provider: "openai",
38436
+ active: false,
38437
+ pricing: {
38438
+ input: 2,
38439
+ output: 12,
38440
+ cacheRead: 0.2,
38441
+ cacheWrite: 2.5,
38442
+ longContext: {
38443
+ inputTokenThreshold: 272e3,
38444
+ input: 4,
38445
+ output: 18,
38446
+ cacheRead: 0.4,
38447
+ cacheWrite: 5
38448
+ }
38449
+ },
38450
+ pricingPeriods: [
38451
+ {
38452
+ from: "2026-07-09",
38453
+ through: "2026-07-29",
38454
+ pricing: {
38455
+ input: 2.5,
38456
+ output: 15,
38457
+ cacheRead: 0.25,
38458
+ cacheWrite: 3.125,
38459
+ longContext: {
38460
+ inputTokenThreshold: 272e3,
38461
+ input: 5,
38462
+ output: 22.5,
38463
+ cacheRead: 0.5,
38464
+ cacheWrite: 6.25
38465
+ }
38466
+ }
38467
+ }
38468
+ ]
38469
+ },
38470
+ // Sol's $4/$20 promo runs through 2026-11-29; standard rates resume November 30.
38471
+ {
38472
+ model: "gpt-5.6-sol",
38473
+ id: "gpt-5.6-sol",
38474
+ name: "GPT 5.6 Sol",
38475
+ subprocessor: "openai",
38476
+ provider: "openai",
38477
+ active: false,
38478
+ pricing: {
38479
+ input: 5,
38480
+ output: 30,
38481
+ cacheRead: 0.5,
38482
+ cacheWrite: 6.25,
38483
+ longContext: {
38484
+ inputTokenThreshold: 272e3,
38485
+ input: 10,
38486
+ output: 45,
38487
+ cacheRead: 1,
38488
+ cacheWrite: 12.5
38489
+ }
38490
+ },
38491
+ pricingPeriods: [
38492
+ {
38493
+ from: "2026-08-21",
38494
+ through: "2026-11-29",
38495
+ pricing: {
38496
+ input: 4,
38497
+ output: 20,
38498
+ cacheRead: 0.4,
38499
+ cacheWrite: 5,
38500
+ longContext: {
38501
+ inputTokenThreshold: 272e3,
38502
+ input: 8,
38503
+ output: 30,
38504
+ cacheRead: 0.8,
38505
+ cacheWrite: 10
38506
+ }
38507
+ }
38508
+ }
38509
+ ]
38510
+ },
38511
+ {
38512
+ model: "gpt-5.6-luna",
38513
+ id: "gpt-5.6-luna",
38514
+ name: "GPT 5.6 Luna",
38515
+ subprocessor: "openai",
38516
+ provider: "openai",
38517
+ active: false,
38518
+ pricing: {
38519
+ input: 0.2,
38520
+ output: 1.2,
38521
+ cacheRead: 0.02,
38522
+ cacheWrite: 0.25,
38523
+ longContext: {
38524
+ inputTokenThreshold: 272e3,
38525
+ input: 0.4,
38526
+ output: 1.8,
38527
+ cacheRead: 0.04,
38528
+ cacheWrite: 0.5
38529
+ }
38530
+ },
38531
+ pricingPeriods: [
38532
+ {
38533
+ from: "2026-07-09",
38534
+ through: "2026-07-29",
38535
+ pricing: {
38536
+ input: 1,
38537
+ output: 6,
38538
+ cacheRead: 0.1,
38539
+ cacheWrite: 1.25,
38540
+ longContext: {
38541
+ inputTokenThreshold: 272e3,
38542
+ input: 2,
38543
+ output: 9,
38544
+ cacheRead: 0.2,
38545
+ cacheWrite: 2.5
38546
+ }
38547
+ }
38548
+ }
38549
+ ]
38550
+ },
38551
+ {
38552
+ model: "gpt-5.4",
38553
+ id: "gpt-5.4",
38554
+ name: "GPT 5.4",
38555
+ subprocessor: "openai",
38556
+ provider: "openai",
38557
+ active: false,
38558
+ pricing: {
38559
+ input: 2.5,
38560
+ output: 15,
38561
+ cacheRead: 0.25,
38562
+ cacheWrite: 2.5,
38563
+ longContext: {
38564
+ inputTokenThreshold: 272e3,
38565
+ input: 5,
38566
+ output: 22.5,
38567
+ cacheRead: 0.5,
38568
+ cacheWrite: 5
38569
+ }
38570
+ }
38571
+ },
38572
+ // Shuts down 2027-04-01. OpenAI names gpt-6-sol as the replacement; the
38573
+ // sunset entry should point at gpt-6.1-sol.
38574
+ {
38575
+ model: "gpt-5.3-codex",
38576
+ id: "gpt-5.3-codex",
38577
+ name: "GPT 5.3 Codex",
38578
+ subprocessor: "openai",
38579
+ provider: "openai",
38580
+ active: false,
38581
+ pricing: { input: 1.75, output: 14, cacheRead: 0.175, cacheWrite: 1.75 }
38582
+ },
38583
+ {
38584
+ model: "gpt-5.4-mini",
38585
+ id: "gpt-5.4-mini",
38586
+ name: "GPT 5.4 Mini",
38587
+ subprocessor: "openai",
38588
+ provider: "openai",
38589
+ active: false,
38590
+ pricing: { input: 0.75, output: 4.5, cacheRead: 0.075, cacheWrite: 0.75 }
38591
+ },
38592
+ {
38593
+ model: "gpt-5.2",
38594
+ id: "gpt-5.2",
38595
+ name: "GPT 5.2",
38596
+ subprocessor: "openai",
38597
+ provider: "openai",
38598
+ active: false,
38599
+ pricing: { input: 1.75, output: 14, cacheRead: 0.175, cacheWrite: 1.75 }
38600
+ },
38601
+ {
38602
+ model: "gpt-5-mini",
38603
+ id: "gpt-5-mini",
38604
+ name: "GPT 5 Mini",
38605
+ subprocessor: "openai",
38606
+ provider: "openai",
38607
+ active: false,
38608
+ pricing: { input: 0.25, output: 2, cacheRead: 0.025, cacheWrite: 0.25 }
38609
+ },
38610
+ {
38611
+ model: "gpt-5",
38612
+ id: "gpt-5",
38613
+ name: "GPT 5",
38614
+ subprocessor: "openai",
38615
+ provider: "openai",
38616
+ active: false,
38617
+ pricing: { input: 1.25, output: 10, cacheRead: 0.125, cacheWrite: 1.25 }
38618
+ },
38619
+ // Shuts down 2027-01-14; OpenAI's replacement is gpt-6-luna.
38620
+ {
38621
+ model: "gpt-4.1",
38622
+ id: "gpt-4.1",
38623
+ name: "GPT 4.1",
38624
+ subprocessor: "openai",
38625
+ provider: "openai",
38626
+ active: false,
38627
+ pricing: { input: 2, output: 8, cacheRead: 0.5, cacheWrite: 2 }
38628
+ },
38629
+ // Google, native Gemini. Vertex retired the 2.5 generation on 2026-10-20;
38630
+ // those rows stay to price historical usage, and SUNSET_MODEL_REPLACEMENTS
38631
+ // rewrites persisted names onto 3.8 Flash. Pricing cacheWrite mirrors input —
38632
+ // Vertex has no separate cache-write rate. 3.8/3.7/3.6 Flash bill a half-price
38633
+ // promo through 2026-12-31 (see GEMINI_FLASH_PROMO_PRICING).
38634
+ // Current Google default (see DEFAULT_GOOGLE_MODEL); kept in the dropdown so
38635
+ // the default is a first-class selectable option, not a hidden ID.
38636
+ ...bothVertexRegions({
38637
+ model: "gemini-3.8-flash",
38638
+ id: "gemini-3.8-flash",
38639
+ name: "Gemini 3.8 Flash",
38640
+ provider: "google",
38641
+ active: true,
38642
+ pricing: { input: 1.5, output: 7.5, cacheRead: 0.15, cacheWrite: 1.5 },
38643
+ pricingPeriods: [
38644
+ {
38645
+ from: "2026-09-02",
38646
+ through: "2026-12-31",
38647
+ pricing: GEMINI_FLASH_PROMO_PRICING
38648
+ }
38649
+ ]
38650
+ }),
38651
+ // Google's Gemini 4 line, GA 2026-10-13 on Vertex from both `global` and the
38652
+ // `us` multi-region per Google's model page (not probed by direct call here).
38653
+ // The top Google model, Pro-priced, with no long-context tier. 1M context
38654
+ // shared between input and output, 64K output per request. Rejects
38655
+ // `thinking_budget` (we send none); implicit context caching only, no
38656
+ // explicit caches.
38657
+ ...bothVertexRegions({
38658
+ model: "gemini-4-argon",
38659
+ id: "gemini-4-argon",
38660
+ name: "Gemini 4 Argon",
38661
+ provider: "google",
38662
+ active: true,
38663
+ pricing: { input: 4, output: 20, cacheRead: 0.2, cacheWrite: 4 },
38664
+ pricingPeriods: [
38665
+ {
38666
+ from: "2026-10-13",
38667
+ through: "2026-11-30",
38668
+ pricing: { input: 2, output: 10, cacheRead: 0.1, cacheWrite: 2 }
38669
+ }
38670
+ ]
38671
+ }),
38672
+ // Superseded by 3.8 Flash (identical pricing, newer); kept addressable for
38673
+ // persisted automations but dropped from the dropdown. Vertex retires it
38674
+ // 2027-01-28.
38675
+ ...bothVertexRegions({
38676
+ model: "gemini-3.7-flash",
38677
+ id: "gemini-3.7-flash",
38678
+ name: "Gemini 3.7 Flash",
38679
+ provider: "google",
38680
+ active: false,
38681
+ pricing: { input: 1.5, output: 7.5, cacheRead: 0.15, cacheWrite: 1.5 },
38682
+ pricingPeriods: [
38683
+ {
38684
+ from: "2026-08-13",
38685
+ through: "2026-12-31",
38686
+ pricing: GEMINI_FLASH_PROMO_PRICING
38687
+ }
38688
+ ]
38689
+ }),
38690
+ // Still the only Pro-branded id, and still preview: Vertex publishes it under
38691
+ // the `-preview` suffix and serves it from the global endpoint only (preview
38692
+ // models are global-only, hence the single route). Switch to a GA ID without
38693
+ // the suffix if Google promotes it. Gemini 4 Argon is the top Google model.
38694
+ {
38695
+ model: "gemini-3.1-pro-preview",
38696
+ id: "gemini-3.1-pro-preview",
38697
+ name: "Gemini 3.1 Pro (Preview)",
38698
+ subprocessor: "vertex-global",
38699
+ provider: "google",
38700
+ active: true,
38701
+ pricing: {
38702
+ input: 2,
38703
+ output: 12,
38704
+ cacheRead: 0.2,
38705
+ cacheWrite: 2,
38706
+ longContext: {
38707
+ inputTokenThreshold: 2e5,
38708
+ input: 4,
38709
+ output: 18,
38710
+ cacheRead: 0.4,
38711
+ cacheWrite: 4
38712
+ }
38713
+ }
38714
+ },
38715
+ // Superseded by 3.7 Flash (identical pricing, newer); kept addressable for
38716
+ // persisted automations but dropped from the dropdown. Vertex retires it
38717
+ // 2026-11-19 (see SUNSET_MODEL_REPLACEMENTS). Google's `us` multi-region allowlist on 3.6 was lifted
38718
+ // 2026-08-18 (Gemini Enterprise release notes), and the `us` endpoint serves
38719
+ // it — verified by direct call.
38720
+ ...bothVertexRegions({
38721
+ model: "gemini-3.6-flash",
38722
+ id: "gemini-3.6-flash",
38723
+ name: "Gemini 3.6 Flash",
38724
+ provider: "google",
38725
+ active: false,
38726
+ pricing: { input: 1.5, output: 7.5, cacheRead: 0.15, cacheWrite: 1.5 },
38727
+ pricingPeriods: [
38728
+ {
38729
+ from: "2026-08-13",
38730
+ through: "2026-12-31",
38731
+ pricing: GEMINI_FLASH_PROMO_PRICING
38732
+ }
38733
+ ]
38734
+ }),
38735
+ ...bothVertexRegions({
38736
+ model: "gemini-3.5-flash-lite",
38737
+ id: "gemini-3.5-flash-lite",
38738
+ name: "Gemini 3.5 Flash-Lite",
38739
+ provider: "google",
38740
+ active: true,
38741
+ pricing: { input: 0.3, output: 2.5, cacheRead: 0.03, cacheWrite: 0.3 }
38742
+ }),
38743
+ // Superseded by 3.6 Flash (same input rate, cheaper output, ~17% fewer
38744
+ // output tokens); kept addressable for persisted automations but dropped
38745
+ // from the dropdown.
38746
+ ...bothVertexRegions({
38747
+ model: "gemini-3.5-flash",
38748
+ id: "gemini-3.5-flash",
38749
+ name: "Gemini 3.5 Flash",
38750
+ provider: "google",
38751
+ active: false,
38752
+ pricing: { input: 1.5, output: 9, cacheRead: 0.15, cacheWrite: 1.5 }
38753
+ }),
38754
+ // Retired by Vertex 2026-10-20 (see SUNSET_MODEL_REPLACEMENTS); kept to price
38755
+ // historical usage. Never served by the `us` multi-region endpoint, hence the
38756
+ // single global route.
38757
+ {
38758
+ model: "gemini-2.5-pro",
38759
+ id: "gemini-2.5-pro",
38760
+ name: "Gemini 2.5 Pro",
38761
+ subprocessor: "vertex-global",
38762
+ provider: "google",
38763
+ active: false,
38764
+ pricing: {
38765
+ input: 1.25,
38766
+ output: 10,
38767
+ cacheRead: 0.125,
38768
+ cacheWrite: 1.25,
38769
+ longContext: {
38770
+ inputTokenThreshold: 2e5,
38771
+ input: 2.5,
38772
+ output: 15,
38773
+ cacheRead: 0.25,
38774
+ cacheWrite: 2.5
38775
+ }
38776
+ }
38777
+ },
38778
+ {
38779
+ model: "gemini-2.5-flash",
38780
+ id: "gemini-2.5-flash",
38781
+ name: "Gemini 2.5 Flash",
38782
+ subprocessor: "vertex-global",
38783
+ provider: "google",
38784
+ active: false,
38785
+ pricing: { input: 0.3, output: 2.5, cacheRead: 0.03, cacheWrite: 0.3 }
38786
+ },
38787
+ /*
38788
+ * Vertex MaaS (Model-as-a-Service). Open/partner models — GLM (ZAI) —
38789
+ * served over Vertex's OpenAI-compatible Chat Completions endpoint, a
38790
+ * different transport from the native Gemini models above (see
38791
+ * `vertexMaasModel` in common-server/src/llm.ts). IDs are the
38792
+ * `<publisher>/<model>-maas` strings Vertex Model Garden lists. Reuse the
38793
+ * same GOOGLE_VERTEX_* service-account creds — no per-model secret — and are
38794
+ * billed as Vertex usage. These models are region-gated: only `global`
38795
+ * serves them — the `us` multi-region endpoint 404s both, verified by
38796
+ * direct call.
38797
+ *
38798
+ * Pricing: these open models expose no prompt-cache discount on Vertex (nor
38799
+ * on Bedrock — only Anthropic and Nova cache there), so cacheRead and
38800
+ * cacheWrite both mirror input: a cache hit costs the same as fresh input.
38801
+ * Rates are each model's standard published rate.
38802
+ */
38803
+ {
38804
+ model: "glm-5",
38805
+ id: "zai-org/glm-5-maas",
38806
+ name: "GLM 5 (Vertex)",
38807
+ subprocessor: "vertex-global",
38808
+ provider: "vertex-maas",
38809
+ active: true,
38810
+ pricing: { input: 1, output: 3.2, cacheRead: 1, cacheWrite: 1 }
38811
+ },
38812
+ {
38813
+ model: "glm-4.7",
38814
+ id: "zai-org/glm-4.7-maas",
38815
+ name: "GLM 4.7 (Vertex)",
38816
+ subprocessor: "vertex-global",
38817
+ provider: "vertex-maas",
38818
+ active: true,
38819
+ pricing: { input: 0.6, output: 2.2, cacheRead: 0.6, cacheWrite: 0.6 }
38820
+ },
38821
+ /*
38822
+ * Amazon Bedrock. IDs are raw Bedrock model strings: `global.*` prefixes are
38823
+ * global cross-region inference profiles (AWS may process in any commercial
38824
+ * region, at the direct-API price), `us.*` are US-only profiles (+10%, kept
38825
+ * for the ids the enclave clusters persisted), and bare `<publisher>.*` IDs
38826
+ * are single-region foundation models. Auth uses the default AWS credential
38827
+ * chain — ECS task role in deploys, AWS_ACCESS_KEY_ID + AWS_REGION env vars
38828
+ * locally.
38829
+ *
38830
+ * Claude first. These rows share `model` with the Anthropic rows so a
38831
+ * cluster can serve every persisted Claude name from Amazon instead:
38832
+ * `claude-sonnet-4-6` resolves to the Bedrock row wherever
38833
+ * LLM_SUBPROCESSOR_PREFERENCE puts `bedrock` first (see
38834
+ * common-server/src/aiSubprocessors.ts). They are `active: false` because
38835
+ * the dropdown already offers each model once, under its Anthropic-named
38836
+ * row, and the router picks the host. Claude on Bedrock dispatches through
38837
+ * the SDK's native Messages API entry point (common-server/src/llm.ts), so
38838
+ * the same `anthropic.cacheControl` breakpoints and provider options apply
38839
+ * as on the direct route, and pricing — base and cache multipliers — matches
38840
+ * Anthropic's on `global.*` profiles.
38841
+ */
38842
+ // The 5.5 generation, priced at the direct rates: Anthropic's Bedrock page
38843
+ // states `global.*` profiles carry no premium (AWS's per-token table renders
38844
+ // client-side and was not read). Unlike Haiku 4.5's, these ids carry no date
38845
+ // or `-v1:0` suffix.
38846
+ {
38847
+ model: "claude-sonnet-5-5",
38848
+ id: "global.anthropic.claude-sonnet-5-5",
38849
+ name: "Claude Sonnet 5.5 (Bedrock)",
38850
+ subprocessor: "bedrock",
38851
+ provider: "bedrock",
38852
+ active: false,
38853
+ pricing: { input: 2, output: 10, cacheRead: 0.1, cacheWrite: 2.5 },
38854
+ pricingPeriods: SONNET_5_5_PRICING_PERIODS
38855
+ },
38856
+ {
38857
+ model: "claude-opus-5-5",
38858
+ id: "global.anthropic.claude-opus-5-5",
38859
+ name: "Claude Opus 5.5 (Bedrock)",
38860
+ subprocessor: "bedrock",
38861
+ provider: "bedrock",
38862
+ active: false,
38863
+ pricing: { input: 4, output: 20, cacheRead: 0.2, cacheWrite: 5 }
38864
+ },
38865
+ {
38866
+ model: "claude-fable-5-1",
38867
+ id: "global.anthropic.claude-fable-5-1",
38868
+ name: "Claude Fable 5.1 (Bedrock)",
38869
+ subprocessor: "bedrock",
38870
+ provider: "bedrock",
38871
+ active: false,
38872
+ pricing: { input: 10, output: 50, cacheRead: 0.25, cacheWrite: 12.5 },
38873
+ providerOptionsTransform: stripAnthropicThinking
38874
+ },
38875
+ // AWS lists the >100K tier as a separate "Long Context" line.
38876
+ {
38877
+ model: "claude-haiku-5-5",
38878
+ id: "global.anthropic.claude-haiku-5-5",
38879
+ name: "Claude Haiku 5.5 (Bedrock)",
38880
+ subprocessor: "bedrock",
38881
+ provider: "bedrock",
38882
+ active: false,
38883
+ pricing: HAIKU_5_5_PRICING
38884
+ },
38885
+ {
38886
+ model: "claude-sonnet-4-6",
38887
+ id: "global.anthropic.claude-sonnet-4-6",
38888
+ name: "Claude Sonnet 4.6 (Bedrock)",
38889
+ subprocessor: "bedrock",
38890
+ provider: "bedrock",
38891
+ active: false,
38892
+ pricing: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 }
38893
+ },
38894
+ {
38895
+ model: "claude-sonnet-5",
38896
+ id: "global.anthropic.claude-sonnet-5",
38897
+ name: "Claude Sonnet 5 (Bedrock)",
38898
+ subprocessor: "bedrock",
38899
+ provider: "bedrock",
38900
+ active: false,
38901
+ pricing: { input: 2, output: 10, cacheRead: 0.2, cacheWrite: 2.5 }
38902
+ },
38903
+ {
38904
+ model: "claude-opus-5",
38905
+ id: "global.anthropic.claude-opus-5",
38906
+ name: "Claude Opus 5 (Bedrock)",
38907
+ subprocessor: "bedrock",
38908
+ provider: "bedrock",
38909
+ active: false,
38910
+ pricing: { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 }
38911
+ },
38912
+ {
38913
+ model: "claude-fable-5",
38914
+ id: "global.anthropic.claude-fable-5",
38915
+ name: "Claude Fable 5 (Bedrock)",
38916
+ subprocessor: "bedrock",
38917
+ provider: "bedrock",
38918
+ active: false,
38919
+ pricing: { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 },
38920
+ providerOptionsTransform: stripAnthropicThinking
38921
+ },
38922
+ {
38923
+ model: "claude-haiku-4-5",
38924
+ id: "global.anthropic.claude-haiku-4-5-20251001-v1:0",
38925
+ name: "Claude Haiku 4.5 (Bedrock)",
38926
+ subprocessor: "bedrock",
38927
+ provider: "bedrock",
38928
+ active: false,
38929
+ pricing: { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 }
38930
+ },
38931
+ // Prior-generation rows so a persisted name keeps resolving to the
38932
+ // preferred host rather than quietly staying on the direct route.
38933
+ {
38934
+ model: "claude-opus-4-8",
38935
+ id: "global.anthropic.claude-opus-4-8",
38936
+ name: "Claude Opus 4.8 (Bedrock)",
38937
+ subprocessor: "bedrock",
38938
+ provider: "bedrock",
38939
+ active: false,
38940
+ pricing: { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 }
38941
+ },
38942
+ {
38943
+ model: "claude-opus-4-7",
38944
+ id: "global.anthropic.claude-opus-4-7",
38945
+ name: "Claude Opus 4.7 (Bedrock)",
38946
+ subprocessor: "bedrock",
38947
+ provider: "bedrock",
38948
+ active: false,
38949
+ pricing: { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 }
38950
+ },
38951
+ {
38952
+ model: "claude-opus-4-6",
38953
+ id: "global.anthropic.claude-opus-4-6-v1",
38954
+ name: "Claude Opus 4.6 (Bedrock)",
38955
+ subprocessor: "bedrock",
38956
+ provider: "bedrock",
38957
+ active: false,
38958
+ pricing: { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 }
38959
+ },
38960
+ {
38961
+ model: "claude-sonnet-4-5",
38962
+ id: "global.anthropic.claude-sonnet-4-5-20250929-v1:0",
38963
+ name: "Claude Sonnet 4.5 (Bedrock)",
38964
+ subprocessor: "bedrock",
38965
+ provider: "bedrock",
38966
+ active: false,
38967
+ pricing: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 }
38968
+ },
38969
+ // US-only profile, kept under its persisted id: the enclave clusters reach
38970
+ // only Bedrock and their saved resources name this string. Ranked behind the
38971
+ // global row so a model name lands there only where global is unreachable.
38972
+ // AWS bills US-pinned profiles 10% over the direct rate.
38973
+ {
38974
+ model: "claude-sonnet-4-5",
38975
+ id: "us.anthropic.claude-sonnet-4-5-20250929-v1:0",
38976
+ name: "Claude Sonnet 4.5 (Bedrock \u2014 US)",
38977
+ subprocessor: "bedrock",
38978
+ provider: "bedrock",
38979
+ active: false,
38980
+ pricing: { input: 3.3, output: 16.5, cacheRead: 0.33, cacheWrite: 4.125 }
38981
+ },
38982
+ // Anthropic retired Opus 4.1 on the direct API 2026-08-05; Bedrock still
38983
+ // serves it, so the enclave's persisted id keeps resolving. Priced at the
38984
+ // legacy $15/$75 — the 4.6 reprice never propagated backwards — plus the
38985
+ // US-pinned 10%.
38986
+ {
38987
+ model: "claude-opus-4-1",
38988
+ id: "us.anthropic.claude-opus-4-1-20250805-v1:0",
38989
+ name: "Claude Opus 4.1 (Bedrock \u2014 US)",
38990
+ subprocessor: "bedrock",
38991
+ provider: "bedrock",
38992
+ active: false,
38993
+ pricing: { input: 16.5, output: 82.5, cacheRead: 1.65, cacheWrite: 20.625 }
38994
+ },
38995
+ // No canonical `-vN:M` suffix — that's how AWS lists Moonshot models. Kimi
38996
+ // on Bedrock gets no prompt-cache discount — Bedrock caching is
38997
+ // Anthropic/Nova only — so cacheRead mirrors input, not the input/10 the
38998
+ // Claude rows assume.
38999
+ {
39000
+ model: "kimi-k2.5",
39001
+ id: "moonshotai.kimi-k2.5",
39002
+ name: "Kimi K2.5 (Bedrock)",
39003
+ subprocessor: "bedrock",
39004
+ provider: "bedrock",
39005
+ active: true,
39006
+ pricing: { input: 0.6, output: 3, cacheRead: 0.6, cacheWrite: 0.6 }
39007
+ },
39008
+ {
39009
+ model: "llama4-maverick",
39010
+ id: "us.meta.llama4-maverick-17b-instruct-v1:0",
39011
+ name: "Llama 4 Maverick (Bedrock)",
39012
+ subprocessor: "bedrock",
39013
+ provider: "bedrock",
39014
+ active: true,
39015
+ pricing: { input: 0.24, output: 0.97, cacheRead: 0.024, cacheWrite: 0.24 }
39016
+ },
39017
+ {
39018
+ model: "llama4-scout",
39019
+ id: "us.meta.llama4-scout-17b-instruct-v1:0",
39020
+ name: "Llama 4 Scout (Bedrock)",
39021
+ subprocessor: "bedrock",
39022
+ provider: "bedrock",
39023
+ active: true,
39024
+ pricing: { input: 0.17, output: 0.66, cacheRead: 0.017, cacheWrite: 0.17 }
39025
+ },
39026
+ // Bedrock no longer recognizes this id (ValidationException on invoke); the
39027
+ // row stays to price historical usage and SUNSET_MODEL_REPLACEMENTS rewrites
39028
+ // persisted names onto Pixtral Large, Mistral's successor in the same family.
39029
+ {
39030
+ model: "mistral-large-2",
39031
+ id: "mistral.mistral-large-2407-v1:0",
39032
+ name: "Mistral Large 2 (Bedrock)",
39033
+ subprocessor: "bedrock",
39034
+ provider: "bedrock",
39035
+ active: false,
39036
+ pricing: { input: 3, output: 9, cacheRead: 0.3, cacheWrite: 3 }
39037
+ },
39038
+ {
39039
+ model: "pixtral-large",
39040
+ id: "mistral.pixtral-large-2502-v1:0",
39041
+ name: "Pixtral Large (Bedrock)",
39042
+ subprocessor: "bedrock",
39043
+ provider: "bedrock",
39044
+ active: true,
39045
+ pricing: { input: 2, output: 6, cacheRead: 0.2, cacheWrite: 2 }
39046
+ },
39047
+ /*
39048
+ * Third-party open-weight hosts — Fireworks, Sail (sailresearch.com), and GMI
39049
+ * Cloud (gmicloud.ai) — serving the GLM 5.3 line. One `model` identity, one route
39050
+ * per host: the hosts genuinely price the same weights differently (Sail's Flash
39051
+ * is 30% under the other two), so the route the org enables decides the bill. All
39052
+ * ship OPT-IN (see OPT_IN_SUBPROCESSORS in policy.ts): every organization starts
39053
+ * with them refused and an admin turns one on from the settings page.
39054
+ *
39055
+ * Registered after Bedrock, at the bottom, on purpose: the default-model walk ranks
39056
+ * top down, and an org that manages to enable ONLY these should get them — putting
39057
+ * them ahead of the hosts most clusters default to would let a fail-open bug flip
39058
+ * everyone's architect onto one silently.
39059
+ *
39060
+ * `active: false` for rollout: hidden from pickers while the opt-in/enterprise wiring
39061
+ * settles, still addressable by the Architect staff override (inactive ids resolve
39062
+ * like any other). Freeing them is a one-line release.
39063
+ *
39064
+ * Pricing per each host's published rates. All bill no separate cache-write premium
39065
+ * (cache fill happens in normal input processing), so cacheWrite is 0 rather than
39066
+ * the mirror-input convention the MaaS/Bedrock rows use (that mirrors "no caching
39067
+ * at all"; these hosts have caching, just no write line item). GMI publishes its
39068
+ * rates inline in the /v1/models catalog (probed 2026-09-12) and applies a
39069
+ * discount_to_user on the account — its rates here are the list rows.
39070
+ *
39071
+ * Fireworks is two routes the way Vertex is: `fireworks` global, and the US-only
39072
+ * endpoint (us.api.fireworks.ai) pinned to `fireworks-us` at the documented +50%
39073
+ * premium. Unlike the dual-region Vertex rows these are DIFFERENT ids — the US
39074
+ * endpoint serves `routers/*-us` names — so an org that refuses global fireworks
39075
+ * must name the US id explicitly; there is no same-id region fallback to land in.
39076
+ *
39077
+ * Sail prices the SAME id by completion window (`metadata.completion_window`:
39078
+ * asap/balanced/flex, flex half of asap) — that is a REQUEST parameter, not a host,
39079
+ * so it can't be a route under this registry (pricing keys off id; one id one price,
39080
+ * enforced by the policy spec). Registered at the asap row's rates; wiring windows
39081
+ * for a lazy lane later means per-request billing, not new routes. Sail is
39082
+ * zero-data-retention by default, SOC 2 / HIPAA, and case-sensitive on model ids —
39083
+ * routes register the lowercase form (what modelFromName dispatches) and the sail
39084
+ * transport restores the canonical wire case (see SAIL_WIRE_IDS in llm.ts).
39085
+ *
39086
+ * Sail is ALSO two routes the way Fireworks is: `sail` global, and `sail-us`, which
39087
+ * pins US residency with a per-request `routing.allowed_countries: ["US"]` flag at
39088
+ * the documented +20% premium. One gateway serves both, so the US rows take
39089
+ * sail-us- prefixed ids (same one-id-one-price constraint as the completion windows)
39090
+ * and the transport strips the prefix and injects the flag.
39091
+ *
39092
+ * GMI is case-sensitive the same way (a lowercase id 400s "No matching target
39093
+ * server found"), and its native ids collide with Sail's (`zai-org/GLM-5.3-Flash`
39094
+ * serves on BOTH hosts) — so gmi routes take gmi- prefixed ids and the transport
39095
+ * restores the wire case (GMI_WIRE_IDS in llm.ts). It also silently TRUNCATES an
39096
+ * over-context prompt by default; the transport overrides that to error. GMI has
39097
+ * no US-only endpoint, so no residency row; and it publishes no zero-retention /
39098
+ * no-training commitment for the shared serverless product — production clusters
39099
+ * don't declare it (LLM_SUBPROCESSORS) until that ships in a DPA.
39100
+ *
39101
+ * GLM-5.3 is NOT on Vertex MaaS (probed 2026-09-12: both `zai-org/glm-5.3-maas`
39102
+ * and a flash variant 404 while `zai-org/glm-5-maas` still serves), and MaaS/Bedrock
39103
+ * expose no prompt-cache discount for open weights anyway — the reason these hosts
39104
+ * exist as a fourth home for the weights.
39105
+ */
39106
+ {
39107
+ model: "glm-5.3-flash",
39108
+ id: "accounts/fireworks/models/glm-5p3-flash",
39109
+ name: "GLM 5.3 Flash (Fireworks)",
39110
+ subprocessor: "fireworks",
39111
+ provider: "fireworks",
39112
+ active: false,
39113
+ pricing: { input: 0.15, output: 0.5, cacheRead: 0.03, cacheWrite: 0 }
39114
+ },
39115
+ {
39116
+ model: "glm-5.3-flash",
39117
+ id: "accounts/fireworks/routers/glm-5p3-flash-us",
39118
+ name: "GLM 5.3 Flash (Fireworks US)",
39119
+ subprocessor: "fireworks-us",
39120
+ provider: "fireworks",
39121
+ active: false,
39122
+ pricing: { input: 0.225, output: 0.75, cacheRead: 0.045, cacheWrite: 0 }
39123
+ },
39124
+ {
39125
+ model: "glm-5.3-flash",
39126
+ id: "zai-org/glm-5.3-flash",
39127
+ name: "GLM 5.3 Flash (Sail)",
39128
+ subprocessor: "sail",
39129
+ provider: "sail",
39130
+ active: false,
39131
+ pricing: { input: 0.105, output: 0.35, cacheRead: 0.021, cacheWrite: 0 }
39132
+ },
39133
+ {
39134
+ model: "glm-5.3-flash",
39135
+ id: "sail-us-zai-org/glm-5.3-flash",
39136
+ name: "GLM 5.3 Flash (Sail \u2014 US)",
39137
+ subprocessor: "sail-us",
39138
+ provider: "sail",
39139
+ active: false,
39140
+ pricing: { input: 0.126, output: 0.42, cacheRead: 0.0252, cacheWrite: 0 }
39141
+ },
39142
+ {
39143
+ model: "glm-5.3-flash",
39144
+ id: "gmi-glm53flash",
39145
+ name: "GLM 5.3 Flash (GMI Cloud)",
39146
+ subprocessor: "gmi",
39147
+ provider: "gmi",
39148
+ active: false,
39149
+ pricing: { input: 0.15, output: 0.5, cacheRead: 0.03, cacheWrite: 0 }
39150
+ },
39151
+ {
39152
+ model: "glm-5.3",
39153
+ id: "accounts/fireworks/models/glm-5p3",
39154
+ name: "GLM 5.3 (Fireworks)",
39155
+ subprocessor: "fireworks",
39156
+ provider: "fireworks",
39157
+ active: false,
39158
+ pricing: { input: 1.4, output: 4.4, cacheRead: 0.26, cacheWrite: 0 }
39159
+ },
39160
+ {
39161
+ model: "glm-5.3",
39162
+ id: "accounts/fireworks/routers/glm-5p3-us",
39163
+ name: "GLM 5.3 (Fireworks US)",
39164
+ subprocessor: "fireworks-us",
39165
+ provider: "fireworks",
39166
+ active: false,
39167
+ pricing: { input: 2.1, output: 6.6, cacheRead: 0.39, cacheWrite: 0 }
39168
+ },
39169
+ {
39170
+ model: "glm-5.3",
39171
+ id: "zai-org/glm-5.3",
39172
+ name: "GLM 5.3 (Sail)",
39173
+ subprocessor: "sail",
39174
+ provider: "sail",
39175
+ active: false,
39176
+ pricing: { input: 0.98, output: 3.08, cacheRead: 0.182, cacheWrite: 0 }
39177
+ },
39178
+ {
39179
+ model: "glm-5.3",
39180
+ id: "sail-us-zai-org/glm-5.3",
39181
+ name: "GLM 5.3 (Sail \u2014 US)",
39182
+ subprocessor: "sail-us",
39183
+ provider: "sail",
39184
+ active: false,
39185
+ pricing: { input: 1.176, output: 3.696, cacheRead: 0.2184, cacheWrite: 0 }
39186
+ },
39187
+ {
39188
+ model: "glm-5.3",
39189
+ id: "gmi-glm53-744b",
39190
+ name: "GLM 5.3 (GMI Cloud)",
39191
+ subprocessor: "gmi",
39192
+ provider: "gmi",
39193
+ active: false,
39194
+ pricing: { input: 1.4, output: 4.4, cacheRead: 0.26, cacheWrite: 0 }
39195
+ }
39196
+ ];
39197
+ var LLM_MODELS = MODEL_ROUTES.filter(
39198
+ (route, index) => MODEL_ROUTES.findIndex((r) => r.id === route.id) === index
39199
+ ).map(({ id, name: name2, provider, active }) => ({ id, name: name2, provider, active }));
39200
+ var ACTIVE_MODEL_IDS = LLM_MODELS.filter((m) => m.active).map((m) => m.id).join(", ");
38125
39201
 
38126
39202
  // ../common/src/agent.ts
38127
39203
  var agentVersionConfigSchema = external_exports.object({
@@ -38129,14 +39205,22 @@ var agentVersionConfigSchema = external_exports.object({
38129
39205
  model: external_exports.string(),
38130
39206
  temperature: external_exports.number(),
38131
39207
  maxTokens: external_exports.number(),
39208
+ /**
39209
+ * Model calls the agent may make in one turn. The last one runs without
39210
+ * tools: a turn that hits the limit mid-investigation would otherwise end on
39211
+ * an unanswered tool call — no reply in chat, and no object at all from
39212
+ * `generate/object`. Defaulted so configs saved before the field still parse.
39213
+ */
39214
+ maxSteps: external_exports.number().int().min(1).max(200).default(20),
38132
39215
  rateLimitRequestsPerMinute: external_exports.number(),
38133
39216
  rateLimitTokensPerMinute: external_exports.number()
38134
39217
  });
38135
39218
  var defaultAgentVersionConfig = {
38136
39219
  systemPrompt: "You are a helpful assistant.",
38137
- model: "claude-sonnet-4-6",
39220
+ model: DEFAULT_MODEL,
38138
39221
  temperature: 0.7,
38139
39222
  maxTokens: 4096,
39223
+ maxSteps: 20,
38140
39224
  rateLimitRequestsPerMinute: 20,
38141
39225
  rateLimitTokensPerMinute: 2e5
38142
39226
  };
@@ -43930,11 +45014,6 @@ var reactDefaultTemplate = `function App() {
43930
45014
  var setSearchParamPayloadSchema = external_exports.object({
43931
45015
  params: external_exports.record(external_exports.string(), external_exports.string().nullable())
43932
45016
  });
43933
- var graphqlRequestPayloadSchema = external_exports.object({
43934
- document: external_exports.string(),
43935
- variables: external_exports.record(external_exports.string(), external_exports.unknown()).nullish(),
43936
- requestId: external_exports.string()
43937
- });
43938
45017
  var llmCompletionPayloadSchema = external_exports.object({
43939
45018
  message: external_exports.string(),
43940
45019
  systemPrompt: external_exports.string().optional(),
@@ -44947,6 +46026,7 @@ var NodeEnum = /* @__PURE__ */ ((NodeEnum2) => {
44947
46026
  NodeEnum2["apsApiRequest"] = "APS_API_REQUEST";
44948
46027
  NodeEnum2["armisAction"] = "ARMIS_ACTION";
44949
46028
  NodeEnum2["attributeChanged"] = "ATTRIBUTE_CHANGED";
46029
+ NodeEnum2["awsAthenaRunQuery"] = "AWS_ATHENA_RUN_QUERY";
44950
46030
  NodeEnum2["awsS3GetObject"] = "AWS_S3_GET_OBJECT";
44951
46031
  NodeEnum2["awsS3ListObjects"] = "AWS_S3_LIST_OBJECTS";
44952
46032
  NodeEnum2["awsS3PutObject"] = "AWS_S3_PUT_OBJECT";
@@ -59732,6 +60812,7 @@ var agentFiles = (v) => ({
59732
60812
  model: v.model,
59733
60813
  temperature: v.temperature,
59734
60814
  maxTokens: v.maxTokens,
60815
+ maxSteps: v.maxSteps,
59735
60816
  rateLimitRequestsPerMinute: v.rateLimitRequestsPerMinute,
59736
60817
  rateLimitTokensPerMinute: v.rateLimitTokensPerMinute,
59737
60818
  functionIds: v.functionIds,
@@ -60191,7 +61272,7 @@ ${findings2.map((f) => ` ${f}`).join("\n")}`
60191
61272
  }
60192
61273
  });
60193
61274
  var agentVersionFields = `
60194
- versionNumber systemPrompt model temperature maxTokens
61275
+ versionNumber systemPrompt model temperature maxTokens maxSteps
60195
61276
  rateLimitRequestsPerMinute rateLimitTokensPerMinute
60196
61277
  agentVersionFunctionsByAgentIdAndVersionNumber(orderBy: [FUNCTION_ID_ASC]) {
60197
61278
  nodes { functionId }
@@ -60215,6 +61296,7 @@ var flattenAgentVersion = (node2) => ({
60215
61296
  model: node2.model,
60216
61297
  temperature: node2.temperature,
60217
61298
  maxTokens: node2.maxTokens,
61299
+ maxSteps: node2.maxSteps,
60218
61300
  rateLimitRequestsPerMinute: node2.rateLimitRequestsPerMinute,
60219
61301
  rateLimitTokensPerMinute: node2.rateLimitTokensPerMinute,
60220
61302
  functionIds: node2.agentVersionFunctionsByAgentIdAndVersionNumber.nodes.map(
@@ -60309,7 +61391,7 @@ var agentSource = (client) => ({
60309
61391
  const data = await client.graphql(
60310
61392
  `mutation SdkCreateAgentVersion($expectedLatestVersion: Int,
60311
61393
  $agentId: String!, $message: String!, $systemPrompt: String!,
60312
- $model: String!, $temperature: Float!, $maxTokens: Int!,
61394
+ $model: String!, $temperature: Float!, $maxTokens: Int!, $maxSteps: Int,
60313
61395
  $rateLimitRequestsPerMinute: Int!, $rateLimitTokensPerMinute: Int!,
60314
61396
  $functionIds: [String], $tableSpaceBindings: JSON, $agentIds: [String],
60315
61397
  $knowledgeBaseIds: [String], $builtinToolSets: [String]
@@ -60317,6 +61399,7 @@ var agentSource = (client) => ({
60317
61399
  createAgentVersionWithAttachments(input: { expectedLatestVersion: $expectedLatestVersion,
60318
61400
  agentId: $agentId, message: $message, systemPrompt: $systemPrompt,
60319
61401
  model: $model, temperature: $temperature, maxTokens: $maxTokens,
61402
+ maxSteps: $maxSteps,
60320
61403
  rateLimitRequestsPerMinute: $rateLimitRequestsPerMinute,
60321
61404
  rateLimitTokensPerMinute: $rateLimitTokensPerMinute,
60322
61405
  functionIds: $functionIds, tableSpaceBindings: $tableSpaceBindings,
@@ -60333,6 +61416,8 @@ var agentSource = (client) => ({
60333
61416
  knowledgeBaseIds: input2.knowledgeBaseIds ?? [],
60334
61417
  // Null keeps the current sets; an empty array clears them.
60335
61418
  builtinToolSets: input2.builtinToolSets ?? null,
61419
+ // Null keeps the latest version's limit.
61420
+ maxSteps: input2.maxSteps ?? null,
60336
61421
  expectedLatestVersion: options.expectedLatestVersion
60337
61422
  }
60338
61423
  );
@@ -61252,6 +62337,24 @@ var genericResource = (client, id) => {
61252
62337
  };
61253
62338
 
61254
62339
  // ../sdk/src/runners.ts
62340
+ var runAction = async (client, input2) => {
62341
+ const organizationId = await client.organizationId();
62342
+ const res = await client.fetch(
62343
+ `/v2/actions/${organizationId}/${input2.workspaceId}/run`,
62344
+ {
62345
+ method: "POST",
62346
+ headers: { "Content-Type": "application/json" },
62347
+ body: JSON.stringify({
62348
+ actionId: input2.actionId,
62349
+ inputs: input2.inputs,
62350
+ ...input2.actionRunnerId != null ? { actionRunnerId: input2.actionRunnerId } : {}
62351
+ })
62352
+ }
62353
+ );
62354
+ const raw2 = await res.text();
62355
+ if (!res.ok) throw apiError("action run", res, raw2, organizationId);
62356
+ return JSON.parse(raw2);
62357
+ };
61255
62358
  var runnerCollection = (client) => ({
61256
62359
  /*
61257
62360
  * Two steps, same as the webapp: createMachineIdentity mints the identity
@@ -61310,7 +62413,6 @@ var runnerCollection = (client) => ({
61310
62413
  });
61311
62414
  var runnerResource = (client, id) => {
61312
62415
  const enqueueAction = async (actionId, inputs) => {
61313
- const organizationId = await client.organizationId();
61314
62416
  const data = await client.graphql(
61315
62417
  `query SdkResolveRunner($id: String!) {
61316
62418
  actionRunners(condition: { id: $id }, first: 1) { nodes { workspaceId } }
@@ -61323,17 +62425,12 @@ var runnerResource = (client, id) => {
61323
62425
  `Unknown runner ${id} (or it has no workspace). Try: overai runner list`
61324
62426
  );
61325
62427
  }
61326
- const res = await client.fetch(
61327
- `/v2/actions/${organizationId}/${workspaceId}/run`,
61328
- {
61329
- method: "POST",
61330
- headers: { "Content-Type": "application/json" },
61331
- body: JSON.stringify({ actionId, inputs, actionRunnerId: id })
61332
- }
61333
- );
61334
- const raw2 = await res.text();
61335
- if (!res.ok) throw apiError("runner run", res, raw2, organizationId);
61336
- return JSON.parse(raw2);
62428
+ return runAction(client, {
62429
+ workspaceId,
62430
+ actionId,
62431
+ inputs,
62432
+ actionRunnerId: id
62433
+ });
61337
62434
  };
61338
62435
  return {
61339
62436
  token: async (options) => {
@@ -61892,11 +62989,12 @@ var OverAIClient = class {
61892
62989
  * POST /v2/graphql. Throws with the server's error messages — callers (and
61893
62990
  * agents driving them) need the actual reason, not a status code.
61894
62991
  */
61895
- async graphql(query, variables) {
62992
+ async graphql(query, variables, options) {
61896
62993
  const res = await this.fetch("/v2/graphql", {
61897
62994
  method: "POST",
61898
62995
  headers: { "Content-Type": "application/json" },
61899
- body: JSON.stringify({ query, variables })
62996
+ body: JSON.stringify({ query, variables }),
62997
+ signal: options?.signal
61900
62998
  });
61901
62999
  const raw2 = await res.text();
61902
63000
  const body = (() => {
@@ -62002,6 +63100,10 @@ var OverAIClient = class {
62002
63100
  ...agentRun(this, id)
62003
63101
  };
62004
63102
  }
63103
+ /** Completion metadata for one run, independent of its potentially offloaded node state. */
63104
+ automationRun(runId, options) {
63105
+ return automationRun(this, runId, options);
63106
+ }
62005
63107
  /** A run addresses no automation, so its state is read by run id alone. */
62006
63108
  automationRunState(runId) {
62007
63109
  return automationRunState(this, runId);
@@ -62027,6 +63129,10 @@ var OverAIClient = class {
62027
63129
  runner(id) {
62028
63130
  return runnerResource(this, id);
62029
63131
  }
63132
+ /** Runs one automation node's action on a runner, outside any automation. */
63133
+ runAction(input2) {
63134
+ return runAction(this, input2);
63135
+ }
62030
63136
  clusterInfo() {
62031
63137
  return clusterInfo(this);
62032
63138
  }
@@ -62410,6 +63516,25 @@ var readStdin = async (claimant = "stdin") => {
62410
63516
  }
62411
63517
  return Buffer.concat(chunks).toString("utf8");
62412
63518
  };
63519
+ var readInputs = async (args) => {
63520
+ const inputs = {};
63521
+ if (args.includes("-")) {
63522
+ const raw2 = (await readStdin()).trim();
63523
+ const [parsed] = resultOfSync(() => JSON.parse(raw2));
63524
+ if (parsed == null || typeof parsed !== "object" || Array.isArray(parsed))
63525
+ throw new Error(`stdin must be a JSON object. Got: ${raw2.slice(0, 120)}`);
63526
+ Object.assign(inputs, parsed);
63527
+ }
63528
+ for (let i = 0; i < args.length; i++) {
63529
+ if (args[i] === "--input") {
63530
+ const pair = args[i + 1] ?? "";
63531
+ const eq = pair.indexOf("=");
63532
+ if (eq <= 0) throw new Error(`--input expects k=v. Got: ${pair}`);
63533
+ inputs[pair.slice(0, eq)] = pair.slice(eq + 1);
63534
+ }
63535
+ }
63536
+ return inputs;
63537
+ };
62413
63538
  var readValue = async (value, label) => {
62414
63539
  if (value?.startsWith("@") !== true) return value;
62415
63540
  if (value.startsWith("@@")) return value.slice(1);
@@ -62915,9 +64040,39 @@ var whoami = (args = []) => {
62915
64040
  );
62916
64041
  };
62917
64042
 
64043
+ // src/commands/action.ts
64044
+ var USAGE = "Usage: overai action run <workspaceId> <ACTION_ID> [--runner <runnerId>] [--input k=v ... | -]\n";
64045
+ var runAction2 = async (args) => {
64046
+ const [sub, workspaceId, actionId, ...rest] = args;
64047
+ if (sub !== "run" || workspaceId == null || workspaceId.startsWith("-") || actionId == null || actionId.startsWith("-")) {
64048
+ process.stderr.write(USAGE);
64049
+ process.exitCode = 1;
64050
+ return;
64051
+ }
64052
+ const unknown2 = unknownFlags(
64053
+ rest,
64054
+ ["--runner", "--input", "-", "-o", "--output"],
64055
+ ["--runner", "--input", "-o", "--output"]
64056
+ );
64057
+ if (unknown2.length > 0)
64058
+ throw new Error(
64059
+ `Unknown flag ${unknown2.join(", ")}. See: overai action --help`
64060
+ );
64061
+ const actionRunnerId = flagValue(rest, "--runner");
64062
+ printResult(
64063
+ await cliClient().runAction({
64064
+ workspaceId,
64065
+ actionId,
64066
+ inputs: await readInputs(rest),
64067
+ ...actionRunnerId != null ? { actionRunnerId } : {}
64068
+ }),
64069
+ resolveFormat(flagValue(rest, "-o", "--output"))
64070
+ );
64071
+ };
64072
+
62918
64073
  // src/commands/agent.ts
62919
64074
  import { createInterface } from "readline";
62920
- var USAGE = [
64075
+ var USAGE2 = [
62921
64076
  "Usage:",
62922
64077
  " overai agent generate <id> --message <text|@file|@->",
62923
64078
  " overai agent tools <id> [--version <n>] [-o json|table|csv]",
@@ -63019,7 +64174,7 @@ Conversation: ${conversationId}
63019
64174
  };
63020
64175
  var runAgent = async (args) => {
63021
64176
  const [operation, id] = args;
63022
- if (id == null) throw new Error(USAGE);
64177
+ if (id == null) throw new Error(USAGE2);
63023
64178
  switch (operation) {
63024
64179
  case "generate":
63025
64180
  await generate(args, id);
@@ -63031,11 +64186,12 @@ var runAgent = async (args) => {
63031
64186
  await chat(args, id);
63032
64187
  return;
63033
64188
  default:
63034
- throw new Error(USAGE);
64189
+ throw new Error(USAGE2);
63035
64190
  }
63036
64191
  };
63037
64192
 
63038
64193
  // src/commands/automation.ts
64194
+ import { setTimeout as sleep } from "timers/promises";
63039
64195
  var runRuns = async (automationId, args) => {
63040
64196
  printResult(
63041
64197
  await cliClient().automation(automationId).runs({
@@ -63054,37 +64210,104 @@ var runState = async (runId, args) => {
63054
64210
  resolveFormat(flagValue(args, "-o", "--output"))
63055
64211
  );
63056
64212
  };
63057
- var runTrigger = async (automationId, args) => {
63058
- const body = {};
63059
- if (args.includes("-")) {
63060
- Object.assign(
63061
- body,
63062
- JSON.parse(await readStdin())
64213
+ var runTrigger = async (automationId, args) => cliClient().automation(automationId).run(await readInputs(args));
64214
+ var waitForRun = async (runId, timeoutSeconds, format2) => {
64215
+ process.stderr.write(`Automation run: ${runId}
64216
+ `);
64217
+ const controller = new AbortController();
64218
+ const interrupt = () => {
64219
+ controller.abort(
64220
+ new Error(
64221
+ `Stopped waiting for run ${runId}; the automation was not cancelled.`
64222
+ )
63063
64223
  );
63064
- }
63065
- for (let i = 0; i < args.length; i++) {
63066
- if (args[i] === "--input") {
63067
- const pair = args[i + 1] ?? "";
63068
- const eq = pair.indexOf("=");
63069
- if (eq > 0) body[pair.slice(0, eq)] = pair.slice(eq + 1);
64224
+ };
64225
+ process.once("SIGINT", interrupt);
64226
+ const stopped = new Promise((_resolve, reject2) => {
64227
+ controller.signal.addEventListener(
64228
+ "abort",
64229
+ () => {
64230
+ reject2(controller.signal.reason);
64231
+ },
64232
+ { once: true }
64233
+ );
64234
+ });
64235
+ const timeout = setTimeout(() => {
64236
+ controller.abort(
64237
+ new Error(
64238
+ `Timed out waiting for run ${runId} after ${String(timeoutSeconds)}s; the automation was not cancelled.`
64239
+ )
64240
+ );
64241
+ }, timeoutSeconds * 1e3);
64242
+ try {
64243
+ for (; ; ) {
64244
+ const run2 = await Promise.race([
64245
+ cliClient().automationRun(runId, { signal: controller.signal }),
64246
+ stopped
64247
+ ]);
64248
+ controller.signal.throwIfAborted();
64249
+ if (run2.status === "cancelled" || run2.endedAt != null) {
64250
+ printResult(run2, format2);
64251
+ if (run2.status !== "ok") process.exitCode = 1;
64252
+ return;
64253
+ }
64254
+ await sleep(1e3, void 0, { signal: controller.signal });
63070
64255
  }
64256
+ } catch (error62) {
64257
+ controller.signal.throwIfAborted();
64258
+ throw error62;
64259
+ } finally {
64260
+ clearTimeout(timeout);
64261
+ process.removeListener("SIGINT", interrupt);
63071
64262
  }
63072
- printResult(
63073
- await cliClient().automation(automationId).run(body),
63074
- resolveFormat(flagValue(args, "-o", "--output"))
63075
- );
63076
64263
  };
63077
64264
  var runAutomation = async (args) => {
63078
64265
  const [sub, id, ...rest] = args;
63079
64266
  if (sub === "runs" && id != null) return runRuns(id, rest);
63080
64267
  if (sub === "state" && id != null) return runState(id, rest);
63081
- if (sub === "run" && id != null) return runTrigger(id, rest);
64268
+ if ((sub === "run" || sub === "wait") && id != null && !id.startsWith("-")) {
64269
+ const unknown2 = unknownFlags(
64270
+ rest,
64271
+ sub === "run" ? ["--input", "-", "--wait", "--timeout", "-o", "--output"] : ["--timeout", "-o", "--output"],
64272
+ ["--input", "--timeout", "-o", "--output"]
64273
+ );
64274
+ if (unknown2.length > 0)
64275
+ throw new Error(
64276
+ `Unknown flag ${unknown2.join(", ")}. See: overai automation --help`
64277
+ );
64278
+ const waiting = sub === "wait" || rest.includes("--wait");
64279
+ if (rest.includes("--timeout") && !waiting)
64280
+ throw new Error("--timeout requires --wait when starting a run.");
64281
+ const timeout = integerFlag(rest, "--timeout", 300);
64282
+ if (rest.includes("--timeout") && flagValue(rest, "--timeout") == null || timeout <= 0 || // Larger delays overflow Node's signed 32-bit millisecond timer and fire immediately.
64283
+ timeout > 2147483) {
64284
+ throw new Error(
64285
+ "--timeout must be a whole number of seconds between 1 and 2147483."
64286
+ );
64287
+ }
64288
+ const format2 = resolveFormat(flagValue(rest, "-o", "--output"));
64289
+ if (sub === "wait") return waitForRun(id, timeout, format2);
64290
+ const result = await runTrigger(id, rest);
64291
+ if (!waiting) {
64292
+ printResult(result, format2);
64293
+ return;
64294
+ }
64295
+ if (result.automationRunId == null)
64296
+ throw new Error(
64297
+ "The server returned no automationRunId; cannot wait. Check automation runs before retrying the trigger."
64298
+ );
64299
+ return waitForRun(result.automationRunId, timeout, format2);
64300
+ }
63082
64301
  process.stderr.write(
63083
- "Usage: overai automation runs <automationId> [--limit N] [--failed] [--running]\n overai automation state <runId> [--node <variableId>]\n overai automation run <automationId> [--input k=v ... | -]\n"
64302
+ "Usage: overai automation runs <automationId> [--limit N] [--failed] [--running]\n overai automation state <runId> [--node <variableId>]\n overai automation run <automationId> [--input k=v ... | -] [--wait] [--timeout seconds]\n overai automation wait <runId> [--timeout seconds]\nWait defaults to 300 seconds. Timeout or Ctrl-C stops waiting, not the automation.\n"
63084
64303
  );
63085
64304
  process.exitCode = 1;
63086
64305
  };
63087
64306
 
64307
+ // src/commands/create.ts
64308
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
64309
+ import { dirname as dirname3, join as join4 } from "path";
64310
+
63088
64311
  // src/workspace/adapters.ts
63089
64312
  var CLONE_DIRS = {
63090
64313
  function: "functions",
@@ -63227,7 +64450,7 @@ var runList = async (args) => {
63227
64450
  });
63228
64451
  printResult(runners, resolveFormat(flagValue(args, "-o", "--output")));
63229
64452
  };
63230
- var USAGE2 = "Usage: overai runner create <workspaceId> [--name <name>]\n overai runner token <runnerId> [--note <note>]\n overai runner run <runnerId> [--interpreter shell|powershell] --code '<code>'\n overai runner upload <runnerId> --path '<path>'\n overai runner list [workspaceId] [--limit <n>]\n";
64453
+ var USAGE3 = "Usage: overai runner create <workspaceId> [--name <name>]\n overai runner token <runnerId> [--note <note>]\n overai runner run <runnerId> [--interpreter shell|powershell] --code '<code>'\n overai runner upload <runnerId> --path '<path>'\n overai runner list [workspaceId] [--limit <n>]\n";
63231
64454
  var runRunner = async (args) => {
63232
64455
  const [sub, id, ...rest] = args;
63233
64456
  if (sub === "create" && id != null) return runCreate(id, rest);
@@ -63235,7 +64458,7 @@ var runRunner = async (args) => {
63235
64458
  if (sub === "run" && id != null) return runRun(id, rest);
63236
64459
  if (sub === "upload" && id != null) return runUpload(id, rest);
63237
64460
  if (sub === "list") return runList([id, ...rest].filter((a) => a != null));
63238
- process.stderr.write(USAGE2);
64461
+ process.stderr.write(USAGE3);
63239
64462
  process.exitCode = 1;
63240
64463
  };
63241
64464
 
@@ -63273,27 +64496,6 @@ var adapterType = {
63273
64496
  "printable-page": "printablePage",
63274
64497
  agent: "agent"
63275
64498
  };
63276
- var addToClone = async (root, manifest, type, id) => {
63277
- const remote = (await adapterFor(type).fetchAll(manifest.workspaceId)).find(
63278
- (resource) => resource.id === id
63279
- );
63280
- if (remote == null) {
63281
- throw new Error(
63282
- `Created ${id}, but could not read it back into the clone.`
63283
- );
63284
- }
63285
- Object.entries(remote.files).forEach(([path, content]) => {
63286
- writeSynced(root, path, content);
63287
- });
63288
- manifest.resources[id] = {
63289
- type,
63290
- name: remote.name,
63291
- files: Object.keys(remote.files),
63292
- baseVersion: remote.latestVersion,
63293
- deployedVersion: remote.deployedVersion
63294
- };
63295
- writeManifest(root, manifest);
63296
- };
63297
64499
  var runCreate2 = async (args) => {
63298
64500
  const rawType = args[0];
63299
64501
  if (!CREATE_TYPES.includes(rawType)) {
@@ -63302,19 +64504,26 @@ var runCreate2 = async (args) => {
63302
64504
  );
63303
64505
  }
63304
64506
  const type = rawType;
64507
+ const format2 = resolveFormat(flagValue(args, "-o", "--output"));
63305
64508
  const explicitWorkspaceId = flagValue(args, "--workspace");
63306
64509
  const clone2 = (() => {
63307
- if (explicitWorkspaceId != null) return null;
63308
64510
  try {
64511
+ if (explicitWorkspaceId != null) {
64512
+ const root = findWorkspaceRoot();
64513
+ const manifest = readManifest(root);
64514
+ if (manifest.workspaceId.toLowerCase() !== explicitWorkspaceId.toLowerCase() || manifest.organizationId !== scopedOrgId() || manifest.apiBaseUrl != null && manifest.apiBaseUrl !== apiBaseUrl())
64515
+ return null;
64516
+ }
63309
64517
  return openClone();
63310
64518
  } catch (error62) {
64519
+ if (explicitWorkspaceId != null) return null;
63311
64520
  if (error62 instanceof Error && error62.message.startsWith("Not inside a cloned workspace")) {
63312
64521
  return null;
63313
64522
  }
63314
64523
  throw error62;
63315
64524
  }
63316
64525
  })();
63317
- const workspaceId = explicitWorkspaceId ?? clone2?.manifest.workspaceId;
64526
+ const workspaceId = clone2?.manifest.workspaceId ?? explicitWorkspaceId;
63318
64527
  if (type !== "workspace" && workspaceId == null) {
63319
64528
  throw new Error(
63320
64529
  "Pass --workspace <id>, or run the command inside a cloned workspace."
@@ -63359,18 +64568,64 @@ var runCreate2 = async (args) => {
63359
64568
  }
63360
64569
  return (await createActionRunner(workspaceId, name2)).actionRunnerId;
63361
64570
  })();
64571
+ const localFiles = [];
64572
+ const result = {
64573
+ type,
64574
+ id,
64575
+ name: name2,
64576
+ workspaceId: type === "workspace" ? id : workspaceId
64577
+ };
63362
64578
  const trackedType = adapterType[type];
63363
- if (clone2 != null && trackedType != null) {
63364
- await addToClone(clone2.root, clone2.manifest, trackedType, id);
64579
+ try {
64580
+ if (clone2 != null && trackedType != null) {
64581
+ const adapter = adapterFor(trackedType);
64582
+ const head = await adapter.head(id);
64583
+ const files = await adapter.fetchVersion(id, head.latestVersion, name2);
64584
+ Object.keys(files).forEach((path) => {
64585
+ if (existsSync3(join4(clone2.root, path)) || existsSync3(basePath(clone2.root, path))) {
64586
+ throw new Error(`Local file already exists: ${path}`);
64587
+ }
64588
+ });
64589
+ Object.entries(files).forEach(([path, content]) => {
64590
+ const workingPath = join4(clone2.root, path);
64591
+ mkdirSync3(dirname3(workingPath), { recursive: true });
64592
+ writeFileSync3(workingPath, content, { flag: "wx" });
64593
+ localFiles.push(workingPath);
64594
+ const pristinePath = basePath(clone2.root, path);
64595
+ mkdirSync3(dirname3(pristinePath), { recursive: true });
64596
+ writeFileSync3(pristinePath, content, { flag: "wx" });
64597
+ });
64598
+ const manifest = readManifest(clone2.root);
64599
+ manifest.resources[id] = {
64600
+ type: trackedType,
64601
+ name: name2,
64602
+ files: Object.keys(files),
64603
+ baseVersion: head.latestVersion,
64604
+ deployedVersion: head.deployedVersion
64605
+ };
64606
+ writeManifest(clone2.root, manifest);
64607
+ }
64608
+ } catch (error62) {
64609
+ const message = `Created ${id}, but local sync failed. Do not create it again. Preserve any existing local files, then run overai pull in the matching clone to recover (pull also syncs other resources). ${error62 instanceof Error ? error62.message : String(error62)}`;
64610
+ printResult(
64611
+ {
64612
+ ...result,
64613
+ localFilesWritten: localFiles.length > 0,
64614
+ localFiles,
64615
+ localSyncError: message
64616
+ },
64617
+ format2
64618
+ );
64619
+ throw new Error(message, { cause: error62 });
63365
64620
  }
63366
64621
  printResult(
63367
- { type, id, name: name2, workspaceId: type === "workspace" ? id : workspaceId },
63368
- resolveFormat(flagValue(args, "-o", "--output"))
64622
+ { ...result, localFilesWritten: localFiles.length > 0, localFiles },
64623
+ format2
63369
64624
  );
63370
64625
  };
63371
64626
 
63372
64627
  // src/commands/dataFile.ts
63373
- import { openAsBlob, writeFileSync as writeFileSync3 } from "fs";
64628
+ import { openAsBlob, writeFileSync as writeFileSync4 } from "fs";
63374
64629
  var runDataFile = async (args) => {
63375
64630
  const [operation, id, path] = args;
63376
64631
  if (id == null || !["upload", "download", "head"].includes(operation ?? "")) {
@@ -63391,7 +64646,7 @@ var runDataFile = async (args) => {
63391
64646
  }
63392
64647
  const response = operation === "head" ? await dataFile.head({ lines: integerFlag(args, "--lines", 50) }) : await dataFile.read();
63393
64648
  const bytes = Buffer.from(await response.arrayBuffer());
63394
- if (path != null && path !== "-") writeFileSync3(path, bytes);
64649
+ if (path != null && path !== "-") writeFileSync4(path, bytes);
63395
64650
  else process.stdout.write(bytes);
63396
64651
  };
63397
64652
 
@@ -63439,18 +64694,18 @@ var runDataStream = async (args) => {
63439
64694
  import { createHash as createHash3 } from "crypto";
63440
64695
  import { watch } from "fs";
63441
64696
  import { createServer as createServer2 } from "http";
63442
- import { dirname as dirname3, join as join5 } from "path";
64697
+ import { dirname as dirname4, join as join6 } from "path";
63443
64698
 
63444
64699
  // src/commands/sync.ts
63445
64700
  import {
63446
- existsSync as existsSync3,
64701
+ existsSync as existsSync4,
63447
64702
  lstatSync,
63448
- mkdirSync as mkdirSync3,
64703
+ mkdirSync as mkdirSync4,
63449
64704
  readFileSync as readFileSync5,
63450
64705
  unlinkSync,
63451
- writeFileSync as writeFileSync4
64706
+ writeFileSync as writeFileSync5
63452
64707
  } from "fs";
63453
- import { join as join4, resolve as resolve3 } from "path";
64708
+ import { join as join5, resolve as resolve3 } from "path";
63454
64709
 
63455
64710
  // src/gql.ts
63456
64711
  var gqlRequest = async (query, variables) => {
@@ -63519,39 +64774,39 @@ var runClone = async (args, isShallow = false) => {
63519
64774
  await getAccessToken();
63520
64775
  const workspace = await fetchWorkspace(workspaceId);
63521
64776
  const root = resolve3(dirArg ?? slugify3(workspace.name));
63522
- const cloneMarker = join4(root, ".overai", "clone-in-progress");
63523
- if (existsSync3(join4(root, ".overai", "manifest.json"))) {
64777
+ const cloneMarker = join5(root, ".overai", "clone-in-progress");
64778
+ if (existsSync4(join5(root, ".overai", "manifest.json"))) {
63524
64779
  throw new Error(
63525
64780
  `${root} is already a cloned workspace. Use \`overai pull\` to refresh.`
63526
64781
  );
63527
64782
  }
63528
- const partialCloneWorkspaceId = existsSync3(cloneMarker) ? readFileSync5(cloneMarker, "utf8").trim() : null;
64783
+ const partialCloneWorkspaceId = existsSync4(cloneMarker) ? readFileSync5(cloneMarker, "utf8").trim() : null;
63529
64784
  if (partialCloneWorkspaceId != null && partialCloneWorkspaceId !== workspaceId) {
63530
64785
  throw new Error(
63531
64786
  `${root} contains an incomplete clone of ${partialCloneWorkspaceId}. Retry that workspace or choose another directory.`
63532
64787
  );
63533
64788
  }
63534
- if (existsSync3(join4(root, "WORKSPACE.md")) && partialCloneWorkspaceId == null) {
64789
+ if (existsSync4(join5(root, "WORKSPACE.md")) && partialCloneWorkspaceId == null) {
63535
64790
  throw new Error(`${root} already contains WORKSPACE.md.`);
63536
64791
  }
63537
64792
  const pending = [{ workspace, root }];
63538
64793
  const seen = /* @__PURE__ */ new Set([workspace.id]);
63539
64794
  const plan = [];
63540
64795
  for (const entry of pending) {
63541
- const marker = join4(entry.root, ".overai", "clone-in-progress");
63542
- const completed = existsSync3(join4(entry.root, ".overai", "manifest.json"));
63543
- if (entry.root !== root && existsSync3(entry.root)) {
64796
+ const marker = join5(entry.root, ".overai", "clone-in-progress");
64797
+ const completed = existsSync4(join5(entry.root, ".overai", "manifest.json"));
64798
+ if (entry.root !== root && existsSync4(entry.root)) {
63544
64799
  if (lstatSync(entry.root).isSymbolicLink())
63545
64800
  throw new Error(`Refusing to clone into symbolic link ${entry.root}.`);
63546
- const savedId = completed ? readManifest(entry.root).workspaceId : existsSync3(marker) ? readFileSync5(marker, "utf8").trim() : null;
64801
+ const savedId = completed ? readManifest(entry.root).workspaceId : existsSync4(marker) ? readFileSync5(marker, "utf8").trim() : null;
63547
64802
  if (partialCloneWorkspaceId !== workspaceId || savedId !== entry.workspace.id)
63548
64803
  throw new Error(
63549
64804
  `${entry.root} already exists. Choose another clone directory.`
63550
64805
  );
63551
64806
  }
63552
- if (existsSync3(join4(entry.root, "workspaces")) && lstatSync(join4(entry.root, "workspaces")).isSymbolicLink())
64807
+ if (existsSync4(join5(entry.root, "workspaces")) && lstatSync(join5(entry.root, "workspaces")).isSymbolicLink())
63553
64808
  throw new Error(
63554
- `Refusing to clone through symbolic link ${join4(entry.root, "workspaces")}.`
64809
+ `Refusing to clone through symbolic link ${join5(entry.root, "workspaces")}.`
63555
64810
  );
63556
64811
  if (!completed) {
63557
64812
  const resourcesByAdapter = await Promise.all(
@@ -63580,7 +64835,7 @@ var runClone = async (args, isShallow = false) => {
63580
64835
  seen.add(child.id);
63581
64836
  pending.push({
63582
64837
  workspace: child,
63583
- root: join4(
64838
+ root: join5(
63584
64839
  entry.root,
63585
64840
  "workspaces",
63586
64841
  `${slugify3(child.name)}.${child.id}`
@@ -63589,14 +64844,14 @@ var runClone = async (args, isShallow = false) => {
63589
64844
  }
63590
64845
  }
63591
64846
  for (const entry of plan) {
63592
- mkdirSync3(join4(entry.root, ".overai"), { recursive: true });
63593
- writeFileSync4(
63594
- join4(entry.root, ".overai", "clone-in-progress"),
64847
+ mkdirSync4(join5(entry.root, ".overai"), { recursive: true });
64848
+ writeFileSync5(
64849
+ join5(entry.root, ".overai", "clone-in-progress"),
63595
64850
  `${entry.manifest.workspaceId}
63596
64851
  `
63597
64852
  );
63598
- writeFileSync4(
63599
- join4(entry.root, "WORKSPACE.md"),
64853
+ writeFileSync5(
64854
+ join5(entry.root, "WORKSPACE.md"),
63600
64855
  entry.description.length === 0 ? "" : `${entry.description.trimEnd()}
63601
64856
  `
63602
64857
  );
@@ -63618,7 +64873,7 @@ var runClone = async (args, isShallow = false) => {
63618
64873
  }
63619
64874
  for (const entry of plan.toReversed()) {
63620
64875
  writeManifest(entry.root, entry.manifest);
63621
- unlinkSync(join4(entry.root, ".overai", "clone-in-progress"));
64876
+ unlinkSync(join5(entry.root, ".overai", "clone-in-progress"));
63622
64877
  }
63623
64878
  process.stdout.write(
63624
64879
  `
@@ -63739,7 +64994,7 @@ var runPull = async (clone2) => {
63739
64994
  continue;
63740
64995
  }
63741
64996
  if (clone2 != null && Object.keys(remote.files).some(
63742
- (path) => !resource.files.includes(path) && existsSync3(join4(root, path))
64997
+ (path) => !resource.files.includes(path) && existsSync4(join5(root, path))
63743
64998
  )) {
63744
64999
  incomplete = true;
63745
65000
  process.stdout.write(
@@ -63760,7 +65015,7 @@ var runPull = async (clone2) => {
63760
65015
  }
63761
65016
  for (const remote of remotes) {
63762
65017
  if (manifest.resources[remote.id] != null) continue;
63763
- if (clone2 != null && Object.keys(remote.files).some((path) => existsSync3(join4(root, path)))) {
65018
+ if (clone2 != null && Object.keys(remote.files).some((path) => existsSync4(join5(root, path)))) {
63764
65019
  incomplete = true;
63765
65020
  process.stdout.write(
63766
65021
  `CONFLICT ${remote.name}: destination files already exist. Kept local.
@@ -64017,7 +65272,7 @@ var runDev = async (args) => {
64017
65272
  }, 100);
64018
65273
  };
64019
65274
  const watchedDirs = [
64020
- ...new Set(resource.files.map((f) => join5(root, dirname3(f))))
65275
+ ...new Set(resource.files.map((f) => join6(root, dirname4(f))))
64021
65276
  ];
64022
65277
  watchedDirs.forEach((dir) => watch(dir, notifyChange));
64023
65278
  const server = createServer2((req, res) => {
@@ -64215,13 +65470,13 @@ var runGraphql = async (args) => {
64215
65470
  };
64216
65471
 
64217
65472
  // src/commands/integration.ts
64218
- var USAGE3 = "Usage: overai integration configure <id> --config <json|@file|@-> | integration config <id> | integration secret set <id> <KEY> --value <@file|@-> | integration secret unset <id> <KEY>";
65473
+ var USAGE4 = "Usage: overai integration configure <id> --config <json|@file|@-> | integration config <id> | integration secret set <id> <KEY> --value <@file|@-> | integration secret unset <id> <KEY>";
64219
65474
  var runIntegration = async (args) => {
64220
65475
  const [operation] = args;
64221
65476
  if (operation === "secret") {
64222
65477
  const [, action, id2, key] = args;
64223
65478
  if (action !== "set" && action !== "unset" || id2 == null || key == null || key.startsWith("-")) {
64224
- throw new Error(USAGE3);
65479
+ throw new Error(USAGE4);
64225
65480
  }
64226
65481
  const integration2 = cliClient().integration(id2);
64227
65482
  if (action === "set") {
@@ -64250,7 +65505,7 @@ var runIntegration = async (args) => {
64250
65505
  }
64251
65506
  const [, id] = args;
64252
65507
  if (id == null || operation !== "configure" && operation !== "config") {
64253
- throw new Error(USAGE3);
65508
+ throw new Error(USAGE4);
64254
65509
  }
64255
65510
  const integration = cliClient().integration(id);
64256
65511
  if (operation === "configure") {
@@ -64612,12 +65867,12 @@ var runPrintablePage = async (args) => {
64612
65867
  };
64613
65868
 
64614
65869
  // src/commands/recursiveSync.ts
64615
- import { existsSync as existsSync4, lstatSync as lstatSync2, readdirSync } from "fs";
64616
- import { join as join7, relative } from "path";
65870
+ import { existsSync as existsSync5, lstatSync as lstatSync2, readdirSync } from "fs";
65871
+ import { join as join8, relative } from "path";
64617
65872
 
64618
65873
  // src/commands/versions.ts
64619
65874
  import { spawnSync } from "child_process";
64620
- import { join as join6 } from "path";
65875
+ import { join as join7 } from "path";
64621
65876
  var parseRef = (raw2) => {
64622
65877
  if (raw2 === "local" || raw2 === "base") return raw2;
64623
65878
  const [ref] = resultOfSync(() => parseVersionRef(raw2));
@@ -64759,7 +66014,7 @@ var runDiff = async (args, clone2) => {
64759
66014
  "--no-index",
64760
66015
  "--src-prefix=base/",
64761
66016
  "--dst-prefix=local/",
64762
- join6(".overai", "base", relPath),
66017
+ join7(".overai", "base", relPath),
64763
66018
  missing ? "/dev/null" : relPath
64764
66019
  ],
64765
66020
  { stdio: "inherit", cwd: root }
@@ -64791,15 +66046,15 @@ var runRecursiveSync = async (command2, args) => {
64791
66046
  const local = /* @__PURE__ */ new Map([[origin.manifest.workspaceId, origin]]);
64792
66047
  const folders = [origin];
64793
66048
  for (const folder of folders) {
64794
- const container = join7(folder.root, "workspaces");
64795
- if (!existsSync4(container)) continue;
66049
+ const container = join8(folder.root, "workspaces");
66050
+ if (!existsSync5(container)) continue;
64796
66051
  if (lstatSync2(container).isSymbolicLink()) throw new Error(`Refusing symbolic link ${container}.`);
64797
66052
  for (const name2 of readdirSync(container)) {
64798
- const root = join7(container, name2);
66053
+ const root = join8(container, name2);
64799
66054
  const stat2 = lstatSync2(root);
64800
66055
  if (stat2.isSymbolicLink()) throw new Error(`Refusing symbolic link ${root}.`);
64801
- if (!stat2.isDirectory() || !existsSync4(join7(root, ".overai/manifest.json"))) continue;
64802
- if (lstatSync2(join7(root, ".overai")).isSymbolicLink() || lstatSync2(join7(root, ".overai/manifest.json")).isSymbolicLink())
66056
+ if (!stat2.isDirectory() || !existsSync5(join8(root, ".overai/manifest.json"))) continue;
66057
+ if (lstatSync2(join8(root, ".overai")).isSymbolicLink() || lstatSync2(join8(root, ".overai/manifest.json")).isSymbolicLink())
64803
66058
  throw new Error(`Refusing linked clone state in ${root}.`);
64804
66059
  const manifest = readManifest(root);
64805
66060
  if (manifest.organizationId !== origin.manifest.organizationId || local.has(manifest.workspaceId))
@@ -64822,7 +66077,7 @@ var runRecursiveSync = async (command2, args) => {
64822
66077
  if (seen.has(child.id) || child.organizationId !== origin.manifest.organizationId)
64823
66078
  throw new Error(`Invalid workspace hierarchy at ${child.id}. No workspaces synced.`);
64824
66079
  seen.add(child.id);
64825
- remote.push({ workspace: child, parentId: entry.workspace.id, root: local.get(child.id)?.root ?? join7(entry.root, "workspaces", `${slugify3(child.name)}.${child.id}`) });
66080
+ remote.push({ workspace: child, parentId: entry.workspace.id, root: local.get(child.id)?.root ?? join8(entry.root, "workspaces", `${slugify3(child.name)}.${child.id}`) });
64826
66081
  }
64827
66082
  }
64828
66083
  const failures = [];
@@ -64849,7 +66104,7 @@ Workspace ${label} (${entry.workspace.id})
64849
66104
  if (clone2 == null) {
64850
66105
  if (entry.parentId != null && !available.has(entry.parentId))
64851
66106
  throw new Error("Parent workspace could not be cloned; skipping this child.");
64852
- if (existsSync4(entry.root))
66107
+ if (existsSync5(entry.root))
64853
66108
  throw new Error("Destination already exists, possibly from an incomplete clone. Local files kept; move the folder aside before retrying.");
64854
66109
  await runClone([entry.workspace.id, entry.root], true);
64855
66110
  available.add(entry.workspace.id);
@@ -65030,12 +66285,12 @@ var runRemote = async (args) => {
65030
66285
 
65031
66286
  // src/commands/update.ts
65032
66287
  import { spawnSync as spawnSync2 } from "child_process";
65033
- import { existsSync as existsSync5, readFileSync as readFileSync7 } from "fs";
66288
+ import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
65034
66289
  import { fileURLToPath } from "url";
65035
66290
  var PACKAGE_NAME = "@overaihq/cli";
65036
66291
  var packageRootUrl = () => {
65037
66292
  for (const up of ["..", "../..", "../../.."]) {
65038
- if (existsSync5(new URL(`${up}/package.json`, import.meta.url))) {
66293
+ if (existsSync6(new URL(`${up}/package.json`, import.meta.url))) {
65039
66294
  return new URL(`${up}/`, import.meta.url);
65040
66295
  }
65041
66296
  }
@@ -65061,7 +66316,7 @@ var detectInstaller = () => {
65061
66316
  };
65062
66317
  var runUpdate = async () => {
65063
66318
  const current = cliVersion();
65064
- if (existsSync5(new URL("src", packageRootUrl()))) {
66319
+ if (existsSync6(new URL("src", packageRootUrl()))) {
65065
66320
  process.stdout.write(
65066
66321
  "Development build \u2014 rebuild from the repo instead of updating from npm.\n"
65067
66322
  );
@@ -65088,7 +66343,7 @@ var runUpdate = async () => {
65088
66343
  const cmp = compareVersions(current, latest);
65089
66344
  if (cmp > 0) {
65090
66345
  process.stdout.write(
65091
- `overai ${current} is ahead of latest (${latest}) \u2014 likely a @next install; nothing to do.
66346
+ `overai ${current} is ahead of latest (${latest}) \u2014 likely an unreleased build; nothing to do.
65092
66347
  `
65093
66348
  );
65094
66349
  return;
@@ -65730,6 +66985,16 @@ automatic conflict recovery \u2014 incorporate the remote changes and retry.
65730
66985
 
65731
66986
  ## Organization context
65732
66987
 
66988
+ \`overai create <type>\` writes the new resource's files when run inside its
66989
+ matching workspace clone, including with an explicit \`--workspace\`. It fetches
66990
+ only that resource and leaves other local edits untouched. Outside a matching
66991
+ clone it creates remotely without creating local files or a clone. The result's
66992
+ \`localFilesWritten\` and \`localFiles\` fields report writes and absolute paths.
66993
+ If local sync fails after creation, the result includes the created id and
66994
+ \`localSyncError\`. Preserve any existing destination files before running
66995
+ \`overai pull\` in the matching clone; pull also syncs other resources. Do not
66996
+ create the resource again.
66997
+
65733
66998
  Commands in a clone select its org automatically. Outside a clone, put
65734
66999
  --org <ref> before the command (overai --org acme sql ...), or set
65735
67000
  OVERAI_ORG_ID for a whole shell. A ref is an org id, a local alias, or
@@ -65784,6 +67049,14 @@ around a clone-sync mismatch; use the correct workspace folder.
65784
67049
  - \`overai automation run <id> --input k=v\` starts a real run of the DEPLOYED
65785
67050
  version (fire-and-forget, returns automationRunId). Run read-only paths
65786
67051
  first; verify before deploying mutating changes.
67052
+ - Add \`--wait --timeout 300\` to start and wait, or use
67053
+ \`overai automation wait <runId> --timeout 300\` for an existing run.
67054
+ Waiting defaults to 300 seconds and returns completion metadata, not node
67055
+ outputs. The run ID prints to stderr immediately; the final summary prints
67056
+ to stdout. Failure, cancellation, and timeout exit nonzero. Timeout or Ctrl-C
67057
+ stops waiting without cancelling the automation. An in-progress credential
67058
+ refresh may finish before the CLI exits. Use \`state\` separately
67059
+ for best-effort node outputs.
65787
67060
  - Console logs from a page are visible in the webapp editor.
65788
67061
 
65789
67062
  ## SQL access
@@ -65801,6 +67074,10 @@ around a clone-sync mismatch; use the correct workspace folder.
65801
67074
  supplied and permitted by organization policy.
65802
67075
  - Give each service its own credential. List, rotate, or revoke it with
65803
67076
  \`overai sql credential list|rotate|revoke\`.
67077
+ - A database outside OverAI (e.g. a customer's SQL Server behind their
67078
+ self-hosted runner) is reached through its node's action, not \`sql\`:
67079
+ \`overai action run <workspaceId> MS_SQL_DATABASE_TRANSACTION_REMOTE --runner <runnerId> -\`
67080
+ with the node's inputs as a JSON object on stdin.
65804
67081
 
65805
67082
  ## From a script
65806
67083
 
@@ -66055,7 +67332,8 @@ A node is "runnable" (can be executed) when ALL of these conditions are met:
66055
67332
  1. **Debug mode is enabled** - Users must enable debug mode to run nodes step-by-step
66056
67333
  2. **Parent resolution** - One of the following must be true:
66057
67334
  - The node has NO parents (unconnected nodes can always run in debug mode)
66058
- - ALL parent nodes have executed and resolved to the correct handle that connects to this node
67335
+ - ALL parent nodes have executed and resolved to the handle that connects to this node
67336
+ - For an **ALWAYS** node: ALL parent nodes have finished, whatever the outcome (success, failure, errored, or skipped), and at least one of them was not skipped
66059
67337
  3. **Loop context** (if applicable) - If the node is inside a loop:
66060
67338
  - The loop node must be in "loop" state (actively iterating)
66061
67339
  - The loop completes when it reaches maxIterations or exhausts items
@@ -66068,6 +67346,15 @@ Nodes connect via "handles" which determine execution flow:
66068
67346
 
66069
67347
  When a parent executes, it resolves to one of these handles. Child nodes only become runnable when their parent resolves to the handle they're connected to.
66070
67348
 
67349
+ ### Skipped Nodes
67350
+ In a deployed run, every node ends in one of: success, failure, errored, or skipped. Skipped is derived from the parents' outcomes wherever state is shown, never recorded, so it appears in run history and while stepping in debug mode: once you run one lane of an IF, the other lane reads as skipped and an ALWAYS after the IF becomes runnable.
67351
+ - A node is **skipped** as soon as a parent makes it unreachable: the parent finished as skipped, or finished on a different handle than the one connecting to this node. An **errored** parent skips all its children except an ALWAYS, because no handle is named errored.
67352
+ - Skips propagate: a skipped node's children are skipped in turn, down to the end of the lane.
67353
+ - **ALWAYS is the join.** It runs once every parent has finished \u2014 succeeded, failed, errored, or skipped \u2014 and at least one of them ran, so an ALWAYS fed by both lanes of an IF runs after the taken lane finishes.
67354
+ - An ALWAYS whose every parent was skipped sits inside an untaken lane: it is skipped too, and the skip keeps propagating through it.
67355
+ - A parent connected through a loop's **loop** handle never causes a skip, and an ALWAYS entered from the loop counts that as a parent that ran. Inside a loop, each iteration applies these rules on its own.
67356
+ - Skipped nodes do not make a run errored. A run with skipped nodes and no errored nodes succeeds.
67357
+
66071
67358
  ### Special Node Types
66072
67359
  - **Event nodes** (timer, webhook, emailReceived, fileUpload): Can always run if debug mode is on. These require a payload to simulate the trigger \u2014 see "Simulating Event Nodes" below.
66073
67360
  - **Loop nodes** (loopArray, loopRange): Execute iteratively, maintaining "loop" state until complete, then always resolve to the **success** handle \u2014 even if some or all iterations errored. A failed iteration does not stop the loop or take a failure path. To react to per-iteration failures, handle errors on the nodes inside the loop, or inspect \`iterations\`. (If any node errored, the run is still marked errored overall, but the loop's success handle still runs.)
@@ -66112,9 +67399,13 @@ var automationsDoc = {
66112
67399
  id: "automations",
66113
67400
  summary: "Automations overview: node model, IF vs legacy IF_ELSE, debugging, webhook/email/run endpoints, SEND_EMAIL, code nodes, Excel input, elements graph rules.",
66114
67401
  body: `
67402
+ ## Error alerts
67403
+
67404
+ When configuring alerts through GraphQL, \`emailRecipients\` is an object: \`{"recipients": ["alerts@example.com"]}\`, not a bare list. Omit \`recipients\` or use an empty array to send no email. Entries must be strings; additional object fields are preserved.
67405
+
66115
67406
  ## Working with Automations
66116
67407
 
66117
- Automations are step-by-step workflows. Automations are initiated by a trigger (timer, email, webhook, file upload) and then proceed to run additional steps or nodes such as \`TYPESCRIPT_CODE\`, \`IF\`, \`SEND_EMAIL\`, \`PARSE_EXCEL\`, \`GENERATE_OBJECT\`, \`GENERATE_TEXT\`. An automation node will run once all of its parent nodes have resolved. Control flow is done through \`LOOP_ARRAY\` and \`LOOP_RANGE\` nodes along with \`IF\`, \`ERROR\`, and \`ALWAYS\`. The \`ALWAYS\` node helps to bring different branches of an \`IF\` node back together. To branch on a condition use the \`IF\` node \u2014 its only input is \`expression\`, a Liquid predicate that renders \`true\` or \`false\` (e.g. \`{{ S1.result.count > 0 }}\`). The \`IF_ELSE\` node is legacy and deprecated: never add it, and replace any existing \`IF_ELSE\` node with an \`IF\` node.
67408
+ Automations are step-by-step workflows. Automations are initiated by a trigger (timer, email, webhook, file upload) and then proceed to run additional steps or nodes such as \`TYPESCRIPT_CODE\`, \`IF\`, \`SEND_EMAIL\`, \`PARSE_EXCEL\`, \`GENERATE_OBJECT\`, \`GENERATE_TEXT\`. An automation node will run once all of its parent nodes have resolved. Control flow is done through \`LOOP_ARRAY\` and \`LOOP_RANGE\` nodes along with \`IF\`, \`ERROR\`, and \`ALWAYS\`. The \`ALWAYS\` node helps to bring different branches of an \`IF\` node back together. The untaken branch of an \`IF\` reads as skipped, and \`ALWAYS\` runs once every parent has finished or been skipped and at least one of them ran; an \`ALWAYS\` whose every parent was skipped is skipped too. To branch on a condition use the \`IF\` node \u2014 its only input is \`expression\`, a Liquid predicate that renders \`true\` or \`false\` (e.g. \`{{ S1.result.count > 0 }}\`). The \`IF_ELSE\` node is legacy and deprecated: never add it, and replace any existing \`IF_ELSE\` node with an \`IF\` node.
66118
67409
 
66119
67410
  Besides full runs, an automation can be worked on piece by piece:
66120
67411
  - simulate the trigger event in debug mode (same trigger-specific inputs as a full run \u2014 e.g. \`fileUrl\`, \`method\`/\`body\` \u2014 but it only seeds the EVENT node, without starting a run),
@@ -66134,6 +67425,14 @@ Important nodes:
66134
67425
  - \`SEND_EMAIL\` sends an email from \`noreply@overai.com\` without needing a 3rd party integration. Inside an automation always use this node rather than sending from a \`TYPESCRIPT_CODE\` node \u2014 it is visible on the canvas, configurable without reading code, and re-runnable on its own in debug. Outside an automation (a Function), send with \`env.EMAIL.send(...)\`.
66135
67426
  - \`TYPESCRIPT_CODE\` runs a TypeScript module in a Deno sandbox. Use \`export default async function step(state: StepState, ctx: StepContext, env: Env) { \u2026 }\`; the return value is the node output. Annotate every parameter you declare \u2014 module code is checked with noImplicitAny, so an unannotated one is an error. Read upstream outputs with \`await state.get("S1")\` (fetched on first read, cached for that node's execution; \`undefined\` for a skipped step; rejects for an id not visible from the node) \u2014 \`state.S1\` does not exist. Keep each code node small and focused \u2014 when the logic grows, break it across multiple nodes and let the automation handle orchestration rather than writing one large block.
66136
67427
 
67428
+ ## Querying Amazon Athena
67429
+
67430
+ Use \`AWS_ATHENA_RUN_QUERY\` with an existing AWS integration (credentials and region). Set database/workgroup on the action; catalog defaults to \`AwsDataCatalog\` and workgroup to \`primary\`. Leave the S3 results location empty to use workgroup storage, including managed results. Enforced workgroup settings take precedence. Grant Athena query/status/results/cancel permissions plus catalog, source and results access (and KMS/Lake Formation where applicable).
67431
+
67432
+ The action accepts one SQL statement, including writes allowed by AWS permissions. Liquid interpolation is not parameter binding; never interpolate untrusted text into SQL. Results are \`rowData\` arrays aligned with \`columns\`; cells remain strings or null. CSV mode streams to OverAI storage and returns \`fileUrl\`, with \`rowData: null\`; it includes headers and writes null as an empty field. Both modes return column metadata, \`queryId\`, \`bytesScanned\` and \`truncated\`.
67433
+
67434
+ Row limit defaults to 0 (unlimited) and applies only to returned rows, not scanned data. Check \`truncated\` before treating output as complete. Inline output is capped at 150 MiB of serialized rows; use CSV for larger results. Set SQL filters/partitions and workgroup scan limits to control costs. The default 60-second timeout covers submission/execution, not download. Cancellation is attempted on timeout or runner abort, but is not guaranteed and does not roll back writes. Re-running a write can repeat it. Customer-hosted runners need a version that includes this action.
67435
+
66137
67436
  ## Excel inside automations
66138
67437
 
66139
67438
  When an automation needs to read an uploaded \`.xlsx\`/\`.xls\` workbook at runtime (e.g. a \`fileUpload\` event), prefer the \`parseExcel\` node over writing TypeScript. It pulls every sheet in one shot and avoids re-deriving brittle parsing code each run.
@@ -66482,13 +67781,13 @@ Generate text or structured objects using LLMs:
66482
67781
  // Generate text. Functions always use non-streaming under the hood, so
66483
67782
  // invalid-PDF / prompt-too-long / rate-limit errors throw with a classified
66484
67783
  // message instead of returning an empty string.
66485
- const text = await env.AI.generateText("Summarize this document", { system: "You are a helpful assistant", model: "claude-sonnet-4-5" });
67784
+ const text = await env.AI.generateText("Summarize this document", { system: "You are a helpful assistant", model: "claude-sonnet-5-5" });
66486
67785
 
66487
67786
  // Generate structured object
66488
67787
  const result = await env.AI.generateObject(
66489
67788
  "Extract the key information",
66490
67789
  { type: "object", properties: { title: { type: "string" }, summary: { type: "string" } } },
66491
- { model: "claude-sonnet-4-5" }
67790
+ { model: "claude-sonnet-5-5" }
66492
67791
  );
66493
67792
 
66494
67793
  // With file attachments \u2014 pass Blobs of images or PDFs alongside the prompt
@@ -66509,7 +67808,9 @@ const transcript = await env.AI.speechToText(fileUrl, { language: "en" });
66509
67808
 
66510
67809
  **Attachment types:** \`image/png\`, \`image/jpeg\`, \`image/gif\`, \`image/webp\`, \`application/pdf\`. The Blob's \`type\` is forwarded as the mediaType \u2014 make sure it's set (e.g. when constructing via \`new Blob([buf], { type: "image/png" })\`).
66511
67810
 
66512
- **generateObject schema limit:** Anthropic models reject schemas with more than 16 nullable or union-typed fields. Prefer non-nullable fields, split into multiple calls, or switch model to \`gpt-5.6-terra\` or \`gemini-3.1-pro-preview\` for larger schemas. Same applies to the agent \`generateObject\` below.
67811
+ **Output budgets:** Pass \`maxOutputTokens\` in the options for \`env.AI.generateText\` or \`env.AI.generateObject\` to set a positive integer output limit. Omitted means 16,000 tokens per model call, including each web-search step; this is not a total workflow budget. Provider/model limits still apply, and reasoning tokens can consume the budget. Larger budgets allow longer answers and greater spend. Truncated structured output fails with an error: increase the budget within the model's limit or request a smaller result.
67812
+
67813
+ **generateObject schema limit:** Anthropic models reject schemas with more than 16 nullable or union-typed fields. Prefer non-nullable fields, split into multiple calls, or switch model to ${LARGE_SCHEMA_MODELS.map((model) => `\`${model}\``).join(" or ")} for larger schemas. Same applies to the agent \`generateObject\` below.
66513
67814
 
66514
67815
  ## Agent Access
66515
67816
 
@@ -66673,6 +67974,8 @@ Each version has a timeout (ms, default 30000) and memory limit (MB, default 512
66673
67974
 
66674
67975
  ## Error alerts
66675
67976
 
67977
+ When configuring alerts through GraphQL, \`emailRecipients\` is an object: \`{"recipients": ["alerts@example.com"]}\`, not a bare list. Omit \`recipients\` or use an empty array to send no email. Entries must be strings; additional object fields are preserved. Functions allow at most 20 recipients.
67978
+
66676
67979
  A function can email recipients when it breaks: the handler throws, times out, or crashes (non-zero exit, output over the size limit, malformed response). A status the handler returns, even a 4xx or 5xx, never alerts \u2014 so catching an error and returning \`new Response(..., { status: 500 })\` silences it. After one alert, later failures send nothing until the cooldown ends (60 minutes by default; 0 sends on every failure). Runs from the function editor never alert, including your \`runFunction\` calls; calls from automations, pages, agents, and HTTP do. The user turns alerts on and picks recipients from the "Alerts" chip in the function's status bar.
66677
67980
  `
66678
67981
  };
@@ -66894,7 +68197,7 @@ var platformApiDoc = {
66894
68197
 
66895
68198
  OverAI's own data model \u2014 organizations, workspaces, identities, workspace membership and roles (\`workspaceIdentityRole\`), automations, functions, pages, integrations, runs, and more \u2014 is queryable over a **v2 GraphQL API** at \`<api_base_url>/v2/graphql\`. Reach for it whenever a build needs to read or manage the platform itself (e.g. "export every user and their workspace permissions", "list automations that failed today"), not just the user's own tables. It is available anywhere the env SDK runs \u2014 code nodes, functions, and pages.
66896
68199
 
66897
- - **From env SDK**: \`env.GRAPHQL.request(query, vars)\`. It authenticates as the **current workspace** and is row-level-security scoped \u2014 it sees only that workspace's data, and an out-of-scope read returns fewer rows with **no error**. Good for reading the workspace you're in; not for org-wide data.
68200
+ - **From env SDK**: \`env.GRAPHQL.request(query, vars)\`. It authenticates with the running code's own token \u2014 the workspace in code nodes and functions, the page's selected authorization mode (workspace or viewer) in pages \u2014 and is row-level-security scoped: an out-of-scope read returns fewer rows with **no error**. Good for reading the workspace you're in; not for org-wide data.
66898
68201
  - **For org-wide / cross-workspace reads**, \`env.GRAPHQL\` is not enough. \`fetch()\` \`<api_base_url>/v2/graphql\` directly with an **org-scoped access token** as \`Authorization: Bearer <token>\`. Keep the token out of code: store it in an HTTP integration and read it with \`env.IN<id>.get()\`. Sandboxed \`fetch()\` sends your headers **verbatim** \u2014 nothing rewrites auth headers, and no action runner is involved (runners are only for endpoints the cloud can't reach, e.g. on-prem).
66899
68202
  - It is **v2 PostGraphile**, not the legacy Hasura API: **cursor** pagination (\`first\`/\`after\` + \`pageInfo { hasNextPage endCursor }\`) and connection shapes (\`{ nodes { ... } }\`), never \`limit\`/\`offset\`.
66900
68203
  - The schema is large, so **do not guess field names \u2014 introspect on demand** (introspection is enabled). List top-level queries with \`{ __schema { queryType { fields { name } } } }\`; inspect one type with \`{ __type(name: "Workspace") { fields { name type { name kind ofType { name kind } } } } }\`. Introspect the exact type before writing the query.
@@ -67338,7 +68641,7 @@ declare global {
67338
68641
  /** Filenames over 128 characters are shortened in the middle, preserving both ends. */
67339
68642
  const uploadFile: (file: File, filename: string, disableRandomPrefix?: boolean) => Promise<string>;
67340
68643
  const presignFile: (fileUrl: string, expiresIn?: number) => Promise<string>;
67341
- const graphqlRequest: (document: string, variables?: Record<string, unknown>) => Promise<Record<string, unknown>>;
68644
+ const graphqlRequest: (document: string, variables?: Record<string, unknown>) => Promise<unknown>; // POSTs to the v2 API (/v2/graphql) with the page's own token, so it sees what the page's authorization mode grants; Hasura-dialect documents are not accepted. Resolves to { data, errors }.
67342
68645
  /** @deprecated Use useLLMText, useLLMObject, or useLLMChat instead. */
67343
68646
  const llmCompletion: (message: string, systemPrompt?: string, model?: string) => Promise<Record<string, unknown>>;
67344
68647
  const callFunction: (functionId: string, options?: { body?: unknown; method?: string; query?: Record<string, string>; path?: string }) => Promise<Response>;
@@ -67438,6 +68741,8 @@ If the warning gives a listed function's spelling, use that spelling instead of
67438
68741
 
67439
68742
  The \`env\` proxy matches the API available in Functions and Automations \u2014 the same code works in all three.
67440
68743
 
68744
+ Generic AI calls and hooks (generateText, generateObject, useLLMText, useLLMObject, useLLMChat, and env.AI) accept \`maxOutputTokens\`: a positive integer, default 16,000. It limits each model call, including each web-search step, not the whole conversation. Provider/model limits still apply; reasoning can consume the budget. Larger budgets allow longer answers and greater spend. Truncated structured output fails: increase the budget within the model's limit or request a smaller result. Text/chat keep their existing partial-output behavior.
68745
+
67441
68746
  For AI in React components, prefer the reactive hooks (useLLMText, useLLMObject, useLLMChat, useAgentChat) \u2014 they re-render as the response streams in. In event handlers or imperative code, call generateText/generateObject (or env.AI.*) directly.
67442
68747
  useAgentChat persists every turn server-side for a signed-in viewer: pass a conversationId to resume a past thread (full transcript, tool calls included, loads automatically), or omit it to start fresh and read the new thread's id off the returned conversationId. useAgentConversations(agentId) backs a history sidebar \u2014 call its refresh() after a first send so the newly created thread appears. listAgentConversations, getAgentConversationMessages, createAgentConversation, renameAgentConversation, and deleteAgentConversation are the imperative equivalents. On a public page none of that persistence exists (see Public pages) \u2014 useAgentChat still streams, holding the transcript in memory.
67443
68748
  All AI functions/hooks accept optional File[] attachments for images/PDFs, and webSearch: true to enable web search.
@@ -67756,8 +69061,18 @@ Archived workspaces are hidden from the workspace tree but are not deleted. Arch
67756
69061
 
67757
69062
  Individual resources (pages, automations, functions, tablespaces, integrations, \u2026) can likewise be archived \u2014 there is no hard delete. An archived resource disappears from the side navigation, from all listings and search, but its data is intact \u2014 except an integration's credentials, which archiving deletes, so a restored integration must be reconnected before anything can use it. When the user says a resource is missing or asks where it went, check archived resources before concluding it doesn't exist, and offer to unarchive it. In the UI, archived resources are listed on the Workspace Overview page under the **\u22EF menu \u2192 Show archived resources**, where each row's menu offers **Unarchive**.
67758
69063
 
69064
+ Workspace managers can rename a workspace by right-clicking its name in the side nav and choosing **Rename**, or by clicking the name at the top of the workspace page.
69065
+
67759
69066
  Workspace managers can duplicate an active workspace subtree by right-clicking its name in the side nav. The copy uses deployed resource versions, disables automations, and never copies run history or OAuth tokens. Direct grants, non-OAuth integration credentials, and TableSpace rows default on; managers can omit either set of credentials or choose schema-only TableSpaces. Editors and managers can also right-click one TableSpace to duplicate its schemas and optionally its rows. The modal shows progress for each copy phase and reports only items that unexpectedly failed.
67760
69067
 
69068
+ On the People page, open a person and click ? beside a workspace to see the direct grant\u2019s creator and creation date, or its parent workspace for inherited access. This is original grant attribution, not necessarily the inviter or the actor behind later role changes. If the record names its recipient as creator, the popover shows only the date; use access history for the recorded source and actor.
69069
+
69070
+ ## Access and workspace history
69071
+
69072
+ Organization admins can open a person on the People page and choose **View access history** for their organization and workspace access changes. The People page\u2019s **\u22EF** menu \u2192 **Organization history** shows history across the organization. Workspace managers and organization admins can open the workspace\u2019s **\u22EF** menu \u2192 **Audit history** for its member access, resource, sharing, and parent-workspace changes. **Filters** narrow the history by **Change type**, **Action**, a **From**/**Through** date range, **Person ID**, or **Resource ID**. Related changes share an operation and are grouped together, without exposing other workspaces the viewer cannot manage.
69073
+
69074
+ Entries preserve the actor, source, timestamp, and before/after values. A baseline records existing state when logging begins; it is not proof of earlier roles or an original organization inviter. Unknown attribution stays unknown. History is retained until the organization is deleted. The audit history records access-relevant events only: creation, deletion, workspace moves, sharing, parent links, and access-setting changes. Resource saves and deployments stay in version history.
69075
+
67761
69076
  ## Public Workspaces
67762
69077
 
67763
69078
  A workspace can be marked **public** (Managers only, by clicking the visibility chip next to the workspace name on the Workspace Overview page). When public, every web page in it is reachable at \`/public/<webPageId>\` without a sign-in \u2014 the visitor's iframe runs against an ephemeral workspace token that can read and write every table space the workspace reaches and call its functions, automations and agents. The visitor is not a member; they just see the page.
@@ -68292,11 +69607,11 @@ var runSql = async (args) => {
68292
69607
  };
68293
69608
 
68294
69609
  // src/commands/storage.ts
68295
- import { openAsBlob as openAsBlob3, writeFileSync as writeFileSync5 } from "fs";
68296
- var USAGE4 = "Usage: overai storage upload <path> [--no-random-prefix] | storage download <key> [path|-] | storage exists <key> | storage head <key> [--lines N] | storage presign <key> [--expires-in <seconds>]";
69610
+ import { openAsBlob as openAsBlob3, writeFileSync as writeFileSync6 } from "fs";
69611
+ var USAGE5 = "Usage: overai storage upload <path> [--no-random-prefix] | storage download <key> [path|-] | storage exists <key> | storage head <key> [--lines N] | storage presign <key> [--expires-in <seconds>]";
68297
69612
  var runStorage = async (args) => {
68298
69613
  const [operation, first2, second] = args;
68299
- if (operation == null) throw new Error(USAGE4);
69614
+ if (operation == null) throw new Error(USAGE5);
68300
69615
  const storage = cliClient().storage;
68301
69616
  if (operation === "upload") {
68302
69617
  if (first2 == null || first2 === "-") {
@@ -68314,11 +69629,11 @@ var runStorage = async (args) => {
68314
69629
  `);
68315
69630
  return;
68316
69631
  }
68317
- if (first2 == null) throw new Error(USAGE4);
69632
+ if (first2 == null) throw new Error(USAGE5);
68318
69633
  if (operation === "download") {
68319
69634
  const response = await storage.download(first2);
68320
69635
  const bytes = Buffer.from(await response.arrayBuffer());
68321
- if (second != null && second !== "-") writeFileSync5(second, bytes);
69636
+ if (second != null && second !== "-") writeFileSync6(second, bytes);
68322
69637
  else process.stdout.write(bytes);
68323
69638
  return;
68324
69639
  }
@@ -68350,7 +69665,7 @@ var runStorage = async (args) => {
68350
69665
  );
68351
69666
  return;
68352
69667
  }
68353
- throw new Error(USAGE4);
69668
+ throw new Error(USAGE5);
68354
69669
  };
68355
69670
 
68356
69671
  // src/commands/workspace.ts
@@ -68480,7 +69795,7 @@ var shareResource = async (args) => {
68480
69795
  process.stdout.write(`Shared ${what} from ${fromId} into ${to}.
68481
69796
  `);
68482
69797
  };
68483
- var USAGE5 = `Usage:
69798
+ var USAGE6 = `Usage:
68484
69799
  overai workspace list [--archived] [--limit <n>]
68485
69800
  overai workspace show [workspaceId]
68486
69801
  overai workspace users [workspaceId]
@@ -68518,13 +69833,13 @@ var runWorkspace = async (args) => {
68518
69833
  await shareResource(args.slice(1));
68519
69834
  return;
68520
69835
  }
68521
- process.stderr.write(USAGE5);
69836
+ process.stderr.write(USAGE6);
68522
69837
  process.exitCode = 1;
68523
69838
  };
68524
69839
 
68525
69840
  // src/updateCheck.ts
68526
69841
  import { request as httpsRequest } from "https";
68527
- import { existsSync as existsSync6 } from "fs";
69842
+ import { existsSync as existsSync7 } from "fs";
68528
69843
  import { fileURLToPath as fileURLToPath2 } from "url";
68529
69844
  var CHECK_TIMEOUT_MS = 2e3;
68530
69845
  var fetchLatestVersion = (signal) => {
@@ -68565,7 +69880,7 @@ var startUpdateCheck = () => {
68565
69880
  const controller = new AbortController();
68566
69881
  const promise2 = (async () => {
68567
69882
  try {
68568
- if (existsSync6(new URL("src", packageRootUrl()))) return null;
69883
+ if (existsSync7(new URL("src", packageRootUrl()))) return null;
68569
69884
  const path = fileURLToPath2(import.meta.url);
68570
69885
  if (path.includes("_npx") || path.includes("dlx-")) return null;
68571
69886
  const body = await fetchLatestVersion(controller.signal);
@@ -68633,7 +69948,7 @@ Usage:
68633
69948
  agent, integration, action_runner), or
68634
69949
  one resource, into a direct child
68635
69950
  overai create <type> [--workspace <id>] [--name <name>]
68636
- Create a complete, usable resource
69951
+ Create a resource; write its files in a matching clone
68637
69952
  overai resource show <id> Show metadata for any resource
68638
69953
  overai resource update <id> [...] Edit metadata, state, or archive status
68639
69954
  overai resource move <id> --to <workspaceId> [--dry-run]
@@ -68698,7 +70013,9 @@ Workspace sync (edit resources as local files):
68698
70013
  Automations:
68699
70014
  overai automation runs <automationId> [--limit N] [--failed] [--running]
68700
70015
  overai automation state <runId> [--node <variableId>]
68701
- overai automation run <automationId> [--input k=v ... | -]
70016
+ overai automation run <automationId> [--input k=v ... | -] [--wait] [--timeout seconds]
70017
+ overai automation wait <runId> [--timeout seconds]
70018
+ Wait up to 300 seconds by default. Timeout or Ctrl-C stops waiting, not the run.
68702
70019
 
68703
70020
  Other resources:
68704
70021
  overai agent generate <agentId> --message <text|@file|@->
@@ -68729,6 +70046,13 @@ Self-hosted runners (enabling remote access stays on-device \u2014 not a CLI act
68729
70046
  overai runner upload <runnerId> --path '<path>'
68730
70047
  overai runner list [workspaceId]
68731
70048
 
70049
+ Actions (one automation node's action, outside any automation):
70050
+ overai action run <workspaceId> <ACTION_ID> [--runner <runnerId>] [--input k=v ... | -]
70051
+ ACTION_ID is a node type, e.g.
70052
+ MS_SQL_DATABASE_TRANSACTION_REMOTE; no
70053
+ --runner uses the org's cloud runner.
70054
+ - reads a JSON object of inputs from stdin
70055
+
68732
70056
  Arguments that carry a document \u2014 the graphql query and --variables, the sql
68733
70057
  statement, runner --code \u2014 also take @<file> to read it from disk, or @- to read
68734
70058
  it from stdin. Prefer that for anything long or quote-heavy: an inline value is
@@ -69006,6 +70330,9 @@ var run = async () => {
69006
70330
  case "runner":
69007
70331
  await runRunner(rest);
69008
70332
  return;
70333
+ case "action":
70334
+ await runAction2(rest);
70335
+ return;
69009
70336
  case "update":
69010
70337
  case "upgrade":
69011
70338
  await runUpdate();