@maxceem/agw 0.11.2 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/agw.mjs +164 -8
  2. package/package.json +3 -1
package/dist/agw.mjs CHANGED
@@ -6,7 +6,7 @@ var __export = (target, all) => {
6
6
  };
7
7
 
8
8
  // <define:__AGW_MANIFEST__>
9
- var define_AGW_MANIFEST_default = { version: "0.11.2", wrangler: "4.96.0", upgradeFrom: ["0.11.2", "0.11.1", "0.11.0", "0.10.11", "0.10.10", "0.10.9", "0.10.8", "0.10.7", "0.10.6", "0.10.5", "0.10.4", "0.10.3", "0.10.2", "0.10.1", "0.10.0", "0.9.3", "0.9.2", "0.9.1", "0.9.0", "0.8.3", "0.8.2", "0.8.1", "0.8.0", "0.7.2", "0.7.1", "0.7.0", "0.6.4", "0.6.3", "0.6.2", "0.6.1", "0.6.0", "0.5.2", "0.5.1", "0.5.0", "0.4.0"], release: { url: "https://github.com/maxceem/app-ai-gateway/releases/download/v0.11.2/gateway-0.11.2.tar.gz", sha256: "735cb199e13b5ae6b3ab97d5d5ebda0fa37db4508fd9ba66a346ebd0113f3ac3", manifest: "bbf263b99700b66f07b5a1ed62c0b55566ee484de8c15ae9d2660452a97e20d7" } };
9
+ var define_AGW_MANIFEST_default = { version: "0.12.1", wrangler: "4.96.0", upgradeFrom: ["0.12.1", "0.12.0", "0.11.2", "0.11.1", "0.11.0", "0.10.11", "0.10.10", "0.10.9", "0.10.8", "0.10.7", "0.10.6", "0.10.5", "0.10.4", "0.10.3", "0.10.2", "0.10.1", "0.10.0", "0.9.3", "0.9.2", "0.9.1", "0.9.0", "0.8.3", "0.8.2", "0.8.1", "0.8.0", "0.7.2", "0.7.1", "0.7.0", "0.6.4", "0.6.3", "0.6.2", "0.6.1", "0.6.0", "0.5.2", "0.5.1", "0.5.0", "0.4.0"], release: { url: "https://github.com/maxceem/app-ai-gateway/releases/download/v0.12.1/gateway-0.12.1.tar.gz", sha256: "c234a2d2f97d69a37e9ca2c6ac435e7d6718cceece957852719403dc1a4f421d", manifest: "1ae57d80d3c5f319c89b1fb34f365d59966311f897afd84227c48ded4707c791" } };
10
10
 
11
11
  // src/main.ts
12
12
  import { Writable as Writable2 } from "node:stream";
@@ -15886,6 +15886,30 @@ var ConfirmEmailVerificationRequestSchema = external_exports.object({
15886
15886
  description: "The `token` query parameter of the link in the verification email."
15887
15887
  })
15888
15888
  }).strict();
15889
+ var SupportMessageKindSchema = external_exports.enum(["bug", "support", "feedback"]);
15890
+ var SUPPORT_TOPICS = {
15891
+ bug: "Bug report",
15892
+ support: "Support request",
15893
+ feedback: "Feedback"
15894
+ };
15895
+ var SUPPORT_CONTEXT_KEY = /^[A-Za-z][A-Za-z0-9_.-]{0,39}$/;
15896
+ var SupportMessageRequestSchema = external_exports.object({
15897
+ kind: SupportMessageKindSchema,
15898
+ message: external_exports.string().trim().min(1, "Write a message").max(1e4).meta({
15899
+ description: "What the person wants support to know, in their words. Never include a key, token or password."
15900
+ }),
15901
+ /** The console page the message was written from, for context. */
15902
+ page: ConsoleReturnPathSchema.optional(),
15903
+ context: external_exports.record(
15904
+ external_exports.string().regex(SUPPORT_CONTEXT_KEY, "Context names are letters, digits, _ . and -, up to 40 characters"),
15905
+ external_exports.string().trim().min(1).max(500)
15906
+ ).refine((value) => Object.keys(value).length <= 20, "At most 20 context entries").optional().meta({
15907
+ description: "Facts that help support act without asking back, such as app_id, request_id, error_code or agw_version. Never a credential."
15908
+ }),
15909
+ client: external_exports.string().trim().min(1).max(100).optional().meta({
15910
+ description: "What sent the message, such as `agw 0.10.3`, as the client reports it. A label only."
15911
+ })
15912
+ }).strict();
15889
15913
 
15890
15914
  // src/common.ts
15891
15915
  import { randomBytes } from "node:crypto";
@@ -16120,7 +16144,8 @@ var commands = {
16120
16144
  "usage show": { args: 0, flags: "app month" },
16121
16145
  "usage breakdown": { args: 0, flags: "app by from to limit" },
16122
16146
  "operation status": { args: 1, flags: "" },
16123
- "operation wait": { args: 1, flags: "timeout" }
16147
+ "operation wait": { args: 1, flags: "timeout" },
16148
+ "support send": { args: 0, flags: "type message message-stdin! context* yes!" }
16124
16149
  };
16125
16150
  function flagList(value) {
16126
16151
  if (value === void 0) return [];
@@ -16201,6 +16226,7 @@ function parse3(argv) {
16201
16226
  ["pricing", "clear-pricing"],
16202
16227
  ["route", "clear-route"],
16203
16228
  ["provider", "all-providers"],
16229
+ ["message", "message-stdin"],
16204
16230
  ...command === "app snippet" ? [["provider", "endpoint"]] : []
16205
16231
  ]) {
16206
16232
  if (group.filter((k) => collected[k] !== void 0).length > 1)
@@ -16241,6 +16267,7 @@ Login: account login and deployment connect open the browser; check that it show
16241
16267
  Credentials: ${MANAGEMENT_KEY_ENV}, when set, is the management key for every command and overrides the saved login. account logout revokes the saved key.
16242
16268
  Secrets: hidden prompt by default; --key-stdin reads one line. Provider --browser submits credentials directly to your gateway.
16243
16269
  Browser operations: --json or --no-input returns pending; operation wait resumes.
16270
+ Support: support send emails human support for the signed-in person, who gets the reply by email. Without a terminal it needs --yes: show the person the message first. Never put a key or token in it.
16244
16271
  No account is created by help, status, list, validation or dry runs.
16245
16272
  `;
16246
16273
  }
@@ -17575,7 +17602,13 @@ var ConsoleCapabilitiesResponseSchema = external_exports.object({
17575
17602
  * account's apps serve requests only once its owner has verified theirs, and
17576
17603
  * the console offers the link to do it.
17577
17604
  */
17578
- emailVerification: external_exports.boolean()
17605
+ emailVerification: external_exports.boolean(),
17606
+ /**
17607
+ * The inbox the Support menu writes to, present only where the deployment
17608
+ * has billing and can send email. The console still offers the menu only to
17609
+ * an organization on a paid plan, and the gateway refuses anyone else.
17610
+ */
17611
+ supportEmail: external_exports.email().optional()
17579
17612
  });
17580
17613
  var GoogleSignInResponseSchema = external_exports.object({
17581
17614
  url: external_exports.url().meta({ description: "Provider authorization URL for the browser to navigate to." }),
@@ -18069,6 +18102,16 @@ var ConfirmEmailVerificationResponseSchema = external_exports.object({
18069
18102
  description: "The console path the link was asked for from, to continue there; null for none."
18070
18103
  })
18071
18104
  });
18105
+ var SupportMessageResponseSchema = external_exports.object({
18106
+ status: external_exports.literal("sent"),
18107
+ reference: external_exports.string().meta({
18108
+ description: "The request's reference, such as SUP-7F3K2Q, in the email's subject too. Quote it when following up."
18109
+ }),
18110
+ replyTo: external_exports.string().meta({ description: "The address support will reply to: the sender's own." }),
18111
+ duplicate: external_exports.boolean().meta({
18112
+ description: "True when the same message was sent in the last ten minutes: nothing new was sent, and reference is the earlier one's."
18113
+ })
18114
+ });
18072
18115
  var OrganizationListResponseSchema = external_exports.object({
18073
18116
  organizations: external_exports.array(OrganizationMembershipSchema)
18074
18117
  });
@@ -18977,6 +19020,38 @@ var CATALOG = {
18977
19020
  response: BillingAccessSchema,
18978
19021
  responseDescription: "Trial access state."
18979
19022
  },
19023
+ /*
19024
+ * Support is part of a paid plan, so it sits with billing: it is offered
19025
+ * only to an organization on one, and refused to everyone else. A person in
19026
+ * the console, the CLI and an MCP client all send through this one entry, so
19027
+ * the plan check, the secret check and the limits are the same for each.
19028
+ *
19029
+ * Any role may write, read-only members included: a message changes nothing
19030
+ * in the account. The grant is the default `manage` all the same, because it
19031
+ * emails a person, and a `read` credential changes nothing outside the
19032
+ * gateway either.
19033
+ */
19034
+ sendSupportMessage: {
19035
+ method: "POST",
19036
+ path: "/v1/admin/support",
19037
+ hidden: true,
19038
+ tags: ["Admin billing"],
19039
+ summary: "Send a message to support",
19040
+ description: "Opens a support ticket for the person the credential belongs to: they get an email with its reference and their message, copied to the deployment's support inbox, and support answers them by replying to it. Send only what the person asked to send, and never a credential: a message or context value that looks like a key, token or password is refused. The same message sent again within ten minutes is not sent twice; the answer says so and repeats the reference.",
19041
+ security: "management",
19042
+ policy: { role: "member", access: "read" },
19043
+ request: SupportMessageRequestSchema,
19044
+ response: SupportMessageResponseSchema,
19045
+ responseDescription: "The ticket was opened; support replies to the sender's email address.",
19046
+ errors: {
19047
+ 400: "The body is malformed, or carries something that looks like a credential (`invalid_request`).",
19048
+ 403: "The account's plan does not include human support (`support_plan_required`), or the credential has the read grant (`grant_insufficient`).",
19049
+ 404: "This deployment does not offer human support (`not_found`).",
19050
+ 409: "The credential's owner has no email address to reply to (`conflict`).",
19051
+ 429: "Too many messages were sent by this person. Carries Retry-After.",
19052
+ 503: "The help desk could not be reached; try again later (`email_unavailable`)."
19053
+ }
19054
+ },
18980
19055
  getApp: {
18981
19056
  method: "GET",
18982
19057
  path: "/v1/admin/apps/{app}",
@@ -21935,8 +22010,19 @@ function render(command, result, context, style2) {
21935
22010
  case "deployment update":
21936
22011
  case "deployment domain":
21937
22012
  return deployment(result, style2);
22013
+ case "support send":
22014
+ return support(result, style2);
21938
22015
  }
21939
22016
  }
22017
+ function support(result, style2) {
22018
+ if (!("reference" in result)) return [];
22019
+ return [
22020
+ style2.headline(
22021
+ result.duplicate ? `Already sent as ${result.reference} in the last ten minutes; nothing new was sent.` : `Sent to human support as ${result.reference}.`
22022
+ ),
22023
+ ...kv([["Replies go to", result.replyTo]], style2)
22024
+ ];
22025
+ }
21940
22026
  function status(command, result, context, style2) {
21941
22027
  const did = command === "account login" ? [style2.headline(`Signed in to ${context.url}.`)] : command === "deployment connect" ? [style2.headline(`Connected to ${context.url}.`)] : [];
21942
22028
  if ("billing" in result)
@@ -22364,6 +22450,73 @@ function deployment(result, style2) {
22364
22450
  return [style2.json(result)];
22365
22451
  }
22366
22452
 
22453
+ // src/support.ts
22454
+ import { stderr as stderr2, stdin as stdin2 } from "node:process";
22455
+ var MAX_MESSAGE = 1e4;
22456
+ async function supportCommand(ctx, flags) {
22457
+ const kind = supportKind(flags.type ?? await prompt("Type (bug, support or feedback)", flags));
22458
+ if (flags.message !== void 0 && flags["message-stdin"])
22459
+ fail("conflicting_flags", "--message and --message-stdin conflict.");
22460
+ const message = (flags["message-stdin"] ? await readMessage() : flags.message ?? await prompt("Message", flags)).trim();
22461
+ if (!message) fail("invalid_input", "Supply a nonempty message.");
22462
+ if (message.length > MAX_MESSAGE)
22463
+ fail("invalid_input", `The message is longer than ${MAX_MESSAGE} characters.`);
22464
+ const context = supportContext(flagList(flags.context));
22465
+ if (!flags.yes) {
22466
+ if (flags["no-input"] || !stdin2.isTTY || !stderr2.isTTY)
22467
+ fail(
22468
+ "confirmation_required",
22469
+ "Sending to human support needs confirmation.",
22470
+ "Show the person the exact message, and once they agree run the same command with --yes."
22471
+ );
22472
+ stderr2.write(preview(kind, message, context));
22473
+ await confirm("Send this to human support?", flags);
22474
+ }
22475
+ return (await ctx.call("sendSupportMessage", {
22476
+ body: {
22477
+ kind,
22478
+ message,
22479
+ ...Object.keys(context).length ? { context } : {},
22480
+ client: `agw ${VERSION}`
22481
+ }
22482
+ })).data;
22483
+ }
22484
+ function supportKind(value) {
22485
+ const parsed = SupportMessageKindSchema.safeParse(value.trim().toLowerCase());
22486
+ if (!parsed.success) fail("invalid_input", "--type must be bug, support or feedback.");
22487
+ return parsed.data;
22488
+ }
22489
+ function supportContext(entries) {
22490
+ const context = {};
22491
+ for (const entry of entries) {
22492
+ const at = entry.indexOf("=");
22493
+ if (at < 1 || at === entry.length - 1)
22494
+ fail("invalid_input", "--context takes name=value, such as --context app_id=my-app.");
22495
+ context[entry.slice(0, at).trim()] = entry.slice(at + 1).trim();
22496
+ }
22497
+ return context;
22498
+ }
22499
+ async function readMessage() {
22500
+ const chunks = [];
22501
+ let size = 0;
22502
+ for await (const chunk of stdin2) {
22503
+ size += chunk.length;
22504
+ if (size > MAX_MESSAGE * 4) fail("invalid_input", `The message is longer than ${MAX_MESSAGE} characters.`);
22505
+ chunks.push(chunk);
22506
+ }
22507
+ return Buffer.concat(chunks).toString();
22508
+ }
22509
+ function preview(kind, message, context) {
22510
+ const lines = [
22511
+ `To: human support (${SUPPORT_TOPICS[kind]})`,
22512
+ ...Object.entries(context).map(([name, value]) => `${name}: ${value}`),
22513
+ "",
22514
+ message,
22515
+ ""
22516
+ ];
22517
+ return lines.join("\n") + "\n";
22518
+ }
22519
+
22367
22520
  // src/transport.ts
22368
22521
  var MESSAGE_LIMIT = 300;
22369
22522
  function* strings(value) {
@@ -22392,6 +22545,8 @@ function nextActionFor(status2, code, details) {
22392
22545
  if (status2 === 401) return "Run agw account login.";
22393
22546
  if (code === "billing_plan_limit_reached")
22394
22547
  return "Delete one you no longer need, or move to a plan whose limits are higher.";
22548
+ if (code === "support_plan_required")
22549
+ return "Tell the person human support is included with paid plans; an account owner can choose one on the console's Billing page.";
22395
22550
  if (status2 === 429) {
22396
22551
  const seconds = details.retryAfterSeconds;
22397
22552
  if (seconds === void 0)
@@ -22541,6 +22696,7 @@ async function execute(parsed, ctx) {
22541
22696
  return resourceCommand(ctx, command, args, flags);
22542
22697
  if (command.startsWith("app ")) return appCommand(ctx, command, args, flags);
22543
22698
  if (command.startsWith("usage ")) return usageCommand(ctx, command, flags);
22699
+ if (command === "support send") return supportCommand(ctx, flags);
22544
22700
  if (command.startsWith("operation "))
22545
22701
  return command === "operation wait" ? ctx.wait(args[0] ?? "", positive(flags.timeout ?? 300)) : ctx.poll(args[0] ?? "");
22546
22702
  if (command === "account status") return (await ctx.call("getCliAccount")).data;
@@ -22571,7 +22727,7 @@ async function main(argv = process.argv.slice(2), {
22571
22727
  store = new StateStore(),
22572
22728
  transport = new Transport(),
22573
22729
  stdout = process.stdout,
22574
- stderr: stderr2 = process.stderr,
22730
+ stderr: stderr3 = process.stderr,
22575
22731
  io = {}
22576
22732
  } = {}) {
22577
22733
  const json2 = argv.includes("--json");
@@ -22586,8 +22742,8 @@ async function main(argv = process.argv.slice(2), {
22586
22742
  return 0;
22587
22743
  }
22588
22744
  const run = async () => {
22589
- const state = await store.read((line) => stderr2.write(line + "\n"));
22590
- const ctx = new Context(store, state, transport, parsed.flags, { ...io, stderr: stderr2 });
22745
+ const state = await store.read((line) => stderr3.write(line + "\n"));
22746
+ const ctx = new Context(store, state, transport, parsed.flags, { ...io, stderr: stderr3 });
22591
22747
  const result = await execute(parsed, ctx);
22592
22748
  const context = {
22593
22749
  url: ctx.url,
@@ -22631,7 +22787,7 @@ async function main(argv = process.argv.slice(2), {
22631
22787
  );
22632
22788
  return e.exitCode;
22633
22789
  }
22634
- const style2 = styleFor(stderr2);
22790
+ const style2 = styleFor(stderr3);
22635
22791
  const lines = [
22636
22792
  style2.alert(`Error: ${e.message}`),
22637
22793
  style2.warn(`Next: ${e.nextAction}`),
@@ -22639,7 +22795,7 @@ async function main(argv = process.argv.slice(2), {
22639
22795
  ];
22640
22796
  for (const [key, value] of Object.entries(reported2.details ?? {}))
22641
22797
  lines.push(` ${style2.dim(`${key}:`)} ${String(value)}`);
22642
- stderr2.write(lines.join("\n") + "\n");
22798
+ stderr3.write(lines.join("\n") + "\n");
22643
22799
  return e.exitCode;
22644
22800
  }
22645
22801
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maxceem/agw",
3
- "version": "0.11.2",
3
+ "version": "0.12.1",
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": [
@@ -26,6 +26,8 @@
26
26
  },
27
27
  "bugs": "https://github.com/maxceem/app-ai-gateway/issues",
28
28
  "upgradeFrom": [
29
+ "0.12.0",
30
+ "0.11.2",
29
31
  "0.11.1",
30
32
  "0.11.0",
31
33
  "0.10.11",