@maxceem/agw 0.5.1 → 0.6.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 +463 -352
- package/package.json +2 -2
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.6.0", wrangler: "4.96.0", upgradeFrom: ["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.6.0/gateway-0.6.0.tar.gz", sha256: "792ad8d9cbb13a98c1e99532061128c53d28549de4345adbcc1c5b2c34cd1fec", manifest: "2edb8dd7a9e80bd906ef0ab9354b97f4c431e726eca04524569fe5435d7a6b2c" } };
|
|
10
10
|
|
|
11
11
|
// src/main.ts
|
|
12
12
|
import { Writable as Writable2 } from "node:stream";
|
|
@@ -14642,13 +14642,6 @@ var API_STYLES = [
|
|
|
14642
14642
|
/** A provider-native operation with no cross-provider contract of its own. */
|
|
14643
14643
|
"other"
|
|
14644
14644
|
];
|
|
14645
|
-
var API_STYLE_PATHS = {
|
|
14646
|
-
responses: "v1/responses",
|
|
14647
|
-
chat_completions: "v1/chat/completions",
|
|
14648
|
-
anthropic_messages: "v1/messages",
|
|
14649
|
-
gemini_native: "v1beta/models/{model}:generateContent",
|
|
14650
|
-
audio_transcription: "v1/audio/transcriptions"
|
|
14651
|
-
};
|
|
14652
14645
|
var ENDPOINT_API_STYLES = ["responses", "audio_transcription"];
|
|
14653
14646
|
var OUTPUT_CLAMP_STYLES = [
|
|
14654
14647
|
"responses",
|
|
@@ -15316,6 +15309,18 @@ var AppUpdateSchema = AppWriteSchema.extend({
|
|
|
15316
15309
|
description: "Required: the revision the application was read at. An absent one answers 400 app_revision_required, a stale one 409 app_revision_conflict."
|
|
15317
15310
|
})
|
|
15318
15311
|
}).meta({ id: "AppUpdate" });
|
|
15312
|
+
var SNIPPET_LANGUAGES = ["swift", "curl"];
|
|
15313
|
+
var AppSnippetQuerySchema = external_exports.object({
|
|
15314
|
+
language: external_exports.enum(SNIPPET_LANGUAGES, { error: "language must be swift or curl" }).optional().meta({
|
|
15315
|
+
description: "swift or curl. Defaults to swift for an App Attest application and curl for a server application; the other one is refused with 400 unsupported_snippet."
|
|
15316
|
+
}),
|
|
15317
|
+
provider: external_exports.string().min(1).optional().meta({
|
|
15318
|
+
description: "The slug of the provider to write the example for. Defaults to the first one the application can reach. Unknown answers 404 provider_not_found; held but outside the application's routing or disabled answers 400 provider_unavailable."
|
|
15319
|
+
}),
|
|
15320
|
+
endpoint: external_exports.string().min(1).optional().meta({
|
|
15321
|
+
description: "The name of one of the application's custom endpoints, to call it instead of a provider path. Unknown answers 404 endpoint_not_found."
|
|
15322
|
+
})
|
|
15323
|
+
});
|
|
15319
15324
|
var AppAttestKeyIdSchema = external_exports.string().min(1).max(200);
|
|
15320
15325
|
var ChallengeSchema = external_exports.string().min(1).max(200);
|
|
15321
15326
|
var AppAttestRegisterRequestSchema = external_exports.object({
|
|
@@ -15489,8 +15494,26 @@ var OrganizationSelectRequestSchema = external_exports.object({
|
|
|
15489
15494
|
var CredentialNameRequestSchema = external_exports.object({
|
|
15490
15495
|
name: external_exports.string().trim().min(1).max(100)
|
|
15491
15496
|
});
|
|
15492
|
-
var
|
|
15497
|
+
var CredentialGrantSchema = external_exports.enum(["read", "manage"]);
|
|
15498
|
+
var ManagementKeyCreateRequestSchema = CredentialNameRequestSchema.extend({
|
|
15499
|
+
grant: CredentialGrantSchema.default("manage").meta({
|
|
15500
|
+
description: "How much of your role the key may use. `read` may list, inspect and validate but cannot change your apps, providers or settings: a change your role would otherwise allow is refused with `403 grant_insufficient`, and the key may still revoke itself. `manage` may do everything your role allows. Defaults to `manage`."
|
|
15501
|
+
})
|
|
15502
|
+
}).meta({ id: "ManagementKeyCreateRequest" });
|
|
15493
15503
|
var ApiKeyCreateRequestSchema = CredentialNameRequestSchema.meta({ id: "ApiKeyCreateRequest" });
|
|
15504
|
+
var OAuthConsentProofTokenSchema = external_exports.string().regex(/^[A-Za-z0-9_-]{32,256}$/).meta({
|
|
15505
|
+
description: "The proof from the consent page URL's fragment."
|
|
15506
|
+
});
|
|
15507
|
+
var OAuthConsentProofSchema = external_exports.object({ submissionToken: OAuthConsentProofTokenSchema }).strict();
|
|
15508
|
+
var OAuthConsentAllowRequestSchema = external_exports.object({
|
|
15509
|
+
submissionToken: OAuthConsentProofTokenSchema,
|
|
15510
|
+
organizationId: external_exports.string().min(1).max(256).meta({
|
|
15511
|
+
description: "The account the connection acts in: one the signed-in person is a member of."
|
|
15512
|
+
}),
|
|
15513
|
+
grant: CredentialGrantSchema.meta({
|
|
15514
|
+
description: "How much of the person's role the connection may use, whatever the client asked for."
|
|
15515
|
+
})
|
|
15516
|
+
}).strict();
|
|
15494
15517
|
|
|
15495
15518
|
// src/common.ts
|
|
15496
15519
|
import { randomBytes } from "node:crypto";
|
|
@@ -16653,15 +16676,6 @@ var StateStore = class {
|
|
|
16653
16676
|
function selectedProviderPolicies(routing) {
|
|
16654
16677
|
return routing.providers.mode === "selected" ? routing.providers.selected : {};
|
|
16655
16678
|
}
|
|
16656
|
-
function providerPolicyFor(routing, slug) {
|
|
16657
|
-
if (routing.providers.mode === "all") return { allowed_paths: [], allowed_models: [] };
|
|
16658
|
-
return Object.hasOwn(routing.providers.selected, slug) ? routing.providers.selected[slug] : void 0;
|
|
16659
|
-
}
|
|
16660
|
-
function reachableProviders(routing, instances) {
|
|
16661
|
-
return instances.filter(
|
|
16662
|
-
(instance) => instance.status === "active" && providerPolicyFor(routing, instance.slug) !== void 0
|
|
16663
|
-
);
|
|
16664
|
-
}
|
|
16665
16679
|
|
|
16666
16680
|
// ../src/shared/protocols.ts
|
|
16667
16681
|
var PROTOCOLS = {
|
|
@@ -16711,23 +16725,6 @@ var PROTOCOLS = {
|
|
|
16711
16725
|
var CLASSIFIED = Object.values(PROTOCOLS).filter(
|
|
16712
16726
|
(protocol) => protocol.path !== null
|
|
16713
16727
|
);
|
|
16714
|
-
function classifyPath(providerPath) {
|
|
16715
|
-
for (const protocol of CLASSIFIED) {
|
|
16716
|
-
const match = protocol.path.exec(providerPath);
|
|
16717
|
-
if (!match) continue;
|
|
16718
|
-
const span = match.indices?.groups?.model;
|
|
16719
|
-
const value = match.groups?.model;
|
|
16720
|
-
if (span === void 0 || value === void 0) return { protocol };
|
|
16721
|
-
return {
|
|
16722
|
-
protocol,
|
|
16723
|
-
model: {
|
|
16724
|
-
value,
|
|
16725
|
-
template: `${providerPath.slice(0, span[0])}{model}${providerPath.slice(span[1])}`
|
|
16726
|
-
}
|
|
16727
|
-
};
|
|
16728
|
-
}
|
|
16729
|
-
return { protocol: PROTOCOLS.other };
|
|
16730
|
-
}
|
|
16731
16728
|
|
|
16732
16729
|
// ../src/shared/first-request.ts
|
|
16733
16730
|
var PROVIDER_PLACEHOLDER = "PROVIDER_SLUG";
|
|
@@ -16738,126 +16735,9 @@ var EXAMPLE_GAP_NOTES = {
|
|
|
16738
16735
|
model: `No priced model was found for this provider, so ${MODEL_PLACEHOLDER} stands in for one. Use a model the provider serves.`,
|
|
16739
16736
|
body: `This app allows no path whose request shape is known here, so ${BODY_PLACEHOLDER} stands in for the body the provider documents.`
|
|
16740
16737
|
};
|
|
16741
|
-
function examplePath(type, { gatewayRouted = false, model } = {}) {
|
|
16742
|
-
const own = providerDescriptor(type).examplePath ?? API_STYLE_PATHS.chat_completions;
|
|
16743
|
-
if (own.includes("{model}")) return model ? own.replace("{model}", model) : void 0;
|
|
16744
|
-
return gatewayRouted && own.endsWith("chat/completions") ? API_STYLE_PATHS.chat_completions : own;
|
|
16745
|
-
}
|
|
16746
|
-
var hello = () => [{ role: "user", content: "Say hello." }];
|
|
16747
|
-
var EXAMPLE_BODIES = {
|
|
16748
|
-
responses: (model) => ({ model, input: "Say hello." }),
|
|
16749
|
-
chat_completions: (model) => ({ model, messages: hello() }),
|
|
16750
|
-
anthropic_messages: (model) => ({ model, max_tokens: 128, messages: hello() }),
|
|
16751
|
-
// The model is in the URL, not the body.
|
|
16752
|
-
gemini_native: () => ({ contents: [{ parts: [{ text: "Say hello." }] }] })
|
|
16753
|
-
};
|
|
16754
|
-
function requestFor(path, model) {
|
|
16755
|
-
const { style: style2 } = classifyPath(path).protocol;
|
|
16756
|
-
return {
|
|
16757
|
-
body: EXAMPLE_BODIES[style2]?.(model) ?? null,
|
|
16758
|
-
anthropic: style2 === "anthropic_messages"
|
|
16759
|
-
};
|
|
16760
|
-
}
|
|
16761
|
-
function modelsFor(provider, policy, prices) {
|
|
16762
|
-
if (policy?.allowed_models?.length) return policy.allowed_models;
|
|
16763
|
-
return [
|
|
16764
|
-
...Object.keys(provider.pricing ?? {}),
|
|
16765
|
-
...Object.entries(prices[provider.type] ?? {}).filter(([, price]) => price.output !== void 0).map(([model]) => model)
|
|
16766
|
-
];
|
|
16767
|
-
}
|
|
16768
|
-
function firstRequest(routing, providers, prices) {
|
|
16769
|
-
let fallback;
|
|
16770
|
-
const candidates = routing ? reachableProviders(routing, providers) : providers.filter((provider) => provider.status === "active");
|
|
16771
|
-
for (const provider of candidates) {
|
|
16772
|
-
const policy = routing?.providers.mode === "selected" ? providerPolicyFor(routing, provider.slug) : void 0;
|
|
16773
|
-
const models = modelsFor(provider, policy, prices);
|
|
16774
|
-
const model = models[0] ?? MODEL_PLACEHOLDER;
|
|
16775
|
-
const gaps = models.length ? [] : ["model"];
|
|
16776
|
-
const fallbackPath = examplePath(provider.type, {
|
|
16777
|
-
gatewayRouted: Boolean(provider.providerGatewayId),
|
|
16778
|
-
model
|
|
16779
|
-
});
|
|
16780
|
-
const paths = policy?.allowed_paths?.length ? policy.allowed_paths : fallbackPath ? [fallbackPath] : [];
|
|
16781
|
-
for (const entry of paths) {
|
|
16782
|
-
const path = typeof entry === "string" ? entry : entry.path;
|
|
16783
|
-
if (path.includes("*")) continue;
|
|
16784
|
-
const fixed = typeof entry !== "string" ? entry.fixed_model : void 0;
|
|
16785
|
-
const { body, anthropic } = requestFor(path, fixed ?? model);
|
|
16786
|
-
const example = {
|
|
16787
|
-
target: { provider: provider.slug, path },
|
|
16788
|
-
body,
|
|
16789
|
-
anthropic,
|
|
16790
|
-
// A fixed model is the app's own, so a catalog that has none of its
|
|
16791
|
-
// own to offer is not a gap on a path that never carries one.
|
|
16792
|
-
gaps: body && fixed ? [] : gaps
|
|
16793
|
-
};
|
|
16794
|
-
if (body) return example;
|
|
16795
|
-
fallback ??= { ...example, gaps: [...example.gaps, "body"] };
|
|
16796
|
-
}
|
|
16797
|
-
}
|
|
16798
|
-
return fallback ?? {
|
|
16799
|
-
target: { provider: PROVIDER_PLACEHOLDER, path: "v1/chat/completions" },
|
|
16800
|
-
body: requestFor("v1/chat/completions", MODEL_PLACEHOLDER).body,
|
|
16801
|
-
anthropic: false,
|
|
16802
|
-
gaps: ["provider", "model"]
|
|
16803
|
-
};
|
|
16804
|
-
}
|
|
16805
|
-
function exampleNotes(example) {
|
|
16806
|
-
return example.gaps.map((gap) => EXAMPLE_GAP_NOTES[gap]);
|
|
16807
|
-
}
|
|
16808
|
-
function exampleUrl(baseUrl, appId, target) {
|
|
16809
|
-
const origin2 = baseUrl.replace(/\/+$/, "");
|
|
16810
|
-
const app2 = `${origin2}/v1/apps/${encodeURIComponent(appId)}`;
|
|
16811
|
-
return "endpoint" in target ? `${app2}/endpoints/${encodeURIComponent(target.endpoint)}` : `${app2}/proxy/${encodeURIComponent(target.provider)}/${target.path}`;
|
|
16812
|
-
}
|
|
16813
16738
|
function shellQuote(value) {
|
|
16814
16739
|
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
16815
16740
|
}
|
|
16816
|
-
function commented(notes2, marker) {
|
|
16817
|
-
return (notes2 ?? []).map((note) => `${marker} ${note}
|
|
16818
|
-
`).join("");
|
|
16819
|
-
}
|
|
16820
|
-
function curlSnippet(options) {
|
|
16821
|
-
const { baseUrl, appId, example, notes: notes2, keyExpression = "$APP_AI_GATEWAY_KEY" } = options;
|
|
16822
|
-
const body = example.body ? JSON.stringify(example.body) : BODY_PLACEHOLDER;
|
|
16823
|
-
return commented(notes2, "#") + `curl --fail-with-body ${shellQuote(exampleUrl(baseUrl, appId, example.target))} \\
|
|
16824
|
-
-H "Authorization: Bearer ${keyExpression}" \\
|
|
16825
|
-
-H 'Content-Type: application/json' \\
|
|
16826
|
-
` + (example.anthropic ? " -H 'anthropic-version: 2023-06-01' \\\n" : "") + ` -d ${shellQuote(body)}
|
|
16827
|
-
`;
|
|
16828
|
-
}
|
|
16829
|
-
var ISSUER_TOKEN_NOTE = "Replace yourIdentitySDK.currentIDToken(forceRefresh: forceRefresh) with your configured issuer integration.";
|
|
16830
|
-
function swiftSignsInUsers(authentication) {
|
|
16831
|
-
return authentication?.type === "apple_app_attest" && authentication.end_user.source === "issuer";
|
|
16832
|
-
}
|
|
16833
|
-
function swiftAuthMode(authentication) {
|
|
16834
|
-
return swiftSignsInUsers(authentication) ? ".appAttest(issuerTokenProvider: { forceRefresh in\n // Return a fresh signed token from your configured identity SDK.\n try await yourIdentitySDK.currentIDToken(forceRefresh: forceRefresh)\n })" : ".appAttestInstall";
|
|
16835
|
-
}
|
|
16836
|
-
function swiftSnippet(options) {
|
|
16837
|
-
const { baseUrl, appId, example, notes: notes2, authentication } = options;
|
|
16838
|
-
const authMode = swiftAuthMode(authentication);
|
|
16839
|
-
const target = "endpoint" in example.target ? `endpointSlug: ${JSON.stringify(example.target.endpoint)}` : `provider: ${JSON.stringify(example.target.provider)},
|
|
16840
|
-
providerPath: ${JSON.stringify(example.target.path)}`;
|
|
16841
|
-
const body = example.body ? `request.httpBody = Data(${JSON.stringify(JSON.stringify(example.body))}.utf8)` : `// Supply the provider-native JSON body.
|
|
16842
|
-
request.httpBody = Data(${JSON.stringify(BODY_PLACEHOLDER)}.utf8)`;
|
|
16843
|
-
return commented(notes2, "//") + `import Foundation
|
|
16844
|
-
import AppAIGateway
|
|
16845
|
-
|
|
16846
|
-
let gateway = AppAIGatewayClient(
|
|
16847
|
-
appID: ${JSON.stringify(appId)},
|
|
16848
|
-
baseURL: URL(string: ${JSON.stringify(baseUrl.replace(/\/+$/, ""))})!,
|
|
16849
|
-
authMode: ${authMode}
|
|
16850
|
-
)
|
|
16851
|
-
|
|
16852
|
-
var request = try await gateway.authorizedRequest(
|
|
16853
|
-
${target}
|
|
16854
|
-
)
|
|
16855
|
-
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
16856
|
-
${example.anthropic ? 'request.setValue("2023-06-01", forHTTPHeaderField: "anthropic-version")\n' : ""}${body}
|
|
16857
|
-
let (data, _) = try await URLSession.shared.data(for: request)
|
|
16858
|
-
print(String(decoding: data, as: UTF8.self))
|
|
16859
|
-
`;
|
|
16860
|
-
}
|
|
16861
16741
|
|
|
16862
16742
|
// ../src/shared/app-defaults.ts
|
|
16863
16743
|
function unlimitedScope() {
|
|
@@ -17003,8 +16883,8 @@ async function appCommand(ctx, command, args, flags) {
|
|
|
17003
16883
|
const action = command.slice(4);
|
|
17004
16884
|
if (action === "list") return (await ctx.call("listApps")).data;
|
|
17005
16885
|
if (action === "validate") {
|
|
17006
|
-
const
|
|
17007
|
-
return { definition:
|
|
16886
|
+
const doc = localApp(await jsonFile(await required2(flags, "file")));
|
|
16887
|
+
return { definition: doc, validation: await remoteValidation(ctx, doc) };
|
|
17008
16888
|
}
|
|
17009
16889
|
if (action === "add" || action === "update") {
|
|
17010
16890
|
let current;
|
|
@@ -17026,32 +16906,32 @@ async function appCommand(ctx, command, args, flags) {
|
|
|
17026
16906
|
))
|
|
17027
16907
|
fail("invalid_input", "Provide at least one application change.");
|
|
17028
16908
|
}
|
|
17029
|
-
const
|
|
17030
|
-
if (flags["key-output"] &&
|
|
16909
|
+
const doc = await appDocument(flags, current);
|
|
16910
|
+
if (flags["key-output"] && doc.config.authentication.type !== "api_key")
|
|
17031
16911
|
fail("invalid_input", "--key-output only applies to server apps.");
|
|
17032
16912
|
if (flags["dry-run"])
|
|
17033
16913
|
return {
|
|
17034
16914
|
dryRun: true,
|
|
17035
|
-
definition:
|
|
17036
|
-
validation: await remoteValidation(ctx,
|
|
16915
|
+
definition: doc,
|
|
16916
|
+
validation: await remoteValidation(ctx, doc, appId2 || void 0)
|
|
17037
16917
|
};
|
|
17038
16918
|
{
|
|
17039
16919
|
let app2;
|
|
17040
16920
|
let key;
|
|
17041
16921
|
if (action === "add") {
|
|
17042
16922
|
await ctx.bootstrap();
|
|
17043
|
-
if (
|
|
17044
|
-
const created = await ctx.keyOperation("app.add",
|
|
16923
|
+
if (doc.config.authentication.type === "api_key") {
|
|
16924
|
+
const created = await ctx.keyOperation("app.add", doc, flags["key-output"]);
|
|
17045
16925
|
app2 = created.operation.result.app;
|
|
17046
16926
|
key = created.key;
|
|
17047
16927
|
} else {
|
|
17048
|
-
const created = await ctx.operation("app.add",
|
|
16928
|
+
const created = await ctx.operation("app.add", doc);
|
|
17049
16929
|
app2 = created.result.app;
|
|
17050
16930
|
}
|
|
17051
16931
|
} else {
|
|
17052
16932
|
const updated = await ctx.call("updateApp", {
|
|
17053
16933
|
params: { app: appId2 },
|
|
17054
|
-
body: { ...
|
|
16934
|
+
body: { ...doc, revision }
|
|
17055
16935
|
});
|
|
17056
16936
|
({ app: app2 } = updated.data);
|
|
17057
16937
|
}
|
|
@@ -17069,7 +16949,7 @@ ${snippet}`;
|
|
|
17069
16949
|
...snippet ? { snippet } : {},
|
|
17070
16950
|
app: app2,
|
|
17071
16951
|
...key ? { applicationKey: key } : {},
|
|
17072
|
-
guidance:
|
|
16952
|
+
guidance: doc.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.`
|
|
17073
16953
|
};
|
|
17074
16954
|
}
|
|
17075
16955
|
}
|
|
@@ -17098,9 +16978,10 @@ ${snippet}`;
|
|
|
17098
16978
|
return { appId: appId2, applicationKey: created.key };
|
|
17099
16979
|
}
|
|
17100
16980
|
const appId = args[0] ?? "";
|
|
16981
|
+
if (action === "check") return (await ctx.call("checkApp", { params: { app: appId } })).data;
|
|
16982
|
+
if (action === "snippet") return appSnippet(ctx, appId, flags);
|
|
17101
16983
|
const { data } = await ctx.call("getApp", { params: { app: appId } });
|
|
17102
16984
|
if (action === "show") return data;
|
|
17103
|
-
const doc = documentOf(data.app);
|
|
17104
16985
|
if (action === "remove") {
|
|
17105
16986
|
await confirm(
|
|
17106
16987
|
`Delete app ${data.app.name} (${appId})? Its keys, users and authentication state will be removed and clients will lose access.`,
|
|
@@ -17108,100 +16989,52 @@ ${snippet}`;
|
|
|
17108
16989
|
);
|
|
17109
16990
|
return (await ctx.call("deleteApp", { params: { app: appId }, query: { confirm: appId } })).data;
|
|
17110
16991
|
}
|
|
17111
|
-
|
|
17112
|
-
|
|
17113
|
-
|
|
17114
|
-
|
|
17115
|
-
|
|
17116
|
-
);
|
|
17117
|
-
|
|
17118
|
-
|
|
17119
|
-
|
|
17120
|
-
|
|
17121
|
-
|
|
17122
|
-
|
|
17123
|
-
|
|
17124
|
-
|
|
17125
|
-
|
|
17126
|
-
ready: doc.status === "active" && reachableProviders(doc.config.routing, selected).length > 0,
|
|
17127
|
-
limitations: [
|
|
17128
|
-
"No inference was sent.",
|
|
17129
|
-
"Physical device attestation, issuer login, subscription entitlement and upstream credentials were not exercised."
|
|
17130
|
-
]
|
|
17131
|
-
};
|
|
17132
|
-
}
|
|
17133
|
-
if (action === "snippet") {
|
|
17134
|
-
const ios = doc.config.authentication.type === "apple_app_attest";
|
|
17135
|
-
const language = flags.language ?? (ios ? "swift" : "curl");
|
|
17136
|
-
if (!["swift", "curl"].includes(language))
|
|
17137
|
-
fail("invalid_input", "--language must be swift or curl.");
|
|
17138
|
-
if (language === "swift" !== ios)
|
|
17139
|
-
fail(
|
|
17140
|
-
"unsupported_snippet",
|
|
17141
|
-
ios ? "iOS applications authenticate with App Attest, which curl cannot perform." : "Swift snippets are for iOS applications.",
|
|
17142
|
-
`Run agw app snippet ${appId} --language ${ios ? "swift" : "curl"}.`
|
|
17143
|
-
);
|
|
17144
|
-
const notes2 = [];
|
|
17145
|
-
let example;
|
|
17146
|
-
if (flags.endpoint) {
|
|
17147
|
-
const endpoint = doc.config.endpoints[flags.endpoint];
|
|
17148
|
-
if (!endpoint)
|
|
17149
|
-
fail("endpoint_not_found", "Choose an existing custom endpoint.");
|
|
17150
|
-
const responses = endpoint.api_style === "responses";
|
|
17151
|
-
example = {
|
|
17152
|
-
target: { endpoint: flags.endpoint },
|
|
17153
|
-
body: responses ? { input: "Say hello." } : null,
|
|
17154
|
-
anthropic: false,
|
|
17155
|
-
gaps: responses ? [] : ["body"]
|
|
17156
|
-
};
|
|
17157
|
-
} else {
|
|
17158
|
-
const routing = doc.config.routing;
|
|
17159
|
-
const { data: all } = await ctx.call("listProviders");
|
|
17160
|
-
const reachable = reachableProviders(routing, all.providers);
|
|
17161
|
-
const requested = typeof flags.provider === "string" ? flags.provider : void 0;
|
|
17162
|
-
if (requested && !reachable.some((p) => p.slug === requested))
|
|
17163
|
-
fail(
|
|
17164
|
-
all.providers.some((p) => p.slug === requested) ? "provider_unavailable" : "provider_not_found",
|
|
17165
|
-
all.providers.some((p) => p.slug === requested) ? "That provider is disabled or outside this app's proxy policy." : "No provider has that slug.",
|
|
17166
|
-
"Run agw provider list for the slugs, and agw app show <id> for the policy."
|
|
17167
|
-
);
|
|
17168
|
-
const { data: catalog } = await ctx.call("listModelPrices");
|
|
17169
|
-
const providers = requested ? reachable.filter((p) => p.slug === requested) : reachable;
|
|
17170
|
-
example = firstRequest(routing, providers, catalog.prices);
|
|
17171
|
-
if (!requested && reachable.length > 1)
|
|
17172
|
-
notes2.push(`This app can reach ${reachable.length} providers. Add --provider <slug> for a different one.`);
|
|
17173
|
-
}
|
|
17174
|
-
notes2.push(...exampleNotes(example));
|
|
17175
|
-
const clientUrl = ctx.active?.deployment?.apiUrl ?? ctx.url;
|
|
17176
|
-
let snippet;
|
|
17177
|
-
if (ios) {
|
|
17178
|
-
if (swiftSignsInUsers(doc.config.authentication)) notes2.push(ISSUER_TOKEN_NOTE);
|
|
17179
|
-
snippet = swiftSnippet({
|
|
17180
|
-
baseUrl: clientUrl,
|
|
17181
|
-
appId,
|
|
17182
|
-
example,
|
|
17183
|
-
authentication: doc.config.authentication,
|
|
17184
|
-
notes: [
|
|
17185
|
-
"Swift package: https://github.com/maxceem/app-ai-gateway-swift (from: 1.0.0)",
|
|
17186
|
-
"Enable App Attest and test on a supported physical device.",
|
|
17187
|
-
...notes2
|
|
17188
|
-
]
|
|
17189
|
-
});
|
|
17190
|
-
} else {
|
|
17191
|
-
snippet = curlSnippet({ baseUrl: clientUrl, appId, example, notes: notes2 });
|
|
17192
|
-
}
|
|
17193
|
-
if (flags.output) {
|
|
17194
|
-
const output = await reserveOutput(flags.output);
|
|
17195
|
-
try {
|
|
17196
|
-
await output.write(snippet);
|
|
17197
|
-
return { output: output.path };
|
|
17198
|
-
} finally {
|
|
17199
|
-
await output.cancel();
|
|
16992
|
+
fail("unknown_command", "Unknown command.");
|
|
16993
|
+
}
|
|
16994
|
+
async function appSnippet(ctx, appId, flags) {
|
|
16995
|
+
const language = flags.language;
|
|
16996
|
+
if (language !== void 0 && !SNIPPET_LANGUAGES.includes(language))
|
|
16997
|
+
fail("invalid_input", "--language must be swift or curl.");
|
|
16998
|
+
const provider = typeof flags.provider === "string" ? flags.provider : void 0;
|
|
16999
|
+
let data;
|
|
17000
|
+
try {
|
|
17001
|
+
({ data } = await ctx.call("getAppSnippet", {
|
|
17002
|
+
params: { app: appId },
|
|
17003
|
+
query: {
|
|
17004
|
+
...language === void 0 ? {} : { language },
|
|
17005
|
+
...provider === void 0 ? {} : { provider },
|
|
17006
|
+
...flags.endpoint === void 0 ? {} : { endpoint: flags.endpoint }
|
|
17200
17007
|
}
|
|
17008
|
+
}));
|
|
17009
|
+
} catch (error51) {
|
|
17010
|
+
const remedy = error51 instanceof CliError ? snippetRemedy(error51.code, appId, language) : void 0;
|
|
17011
|
+
if (remedy === void 0 || !(error51 instanceof CliError)) throw error51;
|
|
17012
|
+
throw new CliError(error51.code, error51.message, remedy, error51.exitCode, error51.details);
|
|
17013
|
+
}
|
|
17014
|
+
if (flags.output) {
|
|
17015
|
+
const output = await reserveOutput(flags.output);
|
|
17016
|
+
try {
|
|
17017
|
+
await output.write(data.snippet);
|
|
17018
|
+
return { output: output.path };
|
|
17019
|
+
} finally {
|
|
17020
|
+
await output.cancel();
|
|
17201
17021
|
}
|
|
17202
|
-
return { language: ios ? "swift" : "shell", snippet, notes: notes2 };
|
|
17203
17022
|
}
|
|
17204
|
-
|
|
17023
|
+
return data;
|
|
17024
|
+
}
|
|
17025
|
+
function snippetRemedy(code, appId, language) {
|
|
17026
|
+
switch (code) {
|
|
17027
|
+
// Only a language that was asked for is refused, so the other one is right.
|
|
17028
|
+
case "unsupported_snippet":
|
|
17029
|
+
return `Run agw app snippet ${appId} --language ${language === "swift" ? "curl" : "swift"}.`;
|
|
17030
|
+
case "provider_not_found":
|
|
17031
|
+
case "provider_unavailable":
|
|
17032
|
+
return "Run agw provider list for the slugs, and agw app show <id> for the policy.";
|
|
17033
|
+
case "endpoint_not_found":
|
|
17034
|
+
return `Run agw app show ${appId} for its custom endpoints.`;
|
|
17035
|
+
default:
|
|
17036
|
+
return void 0;
|
|
17037
|
+
}
|
|
17205
17038
|
}
|
|
17206
17039
|
|
|
17207
17040
|
// src/context.ts
|
|
@@ -17606,11 +17439,22 @@ var ManagementKeySummarySchema = external_exports.object({
|
|
|
17606
17439
|
createdAt: external_exports.string(),
|
|
17607
17440
|
revokedAt: external_exports.string().nullable(),
|
|
17608
17441
|
source: external_exports.string().meta({
|
|
17609
|
-
description: "Where the key was issued: `console`, `cli` for a key a CLI login received,
|
|
17442
|
+
description: "Where the key was issued: `console`, `cli` for a key a CLI login received, `bootstrap` for the key a CLI account starts with, or `oauth` for an OAuth connection an MCP client holds. Display only.",
|
|
17610
17443
|
example: "cli"
|
|
17611
17444
|
}),
|
|
17445
|
+
credentialType: external_exports.enum(["apiKey", "oauth"]).meta({
|
|
17446
|
+
description: "What the row authenticates as: `apiKey` for a management key, `oauth` for an MCP client's connection. `source` says where it was issued and is display only.",
|
|
17447
|
+
example: "apiKey"
|
|
17448
|
+
}),
|
|
17612
17449
|
label: external_exports.string().nullable().meta({
|
|
17613
17450
|
description: "Who holds it, as the issuing client described itself, e.g. `CLI on mac-studio`. Display only."
|
|
17451
|
+
}),
|
|
17452
|
+
grant: CredentialGrantSchema.meta({
|
|
17453
|
+
description: "How much of its holder's role the key may use: `read` may list, inspect and validate but cannot change anything other than revoking itself; `manage` does everything the role allows. Keys issued before grants existed are `manage`.",
|
|
17454
|
+
example: "manage"
|
|
17455
|
+
}),
|
|
17456
|
+
clientId: external_exports.string().nullable().meta({
|
|
17457
|
+
description: "For an OAuth connection (`credentialType: \"oauth\"`), the client it was issued to: a registered client's id, or the https URL a client identifies itself with. Null for a key. A connection's `name` is the client's own name for itself, display only; its `expiresAt` is when it ends unless the client refreshes it first."
|
|
17614
17458
|
})
|
|
17615
17459
|
});
|
|
17616
17460
|
var ManagementKeyListResponseSchema = external_exports.object({
|
|
@@ -17767,7 +17611,7 @@ var IdentitySessionSchema = external_exports.object({
|
|
|
17767
17611
|
organization: OrganizationSummarySchema.nullable(),
|
|
17768
17612
|
role: OrganizationRoleSchema,
|
|
17769
17613
|
memberships: external_exports.array(OrganizationMembershipSchema),
|
|
17770
|
-
credentialType: external_exports.enum(["session", "apiKey"]),
|
|
17614
|
+
credentialType: external_exports.enum(["session", "apiKey", "oauth"]),
|
|
17771
17615
|
/**
|
|
17772
17616
|
* How the caller proved who they are: `interactive` for a human who just
|
|
17773
17617
|
* signed in, `credential` for a key. The CLI's browser handoff refuses to
|
|
@@ -17785,6 +17629,58 @@ var IdentitySessionSchema = external_exports.object({
|
|
|
17785
17629
|
}).nullable()
|
|
17786
17630
|
})
|
|
17787
17631
|
});
|
|
17632
|
+
var OAuthConsentDetailsResponseSchema = external_exports.object({
|
|
17633
|
+
id: external_exports.string(),
|
|
17634
|
+
/** `pending` until a person allows or denies it, or it lapses ten minutes after it opened. */
|
|
17635
|
+
state: external_exports.enum(["pending", "completed", "denied", "expired", "retired"]),
|
|
17636
|
+
client: external_exports.object({
|
|
17637
|
+
/** The `client_id`: a registered client's id, or the https URL of its metadata document. */
|
|
17638
|
+
id: external_exports.string(),
|
|
17639
|
+
/**
|
|
17640
|
+
* The client's own name for itself. Untrusted text: show it as text, as
|
|
17641
|
+
* declared by `domain`, never as markup.
|
|
17642
|
+
*/
|
|
17643
|
+
name: external_exports.string(),
|
|
17644
|
+
/** The host that served the client's metadata document; null for a registered client. */
|
|
17645
|
+
domain: external_exports.string().nullable(),
|
|
17646
|
+
source: external_exports.enum(["cimd", "registered"])
|
|
17647
|
+
}),
|
|
17648
|
+
/** Where the browser is sent once this is decided: the host of the client's redirect URI. */
|
|
17649
|
+
redirectHost: external_exports.string(),
|
|
17650
|
+
/** What the client asked for. The person chooses the grant; it is only the default. */
|
|
17651
|
+
requestedGrant: CredentialGrantSchema,
|
|
17652
|
+
expiresAt: external_exports.string(),
|
|
17653
|
+
/** The person signed in to this browser, who would allow it; null when nobody is. */
|
|
17654
|
+
viewer: external_exports.object({ name: external_exports.string().nullable(), email: external_exports.string().nullable() }).nullable(),
|
|
17655
|
+
/** The accounts that person may connect the client to: every live membership. */
|
|
17656
|
+
accounts: external_exports.array(external_exports.object({
|
|
17657
|
+
id: external_exports.string(),
|
|
17658
|
+
name: external_exports.string(),
|
|
17659
|
+
role: OrganizationRoleSchema
|
|
17660
|
+
})),
|
|
17661
|
+
/**
|
|
17662
|
+
* What stands between this browser and Allow: nobody is signed in, or the
|
|
17663
|
+
* person signed in belongs to no account the client could use. Null when
|
|
17664
|
+
* nothing does.
|
|
17665
|
+
*/
|
|
17666
|
+
blockedBy: external_exports.enum(["registration_required", "no_eligible_organization"]).nullable(),
|
|
17667
|
+
/**
|
|
17668
|
+
* Whether "continue without an account" would be admitted now: always on
|
|
17669
|
+
* the hosted service, and on a self-hosted deployment only while it is
|
|
17670
|
+
* empty. A guest connection always has the `manage` grant.
|
|
17671
|
+
*/
|
|
17672
|
+
guestAvailable: external_exports.boolean(),
|
|
17673
|
+
/** When an account created now without a person would be deleted unless claimed; null where accounts have no deadline. */
|
|
17674
|
+
guestExpiresAt: external_exports.string().nullable(),
|
|
17675
|
+
googleEnabled: external_exports.boolean(),
|
|
17676
|
+
/** Whether the console's own registration would admit a new person now. */
|
|
17677
|
+
registrationOpen: external_exports.boolean()
|
|
17678
|
+
});
|
|
17679
|
+
var OAuthConsentRedirectResponseSchema = external_exports.object({
|
|
17680
|
+
redirect: external_exports.string().meta({
|
|
17681
|
+
description: "The client's redirect URI with `code`, `state` and `iss`, or with `error=access_denied` after a denial. Navigate the browser there."
|
|
17682
|
+
})
|
|
17683
|
+
});
|
|
17788
17684
|
var OrganizationListResponseSchema = external_exports.object({
|
|
17789
17685
|
organizations: external_exports.array(OrganizationMembershipSchema)
|
|
17790
17686
|
});
|
|
@@ -17831,6 +17727,35 @@ var AppValidateResponseSchema = external_exports.object({
|
|
|
17831
17727
|
var AppDraftValidateResponseSchema = external_exports.object({
|
|
17832
17728
|
valid: external_exports.literal(true)
|
|
17833
17729
|
});
|
|
17730
|
+
var AppCheckResponseSchema = external_exports.object({
|
|
17731
|
+
appId: external_exports.string(),
|
|
17732
|
+
validation: AppValidateResponseSchema.meta({
|
|
17733
|
+
description: "The stored configuration judged as an update of it would be. A configuration that would be refused answers the check itself with 400."
|
|
17734
|
+
}),
|
|
17735
|
+
status: external_exports.enum(APP_STATUSES),
|
|
17736
|
+
providers: external_exports.array(external_exports.object({
|
|
17737
|
+
id: external_exports.string(),
|
|
17738
|
+
slug: external_exports.string(),
|
|
17739
|
+
status: external_exports.enum(["active", "disabled"])
|
|
17740
|
+
})).meta({
|
|
17741
|
+
description: "Every provider instance the application's routing names, a disabled one included so that it shows as disabled rather than missing."
|
|
17742
|
+
}),
|
|
17743
|
+
ready: external_exports.boolean().meta({
|
|
17744
|
+
description: "Whether the application is active and at least one provider it names is active."
|
|
17745
|
+
}),
|
|
17746
|
+
limitations: external_exports.array(external_exports.string()).meta({
|
|
17747
|
+
description: "What this check did not exercise, one sentence each."
|
|
17748
|
+
})
|
|
17749
|
+
});
|
|
17750
|
+
var AppSnippetResponseSchema = external_exports.object({
|
|
17751
|
+
language: external_exports.enum(["swift", "shell"]),
|
|
17752
|
+
snippet: external_exports.string().meta({
|
|
17753
|
+
description: "Source to paste. It never contains a credential: a server application's example reads its key from APP_AI_GATEWAY_KEY."
|
|
17754
|
+
}),
|
|
17755
|
+
notes: external_exports.array(external_exports.string()).meta({
|
|
17756
|
+
description: "What the example stands in for or leaves out, one sentence each. They are also written into the snippet as comments."
|
|
17757
|
+
})
|
|
17758
|
+
});
|
|
17834
17759
|
var CreatedApiKeySchema = external_exports.object({
|
|
17835
17760
|
id: external_exports.string(),
|
|
17836
17761
|
name: external_exports.string(),
|
|
@@ -17839,6 +17764,7 @@ var CreatedApiKeySchema = external_exports.object({
|
|
|
17839
17764
|
key_prefix: external_exports.string(),
|
|
17840
17765
|
created_at: external_exports.string()
|
|
17841
17766
|
});
|
|
17767
|
+
var AppKeyMetadataSchema = CreatedApiKeySchema.omit({ key: true });
|
|
17842
17768
|
var CreatedAppResponseSchema = AppResponseSchema.extend({
|
|
17843
17769
|
api_key: CreatedApiKeySchema.nullable()
|
|
17844
17770
|
});
|
|
@@ -17949,6 +17875,41 @@ var ModelPriceSchema = external_exports.object({
|
|
|
17949
17875
|
var PricesResponseSchema = external_exports.object({
|
|
17950
17876
|
prices: external_exports.record(external_exports.string(), external_exports.record(external_exports.string(), ModelPriceSchema))
|
|
17951
17877
|
});
|
|
17878
|
+
var OperationResultSchema = external_exports.object({
|
|
17879
|
+
/** The account a claim settled, a login landed in, or a bootstrap created. */
|
|
17880
|
+
accountId: external_exports.string().optional(),
|
|
17881
|
+
app: AppResponseSchema.shape.app.optional(),
|
|
17882
|
+
api_key: AppKeyMetadataSchema.nullable().optional(),
|
|
17883
|
+
provider: ProviderSummarySchema.optional(),
|
|
17884
|
+
gateway: ProviderGatewaySummarySchema.optional()
|
|
17885
|
+
}).meta({ id: "OperationResult" });
|
|
17886
|
+
var OperationStatusResponseSchema = external_exports.object({
|
|
17887
|
+
id: external_exports.string(),
|
|
17888
|
+
/**
|
|
17889
|
+
* The operation's kind as the gateway runs it: `app.add.reserved` for an app
|
|
17890
|
+
* created under a reservation, `provider.add.browser` for a provider whose
|
|
17891
|
+
* key a person enters in a browser, `claim`, and so on.
|
|
17892
|
+
*/
|
|
17893
|
+
kind: external_exports.string(),
|
|
17894
|
+
state: external_exports.enum(["pending", "completed", "expired"]),
|
|
17895
|
+
/** Set beside `state: "expired"` when a person declined the browser step rather than letting it lapse. */
|
|
17896
|
+
denied: external_exports.literal(true).optional(),
|
|
17897
|
+
createdAt: external_exports.string(),
|
|
17898
|
+
/** While pending, when it lapses unless completed. */
|
|
17899
|
+
expiresAt: external_exports.string(),
|
|
17900
|
+
result: OperationResultSchema.optional(),
|
|
17901
|
+
/** While a key the operation created can still be revealed, once, the page a person opens to see it. */
|
|
17902
|
+
reveal_url: external_exports.url().optional()
|
|
17903
|
+
}).meta({ id: "OperationStatus" });
|
|
17904
|
+
var RevealedOperationResponseSchema = external_exports.object({
|
|
17905
|
+
id: external_exports.string(),
|
|
17906
|
+
kind: external_exports.string(),
|
|
17907
|
+
result: external_exports.object({
|
|
17908
|
+
app: AppResponseSchema.shape.app.optional(),
|
|
17909
|
+
/** `key` is the plaintext. It is shown this once; the gateway keeps only a hash. */
|
|
17910
|
+
api_key: CreatedApiKeySchema.nullable().optional()
|
|
17911
|
+
})
|
|
17912
|
+
}).meta({ id: "RevealedOperation" });
|
|
17952
17913
|
|
|
17953
17914
|
// ../src/contracts/cli.ts
|
|
17954
17915
|
var CliProofSchema = external_exports.string().regex(/^[A-Za-z0-9_-]{32,256}$/);
|
|
@@ -18236,6 +18197,11 @@ var CLI_ERRORS = {
|
|
|
18236
18197
|
410: "The protected credential exchange has expired; no replacement is minted.",
|
|
18237
18198
|
429: "The durable initiation or submission rate limit was reached."
|
|
18238
18199
|
};
|
|
18200
|
+
var CONSENT_ERRORS = {
|
|
18201
|
+
409: "The authorization was denied, already completed by another choice, or the account the choice needed is gone; nothing was written.",
|
|
18202
|
+
410: "The authorization lapsed: it is open for ten minutes. Start the connection again from the client."
|
|
18203
|
+
};
|
|
18204
|
+
var CONSENT_DESCRIPTION = "The API of the console's OAuth consent page, which an MCP client's authorization request sends a person to. First-party browser only: both the request URL origin and the exact Origin header must match the console origin, and `submissionToken` is the proof from the consent page URL's fragment, so it never travels in a URL. Allowing, continuing without an account and denying each answer the `redirect` to send the browser to, and answer it again, byte for byte, when repeated with the same proof.";
|
|
18239
18205
|
var BROWSER_HANDOFF_DESCRIPTION = "First-party browser only: both the request URL origin and exact Origin header must match consoleOrigin; a separate submissionToken is required \u2014 the proof from the approval URL's fragment, or, for a login, the user code the terminal shows. Identity approval (a claim or a login) also requires an interactive human session; registration is limited to a valid pending claim or login. Provider secret values are write-only.";
|
|
18240
18206
|
var SignUpRequestSchema = external_exports.object({
|
|
18241
18207
|
name: external_exports.string(),
|
|
@@ -18486,7 +18452,7 @@ var CATALOG = {
|
|
|
18486
18452
|
security: "management",
|
|
18487
18453
|
// The whole billing subtree is a person's to act on: a service credential
|
|
18488
18454
|
// may run an account but may not buy, change or cancel what pays for it.
|
|
18489
|
-
policy: {
|
|
18455
|
+
policy: { session: true },
|
|
18490
18456
|
response: BillingPlansResponseSchema,
|
|
18491
18457
|
responseDescription: "Billing service response."
|
|
18492
18458
|
},
|
|
@@ -18497,7 +18463,7 @@ var CATALOG = {
|
|
|
18497
18463
|
tags: ["Admin billing"],
|
|
18498
18464
|
summary: "Get billing access and the current allowance period",
|
|
18499
18465
|
security: "management",
|
|
18500
|
-
policy: {
|
|
18466
|
+
policy: { session: true },
|
|
18501
18467
|
response: BillingStatusResponseSchema,
|
|
18502
18468
|
responseDescription: "Billing access, and the current period against the plan's request allowance when an entitlement resolves."
|
|
18503
18469
|
},
|
|
@@ -18508,7 +18474,7 @@ var CATALOG = {
|
|
|
18508
18474
|
tags: ["Admin billing"],
|
|
18509
18475
|
summary: "Create a hosted checkout",
|
|
18510
18476
|
security: "management",
|
|
18511
|
-
policy: {
|
|
18477
|
+
policy: { session: true },
|
|
18512
18478
|
request: BillingCheckoutRequestSchema,
|
|
18513
18479
|
response: BillingCheckoutResponseSchema,
|
|
18514
18480
|
responseDescription: "Hosted checkout URL."
|
|
@@ -18520,7 +18486,7 @@ var CATALOG = {
|
|
|
18520
18486
|
tags: ["Admin billing"],
|
|
18521
18487
|
summary: "Change the subscription plan",
|
|
18522
18488
|
security: "management",
|
|
18523
|
-
policy: {
|
|
18489
|
+
policy: { session: true },
|
|
18524
18490
|
request: BillingPlanSelectionSchema,
|
|
18525
18491
|
response: BillingChangeResponseSchema,
|
|
18526
18492
|
responseDescription: "Billing service response."
|
|
@@ -18532,7 +18498,7 @@ var CATALOG = {
|
|
|
18532
18498
|
tags: ["Admin billing"],
|
|
18533
18499
|
summary: "Resume a canceled subscription",
|
|
18534
18500
|
security: "management",
|
|
18535
|
-
policy: {
|
|
18501
|
+
policy: { session: true },
|
|
18536
18502
|
request: BillingPlanSelectionSchema,
|
|
18537
18503
|
response: BillingChangeResponseSchema,
|
|
18538
18504
|
responseDescription: "Billing service response."
|
|
@@ -18544,7 +18510,7 @@ var CATALOG = {
|
|
|
18544
18510
|
tags: ["Admin billing"],
|
|
18545
18511
|
summary: "Cancel the subscription at period end",
|
|
18546
18512
|
security: "management",
|
|
18547
|
-
policy: {
|
|
18513
|
+
policy: { session: true },
|
|
18548
18514
|
response: BillingCancelResponseSchema,
|
|
18549
18515
|
responseDescription: "Cancellation accepted."
|
|
18550
18516
|
},
|
|
@@ -18555,7 +18521,7 @@ var CATALOG = {
|
|
|
18555
18521
|
tags: ["Admin billing"],
|
|
18556
18522
|
summary: "Start a no-card trial",
|
|
18557
18523
|
security: "management",
|
|
18558
|
-
policy: {
|
|
18524
|
+
policy: { session: true },
|
|
18559
18525
|
request: BillingTrialRequestSchema,
|
|
18560
18526
|
response: BillingAccessSchema,
|
|
18561
18527
|
responseDescription: "Trial access state."
|
|
@@ -18592,7 +18558,7 @@ var CATALOG = {
|
|
|
18592
18558
|
security: "management",
|
|
18593
18559
|
// A POST that stores nothing: it answers whether a body would be accepted,
|
|
18594
18560
|
// which is a read of the configuration rules and not a write.
|
|
18595
|
-
policy: { role: "member", access: "read" },
|
|
18561
|
+
policy: { role: "member", access: "read", grant: "read" },
|
|
18596
18562
|
params: APP_PARAM,
|
|
18597
18563
|
request: AppWriteSchema,
|
|
18598
18564
|
response: AppValidateResponseSchema,
|
|
@@ -18605,11 +18571,34 @@ var CATALOG = {
|
|
|
18605
18571
|
summary: "Validate a new application configuration without saving it",
|
|
18606
18572
|
description: "Judged as a creation would be, against the account's current providers and prices.",
|
|
18607
18573
|
security: "management",
|
|
18608
|
-
policy: { role: "member", access: "read" },
|
|
18574
|
+
policy: { role: "member", access: "read", grant: "read" },
|
|
18609
18575
|
request: AppWriteSchema,
|
|
18610
18576
|
response: AppDraftValidateResponseSchema,
|
|
18611
18577
|
responseDescription: "The configuration would be accepted."
|
|
18612
18578
|
},
|
|
18579
|
+
checkApp: {
|
|
18580
|
+
method: "GET",
|
|
18581
|
+
path: "/v1/admin/apps/{app}/check",
|
|
18582
|
+
tags: ["Admin applications"],
|
|
18583
|
+
summary: "Check whether an application is ready to serve requests",
|
|
18584
|
+
description: "Validates the stored configuration as an update of it would be judged, lists every provider its routing names with that provider's status \u2014 a disabled one included \u2014 and reports `ready` when the application is active and at least one of those providers is active. It sends no inference: App Attest on a physical device, issuer sign-in, subscription entitlements and the upstream credentials themselves are not exercised, and `limitations` says so in every answer. A stored configuration that would no longer be accepted answers `400 invalid_request` with the reason.",
|
|
18585
|
+
security: "management",
|
|
18586
|
+
params: APP_PARAM,
|
|
18587
|
+
response: AppCheckResponseSchema,
|
|
18588
|
+
responseDescription: "What the check established, and what it did not exercise."
|
|
18589
|
+
},
|
|
18590
|
+
getAppSnippet: {
|
|
18591
|
+
method: "GET",
|
|
18592
|
+
path: "/v1/admin/apps/{app}/snippet",
|
|
18593
|
+
tags: ["Admin applications"],
|
|
18594
|
+
summary: "Get the first request an application can send",
|
|
18595
|
+
description: "Written against what the application has today: a provider it can reach and a priced model where those exist, and named placeholders, explained in `notes`, where they do not. An App Attest application gets Swift for the App AI Gateway Swift package and a server application gets curl; asking for the other answers `400 unsupported_snippet`. The URL is the one application clients call, which is this deployment's separate API host where it publishes one. A snippet never contains a credential: a server application's example reads its key from the `APP_AI_GATEWAY_KEY` environment variable.",
|
|
18596
|
+
security: "management",
|
|
18597
|
+
params: APP_PARAM,
|
|
18598
|
+
query: AppSnippetQuerySchema,
|
|
18599
|
+
response: AppSnippetResponseSchema,
|
|
18600
|
+
responseDescription: "The example, its language and the notes written into it."
|
|
18601
|
+
},
|
|
18613
18602
|
deleteApp: {
|
|
18614
18603
|
method: "DELETE",
|
|
18615
18604
|
path: "/v1/admin/apps/{app}",
|
|
@@ -18638,7 +18627,7 @@ var CATALOG = {
|
|
|
18638
18627
|
path: "/v1/admin/keys",
|
|
18639
18628
|
tags: ["Admin management keys"],
|
|
18640
18629
|
summary: "Create a management key",
|
|
18641
|
-
description: "Console session only, and requires the owner or admin role. A management key cannot create another one, so revoking a key you handed out ends that access for good. The plaintext agw_mgmt_ token is returned once, and never expires; the account's own deadline is the only one.",
|
|
18630
|
+
description: "Console session only, and requires the owner or admin role. A management key cannot create another one, so revoking a key you handed out ends that access for good. The plaintext agw_mgmt_ token is returned once, and never expires; the account's own deadline is the only one. A key created with the `read` grant cannot change your apps, providers or settings: a change its owner's role would otherwise allow is refused with `403 grant_insufficient`, and the key may still revoke itself.",
|
|
18642
18631
|
security: "session",
|
|
18643
18632
|
request: ManagementKeyCreateRequestSchema,
|
|
18644
18633
|
status: 201,
|
|
@@ -18797,6 +18786,9 @@ var CATALOG = {
|
|
|
18797
18786
|
tags: ["Admin organizations"],
|
|
18798
18787
|
summary: "List the organizations the caller belongs to",
|
|
18799
18788
|
security: "management",
|
|
18789
|
+
// cf-auth lists them only for an interactive session: a key is scoped to
|
|
18790
|
+
// its one organization and may not read the others its owner belongs to.
|
|
18791
|
+
policy: { session: true },
|
|
18800
18792
|
response: OrganizationListResponseSchema,
|
|
18801
18793
|
responseDescription: "Memberships ordered by organization creation time."
|
|
18802
18794
|
},
|
|
@@ -18810,8 +18802,11 @@ var CATALOG = {
|
|
|
18810
18802
|
// Switching the active organization re-signs the cookie naming which
|
|
18811
18803
|
// tenant the caller reads; gating it behind owner/admin would strand a
|
|
18812
18804
|
// read-only member in one organization, and gating it behind setup access
|
|
18813
|
-
// would strand them in an account whose trial has ended.
|
|
18814
|
-
|
|
18805
|
+
// would strand them in an account whose trial has ended. Only an
|
|
18806
|
+
// interactive session has a cookie to re-sign, and cf-auth refuses any
|
|
18807
|
+
// other caller, so the entry says so rather than leaving a key to learn it
|
|
18808
|
+
// from the handler — after the grant check, with a remedy that cannot work.
|
|
18809
|
+
policy: { role: "member", access: "read", session: true },
|
|
18815
18810
|
request: OrganizationSelectRequestSchema,
|
|
18816
18811
|
response: IdentitySessionSchema,
|
|
18817
18812
|
responseDescription: "Session rescoped to the selected organization."
|
|
@@ -19021,6 +19016,34 @@ var CATALOG = {
|
|
|
19021
19016
|
response: PricesResponseSchema,
|
|
19022
19017
|
responseDescription: "The priced model catalog this deployment enforces."
|
|
19023
19018
|
},
|
|
19019
|
+
getOperation: {
|
|
19020
|
+
method: "GET",
|
|
19021
|
+
path: "/v1/admin/operations/{id}",
|
|
19022
|
+
tags: ["Admin change operations"],
|
|
19023
|
+
summary: "Get where an operation stands",
|
|
19024
|
+
description: "Any credential of the account that opened the operation may read it, whatever its role and grant; any other is answered `404 operation_not_found`, so the answer never says whether the id exists elsewhere. A pending operation reports when it lapses, a completed one what it changed, and one declined in a browser `expired` with `denied: true`. It never returns a secret and never collects one: a key the operation created is revealed only on the page `reveal_url` names, to a person signed in to the console as an owner or admin. The approval link of a pending browser step is in the answer that opened it and nowhere else.",
|
|
19025
|
+
security: "management",
|
|
19026
|
+
params: { id: { example: "4b1e\u2026" } },
|
|
19027
|
+
response: OperationStatusResponseSchema,
|
|
19028
|
+
responseDescription: "Where the operation stands, and what it changed once completed."
|
|
19029
|
+
},
|
|
19030
|
+
revealOperation: {
|
|
19031
|
+
method: "POST",
|
|
19032
|
+
path: "/v1/admin/operations/{id}/reveal",
|
|
19033
|
+
tags: ["Admin change operations"],
|
|
19034
|
+
summary: "Reveal the key an operation created, once",
|
|
19035
|
+
description: "Console session only, and requires the owner or admin role in the operation's account; a management key is refused with `403 session_required`. First-party console only: the request URL's origin and the exact `Origin` header must both be the console origin. Answers the key an app or key creation made once, within 15 minutes of its creation: a second reveal is `409 already_revealed`, and one after the window, or for a key revoked since, is `410 operation_expired`. Reveal it only on a page a person asked to see it on, never on load.",
|
|
19036
|
+
security: "session",
|
|
19037
|
+
// Read access: the key already exists, and collecting it is not a change.
|
|
19038
|
+
policy: { role: "admin", access: "read", session: true },
|
|
19039
|
+
params: { id: { example: "4b1e\u2026" } },
|
|
19040
|
+
response: RevealedOperationResponseSchema,
|
|
19041
|
+
responseDescription: "The key the operation created, with its plaintext.",
|
|
19042
|
+
errors: {
|
|
19043
|
+
409: "The key was already revealed.",
|
|
19044
|
+
410: "The reveal window passed, or the key was revoked since it was created."
|
|
19045
|
+
}
|
|
19046
|
+
},
|
|
19024
19047
|
getCliCapabilities: {
|
|
19025
19048
|
method: "GET",
|
|
19026
19049
|
path: "/v1/cli/capabilities",
|
|
@@ -19091,9 +19114,9 @@ var CATALOG = {
|
|
|
19091
19114
|
summary: "Revoke the management key this request is sent with",
|
|
19092
19115
|
description: "Logging a CLI out. Any management key may end itself, whatever its role and whatever the account's standing; a browser session cannot use this and signs out instead.",
|
|
19093
19116
|
security: "management",
|
|
19094
|
-
// Holding a key is authority enough to end it: a member may,
|
|
19095
|
-
//
|
|
19096
|
-
policy: { role: "member", access: "read" },
|
|
19117
|
+
// Holding a key is authority enough to end it: a member may, an account
|
|
19118
|
+
// past its free window may, and a key with the `read` grant may.
|
|
19119
|
+
policy: { role: "member", access: "read", grant: "read" },
|
|
19097
19120
|
response: CliCredentialRevokeResponseSchema,
|
|
19098
19121
|
responseDescription: "The key is revoked; the next request made with it is refused.",
|
|
19099
19122
|
errors: CLI_ERRORS
|
|
@@ -19205,6 +19228,74 @@ var CATALOG = {
|
|
|
19205
19228
|
responseDescription: "A new human identity for a pending claim or login, with its session set as a cookie.",
|
|
19206
19229
|
errors: CLI_ERRORS
|
|
19207
19230
|
},
|
|
19231
|
+
/*
|
|
19232
|
+
* The OAuth consent page's API. Like the CLI handoff, there is no page to
|
|
19233
|
+
* fetch here: the console renders `/oauth/consent` from its own bundle, and
|
|
19234
|
+
* these four carry the proof from its fragment in their bodies. The OAuth
|
|
19235
|
+
* endpoints themselves — discovery, `/oauth/authorize`, `/oauth/token` and
|
|
19236
|
+
* `/oauth/revoke` — are HTTP-only and not management operations, so they
|
|
19237
|
+
* have no entries; the MCP guide documents them.
|
|
19238
|
+
*/
|
|
19239
|
+
getOauthConsentDetails: {
|
|
19240
|
+
method: "POST",
|
|
19241
|
+
path: "/v1/console/oauth/{id}/details",
|
|
19242
|
+
tags: ["Console OAuth consent"],
|
|
19243
|
+
summary: "OAuth consent: details",
|
|
19244
|
+
description: `${CONSENT_DESCRIPTION} Reading the details changes nothing. The client's name is its own claim, shown as declared by the host that served its metadata document.`,
|
|
19245
|
+
security: "public",
|
|
19246
|
+
params: { id: { description: "The authorization's id, from the consent page URL's `id` query parameter." } },
|
|
19247
|
+
request: OAuthConsentProofSchema,
|
|
19248
|
+
response: OAuthConsentDetailsResponseSchema,
|
|
19249
|
+
responseDescription: "Who is asking and for what, who this browser would allow it as, the accounts that person may choose, and whether continuing without an account is available.",
|
|
19250
|
+
errors: CONSENT_ERRORS
|
|
19251
|
+
},
|
|
19252
|
+
allowOauthConsent: {
|
|
19253
|
+
method: "POST",
|
|
19254
|
+
path: "/v1/console/oauth/{id}/allow",
|
|
19255
|
+
tags: ["Console OAuth consent"],
|
|
19256
|
+
summary: "OAuth consent: allow",
|
|
19257
|
+
description: `${CONSENT_DESCRIPTION} Console session only: the person signed in to this browser connects the client to one of their accounts, with the grant they choose. A management key or an OAuth access token is refused with \`403 session_required\`.`,
|
|
19258
|
+
security: "session",
|
|
19259
|
+
// Any member may connect a client with their own role, as any member may
|
|
19260
|
+
// log a CLI in; the grant is theirs to narrow. Read access, because
|
|
19261
|
+
// connecting changes nothing in the account.
|
|
19262
|
+
policy: { role: "member", access: "read", session: true },
|
|
19263
|
+
params: { id: { description: "The authorization's id." } },
|
|
19264
|
+
request: OAuthConsentAllowRequestSchema,
|
|
19265
|
+
response: OAuthConsentRedirectResponseSchema,
|
|
19266
|
+
responseDescription: "The client's redirect URI with the authorization code.",
|
|
19267
|
+
errors: CONSENT_ERRORS
|
|
19268
|
+
},
|
|
19269
|
+
continueOauthWithoutAccount: {
|
|
19270
|
+
method: "POST",
|
|
19271
|
+
path: "/v1/console/oauth/{id}/guest",
|
|
19272
|
+
tags: ["Console OAuth consent"],
|
|
19273
|
+
summary: "OAuth consent: continue without an account",
|
|
19274
|
+
description: `${CONSENT_DESCRIPTION} Creates an account nobody has claimed yet, owned by the connection's own service identity, and connects the client to it with the \`manage\` grant whatever it asked for. Admitted where the deployment allows a new unclaimed account \u2014 always on the hosted service, where it is deleted unless a person claims it, and on a self-hosted deployment only while it is empty \u2014 and counted per network address with the CLI's account creation. Repeating it for the same authorization answers the same account.`,
|
|
19275
|
+
security: "public",
|
|
19276
|
+
params: { id: { description: "The authorization's id." } },
|
|
19277
|
+
request: OAuthConsentProofSchema,
|
|
19278
|
+
response: OAuthConsentRedirectResponseSchema,
|
|
19279
|
+
responseDescription: "The client's redirect URI with the authorization code.",
|
|
19280
|
+
errors: {
|
|
19281
|
+
...CONSENT_ERRORS,
|
|
19282
|
+
409: "This deployment no longer admits an account without a person (a self-hosted one that has been initialized), or another choice completed the authorization first.",
|
|
19283
|
+
429: "Too many accounts were created from this network address. Carries Retry-After."
|
|
19284
|
+
}
|
|
19285
|
+
},
|
|
19286
|
+
denyOauthConsent: {
|
|
19287
|
+
method: "POST",
|
|
19288
|
+
path: "/v1/console/oauth/{id}/deny",
|
|
19289
|
+
tags: ["Console OAuth consent"],
|
|
19290
|
+
summary: "OAuth consent: deny",
|
|
19291
|
+
description: `${CONSENT_DESCRIPTION} Anyone holding the link may decline, signed in or not.`,
|
|
19292
|
+
security: "public",
|
|
19293
|
+
params: { id: { description: "The authorization's id." } },
|
|
19294
|
+
request: OAuthConsentProofSchema,
|
|
19295
|
+
response: OAuthConsentRedirectResponseSchema,
|
|
19296
|
+
responseDescription: "The client's redirect URI with `error=access_denied`.",
|
|
19297
|
+
errors: CONSENT_ERRORS
|
|
19298
|
+
},
|
|
19208
19299
|
cliBrowserGoogle: {
|
|
19209
19300
|
method: "POST",
|
|
19210
19301
|
path: "/v1/cli/browser/{id}/google",
|
|
@@ -19237,6 +19328,93 @@ function operationPath(name, ...rest) {
|
|
|
19237
19328
|
return query === void 0 ? filled : filled + searchSuffix(query);
|
|
19238
19329
|
}
|
|
19239
19330
|
|
|
19331
|
+
// src/client.ts
|
|
19332
|
+
var CLI_OPERATIONS = /* @__PURE__ */ new Set([
|
|
19333
|
+
"getCliCapabilities",
|
|
19334
|
+
"bootstrapCliAccount",
|
|
19335
|
+
"createCliOperation",
|
|
19336
|
+
"pollCliOperation",
|
|
19337
|
+
"getCliAccount",
|
|
19338
|
+
"getCliUsage",
|
|
19339
|
+
"openCliLogin",
|
|
19340
|
+
"redeemCliLogin",
|
|
19341
|
+
"revokeCliCredential"
|
|
19342
|
+
]);
|
|
19343
|
+
var pathFor = operationPath;
|
|
19344
|
+
var GatewayClient = class {
|
|
19345
|
+
transport;
|
|
19346
|
+
source;
|
|
19347
|
+
constructor(transport, source) {
|
|
19348
|
+
this.transport = transport;
|
|
19349
|
+
this.source = source;
|
|
19350
|
+
}
|
|
19351
|
+
/**
|
|
19352
|
+
* Sends one documented operation and parses its answer with that operation's
|
|
19353
|
+
* own schema.
|
|
19354
|
+
*
|
|
19355
|
+
* The parse is not a formality. zod drops what a schema does not declare, so
|
|
19356
|
+
* this is what keeps an undocumented server field — or anything an unexpected
|
|
19357
|
+
* response carries — out of the values the commands go on to print.
|
|
19358
|
+
*/
|
|
19359
|
+
async publicCall(name, { params, query, body, key, url: url2, headers } = {}) {
|
|
19360
|
+
const wire = await this.transport.request(
|
|
19361
|
+
url2 ?? this.source.url(),
|
|
19362
|
+
pathFor(name, params, query),
|
|
19363
|
+
{
|
|
19364
|
+
method: CATALOG[name].method,
|
|
19365
|
+
...body === void 0 ? {} : { body },
|
|
19366
|
+
...key === void 0 ? {} : { key },
|
|
19367
|
+
...headers === void 0 ? {} : { headers }
|
|
19368
|
+
}
|
|
19369
|
+
);
|
|
19370
|
+
return { data: parse4(name, wire.data) };
|
|
19371
|
+
}
|
|
19372
|
+
/** The same call, with the connection's management credential attached. */
|
|
19373
|
+
async call(name, options = {}) {
|
|
19374
|
+
const key = options.key ?? this.credential(name);
|
|
19375
|
+
try {
|
|
19376
|
+
return await this.publicCall(name, { ...options, key });
|
|
19377
|
+
} catch (error51) {
|
|
19378
|
+
if (error51 instanceof CliError && error51.details?.status === 401 && options.key === void 0 && this.source.environmentKey())
|
|
19379
|
+
throw new CliError(
|
|
19380
|
+
error51.code,
|
|
19381
|
+
error51.message,
|
|
19382
|
+
`Check ${MANAGEMENT_KEY_ENV}: the deployment refused the key it holds.`,
|
|
19383
|
+
error51.exitCode,
|
|
19384
|
+
error51.details
|
|
19385
|
+
);
|
|
19386
|
+
throw error51;
|
|
19387
|
+
}
|
|
19388
|
+
}
|
|
19389
|
+
/**
|
|
19390
|
+
* The current management credential, or the refusal that names the way back.
|
|
19391
|
+
* An admin operation is also reachable from a plain deployment connection,
|
|
19392
|
+
* which is why those get the wider instruction.
|
|
19393
|
+
*/
|
|
19394
|
+
credential(name) {
|
|
19395
|
+
const token = this.source.managementKey();
|
|
19396
|
+
if (!token)
|
|
19397
|
+
fail(
|
|
19398
|
+
"login_required",
|
|
19399
|
+
"The selected connection is not authenticated.",
|
|
19400
|
+
CLI_OPERATIONS.has(name) ? `Run agw account login, or set ${MANAGEMENT_KEY_ENV}.` : `Run agw account login or agw deployment connect, or set ${MANAGEMENT_KEY_ENV}.`,
|
|
19401
|
+
4
|
|
19402
|
+
);
|
|
19403
|
+
return token;
|
|
19404
|
+
}
|
|
19405
|
+
};
|
|
19406
|
+
function parse4(name, data) {
|
|
19407
|
+
const parsed = CATALOG[name].response.safeParse(data);
|
|
19408
|
+
if (!parsed.success)
|
|
19409
|
+
fail(
|
|
19410
|
+
"invalid_response",
|
|
19411
|
+
`The deployment answered ${String(name)} with an unusable body.`,
|
|
19412
|
+
"Check that the CLI and the deployment are the same release.",
|
|
19413
|
+
3
|
|
19414
|
+
);
|
|
19415
|
+
return parsed.data;
|
|
19416
|
+
}
|
|
19417
|
+
|
|
19240
19418
|
// src/login.ts
|
|
19241
19419
|
import { createServer } from "node:http";
|
|
19242
19420
|
import { hostname as hostname4, platform as platform2, release } from "node:os";
|
|
@@ -19399,17 +19577,6 @@ function jsonLine(line, paint) {
|
|
|
19399
19577
|
}
|
|
19400
19578
|
|
|
19401
19579
|
// src/context.ts
|
|
19402
|
-
var CLI_OPERATIONS = /* @__PURE__ */ new Set([
|
|
19403
|
-
"getCliCapabilities",
|
|
19404
|
-
"bootstrapCliAccount",
|
|
19405
|
-
"createCliOperation",
|
|
19406
|
-
"pollCliOperation",
|
|
19407
|
-
"getCliAccount",
|
|
19408
|
-
"getCliUsage",
|
|
19409
|
-
"openCliLogin",
|
|
19410
|
-
"redeemCliLogin",
|
|
19411
|
-
"revokeCliCredential"
|
|
19412
|
-
]);
|
|
19413
19580
|
var LOGIN_TIMEOUT_SECONDS = 300;
|
|
19414
19581
|
var LOGIN_RECORD_MS = 36e5;
|
|
19415
19582
|
var LOOPBACK_GRACE_MS = 1e4;
|
|
@@ -19422,7 +19589,6 @@ var defaultIo = () => ({
|
|
|
19422
19589
|
});
|
|
19423
19590
|
var KEY_OPERATION_KINDS = /* @__PURE__ */ new Set(["app.add", "app.key.add"]);
|
|
19424
19591
|
var OPERATION_RETENTION_MS = 90 * 864e5;
|
|
19425
|
-
var pathFor = operationPath;
|
|
19426
19592
|
var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 425, 429]);
|
|
19427
19593
|
var OPERATION_BOUND_STATUS = /* @__PURE__ */ new Set([409, 410]);
|
|
19428
19594
|
function definitiveRefusal(error51) {
|
|
@@ -19447,7 +19613,8 @@ function bootstrapCredential(operation2) {
|
|
|
19447
19613
|
var Context = class {
|
|
19448
19614
|
store;
|
|
19449
19615
|
state;
|
|
19450
|
-
|
|
19616
|
+
/** Every request this context sends goes through it. */
|
|
19617
|
+
client;
|
|
19451
19618
|
flags;
|
|
19452
19619
|
io;
|
|
19453
19620
|
/** Operations whose result this run has printed, released once stdout has taken it. */
|
|
@@ -19456,9 +19623,13 @@ var Context = class {
|
|
|
19456
19623
|
constructor(store, state, transport, flags, io = {}) {
|
|
19457
19624
|
this.store = store;
|
|
19458
19625
|
this.state = state;
|
|
19459
|
-
this.transport = transport;
|
|
19460
19626
|
this.flags = flags;
|
|
19461
19627
|
this.io = { ...defaultIo(), ...io };
|
|
19628
|
+
this.client = new GatewayClient(transport, {
|
|
19629
|
+
url: () => this.url,
|
|
19630
|
+
managementKey: () => this.managementKey,
|
|
19631
|
+
environmentKey: () => this.environmentKey
|
|
19632
|
+
});
|
|
19462
19633
|
}
|
|
19463
19634
|
get active() {
|
|
19464
19635
|
return this.state.active;
|
|
@@ -19491,70 +19662,13 @@ var Context = class {
|
|
|
19491
19662
|
async save() {
|
|
19492
19663
|
await this.store.write(this.state);
|
|
19493
19664
|
}
|
|
19494
|
-
/**
|
|
19495
|
-
|
|
19496
|
-
|
|
19497
|
-
*
|
|
19498
|
-
* The parse is not a formality. zod drops what a schema does not declare, so
|
|
19499
|
-
* this is what keeps an undocumented server field — or anything an unexpected
|
|
19500
|
-
* response carries — out of the values the commands go on to print.
|
|
19501
|
-
*/
|
|
19502
|
-
async publicCall(name, { params, query, body, key, url: url2, headers } = {}) {
|
|
19503
|
-
const wire = await this.transport.request(
|
|
19504
|
-
url2 ?? this.url,
|
|
19505
|
-
pathFor(name, params, query),
|
|
19506
|
-
{
|
|
19507
|
-
method: CATALOG[name].method,
|
|
19508
|
-
...body === void 0 ? {} : { body },
|
|
19509
|
-
...key === void 0 ? {} : { key },
|
|
19510
|
-
...headers === void 0 ? {} : { headers }
|
|
19511
|
-
}
|
|
19512
|
-
);
|
|
19513
|
-
return { data: this.parse(name, wire.data) };
|
|
19514
|
-
}
|
|
19515
|
-
parse(name, data) {
|
|
19516
|
-
const parsed = CATALOG[name].response.safeParse(data);
|
|
19517
|
-
if (!parsed.success)
|
|
19518
|
-
fail(
|
|
19519
|
-
"invalid_response",
|
|
19520
|
-
`The deployment answered ${String(name)} with an unusable body.`,
|
|
19521
|
-
"Check that the CLI and the deployment are the same release.",
|
|
19522
|
-
3
|
|
19523
|
-
);
|
|
19524
|
-
return parsed.data;
|
|
19665
|
+
/** One documented operation, sent with only the credential `options` names; see {@link GatewayClient}. */
|
|
19666
|
+
publicCall(name, options = {}) {
|
|
19667
|
+
return this.client.publicCall(name, options);
|
|
19525
19668
|
}
|
|
19526
19669
|
/** The same call, with the connection's management credential attached. */
|
|
19527
|
-
|
|
19528
|
-
|
|
19529
|
-
try {
|
|
19530
|
-
return await this.publicCall(name, { ...options, key });
|
|
19531
|
-
} catch (error51) {
|
|
19532
|
-
if (error51 instanceof CliError && error51.details?.status === 401 && options.key === void 0 && this.environmentKey)
|
|
19533
|
-
throw new CliError(
|
|
19534
|
-
error51.code,
|
|
19535
|
-
error51.message,
|
|
19536
|
-
`Check ${MANAGEMENT_KEY_ENV}: the deployment refused the key it holds.`,
|
|
19537
|
-
error51.exitCode,
|
|
19538
|
-
error51.details
|
|
19539
|
-
);
|
|
19540
|
-
throw error51;
|
|
19541
|
-
}
|
|
19542
|
-
}
|
|
19543
|
-
/**
|
|
19544
|
-
* The current management credential, or the refusal that names the way back.
|
|
19545
|
-
* An admin operation is also reachable from a plain deployment connection,
|
|
19546
|
-
* which is why those get the wider instruction.
|
|
19547
|
-
*/
|
|
19548
|
-
credential(name) {
|
|
19549
|
-
const token = this.managementKey;
|
|
19550
|
-
if (!token)
|
|
19551
|
-
fail(
|
|
19552
|
-
"login_required",
|
|
19553
|
-
"The selected connection is not authenticated.",
|
|
19554
|
-
CLI_OPERATIONS.has(name) ? `Run agw account login, or set ${MANAGEMENT_KEY_ENV}.` : `Run agw account login or agw deployment connect, or set ${MANAGEMENT_KEY_ENV}.`,
|
|
19555
|
-
4
|
|
19556
|
-
);
|
|
19557
|
-
return token;
|
|
19670
|
+
call(name, options = {}) {
|
|
19671
|
+
return this.client.call(name, options);
|
|
19558
19672
|
}
|
|
19559
19673
|
/**
|
|
19560
19674
|
* The record for one operation: the one an identical earlier command left,
|
|
@@ -21622,10 +21736,7 @@ function app(command, result, context, style2) {
|
|
|
21622
21736
|
],
|
|
21623
21737
|
style2
|
|
21624
21738
|
),
|
|
21625
|
-
...notes(
|
|
21626
|
-
...result.validation.remote ? [] : result.validation.skipped,
|
|
21627
|
-
...result.limitations
|
|
21628
|
-
])
|
|
21739
|
+
...notes(result.limitations)
|
|
21629
21740
|
];
|
|
21630
21741
|
if ("definition" in result) {
|
|
21631
21742
|
const dryRun = "dryRun" in result;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maxceem/agw",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.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": [
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"directory": "cli"
|
|
26
26
|
},
|
|
27
27
|
"bugs": "https://github.com/maxceem/app-ai-gateway/issues",
|
|
28
|
-
"upgradeFrom": ["0.5.0", "0.4.0"],
|
|
28
|
+
"upgradeFrom": ["0.5.2", "0.5.1", "0.5.0", "0.4.0"],
|
|
29
29
|
"license": "Apache-2.0",
|
|
30
30
|
"bin": {
|
|
31
31
|
"agw": "dist/agw.mjs"
|