@maxceem/agw 0.12.1 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agw.mjs +306 -110
- package/package.json +2 -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.
|
|
9
|
+
var define_AGW_MANIFEST_default = { version: "0.13.0", wrangler: "4.96.0", upgradeFrom: ["0.13.0", "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.13.0/gateway-0.13.0.tar.gz", sha256: "02babcd3375a7412195b94c12ae93d4df10e4373ec29b15a496b900647abeb83", manifest: "f652db00f7a44d87c26779ce20a938a7cdefc98fdc8f5f52f2186f995d65a226" } };
|
|
10
10
|
|
|
11
11
|
// src/main.ts
|
|
12
12
|
import { Writable as Writable2 } from "node:stream";
|
|
@@ -15334,6 +15334,7 @@ var APP_ATTEST_VERIFY_ON = ["every_request", "token_exchange"];
|
|
|
15334
15334
|
var ENDPOINT_SLUG = /^[a-z0-9-]{1,64}$/;
|
|
15335
15335
|
var PROVIDER_SLUG_PATTERN = /^[a-z0-9][a-z0-9-]{0,62}$/;
|
|
15336
15336
|
var HTTP_FIELD_NAME = /^[A-Za-z0-9!#$%&'*+.^_`|~-]+$/;
|
|
15337
|
+
var CLIENT_TRACE_ID_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
15337
15338
|
var MONTH_PATTERN = /^\d{4}-(?:0[1-9]|1[0-2])$/;
|
|
15338
15339
|
var MONTH_FORMAT_MESSAGE = "month must use YYYY-MM format";
|
|
15339
15340
|
var MonthSchema = external_exports.string().regex(MONTH_PATTERN, { error: MONTH_FORMAT_MESSAGE });
|
|
@@ -15426,6 +15427,8 @@ var RESERVED_APP_HEADERS = /* @__PURE__ */ new Set([
|
|
|
15426
15427
|
"x-appattest-proof",
|
|
15427
15428
|
...PROVIDER_CREDENTIAL_HEADERS,
|
|
15428
15429
|
"x-app-version",
|
|
15430
|
+
"x-client-trace-id",
|
|
15431
|
+
"x-conversation-storage",
|
|
15429
15432
|
"content-type",
|
|
15430
15433
|
"content-length",
|
|
15431
15434
|
"host"
|
|
@@ -15577,6 +15580,22 @@ function identifiesEndUsers(authentication) {
|
|
|
15577
15580
|
return authentication.end_user.source !== "none";
|
|
15578
15581
|
}
|
|
15579
15582
|
var scopeHasLimits = (scope) => scope.requests.per_minute !== null || scope.requests.per_day !== null || scope.spending.monthly_usd !== null;
|
|
15583
|
+
var CONVERSATION_RETENTION_DAYS = { default: 180, min: 1, max: 400 };
|
|
15584
|
+
var keptMedia = (field, what) => external_exports.boolean({ error: `conversations.keep.${field} must be true or false` }).default(true).meta({
|
|
15585
|
+
description: `Keep ${what} beside the conversation. Off, each is recorded by its type and size alone.`
|
|
15586
|
+
});
|
|
15587
|
+
var ConversationKeepSchema = external_exports.object({
|
|
15588
|
+
images: keptMedia("images", "images"),
|
|
15589
|
+
audio: keptMedia("audio", "audio: recordings sent for transcription, audio in a request, spoken replies"),
|
|
15590
|
+
files: keptMedia("files", "every other attached file, such as a PDF or a text document")
|
|
15591
|
+
}).strict();
|
|
15592
|
+
var ConversationsSchema = external_exports.object({
|
|
15593
|
+
store: external_exports.boolean({ error: "conversations.store must be true or false" }).default(false).meta({
|
|
15594
|
+
description: "Keep a copy of every request and response this app sends through the gateway, with the media `keep` allows."
|
|
15595
|
+
}),
|
|
15596
|
+
keep: ConversationKeepSchema.prefault({}),
|
|
15597
|
+
retention_days: external_exports.number({ error: "conversations.retention_days must be a whole number of days" }).int({ error: "conversations.retention_days must be a whole number of days" }).min(CONVERSATION_RETENTION_DAYS.min, { error: `conversations.retention_days must be at least ${CONVERSATION_RETENTION_DAYS.min}` }).max(CONVERSATION_RETENTION_DAYS.max, { error: `conversations.retention_days must be at most ${CONVERSATION_RETENTION_DAYS.max}` }).default(CONVERSATION_RETENTION_DAYS.default).meta({ description: "Days a stored conversation is kept before nightly maintenance deletes it." })
|
|
15598
|
+
}).strict();
|
|
15580
15599
|
var AppConfigSchema = external_exports.object({
|
|
15581
15600
|
authentication: external_exports.discriminatedUnion("type", [
|
|
15582
15601
|
AppleAppAttestAuthenticationSchema,
|
|
@@ -15614,7 +15633,8 @@ var AppConfigSchema = external_exports.object({
|
|
|
15614
15633
|
safeKey(external_exports.string().regex(ENDPOINT_SLUG)),
|
|
15615
15634
|
EndpointSchema,
|
|
15616
15635
|
{ error: "is not a valid slug; use 1-64 characters from a-z, 0-9, and -" }
|
|
15617
|
-
).prefault({})
|
|
15636
|
+
).prefault({}),
|
|
15637
|
+
conversations: ConversationsSchema.prefault({})
|
|
15618
15638
|
}).strict().superRefine((config2, context) => {
|
|
15619
15639
|
const authentication = config2.authentication;
|
|
15620
15640
|
const perUser = config2.limits?.per_user;
|
|
@@ -16007,7 +16027,7 @@ async function prompt(label, flags, { hidden = false, defaultValue } = {}) {
|
|
|
16007
16027
|
const wasRaw = stdin.isRaw;
|
|
16008
16028
|
stdin.setRawMode(true);
|
|
16009
16029
|
stdin.resume();
|
|
16010
|
-
return new Promise((
|
|
16030
|
+
return new Promise((resolve4, reject2) => {
|
|
16011
16031
|
let value = "";
|
|
16012
16032
|
let shown = 0;
|
|
16013
16033
|
function mask() {
|
|
@@ -16021,7 +16041,7 @@ async function prompt(label, flags, { hidden = false, defaultValue } = {}) {
|
|
|
16021
16041
|
stdin.pause();
|
|
16022
16042
|
stderr.write("\n");
|
|
16023
16043
|
if (error51) reject2(error51);
|
|
16024
|
-
else
|
|
16044
|
+
else resolve4(value);
|
|
16025
16045
|
}
|
|
16026
16046
|
function onData(chunk) {
|
|
16027
16047
|
for (const char of chunk.toString()) {
|
|
@@ -16137,6 +16157,7 @@ var commands = {
|
|
|
16137
16157
|
"app validate": { args: 0, flags: "file" },
|
|
16138
16158
|
"app check": { args: 1, flags: "" },
|
|
16139
16159
|
"app snippet": { args: 1, flags: "language provider endpoint output" },
|
|
16160
|
+
"app conversations": { args: 1, flags: "from to output media!" },
|
|
16140
16161
|
"app remove": { args: 1, flags: "yes!" },
|
|
16141
16162
|
"app key list": { args: 1, flags: "" },
|
|
16142
16163
|
"app key add": { args: 1, flags: "name key-output" },
|
|
@@ -17396,9 +17417,138 @@ function snippetRemedy(code, appId, language) {
|
|
|
17396
17417
|
}
|
|
17397
17418
|
}
|
|
17398
17419
|
|
|
17420
|
+
// src/conversations.ts
|
|
17421
|
+
import { mkdir as mkdir2, readFile as readFile3, stat, writeFile } from "node:fs/promises";
|
|
17422
|
+
import { dirname, join as join2, resolve as resolve2 } from "node:path";
|
|
17423
|
+
import { gunzipSync } from "node:zlib";
|
|
17424
|
+
|
|
17425
|
+
// src/usage.ts
|
|
17426
|
+
var CLI_BREAKDOWNS = ["provider", "model", "status"];
|
|
17427
|
+
function month(value) {
|
|
17428
|
+
if (!MONTH_PATTERN.test(value))
|
|
17429
|
+
fail(
|
|
17430
|
+
"invalid_input",
|
|
17431
|
+
"Month must use YYYY-MM with a valid calendar month."
|
|
17432
|
+
);
|
|
17433
|
+
return value;
|
|
17434
|
+
}
|
|
17435
|
+
function date5(value) {
|
|
17436
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(value) || !Number.isFinite(Date.parse(value)) || new Date(value).toISOString().slice(0, 10) !== value)
|
|
17437
|
+
fail(
|
|
17438
|
+
"invalid_input",
|
|
17439
|
+
"Dates must be real UTC calendar dates in YYYY-MM-DD format."
|
|
17440
|
+
);
|
|
17441
|
+
return value;
|
|
17442
|
+
}
|
|
17443
|
+
function positive(value, max = 3600) {
|
|
17444
|
+
if (!/^\d+$/.test(String(value)) || Number(value) < 1 || Number(value) > max)
|
|
17445
|
+
fail("invalid_input", `Supply an integer between 1 and ${max}.`);
|
|
17446
|
+
return Number(value);
|
|
17447
|
+
}
|
|
17448
|
+
async function usageCommand(ctx, command, flags) {
|
|
17449
|
+
if (command === "usage show") {
|
|
17450
|
+
const m = month(flags.month ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 7));
|
|
17451
|
+
return flags.app ? (await ctx.call("getAppUsage", { params: { app: flags.app }, query: { month: m } })).data : (await ctx.call("getCliUsage", { query: { month: m } })).data;
|
|
17452
|
+
}
|
|
17453
|
+
const app2 = await required2(flags, "app");
|
|
17454
|
+
const by = CLI_BREAKDOWNS.find((dimension) => dimension === (flags.by ?? "model"));
|
|
17455
|
+
if (by === void 0)
|
|
17456
|
+
fail("invalid_input", "--by must be provider, model or status.");
|
|
17457
|
+
if (Boolean(flags.from) !== Boolean(flags.to))
|
|
17458
|
+
fail("invalid_input", "Supply both --from and --to or neither.");
|
|
17459
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
17460
|
+
const from = date5(flags.from ?? today.slice(0, 7) + "-01");
|
|
17461
|
+
const to = date5(flags.to ?? today);
|
|
17462
|
+
if (from > to) fail("invalid_input", "--from must not be later than --to.");
|
|
17463
|
+
const limit = positive(flags.limit ?? 50, 200);
|
|
17464
|
+
return {
|
|
17465
|
+
...(await ctx.call("getAppUsageBreakdown", {
|
|
17466
|
+
params: { app: app2 },
|
|
17467
|
+
query: { from, to, by, limit }
|
|
17468
|
+
})).data,
|
|
17469
|
+
coverage: {
|
|
17470
|
+
from,
|
|
17471
|
+
to,
|
|
17472
|
+
source: "Raw events and retained daily aggregates; dimensions provider, model and status survive raw event retention."
|
|
17473
|
+
}
|
|
17474
|
+
};
|
|
17475
|
+
}
|
|
17476
|
+
|
|
17477
|
+
// src/conversations.ts
|
|
17478
|
+
var MEDIA_REFERENCE = /agw-media:(\d{4}-\d{2}-\d{2}\/media\/[0-9a-f]{64}\.[a-z0-9]{1,5})/g;
|
|
17479
|
+
async function sizeOf(path) {
|
|
17480
|
+
try {
|
|
17481
|
+
return (await stat(path)).size;
|
|
17482
|
+
} catch {
|
|
17483
|
+
return null;
|
|
17484
|
+
}
|
|
17485
|
+
}
|
|
17486
|
+
async function save(path, bytes) {
|
|
17487
|
+
await mkdir2(dirname(path), { recursive: true });
|
|
17488
|
+
await writeFile(path, bytes);
|
|
17489
|
+
}
|
|
17490
|
+
async function conversationsCommand(ctx, appId, flags) {
|
|
17491
|
+
const from = flags.from === void 0 ? null : date5(flags.from);
|
|
17492
|
+
const to = date5(flags.to ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10));
|
|
17493
|
+
if (from !== null && from > to) fail("invalid_input", "--from must not be later than --to.");
|
|
17494
|
+
const output = resolve2(flags.output ?? `conversations-${appId}`);
|
|
17495
|
+
await mkdir2(output, { recursive: true });
|
|
17496
|
+
const result = {
|
|
17497
|
+
app_id: appId,
|
|
17498
|
+
output,
|
|
17499
|
+
from,
|
|
17500
|
+
to,
|
|
17501
|
+
files: 0,
|
|
17502
|
+
downloaded: 0,
|
|
17503
|
+
media: 0,
|
|
17504
|
+
bytes: 0
|
|
17505
|
+
};
|
|
17506
|
+
const batches = [];
|
|
17507
|
+
let cursor;
|
|
17508
|
+
do {
|
|
17509
|
+
const page2 = (await ctx.call("listAppConversations", {
|
|
17510
|
+
params: { app: appId },
|
|
17511
|
+
query: { ...from ? { from } : {}, to, ...cursor ? { cursor } : {} }
|
|
17512
|
+
})).data;
|
|
17513
|
+
for (const file2 of page2.files) {
|
|
17514
|
+
result.files += 1;
|
|
17515
|
+
const path = join2(output, file2.name);
|
|
17516
|
+
batches.push(path);
|
|
17517
|
+
if (await sizeOf(path) === file2.size) continue;
|
|
17518
|
+
const bytes = await ctx.client.download("getAppConversationFile", {
|
|
17519
|
+
params: { app: appId },
|
|
17520
|
+
query: { name: file2.name }
|
|
17521
|
+
});
|
|
17522
|
+
await save(path, bytes);
|
|
17523
|
+
result.downloaded += 1;
|
|
17524
|
+
result.bytes += bytes.byteLength;
|
|
17525
|
+
}
|
|
17526
|
+
cursor = page2.next_cursor ?? void 0;
|
|
17527
|
+
} while (cursor !== void 0);
|
|
17528
|
+
if (flags.media) {
|
|
17529
|
+
const names = /* @__PURE__ */ new Set();
|
|
17530
|
+
for (const path of batches) {
|
|
17531
|
+
const text2 = gunzipSync(await readFile3(path)).toString("utf8");
|
|
17532
|
+
for (const match of text2.matchAll(MEDIA_REFERENCE)) names.add(match[1]);
|
|
17533
|
+
}
|
|
17534
|
+
for (const name of names) {
|
|
17535
|
+
const path = join2(output, name);
|
|
17536
|
+
if (await sizeOf(path) !== null) continue;
|
|
17537
|
+
const bytes = await ctx.client.download("getAppConversationFile", {
|
|
17538
|
+
params: { app: appId },
|
|
17539
|
+
query: { name }
|
|
17540
|
+
});
|
|
17541
|
+
await save(path, bytes);
|
|
17542
|
+
result.media += 1;
|
|
17543
|
+
result.bytes += bytes.byteLength;
|
|
17544
|
+
}
|
|
17545
|
+
}
|
|
17546
|
+
return result;
|
|
17547
|
+
}
|
|
17548
|
+
|
|
17399
17549
|
// src/context.ts
|
|
17400
17550
|
import { createHash as createHash2 } from "node:crypto";
|
|
17401
|
-
import { resolve as
|
|
17551
|
+
import { resolve as resolve3 } from "node:path";
|
|
17402
17552
|
import { spawn } from "node:child_process";
|
|
17403
17553
|
import { setTimeout as delay2 } from "node:timers/promises";
|
|
17404
17554
|
|
|
@@ -17608,7 +17758,9 @@ var ConsoleCapabilitiesResponseSchema = external_exports.object({
|
|
|
17608
17758
|
* has billing and can send email. The console still offers the menu only to
|
|
17609
17759
|
* an organization on a paid plan, and the gateway refuses anyone else.
|
|
17610
17760
|
*/
|
|
17611
|
-
supportEmail: external_exports.email().optional()
|
|
17761
|
+
supportEmail: external_exports.email().optional(),
|
|
17762
|
+
/** Whether this deployment can store conversations: an R2 bucket is bound for it. */
|
|
17763
|
+
conversationStorage: external_exports.boolean()
|
|
17612
17764
|
});
|
|
17613
17765
|
var GoogleSignInResponseSchema = external_exports.object({
|
|
17614
17766
|
url: external_exports.url().meta({ description: "Provider authorization URL for the browser to navigate to." }),
|
|
@@ -17696,8 +17848,26 @@ var UsageEventSchema = external_exports.object({
|
|
|
17696
17848
|
description: "1 when the client disconnected before the upstream finished streaming, which cancelled the provider call; null otherwise. Not a failure \u2014 the request was served as far as the caller wanted it \u2014 but an aborted stream often takes the provider's end-of-response usage with it, which is why such an event may carry cost_source unresolved."
|
|
17697
17849
|
}),
|
|
17698
17850
|
latency_ms: external_exports.number().int().nullable(),
|
|
17851
|
+
client_trace_id: external_exports.string().nullable().meta({
|
|
17852
|
+
description: "The client's X-Client-Trace-Id for this request, or null where it sent none."
|
|
17853
|
+
}),
|
|
17699
17854
|
created_at: external_exports.string()
|
|
17700
17855
|
}).meta({ id: "UsageEvent" });
|
|
17856
|
+
var ConversationFileSchema = external_exports.object({
|
|
17857
|
+
name: external_exports.string().meta({
|
|
17858
|
+
description: "The file's name within the app, `YYYY-MM-DD/HHMMSS-<batch>.jsonl.gz`; pass it to the download operation."
|
|
17859
|
+
}),
|
|
17860
|
+
size: external_exports.number().int().meta({ description: "Bytes, gzipped." }),
|
|
17861
|
+
records: external_exports.number().int().nullable().meta({ description: "Records in the file, where it says." }),
|
|
17862
|
+
uploaded_at: external_exports.string()
|
|
17863
|
+
}).meta({ id: "ConversationFile" });
|
|
17864
|
+
var ConversationFileListSchema = external_exports.object({
|
|
17865
|
+
app_id: external_exports.string(),
|
|
17866
|
+
files: external_exports.array(ConversationFileSchema),
|
|
17867
|
+
next_cursor: external_exports.string().nullable().meta({
|
|
17868
|
+
description: "Pass as cursor to continue the listing; null when it is complete."
|
|
17869
|
+
})
|
|
17870
|
+
}).meta({ id: "ConversationFileList" });
|
|
17701
17871
|
var UsageEventListSchema = external_exports.object({
|
|
17702
17872
|
app_id: external_exports.string(),
|
|
17703
17873
|
limit: external_exports.number().int(),
|
|
@@ -18705,6 +18875,12 @@ var GatewayClientHeadersSchema = external_exports.object({
|
|
|
18705
18875
|
}),
|
|
18706
18876
|
"x-end-user-id": external_exports.string().optional().meta({
|
|
18707
18877
|
description: "Optional configured end-user identity for issuer-less API-key applications."
|
|
18878
|
+
}),
|
|
18879
|
+
"x-client-trace-id": external_exports.string().regex(CLIENT_TRACE_ID_PATTERN).optional().meta({
|
|
18880
|
+
description: "Optional identifier of the client's own unit of work, such as one chat message that takes several requests. Recorded on the usage event and on the stored conversation, so both can be grouped and joined with the app's own data. 1-128 characters from A-Z, a-z, 0-9, `.`, `_`, `:` and `-`; anything else answers 400 invalid_request. Never forwarded to the provider."
|
|
18881
|
+
}),
|
|
18882
|
+
"x-conversation-storage": external_exports.enum(["off"]).optional().meta({
|
|
18883
|
+
description: "`off` keeps this request out of conversation storage even where the app stores conversations, for a user who has opted out. Any other value answers 400 invalid_request. Never forwarded to the provider."
|
|
18708
18884
|
})
|
|
18709
18885
|
});
|
|
18710
18886
|
var MonthQuerySchema = external_exports.object({
|
|
@@ -19351,11 +19527,50 @@ var CATALOG = {
|
|
|
19351
19527
|
user: external_exports.string().optional(),
|
|
19352
19528
|
install: external_exports.string().optional().meta({ description: "Only events of this install: an App Attest key id." }),
|
|
19353
19529
|
model: external_exports.string().optional(),
|
|
19530
|
+
trace: external_exports.string().regex(CLIENT_TRACE_ID_PATTERN).optional().meta({
|
|
19531
|
+
description: "Only the requests the client sent with this X-Client-Trace-Id."
|
|
19532
|
+
}),
|
|
19354
19533
|
before_id: BeforeIdSchema
|
|
19355
19534
|
}),
|
|
19356
19535
|
response: UsageEventListSchema,
|
|
19357
19536
|
responseDescription: "Paginated application usage events."
|
|
19358
19537
|
},
|
|
19538
|
+
listAppConversations: {
|
|
19539
|
+
method: "GET",
|
|
19540
|
+
path: "/v1/admin/apps/{app}/conversations",
|
|
19541
|
+
tags: ["Admin operations"],
|
|
19542
|
+
summary: "List stored conversation files",
|
|
19543
|
+
description: "The batches of stored conversations for an app with conversations.store on, oldest first, by the UTC day of their earliest record. Each file is gzipped JSON lines, one request and its response per line. Media the app keeps (conversations.keep) is stored beside them as `<YYYY-MM-DD>/media/<sha256>.<ext>` and named in the records as `agw-media:<name>`.",
|
|
19544
|
+
security: "management",
|
|
19545
|
+
params: APP_PARAM,
|
|
19546
|
+
policy: { role: "admin", access: "read", grant: "read" },
|
|
19547
|
+
query: external_exports.object({
|
|
19548
|
+
from: DaySchema("from").optional().meta({
|
|
19549
|
+
description: "First UTC day, YYYY-MM-DD; defaults to the oldest stored."
|
|
19550
|
+
}),
|
|
19551
|
+
to: DaySchema("to").optional().meta({
|
|
19552
|
+
description: "Last UTC day, YYYY-MM-DD, inclusive; defaults to today."
|
|
19553
|
+
}),
|
|
19554
|
+
cursor: external_exports.string().max(1024).optional()
|
|
19555
|
+
}),
|
|
19556
|
+
response: ConversationFileListSchema,
|
|
19557
|
+
responseDescription: "One page of stored conversation files."
|
|
19558
|
+
},
|
|
19559
|
+
getAppConversationFile: {
|
|
19560
|
+
method: "GET",
|
|
19561
|
+
path: "/v1/admin/apps/{app}/conversations/file",
|
|
19562
|
+
tags: ["Admin operations"],
|
|
19563
|
+
summary: "Download a stored conversation file",
|
|
19564
|
+
description: "One file named by the listing, or an image a record names: a batch answers as application/gzip, an image as its own type.",
|
|
19565
|
+
security: "management",
|
|
19566
|
+
params: APP_PARAM,
|
|
19567
|
+
policy: { role: "admin", access: "read", grant: "read" },
|
|
19568
|
+
query: external_exports.object({
|
|
19569
|
+
name: external_exports.string().meta({ description: "A file name from the listing, or a media name a record gives, `<YYYY-MM-DD>/media/<sha256>.<ext>`." })
|
|
19570
|
+
}),
|
|
19571
|
+
response: external_exports.unknown(),
|
|
19572
|
+
responseDescription: "The file's bytes."
|
|
19573
|
+
},
|
|
19359
19574
|
getAppAuthEventSummary: {
|
|
19360
19575
|
method: "GET",
|
|
19361
19576
|
path: "/v1/admin/apps/{app}/auth-events/summary",
|
|
@@ -19950,6 +20165,20 @@ var GatewayClient = class {
|
|
|
19950
20165
|
throw error51;
|
|
19951
20166
|
}
|
|
19952
20167
|
}
|
|
20168
|
+
/**
|
|
20169
|
+
* One operation whose success is a file rather than JSON, with the
|
|
20170
|
+
* connection's management credential: its bytes, as the deployment sent them.
|
|
20171
|
+
*/
|
|
20172
|
+
async download(name, { params, query } = {}) {
|
|
20173
|
+
const wire = await this.transport.request(
|
|
20174
|
+
this.source.url(),
|
|
20175
|
+
pathFor(name, params, query),
|
|
20176
|
+
{ method: CATALOG[name].method, key: this.credential(name), binary: true }
|
|
20177
|
+
);
|
|
20178
|
+
if (!(wire.data instanceof Uint8Array))
|
|
20179
|
+
fail("invalid_response", `The deployment answered ${String(name)} with an unusable body.`, void 0, 3);
|
|
20180
|
+
return wire.data;
|
|
20181
|
+
}
|
|
19953
20182
|
/**
|
|
19954
20183
|
* The current management credential, or the refusal that names the way back.
|
|
19955
20184
|
* An admin operation is also reachable from a plain deployment connection,
|
|
@@ -20003,8 +20232,8 @@ var FAILED_PAGE = page(
|
|
|
20003
20232
|
);
|
|
20004
20233
|
async function listenLoopback() {
|
|
20005
20234
|
let deliver;
|
|
20006
|
-
const code = new Promise((
|
|
20007
|
-
deliver =
|
|
20235
|
+
const code = new Promise((resolve4) => {
|
|
20236
|
+
deliver = resolve4;
|
|
20008
20237
|
});
|
|
20009
20238
|
let waiting = null;
|
|
20010
20239
|
let delivered = false;
|
|
@@ -20023,11 +20252,11 @@ async function listenLoopback() {
|
|
|
20023
20252
|
waiting = response;
|
|
20024
20253
|
deliver(value);
|
|
20025
20254
|
});
|
|
20026
|
-
await new Promise((
|
|
20255
|
+
await new Promise((resolve4, reject2) => {
|
|
20027
20256
|
server.once("error", reject2);
|
|
20028
20257
|
server.listen(0, "127.0.0.1", () => {
|
|
20029
20258
|
server.off("error", reject2);
|
|
20030
|
-
|
|
20259
|
+
resolve4();
|
|
20031
20260
|
});
|
|
20032
20261
|
});
|
|
20033
20262
|
const { port } = server.address();
|
|
@@ -20039,21 +20268,21 @@ async function listenLoopback() {
|
|
|
20039
20268
|
const response = waiting;
|
|
20040
20269
|
if (!response) return;
|
|
20041
20270
|
waiting = null;
|
|
20042
|
-
answered = new Promise((
|
|
20043
|
-
response.once("finish",
|
|
20044
|
-
response.once("close",
|
|
20271
|
+
answered = new Promise((resolve4) => {
|
|
20272
|
+
response.once("finish", resolve4);
|
|
20273
|
+
response.once("close", resolve4);
|
|
20045
20274
|
});
|
|
20046
20275
|
response.writeHead(200, PAGE_HEADERS).end(connected ? CONNECTED_PAGE : FAILED_PAGE);
|
|
20047
20276
|
},
|
|
20048
20277
|
async close() {
|
|
20049
20278
|
loopback.answer(false);
|
|
20050
20279
|
await answered;
|
|
20051
|
-
await new Promise((
|
|
20280
|
+
await new Promise((resolve4) => {
|
|
20052
20281
|
const force = setTimeout(() => server.closeAllConnections(), 1e3);
|
|
20053
20282
|
force.unref();
|
|
20054
20283
|
server.close(() => {
|
|
20055
20284
|
clearTimeout(force);
|
|
20056
|
-
|
|
20285
|
+
resolve4();
|
|
20057
20286
|
});
|
|
20058
20287
|
server.closeIdleConnections();
|
|
20059
20288
|
});
|
|
@@ -20360,7 +20589,7 @@ Operation: ${data.id}
|
|
|
20360
20589
|
);
|
|
20361
20590
|
const target = origin(this.url);
|
|
20362
20591
|
const [id, record2] = await this.reserveOperation(kind, payload, target);
|
|
20363
|
-
const chosen = requestedPath ?
|
|
20592
|
+
const chosen = requestedPath ? resolve3(requestedPath) : record2.output?.path;
|
|
20364
20593
|
if (record2.completedAt && chosen !== record2.output?.path)
|
|
20365
20594
|
fail(
|
|
20366
20595
|
"output_changed",
|
|
@@ -20861,29 +21090,29 @@ function assertTrustedApprovalUrl(url2, capabilities, target) {
|
|
|
20861
21090
|
async function openBrowser(url2) {
|
|
20862
21091
|
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "rundll32" : "xdg-open";
|
|
20863
21092
|
const args = process.platform === "win32" ? ["url.dll,FileProtocolHandler", url2] : [url2];
|
|
20864
|
-
return new Promise((
|
|
21093
|
+
return new Promise((resolve4) => {
|
|
20865
21094
|
const child = spawn(command, args, { stdio: "ignore", detached: true });
|
|
20866
|
-
child.once("error", () =>
|
|
21095
|
+
child.once("error", () => resolve4(false));
|
|
20867
21096
|
child.once("spawn", () => {
|
|
20868
21097
|
child.unref();
|
|
20869
|
-
|
|
21098
|
+
resolve4(true);
|
|
20870
21099
|
});
|
|
20871
21100
|
});
|
|
20872
21101
|
}
|
|
20873
21102
|
|
|
20874
21103
|
// src/deployment.ts
|
|
20875
21104
|
import { createHash as createHash4, randomUUID } from "node:crypto";
|
|
20876
|
-
import { writeFile as
|
|
20877
|
-
import { join as
|
|
21105
|
+
import { writeFile as writeFile4, unlink as unlink2 } from "node:fs/promises";
|
|
21106
|
+
import { join as join5 } from "node:path";
|
|
20878
21107
|
|
|
20879
21108
|
// src/release.ts
|
|
20880
|
-
import { readFile as
|
|
20881
|
-
import { join as
|
|
21109
|
+
import { readFile as readFile4, lstat as lstat2, mkdir as mkdir3, rename as rename2, rm, writeFile as writeFile2 } from "node:fs/promises";
|
|
21110
|
+
import { join as join3, dirname as dirname2 } from "node:path";
|
|
20882
21111
|
import { fileURLToPath } from "node:url";
|
|
20883
21112
|
import { createHash as createHash3, randomBytes as randomBytes3 } from "node:crypto";
|
|
20884
21113
|
|
|
20885
21114
|
// src/archive.ts
|
|
20886
|
-
import { gunzipSync } from "node:zlib";
|
|
21115
|
+
import { gunzipSync as gunzipSync2 } from "node:zlib";
|
|
20887
21116
|
var BLOCK = 512;
|
|
20888
21117
|
var ARCHIVE_LIMIT = 64 * 1024 * 1024;
|
|
20889
21118
|
function text(header, start, length) {
|
|
@@ -20920,7 +21149,7 @@ function safePath(raw, directory) {
|
|
|
20920
21149
|
function readArchive(gzipped, limit = ARCHIVE_LIMIT) {
|
|
20921
21150
|
let tar;
|
|
20922
21151
|
try {
|
|
20923
|
-
tar =
|
|
21152
|
+
tar = gunzipSync2(gzipped, { maxOutputLength: limit });
|
|
20924
21153
|
} catch {
|
|
20925
21154
|
fail("release_archive", "Release archive is not readable gzip within the size limit.");
|
|
20926
21155
|
}
|
|
@@ -21001,26 +21230,26 @@ async function fetchArchive(url2) {
|
|
|
21001
21230
|
return body;
|
|
21002
21231
|
}
|
|
21003
21232
|
async function extract(archive, directory) {
|
|
21004
|
-
await
|
|
21233
|
+
await mkdir3(directory, { recursive: true, mode: 448 });
|
|
21005
21234
|
for (const entry of readArchive(archive)) {
|
|
21006
|
-
const path =
|
|
21235
|
+
const path = join3(directory, entry.path);
|
|
21007
21236
|
if (entry.directory) {
|
|
21008
|
-
await
|
|
21237
|
+
await mkdir3(path, { recursive: true, mode: 448 });
|
|
21009
21238
|
continue;
|
|
21010
21239
|
}
|
|
21011
|
-
await
|
|
21012
|
-
await
|
|
21240
|
+
await mkdir3(dirname2(path), { recursive: true, mode: 448 });
|
|
21241
|
+
await writeFile2(path, entry.data, { mode: 384, flag: "wx" });
|
|
21013
21242
|
}
|
|
21014
21243
|
}
|
|
21015
21244
|
async function cachedRelease(version2, integrity, source) {
|
|
21016
|
-
const root = source.cache ??
|
|
21017
|
-
const directory =
|
|
21018
|
-
const cached2 = await
|
|
21245
|
+
const root = source.cache ?? join3(cacheDirectory(), "releases");
|
|
21246
|
+
const directory = join3(root, version2);
|
|
21247
|
+
const cached2 = await readFile4(join3(directory, "manifest.json")).catch(() => null);
|
|
21019
21248
|
if (cached2 && digest(cached2) === integrity.manifest) return directory;
|
|
21020
21249
|
const supplied = source.archive ?? process.env["AGW_RELEASE_ARCHIVE"] ?? void 0;
|
|
21021
21250
|
let archive;
|
|
21022
21251
|
if (supplied) {
|
|
21023
|
-
archive = await
|
|
21252
|
+
archive = await readFile4(supplied).catch(
|
|
21024
21253
|
() => fail(
|
|
21025
21254
|
"release_unavailable",
|
|
21026
21255
|
`Cannot read the gateway release archive at ${supplied}.`,
|
|
@@ -21039,10 +21268,10 @@ async function cachedRelease(version2, integrity, source) {
|
|
|
21039
21268
|
4
|
|
21040
21269
|
);
|
|
21041
21270
|
await protectedDirectory(root);
|
|
21042
|
-
const staging =
|
|
21271
|
+
const staging = join3(root, `.${version2}-${randomBytes3(8).toString("hex")}`);
|
|
21043
21272
|
try {
|
|
21044
21273
|
await extract(archive, staging);
|
|
21045
|
-
const manifest2 = await
|
|
21274
|
+
const manifest2 = await readFile4(join3(staging, "manifest.json")).catch(() => null);
|
|
21046
21275
|
if (!manifest2 || digest(manifest2) !== integrity.manifest)
|
|
21047
21276
|
fail(
|
|
21048
21277
|
"release_integrity",
|
|
@@ -21057,7 +21286,7 @@ async function cachedRelease(version2, integrity, source) {
|
|
|
21057
21286
|
}
|
|
21058
21287
|
return directory;
|
|
21059
21288
|
}
|
|
21060
|
-
async function release2(requested = VERSION, base =
|
|
21289
|
+
async function release2(requested = VERSION, base = dirname2(fileURLToPath(import.meta.url)), source = {}) {
|
|
21061
21290
|
if (requested !== VERSION)
|
|
21062
21291
|
fail(
|
|
21063
21292
|
"unsupported_release",
|
|
@@ -21068,7 +21297,7 @@ async function release2(requested = VERSION, base = dirname(fileURLToPath(import
|
|
|
21068
21297
|
if (major === void 0 || minor === void 0 || major < 22 || major === 22 && minor < 19)
|
|
21069
21298
|
fail("unsupported_runtime", "Node.js 22.19.0 or newer is required.");
|
|
21070
21299
|
const integrity = source.integrity === void 0 ? RELEASE : source.integrity;
|
|
21071
|
-
const packaged =
|
|
21300
|
+
const packaged = join3(base, "release", requested);
|
|
21072
21301
|
const local = await lstat2(packaged).catch(() => null);
|
|
21073
21302
|
let directory;
|
|
21074
21303
|
if (local?.isDirectory()) directory = packaged;
|
|
@@ -21082,7 +21311,7 @@ async function release2(requested = VERSION, base = dirname(fileURLToPath(import
|
|
|
21082
21311
|
);
|
|
21083
21312
|
let serialized;
|
|
21084
21313
|
try {
|
|
21085
|
-
serialized = await
|
|
21314
|
+
serialized = await readFile4(join3(directory, "manifest.json"), "utf8");
|
|
21086
21315
|
} catch {
|
|
21087
21316
|
fail(
|
|
21088
21317
|
"release_missing",
|
|
@@ -21102,16 +21331,16 @@ async function release2(requested = VERSION, base = dirname(fileURLToPath(import
|
|
|
21102
21331
|
for (const [path, hash2] of Object.entries(manifest2.files)) {
|
|
21103
21332
|
if (path.startsWith("/") || path.split(/[\\/]/).some((p) => p === ".." || p === ""))
|
|
21104
21333
|
fail("release_integrity", "Release includes an unsafe path.");
|
|
21105
|
-
const file2 =
|
|
21334
|
+
const file2 = join3(directory, path);
|
|
21106
21335
|
const info = await lstat2(file2);
|
|
21107
|
-
if (!info.isFile() || info.isSymbolicLink() || digest(await
|
|
21336
|
+
if (!info.isFile() || info.isSymbolicLink() || digest(await readFile4(file2)) !== hash2)
|
|
21108
21337
|
fail("release_integrity", "Release asset integrity check failed.");
|
|
21109
21338
|
}
|
|
21110
21339
|
return {
|
|
21111
21340
|
directory,
|
|
21112
21341
|
manifest: manifest2,
|
|
21113
21342
|
config: JSON.parse(
|
|
21114
|
-
await
|
|
21343
|
+
await readFile4(join3(directory, "wrangler.json"), "utf8")
|
|
21115
21344
|
)
|
|
21116
21345
|
};
|
|
21117
21346
|
}
|
|
@@ -21147,7 +21376,7 @@ function wranglerEnvironment({
|
|
|
21147
21376
|
};
|
|
21148
21377
|
}
|
|
21149
21378
|
async function runWrangler(args, { cwd, input, interactive = false, redact } = {}) {
|
|
21150
|
-
return new Promise((
|
|
21379
|
+
return new Promise((resolve4, reject2) => {
|
|
21151
21380
|
const child = spawn2(process.execPath, [require2.resolve("wrangler/bin/wrangler.js"), ...args], {
|
|
21152
21381
|
...cwd === void 0 ? {} : { cwd },
|
|
21153
21382
|
env: wranglerEnvironment({ interactive }),
|
|
@@ -21177,7 +21406,7 @@ async function runWrangler(args, { cwd, input, interactive = false, redact } = {
|
|
|
21177
21406
|
);
|
|
21178
21407
|
child.once("close", (code) => {
|
|
21179
21408
|
if (code !== 0) reject2(wranglerFailure(args, code, errors || output, redact));
|
|
21180
|
-
else
|
|
21409
|
+
else resolve4(output);
|
|
21181
21410
|
});
|
|
21182
21411
|
});
|
|
21183
21412
|
}
|
|
@@ -21367,8 +21596,8 @@ async function whileWarming(run, sleep = delay3, report = (line) => process.stde
|
|
|
21367
21596
|
}
|
|
21368
21597
|
|
|
21369
21598
|
// src/installation.ts
|
|
21370
|
-
import { cp, writeFile as
|
|
21371
|
-
import { join as
|
|
21599
|
+
import { cp, writeFile as writeFile3 } from "node:fs/promises";
|
|
21600
|
+
import { join as join4 } from "node:path";
|
|
21372
21601
|
import { setTimeout as delay4 } from "node:timers/promises";
|
|
21373
21602
|
async function advance(ctx, journal, phase, patch = {}) {
|
|
21374
21603
|
const fields = journal;
|
|
@@ -21395,9 +21624,9 @@ function selectedInstallation(ctx) {
|
|
|
21395
21624
|
};
|
|
21396
21625
|
}
|
|
21397
21626
|
async function configFile(ctx, journal, artifact) {
|
|
21398
|
-
const directory =
|
|
21627
|
+
const directory = join4(ctx.store.directory, "deployments", journal.id);
|
|
21399
21628
|
await protectedDirectory(directory);
|
|
21400
|
-
await cp(artifact.directory,
|
|
21629
|
+
await cp(artifact.directory, join4(directory, "release"), { recursive: true });
|
|
21401
21630
|
const config2 = {
|
|
21402
21631
|
...artifact.config,
|
|
21403
21632
|
name: journal.name,
|
|
@@ -21425,8 +21654,8 @@ async function configFile(ctx, journal, artifact) {
|
|
|
21425
21654
|
}))
|
|
21426
21655
|
} : {}
|
|
21427
21656
|
};
|
|
21428
|
-
const path =
|
|
21429
|
-
await
|
|
21657
|
+
const path = join4(directory, "wrangler.json");
|
|
21658
|
+
await writeFile3(path, JSON.stringify(config2, null, 2), { mode: 384 });
|
|
21430
21659
|
return { path, directory, config: config2 };
|
|
21431
21660
|
}
|
|
21432
21661
|
async function verifyDeployment(ctx, url2, id) {
|
|
@@ -21533,7 +21762,8 @@ async function currentInventory(cf, accountId, name, settings, expectedDatabaseI
|
|
|
21533
21762
|
DB: "d1",
|
|
21534
21763
|
USER_LIMITER: "durable_object_namespace",
|
|
21535
21764
|
ORG_QUOTA: "durable_object_namespace",
|
|
21536
|
-
ENDPOINT_RATE_LIMITER: "durable_object_namespace"
|
|
21765
|
+
ENDPOINT_RATE_LIMITER: "durable_object_namespace",
|
|
21766
|
+
CONVERSATION_LOG: "durable_object_namespace"
|
|
21537
21767
|
};
|
|
21538
21768
|
if (bindings.some(
|
|
21539
21769
|
(b) => !["plain_text", "secret_text"].includes(b.type) && resourceTypes[b.name] !== b.type
|
|
@@ -21546,7 +21776,8 @@ async function currentInventory(cf, accountId, name, settings, expectedDatabaseI
|
|
|
21546
21776
|
const localClasses = {
|
|
21547
21777
|
USER_LIMITER: "UserLimiter",
|
|
21548
21778
|
ORG_QUOTA: "OrgQuota",
|
|
21549
|
-
ENDPOINT_RATE_LIMITER: "EndpointRateLimiter"
|
|
21779
|
+
ENDPOINT_RATE_LIMITER: "EndpointRateLimiter",
|
|
21780
|
+
CONVERSATION_LOG: "ConversationLog"
|
|
21550
21781
|
};
|
|
21551
21782
|
if (bindings.some(
|
|
21552
21783
|
(binding) => binding.type === "durable_object_namespace" && (binding.class_name !== localClasses[binding.name] || binding.script_name && binding.script_name !== name || binding.environment && binding.environment !== "production")
|
|
@@ -21770,8 +22001,8 @@ async function install(ctx, cf, journal, artifact, url2, domain2) {
|
|
|
21770
22001
|
// the account is this CLI's however soon anyone else finds the address.
|
|
21771
22002
|
CLI_BOOTSTRAP_TOKEN_DIGEST: createHash4("sha256").update(journal.bootstrap.token).digest("hex")
|
|
21772
22003
|
};
|
|
21773
|
-
const secretPath =
|
|
21774
|
-
await
|
|
22004
|
+
const secretPath = join5(file2.directory, "secrets.json");
|
|
22005
|
+
await writeFile4(secretPath, JSON.stringify(secrets), {
|
|
21775
22006
|
mode: 384
|
|
21776
22007
|
});
|
|
21777
22008
|
try {
|
|
@@ -22012,8 +22243,23 @@ function render(command, result, context, style2) {
|
|
|
22012
22243
|
return deployment(result, style2);
|
|
22013
22244
|
case "support send":
|
|
22014
22245
|
return support(result, style2);
|
|
22246
|
+
case "app conversations":
|
|
22247
|
+
return conversations(result, style2);
|
|
22015
22248
|
}
|
|
22016
22249
|
}
|
|
22250
|
+
function conversations(result, style2) {
|
|
22251
|
+
if (!("downloaded" in result)) return [];
|
|
22252
|
+
return [
|
|
22253
|
+
style2.headline(
|
|
22254
|
+
result.files === 0 ? `No stored conversations for ${result.app_id} in that range.` : `Exported ${result.files} conversation files for ${result.app_id}.`
|
|
22255
|
+
),
|
|
22256
|
+
...kv([
|
|
22257
|
+
["Folder", result.output],
|
|
22258
|
+
["Downloaded", `${result.downloaded} files${result.media ? `, ${result.media} media` : ""}`],
|
|
22259
|
+
["Range", `${result.from ?? "oldest"} to ${result.to}`]
|
|
22260
|
+
], style2)
|
|
22261
|
+
];
|
|
22262
|
+
}
|
|
22017
22263
|
function support(result, style2) {
|
|
22018
22264
|
if (!("reference" in result)) return [];
|
|
22019
22265
|
return [
|
|
@@ -22560,7 +22806,7 @@ var Transport = class {
|
|
|
22560
22806
|
constructor(fetchImpl = fetch) {
|
|
22561
22807
|
this.fetch = fetchImpl;
|
|
22562
22808
|
}
|
|
22563
|
-
async request(url2, path, { key, method = "GET", body, headers = {} } = {}) {
|
|
22809
|
+
async request(url2, path, { key, method = "GET", body, headers = {}, binary } = {}) {
|
|
22564
22810
|
const base = origin(url2);
|
|
22565
22811
|
if (!path.startsWith("/") || path.startsWith("//"))
|
|
22566
22812
|
fail("invalid_request", "Invalid API path.");
|
|
@@ -22571,7 +22817,7 @@ var Transport = class {
|
|
|
22571
22817
|
redirect: "manual",
|
|
22572
22818
|
signal: AbortSignal.timeout(3e4),
|
|
22573
22819
|
headers: {
|
|
22574
|
-
accept: "application/json",
|
|
22820
|
+
accept: binary ? "*/*" : "application/json",
|
|
22575
22821
|
"user-agent": `agw/${VERSION}`,
|
|
22576
22822
|
...body ? { "content-type": "application/json" } : {},
|
|
22577
22823
|
...key ? { authorization: `Bearer ${key}` } : {},
|
|
@@ -22594,6 +22840,7 @@ var Transport = class {
|
|
|
22594
22840
|
"Connect explicitly to the intended deployment.",
|
|
22595
22841
|
3
|
|
22596
22842
|
);
|
|
22843
|
+
if (binary && response.ok) return { data: new Uint8Array(await response.arrayBuffer()) };
|
|
22597
22844
|
let data;
|
|
22598
22845
|
try {
|
|
22599
22846
|
data = await response.json();
|
|
@@ -22637,63 +22884,12 @@ var Transport = class {
|
|
|
22637
22884
|
}
|
|
22638
22885
|
};
|
|
22639
22886
|
|
|
22640
|
-
// src/usage.ts
|
|
22641
|
-
var CLI_BREAKDOWNS = ["provider", "model", "status"];
|
|
22642
|
-
function month(value) {
|
|
22643
|
-
if (!MONTH_PATTERN.test(value))
|
|
22644
|
-
fail(
|
|
22645
|
-
"invalid_input",
|
|
22646
|
-
"Month must use YYYY-MM with a valid calendar month."
|
|
22647
|
-
);
|
|
22648
|
-
return value;
|
|
22649
|
-
}
|
|
22650
|
-
function date5(value) {
|
|
22651
|
-
if (!/^\d{4}-\d{2}-\d{2}$/.test(value) || !Number.isFinite(Date.parse(value)) || new Date(value).toISOString().slice(0, 10) !== value)
|
|
22652
|
-
fail(
|
|
22653
|
-
"invalid_input",
|
|
22654
|
-
"Dates must be real UTC calendar dates in YYYY-MM-DD format."
|
|
22655
|
-
);
|
|
22656
|
-
return value;
|
|
22657
|
-
}
|
|
22658
|
-
function positive(value, max = 3600) {
|
|
22659
|
-
if (!/^\d+$/.test(String(value)) || Number(value) < 1 || Number(value) > max)
|
|
22660
|
-
fail("invalid_input", `Supply an integer between 1 and ${max}.`);
|
|
22661
|
-
return Number(value);
|
|
22662
|
-
}
|
|
22663
|
-
async function usageCommand(ctx, command, flags) {
|
|
22664
|
-
if (command === "usage show") {
|
|
22665
|
-
const m = month(flags.month ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 7));
|
|
22666
|
-
return flags.app ? (await ctx.call("getAppUsage", { params: { app: flags.app }, query: { month: m } })).data : (await ctx.call("getCliUsage", { query: { month: m } })).data;
|
|
22667
|
-
}
|
|
22668
|
-
const app2 = await required2(flags, "app");
|
|
22669
|
-
const by = CLI_BREAKDOWNS.find((dimension) => dimension === (flags.by ?? "model"));
|
|
22670
|
-
if (by === void 0)
|
|
22671
|
-
fail("invalid_input", "--by must be provider, model or status.");
|
|
22672
|
-
if (Boolean(flags.from) !== Boolean(flags.to))
|
|
22673
|
-
fail("invalid_input", "Supply both --from and --to or neither.");
|
|
22674
|
-
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
22675
|
-
const from = date5(flags.from ?? today.slice(0, 7) + "-01");
|
|
22676
|
-
const to = date5(flags.to ?? today);
|
|
22677
|
-
if (from > to) fail("invalid_input", "--from must not be later than --to.");
|
|
22678
|
-
const limit = positive(flags.limit ?? 50, 200);
|
|
22679
|
-
return {
|
|
22680
|
-
...(await ctx.call("getAppUsageBreakdown", {
|
|
22681
|
-
params: { app: app2 },
|
|
22682
|
-
query: { from, to, by, limit }
|
|
22683
|
-
})).data,
|
|
22684
|
-
coverage: {
|
|
22685
|
-
from,
|
|
22686
|
-
to,
|
|
22687
|
-
source: "Raw events and retained daily aggregates; dimensions provider, model and status survive raw event retention."
|
|
22688
|
-
}
|
|
22689
|
-
};
|
|
22690
|
-
}
|
|
22691
|
-
|
|
22692
22887
|
// src/main.ts
|
|
22693
22888
|
async function execute(parsed, ctx) {
|
|
22694
22889
|
const { command, flags, args } = parsed;
|
|
22695
22890
|
if (command.startsWith("provider"))
|
|
22696
22891
|
return resourceCommand(ctx, command, args, flags);
|
|
22892
|
+
if (command === "app conversations") return conversationsCommand(ctx, args[0] ?? "", flags);
|
|
22697
22893
|
if (command.startsWith("app ")) return appCommand(ctx, command, args, flags);
|
|
22698
22894
|
if (command.startsWith("usage ")) return usageCommand(ctx, command, flags);
|
|
22699
22895
|
if (command === "support send") return supportCommand(ctx, flags);
|
|
@@ -22755,9 +22951,9 @@ async function main(argv = process.argv.slice(2), {
|
|
|
22755
22951
|
const text2 = json2 ? JSON.stringify(output) + "\n" : humanResult(parsed.command, rendered, context, styleFor(stdout));
|
|
22756
22952
|
if (stdout instanceof Writable2) {
|
|
22757
22953
|
await new Promise(
|
|
22758
|
-
(
|
|
22954
|
+
(resolve4, reject2) => stdout.write(
|
|
22759
22955
|
text2,
|
|
22760
|
-
(error51) => error51 ? reject2(error51) :
|
|
22956
|
+
(error51) => error51 ? reject2(error51) : resolve4()
|
|
22761
22957
|
)
|
|
22762
22958
|
);
|
|
22763
22959
|
} else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maxceem/agw",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
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,7 @@
|
|
|
26
26
|
},
|
|
27
27
|
"bugs": "https://github.com/maxceem/app-ai-gateway/issues",
|
|
28
28
|
"upgradeFrom": [
|
|
29
|
+
"0.12.1",
|
|
29
30
|
"0.12.0",
|
|
30
31
|
"0.11.2",
|
|
31
32
|
"0.11.1",
|