@maxceem/agw 0.1.9 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +4 -1
  2. package/dist/agw.mjs +685 -149
  3. package/package.json +14 -2
package/README.md CHANGED
@@ -88,7 +88,10 @@ The whole documentation is also published as
88
88
  [agents.md](https://docs.appaigateway.com/agents.md).
89
89
 
90
90
  **Output.** Add `--json` for exactly one JSON document on stdout; progress goes
91
- to stderr. Add `--no-input` to fail instead of prompting.
91
+ to stderr. Without `--json`, output is plain text and a failure is printed to
92
+ stderr, so stdout carries only the command's own output. Colour is used only
93
+ when the destination is a terminal, and `NO_COLOR` turns it off. Add
94
+ `--no-input` to fail instead of prompting.
92
95
 
93
96
  ```json
94
97
  { "schemaVersion": 1, "ok": true, "context": { "url": "...", "accountId": "..." }, "result": { } }
package/dist/agw.mjs CHANGED
@@ -6,10 +6,10 @@ var __export = (target, all) => {
6
6
  };
7
7
 
8
8
  // <define:__AGW_MANIFEST__>
9
- var define_AGW_MANIFEST_default = { version: "0.1.9", wrangler: "4.96.0", upgradeFrom: ["0.1.9", "0.1.8", "0.1.7", "0.1.6", "0.1.5", "0.1.4", "0.1.3", "0.1.2", "0.1.1", "0.1.0"], release: { url: "https://github.com/maxceem/app-ai-gateway/releases/download/v0.1.9/gateway-0.1.9.tar.gz", sha256: "a3722d70b8345b41d9e57662985141006214a1b02628af6016b4763e0d177287", manifest: "40ef2a1c2a44d1530a45a3a20eb4df4eb2a840af71ee53762432430f8262c142" } };
9
+ var define_AGW_MANIFEST_default = { version: "0.1.11", wrangler: "4.96.0", upgradeFrom: ["0.1.11", "0.1.10", "0.1.9", "0.1.8", "0.1.7", "0.1.6", "0.1.5", "0.1.4", "0.1.3", "0.1.2", "0.1.1", "0.1.0"], release: { url: "https://github.com/maxceem/app-ai-gateway/releases/download/v0.1.11/gateway-0.1.11.tar.gz", sha256: "44e32e93c2867dd253b493c5e5e7d75853296a30e187acc02711cf76b95ac7b5", manifest: "cb6cb5f3e29806aca543aca78bd8a8617e8f2e27d23cc104326b00db47cdb17d" } };
10
10
 
11
11
  // src/main.ts
12
- import { Writable } from "node:stream";
12
+ import { Writable as Writable2 } from "node:stream";
13
13
 
14
14
  // ../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
15
15
  var external_exports = {};
@@ -16050,6 +16050,11 @@ function validate(schema, value) {
16050
16050
  // src/input.ts
16051
16051
  import { createInterface } from "node:readline/promises";
16052
16052
  import { stdin, stderr } from "node:process";
16053
+ var MASK_LIMIT = 64;
16054
+ function maskDelta(shown, length) {
16055
+ const target = Math.min(length, MASK_LIMIT);
16056
+ return target > shown ? "*".repeat(target - shown) : "\b \b".repeat(shown - target);
16057
+ }
16053
16058
  async function prompt(label, flags, { hidden = false, defaultValue } = {}) {
16054
16059
  if (flags["no-input"] || !stdin.isTTY || !stderr.isTTY)
16055
16060
  fail(
@@ -16073,6 +16078,12 @@ async function prompt(label, flags, { hidden = false, defaultValue } = {}) {
16073
16078
  stdin.resume();
16074
16079
  return new Promise((resolve3, reject2) => {
16075
16080
  let value = "";
16081
+ let shown = 0;
16082
+ function mask() {
16083
+ const delta = maskDelta(shown, value.length);
16084
+ if (delta) stderr.write(delta);
16085
+ shown = Math.min(value.length, MASK_LIMIT);
16086
+ }
16076
16087
  function done(error51) {
16077
16088
  stdin.off("data", onData);
16078
16089
  stdin.setRawMode(wasRaw);
@@ -16093,9 +16104,13 @@ async function prompt(label, flags, { hidden = false, defaultValue } = {}) {
16093
16104
  }
16094
16105
  if (char === "\x7F" || char === "\b") {
16095
16106
  value = value.slice(0, -1);
16107
+ mask();
16096
16108
  continue;
16097
16109
  }
16098
- if (char >= " ") value += char;
16110
+ if (char >= " ") {
16111
+ value += char;
16112
+ mask();
16113
+ }
16099
16114
  if (value.length > 16384) {
16100
16115
  done(new Error("input too long"));
16101
16116
  return;
@@ -16258,9 +16273,9 @@ function parse3(argv) {
16258
16273
  fail("missing_value", `--${name} requires a value.`);
16259
16274
  if (type === "multiple") {
16260
16275
  const existing = collected[name];
16261
- const list = Array.isArray(existing) ? existing : [];
16262
- list.push(value);
16263
- collected[name] = list;
16276
+ const list2 = Array.isArray(existing) ? existing : [];
16277
+ list2.push(value);
16278
+ collected[name] = list2;
16264
16279
  } else collected[name] = value;
16265
16280
  }
16266
16281
  }
@@ -17032,32 +17047,32 @@ function exampleNotes(example) {
17032
17047
  }
17033
17048
  function exampleUrl(baseUrl, appId, target) {
17034
17049
  const origin2 = baseUrl.replace(/\/+$/, "");
17035
- const app = `${origin2}/v1/apps/${encodeURIComponent(appId)}`;
17036
- return "endpoint" in target ? `${app}/endpoints/${encodeURIComponent(target.endpoint)}` : `${app}/proxy/${encodeURIComponent(target.provider)}/${target.path}`;
17050
+ const app2 = `${origin2}/v1/apps/${encodeURIComponent(appId)}`;
17051
+ return "endpoint" in target ? `${app2}/endpoints/${encodeURIComponent(target.endpoint)}` : `${app2}/proxy/${encodeURIComponent(target.provider)}/${target.path}`;
17037
17052
  }
17038
17053
  function shellQuote(value) {
17039
17054
  return `'${value.replaceAll("'", "'\\''")}'`;
17040
17055
  }
17041
- function commented(notes, marker) {
17042
- return (notes ?? []).map((note) => `${marker} ${note}
17056
+ function commented(notes2, marker) {
17057
+ return (notes2 ?? []).map((note) => `${marker} ${note}
17043
17058
  `).join("");
17044
17059
  }
17045
17060
  function curlSnippet(options) {
17046
- const { baseUrl, appId, example, notes, keyExpression = "$APP_AI_GATEWAY_KEY" } = options;
17061
+ const { baseUrl, appId, example, notes: notes2, keyExpression = "$APP_AI_GATEWAY_KEY" } = options;
17047
17062
  const body = example.body ? JSON.stringify(example.body) : BODY_PLACEHOLDER;
17048
- return commented(notes, "#") + `curl --fail-with-body ${shellQuote(exampleUrl(baseUrl, appId, example.target))} \\
17063
+ return commented(notes2, "#") + `curl --fail-with-body ${shellQuote(exampleUrl(baseUrl, appId, example.target))} \\
17049
17064
  -H "Authorization: Bearer ${keyExpression}" \\
17050
17065
  -H 'Content-Type: application/json' \\
17051
17066
  ` + (example.anthropic ? " -H 'anthropic-version: 2023-06-01' \\\n" : "") + ` -d ${shellQuote(body)}
17052
17067
  `;
17053
17068
  }
17054
17069
  function swiftSnippet(options) {
17055
- const { baseUrl, appId, example, notes, authMode = ".appAttestInstall" } = options;
17070
+ const { baseUrl, appId, example, notes: notes2, authMode = ".appAttestInstall" } = options;
17056
17071
  const target = "endpoint" in example.target ? `endpointSlug: ${JSON.stringify(example.target.endpoint)}` : `provider: ${JSON.stringify(example.target.provider)},
17057
17072
  providerPath: ${JSON.stringify(example.target.path)}`;
17058
17073
  const body = example.body ? `request.httpBody = Data(${JSON.stringify(JSON.stringify(example.body))}.utf8)` : `// Supply the provider-native JSON body.
17059
17074
  request.httpBody = Data(${JSON.stringify(BODY_PLACEHOLDER)}.utf8)`;
17060
- return commented(notes, "//") + `import Foundation
17075
+ return commented(notes2, "//") + `import Foundation
17061
17076
  import AppAIGateway
17062
17077
 
17063
17078
  let gateway = AppAIGatewayClient(
@@ -17082,8 +17097,8 @@ var unlimited = () => ({
17082
17097
  spending: { monthly_usd: null }
17083
17098
  });
17084
17099
  var policy = () => ({ allowed_paths: [], allowed_models: [] });
17085
- function documentOf(app) {
17086
- return localApp({ name: app.name, config: app.config, status: app.status });
17100
+ function documentOf(app2) {
17101
+ return localApp({ name: app2.name, config: app2.config, status: app2.status });
17087
17102
  }
17088
17103
  function localApp(value) {
17089
17104
  const doc = validate(AppWriteSchema, value);
@@ -17284,14 +17299,14 @@ async function appCommand(ctx, command, args, flags) {
17284
17299
  };
17285
17300
  const output = action === "add" && doc2.config.authentication.type === "api_key" ? await ctx.keyOutput(operations.createApp.path(), doc2, flags["key-output"]) : null;
17286
17301
  try {
17287
- let app;
17302
+ let app2;
17288
17303
  let resolved;
17289
17304
  let configError;
17290
17305
  let key;
17291
17306
  if (action === "add") {
17292
17307
  await ctx.bootstrap();
17293
17308
  const created = await ctx.create("createApp", [], doc2);
17294
- ({ app, resolved, config_error: configError } = created.data);
17309
+ ({ app: app2, resolved, config_error: configError } = created.data);
17295
17310
  if (output) {
17296
17311
  if (created.keyMetadata) key = created.keyMetadata;
17297
17312
  else {
@@ -17303,7 +17318,7 @@ async function appCommand(ctx, command, args, flags) {
17303
17318
  "Inspect the app key list before retrying.",
17304
17319
  3
17305
17320
  );
17306
- key = await saveKey(ctx, minted, output, app.id);
17321
+ key = await saveKey(ctx, minted, output, app2.id);
17307
17322
  }
17308
17323
  await created.keyStored?.(key);
17309
17324
  }
@@ -17312,11 +17327,11 @@ async function appCommand(ctx, command, args, flags) {
17312
17327
  const updated = await ctx.call("updateApp", [appId2], {
17313
17328
  body: { ...doc2, revision }
17314
17329
  });
17315
- ({ app, resolved, config_error: configError } = updated.data);
17330
+ ({ app: app2, resolved, config_error: configError } = updated.data);
17316
17331
  }
17317
17332
  let snippet;
17318
17333
  try {
17319
- const example = await appCommand(ctx, "app snippet", [app.id], {});
17334
+ const example = await appCommand(ctx, "app snippet", [app2.id], {});
17320
17335
  if ("snippet" in example) snippet = example.snippet;
17321
17336
  } catch {
17322
17337
  }
@@ -17326,11 +17341,11 @@ async function appCommand(ctx, command, args, flags) {
17326
17341
  ${snippet}`;
17327
17342
  return {
17328
17343
  ...snippet ? { snippet } : {},
17329
- app,
17344
+ app: app2,
17330
17345
  resolved,
17331
17346
  config_error: configError,
17332
17347
  ...key ? { applicationKey: key } : {},
17333
- guidance: doc2.config.authentication.type === "apple_app_attest" ? `Add https://github.com/maxceem/app-ai-gateway-swift from 1.0.0, enable App Attest, and test on a supported physical device. Run agw app snippet ${app.id} --provider <slug> for integration code. Production releases should use production-only App Attest.` : `Store the generated key on your server; never embed it in a mobile application. Run agw app snippet ${app.id} for this request again.`
17348
+ guidance: doc2.config.authentication.type === "apple_app_attest" ? `Add https://github.com/maxceem/app-ai-gateway-swift from 1.0.0, enable App Attest, and test on a supported physical device. Run agw app snippet ${app2.id} --provider <slug> for integration code. Production releases should use production-only App Attest.` : `Store the generated key on your server; never embed it in a mobile application. Run agw app snippet ${app2.id} for this request again.`
17334
17349
  };
17335
17350
  } finally {
17336
17351
  await output?.cancel();
@@ -17339,8 +17354,8 @@ ${snippet}`;
17339
17354
  if (action.startsWith("key ")) {
17340
17355
  const sub = action.slice(4);
17341
17356
  const appId2 = args[0] ?? "";
17342
- const { data: app } = await ctx.call("getApp", [appId2]);
17343
- if (documentOf(app.app).config.authentication.type !== "api_key")
17357
+ const { data: app2 } = await ctx.call("getApp", [appId2]);
17358
+ if (documentOf(app2.app).config.authentication.type !== "api_key")
17344
17359
  fail(
17345
17360
  "invalid_input",
17346
17361
  "Only server applications support application keys."
@@ -17427,7 +17442,7 @@ ${snippet}`;
17427
17442
  ios ? "iOS applications authenticate with App Attest, which curl cannot perform." : "Swift snippets are for iOS applications.",
17428
17443
  `Run agw app snippet ${appId} --language ${ios ? "swift" : "curl"}.`
17429
17444
  );
17430
- const notes = [];
17445
+ const notes2 = [];
17431
17446
  let example;
17432
17447
  if (flags.endpoint) {
17433
17448
  const endpoint = doc.config.endpoints?.[flags.endpoint];
@@ -17460,16 +17475,16 @@ ${snippet}`;
17460
17475
  const providers = requested ? reachable.filter((p) => p.slug === requested) : reachable;
17461
17476
  example = firstRequest(routing, providers, catalog.prices);
17462
17477
  if (!requested && reachable.length > 1)
17463
- notes.push(`This app can reach ${reachable.length} providers. Add --provider <slug> for a different one.`);
17478
+ notes2.push(`This app can reach ${reachable.length} providers. Add --provider <slug> for a different one.`);
17464
17479
  }
17465
- notes.push(...exampleNotes(example));
17480
+ notes2.push(...exampleNotes(example));
17466
17481
  const clientUrl = ctx.active?.deployment?.apiUrl ?? ctx.url;
17467
17482
  let snippet;
17468
17483
  if (ios) {
17469
17484
  const endUser = doc.config.authentication.type === "apple_app_attest" ? doc.config.authentication.end_user : void 0;
17470
17485
  const issuer = endUser?.source === "issuer";
17471
17486
  if (issuer)
17472
- notes.push(
17487
+ notes2.push(
17473
17488
  "Replace yourIdentitySDK.currentIDToken(forceRefresh: forceRefresh) with your configured issuer integration."
17474
17489
  );
17475
17490
  snippet = swiftSnippet({
@@ -17480,11 +17495,11 @@ ${snippet}`;
17480
17495
  notes: [
17481
17496
  "Swift package: https://github.com/maxceem/app-ai-gateway-swift (from: 1.0.0)",
17482
17497
  "Enable App Attest and test on a supported physical device.",
17483
- ...notes
17498
+ ...notes2
17484
17499
  ]
17485
17500
  });
17486
17501
  } else {
17487
- snippet = curlSnippet({ baseUrl: clientUrl, appId, example, notes });
17502
+ snippet = curlSnippet({ baseUrl: clientUrl, appId, example, notes: notes2 });
17488
17503
  }
17489
17504
  if (flags.output) {
17490
17505
  const output = await reserveOutput(flags.output);
@@ -17495,7 +17510,7 @@ ${snippet}`;
17495
17510
  await output.cancel();
17496
17511
  }
17497
17512
  }
17498
- return { language: ios ? "swift" : "shell", snippet, notes };
17513
+ return { language: ios ? "swift" : "shell", snippet, notes: notes2 };
17499
17514
  }
17500
17515
  fail("unknown_command", "Unknown command.");
17501
17516
  }
@@ -17526,8 +17541,8 @@ var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 425, 429]);
17526
17541
  var RECEIPT_BOUND_STATUS = /* @__PURE__ */ new Set([409, 410]);
17527
17542
  function definitiveRefusal(error51) {
17528
17543
  if (!(error51 instanceof CliError)) return false;
17529
- const status = error51.details?.status;
17530
- return typeof status === "number" && status >= 400 && status < 500 && !RETRYABLE_STATUS.has(status) && !RECEIPT_BOUND_STATUS.has(status);
17544
+ const status2 = error51.details?.status;
17545
+ return typeof status2 === "number" && status2 >= 400 && status2 < 500 && !RETRYABLE_STATUS.has(status2) && !RECEIPT_BOUND_STATUS.has(status2);
17531
17546
  }
17532
17547
  var Context = class {
17533
17548
  store;
@@ -17627,7 +17642,7 @@ var Context = class {
17627
17642
  (entry) => entry.requestHash === requestHash && (entry.accountId === null || entry.accountId === this.active?.account?.id)
17628
17643
  ),
17629
17644
  (state) => {
17630
- const mutation = {
17645
+ const mutation2 = {
17631
17646
  id: randomToken(),
17632
17647
  proof: randomToken(),
17633
17648
  requestHash,
@@ -17636,27 +17651,27 @@ var Context = class {
17636
17651
  path,
17637
17652
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
17638
17653
  };
17639
- (state.mutations ??= {})[mutation.id] = mutation;
17640
- return mutation;
17654
+ (state.mutations ??= {})[mutation2.id] = mutation2;
17655
+ return mutation2;
17641
17656
  }
17642
17657
  );
17643
17658
  }
17644
17659
  async keyOutput(path, body, requestedPath) {
17645
- const mutation = await this.prepareCreate(path, body);
17646
- const chosen = requestedPath ? resolve2(requestedPath) : mutation.output?.path;
17647
- if (mutation.completedAt && chosen !== mutation.output?.path)
17660
+ const mutation2 = await this.prepareCreate(path, body);
17661
+ const chosen = requestedPath ? resolve2(requestedPath) : mutation2.output?.path;
17662
+ if (mutation2.completedAt && chosen !== mutation2.output?.path)
17648
17663
  fail(
17649
17664
  "output_changed",
17650
17665
  "A completed key creation cannot move its output.",
17651
17666
  "Copy the protected existing file, or create a replacement with a new key name.",
17652
17667
  4
17653
17668
  );
17654
- const existing = chosen && chosen === mutation.output?.path ? mutation.output : void 0;
17669
+ const existing = chosen && chosen === mutation2.output?.path ? mutation2.output : void 0;
17655
17670
  const output = await this.store.keyOutput(chosen, {
17656
17671
  reservation: existing,
17657
17672
  retain: true
17658
17673
  });
17659
- if (mutation.completedAt && mutation.keyMetadata?.contentHash !== await output.contentHash()) {
17674
+ if (mutation2.completedAt && mutation2.keyMetadata?.contentHash !== await output.contentHash()) {
17660
17675
  await output.cancel();
17661
17676
  fail(
17662
17677
  "output_changed",
@@ -17665,15 +17680,15 @@ var Context = class {
17665
17680
  4
17666
17681
  );
17667
17682
  }
17668
- mutation.output = output.reservation;
17683
+ mutation2.output = output.reservation;
17669
17684
  await this.save();
17670
17685
  return {
17671
17686
  ...output,
17672
- recoveryAvailable: () => Boolean(mutation.response),
17673
- mutation,
17687
+ recoveryAvailable: () => Boolean(mutation2.response),
17688
+ mutation: mutation2,
17674
17689
  cancel: async () => {
17675
17690
  await output.cancel();
17676
- if (!this.state.mutations?.[mutation.id])
17691
+ if (!this.state.mutations?.[mutation2.id])
17677
17692
  await releaseOutput(output.reservation);
17678
17693
  }
17679
17694
  };
@@ -17694,32 +17709,32 @@ var Context = class {
17694
17709
  );
17695
17710
  const descriptor = operations[name];
17696
17711
  const path = descriptor.path(...params);
17697
- const mutation = await this.prepareCreate(path, body);
17698
- if (mutation.accountId === null) {
17699
- mutation.accountId = this.active.account?.id ?? null;
17712
+ const mutation2 = await this.prepareCreate(path, body);
17713
+ if (mutation2.accountId === null) {
17714
+ mutation2.accountId = this.active.account?.id ?? null;
17700
17715
  await this.save();
17701
17716
  }
17702
- if (mutation.failure)
17717
+ if (mutation2.failure)
17703
17718
  fail(
17704
17719
  "key_storage_failed",
17705
17720
  "This creation ended with a revoked or unverified key.",
17706
- `Inspect agw app key list ${mutation.failure.appId}; use a new key name to replace it.`,
17721
+ `Inspect agw app key list ${mutation2.failure.appId}; use a new key name to replace it.`,
17707
17722
  4,
17708
- mutation.failure
17723
+ mutation2.failure
17709
17724
  );
17710
- if (mutation.completedAt) {
17711
- this.deliveredMutations.add(mutation.id);
17725
+ if (mutation2.completedAt) {
17726
+ this.deliveredMutations.add(mutation2.id);
17712
17727
  return {
17713
17728
  // Parsed against the recorded shape, not the live one: this copy never
17714
17729
  // held the plaintext, so the live schema would reject it — and a state
17715
17730
  // file that somehow grew one is stripped again on the way out.
17716
- data: this.parseRecorded(name, mutation.result),
17717
- keyMetadata: mutation.keyMetadata,
17731
+ data: this.parseRecorded(name, mutation2.result),
17732
+ keyMetadata: mutation2.keyMetadata,
17718
17733
  complete: async () => {
17719
17734
  }
17720
17735
  };
17721
17736
  }
17722
- if (!mutation.response && Date.now() - Date.parse(mutation.createdAt) > 90 * 24 * 60 * 60 * 1e3)
17737
+ if (!mutation2.response && Date.now() - Date.parse(mutation2.createdAt) > 90 * 24 * 60 * 60 * 1e3)
17723
17738
  fail(
17724
17739
  "resource_retry_expired",
17725
17740
  "This unfinished creation is older than the supported 90-day retry window.",
@@ -17727,8 +17742,8 @@ var Context = class {
17727
17742
  4
17728
17743
  );
17729
17744
  let data;
17730
- if (mutation.response !== void 0) {
17731
- const recovered = this.parse(name, mutation.response);
17745
+ if (mutation2.response !== void 0) {
17746
+ const recovered = this.parse(name, mutation2.response);
17732
17747
  const keyRecord = recoveredKey(name, recovered);
17733
17748
  if (keyRecord) {
17734
17749
  const appId = recoveredAppId(name, recovered, params);
@@ -17736,15 +17751,15 @@ var Context = class {
17736
17751
  if (!existing.keys.some(
17737
17752
  (key) => key.id === keyRecord.id && key.status === "active"
17738
17753
  )) {
17739
- mutation.failure = { appId, keyId: keyRecord.id, revoked: true };
17740
- delete mutation.response;
17754
+ mutation2.failure = { appId, keyId: keyRecord.id, revoked: true };
17755
+ delete mutation2.response;
17741
17756
  await this.save();
17742
17757
  fail(
17743
17758
  "resource_key_unavailable",
17744
17759
  "The recovered application key was revoked or removed.",
17745
17760
  "Create a replacement key with a new name; the previous creation will not be repeated.",
17746
17761
  4,
17747
- mutation.failure
17762
+ mutation2.failure
17748
17763
  );
17749
17764
  }
17750
17765
  }
@@ -17753,29 +17768,29 @@ var Context = class {
17753
17768
  const response = await this.call(name, params, {
17754
17769
  body,
17755
17770
  headers: {
17756
- "Idempotency-Key": mutation.id,
17757
- "X-Idempotency-Proof": mutation.proof
17771
+ "Idempotency-Key": mutation2.id,
17772
+ "X-Idempotency-Proof": mutation2.proof
17758
17773
  }
17759
17774
  }).catch(async (error51) => {
17760
- await this.discard(mutation, error51);
17775
+ await this.discard(mutation2, error51);
17761
17776
  throw error51;
17762
17777
  });
17763
17778
  data = response.data;
17764
- mutation.response = data;
17779
+ mutation2.response = data;
17765
17780
  await this.save();
17766
17781
  }
17767
17782
  return {
17768
17783
  data,
17769
17784
  keyStored: async (metadata) => {
17770
- mutation.keyMetadata = metadata;
17785
+ mutation2.keyMetadata = metadata;
17771
17786
  await this.save();
17772
17787
  },
17773
17788
  complete: async () => {
17774
- mutation.result = recordedResultFor[name].parse(data);
17775
- mutation.completedAt = (/* @__PURE__ */ new Date()).toISOString();
17776
- delete mutation.response;
17789
+ mutation2.result = recordedResultFor[name].parse(data);
17790
+ mutation2.completedAt = (/* @__PURE__ */ new Date()).toISOString();
17791
+ delete mutation2.response;
17777
17792
  await this.save();
17778
- this.deliveredMutations.add(mutation.id);
17793
+ this.deliveredMutations.add(mutation2.id);
17779
17794
  }
17780
17795
  };
17781
17796
  }
@@ -17787,9 +17802,9 @@ var Context = class {
17787
17802
  * different failure. The worst a failed write leaves behind is the record
17788
17803
  * this would have removed, which is where the CLI stood before.
17789
17804
  */
17790
- async discard(mutation, error51) {
17805
+ async discard(mutation2, error51) {
17791
17806
  if (!definitiveRefusal(error51)) return;
17792
- delete this.state.mutations?.[mutation.id];
17807
+ delete this.state.mutations?.[mutation2.id];
17793
17808
  await this.save().catch(() => {
17794
17809
  });
17795
17810
  }
@@ -17914,12 +17929,12 @@ var Context = class {
17914
17929
  };
17915
17930
  await this.save();
17916
17931
  }
17917
- const operation2 = this.state.operations[localId];
17932
+ const operation3 = this.state.operations[localId];
17918
17933
  const { data: capabilities } = await this.publicCall("getCliCapabilities", [], {
17919
17934
  url: target
17920
17935
  });
17921
17936
  const { data } = await this.publicCall("createCliOperation", [], {
17922
- body: { kind, payload, pollToken: operation2.pollToken },
17937
+ body: { kind, payload, pollToken: operation3.pollToken },
17923
17938
  url: target,
17924
17939
  ...authenticated ? { key: this.active?.credential } : {}
17925
17940
  });
@@ -17932,7 +17947,7 @@ var Context = class {
17932
17947
  "invalid_response",
17933
17948
  "The handoff URL is not on this deployment\u2019s trusted console origin."
17934
17949
  );
17935
- this.state.operations[data.id] = { ...operation2, phase: "pending" };
17950
+ this.state.operations[data.id] = { ...operation3, phase: "pending" };
17936
17951
  delete this.state.operations[localId];
17937
17952
  await this.save();
17938
17953
  if (!this.flags["no-open"]) await openBrowser(data.url);
@@ -17946,15 +17961,15 @@ Operation: ${data.id}
17946
17961
  return this.wait(data.id, 300);
17947
17962
  }
17948
17963
  async poll(id) {
17949
- const operation2 = this.state.operations[id];
17950
- if (!operation2)
17964
+ const operation3 = this.state.operations[id];
17965
+ if (!operation3)
17951
17966
  fail(
17952
17967
  "operation_unknown",
17953
17968
  "This operation has no locally stored polling authorization.",
17954
17969
  "Resume it from the machine that initiated the operation.",
17955
17970
  4
17956
17971
  );
17957
- if (this.active && this.active.url !== operation2.url)
17972
+ if (this.active && this.active.url !== operation3.url)
17958
17973
  fail(
17959
17974
  "operation_context",
17960
17975
  "This operation belongs to another selected deployment.",
@@ -17962,18 +17977,18 @@ Operation: ${data.id}
17962
17977
  4
17963
17978
  );
17964
17979
  const { data } = await this.publicCall("pollCliOperation", [id], {
17965
- key: operation2.pollToken,
17966
- url: operation2.url
17980
+ key: operation3.pollToken,
17981
+ url: operation3.url
17967
17982
  });
17968
- if (data.state === "completed" && !operation2.completed && operation2.kind === "claim") {
17969
- if ((this.state.generation ?? 0) !== operation2.generation)
17983
+ if (data.state === "completed" && !operation3.completed && operation3.kind === "claim") {
17984
+ if ((this.state.generation ?? 0) !== operation3.generation)
17970
17985
  fail(
17971
17986
  "operation_context",
17972
17987
  "The active connection changed after this operation began.",
17973
17988
  "Start a new login for the intended connection.",
17974
17989
  4
17975
17990
  );
17976
- operation2.completed = true;
17991
+ operation3.completed = true;
17977
17992
  if (this.active && data.account) this.active.account = data.account;
17978
17993
  await this.save();
17979
17994
  }
@@ -18981,60 +18996,570 @@ async function deploymentCommand(ctx, command, flags, cf = new Cloudflare(), loa
18981
18996
  }
18982
18997
  }
18983
18998
 
18999
+ // src/style.ts
19000
+ import { Writable } from "node:stream";
19001
+ import { styleText } from "node:util";
19002
+ var STATES = /* @__PURE__ */ new Map([
19003
+ ["active", "green"],
19004
+ ["ready", "green"],
19005
+ ["completed", "green"],
19006
+ ["yes", "green"],
19007
+ ["pending", "yellow"],
19008
+ ["failed", "red"],
19009
+ ["expired", "red"],
19010
+ ["blocked", "red"],
19011
+ ["inactive", "red"],
19012
+ ["disabled", "red"],
19013
+ ["revoked", "red"],
19014
+ ["no", "red"]
19015
+ ]);
19016
+ function colorEnabled(sink, env = process.env) {
19017
+ if (env["NO_COLOR"]) return false;
19018
+ if (!(sink instanceof Writable)) return false;
19019
+ if (env["FORCE_COLOR"] && env["FORCE_COLOR"] !== "0") return true;
19020
+ if (!("isTTY" in sink && sink.isTTY === true)) return false;
19021
+ return styleText("dim", "?", { stream: sink, validateStream: true }) !== "?";
19022
+ }
19023
+ var plain = style(false);
19024
+ var styleFor = (sink) => style(colorEnabled(sink));
19025
+ function style(enabled) {
19026
+ const paint = (format, text2) => enabled ? styleText(format, text2, { validateStream: false }) : text2;
19027
+ const self = {
19028
+ enabled,
19029
+ headline: (text2) => paint("bold", text2),
19030
+ dim: (text2) => paint("dim", text2),
19031
+ alert: (text2) => paint("red", text2),
19032
+ warn: (text2) => paint("yellow", text2),
19033
+ state: (value) => {
19034
+ const tone = STATES.get(value.toLowerCase());
19035
+ return tone ? paint(tone, value) : value;
19036
+ },
19037
+ code: (text2, language) => {
19038
+ const rule = `\u2500\u2500 ${language} \u2500\u2500`;
19039
+ return [
19040
+ self.dim(rule),
19041
+ ...text2.split("\n").map((line) => self.dim(line)),
19042
+ self.dim("\u2500".repeat(rule.length))
19043
+ ];
19044
+ },
19045
+ json: (value) => JSON.stringify(value, null, 2).split("\n").map((line) => jsonLine(line, paint)).join("\n")
19046
+ };
19047
+ return self;
19048
+ }
19049
+ function jsonLine(line, paint) {
19050
+ const match = /^(\s*)(?:("(?:[^"\\]|\\.)*")(: ))?(.*?)(,?)$/.exec(line);
19051
+ if (!match) return line;
19052
+ const [, indent = "", key, colon, value = "", comma = ""] = match;
19053
+ const painted = value === "" ? "" : /^[[\]{}]+$/.test(value) ? paint("dim", value) : value.startsWith('"') ? value : paint("green", value);
19054
+ return indent + (key && colon ? paint("bold", key) + paint("dim", colon) : "") + painted + (comma ? paint("dim", comma) : "");
19055
+ }
19056
+
18984
19057
  // src/human.ts
18985
- function humanResult(command, result, context) {
18986
- const lines = [];
18987
- if (command === "app snippet" && "snippet" in result && typeof result.snippet === "string")
18988
- return result.snippet;
18989
- if ("state" in result && result.state === "pending" && "url" in result) {
18990
- lines.push(`Waiting for browser handoff (${result.id}).`, result.url);
18991
- lines.push(`Resume: agw operation wait ${result.id}`);
18992
- } else if ("guidance" in result && "app" in result) {
18993
- lines.push(
18994
- `${command === "app add" ? "Created" : "Updated"} ${result.app.name}`,
18995
- `Gateway: ${context.url}`,
18996
- `App ID: ${result.app.id}`,
18997
- ""
18998
- );
18999
- const parsed = AppConfigSchema.safeParse(result.app.config);
19000
- if (parsed.success) {
19001
- const config2 = parsed.data;
19002
- if (config2.authentication.type === "apple_app_attest") {
19003
- const endUser = config2.authentication.end_user;
19004
- lines.push(
19005
- `App Attest: ${(config2.authentication.app_attest.environments ?? ["production"]).join(" + ")}`,
19006
- `User identity: ${endUser.source === "app_install" ? "per installation; sign-in not required" : "verified issuer; integrate your sign-in SDK"}`,
19007
- `Paid subscription check: ${endUser.source === "issuer" && endUser.issuer.required_claims.length ? "configured claim requirements" : "off"}`
19008
- );
19009
- } else lines.push("Authentication: server application API key");
19010
- const limits = config2.limits?.per_user;
19011
- lines.push(
19012
- `Per-user limits: ${limits?.requests.per_minute ?? "unlimited"} requests/minute, ${limits?.requests.per_day ?? "unlimited"}/day`
19013
- );
19014
- }
19015
- if (result.applicationKey)
19016
- lines.push(`Key saved: ${result.applicationKey.storagePath}`);
19017
- if (result.guidance) lines.push("", result.guidance);
19018
- if ("snippet" in result && result.snippet) lines.push("", result.snippet);
19019
- } else if (command === "provider add" || command === "provider-gateway add") {
19020
- const stored = "provider" in result ? result.provider : "gateway" in result ? result.gateway : void 0;
19021
- const label = stored?.name ?? stored?.type ?? "connection";
19022
- const id = stored?.id ?? ("id" in result ? result.id : void 0);
19023
- lines.push(
19024
- `Stored ${label} (${id}).`,
19025
- `Gateway: ${context.url}`,
19026
- "Credential stored; no upstream probe or inference was performed."
19027
- );
19028
- } else if ("output" in result && typeof result.output === "string") {
19029
- lines.push(`Saved: ${result.output}`);
19030
- } else lines.push(JSON.stringify(result, null, 2));
19031
- if (context.accountId) lines.push(`Account: ${context.accountId}`);
19058
+ function kv(entries, style2) {
19059
+ const rows = [];
19060
+ for (const [key, value] of entries)
19061
+ if (value !== void 0 && value !== null) rows.push([key, String(value)]);
19062
+ const width = Math.max(0, ...rows.map(([key]) => key.length + 1));
19063
+ return rows.map(
19064
+ ([key, value]) => style2.dim(`${key}:`.padEnd(width + 1)) + style2.state(value)
19065
+ );
19066
+ }
19067
+ function table(headers, rows, style2) {
19068
+ const widths = headers.map(
19069
+ (header, column) => Math.max(header.length, ...rows.map((row) => (row[column] ?? "").length))
19070
+ );
19071
+ const line = (cells, tone) => cells.map(
19072
+ (cell, column) => tone(cell) + (column === widths.length - 1 ? "" : " ".repeat((widths[column] ?? 0) + 2 - cell.length))
19073
+ ).join("").trimEnd();
19074
+ return [
19075
+ line(headers, (cell) => style2.dim(cell)),
19076
+ ...rows.map((row) => line(row, (cell) => style2.state(cell)))
19077
+ ];
19078
+ }
19079
+ var yesNo = (value) => value ? "yes" : "no";
19080
+ function humanResult(command, result, context, style2 = plain) {
19081
+ if (command === "app snippet" && "snippet" in result && typeof result.snippet === "string") {
19082
+ if (!style2.enabled) return result.snippet;
19083
+ const language = "language" in result ? result.language : "code";
19084
+ return style2.code(result.snippet, language).join("\n") + "\n";
19085
+ }
19086
+ const lines = render(command, result, context, style2);
19032
19087
  if ("trial" in result && result.trial)
19033
19088
  lines.push(`Free access ends: ${result.trial.endsAt}`);
19034
19089
  if ("account" in result && result.account?.expiresAt)
19035
19090
  lines.push(`Recovery deadline: ${result.account.expiresAt}`);
19036
19091
  return lines.join("\n") + "\n";
19037
19092
  }
19093
+ function render(command, result, context, style2) {
19094
+ if ("state" in result && result.state === "pending" && "url" in result)
19095
+ return [
19096
+ style2.headline(`Waiting for browser handoff (${result.id}).`),
19097
+ result.url,
19098
+ `Resume: agw operation wait ${result.id}`
19099
+ ];
19100
+ switch (command) {
19101
+ case "account status":
19102
+ case "account login":
19103
+ case "account claim":
19104
+ case "deployment connect":
19105
+ case "deployment status":
19106
+ return status(command, result, context, style2);
19107
+ case "provider types":
19108
+ case "provider-gateway types":
19109
+ case "provider list":
19110
+ case "provider-gateway list":
19111
+ case "app list":
19112
+ case "app key list":
19113
+ return list(command, result, style2);
19114
+ case "provider show":
19115
+ case "provider-gateway show":
19116
+ case "app show":
19117
+ return show(command, result, style2);
19118
+ case "provider update":
19119
+ case "provider-gateway update":
19120
+ case "provider rotate-key":
19121
+ case "provider-gateway rotate-key":
19122
+ case "provider add":
19123
+ case "provider-gateway add":
19124
+ case "provider remove":
19125
+ case "provider-gateway remove":
19126
+ case "app remove":
19127
+ case "app key add":
19128
+ case "app key revoke":
19129
+ case "account logout":
19130
+ return mutation(command, result, context, style2);
19131
+ case "usage show":
19132
+ case "usage breakdown":
19133
+ return usage(command, result, style2);
19134
+ case "operation status":
19135
+ case "operation wait":
19136
+ return operation2(result, style2);
19137
+ case "app add":
19138
+ case "app update":
19139
+ case "app validate":
19140
+ case "app check":
19141
+ case "app snippet":
19142
+ return app(command, result, context, style2);
19143
+ case "deployment setup":
19144
+ case "deployment update":
19145
+ case "deployment domain":
19146
+ return deployment(result, style2);
19147
+ }
19148
+ }
19149
+ function status(command, result, context, style2) {
19150
+ const did = command === "account login" ? [style2.headline(`Signed in to ${context.url}.`)] : command === "deployment connect" ? [style2.headline(`Connected to ${context.url}.`)] : [];
19151
+ if ("billing" in result)
19152
+ return [
19153
+ ...did,
19154
+ ...kv(
19155
+ [
19156
+ ["Gateway", context.url],
19157
+ ["Account", `${result.account.name} (${result.account.id})`],
19158
+ ["Claimed", yesNo(result.account.claimed)],
19159
+ ["Deployment", result.deployment.mode === "cloud" ? "cloud" : "self-hosted"],
19160
+ ["Console", result.deployment.consoleOrigin]
19161
+ ],
19162
+ style2
19163
+ )
19164
+ ];
19165
+ if ("protocolVersion" in result)
19166
+ return [
19167
+ ...did,
19168
+ ...kv(
19169
+ [
19170
+ ["Gateway", result.url],
19171
+ ["Connected", yesNo(result.connected)],
19172
+ ["Authenticated", yesNo(result.authenticated)],
19173
+ ["Version", result.serverVersion],
19174
+ ["Deployment", result.deployment.mode === "cloud" ? "cloud" : "self-hosted"],
19175
+ ["Console", result.consoleOrigin],
19176
+ ["Provider types", result.providers.length],
19177
+ ["Provider gateway types", result.providerGateways.length]
19178
+ ],
19179
+ style2
19180
+ )
19181
+ ];
19182
+ if ("state" in result) return [...did, ...operation2(result, style2)];
19183
+ return [...did, style2.json(result)];
19184
+ }
19185
+ function list(command, result, style2) {
19186
+ if (command === "provider types" || command === "provider-gateway types") {
19187
+ if (!Array.isArray(result)) return [style2.json(result)];
19188
+ const types = result;
19189
+ if (!types.length) return ["No types."];
19190
+ return table(
19191
+ ["TYPE", "NAME", "BASE URL"],
19192
+ types.map((entry) => [entry.type, entry.name, "baseUrl" in entry ? entry.baseUrl : ""]),
19193
+ style2
19194
+ );
19195
+ }
19196
+ if (command === "provider list") {
19197
+ if (!("providers" in result) || "appId" in result || "protocolVersion" in result)
19198
+ return [style2.json(result)];
19199
+ if (!result.providers.length) return ["No providers."];
19200
+ return table(
19201
+ ["ID", "SLUG", "TYPE", "NAME", "STATUS", "CREATED"],
19202
+ result.providers.map((provider) => [
19203
+ provider.id,
19204
+ provider.slug,
19205
+ provider.type,
19206
+ provider.name,
19207
+ provider.status,
19208
+ provider.createdAt
19209
+ ]),
19210
+ style2
19211
+ );
19212
+ }
19213
+ if (command === "provider-gateway list" && "gateways" in result) {
19214
+ if (!result.gateways.length) return ["No provider gateways."];
19215
+ return table(
19216
+ ["ID", "TYPE", "NAME", "STATUS", "PROVIDERS", "CREATED"],
19217
+ result.gateways.map((gateway) => [
19218
+ gateway.id,
19219
+ gateway.type,
19220
+ gateway.name,
19221
+ gateway.status,
19222
+ String(gateway.providerCount),
19223
+ gateway.createdAt
19224
+ ]),
19225
+ style2
19226
+ );
19227
+ }
19228
+ if (command === "app list" && "apps" in result && !("totals" in result)) {
19229
+ if (!result.apps.length) return ["No apps."];
19230
+ return table(
19231
+ ["ID", "NAME", "AUTH", "STATUS", "PROVIDERS", "CREATED"],
19232
+ result.apps.map((entry) => [
19233
+ entry.id,
19234
+ entry.name,
19235
+ entry.authentication_type,
19236
+ entry.status,
19237
+ String(entry.providers.length),
19238
+ entry.created_at
19239
+ ]),
19240
+ style2
19241
+ );
19242
+ }
19243
+ if (command === "app key list" && "keys" in result && "app_id" in result) {
19244
+ if (!result.keys.length) return [`No keys for ${result.app_id}.`];
19245
+ return table(
19246
+ ["ID", "NAME", "PREFIX", "STATUS", "CREATED", "LAST USED"],
19247
+ result.keys.map((key) => [
19248
+ key.id,
19249
+ key.name,
19250
+ key.key_prefix,
19251
+ key.status,
19252
+ key.created_at,
19253
+ key.last_used_at ?? "never"
19254
+ ]),
19255
+ style2
19256
+ );
19257
+ }
19258
+ return [style2.json(result)];
19259
+ }
19260
+ function show(command, result, style2) {
19261
+ if (command === "app show" && "app" in result && !("guidance" in result)) {
19262
+ const lines = [style2.headline(`${result.app.name} (${result.app.id})`)];
19263
+ if (result.config_error)
19264
+ lines.push(style2.alert(`Configuration error: ${result.config_error}`));
19265
+ return [...lines, "", style2.json(result)];
19266
+ }
19267
+ if ("slug" in result)
19268
+ return [
19269
+ style2.headline(`${result.name} (${result.id}), type ${result.type}, slug ${result.slug}`),
19270
+ "",
19271
+ style2.json(result)
19272
+ ];
19273
+ if ("referencedCount" in result)
19274
+ return [
19275
+ style2.headline(`${result.name} (${result.id}), type ${result.type}`),
19276
+ "",
19277
+ style2.json(result)
19278
+ ];
19279
+ return [style2.json(result)];
19280
+ }
19281
+ function mutation(command, result, context, style2) {
19282
+ if ("loggedOut" in result)
19283
+ return [
19284
+ style2.headline(`Signed out of ${context.url}.`) + " The stored credential was removed."
19285
+ ];
19286
+ if ("deleted" in result) {
19287
+ if ("provider_id" in result)
19288
+ return [style2.headline(`Removed provider ${result.provider_id}.`)];
19289
+ if ("provider_gateway_id" in result)
19290
+ return [style2.headline(`Removed provider gateway ${result.provider_gateway_id}.`)];
19291
+ return [
19292
+ style2.headline(`Removed app ${result.app_id}.`) + ` ${result.removed_users} users removed; usage events retained.`
19293
+ ];
19294
+ }
19295
+ if ("key" in result)
19296
+ return [
19297
+ style2.headline(
19298
+ `Revoked key ${result.key.name} (${result.key.id}) for app ${result.app_id}.`
19299
+ )
19300
+ ];
19301
+ if ("applicationKey" in result && "appId" in result)
19302
+ return [
19303
+ style2.headline(
19304
+ `Created key ${result.applicationKey.name} (${result.applicationKey.id}) for app ${result.appId}.`
19305
+ ),
19306
+ ...kv([["Key saved", result.applicationKey.storagePath]], style2)
19307
+ ];
19308
+ if ("provider" in result) {
19309
+ const added = command === "provider add";
19310
+ return [
19311
+ style2.headline(
19312
+ `${added ? "Stored" : "Updated"} provider ${result.provider.name} (${result.provider.id}).`
19313
+ ),
19314
+ ...kv(
19315
+ [
19316
+ ["Gateway", context.url],
19317
+ ["Status", result.provider.status],
19318
+ ["Key hint", result.provider.secretHint]
19319
+ ],
19320
+ style2
19321
+ ),
19322
+ ...added ? ["Credential stored; no upstream probe or inference was performed."] : []
19323
+ ];
19324
+ }
19325
+ if ("gateway" in result) {
19326
+ const added = command === "provider-gateway add";
19327
+ return [
19328
+ style2.headline(
19329
+ `${added ? "Stored" : "Updated"} provider gateway ${result.gateway.name} (${result.gateway.id}).`
19330
+ ),
19331
+ ...kv(
19332
+ [
19333
+ ["Gateway", context.url],
19334
+ ["Status", result.gateway.status],
19335
+ ["Token hint", result.gateway.secretHint]
19336
+ ],
19337
+ style2
19338
+ ),
19339
+ ...added ? ["Credential stored; no upstream probe or inference was performed."] : []
19340
+ ];
19341
+ }
19342
+ return [style2.json(result)];
19343
+ }
19344
+ function usage(command, result, style2) {
19345
+ if (command === "usage breakdown" && "rows" in result) {
19346
+ const head = kv(
19347
+ [
19348
+ ["App", result.app_id],
19349
+ ["Grouped by", result.by],
19350
+ ["Period", `${result.from} to ${result.to}`]
19351
+ ],
19352
+ style2
19353
+ );
19354
+ const rows = result.rows.length ? table(
19355
+ ["KEY", "REQUESTS", "INPUT", "OUTPUT", "COST USD", "ERRORS", "BLOCKED"],
19356
+ result.rows.map((row) => [
19357
+ row.key ?? "(none)",
19358
+ String(row.requests),
19359
+ String(row.input_tokens),
19360
+ String(row.output_tokens),
19361
+ row.cost_usd.toFixed(4),
19362
+ String(row.errors),
19363
+ String(row.blocked)
19364
+ ]),
19365
+ style2
19366
+ ) : ["No usage in this period."];
19367
+ return [...head, "", ...rows, "", result.coverage.source];
19368
+ }
19369
+ if ("totals" in result)
19370
+ return [
19371
+ ...kv(
19372
+ [
19373
+ ["Month", result.month],
19374
+ ["Requests", result.totals.requests],
19375
+ ["Input tokens", result.totals.input_tokens],
19376
+ ["Output tokens", result.totals.output_tokens],
19377
+ ["Cost USD", result.totals.cost_usd.toFixed(4)],
19378
+ ["Errors", result.totals.errors],
19379
+ ["Blocked", result.totals.blocked]
19380
+ ],
19381
+ style2
19382
+ ),
19383
+ "",
19384
+ // The totals are the sum of these rows, so the apps they came from are
19385
+ // printed rather than counted: a month is read to find which app spent it.
19386
+ ...result.apps.length ? table(
19387
+ ["APP", "REQUESTS", "INPUT", "OUTPUT", "COST USD", "ERRORS", "BLOCKED"],
19388
+ result.apps.map((entry) => [
19389
+ entry.deleted ? `${entry.appId} (deleted)` : entry.appId,
19390
+ String(entry.requests),
19391
+ String(entry.input_tokens),
19392
+ String(entry.output_tokens),
19393
+ entry.cost_usd.toFixed(4),
19394
+ String(entry.errors),
19395
+ String(entry.blocked)
19396
+ ]),
19397
+ style2
19398
+ ) : ["No app usage recorded."],
19399
+ "",
19400
+ result.coverage.historicalAttribution
19401
+ ];
19402
+ if ("requests" in result)
19403
+ return kv(
19404
+ [
19405
+ ["App", result.app_id],
19406
+ ["Month", result.month],
19407
+ ["Requests", result.requests],
19408
+ ["Input tokens", result.input_tokens],
19409
+ ["Cached input tokens", result.cached_input_tokens],
19410
+ ["Cache write tokens", result.cache_write_tokens],
19411
+ ["Output tokens", result.output_tokens],
19412
+ ["Cost USD", result.cost_usd.toFixed(4)]
19413
+ ],
19414
+ style2
19415
+ );
19416
+ return [style2.json(result)];
19417
+ }
19418
+ function operation2(result, style2) {
19419
+ if (!("state" in result)) return [style2.json(result)];
19420
+ const stored = "result" in result ? result.result : void 0;
19421
+ return kv(
19422
+ [
19423
+ ["Operation", result.id],
19424
+ ["State", result.state],
19425
+ ["Expires", result.expiresAt],
19426
+ ["Account", stored?.accountId],
19427
+ ["Provider", stored?.provider && `${stored.provider.name} (${stored.provider.id})`],
19428
+ ["Provider gateway", stored?.gateway && `${stored.gateway.name} (${stored.gateway.id})`]
19429
+ ],
19430
+ style2
19431
+ );
19432
+ }
19433
+ function app(command, result, context, style2) {
19434
+ if ("output" in result) return [style2.headline(`Saved: ${result.output}`)];
19435
+ if ("ready" in result)
19436
+ return [
19437
+ style2.headline(
19438
+ `${result.appId} is ${result.ready ? "ready to serve requests" : "not ready to serve requests"}.`
19439
+ ),
19440
+ ...kv(
19441
+ [
19442
+ ["Status", result.status],
19443
+ ["Providers", result.providers.length],
19444
+ ["Active providers", result.providers.filter((p) => p.status === "active").length]
19445
+ ],
19446
+ style2
19447
+ ),
19448
+ ...notes([
19449
+ ...result.validation.remote ? [] : result.validation.skipped,
19450
+ ...result.limitations
19451
+ ])
19452
+ ];
19453
+ if ("definition" in result) {
19454
+ const dryRun = "dryRun" in result;
19455
+ return [
19456
+ style2.headline(
19457
+ `${dryRun ? "Dry run: " : ""}${result.definition.name} is valid${result.validation.remote ? " here and on the gateway" : " locally"}.`
19458
+ ),
19459
+ ...kv(
19460
+ [
19461
+ ["Gateway", context.url],
19462
+ ["Status", result.definition.status],
19463
+ ["Authentication", result.definition.config.authentication.type],
19464
+ ["App ID", result.validation.remote ? result.validation.app_id : void 0]
19465
+ ],
19466
+ style2
19467
+ ),
19468
+ ...notes(result.validation.remote ? [] : result.validation.skipped),
19469
+ ...dryRun ? ["", "Nothing was written."] : []
19470
+ ];
19471
+ }
19472
+ if ("guidance" in result && "app" in result)
19473
+ return written(command, result, context, style2);
19474
+ return [style2.json(result)];
19475
+ }
19476
+ var notes = (values) => values.length ? ["", ...values.map((value) => `- ${value}`)] : [];
19477
+ function written(command, result, context, style2) {
19478
+ const parsed = AppConfigSchema.safeParse(result.app.config);
19479
+ const details = [];
19480
+ let language = "shell";
19481
+ if (parsed.success) {
19482
+ const config2 = parsed.data;
19483
+ if (config2.authentication.type === "apple_app_attest") {
19484
+ language = "swift";
19485
+ const endUser = config2.authentication.end_user;
19486
+ details.push(
19487
+ [
19488
+ "App Attest",
19489
+ (config2.authentication.app_attest.environments ?? ["production"]).join(" + ")
19490
+ ],
19491
+ [
19492
+ "User identity",
19493
+ endUser.source === "app_install" ? "per installation; sign-in not required" : "verified issuer; integrate your sign-in SDK"
19494
+ ],
19495
+ [
19496
+ "Paid subscription check",
19497
+ endUser.source === "issuer" && endUser.issuer.required_claims.length ? "configured claim requirements" : "off"
19498
+ ]
19499
+ );
19500
+ } else details.push(["Authentication", "server application API key"]);
19501
+ const limits = config2.limits?.per_user;
19502
+ details.push([
19503
+ "Per-user limits",
19504
+ `${limits?.requests.per_minute ?? "unlimited"} requests/minute, ${limits?.requests.per_day ?? "unlimited"}/day`
19505
+ ]);
19506
+ }
19507
+ if (result.applicationKey)
19508
+ details.push(["Key saved", result.applicationKey.storagePath]);
19509
+ return [
19510
+ style2.headline(
19511
+ `${command === "app add" ? "Created" : "Updated"} app ${result.app.name} (${result.app.id}).`
19512
+ ),
19513
+ ...kv([["Gateway", context.url], ...details], style2),
19514
+ ...result.guidance ? ["", result.guidance] : [],
19515
+ ..."snippet" in result && result.snippet ? ["", ...style2.code(result.snippet, language)] : []
19516
+ ];
19517
+ }
19518
+ function deployment(result, style2) {
19519
+ if ("hostname" in result)
19520
+ return kv(
19521
+ [
19522
+ [result.dryRun ? "Would attach domain" : "Domain", result.hostname],
19523
+ ["Worker", result.worker],
19524
+ ["Cloudflare account", result.accountId],
19525
+ ["Zone", result.zoneId],
19526
+ ["Previous URL", result.previousUrl],
19527
+ ["URL", result.url],
19528
+ ["Note", result.note]
19529
+ ],
19530
+ style2
19531
+ );
19532
+ if ("fromVersion" in result)
19533
+ return kv(
19534
+ [
19535
+ [result.dryRun ? "Would update worker" : "Worker", result.worker],
19536
+ ["Cloudflare account", result.accountId],
19537
+ ["Database", result.databaseId],
19538
+ ["From version", result.fromVersion],
19539
+ ["To version", result.toVersion],
19540
+ ["Updated", result.updated ? "yes" : void 0]
19541
+ ],
19542
+ style2
19543
+ );
19544
+ if ("installed" in result || "resources" in result)
19545
+ return kv(
19546
+ [
19547
+ [result.dryRun ? "Would install worker" : "Worker", result.name],
19548
+ ["Cloudflare account", result.accountId],
19549
+ ["URL", result.url],
19550
+ ["Version", result.version],
19551
+ ["Domain", result.domain],
19552
+ ["Pending domain", result.pendingDomain],
19553
+ ["Deployment", result.deploymentId],
19554
+ ["Resources", result.resources?.join(", ")],
19555
+ ["Backup", result.backup],
19556
+ ["Installed", result.installed ? "yes" : void 0],
19557
+ ["Note", result.note]
19558
+ ],
19559
+ style2
19560
+ );
19561
+ return [style2.json(result)];
19562
+ }
19038
19563
 
19039
19564
  // src/transport.ts
19040
19565
  var MESSAGE_LIMIT = 300;
@@ -19062,11 +19587,11 @@ var LIMIT_CODES = [
19062
19587
  ];
19063
19588
  var LIMIT_TEXT_FIELDS = ["scope", "resetAt"];
19064
19589
  var LIMIT_NUMBER_FIELDS = ["limit", "used", "windowSeconds", "retryAfterSeconds"];
19065
- function nextActionFor(status, code, details) {
19066
- if (status === 401) return "Run agw account login.";
19590
+ function nextActionFor(status2, code, details) {
19591
+ if (status2 === 401) return "Run agw account login.";
19067
19592
  if (code === "billing_plan_limit_reached")
19068
19593
  return "Delete one you no longer need, or move to a plan whose limits are higher.";
19069
- if (status === 429) {
19594
+ if (status2 === 429) {
19070
19595
  const seconds = details.retryAfterSeconds;
19071
19596
  if (seconds === void 0)
19072
19597
  return "Wait for the limit named in the message to reset, then run the same command again.";
@@ -19184,7 +19709,7 @@ async function usageCommand(ctx, command, flags) {
19184
19709
  const m = month(flags.month ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 7));
19185
19710
  return flags.app ? (await ctx.call("getAppUsage", [flags.app, { month: m }])).data : (await ctx.call("getCliUsage", [{ month: m }])).data;
19186
19711
  }
19187
- const app = await required2(flags, "app");
19712
+ const app2 = await required2(flags, "app");
19188
19713
  const by = flags.by ?? "model";
19189
19714
  if (!["provider", "model", "status"].includes(by))
19190
19715
  fail("invalid_input", "--by must be provider, model or status.");
@@ -19196,7 +19721,7 @@ async function usageCommand(ctx, command, flags) {
19196
19721
  if (from > to) fail("invalid_input", "--from must not be later than --to.");
19197
19722
  const limit = positive(flags.limit ?? 50, 200);
19198
19723
  return {
19199
- ...(await ctx.call("getAppUsageBreakdown", [app, { from, to, by, limit }])).data,
19724
+ ...(await ctx.call("getAppUsageBreakdown", [app2, { from, to, by, limit }])).data,
19200
19725
  coverage: {
19201
19726
  from,
19202
19727
  to,
@@ -19250,7 +19775,8 @@ async function execute(parsed, ctx) {
19250
19775
  async function main(argv = process.argv.slice(2), {
19251
19776
  store = new StateStore(),
19252
19777
  transport = new Transport(),
19253
- stdout = process.stdout
19778
+ stdout = process.stdout,
19779
+ stderr: stderr2 = process.stderr
19254
19780
  } = {}) {
19255
19781
  const json2 = argv.includes("--json");
19256
19782
  try {
@@ -19274,8 +19800,8 @@ async function main(argv = process.argv.slice(2), {
19274
19800
  };
19275
19801
  const rendered = ctx.onboarding ? { ...result, ...ctx.onboarding } : result;
19276
19802
  const output = { schemaVersion: 1, ok: true, context, result: rendered };
19277
- const text2 = json2 ? JSON.stringify(output) + "\n" : humanResult(parsed.command, rendered, context);
19278
- if (stdout instanceof Writable) {
19803
+ const text2 = json2 ? JSON.stringify(output) + "\n" : humanResult(parsed.command, rendered, context, styleFor(stdout));
19804
+ if (stdout instanceof Writable2) {
19279
19805
  await new Promise(
19280
19806
  (resolve3, reject2) => stdout.write(
19281
19807
  text2,
@@ -19297,17 +19823,27 @@ async function main(argv = process.argv.slice(2), {
19297
19823
  4
19298
19824
  );
19299
19825
  const details = e.details ? CliErrorDetailsSchema.safeParse(e.details) : void 0;
19300
- const output = {
19301
- schemaVersion: 1,
19302
- ok: false,
19303
- error: {
19304
- code: e.code,
19305
- message: e.message,
19306
- nextAction: e.nextAction,
19307
- ...details?.success ? { details: details.data } : {}
19308
- }
19826
+ const reported2 = {
19827
+ code: e.code,
19828
+ message: e.message,
19829
+ nextAction: e.nextAction,
19830
+ ...details?.success ? { details: details.data } : {}
19309
19831
  };
19310
- stdout.write(JSON.stringify(output, null, json2 ? void 0 : 2) + "\n");
19832
+ if (json2) {
19833
+ stdout.write(
19834
+ JSON.stringify({ schemaVersion: 1, ok: false, error: reported2 }) + "\n"
19835
+ );
19836
+ return e.exitCode;
19837
+ }
19838
+ const style2 = styleFor(stderr2);
19839
+ const lines = [
19840
+ style2.alert(`Error: ${e.message}`),
19841
+ style2.warn(`Next: ${e.nextAction}`),
19842
+ style2.dim(`Code: ${e.code}`)
19843
+ ];
19844
+ for (const [key, value] of Object.entries(reported2.details ?? {}))
19845
+ lines.push(` ${style2.dim(`${key}:`)} ${String(value)}`);
19846
+ stderr2.write(lines.join("\n") + "\n");
19311
19847
  return e.exitCode;
19312
19848
  }
19313
19849
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maxceem/agw",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "type": "module",
5
5
  "description": "Manage App AI Gateway from a terminal or coding agent: providers, iOS and server apps, usage, and self-hosted deployment on Cloudflare",
6
6
  "keywords": [
@@ -25,7 +25,19 @@
25
25
  "directory": "cli"
26
26
  },
27
27
  "bugs": "https://github.com/maxceem/app-ai-gateway/issues",
28
- "upgradeFrom": ["0.1.8", "0.1.7", "0.1.6", "0.1.5", "0.1.4", "0.1.3", "0.1.2", "0.1.1", "0.1.0"],
28
+ "upgradeFrom": [
29
+ "0.1.10",
30
+ "0.1.9",
31
+ "0.1.8",
32
+ "0.1.7",
33
+ "0.1.6",
34
+ "0.1.5",
35
+ "0.1.4",
36
+ "0.1.3",
37
+ "0.1.2",
38
+ "0.1.1",
39
+ "0.1.0"
40
+ ],
29
41
  "license": "Apache-2.0",
30
42
  "bin": {
31
43
  "agw": "dist/agw.mjs"