@maxceem/agw 0.1.8 → 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 +48 -9
- package/dist/agw.mjs +1004 -218
- package/package.json +13 -2
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 = {};
|
|
@@ -15018,7 +15018,7 @@ var ErrorResponseSchema = external_exports.object({
|
|
|
15018
15018
|
code: external_exports.string(),
|
|
15019
15019
|
message: external_exports.string(),
|
|
15020
15020
|
data: external_exports.record(external_exports.string(), external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean()])).optional().meta({
|
|
15021
|
-
description: "Machine-readable facts about this rejection, present only where the code alone is not actionable. A billing_request_quota_exceeded rejection carries periodId, periodStart, periodEnd, limit, used, and resetAt; an app_rate_limited or app_budget_exhausted rejection carries scope, either user or app."
|
|
15021
|
+
description: "Machine-readable facts about this rejection, present only where the code alone is not actionable. A billing_request_quota_exceeded rejection carries periodId, periodStart, periodEnd, limit, used, and resetAt; a billing_plan_limit_reached rejection carries limit and used; an app_rate_limited or app_budget_exhausted rejection carries scope, either user or app; a rate_limited rejection carries scope naming the endpoint policy that refused, plus limit, windowSeconds, retryAfterSeconds and resetAt."
|
|
15022
15022
|
})
|
|
15023
15023
|
})
|
|
15024
15024
|
}).meta({ id: "ErrorResponse" });
|
|
@@ -15751,12 +15751,27 @@ var CliErrorDetailsSchema = external_exports.object({
|
|
|
15751
15751
|
providerGatewayId: external_exports.string().optional(),
|
|
15752
15752
|
revoked: external_exports.boolean().optional(),
|
|
15753
15753
|
storagePath: external_exports.string().optional(),
|
|
15754
|
+
/** Which ceiling refused the request, for the codes that name one. */
|
|
15755
|
+
scope: external_exports.string().optional(),
|
|
15756
|
+
limit: external_exports.number().optional(),
|
|
15757
|
+
used: external_exports.number().optional(),
|
|
15758
|
+
windowSeconds: external_exports.number().optional(),
|
|
15759
|
+
retryAfterSeconds: external_exports.number().optional(),
|
|
15760
|
+
resetAt: external_exports.string().optional(),
|
|
15754
15761
|
id: external_exports.string().optional(),
|
|
15755
15762
|
state: external_exports.string().optional(),
|
|
15756
15763
|
expiresAt: external_exports.string().optional(),
|
|
15757
15764
|
url: external_exports.string().optional(),
|
|
15758
15765
|
/** The tail of a failed subprocess's own output, for failures it explains. */
|
|
15759
|
-
output: external_exports.string().optional()
|
|
15766
|
+
output: external_exports.string().optional(),
|
|
15767
|
+
/**
|
|
15768
|
+
* What Cloudflare itself said about a refused API call: each error's message
|
|
15769
|
+
* and its code. Carried because the things that refuse a deployment — an
|
|
15770
|
+
* account ceiling, a permission a token lacks — are named only there, and
|
|
15771
|
+
* wrangler reports an API refusal it aggregates as the bare sentence that a
|
|
15772
|
+
* request failed.
|
|
15773
|
+
*/
|
|
15774
|
+
apiErrors: external_exports.string().optional()
|
|
15760
15775
|
});
|
|
15761
15776
|
|
|
15762
15777
|
// src/apps.ts
|
|
@@ -16035,6 +16050,11 @@ function validate(schema, value) {
|
|
|
16035
16050
|
// src/input.ts
|
|
16036
16051
|
import { createInterface } from "node:readline/promises";
|
|
16037
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
|
+
}
|
|
16038
16058
|
async function prompt(label, flags, { hidden = false, defaultValue } = {}) {
|
|
16039
16059
|
if (flags["no-input"] || !stdin.isTTY || !stderr.isTTY)
|
|
16040
16060
|
fail(
|
|
@@ -16058,6 +16078,12 @@ async function prompt(label, flags, { hidden = false, defaultValue } = {}) {
|
|
|
16058
16078
|
stdin.resume();
|
|
16059
16079
|
return new Promise((resolve3, reject2) => {
|
|
16060
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
|
+
}
|
|
16061
16087
|
function done(error51) {
|
|
16062
16088
|
stdin.off("data", onData);
|
|
16063
16089
|
stdin.setRawMode(wasRaw);
|
|
@@ -16078,9 +16104,13 @@ async function prompt(label, flags, { hidden = false, defaultValue } = {}) {
|
|
|
16078
16104
|
}
|
|
16079
16105
|
if (char === "\x7F" || char === "\b") {
|
|
16080
16106
|
value = value.slice(0, -1);
|
|
16107
|
+
mask();
|
|
16081
16108
|
continue;
|
|
16082
16109
|
}
|
|
16083
|
-
if (char >= " ")
|
|
16110
|
+
if (char >= " ") {
|
|
16111
|
+
value += char;
|
|
16112
|
+
mask();
|
|
16113
|
+
}
|
|
16084
16114
|
if (value.length > 16384) {
|
|
16085
16115
|
done(new Error("input too long"));
|
|
16086
16116
|
return;
|
|
@@ -16223,7 +16253,10 @@ function parse3(argv) {
|
|
|
16223
16253
|
if (!match || !types[match[1]])
|
|
16224
16254
|
fail(
|
|
16225
16255
|
"unknown_flag",
|
|
16226
|
-
`Unknown flag ${token.split("=")[0]} for ${command}
|
|
16256
|
+
`Unknown flag ${token.split("=")[0]} for ${command}.`,
|
|
16257
|
+
// Resources are addressed positionally throughout, so the one mistake
|
|
16258
|
+
// worth answering with more than the help text is naming one as a flag.
|
|
16259
|
+
spec.args && ["id", "app", "app-id"].includes(match?.[1] ?? "") ? `Pass the identifier as an argument: agw ${command}${" <id>".repeat(spec.args)}.` : void 0
|
|
16227
16260
|
);
|
|
16228
16261
|
const name = match[1];
|
|
16229
16262
|
const inline = match[2];
|
|
@@ -16240,9 +16273,9 @@ function parse3(argv) {
|
|
|
16240
16273
|
fail("missing_value", `--${name} requires a value.`);
|
|
16241
16274
|
if (type === "multiple") {
|
|
16242
16275
|
const existing = collected[name];
|
|
16243
|
-
const
|
|
16244
|
-
|
|
16245
|
-
collected[name] =
|
|
16276
|
+
const list2 = Array.isArray(existing) ? existing : [];
|
|
16277
|
+
list2.push(value);
|
|
16278
|
+
collected[name] = list2;
|
|
16246
16279
|
} else collected[name] = value;
|
|
16247
16280
|
}
|
|
16248
16281
|
}
|
|
@@ -16935,14 +16968,137 @@ var StateStore = class {
|
|
|
16935
16968
|
}
|
|
16936
16969
|
};
|
|
16937
16970
|
|
|
16971
|
+
// ../src/shared/first-request.ts
|
|
16972
|
+
var PROVIDER_PLACEHOLDER = "PROVIDER_SLUG";
|
|
16973
|
+
var MODEL_PLACEHOLDER = "MODEL";
|
|
16974
|
+
var BODY_PLACEHOLDER = "REQUEST_BODY";
|
|
16975
|
+
var EXAMPLE_GAP_NOTES = {
|
|
16976
|
+
provider: `No provider is configured yet, so ${PROVIDER_PLACEHOLDER} stands in for one. Add a provider key, then use its slug.`,
|
|
16977
|
+
model: `No priced model was found for this provider, so ${MODEL_PLACEHOLDER} stands in for one. Use a model the provider serves.`,
|
|
16978
|
+
body: `This app allows no path whose request shape is known here, so ${BODY_PLACEHOLDER} stands in for the body the provider documents.`
|
|
16979
|
+
};
|
|
16980
|
+
function examplePath(type, { gatewayRouted = false, model } = {}) {
|
|
16981
|
+
if (type === "openai") return "v1/responses";
|
|
16982
|
+
if (type === "anthropic") return "v1/messages";
|
|
16983
|
+
if (type === "gemini")
|
|
16984
|
+
return model ? `v1beta/models/${model}:generateContent` : void 0;
|
|
16985
|
+
if (gatewayRouted) return "v1/chat/completions";
|
|
16986
|
+
if (type === "groq") return "openai/v1/chat/completions";
|
|
16987
|
+
if (type === "fireworks") return "inference/v1/chat/completions";
|
|
16988
|
+
if (["deepseek", "perplexity", "bytedance"].includes(type)) return "chat/completions";
|
|
16989
|
+
return "v1/chat/completions";
|
|
16990
|
+
}
|
|
16991
|
+
function bodyFor(path, model) {
|
|
16992
|
+
const messages = [{ role: "user", content: "Say hello." }];
|
|
16993
|
+
if (path.endsWith("responses")) return { model, input: "Say hello." };
|
|
16994
|
+
if (path.endsWith("chat/completions")) return { model, messages };
|
|
16995
|
+
if (path.endsWith("messages")) return { model, max_tokens: 128, messages };
|
|
16996
|
+
if (path.endsWith(":generateContent"))
|
|
16997
|
+
return { contents: [{ parts: [{ text: "Say hello." }] }] };
|
|
16998
|
+
return null;
|
|
16999
|
+
}
|
|
17000
|
+
function modelsFor(provider, policy2, prices) {
|
|
17001
|
+
if (policy2?.allowed_models?.length) return policy2.allowed_models;
|
|
17002
|
+
return [
|
|
17003
|
+
...Object.keys(provider.pricing ?? {}),
|
|
17004
|
+
...Object.entries(prices[provider.type] ?? {}).filter(([, price]) => price.output !== void 0).map(([model]) => model)
|
|
17005
|
+
];
|
|
17006
|
+
}
|
|
17007
|
+
function firstRequest(routing, providers, prices) {
|
|
17008
|
+
let fallback;
|
|
17009
|
+
for (const provider of providers) {
|
|
17010
|
+
if (provider.status !== "active") continue;
|
|
17011
|
+
const policy2 = routing?.providers?.[provider.slug];
|
|
17012
|
+
if (routing?.providerMode === "selected" && !policy2) continue;
|
|
17013
|
+
const models = modelsFor(provider, policy2, prices);
|
|
17014
|
+
const model = models[0] ?? MODEL_PLACEHOLDER;
|
|
17015
|
+
const gaps = models.length ? [] : ["model"];
|
|
17016
|
+
const fallbackPath = examplePath(provider.type, {
|
|
17017
|
+
gatewayRouted: Boolean(provider.providerGatewayId),
|
|
17018
|
+
model
|
|
17019
|
+
});
|
|
17020
|
+
const paths = policy2?.allowed_paths?.length ? policy2.allowed_paths : fallbackPath ? [fallbackPath] : [];
|
|
17021
|
+
for (const entry of paths) {
|
|
17022
|
+
const path = typeof entry === "string" ? entry : entry.path;
|
|
17023
|
+
if (path.includes("*")) continue;
|
|
17024
|
+
const fixed = typeof entry !== "string" ? entry.fixed_model : void 0;
|
|
17025
|
+
const body = bodyFor(path, fixed ?? model);
|
|
17026
|
+
const example = {
|
|
17027
|
+
target: { provider: provider.slug, path },
|
|
17028
|
+
body,
|
|
17029
|
+
anthropic: path.endsWith("messages"),
|
|
17030
|
+
// A fixed model is the app's own, so a catalog that has none of its
|
|
17031
|
+
// own to offer is not a gap on a path that never carries one.
|
|
17032
|
+
gaps: body && fixed ? [] : gaps
|
|
17033
|
+
};
|
|
17034
|
+
if (body) return example;
|
|
17035
|
+
fallback ??= { ...example, gaps: [...example.gaps, "body"] };
|
|
17036
|
+
}
|
|
17037
|
+
}
|
|
17038
|
+
return fallback ?? {
|
|
17039
|
+
target: { provider: PROVIDER_PLACEHOLDER, path: "v1/chat/completions" },
|
|
17040
|
+
body: bodyFor("v1/chat/completions", MODEL_PLACEHOLDER),
|
|
17041
|
+
anthropic: false,
|
|
17042
|
+
gaps: ["provider", "model"]
|
|
17043
|
+
};
|
|
17044
|
+
}
|
|
17045
|
+
function exampleNotes(example) {
|
|
17046
|
+
return example.gaps.map((gap) => EXAMPLE_GAP_NOTES[gap]);
|
|
17047
|
+
}
|
|
17048
|
+
function exampleUrl(baseUrl, appId, target) {
|
|
17049
|
+
const origin2 = baseUrl.replace(/\/+$/, "");
|
|
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}`;
|
|
17052
|
+
}
|
|
17053
|
+
function shellQuote(value) {
|
|
17054
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
17055
|
+
}
|
|
17056
|
+
function commented(notes2, marker) {
|
|
17057
|
+
return (notes2 ?? []).map((note) => `${marker} ${note}
|
|
17058
|
+
`).join("");
|
|
17059
|
+
}
|
|
17060
|
+
function curlSnippet(options) {
|
|
17061
|
+
const { baseUrl, appId, example, notes: notes2, keyExpression = "$APP_AI_GATEWAY_KEY" } = options;
|
|
17062
|
+
const body = example.body ? JSON.stringify(example.body) : BODY_PLACEHOLDER;
|
|
17063
|
+
return commented(notes2, "#") + `curl --fail-with-body ${shellQuote(exampleUrl(baseUrl, appId, example.target))} \\
|
|
17064
|
+
-H "Authorization: Bearer ${keyExpression}" \\
|
|
17065
|
+
-H 'Content-Type: application/json' \\
|
|
17066
|
+
` + (example.anthropic ? " -H 'anthropic-version: 2023-06-01' \\\n" : "") + ` -d ${shellQuote(body)}
|
|
17067
|
+
`;
|
|
17068
|
+
}
|
|
17069
|
+
function swiftSnippet(options) {
|
|
17070
|
+
const { baseUrl, appId, example, notes: notes2, authMode = ".appAttestInstall" } = options;
|
|
17071
|
+
const target = "endpoint" in example.target ? `endpointSlug: ${JSON.stringify(example.target.endpoint)}` : `provider: ${JSON.stringify(example.target.provider)},
|
|
17072
|
+
providerPath: ${JSON.stringify(example.target.path)}`;
|
|
17073
|
+
const body = example.body ? `request.httpBody = Data(${JSON.stringify(JSON.stringify(example.body))}.utf8)` : `// Supply the provider-native JSON body.
|
|
17074
|
+
request.httpBody = Data(${JSON.stringify(BODY_PLACEHOLDER)}.utf8)`;
|
|
17075
|
+
return commented(notes2, "//") + `import Foundation
|
|
17076
|
+
import AppAIGateway
|
|
17077
|
+
|
|
17078
|
+
let gateway = AppAIGatewayClient(
|
|
17079
|
+
appID: ${JSON.stringify(appId)},
|
|
17080
|
+
baseURL: URL(string: ${JSON.stringify(baseUrl.replace(/\/+$/, ""))})!,
|
|
17081
|
+
authMode: ${authMode}
|
|
17082
|
+
)
|
|
17083
|
+
|
|
17084
|
+
var request = try await gateway.authorizedRequest(
|
|
17085
|
+
${target}
|
|
17086
|
+
)
|
|
17087
|
+
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
17088
|
+
${example.anthropic ? 'request.setValue("2023-06-01", forHTTPHeaderField: "anthropic-version")\n' : ""}${body}
|
|
17089
|
+
let (data, _) = try await URLSession.shared.data(for: request)
|
|
17090
|
+
print(String(decoding: data, as: UTF8.self))
|
|
17091
|
+
`;
|
|
17092
|
+
}
|
|
17093
|
+
|
|
16938
17094
|
// src/apps.ts
|
|
16939
17095
|
var unlimited = () => ({
|
|
16940
17096
|
requests: { per_minute: null, per_day: null },
|
|
16941
17097
|
spending: { monthly_usd: null }
|
|
16942
17098
|
});
|
|
16943
17099
|
var policy = () => ({ allowed_paths: [], allowed_models: [] });
|
|
16944
|
-
function documentOf(
|
|
16945
|
-
return localApp({ name:
|
|
17100
|
+
function documentOf(app2) {
|
|
17101
|
+
return localApp({ name: app2.name, config: app2.config, status: app2.status });
|
|
16946
17102
|
}
|
|
16947
17103
|
function localApp(value) {
|
|
16948
17104
|
const doc = validate(AppWriteSchema, value);
|
|
@@ -17143,14 +17299,14 @@ async function appCommand(ctx, command, args, flags) {
|
|
|
17143
17299
|
};
|
|
17144
17300
|
const output = action === "add" && doc2.config.authentication.type === "api_key" ? await ctx.keyOutput(operations.createApp.path(), doc2, flags["key-output"]) : null;
|
|
17145
17301
|
try {
|
|
17146
|
-
let
|
|
17302
|
+
let app2;
|
|
17147
17303
|
let resolved;
|
|
17148
17304
|
let configError;
|
|
17149
17305
|
let key;
|
|
17150
17306
|
if (action === "add") {
|
|
17151
17307
|
await ctx.bootstrap();
|
|
17152
17308
|
const created = await ctx.create("createApp", [], doc2);
|
|
17153
|
-
({ app, resolved, config_error: configError } = created.data);
|
|
17309
|
+
({ app: app2, resolved, config_error: configError } = created.data);
|
|
17154
17310
|
if (output) {
|
|
17155
17311
|
if (created.keyMetadata) key = created.keyMetadata;
|
|
17156
17312
|
else {
|
|
@@ -17162,7 +17318,7 @@ async function appCommand(ctx, command, args, flags) {
|
|
|
17162
17318
|
"Inspect the app key list before retrying.",
|
|
17163
17319
|
3
|
|
17164
17320
|
);
|
|
17165
|
-
key = await saveKey(ctx, minted, output,
|
|
17321
|
+
key = await saveKey(ctx, minted, output, app2.id);
|
|
17166
17322
|
}
|
|
17167
17323
|
await created.keyStored?.(key);
|
|
17168
17324
|
}
|
|
@@ -17171,23 +17327,25 @@ async function appCommand(ctx, command, args, flags) {
|
|
|
17171
17327
|
const updated = await ctx.call("updateApp", [appId2], {
|
|
17172
17328
|
body: { ...doc2, revision }
|
|
17173
17329
|
});
|
|
17174
|
-
({ app, resolved, config_error: configError } = updated.data);
|
|
17330
|
+
({ app: app2, resolved, config_error: configError } = updated.data);
|
|
17175
17331
|
}
|
|
17176
17332
|
let snippet;
|
|
17177
|
-
|
|
17178
|
-
|
|
17179
|
-
|
|
17180
|
-
|
|
17181
|
-
} catch {
|
|
17182
|
-
}
|
|
17333
|
+
try {
|
|
17334
|
+
const example = await appCommand(ctx, "app snippet", [app2.id], {});
|
|
17335
|
+
if ("snippet" in example) snippet = example.snippet;
|
|
17336
|
+
} catch {
|
|
17183
17337
|
}
|
|
17338
|
+
if (snippet && key)
|
|
17339
|
+
snippet = `export APP_AI_GATEWAY_KEY="$(cat ${shellQuote(key.storagePath)})"
|
|
17340
|
+
|
|
17341
|
+
${snippet}`;
|
|
17184
17342
|
return {
|
|
17185
17343
|
...snippet ? { snippet } : {},
|
|
17186
|
-
app,
|
|
17344
|
+
app: app2,
|
|
17187
17345
|
resolved,
|
|
17188
17346
|
config_error: configError,
|
|
17189
17347
|
...key ? { applicationKey: key } : {},
|
|
17190
|
-
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.`
|
|
17191
17349
|
};
|
|
17192
17350
|
} finally {
|
|
17193
17351
|
await output?.cancel();
|
|
@@ -17196,8 +17354,8 @@ async function appCommand(ctx, command, args, flags) {
|
|
|
17196
17354
|
if (action.startsWith("key ")) {
|
|
17197
17355
|
const sub = action.slice(4);
|
|
17198
17356
|
const appId2 = args[0] ?? "";
|
|
17199
|
-
const { data:
|
|
17200
|
-
if (documentOf(
|
|
17357
|
+
const { data: app2 } = await ctx.call("getApp", [appId2]);
|
|
17358
|
+
if (documentOf(app2.app).config.authentication.type !== "api_key")
|
|
17201
17359
|
fail(
|
|
17202
17360
|
"invalid_input",
|
|
17203
17361
|
"Only server applications support application keys."
|
|
@@ -17274,62 +17432,75 @@ async function appCommand(ctx, command, args, flags) {
|
|
|
17274
17432
|
};
|
|
17275
17433
|
}
|
|
17276
17434
|
if (action === "snippet") {
|
|
17277
|
-
|
|
17278
|
-
|
|
17279
|
-
if (
|
|
17435
|
+
const ios = doc.config.authentication.type === "apple_app_attest";
|
|
17436
|
+
const language = flags.language ?? (ios ? "swift" : "curl");
|
|
17437
|
+
if (!["swift", "curl"].includes(language))
|
|
17438
|
+
fail("invalid_input", "--language must be swift or curl.");
|
|
17439
|
+
if (language === "swift" !== ios)
|
|
17280
17440
|
fail(
|
|
17281
17441
|
"unsupported_snippet",
|
|
17282
|
-
"Swift snippets are for iOS applications.",
|
|
17283
|
-
|
|
17442
|
+
ios ? "iOS applications authenticate with App Attest, which curl cannot perform." : "Swift snippets are for iOS applications.",
|
|
17443
|
+
`Run agw app snippet ${appId} --language ${ios ? "swift" : "curl"}.`
|
|
17284
17444
|
);
|
|
17285
|
-
|
|
17445
|
+
const notes2 = [];
|
|
17446
|
+
let example;
|
|
17286
17447
|
if (flags.endpoint) {
|
|
17287
|
-
|
|
17448
|
+
const endpoint = doc.config.endpoints?.[flags.endpoint];
|
|
17449
|
+
if (!endpoint)
|
|
17288
17450
|
fail("endpoint_not_found", "Choose an existing named endpoint.");
|
|
17289
|
-
|
|
17451
|
+
const responses = endpoint.api_style === "responses";
|
|
17452
|
+
example = {
|
|
17453
|
+
target: { endpoint: flags.endpoint },
|
|
17454
|
+
body: responses ? { input: "Say hello." } : null,
|
|
17455
|
+
anthropic: false,
|
|
17456
|
+
gaps: responses ? [] : ["body"]
|
|
17457
|
+
};
|
|
17290
17458
|
} else {
|
|
17459
|
+
const routing = {
|
|
17460
|
+
providerMode: doc.config.routing.providers.mode,
|
|
17461
|
+
providers: doc.config.routing.providers.selected
|
|
17462
|
+
};
|
|
17291
17463
|
const { data: all } = await ctx.call("listProviders", []);
|
|
17292
|
-
|
|
17293
|
-
(
|
|
17464
|
+
const reachable = all.providers.filter(
|
|
17465
|
+
(p) => p.status === "active" && (routing.providerMode === "all" || Object.hasOwn(routing.providers ?? {}, p.slug))
|
|
17294
17466
|
);
|
|
17295
17467
|
const requested = typeof flags.provider === "string" ? flags.provider : void 0;
|
|
17296
|
-
if (requested
|
|
17297
|
-
const p = providers[0];
|
|
17298
|
-
if (providers.length !== 1 || !p)
|
|
17468
|
+
if (requested && !reachable.some((p) => p.slug === requested))
|
|
17299
17469
|
fail(
|
|
17300
|
-
"
|
|
17301
|
-
|
|
17470
|
+
all.providers.some((p) => p.slug === requested) ? "provider_unavailable" : "provider_not_found",
|
|
17471
|
+
all.providers.some((p) => p.slug === requested) ? "That provider is disabled or outside this app's proxy policy." : "No provider has that slug.",
|
|
17472
|
+
"Run agw provider list for the slugs, and agw app show <id> for the policy."
|
|
17302
17473
|
);
|
|
17303
|
-
const { data:
|
|
17304
|
-
const
|
|
17305
|
-
|
|
17306
|
-
if (!
|
|
17307
|
-
|
|
17308
|
-
|
|
17309
|
-
|
|
17310
|
-
|
|
17474
|
+
const { data: catalog } = await ctx.call("listModelPrices", []);
|
|
17475
|
+
const providers = requested ? reachable.filter((p) => p.slug === requested) : reachable;
|
|
17476
|
+
example = firstRequest(routing, providers, catalog.prices);
|
|
17477
|
+
if (!requested && reachable.length > 1)
|
|
17478
|
+
notes2.push(`This app can reach ${reachable.length} providers. Add --provider <slug> for a different one.`);
|
|
17479
|
+
}
|
|
17480
|
+
notes2.push(...exampleNotes(example));
|
|
17481
|
+
const clientUrl = ctx.active?.deployment?.apiUrl ?? ctx.url;
|
|
17482
|
+
let snippet;
|
|
17483
|
+
if (ios) {
|
|
17484
|
+
const endUser = doc.config.authentication.type === "apple_app_attest" ? doc.config.authentication.end_user : void 0;
|
|
17485
|
+
const issuer = endUser?.source === "issuer";
|
|
17486
|
+
if (issuer)
|
|
17487
|
+
notes2.push(
|
|
17488
|
+
"Replace yourIdentitySDK.currentIDToken(forceRefresh: forceRefresh) with your configured issuer integration."
|
|
17311
17489
|
);
|
|
17312
|
-
|
|
17313
|
-
|
|
17314
|
-
|
|
17315
|
-
|
|
17316
|
-
|
|
17317
|
-
|
|
17318
|
-
|
|
17319
|
-
|
|
17320
|
-
|
|
17321
|
-
|
|
17322
|
-
|
|
17323
|
-
|
|
17324
|
-
|
|
17325
|
-
|
|
17326
|
-
)
|
|
17327
|
-
|
|
17328
|
-
var request = try await gateway.authorizedRequest(
|
|
17329
|
-
${target}
|
|
17330
|
-
)
|
|
17331
|
-
// Supply the provider-native JSON body, then send request with URLSession.
|
|
17332
|
-
`;
|
|
17490
|
+
snippet = swiftSnippet({
|
|
17491
|
+
baseUrl: clientUrl,
|
|
17492
|
+
appId,
|
|
17493
|
+
example,
|
|
17494
|
+
authMode: issuer ? ".appAttest(issuerTokenProvider: { forceRefresh in\n // Return a fresh signed token from your configured identity SDK.\n try await yourIdentitySDK.currentIDToken(forceRefresh: forceRefresh)\n })" : ".appAttestInstall",
|
|
17495
|
+
notes: [
|
|
17496
|
+
"Swift package: https://github.com/maxceem/app-ai-gateway-swift (from: 1.0.0)",
|
|
17497
|
+
"Enable App Attest and test on a supported physical device.",
|
|
17498
|
+
...notes2
|
|
17499
|
+
]
|
|
17500
|
+
});
|
|
17501
|
+
} else {
|
|
17502
|
+
snippet = curlSnippet({ baseUrl: clientUrl, appId, example, notes: notes2 });
|
|
17503
|
+
}
|
|
17333
17504
|
if (flags.output) {
|
|
17334
17505
|
const output = await reserveOutput(flags.output);
|
|
17335
17506
|
try {
|
|
@@ -17339,13 +17510,7 @@ var request = try await gateway.authorizedRequest(
|
|
|
17339
17510
|
await output.cancel();
|
|
17340
17511
|
}
|
|
17341
17512
|
}
|
|
17342
|
-
return {
|
|
17343
|
-
language: "swift",
|
|
17344
|
-
snippet,
|
|
17345
|
-
...issuer ? {
|
|
17346
|
-
requires: "Replace yourIdentitySDK.currentIDToken(forceRefresh: forceRefresh) with your configured issuer integration."
|
|
17347
|
-
} : {}
|
|
17348
|
-
};
|
|
17513
|
+
return { language: ios ? "swift" : "shell", snippet, notes: notes2 };
|
|
17349
17514
|
}
|
|
17350
17515
|
fail("unknown_command", "Unknown command.");
|
|
17351
17516
|
}
|
|
@@ -17376,8 +17541,8 @@ var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 425, 429]);
|
|
|
17376
17541
|
var RECEIPT_BOUND_STATUS = /* @__PURE__ */ new Set([409, 410]);
|
|
17377
17542
|
function definitiveRefusal(error51) {
|
|
17378
17543
|
if (!(error51 instanceof CliError)) return false;
|
|
17379
|
-
const
|
|
17380
|
-
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);
|
|
17381
17546
|
}
|
|
17382
17547
|
var Context = class {
|
|
17383
17548
|
store;
|
|
@@ -17477,7 +17642,7 @@ var Context = class {
|
|
|
17477
17642
|
(entry) => entry.requestHash === requestHash && (entry.accountId === null || entry.accountId === this.active?.account?.id)
|
|
17478
17643
|
),
|
|
17479
17644
|
(state) => {
|
|
17480
|
-
const
|
|
17645
|
+
const mutation2 = {
|
|
17481
17646
|
id: randomToken(),
|
|
17482
17647
|
proof: randomToken(),
|
|
17483
17648
|
requestHash,
|
|
@@ -17486,27 +17651,27 @@ var Context = class {
|
|
|
17486
17651
|
path,
|
|
17487
17652
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
17488
17653
|
};
|
|
17489
|
-
(state.mutations ??= {})[
|
|
17490
|
-
return
|
|
17654
|
+
(state.mutations ??= {})[mutation2.id] = mutation2;
|
|
17655
|
+
return mutation2;
|
|
17491
17656
|
}
|
|
17492
17657
|
);
|
|
17493
17658
|
}
|
|
17494
17659
|
async keyOutput(path, body, requestedPath) {
|
|
17495
|
-
const
|
|
17496
|
-
const chosen = requestedPath ? resolve2(requestedPath) :
|
|
17497
|
-
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)
|
|
17498
17663
|
fail(
|
|
17499
17664
|
"output_changed",
|
|
17500
17665
|
"A completed key creation cannot move its output.",
|
|
17501
17666
|
"Copy the protected existing file, or create a replacement with a new key name.",
|
|
17502
17667
|
4
|
|
17503
17668
|
);
|
|
17504
|
-
const existing = chosen && chosen ===
|
|
17669
|
+
const existing = chosen && chosen === mutation2.output?.path ? mutation2.output : void 0;
|
|
17505
17670
|
const output = await this.store.keyOutput(chosen, {
|
|
17506
17671
|
reservation: existing,
|
|
17507
17672
|
retain: true
|
|
17508
17673
|
});
|
|
17509
|
-
if (
|
|
17674
|
+
if (mutation2.completedAt && mutation2.keyMetadata?.contentHash !== await output.contentHash()) {
|
|
17510
17675
|
await output.cancel();
|
|
17511
17676
|
fail(
|
|
17512
17677
|
"output_changed",
|
|
@@ -17515,15 +17680,15 @@ var Context = class {
|
|
|
17515
17680
|
4
|
|
17516
17681
|
);
|
|
17517
17682
|
}
|
|
17518
|
-
|
|
17683
|
+
mutation2.output = output.reservation;
|
|
17519
17684
|
await this.save();
|
|
17520
17685
|
return {
|
|
17521
17686
|
...output,
|
|
17522
|
-
recoveryAvailable: () => Boolean(
|
|
17523
|
-
mutation,
|
|
17687
|
+
recoveryAvailable: () => Boolean(mutation2.response),
|
|
17688
|
+
mutation: mutation2,
|
|
17524
17689
|
cancel: async () => {
|
|
17525
17690
|
await output.cancel();
|
|
17526
|
-
if (!this.state.mutations?.[
|
|
17691
|
+
if (!this.state.mutations?.[mutation2.id])
|
|
17527
17692
|
await releaseOutput(output.reservation);
|
|
17528
17693
|
}
|
|
17529
17694
|
};
|
|
@@ -17544,32 +17709,32 @@ var Context = class {
|
|
|
17544
17709
|
);
|
|
17545
17710
|
const descriptor = operations[name];
|
|
17546
17711
|
const path = descriptor.path(...params);
|
|
17547
|
-
const
|
|
17548
|
-
if (
|
|
17549
|
-
|
|
17712
|
+
const mutation2 = await this.prepareCreate(path, body);
|
|
17713
|
+
if (mutation2.accountId === null) {
|
|
17714
|
+
mutation2.accountId = this.active.account?.id ?? null;
|
|
17550
17715
|
await this.save();
|
|
17551
17716
|
}
|
|
17552
|
-
if (
|
|
17717
|
+
if (mutation2.failure)
|
|
17553
17718
|
fail(
|
|
17554
17719
|
"key_storage_failed",
|
|
17555
17720
|
"This creation ended with a revoked or unverified key.",
|
|
17556
|
-
`Inspect agw app key list ${
|
|
17721
|
+
`Inspect agw app key list ${mutation2.failure.appId}; use a new key name to replace it.`,
|
|
17557
17722
|
4,
|
|
17558
|
-
|
|
17723
|
+
mutation2.failure
|
|
17559
17724
|
);
|
|
17560
|
-
if (
|
|
17561
|
-
this.deliveredMutations.add(
|
|
17725
|
+
if (mutation2.completedAt) {
|
|
17726
|
+
this.deliveredMutations.add(mutation2.id);
|
|
17562
17727
|
return {
|
|
17563
17728
|
// Parsed against the recorded shape, not the live one: this copy never
|
|
17564
17729
|
// held the plaintext, so the live schema would reject it — and a state
|
|
17565
17730
|
// file that somehow grew one is stripped again on the way out.
|
|
17566
|
-
data: this.parseRecorded(name,
|
|
17567
|
-
keyMetadata:
|
|
17731
|
+
data: this.parseRecorded(name, mutation2.result),
|
|
17732
|
+
keyMetadata: mutation2.keyMetadata,
|
|
17568
17733
|
complete: async () => {
|
|
17569
17734
|
}
|
|
17570
17735
|
};
|
|
17571
17736
|
}
|
|
17572
|
-
if (!
|
|
17737
|
+
if (!mutation2.response && Date.now() - Date.parse(mutation2.createdAt) > 90 * 24 * 60 * 60 * 1e3)
|
|
17573
17738
|
fail(
|
|
17574
17739
|
"resource_retry_expired",
|
|
17575
17740
|
"This unfinished creation is older than the supported 90-day retry window.",
|
|
@@ -17577,8 +17742,8 @@ var Context = class {
|
|
|
17577
17742
|
4
|
|
17578
17743
|
);
|
|
17579
17744
|
let data;
|
|
17580
|
-
if (
|
|
17581
|
-
const recovered = this.parse(name,
|
|
17745
|
+
if (mutation2.response !== void 0) {
|
|
17746
|
+
const recovered = this.parse(name, mutation2.response);
|
|
17582
17747
|
const keyRecord = recoveredKey(name, recovered);
|
|
17583
17748
|
if (keyRecord) {
|
|
17584
17749
|
const appId = recoveredAppId(name, recovered, params);
|
|
@@ -17586,15 +17751,15 @@ var Context = class {
|
|
|
17586
17751
|
if (!existing.keys.some(
|
|
17587
17752
|
(key) => key.id === keyRecord.id && key.status === "active"
|
|
17588
17753
|
)) {
|
|
17589
|
-
|
|
17590
|
-
delete
|
|
17754
|
+
mutation2.failure = { appId, keyId: keyRecord.id, revoked: true };
|
|
17755
|
+
delete mutation2.response;
|
|
17591
17756
|
await this.save();
|
|
17592
17757
|
fail(
|
|
17593
17758
|
"resource_key_unavailable",
|
|
17594
17759
|
"The recovered application key was revoked or removed.",
|
|
17595
17760
|
"Create a replacement key with a new name; the previous creation will not be repeated.",
|
|
17596
17761
|
4,
|
|
17597
|
-
|
|
17762
|
+
mutation2.failure
|
|
17598
17763
|
);
|
|
17599
17764
|
}
|
|
17600
17765
|
}
|
|
@@ -17603,29 +17768,29 @@ var Context = class {
|
|
|
17603
17768
|
const response = await this.call(name, params, {
|
|
17604
17769
|
body,
|
|
17605
17770
|
headers: {
|
|
17606
|
-
"Idempotency-Key":
|
|
17607
|
-
"X-Idempotency-Proof":
|
|
17771
|
+
"Idempotency-Key": mutation2.id,
|
|
17772
|
+
"X-Idempotency-Proof": mutation2.proof
|
|
17608
17773
|
}
|
|
17609
17774
|
}).catch(async (error51) => {
|
|
17610
|
-
await this.discard(
|
|
17775
|
+
await this.discard(mutation2, error51);
|
|
17611
17776
|
throw error51;
|
|
17612
17777
|
});
|
|
17613
17778
|
data = response.data;
|
|
17614
|
-
|
|
17779
|
+
mutation2.response = data;
|
|
17615
17780
|
await this.save();
|
|
17616
17781
|
}
|
|
17617
17782
|
return {
|
|
17618
17783
|
data,
|
|
17619
17784
|
keyStored: async (metadata) => {
|
|
17620
|
-
|
|
17785
|
+
mutation2.keyMetadata = metadata;
|
|
17621
17786
|
await this.save();
|
|
17622
17787
|
},
|
|
17623
17788
|
complete: async () => {
|
|
17624
|
-
|
|
17625
|
-
|
|
17626
|
-
delete
|
|
17789
|
+
mutation2.result = recordedResultFor[name].parse(data);
|
|
17790
|
+
mutation2.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
17791
|
+
delete mutation2.response;
|
|
17627
17792
|
await this.save();
|
|
17628
|
-
this.deliveredMutations.add(
|
|
17793
|
+
this.deliveredMutations.add(mutation2.id);
|
|
17629
17794
|
}
|
|
17630
17795
|
};
|
|
17631
17796
|
}
|
|
@@ -17637,9 +17802,9 @@ var Context = class {
|
|
|
17637
17802
|
* different failure. The worst a failed write leaves behind is the record
|
|
17638
17803
|
* this would have removed, which is where the CLI stood before.
|
|
17639
17804
|
*/
|
|
17640
|
-
async discard(
|
|
17805
|
+
async discard(mutation2, error51) {
|
|
17641
17806
|
if (!definitiveRefusal(error51)) return;
|
|
17642
|
-
delete this.state.mutations?.[
|
|
17807
|
+
delete this.state.mutations?.[mutation2.id];
|
|
17643
17808
|
await this.save().catch(() => {
|
|
17644
17809
|
});
|
|
17645
17810
|
}
|
|
@@ -17764,12 +17929,12 @@ var Context = class {
|
|
|
17764
17929
|
};
|
|
17765
17930
|
await this.save();
|
|
17766
17931
|
}
|
|
17767
|
-
const
|
|
17932
|
+
const operation3 = this.state.operations[localId];
|
|
17768
17933
|
const { data: capabilities } = await this.publicCall("getCliCapabilities", [], {
|
|
17769
17934
|
url: target
|
|
17770
17935
|
});
|
|
17771
17936
|
const { data } = await this.publicCall("createCliOperation", [], {
|
|
17772
|
-
body: { kind, payload, pollToken:
|
|
17937
|
+
body: { kind, payload, pollToken: operation3.pollToken },
|
|
17773
17938
|
url: target,
|
|
17774
17939
|
...authenticated ? { key: this.active?.credential } : {}
|
|
17775
17940
|
});
|
|
@@ -17782,7 +17947,7 @@ var Context = class {
|
|
|
17782
17947
|
"invalid_response",
|
|
17783
17948
|
"The handoff URL is not on this deployment\u2019s trusted console origin."
|
|
17784
17949
|
);
|
|
17785
|
-
this.state.operations[data.id] = { ...
|
|
17950
|
+
this.state.operations[data.id] = { ...operation3, phase: "pending" };
|
|
17786
17951
|
delete this.state.operations[localId];
|
|
17787
17952
|
await this.save();
|
|
17788
17953
|
if (!this.flags["no-open"]) await openBrowser(data.url);
|
|
@@ -17796,15 +17961,15 @@ Operation: ${data.id}
|
|
|
17796
17961
|
return this.wait(data.id, 300);
|
|
17797
17962
|
}
|
|
17798
17963
|
async poll(id) {
|
|
17799
|
-
const
|
|
17800
|
-
if (!
|
|
17964
|
+
const operation3 = this.state.operations[id];
|
|
17965
|
+
if (!operation3)
|
|
17801
17966
|
fail(
|
|
17802
17967
|
"operation_unknown",
|
|
17803
17968
|
"This operation has no locally stored polling authorization.",
|
|
17804
17969
|
"Resume it from the machine that initiated the operation.",
|
|
17805
17970
|
4
|
|
17806
17971
|
);
|
|
17807
|
-
if (this.active && this.active.url !==
|
|
17972
|
+
if (this.active && this.active.url !== operation3.url)
|
|
17808
17973
|
fail(
|
|
17809
17974
|
"operation_context",
|
|
17810
17975
|
"This operation belongs to another selected deployment.",
|
|
@@ -17812,18 +17977,18 @@ Operation: ${data.id}
|
|
|
17812
17977
|
4
|
|
17813
17978
|
);
|
|
17814
17979
|
const { data } = await this.publicCall("pollCliOperation", [id], {
|
|
17815
|
-
key:
|
|
17816
|
-
url:
|
|
17980
|
+
key: operation3.pollToken,
|
|
17981
|
+
url: operation3.url
|
|
17817
17982
|
});
|
|
17818
|
-
if (data.state === "completed" && !
|
|
17819
|
-
if ((this.state.generation ?? 0) !==
|
|
17983
|
+
if (data.state === "completed" && !operation3.completed && operation3.kind === "claim") {
|
|
17984
|
+
if ((this.state.generation ?? 0) !== operation3.generation)
|
|
17820
17985
|
fail(
|
|
17821
17986
|
"operation_context",
|
|
17822
17987
|
"The active connection changed after this operation began.",
|
|
17823
17988
|
"Start a new login for the intended connection.",
|
|
17824
17989
|
4
|
|
17825
17990
|
);
|
|
17826
|
-
|
|
17991
|
+
operation3.completed = true;
|
|
17827
17992
|
if (this.active && data.account) this.active.account = data.account;
|
|
17828
17993
|
await this.save();
|
|
17829
17994
|
}
|
|
@@ -17885,9 +18050,11 @@ async function openBrowser(url2) {
|
|
|
17885
18050
|
|
|
17886
18051
|
// src/deployment.ts
|
|
17887
18052
|
import { spawn as spawn2 } from "node:child_process";
|
|
18053
|
+
import { stripVTControlCharacters } from "node:util";
|
|
17888
18054
|
import { createRequire as createRequire2 } from "node:module";
|
|
17889
18055
|
import { randomUUID } from "node:crypto";
|
|
17890
18056
|
import { cp, writeFile as writeFile2, unlink as unlink2 } from "node:fs/promises";
|
|
18057
|
+
import { tmpdir } from "node:os";
|
|
17891
18058
|
import { join as join3 } from "node:path";
|
|
17892
18059
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
17893
18060
|
|
|
@@ -18133,9 +18300,11 @@ async function release(requested = VERSION, base = dirname(fileURLToPath(import.
|
|
|
18133
18300
|
|
|
18134
18301
|
// src/deployment.ts
|
|
18135
18302
|
var require2 = createRequire2(import.meta.url);
|
|
18136
|
-
var OUTPUT_TAIL =
|
|
18303
|
+
var OUTPUT_TAIL = 5e3;
|
|
18137
18304
|
function wranglerFailure(args, code, output, redact = []) {
|
|
18138
|
-
const tail =
|
|
18305
|
+
const tail = stripVTControlCharacters(
|
|
18306
|
+
redact.filter((value) => value.length >= 8).reduce((text2, value) => text2.split(value).join("[redacted]"), output)
|
|
18307
|
+
).trim().slice(-OUTPUT_TAIL);
|
|
18139
18308
|
return new CliError(
|
|
18140
18309
|
"wrangler_failed",
|
|
18141
18310
|
`wrangler ${args[0] ?? ""} failed${code === null ? "" : ` (exit ${code})`}.`,
|
|
@@ -18144,16 +18313,21 @@ function wranglerFailure(args, code, output, redact = []) {
|
|
|
18144
18313
|
tail ? { output: tail } : void 0
|
|
18145
18314
|
);
|
|
18146
18315
|
}
|
|
18316
|
+
function wranglerEnvironment({
|
|
18317
|
+
interactive = false
|
|
18318
|
+
} = {}) {
|
|
18319
|
+
return {
|
|
18320
|
+
...process.env,
|
|
18321
|
+
WRANGLER_SEND_METRICS: "false",
|
|
18322
|
+
WRANGLER_LOG: "log",
|
|
18323
|
+
CI: interactive ? "" : "true"
|
|
18324
|
+
};
|
|
18325
|
+
}
|
|
18147
18326
|
async function runWrangler(args, { cwd, input, interactive = false, redact } = {}) {
|
|
18148
18327
|
return new Promise((resolve3, reject2) => {
|
|
18149
18328
|
const child = spawn2(process.execPath, [require2.resolve("wrangler/bin/wrangler.js"), ...args], {
|
|
18150
18329
|
...cwd === void 0 ? {} : { cwd },
|
|
18151
|
-
env: {
|
|
18152
|
-
...process.env,
|
|
18153
|
-
WRANGLER_SEND_METRICS: "false",
|
|
18154
|
-
WRANGLER_LOG: "error",
|
|
18155
|
-
CI: interactive ? "" : "true"
|
|
18156
|
-
},
|
|
18330
|
+
env: wranglerEnvironment({ interactive }),
|
|
18157
18331
|
stdio: [input ? "pipe" : interactive ? "inherit" : "ignore", "pipe", "pipe"]
|
|
18158
18332
|
});
|
|
18159
18333
|
let output = "";
|
|
@@ -18184,6 +18358,24 @@ async function runWrangler(args, { cwd, input, interactive = false, redact } = {
|
|
|
18184
18358
|
});
|
|
18185
18359
|
});
|
|
18186
18360
|
}
|
|
18361
|
+
function usable(auth) {
|
|
18362
|
+
return auth.type === "api_key" ? Boolean(auth.key && auth.email) : Boolean(auth.token);
|
|
18363
|
+
}
|
|
18364
|
+
function environmentCredentials() {
|
|
18365
|
+
const env = process.env;
|
|
18366
|
+
const token = env.CLOUDFLARE_API_TOKEN ?? env.CF_API_TOKEN;
|
|
18367
|
+
const key = env.CLOUDFLARE_API_KEY ?? env.CF_API_KEY;
|
|
18368
|
+
const email3 = env.CLOUDFLARE_EMAIL ?? env.CF_EMAIL;
|
|
18369
|
+
return [token, key && email3 ? key : void 0].filter(
|
|
18370
|
+
(value) => Boolean(value)
|
|
18371
|
+
);
|
|
18372
|
+
}
|
|
18373
|
+
var API_ERROR_TAIL = 500;
|
|
18374
|
+
function apiErrors(data) {
|
|
18375
|
+
return (data.errors ?? []).map(
|
|
18376
|
+
(error51) => [error51.message, error51.code === void 0 ? "" : `[code: ${error51.code}]`].filter(Boolean).join(" ")
|
|
18377
|
+
).filter(Boolean).join("; ").slice(0, API_ERROR_TAIL);
|
|
18378
|
+
}
|
|
18187
18379
|
var Cloudflare = class {
|
|
18188
18380
|
run;
|
|
18189
18381
|
fetch;
|
|
@@ -18192,22 +18384,62 @@ var Cloudflare = class {
|
|
|
18192
18384
|
this.run = run;
|
|
18193
18385
|
this.fetch = fetchImpl;
|
|
18194
18386
|
}
|
|
18195
|
-
|
|
18387
|
+
/**
|
|
18388
|
+
* The authorization wrangler holds, or undefined when it holds none.
|
|
18389
|
+
*
|
|
18390
|
+
* Only one outcome may be read as "logged out": wrangler exiting non-zero
|
|
18391
|
+
* with nothing in the environment for it to have used. A run that failed
|
|
18392
|
+
* while credentials were set failed at something else, a run that could not
|
|
18393
|
+
* start failed at nothing to do with authorization, and output that will not
|
|
18394
|
+
* parse means this CLI and the wrangler it drives disagree about the
|
|
18395
|
+
* command. Answering any of those with a login would hide the cause behind a
|
|
18396
|
+
* message about something the caller had already done.
|
|
18397
|
+
*/
|
|
18398
|
+
async readAuth() {
|
|
18399
|
+
const credentials = environmentCredentials();
|
|
18400
|
+
let reported2;
|
|
18401
|
+
try {
|
|
18402
|
+
reported2 = await this.run(["auth", "token", "--json"], {
|
|
18403
|
+
cwd: tmpdir(),
|
|
18404
|
+
redact: credentials
|
|
18405
|
+
});
|
|
18406
|
+
} catch (error51) {
|
|
18407
|
+
if (credentials.length > 0 || !(error51 instanceof CliError) || error51.code !== "wrangler_failed")
|
|
18408
|
+
throw error51;
|
|
18409
|
+
return void 0;
|
|
18410
|
+
}
|
|
18411
|
+
let auth;
|
|
18196
18412
|
try {
|
|
18197
|
-
|
|
18413
|
+
auth = JSON.parse(reported2);
|
|
18198
18414
|
} catch {
|
|
18199
|
-
|
|
18200
|
-
|
|
18201
|
-
|
|
18202
|
-
|
|
18203
|
-
|
|
18204
|
-
|
|
18205
|
-
);
|
|
18206
|
-
await this.run(["login"], { interactive: true });
|
|
18207
|
-
this.auth = JSON.parse(await this.run(["auth", "token", "--json"]));
|
|
18415
|
+
fail(
|
|
18416
|
+
"cloudflare_auth_unreadable",
|
|
18417
|
+
"Wrangler did not report the Cloudflare authorization it holds.",
|
|
18418
|
+
"Reinstall the CLI so the wrangler it bundles is the one it expects, then retry.",
|
|
18419
|
+
3
|
|
18420
|
+
);
|
|
18208
18421
|
}
|
|
18209
|
-
|
|
18210
|
-
|
|
18422
|
+
return usable(auth) ? auth : void 0;
|
|
18423
|
+
}
|
|
18424
|
+
async authenticate(flags) {
|
|
18425
|
+
this.auth = await this.readAuth();
|
|
18426
|
+
if (this.auth) return;
|
|
18427
|
+
if (flags["no-input"] || flags["dry-run"])
|
|
18428
|
+
fail(
|
|
18429
|
+
"cloudflare_auth_required",
|
|
18430
|
+
"Cloudflare authentication is required.",
|
|
18431
|
+
"Set CLOUDFLARE_API_TOKEN securely, or run setup interactively to authorize Cloudflare.",
|
|
18432
|
+
4
|
|
18433
|
+
);
|
|
18434
|
+
await this.run(["login"], { interactive: true, cwd: tmpdir() });
|
|
18435
|
+
this.auth = await this.readAuth();
|
|
18436
|
+
if (!this.auth)
|
|
18437
|
+
fail(
|
|
18438
|
+
"cloudflare_auth_required",
|
|
18439
|
+
"Cloudflare authorization was not completed.",
|
|
18440
|
+
"Read the wrangler output above, complete the browser login, then retry the same command.",
|
|
18441
|
+
4
|
|
18442
|
+
);
|
|
18211
18443
|
}
|
|
18212
18444
|
authHeaders() {
|
|
18213
18445
|
const auth = this.auth;
|
|
@@ -18243,13 +18475,16 @@ var Cloudflare = class {
|
|
|
18243
18475
|
} catch {
|
|
18244
18476
|
fail("cloudflare_error", "Cloudflare returned an invalid response.");
|
|
18245
18477
|
}
|
|
18246
|
-
if (!response.ok || !data.success)
|
|
18478
|
+
if (!response.ok || !data.success) {
|
|
18479
|
+
const reported2 = apiErrors(data);
|
|
18247
18480
|
fail(
|
|
18248
18481
|
"cloudflare_error",
|
|
18249
|
-
`Cloudflare rejected the request (HTTP ${response.status})
|
|
18482
|
+
`Cloudflare rejected the request (HTTP ${response.status})${reported2 ? `: ${reported2}` : ""}`,
|
|
18250
18483
|
"Check Cloudflare permissions and the deployment resource IDs; retry the same command.",
|
|
18251
|
-
3
|
|
18484
|
+
3,
|
|
18485
|
+
reported2 ? { apiErrors: reported2 } : void 0
|
|
18252
18486
|
);
|
|
18487
|
+
}
|
|
18253
18488
|
return data;
|
|
18254
18489
|
}
|
|
18255
18490
|
async all(path) {
|
|
@@ -18761,60 +18996,570 @@ async function deploymentCommand(ctx, command, flags, cf = new Cloudflare(), loa
|
|
|
18761
18996
|
}
|
|
18762
18997
|
}
|
|
18763
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
|
+
|
|
18764
19057
|
// src/human.ts
|
|
18765
|
-
function
|
|
18766
|
-
const
|
|
18767
|
-
|
|
18768
|
-
|
|
18769
|
-
|
|
18770
|
-
|
|
18771
|
-
|
|
18772
|
-
|
|
18773
|
-
|
|
18774
|
-
|
|
18775
|
-
|
|
18776
|
-
|
|
18777
|
-
|
|
18778
|
-
|
|
18779
|
-
|
|
18780
|
-
|
|
18781
|
-
|
|
18782
|
-
|
|
18783
|
-
|
|
18784
|
-
|
|
18785
|
-
|
|
18786
|
-
|
|
18787
|
-
|
|
18788
|
-
|
|
18789
|
-
|
|
18790
|
-
|
|
18791
|
-
|
|
18792
|
-
|
|
18793
|
-
|
|
18794
|
-
}
|
|
18795
|
-
if (result.applicationKey)
|
|
18796
|
-
lines.push(`Key saved: ${result.applicationKey.storagePath}`);
|
|
18797
|
-
if (result.guidance) lines.push("", result.guidance);
|
|
18798
|
-
if ("snippet" in result && result.snippet) lines.push("", result.snippet);
|
|
18799
|
-
} else if (command === "provider add" || command === "provider-gateway add") {
|
|
18800
|
-
const stored = "provider" in result ? result.provider : "gateway" in result ? result.gateway : void 0;
|
|
18801
|
-
const label = stored?.name ?? stored?.type ?? "connection";
|
|
18802
|
-
const id = stored?.id ?? ("id" in result ? result.id : void 0);
|
|
18803
|
-
lines.push(
|
|
18804
|
-
`Stored ${label} (${id}).`,
|
|
18805
|
-
`Gateway: ${context.url}`,
|
|
18806
|
-
"Credential stored; no upstream probe or inference was performed."
|
|
18807
|
-
);
|
|
18808
|
-
} else if ("output" in result && typeof result.output === "string") {
|
|
18809
|
-
lines.push(`Saved: ${result.output}`);
|
|
18810
|
-
} else lines.push(JSON.stringify(result, null, 2));
|
|
18811
|
-
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);
|
|
18812
19087
|
if ("trial" in result && result.trial)
|
|
18813
19088
|
lines.push(`Free access ends: ${result.trial.endsAt}`);
|
|
18814
19089
|
if ("account" in result && result.account?.expiresAt)
|
|
18815
19090
|
lines.push(`Recovery deadline: ${result.account.expiresAt}`);
|
|
18816
19091
|
return lines.join("\n") + "\n";
|
|
18817
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
|
+
}
|
|
18818
19563
|
|
|
18819
19564
|
// src/transport.ts
|
|
18820
19565
|
var MESSAGE_LIMIT = 300;
|
|
@@ -18833,6 +19578,27 @@ function reported(value, sent) {
|
|
|
18833
19578
|
}
|
|
18834
19579
|
var RECOVERABLE_RESOURCE_CODES = ["resource_receipt_expired", "resource_key_unavailable"];
|
|
18835
19580
|
var RECOVERABLE_RESOURCE_FIELDS = ["appId", "keyId", "providerId", "providerGatewayId"];
|
|
19581
|
+
var LIMIT_CODES = [
|
|
19582
|
+
"rate_limited",
|
|
19583
|
+
"app_rate_limited",
|
|
19584
|
+
"app_budget_exhausted",
|
|
19585
|
+
"billing_request_quota_exceeded",
|
|
19586
|
+
"billing_plan_limit_reached"
|
|
19587
|
+
];
|
|
19588
|
+
var LIMIT_TEXT_FIELDS = ["scope", "resetAt"];
|
|
19589
|
+
var LIMIT_NUMBER_FIELDS = ["limit", "used", "windowSeconds", "retryAfterSeconds"];
|
|
19590
|
+
function nextActionFor(status2, code, details) {
|
|
19591
|
+
if (status2 === 401) return "Run agw account login.";
|
|
19592
|
+
if (code === "billing_plan_limit_reached")
|
|
19593
|
+
return "Delete one you no longer need, or move to a plan whose limits are higher.";
|
|
19594
|
+
if (status2 === 429) {
|
|
19595
|
+
const seconds = details.retryAfterSeconds;
|
|
19596
|
+
if (seconds === void 0)
|
|
19597
|
+
return "Wait for the limit named in the message to reset, then run the same command again.";
|
|
19598
|
+
return seconds >= 3600 && details.resetAt !== void 0 ? `Wait until ${details.resetAt}, then run the same command again.` : `Wait ${seconds} seconds, then run the same command again.`;
|
|
19599
|
+
}
|
|
19600
|
+
return "Review command configuration and account status.";
|
|
19601
|
+
}
|
|
18836
19602
|
var Transport = class {
|
|
18837
19603
|
fetch;
|
|
18838
19604
|
constructor(fetchImpl = fetch) {
|
|
@@ -18887,18 +19653,27 @@ var Transport = class {
|
|
|
18887
19653
|
const wire = data ?? {};
|
|
18888
19654
|
const code = typeof wire.error?.code === "string" ? wire.error.code : typeof wire.code === "string" ? wire.code : `http_${response.status}`;
|
|
18889
19655
|
const details = { status: response.status };
|
|
18890
|
-
|
|
18891
|
-
|
|
19656
|
+
const facts = wire.error?.data ?? {};
|
|
19657
|
+
if (RECOVERABLE_RESOURCE_CODES.includes(code))
|
|
18892
19658
|
for (const field of RECOVERABLE_RESOURCE_FIELDS) {
|
|
18893
|
-
const value =
|
|
19659
|
+
const value = facts[field];
|
|
18894
19660
|
if (typeof value === "string") details[field] = value;
|
|
18895
19661
|
}
|
|
19662
|
+
if (LIMIT_CODES.includes(code)) {
|
|
19663
|
+
for (const field of LIMIT_TEXT_FIELDS) {
|
|
19664
|
+
const value = facts[field];
|
|
19665
|
+
if (typeof value === "string") details[field] = value;
|
|
19666
|
+
}
|
|
19667
|
+
for (const field of LIMIT_NUMBER_FIELDS) {
|
|
19668
|
+
const value = facts[field];
|
|
19669
|
+
if (typeof value === "number") details[field] = value;
|
|
19670
|
+
}
|
|
18896
19671
|
}
|
|
18897
19672
|
const explanation = reported(wire.error?.message ?? wire.message, body);
|
|
18898
19673
|
fail(
|
|
18899
19674
|
code,
|
|
18900
19675
|
`Deployment rejected the request (HTTP ${response.status})` + (explanation ? `: ${explanation}` : "."),
|
|
18901
|
-
response.status
|
|
19676
|
+
nextActionFor(response.status, code, details),
|
|
18902
19677
|
3,
|
|
18903
19678
|
details
|
|
18904
19679
|
);
|
|
@@ -18934,7 +19709,7 @@ async function usageCommand(ctx, command, flags) {
|
|
|
18934
19709
|
const m = month(flags.month ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 7));
|
|
18935
19710
|
return flags.app ? (await ctx.call("getAppUsage", [flags.app, { month: m }])).data : (await ctx.call("getCliUsage", [{ month: m }])).data;
|
|
18936
19711
|
}
|
|
18937
|
-
const
|
|
19712
|
+
const app2 = await required2(flags, "app");
|
|
18938
19713
|
const by = flags.by ?? "model";
|
|
18939
19714
|
if (!["provider", "model", "status"].includes(by))
|
|
18940
19715
|
fail("invalid_input", "--by must be provider, model or status.");
|
|
@@ -18946,7 +19721,7 @@ async function usageCommand(ctx, command, flags) {
|
|
|
18946
19721
|
if (from > to) fail("invalid_input", "--from must not be later than --to.");
|
|
18947
19722
|
const limit = positive(flags.limit ?? 50, 200);
|
|
18948
19723
|
return {
|
|
18949
|
-
...(await ctx.call("getAppUsageBreakdown", [
|
|
19724
|
+
...(await ctx.call("getAppUsageBreakdown", [app2, { from, to, by, limit }])).data,
|
|
18950
19725
|
coverage: {
|
|
18951
19726
|
from,
|
|
18952
19727
|
to,
|
|
@@ -19000,7 +19775,8 @@ async function execute(parsed, ctx) {
|
|
|
19000
19775
|
async function main(argv = process.argv.slice(2), {
|
|
19001
19776
|
store = new StateStore(),
|
|
19002
19777
|
transport = new Transport(),
|
|
19003
|
-
stdout = process.stdout
|
|
19778
|
+
stdout = process.stdout,
|
|
19779
|
+
stderr: stderr2 = process.stderr
|
|
19004
19780
|
} = {}) {
|
|
19005
19781
|
const json2 = argv.includes("--json");
|
|
19006
19782
|
try {
|
|
@@ -19024,8 +19800,8 @@ async function main(argv = process.argv.slice(2), {
|
|
|
19024
19800
|
};
|
|
19025
19801
|
const rendered = ctx.onboarding ? { ...result, ...ctx.onboarding } : result;
|
|
19026
19802
|
const output = { schemaVersion: 1, ok: true, context, result: rendered };
|
|
19027
|
-
const text2 = json2 ? JSON.stringify(output) + "\n" : humanResult(parsed.command, rendered, context);
|
|
19028
|
-
if (stdout instanceof
|
|
19803
|
+
const text2 = json2 ? JSON.stringify(output) + "\n" : humanResult(parsed.command, rendered, context, styleFor(stdout));
|
|
19804
|
+
if (stdout instanceof Writable2) {
|
|
19029
19805
|
await new Promise(
|
|
19030
19806
|
(resolve3, reject2) => stdout.write(
|
|
19031
19807
|
text2,
|
|
@@ -19047,17 +19823,27 @@ async function main(argv = process.argv.slice(2), {
|
|
|
19047
19823
|
4
|
|
19048
19824
|
);
|
|
19049
19825
|
const details = e.details ? CliErrorDetailsSchema.safeParse(e.details) : void 0;
|
|
19050
|
-
const
|
|
19051
|
-
|
|
19052
|
-
|
|
19053
|
-
|
|
19054
|
-
|
|
19055
|
-
message: e.message,
|
|
19056
|
-
nextAction: e.nextAction,
|
|
19057
|
-
...details?.success ? { details: details.data } : {}
|
|
19058
|
-
}
|
|
19826
|
+
const reported2 = {
|
|
19827
|
+
code: e.code,
|
|
19828
|
+
message: e.message,
|
|
19829
|
+
nextAction: e.nextAction,
|
|
19830
|
+
...details?.success ? { details: details.data } : {}
|
|
19059
19831
|
};
|
|
19060
|
-
|
|
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");
|
|
19061
19847
|
return e.exitCode;
|
|
19062
19848
|
}
|
|
19063
19849
|
}
|