@maxceem/agw 0.1.9 → 0.1.10
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.
- package/README.md +4 -1
- package/dist/agw.mjs +685 -149
- package/package.json +13 -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.
|
|
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
|
+
var define_AGW_MANIFEST_default = { version: "0.1.10", wrangler: "4.96.0", upgradeFrom: ["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.10/gateway-0.1.10.tar.gz", sha256: "39a8b65f05d666e323a08db70c90e345dd3eec698cbf7491b288dd5b9f864efa", manifest: "5f58bcdb8dee3dd4ca72f6da3a52ca086f2f7529cd39bd0e331a72779b8fd506" } };
|
|
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 >= " ")
|
|
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
|
|
16262
|
-
|
|
16263
|
-
collected[name] =
|
|
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
|
|
17036
|
-
return "endpoint" in target ? `${
|
|
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(
|
|
17042
|
-
return (
|
|
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(
|
|
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(
|
|
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(
|
|
17086
|
-
return localApp({ name:
|
|
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
|
|
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,
|
|
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", [
|
|
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 ${
|
|
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:
|
|
17343
|
-
if (documentOf(
|
|
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
|
|
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
|
-
|
|
17478
|
+
notes2.push(`This app can reach ${reachable.length} providers. Add --provider <slug> for a different one.`);
|
|
17464
17479
|
}
|
|
17465
|
-
|
|
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
|
-
|
|
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
|
-
...
|
|
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
|
|
17530
|
-
return typeof
|
|
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
|
|
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 ??= {})[
|
|
17640
|
-
return
|
|
17654
|
+
(state.mutations ??= {})[mutation2.id] = mutation2;
|
|
17655
|
+
return mutation2;
|
|
17641
17656
|
}
|
|
17642
17657
|
);
|
|
17643
17658
|
}
|
|
17644
17659
|
async keyOutput(path, body, requestedPath) {
|
|
17645
|
-
const
|
|
17646
|
-
const chosen = requestedPath ? resolve2(requestedPath) :
|
|
17647
|
-
if (
|
|
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 ===
|
|
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 (
|
|
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
|
-
|
|
17683
|
+
mutation2.output = output.reservation;
|
|
17669
17684
|
await this.save();
|
|
17670
17685
|
return {
|
|
17671
17686
|
...output,
|
|
17672
|
-
recoveryAvailable: () => Boolean(
|
|
17673
|
-
mutation,
|
|
17687
|
+
recoveryAvailable: () => Boolean(mutation2.response),
|
|
17688
|
+
mutation: mutation2,
|
|
17674
17689
|
cancel: async () => {
|
|
17675
17690
|
await output.cancel();
|
|
17676
|
-
if (!this.state.mutations?.[
|
|
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
|
|
17698
|
-
if (
|
|
17699
|
-
|
|
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 (
|
|
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 ${
|
|
17721
|
+
`Inspect agw app key list ${mutation2.failure.appId}; use a new key name to replace it.`,
|
|
17707
17722
|
4,
|
|
17708
|
-
|
|
17723
|
+
mutation2.failure
|
|
17709
17724
|
);
|
|
17710
|
-
if (
|
|
17711
|
-
this.deliveredMutations.add(
|
|
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,
|
|
17717
|
-
keyMetadata:
|
|
17731
|
+
data: this.parseRecorded(name, mutation2.result),
|
|
17732
|
+
keyMetadata: mutation2.keyMetadata,
|
|
17718
17733
|
complete: async () => {
|
|
17719
17734
|
}
|
|
17720
17735
|
};
|
|
17721
17736
|
}
|
|
17722
|
-
if (!
|
|
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 (
|
|
17731
|
-
const recovered = this.parse(name,
|
|
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
|
-
|
|
17740
|
-
delete
|
|
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
|
-
|
|
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":
|
|
17757
|
-
"X-Idempotency-Proof":
|
|
17771
|
+
"Idempotency-Key": mutation2.id,
|
|
17772
|
+
"X-Idempotency-Proof": mutation2.proof
|
|
17758
17773
|
}
|
|
17759
17774
|
}).catch(async (error51) => {
|
|
17760
|
-
await this.discard(
|
|
17775
|
+
await this.discard(mutation2, error51);
|
|
17761
17776
|
throw error51;
|
|
17762
17777
|
});
|
|
17763
17778
|
data = response.data;
|
|
17764
|
-
|
|
17779
|
+
mutation2.response = data;
|
|
17765
17780
|
await this.save();
|
|
17766
17781
|
}
|
|
17767
17782
|
return {
|
|
17768
17783
|
data,
|
|
17769
17784
|
keyStored: async (metadata) => {
|
|
17770
|
-
|
|
17785
|
+
mutation2.keyMetadata = metadata;
|
|
17771
17786
|
await this.save();
|
|
17772
17787
|
},
|
|
17773
17788
|
complete: async () => {
|
|
17774
|
-
|
|
17775
|
-
|
|
17776
|
-
delete
|
|
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(
|
|
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(
|
|
17805
|
+
async discard(mutation2, error51) {
|
|
17791
17806
|
if (!definitiveRefusal(error51)) return;
|
|
17792
|
-
delete this.state.mutations?.[
|
|
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
|
|
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:
|
|
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] = { ...
|
|
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
|
|
17950
|
-
if (!
|
|
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 !==
|
|
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:
|
|
17966
|
-
url:
|
|
17980
|
+
key: operation3.pollToken,
|
|
17981
|
+
url: operation3.url
|
|
17967
17982
|
});
|
|
17968
|
-
if (data.state === "completed" && !
|
|
17969
|
-
if ((this.state.generation ?? 0) !==
|
|
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
|
-
|
|
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
|
|
18986
|
-
const
|
|
18987
|
-
|
|
18988
|
-
|
|
18989
|
-
|
|
18990
|
-
|
|
18991
|
-
|
|
18992
|
-
|
|
18993
|
-
|
|
18994
|
-
|
|
18995
|
-
|
|
18996
|
-
|
|
18997
|
-
|
|
18998
|
-
|
|
18999
|
-
|
|
19000
|
-
|
|
19001
|
-
|
|
19002
|
-
|
|
19003
|
-
|
|
19004
|
-
|
|
19005
|
-
|
|
19006
|
-
|
|
19007
|
-
|
|
19008
|
-
|
|
19009
|
-
|
|
19010
|
-
|
|
19011
|
-
|
|
19012
|
-
|
|
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(
|
|
19066
|
-
if (
|
|
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 (
|
|
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
|
|
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", [
|
|
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
|
|
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
|
|
19301
|
-
|
|
19302
|
-
|
|
19303
|
-
|
|
19304
|
-
|
|
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
|
-
|
|
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.
|
|
3
|
+
"version": "0.1.10",
|
|
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,18 @@
|
|
|
25
25
|
"directory": "cli"
|
|
26
26
|
},
|
|
27
27
|
"bugs": "https://github.com/maxceem/app-ai-gateway/issues",
|
|
28
|
-
"upgradeFrom": [
|
|
28
|
+
"upgradeFrom": [
|
|
29
|
+
"0.1.9",
|
|
30
|
+
"0.1.8",
|
|
31
|
+
"0.1.7",
|
|
32
|
+
"0.1.6",
|
|
33
|
+
"0.1.5",
|
|
34
|
+
"0.1.4",
|
|
35
|
+
"0.1.3",
|
|
36
|
+
"0.1.2",
|
|
37
|
+
"0.1.1",
|
|
38
|
+
"0.1.0"
|
|
39
|
+
],
|
|
29
40
|
"license": "Apache-2.0",
|
|
30
41
|
"bin": {
|
|
31
42
|
"agw": "dist/agw.mjs"
|