@maxceem/agw 0.2.1 → 0.3.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 +2130 -898
- package/package.json +2 -1
package/dist/agw.mjs
CHANGED
|
@@ -6,7 +6,7 @@ var __export = (target, all) => {
|
|
|
6
6
|
};
|
|
7
7
|
|
|
8
8
|
// <define:__AGW_MANIFEST__>
|
|
9
|
-
var define_AGW_MANIFEST_default = { version: "0.
|
|
9
|
+
var define_AGW_MANIFEST_default = { version: "0.3.0", wrangler: "4.96.0", upgradeFrom: ["0.3.0", "0.2.1", "0.2.0", "0.1.13", "0.1.12", "0.1.11", "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.3.0/gateway-0.3.0.tar.gz", sha256: "ead1f1da6ad0b2d2c35397fae2e58c640ffd2a5ba51a096b3a91facd877b6b61", manifest: "86b7799f57fe35aae0a372dd01e3cf32e3d6e7f8a3f3c8e6e1e207cf321c1c57" } };
|
|
10
10
|
|
|
11
11
|
// src/main.ts
|
|
12
12
|
import { Writable as Writable2 } from "node:stream";
|
|
@@ -1418,18 +1418,18 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
|
1418
1418
|
}
|
|
1419
1419
|
function toDotPath(_path) {
|
|
1420
1420
|
const segs = [];
|
|
1421
|
-
const path = _path.map((
|
|
1422
|
-
for (const
|
|
1423
|
-
if (typeof
|
|
1424
|
-
segs.push(`[${
|
|
1425
|
-
else if (typeof
|
|
1426
|
-
segs.push(`[${JSON.stringify(String(
|
|
1427
|
-
else if (/[^\w$]/.test(
|
|
1428
|
-
segs.push(`[${JSON.stringify(
|
|
1421
|
+
const path = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
1422
|
+
for (const seg of path) {
|
|
1423
|
+
if (typeof seg === "number")
|
|
1424
|
+
segs.push(`[${seg}]`);
|
|
1425
|
+
else if (typeof seg === "symbol")
|
|
1426
|
+
segs.push(`[${JSON.stringify(String(seg))}]`);
|
|
1427
|
+
else if (/[^\w$]/.test(seg))
|
|
1428
|
+
segs.push(`[${JSON.stringify(seg)}]`);
|
|
1429
1429
|
else {
|
|
1430
1430
|
if (segs.length)
|
|
1431
1431
|
segs.push(".");
|
|
1432
|
-
segs.push(
|
|
1432
|
+
segs.push(seg);
|
|
1433
1433
|
}
|
|
1434
1434
|
}
|
|
1435
1435
|
return segs.join("");
|
|
@@ -14525,12 +14525,64 @@ function date4(params) {
|
|
|
14525
14525
|
// ../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
14526
14526
|
config(en_default());
|
|
14527
14527
|
|
|
14528
|
-
// ../src/
|
|
14528
|
+
// ../src/shared/capabilities.ts
|
|
14529
|
+
var API_STYLES = [
|
|
14530
|
+
"responses",
|
|
14531
|
+
"chat_completions",
|
|
14532
|
+
"anthropic_messages",
|
|
14533
|
+
"gemini_native",
|
|
14534
|
+
"audio_transcription",
|
|
14535
|
+
/** A provider-native operation with no cross-provider contract of its own. */
|
|
14536
|
+
"other"
|
|
14537
|
+
];
|
|
14538
|
+
var API_STYLE_PATHS = {
|
|
14539
|
+
responses: "v1/responses",
|
|
14540
|
+
chat_completions: "v1/chat/completions",
|
|
14541
|
+
anthropic_messages: "v1/messages",
|
|
14542
|
+
gemini_native: "v1beta/models/{model}:generateContent",
|
|
14543
|
+
audio_transcription: "v1/audio/transcriptions"
|
|
14544
|
+
};
|
|
14545
|
+
var ENDPOINT_API_STYLES = ["responses", "transcription"];
|
|
14546
|
+
var OUTPUT_CLAMP_STYLES = [
|
|
14547
|
+
"responses",
|
|
14548
|
+
"chat_completions",
|
|
14549
|
+
"gemini_native",
|
|
14550
|
+
"anthropic",
|
|
14551
|
+
"none"
|
|
14552
|
+
];
|
|
14553
|
+
var VERCEL_API_STYLES = [
|
|
14554
|
+
"responses",
|
|
14555
|
+
"chat_completions",
|
|
14556
|
+
"anthropic_messages"
|
|
14557
|
+
];
|
|
14558
|
+
var VERCEL_ENDPOINT_STYLES = ["responses"];
|
|
14559
|
+
function vercelRoute(modelPrefix) {
|
|
14560
|
+
return {
|
|
14561
|
+
modelPrefix,
|
|
14562
|
+
apiStyles: VERCEL_API_STYLES,
|
|
14563
|
+
endpointStyles: VERCEL_ENDPOINT_STYLES
|
|
14564
|
+
};
|
|
14565
|
+
}
|
|
14566
|
+
var VERCEL_ROUTES = {
|
|
14567
|
+
openai: vercelRoute("openai/"),
|
|
14568
|
+
anthropic: vercelRoute("anthropic/"),
|
|
14569
|
+
// Google AI Studio's models are Google's, and Vercel namespaces them by
|
|
14570
|
+
// author: `gemini-2.5-flash` goes out as `google/gemini-2.5-flash`.
|
|
14571
|
+
gemini: vercelRoute("google/"),
|
|
14572
|
+
xai: vercelRoute("spacexai/"),
|
|
14573
|
+
perplexity: vercelRoute("perplexity/"),
|
|
14574
|
+
deepseek: vercelRoute("deepseek/"),
|
|
14575
|
+
// Moonshot's own IDs are the Kimi ones, and Vercel publishes them verbatim
|
|
14576
|
+
// under the lab's name rather than the product's: `moonshotai/kimi-k3`.
|
|
14577
|
+
moonshot: vercelRoute("moonshotai/")
|
|
14578
|
+
};
|
|
14579
|
+
|
|
14580
|
+
// ../src/shared/records.ts
|
|
14529
14581
|
function asRecord(value) {
|
|
14530
14582
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
14531
14583
|
}
|
|
14532
14584
|
|
|
14533
|
-
// ../src/
|
|
14585
|
+
// ../src/shared/cost-report.ts
|
|
14534
14586
|
function finiteNumber(value) {
|
|
14535
14587
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
14536
14588
|
}
|
|
@@ -14595,133 +14647,29 @@ var OPENROUTER_COST_REPORT = {
|
|
|
14595
14647
|
}
|
|
14596
14648
|
};
|
|
14597
14649
|
|
|
14598
|
-
// ../src/shared/
|
|
14599
|
-
var
|
|
14600
|
-
openai: { header: "authorization", scheme: "Bearer " },
|
|
14601
|
-
anthropic: { header: "x-api-key" },
|
|
14602
|
-
xai: { header: "authorization", scheme: "Bearer " },
|
|
14603
|
-
gemini: { header: "x-goog-api-key" },
|
|
14604
|
-
perplexity: { header: "authorization", scheme: "Bearer " },
|
|
14605
|
-
deepseek: { header: "authorization", scheme: "Bearer " },
|
|
14606
|
-
groq: { header: "authorization", scheme: "Bearer " },
|
|
14607
|
-
mistral: { header: "authorization", scheme: "Bearer " },
|
|
14608
|
-
together: { header: "authorization", scheme: "Bearer " },
|
|
14609
|
-
fireworks: { header: "authorization", scheme: "Bearer " },
|
|
14610
|
-
cerebras: { header: "authorization", scheme: "Bearer " },
|
|
14611
|
-
moonshot: { header: "authorization", scheme: "Bearer " },
|
|
14612
|
-
huggingface: { header: "authorization", scheme: "Bearer " },
|
|
14613
|
-
baseten: { header: "authorization", scheme: "Bearer " },
|
|
14614
|
-
bytedance: { header: "authorization", scheme: "Bearer " },
|
|
14615
|
-
openrouter: { header: "authorization", scheme: "Bearer " }
|
|
14616
|
-
};
|
|
14617
|
-
var PROVIDER_CREDENTIAL_HEADERS = [
|
|
14618
|
-
...new Set(Object.values(PROVIDER_AUTH).map((auth) => auth.header.toLowerCase()))
|
|
14619
|
-
];
|
|
14620
|
-
|
|
14621
|
-
// ../src/shared/capabilities.ts
|
|
14622
|
-
var PROVIDER_TYPES = [
|
|
14623
|
-
"openai",
|
|
14624
|
-
"anthropic",
|
|
14625
|
-
"xai",
|
|
14626
|
-
"gemini",
|
|
14627
|
-
"perplexity",
|
|
14628
|
-
"deepseek",
|
|
14629
|
-
"groq",
|
|
14630
|
-
"mistral",
|
|
14631
|
-
"together",
|
|
14632
|
-
"fireworks",
|
|
14633
|
-
"cerebras",
|
|
14634
|
-
"moonshot",
|
|
14635
|
-
"huggingface",
|
|
14636
|
-
"baseten",
|
|
14637
|
-
"bytedance",
|
|
14638
|
-
"openrouter"
|
|
14639
|
-
];
|
|
14640
|
-
var API_STYLES = [
|
|
14641
|
-
"responses",
|
|
14642
|
-
"chat_completions",
|
|
14643
|
-
"anthropic_messages",
|
|
14644
|
-
"gemini_native",
|
|
14645
|
-
"audio_transcription",
|
|
14646
|
-
/** A provider-native operation with no cross-provider contract of its own. */
|
|
14647
|
-
"other"
|
|
14648
|
-
];
|
|
14649
|
-
var PASSTHROUGH_CAPABILITY = {
|
|
14650
|
-
apiStyles: API_STYLES,
|
|
14651
|
-
endpointStyles: []
|
|
14652
|
-
};
|
|
14653
|
-
var PROVIDER_CAPABILITY_EXCEPTIONS = {
|
|
14654
|
-
// The two types whose Responses and transcription request shapes the gateway
|
|
14655
|
-
// composes itself for named endpoints.
|
|
14656
|
-
openai: { apiStyles: API_STYLES, endpointStyles: ["responses", "transcription"] },
|
|
14657
|
-
xai: { apiStyles: API_STYLES, endpointStyles: ["responses", "transcription"] },
|
|
14658
|
-
// The one narrowed API surface, and it is a metering constraint rather than a
|
|
14659
|
-
// missing one: OpenRouter also serves `/responses` and `/messages`, but only
|
|
14660
|
-
// its chat-completions response carries `usage.cost`, and its slugs have no
|
|
14661
|
-
// local price. Any other style would proxy traffic nothing could bill —
|
|
14662
|
-
// exactly the silent $0 the fail-closed gate exists to prevent — so it is
|
|
14663
|
-
// refused at the edge instead.
|
|
14664
|
-
openrouter: { apiStyles: ["chat_completions"], endpointStyles: [] }
|
|
14665
|
-
};
|
|
14666
|
-
function providerCapability(provider) {
|
|
14667
|
-
const exceptions = PROVIDER_CAPABILITY_EXCEPTIONS;
|
|
14668
|
-
return exceptions[provider] ?? PASSTHROUGH_CAPABILITY;
|
|
14669
|
-
}
|
|
14670
|
-
var ENDPOINT_PROVIDER_TYPES = PROVIDER_TYPES.filter(
|
|
14671
|
-
(type) => providerCapability(type).endpointStyles.length > 0
|
|
14672
|
-
);
|
|
14673
|
-
var VERCEL_API_STYLES = [
|
|
14674
|
-
"responses",
|
|
14675
|
-
"chat_completions",
|
|
14676
|
-
"anthropic_messages"
|
|
14677
|
-
];
|
|
14678
|
-
var VERCEL_ENDPOINT_STYLES = ["responses"];
|
|
14679
|
-
function vercelRoute(modelPrefix) {
|
|
14680
|
-
return {
|
|
14681
|
-
modelPrefix,
|
|
14682
|
-
apiStyles: VERCEL_API_STYLES,
|
|
14683
|
-
endpointStyles: VERCEL_ENDPOINT_STYLES
|
|
14684
|
-
};
|
|
14685
|
-
}
|
|
14686
|
-
var VERCEL_ROUTES = {
|
|
14687
|
-
openai: vercelRoute("openai/"),
|
|
14688
|
-
anthropic: vercelRoute("anthropic/"),
|
|
14689
|
-
// Google AI Studio's models are Google's, and Vercel namespaces them by
|
|
14690
|
-
// author: `gemini-2.5-flash` goes out as `google/gemini-2.5-flash`.
|
|
14691
|
-
gemini: vercelRoute("google/"),
|
|
14692
|
-
xai: vercelRoute("spacexai/"),
|
|
14693
|
-
perplexity: vercelRoute("perplexity/"),
|
|
14694
|
-
deepseek: vercelRoute("deepseek/"),
|
|
14695
|
-
// Moonshot's own IDs are the Kimi ones, and Vercel publishes them verbatim
|
|
14696
|
-
// under the lab's name rather than the product's: `moonshotai/kimi-k3`.
|
|
14697
|
-
moonshot: vercelRoute("moonshotai/")
|
|
14698
|
-
};
|
|
14699
|
-
|
|
14700
|
-
// ../src/shared/app-config.ts
|
|
14701
|
-
var ISSUER_PROVIDERS = ["firebase", "supabase", "auth0", "clerk", "custom"];
|
|
14702
|
-
var ENTITLEMENT_CHECKS = ["revenuecat", "custom"];
|
|
14703
|
-
var ENDPOINT_SLUG = /^[a-z0-9-]{1,64}$/;
|
|
14704
|
-
var PROVIDER_SLUG_PATTERN = /^[a-z0-9][a-z0-9-]{0,62}$/;
|
|
14705
|
-
var HTTP_FIELD_NAME = /^[A-Za-z0-9!#$%&'*+.^_`|~-]+$/;
|
|
14706
|
-
var RESERVED_END_USER_HEADERS = /* @__PURE__ */ new Set([
|
|
14707
|
-
"authorization",
|
|
14708
|
-
...PROVIDER_CREDENTIAL_HEADERS,
|
|
14709
|
-
"x-app-version",
|
|
14710
|
-
"content-type",
|
|
14711
|
-
"content-length",
|
|
14712
|
-
"host"
|
|
14713
|
-
]);
|
|
14714
|
-
|
|
14715
|
-
// ../src/core/providers.ts
|
|
14716
|
-
var PROVIDER_REGISTRY = {
|
|
14650
|
+
// ../src/shared/providers.ts
|
|
14651
|
+
var PROVIDER_DESCRIPTORS = {
|
|
14717
14652
|
openai: {
|
|
14718
14653
|
directBaseUrl: "https://api.openai.com/",
|
|
14719
|
-
auth:
|
|
14654
|
+
auth: { header: "authorization", scheme: "Bearer " },
|
|
14655
|
+
nativeClampStyle: "responses",
|
|
14656
|
+
probePath: "v1/models",
|
|
14657
|
+
examplePath: "v1/responses",
|
|
14658
|
+
// OpenAI's chat-completions surface deprecated `max_tokens` in favour of
|
|
14659
|
+
// `max_completion_tokens`; every other OpenAI-compatible service still
|
|
14660
|
+
// reads the older field.
|
|
14661
|
+
chatCompletionsCapField: "max_completion_tokens",
|
|
14662
|
+
// The two types whose Responses and transcription request shapes the
|
|
14663
|
+
// gateway composes itself for named endpoints.
|
|
14664
|
+
endpointPaths: { responses: "v1/responses", transcription: "v1/audio/transcriptions" },
|
|
14720
14665
|
modelAuthor: "OpenAI"
|
|
14721
14666
|
},
|
|
14722
14667
|
anthropic: {
|
|
14723
14668
|
directBaseUrl: "https://api.anthropic.com/",
|
|
14724
|
-
auth:
|
|
14669
|
+
auth: { header: "x-api-key" },
|
|
14670
|
+
nativeClampStyle: "anthropic",
|
|
14671
|
+
probePath: "v1/models",
|
|
14672
|
+
examplePath: "v1/messages",
|
|
14725
14673
|
modelAuthor: "Anthropic",
|
|
14726
14674
|
// Anthropic refuses any request without a version header, probe included —
|
|
14727
14675
|
// and a probe has no client to send one for it. Deliberately not a
|
|
@@ -14732,17 +14680,31 @@ var PROVIDER_REGISTRY = {
|
|
|
14732
14680
|
},
|
|
14733
14681
|
xai: {
|
|
14734
14682
|
directBaseUrl: "https://api.x.ai/",
|
|
14735
|
-
auth:
|
|
14683
|
+
auth: { header: "authorization", scheme: "Bearer " },
|
|
14684
|
+
nativeClampStyle: "responses",
|
|
14685
|
+
probePath: "v1/models",
|
|
14686
|
+
// Native provider paths: xAI transcribes at `v1/stt`, where OpenAI serves
|
|
14687
|
+
// `v1/audio/transcriptions`.
|
|
14688
|
+
endpointPaths: { responses: "v1/responses", transcription: "v1/stt" },
|
|
14736
14689
|
modelAuthor: "xAI"
|
|
14737
14690
|
},
|
|
14738
14691
|
gemini: {
|
|
14739
14692
|
directBaseUrl: "https://generativelanguage.googleapis.com/",
|
|
14740
|
-
auth:
|
|
14693
|
+
auth: { header: "x-goog-api-key" },
|
|
14694
|
+
nativeClampStyle: "gemini_native",
|
|
14695
|
+
probePath: "v1beta/models",
|
|
14696
|
+
// Native Gemini generation requests carry the model in the URL rather than
|
|
14697
|
+
// in the JSON body, so there is no example path without a model and a
|
|
14698
|
+
// request on one has its model captured from the path.
|
|
14699
|
+
modelInPath: true,
|
|
14741
14700
|
modelAuthor: "Google"
|
|
14742
14701
|
},
|
|
14743
14702
|
perplexity: {
|
|
14744
14703
|
directBaseUrl: "https://api.perplexity.ai/",
|
|
14745
|
-
auth:
|
|
14704
|
+
auth: { header: "authorization", scheme: "Bearer " },
|
|
14705
|
+
nativeClampStyle: "responses",
|
|
14706
|
+
// No `probePath`: Perplexity has no unmetered authenticated endpoint.
|
|
14707
|
+
examplePath: "chat/completions",
|
|
14746
14708
|
modelAuthor: "Perplexity"
|
|
14747
14709
|
},
|
|
14748
14710
|
// OpenAI-compatible chat-completions services. `modelAuthor` is set only
|
|
@@ -14753,41 +14715,62 @@ var PROVIDER_REGISTRY = {
|
|
|
14753
14715
|
// No `v1` segment: DeepSeek documents the bare origin as its OpenAI base
|
|
14754
14716
|
// URL, and `https://api.deepseek.com/anthropic` for the Anthropic format.
|
|
14755
14717
|
directBaseUrl: "https://api.deepseek.com/",
|
|
14756
|
-
auth:
|
|
14718
|
+
auth: { header: "authorization", scheme: "Bearer " },
|
|
14719
|
+
nativeClampStyle: "chat_completions",
|
|
14720
|
+
// DeepSeek's OpenAI base URL carries no `v1` segment.
|
|
14721
|
+
probePath: "models",
|
|
14722
|
+
examplePath: "chat/completions",
|
|
14757
14723
|
modelAuthor: "DeepSeek"
|
|
14758
14724
|
},
|
|
14759
14725
|
groq: {
|
|
14760
14726
|
// Groq's OpenAI-compatible surface lives under `openai/v1/`, so the client
|
|
14761
14727
|
// path is `openai/v1/chat/completions` rather than `v1/chat/completions`.
|
|
14762
14728
|
directBaseUrl: "https://api.groq.com/",
|
|
14763
|
-
auth:
|
|
14729
|
+
auth: { header: "authorization", scheme: "Bearer " },
|
|
14730
|
+
nativeClampStyle: "chat_completions",
|
|
14731
|
+
// Groq's OpenAI-compatible surface is namespaced under `openai/`.
|
|
14732
|
+
probePath: "openai/v1/models",
|
|
14733
|
+
examplePath: "openai/v1/chat/completions"
|
|
14764
14734
|
},
|
|
14765
14735
|
mistral: {
|
|
14766
14736
|
directBaseUrl: "https://api.mistral.ai/",
|
|
14767
|
-
auth:
|
|
14737
|
+
auth: { header: "authorization", scheme: "Bearer " },
|
|
14738
|
+
nativeClampStyle: "chat_completions",
|
|
14739
|
+
probePath: "v1/models",
|
|
14768
14740
|
modelAuthor: "Mistral"
|
|
14769
14741
|
},
|
|
14770
14742
|
together: {
|
|
14771
14743
|
// `api.together.ai`, not the `.xyz` host older SDKs default to: the current
|
|
14772
14744
|
// OpenAI-compatibility guide names this one and warns against the other.
|
|
14773
14745
|
directBaseUrl: "https://api.together.ai/",
|
|
14774
|
-
auth:
|
|
14746
|
+
auth: { header: "authorization", scheme: "Bearer " },
|
|
14747
|
+
nativeClampStyle: "chat_completions",
|
|
14748
|
+
probePath: "v1/models"
|
|
14775
14749
|
},
|
|
14776
14750
|
fireworks: {
|
|
14777
14751
|
// The inference plane is `/inference/v1`; `/v1` on the same host is the
|
|
14778
14752
|
// control plane, so the client path is `inference/v1/chat/completions`.
|
|
14779
14753
|
directBaseUrl: "https://api.fireworks.ai/",
|
|
14780
|
-
auth:
|
|
14754
|
+
auth: { header: "authorization", scheme: "Bearer " },
|
|
14755
|
+
nativeClampStyle: "chat_completions",
|
|
14756
|
+
// No `probePath`: its list-models call is `v1/accounts/{account}/models`,
|
|
14757
|
+
// and the account id cannot be derived from the key. Nothing under
|
|
14758
|
+
// `inference/v1/` is documented as a GET.
|
|
14759
|
+
examplePath: "inference/v1/chat/completions"
|
|
14781
14760
|
},
|
|
14782
14761
|
cerebras: {
|
|
14783
14762
|
directBaseUrl: "https://api.cerebras.ai/",
|
|
14784
|
-
auth:
|
|
14763
|
+
auth: { header: "authorization", scheme: "Bearer " },
|
|
14764
|
+
nativeClampStyle: "chat_completions",
|
|
14765
|
+
probePath: "v1/models"
|
|
14785
14766
|
},
|
|
14786
14767
|
moonshot: {
|
|
14787
14768
|
// The international host. `api.moonshot.cn` is the separate China platform
|
|
14788
14769
|
// and is not reachable with a key issued for this one.
|
|
14789
14770
|
directBaseUrl: "https://api.moonshot.ai/",
|
|
14790
|
-
auth:
|
|
14771
|
+
auth: { header: "authorization", scheme: "Bearer " },
|
|
14772
|
+
nativeClampStyle: "chat_completions",
|
|
14773
|
+
probePath: "v1/models",
|
|
14791
14774
|
modelAuthor: "Moonshot AI"
|
|
14792
14775
|
},
|
|
14793
14776
|
huggingface: {
|
|
@@ -14797,22 +14780,34 @@ var PROVIDER_REGISTRY = {
|
|
|
14797
14780
|
// order of magnitude more on some of them than others. It ships with no
|
|
14798
14781
|
// catalog section for that reason; see `catalogPrice` in usage.ts.
|
|
14799
14782
|
directBaseUrl: "https://router.huggingface.co/",
|
|
14800
|
-
auth:
|
|
14783
|
+
auth: { header: "authorization", scheme: "Bearer " },
|
|
14784
|
+
nativeClampStyle: "chat_completions"
|
|
14785
|
+
// No `probePath`: `router.huggingface.co/v1/models` is public — it answers
|
|
14786
|
+
// 200 to a garbage token, so probing it would validate every key. The
|
|
14787
|
+
// endpoint that does check a token lives on a different origin
|
|
14788
|
+
// (`huggingface.co`), which a path under this base URL cannot express.
|
|
14801
14789
|
},
|
|
14802
14790
|
baseten: {
|
|
14803
14791
|
// The Model APIs inference host. `api.baseten.co` is the management plane
|
|
14804
14792
|
// for dedicated deployments and answers to different paths entirely.
|
|
14805
14793
|
directBaseUrl: "https://inference.baseten.co/",
|
|
14806
|
-
auth:
|
|
14794
|
+
auth: { header: "authorization", scheme: "Bearer " },
|
|
14795
|
+
nativeClampStyle: "chat_completions",
|
|
14796
|
+
probePath: "v1/models"
|
|
14807
14797
|
},
|
|
14808
14798
|
bytedance: {
|
|
14809
14799
|
// BytePlus ModelArk, the international edition: `/api/v3` is its version
|
|
14810
14800
|
// segment, so client paths carry no `v1`. `ark.cn-beijing.volces.com` is
|
|
14811
14801
|
// the separate China platform, and `ark.eu-west.bytepluses.com` is a second
|
|
14812
14802
|
// region — keys and catalogs are region-isolated, so reaching either needs
|
|
14813
|
-
// the per-row base URL override
|
|
14803
|
+
// the per-row base URL override.
|
|
14814
14804
|
directBaseUrl: "https://ark.ap-southeast.bytepluses.com/api/v3/",
|
|
14815
|
-
auth:
|
|
14805
|
+
auth: { header: "authorization", scheme: "Bearer " },
|
|
14806
|
+
nativeClampStyle: "chat_completions",
|
|
14807
|
+
// No `probePath`: ModelArk publishes no list-models call, and its own SDK
|
|
14808
|
+
// has no models resource. It also authenticates before it routes, so every
|
|
14809
|
+
// path answers the same 401 and a probe would prove nothing about the key.
|
|
14810
|
+
examplePath: "chat/completions"
|
|
14816
14811
|
},
|
|
14817
14812
|
openrouter: {
|
|
14818
14813
|
// An aggregator treated as a provider type: it is the counterparty that
|
|
@@ -14821,19 +14816,57 @@ var PROVIDER_REGISTRY = {
|
|
|
14821
14816
|
// `/api/v1` is its documented server URL, so the client path is
|
|
14822
14817
|
// `v1/chat/completions` under this origin.
|
|
14823
14818
|
directBaseUrl: "https://openrouter.ai/api/",
|
|
14824
|
-
auth:
|
|
14819
|
+
auth: { header: "authorization", scheme: "Bearer " },
|
|
14820
|
+
nativeClampStyle: "chat_completions",
|
|
14821
|
+
// OpenRouter's key-status call, not its model list: `v1/models` is public
|
|
14822
|
+
// and answers 200 to any token, exactly the trap `huggingface` has no
|
|
14823
|
+
// probe for. `v1/key` answers 401 to a key that does not exist.
|
|
14824
|
+
probePath: "v1/key",
|
|
14825
|
+
// The one narrowed API surface, and it is a metering constraint rather than
|
|
14826
|
+
// a missing one: OpenRouter also serves `/responses` and `/messages`, but
|
|
14827
|
+
// only its chat-completions response carries `usage.cost`, and its slugs
|
|
14828
|
+
// have no local price. Any other style would proxy traffic nothing could
|
|
14829
|
+
// bill — exactly the silent $0 the fail-closed gate exists to prevent — so
|
|
14830
|
+
// it is refused at the edge instead.
|
|
14831
|
+
apiStyles: ["chat_completions"],
|
|
14825
14832
|
// Every chat-completions response carries `usage.cost`: what OpenRouter
|
|
14826
14833
|
// actually charged for that request, which beats any local estimate of a
|
|
14827
14834
|
// catalog this deployment does not track. It is also the only way to bill
|
|
14828
14835
|
// the type at all, since no static price list covers 400 models across
|
|
14829
14836
|
// every lab. The declaration owns the parsing and the header that asks for
|
|
14830
|
-
// the routing metadata — see `src/
|
|
14837
|
+
// the routing metadata — see `src/shared/cost-report.ts`.
|
|
14831
14838
|
costReport: OPENROUTER_COST_REPORT,
|
|
14832
14839
|
// No `modelAuthor`: an aggregator serves everyone's models, and the slug
|
|
14833
14840
|
// namespace answers per model instead.
|
|
14834
14841
|
authorNamespacedModels: true
|
|
14835
14842
|
}
|
|
14836
14843
|
};
|
|
14844
|
+
var PROVIDER_TYPES = Object.keys(PROVIDER_DESCRIPTORS);
|
|
14845
|
+
function providerDescriptor(type) {
|
|
14846
|
+
return PROVIDER_DESCRIPTORS[type];
|
|
14847
|
+
}
|
|
14848
|
+
function isProviderType(value) {
|
|
14849
|
+
return typeof value === "string" && Object.hasOwn(PROVIDER_DESCRIPTORS, value);
|
|
14850
|
+
}
|
|
14851
|
+
var PROVIDER_CREDENTIAL_HEADERS = [
|
|
14852
|
+
...new Set(
|
|
14853
|
+
PROVIDER_TYPES.map((type) => providerDescriptor(type).auth.header.toLowerCase())
|
|
14854
|
+
)
|
|
14855
|
+
];
|
|
14856
|
+
function providerCapability(provider) {
|
|
14857
|
+
const descriptor = providerDescriptor(provider);
|
|
14858
|
+
return {
|
|
14859
|
+
apiStyles: descriptor.apiStyles ?? API_STYLES,
|
|
14860
|
+
endpointStyles: Object.keys(descriptor.endpointPaths ?? {})
|
|
14861
|
+
};
|
|
14862
|
+
}
|
|
14863
|
+
var ENDPOINT_PROVIDER_TYPES = PROVIDER_TYPES.filter(
|
|
14864
|
+
(type) => providerCapability(type).endpointStyles.length > 0
|
|
14865
|
+
);
|
|
14866
|
+
function reportsCost(type) {
|
|
14867
|
+
return isProviderType(type) && providerDescriptor(type).costReport !== void 0;
|
|
14868
|
+
}
|
|
14869
|
+
var COST_REPORTING_PROVIDER_TYPES = PROVIDER_TYPES.filter(reportsCost);
|
|
14837
14870
|
|
|
14838
14871
|
// ../src/core/origin-guard.ts
|
|
14839
14872
|
var MAX_BASE_URL_LENGTH = 200;
|
|
@@ -14943,18 +14976,61 @@ function checkOperatorBaseUrl(input) {
|
|
|
14943
14976
|
}
|
|
14944
14977
|
|
|
14945
14978
|
// ../src/contracts/schemas.ts
|
|
14979
|
+
var ISSUER_PROVIDERS = ["firebase", "supabase", "auth0", "clerk", "custom"];
|
|
14980
|
+
var ENTITLEMENT_CHECKS = ["revenuecat", "custom"];
|
|
14981
|
+
var APP_ATTEST_ENVIRONMENTS = ["production", "development"];
|
|
14982
|
+
var ENDPOINT_SLUG = /^[a-z0-9-]{1,64}$/;
|
|
14983
|
+
var PROVIDER_SLUG_PATTERN = /^[a-z0-9][a-z0-9-]{0,62}$/;
|
|
14984
|
+
var HTTP_FIELD_NAME = /^[A-Za-z0-9!#$%&'*+.^_`|~-]+$/;
|
|
14985
|
+
var MONTH_PATTERN = /^\d{4}-(?:0[1-9]|1[0-2])$/;
|
|
14986
|
+
var MONTH_FORMAT_MESSAGE = "month must use YYYY-MM format";
|
|
14987
|
+
var MonthSchema = external_exports.string().regex(MONTH_PATTERN, { error: MONTH_FORMAT_MESSAGE });
|
|
14988
|
+
var APPLE_TEAM_ID = /^[A-Z0-9]{10}$/;
|
|
14989
|
+
var APPLE_BUNDLE_ID = /^[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+$/;
|
|
14990
|
+
var RESERVED_OBJECT_KEYS = /* @__PURE__ */ new Set([
|
|
14991
|
+
"__proto__",
|
|
14992
|
+
"constructor",
|
|
14993
|
+
"prototype"
|
|
14994
|
+
]);
|
|
14995
|
+
var RESERVED_KEY_ERROR = "key cannot be __proto__, constructor or prototype";
|
|
14996
|
+
var safeKey = (schema) => schema.refine((key) => !RESERVED_OBJECT_KEYS.has(key), { error: RESERVED_KEY_ERROR });
|
|
14946
14997
|
var ProviderTypeSchema = external_exports.enum(PROVIDER_TYPES);
|
|
14947
14998
|
var NullableRequestLimit = external_exports.number().int().positive().nullable();
|
|
14948
|
-
var NullableSpendLimit = external_exports.number().nonnegative().
|
|
14949
|
-
|
|
14999
|
+
var NullableSpendLimit = external_exports.number().nonnegative().refine((usd) => Number.isSafeInteger(Math.round(usd * 1e6)), {
|
|
15000
|
+
error: "monthly_usd is too large"
|
|
15001
|
+
}).nullable();
|
|
15002
|
+
var SlugSchema = safeKey(
|
|
15003
|
+
external_exports.string().regex(PROVIDER_SLUG_PATTERN).meta({
|
|
15004
|
+
description: "Lowercase letters, digits and hyphens, starting with a letter or digit. `constructor` and `prototype` are reserved."
|
|
15005
|
+
})
|
|
15006
|
+
);
|
|
15007
|
+
var StoredSlugSchema = external_exports.string().regex(PROVIDER_SLUG_PATTERN);
|
|
14950
15008
|
var ClaimRequirementSchema = external_exports.object({
|
|
14951
|
-
path: external_exports.string(),
|
|
15009
|
+
path: external_exports.string().min(1),
|
|
14952
15010
|
contains: external_exports.union([external_exports.string(), external_exports.array(external_exports.string()).min(1)]).optional(),
|
|
14953
15011
|
equals: external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean()]).optional()
|
|
15012
|
+
}).strict().superRefine((value, context) => {
|
|
15013
|
+
if (value.contains === void 0 === (value.equals === void 0)) {
|
|
15014
|
+
context.addIssue({
|
|
15015
|
+
code: "custom",
|
|
15016
|
+
message: "Claim requirements need exactly one of contains or equals"
|
|
15017
|
+
});
|
|
15018
|
+
}
|
|
14954
15019
|
});
|
|
14955
|
-
var IssuerClaimValuesSchema = external_exports.union([external_exports.string().min(1), external_exports.array(external_exports.string().min(1)).min(1)]);
|
|
15020
|
+
var IssuerClaimValuesSchema = external_exports.union([external_exports.string().min(1), external_exports.array(external_exports.string().min(1)).min(1)]).transform((value) => typeof value === "string" ? [value] : value);
|
|
14956
15021
|
var IssuerAuthenticationSchema = external_exports.object({
|
|
14957
|
-
|
|
15022
|
+
/**
|
|
15023
|
+
* HTTPS only, and stored in the canonical form `URL` prints: the gateway
|
|
15024
|
+
* fetches this on the authentication path, so a plaintext origin would put
|
|
15025
|
+
* the keys that verify every token on the wire for anyone to replace.
|
|
15026
|
+
*/
|
|
15027
|
+
jwks_url: external_exports.url({ protocol: /^https$/, error: "Issuer JWKS URLs must be valid and use HTTPS" }).overwrite((value) => {
|
|
15028
|
+
try {
|
|
15029
|
+
return new URL(value).toString();
|
|
15030
|
+
} catch {
|
|
15031
|
+
return value;
|
|
15032
|
+
}
|
|
15033
|
+
}).meta({ description: "An https URL. Stored in the canonical form `URL` prints." }),
|
|
14958
15034
|
/**
|
|
14959
15035
|
* Required: several issuers publish one JWKS for every customer — Firebase
|
|
14960
15036
|
* signs all projects with the same keys — so without `iss` and `aud` a token
|
|
@@ -14962,8 +15038,9 @@ var IssuerAuthenticationSchema = external_exports.object({
|
|
|
14962
15038
|
*/
|
|
14963
15039
|
issuer: IssuerClaimValuesSchema,
|
|
14964
15040
|
audience: IssuerClaimValuesSchema,
|
|
14965
|
-
user_id_claim: external_exports.string(),
|
|
14966
|
-
|
|
15041
|
+
user_id_claim: external_exports.string().min(1),
|
|
15042
|
+
/** Lowercased, because header lookups ignore case. */
|
|
15043
|
+
token_header: external_exports.string().min(1).toLowerCase().optional(),
|
|
14967
15044
|
required_claims: external_exports.array(ClaimRequirementSchema),
|
|
14968
15045
|
max_token_lifetime_seconds: external_exports.number().int().positive(),
|
|
14969
15046
|
/**
|
|
@@ -14971,11 +15048,30 @@ var IssuerAuthenticationSchema = external_exports.object({
|
|
|
14971
15048
|
* which kind of paid-user check `required_claims` implements. Bookkeeping for
|
|
14972
15049
|
* the console, so it can show "Firebase, project X" and reopen the same
|
|
14973
15050
|
* form: the gateway verifies tokens from the fields above and reads neither.
|
|
15051
|
+
*
|
|
15052
|
+
* Refused rather than dropped when this build does not know the name: this
|
|
15053
|
+
* schema is checked on the way in, so a name it cannot interpret never
|
|
15054
|
+
* reaches storage in the first place.
|
|
14974
15055
|
*/
|
|
14975
15056
|
provider: external_exports.enum(ISSUER_PROVIDERS).optional(),
|
|
14976
15057
|
entitlement: external_exports.enum(ENTITLEMENT_CHECKS).optional()
|
|
14977
15058
|
}).strict();
|
|
14978
|
-
var
|
|
15059
|
+
var RESERVED_END_USER_HEADERS = /* @__PURE__ */ new Set([
|
|
15060
|
+
"authorization",
|
|
15061
|
+
...PROVIDER_CREDENTIAL_HEADERS,
|
|
15062
|
+
"x-app-version",
|
|
15063
|
+
"content-type",
|
|
15064
|
+
"content-length",
|
|
15065
|
+
"host"
|
|
15066
|
+
]);
|
|
15067
|
+
var EndUserHeaderSchema = external_exports.string().trim().toLowerCase().min(1).max(64).regex(HTTP_FIELD_NAME, { error: "must be a valid HTTP header name" }).superRefine((header, context) => {
|
|
15068
|
+
if (RESERVED_END_USER_HEADERS.has(header)) {
|
|
15069
|
+
context.addIssue({
|
|
15070
|
+
code: "custom",
|
|
15071
|
+
message: `cannot be ${header}: the gateway already uses that header`
|
|
15072
|
+
});
|
|
15073
|
+
}
|
|
15074
|
+
});
|
|
14979
15075
|
var HeaderEndUserSchema = external_exports.object({
|
|
14980
15076
|
source: external_exports.literal("header"),
|
|
14981
15077
|
header: EndUserHeaderSchema
|
|
@@ -14995,23 +15091,29 @@ var AppAttestEndUserSchema = external_exports.discriminatedUnion("source", [
|
|
|
14995
15091
|
IssuerEndUserSchema,
|
|
14996
15092
|
AppInstallEndUserSchema
|
|
14997
15093
|
], { error: "authentication.end_user.source must be one of issuer, app_install" });
|
|
15094
|
+
var AppleAppIdentitySchema = external_exports.object({
|
|
15095
|
+
team_id: external_exports.string().regex(APPLE_TEAM_ID, {
|
|
15096
|
+
error: "team_id must contain ten uppercase letters or digits"
|
|
15097
|
+
}),
|
|
15098
|
+
bundle_id: external_exports.string().regex(APPLE_BUNDLE_ID, {
|
|
15099
|
+
error: "bundle_id must be a reverse DNS identifier"
|
|
15100
|
+
})
|
|
15101
|
+
});
|
|
14998
15102
|
var AppleAppAttestAuthenticationSchema = external_exports.object({
|
|
14999
15103
|
type: external_exports.literal("apple_app_attest"),
|
|
15000
15104
|
end_user: AppAttestEndUserSchema,
|
|
15001
|
-
app_attest:
|
|
15002
|
-
team_id: external_exports.string(),
|
|
15003
|
-
bundle_id: external_exports.string(),
|
|
15105
|
+
app_attest: AppleAppIdentitySchema.extend({
|
|
15004
15106
|
/**
|
|
15005
15107
|
* Which of Apple's two App Attest environments this application accepts.
|
|
15006
|
-
*
|
|
15108
|
+
* Defaulted to `["production"]` alone, because a development-signed build
|
|
15007
15109
|
* stamps a different aaguid and is debuggable on any device carrying the
|
|
15008
15110
|
* team's provisioning profile. Accepting one is therefore a deliberate
|
|
15009
15111
|
* per-application opt-in, and belongs on a development bundle id rather
|
|
15010
15112
|
* than the one shipped to the App Store.
|
|
15011
15113
|
*/
|
|
15012
|
-
environments: external_exports.array(external_exports.enum(
|
|
15114
|
+
environments: external_exports.array(external_exports.enum(APP_ATTEST_ENVIRONMENTS)).min(1, { error: "environments must name at least one environment" }).refine((values) => new Set(values).size === values.length, {
|
|
15013
15115
|
error: "environments cannot repeat a value"
|
|
15014
|
-
}).
|
|
15116
|
+
}).prefault(["production"])
|
|
15015
15117
|
}).strict()
|
|
15016
15118
|
}).strict();
|
|
15017
15119
|
var ApiKeyAuthenticationSchema = external_exports.object({
|
|
@@ -15024,59 +15126,96 @@ var ApiKeyAuthenticationSchema = external_exports.object({
|
|
|
15024
15126
|
*/
|
|
15025
15127
|
end_user: ApiKeyEndUserSchema.optional()
|
|
15026
15128
|
}).strict();
|
|
15129
|
+
var AllowedPathSchema = external_exports.union([
|
|
15130
|
+
external_exports.string().min(1),
|
|
15131
|
+
external_exports.object({
|
|
15132
|
+
path: external_exports.string().min(1),
|
|
15133
|
+
fixed_model: external_exports.string().min(1).optional(),
|
|
15134
|
+
clamp: external_exports.enum(OUTPUT_CLAMP_STYLES).optional()
|
|
15135
|
+
}).strict()
|
|
15136
|
+
]);
|
|
15027
15137
|
var ProviderPolicySchema = external_exports.object({
|
|
15028
|
-
allowed_paths: external_exports.array(
|
|
15029
|
-
|
|
15030
|
-
external_exports.object({
|
|
15031
|
-
path: external_exports.string(),
|
|
15032
|
-
fixed_model: external_exports.string().optional(),
|
|
15033
|
-
clamp: external_exports.enum(["responses", "chat_completions", "gemini_native", "anthropic", "none"]).optional()
|
|
15034
|
-
})
|
|
15035
|
-
])),
|
|
15036
|
-
allowed_models: external_exports.array(external_exports.string()),
|
|
15138
|
+
allowed_paths: external_exports.array(AllowedPathSchema),
|
|
15139
|
+
allowed_models: external_exports.array(external_exports.string().min(1)),
|
|
15037
15140
|
max_output_tokens: external_exports.number().int().positive().optional()
|
|
15038
|
-
});
|
|
15141
|
+
}).strict();
|
|
15039
15142
|
var EndpointTargetSchema = external_exports.object({
|
|
15040
15143
|
provider: SlugSchema,
|
|
15041
15144
|
model: external_exports.string().min(1)
|
|
15042
15145
|
});
|
|
15043
15146
|
var EndpointSchema = EndpointTargetSchema.extend({
|
|
15044
|
-
api_style: external_exports.enum(
|
|
15147
|
+
api_style: external_exports.enum(ENDPOINT_API_STYLES),
|
|
15045
15148
|
params: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
|
|
15046
15149
|
max_output_tokens: external_exports.number().int().positive().optional(),
|
|
15047
15150
|
fallback: external_exports.array(EndpointTargetSchema).optional()
|
|
15048
|
-
});
|
|
15151
|
+
}).strict();
|
|
15049
15152
|
var LimitScopeSchema = external_exports.object({
|
|
15050
15153
|
requests: external_exports.object({
|
|
15051
15154
|
per_minute: NullableRequestLimit,
|
|
15052
15155
|
per_day: NullableRequestLimit
|
|
15053
|
-
}),
|
|
15054
|
-
spending: external_exports.object({ monthly_usd: NullableSpendLimit })
|
|
15055
|
-
});
|
|
15156
|
+
}).strict(),
|
|
15157
|
+
spending: external_exports.object({ monthly_usd: NullableSpendLimit }).strict()
|
|
15158
|
+
}).strict();
|
|
15159
|
+
var UNLIMITED_SCOPE = {
|
|
15160
|
+
requests: { per_minute: null, per_day: null },
|
|
15161
|
+
spending: { monthly_usd: null }
|
|
15162
|
+
};
|
|
15163
|
+
function identifiesEndUsers(authentication) {
|
|
15164
|
+
return authentication.type !== "api_key" || authentication.end_user !== void 0;
|
|
15165
|
+
}
|
|
15166
|
+
var scopeHasLimits = (scope) => scope.requests.per_minute !== null || scope.requests.per_day !== null || scope.spending.monthly_usd !== null;
|
|
15056
15167
|
var AppConfigSchema = external_exports.object({
|
|
15057
15168
|
authentication: external_exports.discriminatedUnion("type", [
|
|
15058
15169
|
AppleAppAttestAuthenticationSchema,
|
|
15059
15170
|
ApiKeyAuthenticationSchema
|
|
15060
15171
|
]),
|
|
15061
15172
|
routing: external_exports.object({
|
|
15062
|
-
|
|
15063
|
-
|
|
15064
|
-
|
|
15065
|
-
|
|
15066
|
-
|
|
15067
|
-
|
|
15173
|
+
/**
|
|
15174
|
+
* Discriminated, because the two modes carry different fields: `all` names
|
|
15175
|
+
* nothing and `selected` must name its policies. Declaring `selected` as an
|
|
15176
|
+
* optional member of one object would make "all mode with a selection" and
|
|
15177
|
+
* "selected mode with nothing selected" both expressible.
|
|
15178
|
+
*/
|
|
15179
|
+
providers: external_exports.discriminatedUnion("mode", [
|
|
15180
|
+
external_exports.object({ mode: external_exports.literal("all") }).strict(),
|
|
15181
|
+
external_exports.object({
|
|
15182
|
+
mode: external_exports.literal("selected"),
|
|
15183
|
+
selected: external_exports.record(SlugSchema, ProviderPolicySchema, {
|
|
15184
|
+
error: "routing.providers.selected must be keyed by provider instance slug"
|
|
15185
|
+
})
|
|
15186
|
+
}).strict()
|
|
15187
|
+
], { error: "routing.providers.mode must be all or selected" }),
|
|
15188
|
+
model_rewrites: external_exports.record(safeKey(external_exports.string().min(1)), external_exports.string().min(1), {
|
|
15189
|
+
error: "routing.model_rewrites must be keyed by a model name"
|
|
15190
|
+
})
|
|
15191
|
+
}).strict(),
|
|
15068
15192
|
/**
|
|
15069
|
-
* The organization's own limits on its app's end users
|
|
15070
|
-
*
|
|
15193
|
+
* The organization's own limits on its app's end users, always present on a
|
|
15194
|
+
* parsed configuration: an unwritten block is the unlimited one.
|
|
15071
15195
|
*/
|
|
15072
15196
|
limits: external_exports.object({
|
|
15073
|
-
per_user: LimitScopeSchema,
|
|
15074
|
-
per_app: LimitScopeSchema
|
|
15075
|
-
}).
|
|
15076
|
-
endpoints: external_exports.record(
|
|
15197
|
+
per_user: LimitScopeSchema.prefault(UNLIMITED_SCOPE),
|
|
15198
|
+
per_app: LimitScopeSchema.prefault(UNLIMITED_SCOPE)
|
|
15199
|
+
}).strict().prefault({}),
|
|
15200
|
+
endpoints: external_exports.record(
|
|
15201
|
+
safeKey(external_exports.string().regex(ENDPOINT_SLUG)),
|
|
15202
|
+
EndpointSchema,
|
|
15203
|
+
{ error: "is not a valid slug; use 1-64 characters from a-z, 0-9, and -" }
|
|
15204
|
+
).prefault({})
|
|
15205
|
+
}).strict().superRefine((config2, context) => {
|
|
15206
|
+
const authentication = config2.authentication;
|
|
15207
|
+
const perUser = config2.limits?.per_user;
|
|
15208
|
+
if (authentication !== void 0 && perUser !== void 0 && !identifiesEndUsers(authentication) && scopeHasLimits(perUser)) {
|
|
15209
|
+
context.addIssue({
|
|
15210
|
+
code: "custom",
|
|
15211
|
+
path: ["limits", "per_user"],
|
|
15212
|
+
message: "needs an authentication.end_user source: this application identifies no end users, so use limits.per_app"
|
|
15213
|
+
});
|
|
15214
|
+
}
|
|
15077
15215
|
}).meta({ id: "AppConfig" });
|
|
15078
15216
|
var AppWriteSchema = external_exports.object({
|
|
15079
|
-
name
|
|
15217
|
+
/** Trimmed, so a name of nothing but spaces is the empty name it looks like. */
|
|
15218
|
+
name: external_exports.string().trim().min(1).max(100),
|
|
15080
15219
|
config: AppConfigSchema,
|
|
15081
15220
|
status: external_exports.enum(["active", "disabled"]).optional()
|
|
15082
15221
|
}).strict().meta({ id: "AppWrite" });
|
|
@@ -15104,7 +15243,7 @@ var ApiKeyTokenRequestSchema = external_exports.object({
|
|
|
15104
15243
|
var UsageRepriceRequestSchema = external_exports.object({
|
|
15105
15244
|
provider: ProviderTypeSchema,
|
|
15106
15245
|
model: external_exports.string().min(1),
|
|
15107
|
-
month:
|
|
15246
|
+
month: MonthSchema,
|
|
15108
15247
|
apply: external_exports.boolean().default(false)
|
|
15109
15248
|
}).strict().meta({ id: "UsageRepriceRequest" });
|
|
15110
15249
|
var ProviderPricingSchema = external_exports.record(
|
|
@@ -15130,15 +15269,17 @@ var GatewayRouteConfigSchema = external_exports.object({
|
|
|
15130
15269
|
modelPrefix: external_exports.string().trim().min(1).max(100).optional(),
|
|
15131
15270
|
providerOnly: external_exports.array(external_exports.string().trim().min(1).max(100)).min(1).max(20).optional()
|
|
15132
15271
|
}).strict().meta({ id: "GatewayRouteConfig" });
|
|
15133
|
-
var
|
|
15272
|
+
var ProviderCreateFieldsSchema = external_exports.object({
|
|
15134
15273
|
type: ProviderTypeSchema,
|
|
15135
15274
|
name: ProviderNameSchema,
|
|
15136
15275
|
slug: SlugSchema.optional(),
|
|
15137
|
-
secret: ProviderSecretSchema.optional(),
|
|
15138
15276
|
providerGatewayId: external_exports.string().trim().min(1).optional(),
|
|
15139
15277
|
gatewayRoute: GatewayRouteConfigSchema.optional(),
|
|
15140
15278
|
baseUrl: ProviderBaseUrlSchema.optional(),
|
|
15141
15279
|
pricing: ProviderPricingSchema.optional()
|
|
15280
|
+
});
|
|
15281
|
+
var ProviderCreateRequestSchema = ProviderCreateFieldsSchema.extend({
|
|
15282
|
+
secret: ProviderSecretSchema.optional()
|
|
15142
15283
|
}).strict().superRefine((value, context) => {
|
|
15143
15284
|
if (value.secret === void 0 === (value.providerGatewayId === void 0)) {
|
|
15144
15285
|
context.addIssue({
|
|
@@ -15165,21 +15306,22 @@ var ProviderTestRequestSchema = external_exports.object({
|
|
|
15165
15306
|
}
|
|
15166
15307
|
assertBaseUrlIsDirect(value, context);
|
|
15167
15308
|
}).meta({ id: "ProviderTestRequest" });
|
|
15309
|
+
var CfAigGatewayFieldsSchema = external_exports.object({
|
|
15310
|
+
type: external_exports.literal("cf_aig"),
|
|
15311
|
+
name: ProviderNameSchema,
|
|
15312
|
+
accountId: external_exports.string().trim().min(1).max(100),
|
|
15313
|
+
gatewayId: external_exports.string().trim().min(1).max(100)
|
|
15314
|
+
});
|
|
15315
|
+
var VercelGatewayFieldsSchema = external_exports.object({
|
|
15316
|
+
type: external_exports.literal("vercel"),
|
|
15317
|
+
name: ProviderNameSchema
|
|
15318
|
+
});
|
|
15319
|
+
var GATEWAY_TYPE_ERROR = "Provider gateway type must be one of cf_aig, vercel";
|
|
15168
15320
|
var ProviderGatewayCreateRequestSchema = external_exports.discriminatedUnion("type", [
|
|
15169
|
-
|
|
15170
|
-
|
|
15171
|
-
name: ProviderNameSchema,
|
|
15172
|
-
accountId: external_exports.string().trim().min(1).max(100),
|
|
15173
|
-
gatewayId: external_exports.string().trim().min(1).max(100),
|
|
15174
|
-
token: ProviderSecretSchema
|
|
15175
|
-
}).strict(),
|
|
15176
|
-
external_exports.object({
|
|
15177
|
-
type: external_exports.literal("vercel"),
|
|
15178
|
-
name: ProviderNameSchema,
|
|
15179
|
-
token: ProviderSecretSchema
|
|
15180
|
-
}).strict()
|
|
15321
|
+
CfAigGatewayFieldsSchema.extend({ token: ProviderSecretSchema }).strict(),
|
|
15322
|
+
VercelGatewayFieldsSchema.extend({ token: ProviderSecretSchema }).strict()
|
|
15181
15323
|
], {
|
|
15182
|
-
error:
|
|
15324
|
+
error: GATEWAY_TYPE_ERROR
|
|
15183
15325
|
}).meta({ id: "ProviderGatewayCreateRequest" });
|
|
15184
15326
|
var ProviderGatewayTestRequestSchema = external_exports.discriminatedUnion("type", [
|
|
15185
15327
|
external_exports.object({
|
|
@@ -15204,10 +15346,8 @@ var ProviderGatewayRotateRequestSchema = external_exports.object({
|
|
|
15204
15346
|
revision: external_exports.number().int().positive().meta({ description: "The gateway revision returned by the read this rotation is based on." })
|
|
15205
15347
|
}).strict().meta({ id: "ProviderGatewayRotateRequest" });
|
|
15206
15348
|
var BASE_URL_REQUIRES_SECRET = "Changing the base URL requires re-supplying the provider key, because the stored key is never sent to a new origin";
|
|
15207
|
-
var
|
|
15208
|
-
revision: external_exports.number().int().positive().meta({ description: "The provider revision returned by the read this update is based on." }),
|
|
15349
|
+
var ProviderUpdateFieldsSchema = external_exports.object({
|
|
15209
15350
|
name: ProviderNameSchema.optional(),
|
|
15210
|
-
secret: ProviderSecretSchema.optional(),
|
|
15211
15351
|
/** A full replace; `null` clears the row's routing configuration. */
|
|
15212
15352
|
gatewayRoute: GatewayRouteConfigSchema.nullable().optional(),
|
|
15213
15353
|
/**
|
|
@@ -15223,6 +15363,10 @@ var ProviderUpdateRequestSchema = external_exports.object({
|
|
|
15223
15363
|
* nothing can take the slug meanwhile and re-enabling always succeeds.
|
|
15224
15364
|
*/
|
|
15225
15365
|
status: external_exports.enum(["active", "disabled"]).optional()
|
|
15366
|
+
});
|
|
15367
|
+
var ProviderUpdateRequestSchema = ProviderUpdateFieldsSchema.extend({
|
|
15368
|
+
revision: external_exports.number().int().positive().meta({ description: "The provider revision returned by the read this update is based on." }),
|
|
15369
|
+
secret: ProviderSecretSchema.optional()
|
|
15226
15370
|
}).strict().superRefine((value, context) => {
|
|
15227
15371
|
if (value.name === void 0 && value.secret === void 0 && value.gatewayRoute === void 0 && value.baseUrl === void 0 && value.pricing === void 0 && value.status === void 0) {
|
|
15228
15372
|
context.addIssue({
|
|
@@ -15234,12 +15378,73 @@ var ProviderUpdateRequestSchema = external_exports.object({
|
|
|
15234
15378
|
context.addIssue({ code: "custom", message: BASE_URL_REQUIRES_SECRET, path: ["secret"] });
|
|
15235
15379
|
}
|
|
15236
15380
|
}).meta({ id: "ProviderUpdateRequest" });
|
|
15381
|
+
var HandoffTargetFields = {
|
|
15382
|
+
/** The existing row the handoff edits. */
|
|
15383
|
+
id: external_exports.string().trim().min(1),
|
|
15384
|
+
/** The revision the CLI read, when it read one; the server pins the current one either way. */
|
|
15385
|
+
revision: external_exports.number().int().positive().optional()
|
|
15386
|
+
};
|
|
15387
|
+
var HandoffProviderAddPayloadSchema = ProviderCreateFieldsSchema.strict().superRefine(assertBaseUrlIsDirect);
|
|
15388
|
+
var HandoffProviderUpdatePayloadSchema = ProviderUpdateFieldsSchema.extend(HandoffTargetFields).strict().superRefine((value, context) => {
|
|
15389
|
+
if (value.name === void 0 && value.gatewayRoute === void 0 && value.baseUrl === void 0 && value.pricing === void 0 && value.status === void 0) {
|
|
15390
|
+
context.addIssue({
|
|
15391
|
+
code: "custom",
|
|
15392
|
+
message: "Provide at least one of name, gatewayRoute, baseUrl, pricing, or status"
|
|
15393
|
+
});
|
|
15394
|
+
}
|
|
15395
|
+
});
|
|
15396
|
+
var HandoffRotatePayloadSchema = external_exports.object(HandoffTargetFields).strict();
|
|
15397
|
+
var HandoffProviderGatewayAddPayloadSchema = external_exports.discriminatedUnion("type", [
|
|
15398
|
+
CfAigGatewayFieldsSchema.strict(),
|
|
15399
|
+
VercelGatewayFieldsSchema.strict()
|
|
15400
|
+
], { error: GATEWAY_TYPE_ERROR });
|
|
15237
15401
|
var OrganizationRoleSchema = external_exports.enum(["owner", "admin", "member"]);
|
|
15238
15402
|
var OrganizationSelectRequestSchema = external_exports.object({
|
|
15239
15403
|
organizationId: external_exports.string().trim().min(1)
|
|
15240
15404
|
}).meta({ id: "OrganizationSelectRequest" });
|
|
15405
|
+
var CredentialNameRequestSchema = external_exports.object({
|
|
15406
|
+
name: external_exports.string().trim().min(1).max(100)
|
|
15407
|
+
});
|
|
15408
|
+
var ManagementKeyCreateRequestSchema = CredentialNameRequestSchema.meta({ id: "ManagementKeyCreateRequest" });
|
|
15409
|
+
var ApiKeyCreateRequestSchema = CredentialNameRequestSchema.meta({ id: "ApiKeyCreateRequest" });
|
|
15410
|
+
|
|
15411
|
+
// ../src/shared/app-config.ts
|
|
15412
|
+
function selectedProviderPolicies(routing) {
|
|
15413
|
+
return routing.providers.mode === "selected" ? routing.providers.selected : {};
|
|
15414
|
+
}
|
|
15415
|
+
function providerPolicyFor(routing, slug) {
|
|
15416
|
+
if (routing.providers.mode === "all") return { allowed_paths: [], allowed_models: [] };
|
|
15417
|
+
return Object.hasOwn(routing.providers.selected, slug) ? routing.providers.selected[slug] : void 0;
|
|
15418
|
+
}
|
|
15419
|
+
function reachableProviders(routing, instances) {
|
|
15420
|
+
return instances.filter(
|
|
15421
|
+
(instance) => instance.status === "active" && providerPolicyFor(routing, instance.slug) !== void 0
|
|
15422
|
+
);
|
|
15423
|
+
}
|
|
15241
15424
|
|
|
15242
15425
|
// ../src/contracts/responses.ts
|
|
15426
|
+
var USAGE_STATUSES = [
|
|
15427
|
+
"ok",
|
|
15428
|
+
"provider_error",
|
|
15429
|
+
"blocked_app_rate",
|
|
15430
|
+
"blocked_app_budget",
|
|
15431
|
+
"blocked_billing",
|
|
15432
|
+
"blocked_user"
|
|
15433
|
+
];
|
|
15434
|
+
var USAGE_BREAKDOWN_DIMENSIONS = [
|
|
15435
|
+
"model",
|
|
15436
|
+
"provider",
|
|
15437
|
+
"provider_slug",
|
|
15438
|
+
"provider_gateway",
|
|
15439
|
+
"credential_source",
|
|
15440
|
+
"model_author",
|
|
15441
|
+
"user",
|
|
15442
|
+
"status",
|
|
15443
|
+
"cost_source",
|
|
15444
|
+
"route",
|
|
15445
|
+
"endpoint",
|
|
15446
|
+
"app_version"
|
|
15447
|
+
];
|
|
15243
15448
|
var ErrorResponseSchema = external_exports.object({
|
|
15244
15449
|
error: external_exports.object({
|
|
15245
15450
|
code: external_exports.string(),
|
|
@@ -15267,6 +15472,24 @@ var AppAttestChallengeResponseSchema = external_exports.object({
|
|
|
15267
15472
|
expires_in: external_exports.number()
|
|
15268
15473
|
});
|
|
15269
15474
|
var AppAttestRegisterResponseSchema = external_exports.object({ user_id: external_exports.string() });
|
|
15475
|
+
var GatewayTokenResponseSchema = external_exports.object({
|
|
15476
|
+
access_token: external_exports.string(),
|
|
15477
|
+
expires_in: external_exports.number()
|
|
15478
|
+
});
|
|
15479
|
+
var CurrentUserResponseSchema = external_exports.object({
|
|
15480
|
+
user_id: external_exports.string(),
|
|
15481
|
+
limits: external_exports.object({
|
|
15482
|
+
requests_today: external_exports.number().int().nullable().meta({ description: "Requests this user has made so far in the current UTC day. Null when the application sets no per-user limit, in which case requests are not counted at all." }),
|
|
15483
|
+
requests_remaining: external_exports.number().int().nullable().meta({ description: "What is left of requests_per_day. Null when no daily limit is set." }),
|
|
15484
|
+
requests_per_minute: external_exports.number().nullable().meta({ description: "The per-user per-minute limit this app sets. Null means unlimited." }),
|
|
15485
|
+
requests_per_day: external_exports.number().nullable().meta({ description: "The per-user per-day limit this app sets. Null means unlimited." }),
|
|
15486
|
+
monthly_cost_usd: external_exports.number().meta({ description: "What this user's traffic has cost so far in the current UTC calendar month." }),
|
|
15487
|
+
monthly_budget_usd: external_exports.number().nullable().meta({ description: "The per-user monthly spending budget this app sets. Null means unlimited." }),
|
|
15488
|
+
blocked: external_exports.boolean().meta({ description: "Whether this user has been blocked in the console." })
|
|
15489
|
+
}).meta({
|
|
15490
|
+
description: "The limits the app sets on this user, and where the user stands against them. The account's request allowance is not reported here: it is shared by all apps, and is reported on the rejection that spends it."
|
|
15491
|
+
})
|
|
15492
|
+
});
|
|
15270
15493
|
var UsageEventSchema = external_exports.object({
|
|
15271
15494
|
id: external_exports.number().int(),
|
|
15272
15495
|
/** Null for an application that identifies no end users. */
|
|
@@ -15310,7 +15533,7 @@ var UsageEventSchema = external_exports.object({
|
|
|
15310
15533
|
}),
|
|
15311
15534
|
app_version: external_exports.string().nullable(),
|
|
15312
15535
|
auth_method: external_exports.enum(["attest", "api_key"]).nullable(),
|
|
15313
|
-
status: external_exports.enum(
|
|
15536
|
+
status: external_exports.enum(USAGE_STATUSES),
|
|
15314
15537
|
client_aborted: external_exports.number().int().nullable().meta({
|
|
15315
15538
|
description: "1 when the client disconnected before the upstream finished streaming, which cancelled the provider call; null otherwise. Not a failure \u2014 the request was served as far as the caller wanted it \u2014 but an aborted stream often takes the provider's end-of-response usage with it, which is why such an event may carry cost_source unresolved."
|
|
15316
15539
|
}),
|
|
@@ -15389,18 +15612,18 @@ var AppResponseSchema = external_exports.object({
|
|
|
15389
15612
|
id: external_exports.string().meta({ description: "The gateway-assigned id, and the `{app}` segment of every URL for this application." }),
|
|
15390
15613
|
name: external_exports.string(),
|
|
15391
15614
|
/**
|
|
15392
|
-
* An `AppConfig
|
|
15393
|
-
*
|
|
15394
|
-
* it
|
|
15395
|
-
*
|
|
15396
|
-
*
|
|
15615
|
+
* An `AppConfig` — the parsed one, which is also the stored one: what the
|
|
15616
|
+
* gateway accepts is what it keeps, so there is no second "resolved" view
|
|
15617
|
+
* of it to publish. The one exception is the row this shape's own
|
|
15618
|
+
* `config_error` describes: a configuration written before a schema change
|
|
15619
|
+
* is returned as it is stored, so an operator can read and repair it, and
|
|
15620
|
+
* that is why this is declared as the union rather than as `AppConfig`.
|
|
15397
15621
|
*/
|
|
15398
15622
|
config: external_exports.union([AppConfigSchema, external_exports.record(external_exports.string(), external_exports.unknown())]),
|
|
15399
15623
|
status: external_exports.enum(["active", "disabled"]),
|
|
15400
15624
|
created_at: external_exports.string(),
|
|
15401
15625
|
updated_at: external_exports.string()
|
|
15402
15626
|
}),
|
|
15403
|
-
resolved: external_exports.record(external_exports.string(), external_exports.unknown()).nullable().meta({ description: "The configuration as the request path resolves it, with provider routing and limits applied. Null when the stored configuration does not parse, which is the one case `app.config` is not an AppConfig." }),
|
|
15404
15627
|
config_error: external_exports.string().nullable().meta({ description: "Why the stored configuration does not parse, for a row written before a schema change. Always null on create and update, which validate before they write." })
|
|
15405
15628
|
}).meta({ id: "AppResponse" });
|
|
15406
15629
|
var AppDeleteResponseSchema = external_exports.object({
|
|
@@ -15435,7 +15658,7 @@ var ManagementKeyResponseSchema = external_exports.object({ key: ManagementKeySu
|
|
|
15435
15658
|
var ProviderSummarySchema = external_exports.object({
|
|
15436
15659
|
id: external_exports.string(),
|
|
15437
15660
|
type: external_exports.enum(PROVIDER_TYPES),
|
|
15438
|
-
slug:
|
|
15661
|
+
slug: StoredSlugSchema.meta({ description: "The URL segment used under /proxy/{slug}/, unique across your providers." }),
|
|
15439
15662
|
name: external_exports.string(),
|
|
15440
15663
|
secretHint: external_exports.string().nullable().meta({
|
|
15441
15664
|
description: "Last characters of a direct provider key; null when a shared provider gateway owns the token."
|
|
@@ -15611,13 +15834,17 @@ var AppSummarySchema = external_exports.object({
|
|
|
15611
15834
|
});
|
|
15612
15835
|
var AppListResponseSchema = external_exports.object({
|
|
15613
15836
|
month: external_exports.string(),
|
|
15614
|
-
has_proxied_requests: external_exports.boolean()
|
|
15837
|
+
has_proxied_requests: external_exports.boolean().meta({
|
|
15838
|
+
description: "Whether this account has ever had a request recorded, at any time. Unlike the per-application `usage` totals beside it, which cover `month` only, this does not reset when a new month begins, and it never goes from true back to false. Intended for first-run interfaces that stop offering setup guidance once traffic has started."
|
|
15839
|
+
}),
|
|
15615
15840
|
apps: external_exports.array(AppSummarySchema)
|
|
15616
15841
|
});
|
|
15617
15842
|
var AppValidateResponseSchema = external_exports.object({
|
|
15618
15843
|
valid: external_exports.literal(true),
|
|
15619
|
-
app_id: external_exports.string()
|
|
15620
|
-
|
|
15844
|
+
app_id: external_exports.string()
|
|
15845
|
+
});
|
|
15846
|
+
var AppDraftValidateResponseSchema = external_exports.object({
|
|
15847
|
+
valid: external_exports.literal(true)
|
|
15621
15848
|
});
|
|
15622
15849
|
var CreatedApiKeySchema = external_exports.object({
|
|
15623
15850
|
id: external_exports.string(),
|
|
@@ -15700,11 +15927,32 @@ var BreakdownRowSchema = UsageTotalsSchema.extend({
|
|
|
15700
15927
|
});
|
|
15701
15928
|
var BreakdownResponseSchema = external_exports.object({
|
|
15702
15929
|
app_id: external_exports.string(),
|
|
15703
|
-
by: external_exports.
|
|
15930
|
+
by: external_exports.enum(USAGE_BREAKDOWN_DIMENSIONS),
|
|
15704
15931
|
from: external_exports.string(),
|
|
15705
15932
|
to: external_exports.string(),
|
|
15706
15933
|
rows: external_exports.array(BreakdownRowSchema)
|
|
15707
15934
|
});
|
|
15935
|
+
var UsageRepriceResponseSchema = external_exports.object({
|
|
15936
|
+
app_id: external_exports.string(),
|
|
15937
|
+
provider: external_exports.string(),
|
|
15938
|
+
model: external_exports.string(),
|
|
15939
|
+
month: external_exports.string(),
|
|
15940
|
+
applied: external_exports.boolean(),
|
|
15941
|
+
matched_events: external_exports.number().int(),
|
|
15942
|
+
unmetered_events: external_exports.number().int().meta({
|
|
15943
|
+
description: "Matched events that carried no readable usage, and so were repriced to the zero their zero counts imply while keeping whatever cost_source they had. A non-zero number means the month contains spend nothing could meter, which repricing cannot fix and must not appear to have fixed."
|
|
15944
|
+
}),
|
|
15945
|
+
unpriced_events: external_exports.number().int().meta({
|
|
15946
|
+
description: "Dry-run only: matched events whose serving instance can no longer price them."
|
|
15947
|
+
}),
|
|
15948
|
+
unpriced_cost_usd: external_exports.number(),
|
|
15949
|
+
previous_cost_usd: external_exports.number(),
|
|
15950
|
+
recalculated_cost_usd: external_exports.number(),
|
|
15951
|
+
delta_usd: external_exports.number(),
|
|
15952
|
+
reconciled_users: external_exports.number().int().meta({
|
|
15953
|
+
description: "End-user spend ledgers reprojected inside this request; the rest are left to scheduled recovery."
|
|
15954
|
+
})
|
|
15955
|
+
});
|
|
15708
15956
|
var ModelPriceSchema = external_exports.object({
|
|
15709
15957
|
input: external_exports.number().optional(),
|
|
15710
15958
|
output: external_exports.number().optional(),
|
|
@@ -15725,18 +15973,25 @@ var PricesResponseSchema = external_exports.object({
|
|
|
15725
15973
|
// ../src/contracts/cli.ts
|
|
15726
15974
|
var CliProofSchema = external_exports.string().regex(/^[A-Za-z0-9_-]{32,256}$/);
|
|
15727
15975
|
var CliBootstrapRequestSchema = external_exports.object({ idempotencyKey: CliProofSchema, pollToken: CliProofSchema }).strict();
|
|
15728
|
-
var
|
|
15729
|
-
|
|
15730
|
-
|
|
15731
|
-
|
|
15732
|
-
|
|
15733
|
-
|
|
15734
|
-
|
|
15735
|
-
|
|
15736
|
-
|
|
15737
|
-
|
|
15738
|
-
|
|
15739
|
-
|
|
15976
|
+
var CliOperationKindSchema = external_exports.enum([
|
|
15977
|
+
"claim",
|
|
15978
|
+
"provider.add",
|
|
15979
|
+
"provider.rotate-key",
|
|
15980
|
+
"provider.update",
|
|
15981
|
+
"provider-gateway.add",
|
|
15982
|
+
"provider-gateway.rotate-key"
|
|
15983
|
+
]);
|
|
15984
|
+
function operationRequest(kind, payload) {
|
|
15985
|
+
return external_exports.object({ kind: external_exports.literal(kind), payload, pollToken: CliProofSchema }).strict();
|
|
15986
|
+
}
|
|
15987
|
+
var CliOperationRequestSchema = external_exports.discriminatedUnion("kind", [
|
|
15988
|
+
operationRequest("claim", external_exports.object({}).strict().default({})),
|
|
15989
|
+
operationRequest("provider.add", HandoffProviderAddPayloadSchema),
|
|
15990
|
+
operationRequest("provider.rotate-key", HandoffRotatePayloadSchema),
|
|
15991
|
+
operationRequest("provider.update", HandoffProviderUpdatePayloadSchema),
|
|
15992
|
+
operationRequest("provider-gateway.add", HandoffProviderGatewayAddPayloadSchema),
|
|
15993
|
+
operationRequest("provider-gateway.rotate-key", HandoffRotatePayloadSchema)
|
|
15994
|
+
]);
|
|
15740
15995
|
var CliSubmissionRequestSchema = external_exports.object({
|
|
15741
15996
|
submissionToken: CliProofSchema,
|
|
15742
15997
|
approve: external_exports.literal(true).optional(),
|
|
@@ -15767,7 +16022,7 @@ var CliViewerSchema = external_exports.object({
|
|
|
15767
16022
|
email: external_exports.string().nullable()
|
|
15768
16023
|
});
|
|
15769
16024
|
var CliBrowserDetailsResponseSchema = external_exports.object({
|
|
15770
|
-
kind:
|
|
16025
|
+
kind: CliOperationKindSchema,
|
|
15771
16026
|
payload: external_exports.record(external_exports.string(), external_exports.unknown()),
|
|
15772
16027
|
account: CliAccountSchema,
|
|
15773
16028
|
viewer: CliViewerSchema.nullable(),
|
|
@@ -15800,11 +16055,17 @@ var CliBrowserGoogleResponseSchema = external_exports.object({
|
|
|
15800
16055
|
var CliCredentialSchema = external_exports.object({
|
|
15801
16056
|
token: external_exports.string()
|
|
15802
16057
|
});
|
|
16058
|
+
var UnclaimedAccessSchema = external_exports.object({ endsAt: external_exports.string(), limit: external_exports.number().int().optional() }).nullable();
|
|
15803
16059
|
var CliBootstrapResponseSchema = external_exports.object({
|
|
15804
16060
|
deployment: CliDeploymentSchema,
|
|
15805
16061
|
account: CliAccountSchema,
|
|
15806
16062
|
credential: CliCredentialSchema,
|
|
15807
|
-
|
|
16063
|
+
/**
|
|
16064
|
+
* On a hosted deployment, when this unclaimed account's free access ends and
|
|
16065
|
+
* the request allowance it has until then; null on a self-host, where an
|
|
16066
|
+
* account has no such window.
|
|
16067
|
+
*/
|
|
16068
|
+
unclaimedAccess: UnclaimedAccessSchema
|
|
15808
16069
|
});
|
|
15809
16070
|
var CliOperationResponseSchema = external_exports.object({
|
|
15810
16071
|
id: external_exports.string(),
|
|
@@ -15921,57 +16182,6 @@ var CliAccountResponseSchema = external_exports.object({
|
|
|
15921
16182
|
billing: CliBillingSchema,
|
|
15922
16183
|
usage: CliQuotaUsageSchema
|
|
15923
16184
|
});
|
|
15924
|
-
|
|
15925
|
-
// ../src/contracts/operation-schemas.ts
|
|
15926
|
-
var responseSchemas = {
|
|
15927
|
-
getConsoleCapabilities: ConsoleCapabilitiesResponseSchema,
|
|
15928
|
-
getAdminSession: IdentitySessionSchema,
|
|
15929
|
-
listOrganizations: OrganizationListResponseSchema,
|
|
15930
|
-
selectOrganization: IdentitySessionSchema,
|
|
15931
|
-
listManagementKeys: ManagementKeyListResponseSchema,
|
|
15932
|
-
createManagementKey: CreatedManagementKeyResponseSchema,
|
|
15933
|
-
revokeManagementKey: ManagementKeyResponseSchema,
|
|
15934
|
-
listProviders: ProviderListResponseSchema,
|
|
15935
|
-
createProvider: ProviderResponseSchema,
|
|
15936
|
-
testProvider: ProviderTestResponseSchema,
|
|
15937
|
-
updateProvider: ProviderResponseSchema,
|
|
15938
|
-
deleteProvider: ProviderDeleteResponseSchema,
|
|
15939
|
-
listProviderGateways: ProviderGatewayListResponseSchema,
|
|
15940
|
-
createProviderGateway: ProviderGatewayResponseSchema,
|
|
15941
|
-
testProviderGateway: ProviderGatewayTestResponseSchema,
|
|
15942
|
-
updateProviderGateway: ProviderGatewayResponseSchema,
|
|
15943
|
-
rotateProviderGateway: ProviderGatewayResponseSchema,
|
|
15944
|
-
deleteProviderGateway: ProviderGatewayDeleteResponseSchema,
|
|
15945
|
-
listApps: AppListResponseSchema,
|
|
15946
|
-
createApp: CreatedAppResponseSchema,
|
|
15947
|
-
getApp: AppResponseSchema,
|
|
15948
|
-
updateApp: AppResponseSchema,
|
|
15949
|
-
deleteApp: AppDeleteResponseSchema,
|
|
15950
|
-
validateApp: AppValidateResponseSchema,
|
|
15951
|
-
listAppKeys: ApiKeyListResponseSchema,
|
|
15952
|
-
createAppKey: CreatedApiKeySchema,
|
|
15953
|
-
revokeAppKey: ApiKeyRevokeResponseSchema,
|
|
15954
|
-
listAppUsers: UserListResponseSchema,
|
|
15955
|
-
setAppUserBlocked: UserBlockResponseSchema,
|
|
15956
|
-
getAppUsage: MonthlyUsageResponseSchema,
|
|
15957
|
-
getAppUsageTimeseries: TimeseriesResponseSchema,
|
|
15958
|
-
getAppUsageBreakdown: BreakdownResponseSchema,
|
|
15959
|
-
listAppEvents: UsageEventListSchema,
|
|
15960
|
-
getAppAuthEventSummary: AuthEventSummarySchema,
|
|
15961
|
-
listAppAuthEvents: AuthEventListSchema,
|
|
15962
|
-
listModelPrices: PricesResponseSchema,
|
|
15963
|
-
getCliCapabilities: CliCapabilitiesResponseSchema,
|
|
15964
|
-
bootstrapCliAccount: CliBootstrapResponseSchema,
|
|
15965
|
-
createCliOperation: CliOperationResponseSchema,
|
|
15966
|
-
pollCliOperation: CliPollResponseSchema,
|
|
15967
|
-
getCliAccount: CliAccountResponseSchema,
|
|
15968
|
-
getCliUsage: CliUsageResponseSchema,
|
|
15969
|
-
cliBrowserDetails: CliBrowserDetailsResponseSchema,
|
|
15970
|
-
cliBrowserSubmit: CliBrowserSubmitResponseSchema,
|
|
15971
|
-
cliBrowserRegister: CliBrowserRegisterResponseSchema,
|
|
15972
|
-
cliBrowserGoogle: CliBrowserGoogleResponseSchema
|
|
15973
|
-
};
|
|
15974
|
-
var responseSchemaFor = responseSchemas;
|
|
15975
16185
|
var CliErrorDetailsSchema = external_exports.object({
|
|
15976
16186
|
status: external_exports.number().optional(),
|
|
15977
16187
|
appId: external_exports.string().optional(),
|
|
@@ -16006,7 +16216,1058 @@ var CliErrorDetailsSchema = external_exports.object({
|
|
|
16006
16216
|
// src/apps.ts
|
|
16007
16217
|
import { createHash as createHash2 } from "node:crypto";
|
|
16008
16218
|
|
|
16009
|
-
// ../src/contracts/
|
|
16219
|
+
// ../src/contracts/billing.ts
|
|
16220
|
+
var BillingSubscriptionStatusSchema = external_exports.enum([
|
|
16221
|
+
"on_trial",
|
|
16222
|
+
"active",
|
|
16223
|
+
"paused",
|
|
16224
|
+
"past_due",
|
|
16225
|
+
"unpaid",
|
|
16226
|
+
"cancelled",
|
|
16227
|
+
"expired"
|
|
16228
|
+
]);
|
|
16229
|
+
var BillingSourceSchema = external_exports.enum(["lemon_squeezy", "manual"]);
|
|
16230
|
+
var BillingPeriodSchema = external_exports.enum(["month", "year"]);
|
|
16231
|
+
var EntitledPlanSchema = external_exports.object({
|
|
16232
|
+
planKey: external_exports.string(),
|
|
16233
|
+
planName: external_exports.string(),
|
|
16234
|
+
limits: external_exports.unknown().optional(),
|
|
16235
|
+
/** True when it came from the service's default plan rather than a subscription. */
|
|
16236
|
+
isDefault: external_exports.boolean()
|
|
16237
|
+
}).meta({ id: "EntitledPlan" });
|
|
16238
|
+
var SubscriptionStateSchema = external_exports.object({
|
|
16239
|
+
subscriptionId: external_exports.string().nullable(),
|
|
16240
|
+
status: BillingSubscriptionStatusSchema,
|
|
16241
|
+
planKey: external_exports.string(),
|
|
16242
|
+
planName: external_exports.string(),
|
|
16243
|
+
/** Null for trials and manual grants, which carry no price. */
|
|
16244
|
+
billingPeriod: BillingPeriodSchema.nullable(),
|
|
16245
|
+
renewsAt: external_exports.string().nullable(),
|
|
16246
|
+
endsAt: external_exports.string().nullable(),
|
|
16247
|
+
trialEndsAt: external_exports.string().nullable(),
|
|
16248
|
+
source: BillingSourceSchema,
|
|
16249
|
+
/** This subscription generation */
|
|
16250
|
+
createdAt: external_exports.string(),
|
|
16251
|
+
updatedAt: external_exports.string(),
|
|
16252
|
+
/** Original provider day, retained when short months clamp the exact anchor. */
|
|
16253
|
+
billingAnchorDay: external_exports.number().nullable(),
|
|
16254
|
+
/** Exact normalized UTC origin of the monthly allowance schedule. */
|
|
16255
|
+
billingAnchorAt: external_exports.string(),
|
|
16256
|
+
/** When the current allowance schedule took effect. */
|
|
16257
|
+
billingScheduleUpdatedAt: external_exports.string()
|
|
16258
|
+
}).meta({ id: "Subscription" });
|
|
16259
|
+
var BillingAccessSchema = external_exports.object({
|
|
16260
|
+
/** `null` means no entitlement at all, and the gateway refuses the request. */
|
|
16261
|
+
plan: EntitledPlanSchema.nullable(),
|
|
16262
|
+
/** `null` means the organization has no billing row. */
|
|
16263
|
+
subscription: SubscriptionStateSchema.nullable()
|
|
16264
|
+
});
|
|
16265
|
+
var GatewayBillingAccessSchema = external_exports.discriminatedUnion("state", [
|
|
16266
|
+
/** No `BILLING` binding: self-hosted, unlimited, never refused. */
|
|
16267
|
+
external_exports.object({ state: external_exports.literal("self_hosted") }),
|
|
16268
|
+
/** The billing RPC failed. The allowance is unknown, so traffic waits. */
|
|
16269
|
+
external_exports.object({ state: external_exports.literal("unavailable"), billingErrorCode: external_exports.string().optional() }),
|
|
16270
|
+
/** The billing service answered. `plan === null` means no entitlement at all. */
|
|
16271
|
+
external_exports.object({
|
|
16272
|
+
state: external_exports.literal("billed"),
|
|
16273
|
+
plan: EntitledPlanSchema.nullable(),
|
|
16274
|
+
subscription: SubscriptionStateSchema.nullable(),
|
|
16275
|
+
/**
|
|
16276
|
+
* Set when the billing service could not be reached and this is the last
|
|
16277
|
+
* reading it gave for the organization. Entitlement decisions are
|
|
16278
|
+
* unchanged: a stale `plan === null` is still a paywall.
|
|
16279
|
+
*/
|
|
16280
|
+
stale: external_exports.literal(true).optional(),
|
|
16281
|
+
/** Why the refresh failed, on a stale reading. */
|
|
16282
|
+
billingErrorCode: external_exports.string().optional()
|
|
16283
|
+
})
|
|
16284
|
+
]).meta({ id: "BillingAccess" });
|
|
16285
|
+
var PlanLimitsSchema = external_exports.object({
|
|
16286
|
+
maxRequestsPerMonth: external_exports.number().int().optional().meta({ description: "Requests the account may dispatch per allowance period." }),
|
|
16287
|
+
maxApps: external_exports.number().int().optional().meta({ description: "Applications the account may own." }),
|
|
16288
|
+
maxProviders: external_exports.number().int().optional().meta({ description: "Providers the account may own." }),
|
|
16289
|
+
maxProviderGateways: external_exports.number().int().optional().meta({ description: "Provider gateways the account may own." }),
|
|
16290
|
+
maxActiveKeysPerApp: external_exports.number().int().optional().meta({ description: "Active API keys each of the account's applications may hold." })
|
|
16291
|
+
}).meta({
|
|
16292
|
+
description: "The plan's ceilings, as this gateway enforces them. Every key is optional and an absent key means unlimited, so an empty object is a plan with no ceilings. A write that would exceed one is refused with billing_plan_limit_reached."
|
|
16293
|
+
});
|
|
16294
|
+
var OrganizationQuotaStatusSchema = external_exports.object({
|
|
16295
|
+
periodId: external_exports.string().meta({ description: "Opaque identifier for the allowance period being reported." }),
|
|
16296
|
+
periodStart: external_exports.string().meta({ description: "Inclusive UTC instant at which this allowance period began." }),
|
|
16297
|
+
periodEnd: external_exports.string().meta({ description: "Exclusive UTC instant at which this allowance period ends." }),
|
|
16298
|
+
used: external_exports.number().int().meta({ description: "Requests dispatched to a provider in this period, across all apps." }),
|
|
16299
|
+
limit: external_exports.number().int().optional().meta({ description: "The plan's maxRequestsPerMonth. Absent means unlimited." }),
|
|
16300
|
+
resetAt: external_exports.string().meta({ description: "UTC instant at which the allowance resets; currently equal to periodEnd." })
|
|
16301
|
+
});
|
|
16302
|
+
var SubscriptionActionsSchema = external_exports.object({
|
|
16303
|
+
cancel: external_exports.boolean().meta({ description: "A self-service subscription can be cancelled at the end of its period." }),
|
|
16304
|
+
resume: external_exports.boolean().meta({ description: "A cancelled self-service subscription can be resumed." }),
|
|
16305
|
+
manual: external_exports.boolean().meta({ description: "A live grant made by hand, which no self-service action may change." })
|
|
16306
|
+
});
|
|
16307
|
+
var BillingStatusResponseSchema = external_exports.object({
|
|
16308
|
+
access: GatewayBillingAccessSchema,
|
|
16309
|
+
limits: PlanLimitsSchema,
|
|
16310
|
+
quota: OrganizationQuotaStatusSchema.nullable().meta({
|
|
16311
|
+
description: "Null when billing is unavailable or no plan entitlement resolves."
|
|
16312
|
+
}),
|
|
16313
|
+
actions: SubscriptionActionsSchema,
|
|
16314
|
+
unclaimedAccessEndsAt: external_exports.string().nullable().meta({
|
|
16315
|
+
description: "When this account's free access ends if nobody claims it; null once a person owns it."
|
|
16316
|
+
})
|
|
16317
|
+
});
|
|
16318
|
+
var BillingPlanOfferPriceSchema = external_exports.object({
|
|
16319
|
+
billingPeriod: BillingPeriodSchema,
|
|
16320
|
+
priceAmountCents: external_exports.number(),
|
|
16321
|
+
priceCurrency: external_exports.string()
|
|
16322
|
+
});
|
|
16323
|
+
var BillingPlanOfferSchema = external_exports.object({
|
|
16324
|
+
planKey: external_exports.string(),
|
|
16325
|
+
name: external_exports.string(),
|
|
16326
|
+
description: external_exports.string(),
|
|
16327
|
+
features: external_exports.array(external_exports.string()),
|
|
16328
|
+
limits: external_exports.unknown().optional(),
|
|
16329
|
+
trialDays: external_exports.number(),
|
|
16330
|
+
prices: external_exports.array(BillingPlanOfferPriceSchema)
|
|
16331
|
+
}).meta({ id: "BillingPlan" });
|
|
16332
|
+
var BillingPlansResponseSchema = external_exports.object({
|
|
16333
|
+
plans: external_exports.array(BillingPlanOfferSchema)
|
|
16334
|
+
});
|
|
16335
|
+
var BillingPlanSelectionSchema = external_exports.object({
|
|
16336
|
+
planKey: external_exports.string().min(1),
|
|
16337
|
+
billingPeriod: BillingPeriodSchema
|
|
16338
|
+
});
|
|
16339
|
+
var BillingCheckoutRequestSchema = BillingPlanSelectionSchema.extend({
|
|
16340
|
+
successUrl: external_exports.url().optional(),
|
|
16341
|
+
cancelUrl: external_exports.url().optional()
|
|
16342
|
+
});
|
|
16343
|
+
var BillingCheckoutResponseSchema = external_exports.object({ url: external_exports.string() });
|
|
16344
|
+
var BillingChangeResponseSchema = external_exports.object({
|
|
16345
|
+
ok: external_exports.literal(true),
|
|
16346
|
+
requiredActionUrl: external_exports.string().optional()
|
|
16347
|
+
});
|
|
16348
|
+
var BillingCancelResponseSchema = external_exports.object({ ok: external_exports.literal(true) });
|
|
16349
|
+
var BillingTrialRequestSchema = external_exports.object({ planKey: external_exports.string().min(1) });
|
|
16350
|
+
var billingSubscriptionStatuses = BillingSubscriptionStatusSchema.options;
|
|
16351
|
+
var billingSources = BillingSourceSchema.options;
|
|
16352
|
+
var billingPeriods = BillingPeriodSchema.options;
|
|
16353
|
+
|
|
16354
|
+
// ../src/contracts/catalog.ts
|
|
16355
|
+
var NO_PLAN = "No billing plan resolves for the account.";
|
|
16356
|
+
var APP_AUTH_RATE_LIMITED = "Too many application authentication requests from this network address for this application. Carries Retry-After.";
|
|
16357
|
+
var NO_END_USERS = "This application identifies no end users, so there is no per-user standing to report. Answered with auth_method_not_supported: the request was well-formed, and nothing the caller can rephrase would make it work.";
|
|
16358
|
+
var DATA_PLANE_RATE_LIMITED = "Either the app's own limits or the account's request allowance refused the request. The app's limits are checked first and answer app_rate_limited or app_budget_exhausted, carrying scope; the plan allowance answers billing_request_quota_exceeded with limit, used, and the UTC resetAt. Every one of them carries a Retry-After header.";
|
|
16359
|
+
var ISSUER_ERRORS = {
|
|
16360
|
+
403: "`issuer_token_rejected` \u2014 the token did not verify; get a fresh one, retry once, then fail. `issuer_claims_missing` \u2014 the token is valid but a required entitlement claim has not propagated yet; do not re-authenticate, wait and retry. `auth_required` \u2014 the key was refused or the user is blocked. `attest_failed` \u2014 the App Attest proof did not hold.",
|
|
16361
|
+
503: "`issuer_verification_unavailable` \u2014 the gateway could not reach or read the issuer's keys, so the token was never judged. Retry with backoff."
|
|
16362
|
+
};
|
|
16363
|
+
var CLI_ERRORS = {
|
|
16364
|
+
409: "A conflicting transition or setup cap prevents this operation.",
|
|
16365
|
+
410: "The protected credential exchange has expired; no replacement is minted.",
|
|
16366
|
+
429: "The durable initiation or submission rate limit was reached."
|
|
16367
|
+
};
|
|
16368
|
+
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. Identity approval also requires an interactive human session; registration is limited to a valid pending claim. Provider secret values are write-only.";
|
|
16369
|
+
var SignUpRequestSchema = external_exports.object({
|
|
16370
|
+
name: external_exports.string(),
|
|
16371
|
+
email: external_exports.email(),
|
|
16372
|
+
password: external_exports.string().min(8)
|
|
16373
|
+
});
|
|
16374
|
+
var SignInRequestSchema = external_exports.object({ email: external_exports.email(), password: external_exports.string() });
|
|
16375
|
+
var ProviderNativeBodySchema = external_exports.record(external_exports.string(), external_exports.unknown()).meta({
|
|
16376
|
+
description: "Provider-native JSON request. Consult the selected provider's API reference for the exact shape."
|
|
16377
|
+
});
|
|
16378
|
+
var ResponsesStyleBodySchema = external_exports.record(external_exports.string(), external_exports.unknown()).meta({
|
|
16379
|
+
description: "OpenAI Responses API body for endpoints whose api_style is responses. The gateway overwrites model and deep-merges the configured params."
|
|
16380
|
+
});
|
|
16381
|
+
var TranscriptionStyleBodySchema = external_exports.object({
|
|
16382
|
+
file: external_exports.string().meta({ format: "binary" }),
|
|
16383
|
+
model: external_exports.string().optional().meta({ description: "Ignored; the gateway sets the configured model." }),
|
|
16384
|
+
prompt: external_exports.string().optional(),
|
|
16385
|
+
language: external_exports.string().optional(),
|
|
16386
|
+
response_format: external_exports.string().optional()
|
|
16387
|
+
}).meta({ description: "Body for endpoints whose api_style is transcription." });
|
|
16388
|
+
var GatewayClientHeadersSchema = external_exports.object({
|
|
16389
|
+
"x-app-version": external_exports.string().optional().meta({
|
|
16390
|
+
description: "Required for gateway-token clients; optional for issuer-less API-key clients."
|
|
16391
|
+
}),
|
|
16392
|
+
"x-end-user-id": external_exports.string().optional().meta({
|
|
16393
|
+
description: "Optional configured end-user identity for issuer-less API-key applications."
|
|
16394
|
+
})
|
|
16395
|
+
});
|
|
16396
|
+
var MonthQuerySchema = external_exports.object({
|
|
16397
|
+
month: MonthSchema.optional().meta({ description: "YYYY-MM; defaults to the current UTC month." })
|
|
16398
|
+
});
|
|
16399
|
+
var DaySchema = (name) => external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/, { error: `${name} must use YYYY-MM-DD format` });
|
|
16400
|
+
var RangeQuerySchema = external_exports.object({
|
|
16401
|
+
from: DaySchema("from").optional().meta({ description: "Inclusive YYYY-MM-DD start; defaults to 29 days before `to`." }),
|
|
16402
|
+
to: DaySchema("to").optional().meta({ description: "Inclusive YYYY-MM-DD end; defaults to today." })
|
|
16403
|
+
});
|
|
16404
|
+
var PageLimitSchema = external_exports.coerce.number().int({ error: "limit must be an integer between 1 and 200" }).min(1, { error: "limit must be an integer between 1 and 200" }).max(200, { error: "limit must be an integer between 1 and 200" }).default(50);
|
|
16405
|
+
var BeforeIdSchema = external_exports.coerce.number().int({ error: "before_id must be a positive integer" }).positive({ error: "before_id must be a positive integer" }).optional().meta({ description: "Page backwards: only events older than this id." });
|
|
16406
|
+
var APP_PARAM = { app: { example: "my-app" } };
|
|
16407
|
+
var NO_RESPONSE_BODY = external_exports.never();
|
|
16408
|
+
var CATALOG = {
|
|
16409
|
+
getHealth: {
|
|
16410
|
+
method: "GET",
|
|
16411
|
+
path: "/v1/healthz",
|
|
16412
|
+
tags: ["Operations"],
|
|
16413
|
+
summary: "Check gateway health",
|
|
16414
|
+
security: "public",
|
|
16415
|
+
response: HealthResponseSchema,
|
|
16416
|
+
responseDescription: "The Worker is accepting requests.",
|
|
16417
|
+
errors: "none"
|
|
16418
|
+
},
|
|
16419
|
+
getConsoleCapabilities: {
|
|
16420
|
+
method: "GET",
|
|
16421
|
+
path: "/v1/console/capabilities",
|
|
16422
|
+
tags: ["Operations"],
|
|
16423
|
+
summary: "Discover optional deployment capabilities",
|
|
16424
|
+
security: "public",
|
|
16425
|
+
response: ConsoleCapabilitiesResponseSchema,
|
|
16426
|
+
responseDescription: "Capabilities the console adapts to.",
|
|
16427
|
+
errors: "none"
|
|
16428
|
+
},
|
|
16429
|
+
signUp: {
|
|
16430
|
+
method: "POST",
|
|
16431
|
+
path: "/v1/auth/sign-up/email",
|
|
16432
|
+
tags: ["Console authentication"],
|
|
16433
|
+
summary: "Create an account",
|
|
16434
|
+
security: "public",
|
|
16435
|
+
request: SignUpRequestSchema,
|
|
16436
|
+
response: external_exports.unknown(),
|
|
16437
|
+
responseDescription: "Authenticated session."
|
|
16438
|
+
},
|
|
16439
|
+
// Only the password route is throttled by the gateway, so the two console
|
|
16440
|
+
// authentication routes do not document the same responses.
|
|
16441
|
+
signIn: {
|
|
16442
|
+
method: "POST",
|
|
16443
|
+
path: "/v1/auth/sign-in/email",
|
|
16444
|
+
tags: ["Console authentication"],
|
|
16445
|
+
summary: "Sign in with email and password",
|
|
16446
|
+
security: "public",
|
|
16447
|
+
request: SignInRequestSchema,
|
|
16448
|
+
response: external_exports.unknown(),
|
|
16449
|
+
responseDescription: "Authenticated session.",
|
|
16450
|
+
errors: {
|
|
16451
|
+
429: "Too many sign-in attempts. Counted twice over: per client address, and per email address so that a spread of addresses cannot grind through one account's passwords. Carries Retry-After."
|
|
16452
|
+
}
|
|
16453
|
+
},
|
|
16454
|
+
getSession: {
|
|
16455
|
+
method: "GET",
|
|
16456
|
+
path: "/v1/auth/get-session",
|
|
16457
|
+
tags: ["Console authentication"],
|
|
16458
|
+
summary: "Get the current session",
|
|
16459
|
+
security: "session",
|
|
16460
|
+
response: external_exports.unknown(),
|
|
16461
|
+
responseDescription: "Current session or null."
|
|
16462
|
+
},
|
|
16463
|
+
signOut: {
|
|
16464
|
+
method: "POST",
|
|
16465
|
+
path: "/v1/auth/sign-out",
|
|
16466
|
+
tags: ["Console authentication"],
|
|
16467
|
+
summary: "End the current session",
|
|
16468
|
+
security: "session",
|
|
16469
|
+
response: external_exports.unknown(),
|
|
16470
|
+
responseDescription: "Session ended."
|
|
16471
|
+
},
|
|
16472
|
+
signInWithGoogle: {
|
|
16473
|
+
method: "GET",
|
|
16474
|
+
path: "/v1/auth/sign-in/social",
|
|
16475
|
+
tags: ["Console authentication"],
|
|
16476
|
+
summary: "Start optional Google sign-in",
|
|
16477
|
+
description: "Available only when GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET are configured.",
|
|
16478
|
+
security: "public",
|
|
16479
|
+
query: external_exports.object({ provider: external_exports.literal("google") }),
|
|
16480
|
+
status: 302,
|
|
16481
|
+
response: NO_RESPONSE_BODY,
|
|
16482
|
+
responseDescription: "Redirect to Google."
|
|
16483
|
+
},
|
|
16484
|
+
createAppAttestChallenge: {
|
|
16485
|
+
method: "POST",
|
|
16486
|
+
path: "/v1/apps/{app}/auth/challenge",
|
|
16487
|
+
tags: ["Application authentication"],
|
|
16488
|
+
summary: "Create an App Attest challenge",
|
|
16489
|
+
security: "public",
|
|
16490
|
+
params: APP_PARAM,
|
|
16491
|
+
response: AppAttestChallengeResponseSchema,
|
|
16492
|
+
responseDescription: "A five-minute, single-use challenge.",
|
|
16493
|
+
errors: { 402: NO_PLAN, 429: APP_AUTH_RATE_LIMITED, 404: NO_END_USERS }
|
|
16494
|
+
},
|
|
16495
|
+
registerAppAttestKey: {
|
|
16496
|
+
method: "POST",
|
|
16497
|
+
path: "/v1/apps/{app}/auth/register",
|
|
16498
|
+
tags: ["Application authentication"],
|
|
16499
|
+
summary: "Register an App Attest key",
|
|
16500
|
+
security: "public",
|
|
16501
|
+
params: APP_PARAM,
|
|
16502
|
+
request: AppAttestRegisterRequestSchema,
|
|
16503
|
+
response: AppAttestRegisterResponseSchema,
|
|
16504
|
+
responseDescription: "The key was registered for the verified issuer identity.",
|
|
16505
|
+
errors: { ...ISSUER_ERRORS, 402: NO_PLAN, 429: APP_AUTH_RATE_LIMITED }
|
|
16506
|
+
},
|
|
16507
|
+
exchangeGatewayToken: {
|
|
16508
|
+
method: "POST",
|
|
16509
|
+
path: "/v1/apps/{app}/auth/token",
|
|
16510
|
+
tags: ["Application authentication"],
|
|
16511
|
+
summary: "Exchange issuer identity and client proof for a gateway token",
|
|
16512
|
+
description: "App Attest clients send key_id, assertion, and challenge. Issuer-backed API-key clients send api_key and issuer_token. The verified issuer claim is always the resulting user identity.",
|
|
16513
|
+
security: "public",
|
|
16514
|
+
params: APP_PARAM,
|
|
16515
|
+
request: external_exports.union([AppAttestTokenRequestSchema, ApiKeyTokenRequestSchema]),
|
|
16516
|
+
response: GatewayTokenResponseSchema,
|
|
16517
|
+
responseDescription: "A short-lived gateway access token.",
|
|
16518
|
+
errors: { ...ISSUER_ERRORS, 402: NO_PLAN, 429: APP_AUTH_RATE_LIMITED }
|
|
16519
|
+
},
|
|
16520
|
+
getCurrentUser: {
|
|
16521
|
+
method: "GET",
|
|
16522
|
+
path: "/v1/apps/{app}/me",
|
|
16523
|
+
tags: ["Application"],
|
|
16524
|
+
summary: "Get the current user's limits, spend and block state",
|
|
16525
|
+
security: "gateway",
|
|
16526
|
+
params: APP_PARAM,
|
|
16527
|
+
response: CurrentUserResponseSchema,
|
|
16528
|
+
responseDescription: "Current user state.",
|
|
16529
|
+
errors: { 402: NO_PLAN, 404: NO_END_USERS }
|
|
16530
|
+
},
|
|
16531
|
+
proxyProviderRequest: {
|
|
16532
|
+
method: "POST",
|
|
16533
|
+
path: "/v1/apps/{app}/proxy/{provider}/{path}",
|
|
16534
|
+
tags: ["Provider proxy"],
|
|
16535
|
+
summary: "Proxy a provider-native model request",
|
|
16536
|
+
description: "The path, body, and successful response retain the selected provider's native contract. For example, OpenAI clients send v1/responses or v1/chat/completions; gateway-specific provider slug quirks are never part of the client path. The gateway validates the configured path and model, spends one request from the account's monthly allowance, and streams the upstream response without buffering.",
|
|
16537
|
+
security: "gateway",
|
|
16538
|
+
params: {
|
|
16539
|
+
app: { example: "my-app" },
|
|
16540
|
+
provider: { example: "openai-dev", pattern: PROVIDER_SLUG_PATTERN },
|
|
16541
|
+
path: {
|
|
16542
|
+
description: "The provider's native API path verbatim, without a leading slash.",
|
|
16543
|
+
example: "v1/responses"
|
|
16544
|
+
}
|
|
16545
|
+
},
|
|
16546
|
+
headers: GatewayClientHeadersSchema,
|
|
16547
|
+
request: ProviderNativeBodySchema,
|
|
16548
|
+
response: external_exports.unknown(),
|
|
16549
|
+
responseDescription: "Provider-native response. Streaming responses remain streamed.",
|
|
16550
|
+
errors: {
|
|
16551
|
+
402: NO_PLAN,
|
|
16552
|
+
429: DATA_PLANE_RATE_LIMITED,
|
|
16553
|
+
502: "The upstream provider request failed.",
|
|
16554
|
+
504: "The upstream provider sent no response headers within the gateway's time-to-first-byte budget."
|
|
16555
|
+
}
|
|
16556
|
+
},
|
|
16557
|
+
callNamedEndpoint: {
|
|
16558
|
+
method: "POST",
|
|
16559
|
+
path: "/v1/apps/{app}/endpoints/{slug}",
|
|
16560
|
+
tags: ["Named endpoints"],
|
|
16561
|
+
summary: "Call a server-configured named endpoint",
|
|
16562
|
+
description: "The endpoint's provider, model, fixed parameters, output cap, and fallback chain come from the application configuration, so you can change models without shipping a client release. Responses-style endpoints accept an OpenAI Responses body; transcription-style endpoints accept an OpenAI audio transcription multipart body and may omit the model field. The successful response keeps the serving provider's native format and streaming behaviour.",
|
|
16563
|
+
security: "gateway",
|
|
16564
|
+
params: {
|
|
16565
|
+
app: { example: "my-app" },
|
|
16566
|
+
slug: { example: "chat", pattern: /^[a-z0-9-]{1,64}$/ }
|
|
16567
|
+
},
|
|
16568
|
+
headers: GatewayClientHeadersSchema,
|
|
16569
|
+
request: {
|
|
16570
|
+
content: {
|
|
16571
|
+
"application/json": ResponsesStyleBodySchema,
|
|
16572
|
+
"multipart/form-data": TranscriptionStyleBodySchema
|
|
16573
|
+
}
|
|
16574
|
+
},
|
|
16575
|
+
response: external_exports.unknown(),
|
|
16576
|
+
responseDescription: "Provider-native response. Streaming responses remain streamed.",
|
|
16577
|
+
errors: {
|
|
16578
|
+
402: NO_PLAN,
|
|
16579
|
+
429: DATA_PLANE_RATE_LIMITED,
|
|
16580
|
+
502: "Every configured target failed.",
|
|
16581
|
+
504: "Every configured target failed, and the last one sent no response headers within the gateway's time-to-first-byte budget."
|
|
16582
|
+
}
|
|
16583
|
+
},
|
|
16584
|
+
listApps: {
|
|
16585
|
+
method: "GET",
|
|
16586
|
+
path: "/v1/admin/apps",
|
|
16587
|
+
tags: ["Admin applications"],
|
|
16588
|
+
summary: "List applications",
|
|
16589
|
+
security: "management",
|
|
16590
|
+
query: MonthQuerySchema,
|
|
16591
|
+
response: AppListResponseSchema,
|
|
16592
|
+
responseDescription: "Applications and current usage summaries."
|
|
16593
|
+
},
|
|
16594
|
+
createApp: {
|
|
16595
|
+
method: "POST",
|
|
16596
|
+
path: "/v1/admin/apps",
|
|
16597
|
+
tags: ["Admin applications"],
|
|
16598
|
+
summary: "Create an application",
|
|
16599
|
+
description: "Send only `name`, `config` and an optional `status`. The gateway assigns the id \u2014 the name slugified plus a six-character random suffix \u2014 and returns it as `app.id`; it cannot be chosen, and it cannot change once the application exists. A body that still carries `id` is refused with `400`. API-key applications receive a one-time plaintext initial key in the response.",
|
|
16600
|
+
security: "management",
|
|
16601
|
+
request: AppWriteSchema,
|
|
16602
|
+
status: 201,
|
|
16603
|
+
response: CreatedAppResponseSchema,
|
|
16604
|
+
responseDescription: "Application created. `app.id` is the assigned id and the segment every gateway URL for this app uses. `api_key` is the one-time plaintext initial key for an API-key application, and null for any other.",
|
|
16605
|
+
errors: {
|
|
16606
|
+
409: "`invalid_request` \u2014 no unique generated id could be allocated after repeated attempts. Retrying is safe."
|
|
16607
|
+
},
|
|
16608
|
+
receipt: true
|
|
16609
|
+
},
|
|
16610
|
+
listBillingPlans: {
|
|
16611
|
+
method: "GET",
|
|
16612
|
+
path: "/v1/admin/billing/plans",
|
|
16613
|
+
hidden: true,
|
|
16614
|
+
tags: ["Admin billing"],
|
|
16615
|
+
summary: "List billing plans",
|
|
16616
|
+
security: "management",
|
|
16617
|
+
// The whole billing subtree is a person's to act on: a service credential
|
|
16618
|
+
// may run an account but may not buy, change or cancel what pays for it.
|
|
16619
|
+
policy: { identity: "human" },
|
|
16620
|
+
response: BillingPlansResponseSchema,
|
|
16621
|
+
responseDescription: "Billing service response."
|
|
16622
|
+
},
|
|
16623
|
+
getBillingStatus: {
|
|
16624
|
+
method: "GET",
|
|
16625
|
+
path: "/v1/admin/billing/status",
|
|
16626
|
+
hidden: true,
|
|
16627
|
+
tags: ["Admin billing"],
|
|
16628
|
+
summary: "Get billing access and the current allowance period",
|
|
16629
|
+
security: "management",
|
|
16630
|
+
policy: { identity: "human" },
|
|
16631
|
+
response: BillingStatusResponseSchema,
|
|
16632
|
+
responseDescription: "Billing access, and the current period against the plan's request allowance when an entitlement resolves."
|
|
16633
|
+
},
|
|
16634
|
+
startCheckout: {
|
|
16635
|
+
method: "POST",
|
|
16636
|
+
path: "/v1/admin/billing/checkout",
|
|
16637
|
+
hidden: true,
|
|
16638
|
+
tags: ["Admin billing"],
|
|
16639
|
+
summary: "Create a hosted checkout",
|
|
16640
|
+
security: "management",
|
|
16641
|
+
policy: { identity: "human" },
|
|
16642
|
+
request: BillingCheckoutRequestSchema,
|
|
16643
|
+
response: BillingCheckoutResponseSchema,
|
|
16644
|
+
responseDescription: "Hosted checkout URL."
|
|
16645
|
+
},
|
|
16646
|
+
changePlan: {
|
|
16647
|
+
method: "POST",
|
|
16648
|
+
path: "/v1/admin/billing/change",
|
|
16649
|
+
hidden: true,
|
|
16650
|
+
tags: ["Admin billing"],
|
|
16651
|
+
summary: "Change the subscription plan",
|
|
16652
|
+
security: "management",
|
|
16653
|
+
policy: { identity: "human" },
|
|
16654
|
+
request: BillingPlanSelectionSchema,
|
|
16655
|
+
response: BillingChangeResponseSchema,
|
|
16656
|
+
responseDescription: "Billing service response."
|
|
16657
|
+
},
|
|
16658
|
+
resumeSubscription: {
|
|
16659
|
+
method: "POST",
|
|
16660
|
+
path: "/v1/admin/billing/resume",
|
|
16661
|
+
hidden: true,
|
|
16662
|
+
tags: ["Admin billing"],
|
|
16663
|
+
summary: "Resume a canceled subscription",
|
|
16664
|
+
security: "management",
|
|
16665
|
+
policy: { identity: "human" },
|
|
16666
|
+
request: BillingPlanSelectionSchema,
|
|
16667
|
+
response: BillingChangeResponseSchema,
|
|
16668
|
+
responseDescription: "Billing service response."
|
|
16669
|
+
},
|
|
16670
|
+
cancelSubscription: {
|
|
16671
|
+
method: "POST",
|
|
16672
|
+
path: "/v1/admin/billing/cancel",
|
|
16673
|
+
hidden: true,
|
|
16674
|
+
tags: ["Admin billing"],
|
|
16675
|
+
summary: "Cancel the subscription at period end",
|
|
16676
|
+
security: "management",
|
|
16677
|
+
policy: { identity: "human" },
|
|
16678
|
+
response: BillingCancelResponseSchema,
|
|
16679
|
+
responseDescription: "Cancellation accepted."
|
|
16680
|
+
},
|
|
16681
|
+
startTrial: {
|
|
16682
|
+
method: "POST",
|
|
16683
|
+
path: "/v1/admin/billing/trial",
|
|
16684
|
+
hidden: true,
|
|
16685
|
+
tags: ["Admin billing"],
|
|
16686
|
+
summary: "Start a no-card trial",
|
|
16687
|
+
security: "management",
|
|
16688
|
+
policy: { identity: "human" },
|
|
16689
|
+
request: BillingTrialRequestSchema,
|
|
16690
|
+
response: BillingAccessSchema,
|
|
16691
|
+
responseDescription: "Trial access state."
|
|
16692
|
+
},
|
|
16693
|
+
getApp: {
|
|
16694
|
+
method: "GET",
|
|
16695
|
+
path: "/v1/admin/apps/{app}",
|
|
16696
|
+
tags: ["Admin applications"],
|
|
16697
|
+
summary: "Get an application",
|
|
16698
|
+
security: "management",
|
|
16699
|
+
params: APP_PARAM,
|
|
16700
|
+
response: AppResponseSchema,
|
|
16701
|
+
responseDescription: "Application state."
|
|
16702
|
+
},
|
|
16703
|
+
updateApp: {
|
|
16704
|
+
method: "PUT",
|
|
16705
|
+
path: "/v1/admin/apps/{app}",
|
|
16706
|
+
tags: ["Admin applications"],
|
|
16707
|
+
summary: "Update an application",
|
|
16708
|
+
description: "Requires `revision` in the body, the one the application was read at; a stale revision answers `409 app_revision_conflict` and an absent one `400 app_revision_required`. Updates an existing application in place. It never creates one: an id none of your applications holds answers `404 app_not_found`, and nothing is written. Applications are created only by `POST /v1/admin/apps`, which assigns the id.",
|
|
16709
|
+
security: "management",
|
|
16710
|
+
params: APP_PARAM,
|
|
16711
|
+
request: AppUpdateSchema,
|
|
16712
|
+
response: AppResponseSchema,
|
|
16713
|
+
responseDescription: "Application state.",
|
|
16714
|
+
errors: { 409: "The application changed since it was read." }
|
|
16715
|
+
},
|
|
16716
|
+
validateApp: {
|
|
16717
|
+
method: "POST",
|
|
16718
|
+
path: "/v1/admin/apps/{app}/validate",
|
|
16719
|
+
tags: ["Admin applications"],
|
|
16720
|
+
summary: "Validate an edit of an application without saving it",
|
|
16721
|
+
description: "Judged as an update of this application would be: provider slugs its stored configuration already names stay acceptable even if their provider has since been deleted. Use the draft validation for an application that does not exist yet.",
|
|
16722
|
+
security: "management",
|
|
16723
|
+
// A POST that stores nothing: it answers whether a body would be accepted,
|
|
16724
|
+
// which is a read of the configuration rules and not a write.
|
|
16725
|
+
policy: { role: "member", access: "read" },
|
|
16726
|
+
params: APP_PARAM,
|
|
16727
|
+
request: AppWriteSchema,
|
|
16728
|
+
response: AppValidateResponseSchema,
|
|
16729
|
+
responseDescription: "Resolved valid configuration."
|
|
16730
|
+
},
|
|
16731
|
+
validateAppDraft: {
|
|
16732
|
+
method: "POST",
|
|
16733
|
+
path: "/v1/admin/app-drafts/validate",
|
|
16734
|
+
tags: ["Admin applications"],
|
|
16735
|
+
summary: "Validate a new application configuration without saving it",
|
|
16736
|
+
description: "Judged as a creation would be, against the account's current providers and prices.",
|
|
16737
|
+
security: "management",
|
|
16738
|
+
policy: { role: "member", access: "read" },
|
|
16739
|
+
request: AppWriteSchema,
|
|
16740
|
+
response: AppDraftValidateResponseSchema,
|
|
16741
|
+
responseDescription: "The configuration would be accepted."
|
|
16742
|
+
},
|
|
16743
|
+
deleteApp: {
|
|
16744
|
+
method: "DELETE",
|
|
16745
|
+
path: "/v1/admin/apps/{app}",
|
|
16746
|
+
tags: ["Admin applications"],
|
|
16747
|
+
summary: "Delete an application and its associated operational data",
|
|
16748
|
+
security: "management",
|
|
16749
|
+
params: APP_PARAM,
|
|
16750
|
+
query: external_exports.object({
|
|
16751
|
+
confirm: external_exports.string({ error: "Pass ?confirm=<app-id> to delete an app" })
|
|
16752
|
+
}),
|
|
16753
|
+
response: AppDeleteResponseSchema,
|
|
16754
|
+
responseDescription: "Application deleted. Its usage events are kept, which is what `usage_events_retained` reports."
|
|
16755
|
+
},
|
|
16756
|
+
listManagementKeys: {
|
|
16757
|
+
method: "GET",
|
|
16758
|
+
path: "/v1/admin/keys",
|
|
16759
|
+
tags: ["Admin management keys"],
|
|
16760
|
+
summary: "List management keys",
|
|
16761
|
+
description: "Console session only. Management keys cannot administer management keys.",
|
|
16762
|
+
security: "session",
|
|
16763
|
+
response: ManagementKeyListResponseSchema,
|
|
16764
|
+
responseDescription: "Management key metadata without plaintext tokens."
|
|
16765
|
+
},
|
|
16766
|
+
createManagementKey: {
|
|
16767
|
+
method: "POST",
|
|
16768
|
+
path: "/v1/admin/keys",
|
|
16769
|
+
tags: ["Admin management keys"],
|
|
16770
|
+
summary: "Create a management key",
|
|
16771
|
+
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.",
|
|
16772
|
+
security: "session",
|
|
16773
|
+
request: ManagementKeyCreateRequestSchema,
|
|
16774
|
+
status: 201,
|
|
16775
|
+
response: CreatedManagementKeyResponseSchema,
|
|
16776
|
+
responseDescription: "One-time plaintext management key."
|
|
16777
|
+
},
|
|
16778
|
+
revokeManagementKey: {
|
|
16779
|
+
method: "POST",
|
|
16780
|
+
path: "/v1/admin/keys/{id}/revoke",
|
|
16781
|
+
tags: ["Admin management keys"],
|
|
16782
|
+
summary: "Revoke a management key",
|
|
16783
|
+
description: "Console session only, and requires the owner or admin role.",
|
|
16784
|
+
security: "session",
|
|
16785
|
+
params: { id: { example: "key_123" } },
|
|
16786
|
+
response: ManagementKeyResponseSchema,
|
|
16787
|
+
responseDescription: "Revoked management key metadata."
|
|
16788
|
+
},
|
|
16789
|
+
listProviders: {
|
|
16790
|
+
method: "GET",
|
|
16791
|
+
path: "/v1/admin/providers",
|
|
16792
|
+
tags: ["Admin providers"],
|
|
16793
|
+
summary: "List provider credentials",
|
|
16794
|
+
security: "management",
|
|
16795
|
+
response: ProviderListResponseSchema,
|
|
16796
|
+
responseDescription: "Provider metadata without credentials."
|
|
16797
|
+
},
|
|
16798
|
+
createProvider: {
|
|
16799
|
+
method: "POST",
|
|
16800
|
+
path: "/v1/admin/providers",
|
|
16801
|
+
tags: ["Admin providers"],
|
|
16802
|
+
summary: "Store a provider credential",
|
|
16803
|
+
description: "Creates one named provider instance. Supply exactly one direct provider secret or reusable providerGatewayId. The credential is stored as given and never probed: check one first with POST /v1/admin/providers/test. The slug defaults to the provider type and is unique among your provider instances, disabled ones included; only deleting an instance frees its slug.",
|
|
16804
|
+
security: "management",
|
|
16805
|
+
request: ProviderCreateRequestSchema,
|
|
16806
|
+
status: 201,
|
|
16807
|
+
response: ProviderResponseSchema,
|
|
16808
|
+
responseDescription: "Stored provider.",
|
|
16809
|
+
errors: { 409: "The requested active provider slug is already in use." },
|
|
16810
|
+
receipt: true
|
|
16811
|
+
},
|
|
16812
|
+
testProviderCredential: {
|
|
16813
|
+
method: "POST",
|
|
16814
|
+
path: "/v1/admin/providers/test",
|
|
16815
|
+
tags: ["Admin providers"],
|
|
16816
|
+
summary: "Probe a provider credential without storing it",
|
|
16817
|
+
description: "Calls the provider with a credential, and reports what it answered. Nothing is stored, and no other endpoint runs this check: a write stores what it is given. Supply exactly one direct provider secret or an existing providerGatewayId.",
|
|
16818
|
+
security: "management",
|
|
16819
|
+
request: ProviderTestRequestSchema,
|
|
16820
|
+
response: ProviderTestResponseSchema,
|
|
16821
|
+
responseDescription: "Probe outcome."
|
|
16822
|
+
},
|
|
16823
|
+
updateProvider: {
|
|
16824
|
+
method: "PUT",
|
|
16825
|
+
path: "/v1/admin/providers/{id}",
|
|
16826
|
+
tags: ["Admin providers"],
|
|
16827
|
+
summary: "Rotate a credential, rename it, move it to another origin, replace its custom pricing, or disable it",
|
|
16828
|
+
description: "Requires the revision returned by the provider read this edit is based on; a stale revision returns 409 conflict. Sending status disables or re-enables the instance. Disabling keeps the secret, the pricing and the slug, so requests to it fail with provider_disabled and no other instance can take its slug meanwhile. Re-enabling does not require reclaiming the slug. Sending a non-null baseUrl also requires secret in the same request: the stored key is write-only and is never decrypted to be sent to an origin it has not been sent to before, so a move carries the key it is to be used with. Sending baseUrl: null returns the instance to its provider type's own origin and needs nothing else.",
|
|
16829
|
+
security: "management",
|
|
16830
|
+
params: { id: { example: "b0a1\u2026" } },
|
|
16831
|
+
request: ProviderUpdateRequestSchema,
|
|
16832
|
+
response: ProviderResponseSchema,
|
|
16833
|
+
responseDescription: "Updated provider."
|
|
16834
|
+
},
|
|
16835
|
+
deleteProvider: {
|
|
16836
|
+
method: "DELETE",
|
|
16837
|
+
path: "/v1/admin/providers/{id}",
|
|
16838
|
+
tags: ["Admin providers"],
|
|
16839
|
+
summary: "Delete a provider credential and its custom pricing",
|
|
16840
|
+
description: 'A hard delete, secret and pricing included. Applications using this provider start failing with provider_not_configured within a minute. To pause an instance reversibly instead, send status: "disabled" to PUT /v1/admin/providers/{id}.',
|
|
16841
|
+
security: "management",
|
|
16842
|
+
params: { id: { example: "b0a1\u2026" } },
|
|
16843
|
+
response: ProviderDeleteResponseSchema,
|
|
16844
|
+
responseDescription: "Provider deleted."
|
|
16845
|
+
},
|
|
16846
|
+
listProviderGateways: {
|
|
16847
|
+
method: "GET",
|
|
16848
|
+
path: "/v1/admin/provider-gateways",
|
|
16849
|
+
tags: ["Admin provider gateways"],
|
|
16850
|
+
summary: "List reusable provider gateways",
|
|
16851
|
+
security: "management",
|
|
16852
|
+
response: ProviderGatewayListResponseSchema,
|
|
16853
|
+
responseDescription: "Provider gateway metadata without tokens."
|
|
16854
|
+
},
|
|
16855
|
+
createProviderGateway: {
|
|
16856
|
+
method: "POST",
|
|
16857
|
+
path: "/v1/admin/provider-gateways",
|
|
16858
|
+
tags: ["Admin provider gateways"],
|
|
16859
|
+
summary: "Create a reusable provider gateway connection",
|
|
16860
|
+
description: "Cloudflare AI Gateway takes an account and gateway id; Vercel AI Gateway takes only a name and a token. The token is encrypted and stored as given, never probed: check a connection first with POST /v1/admin/provider-gateways/test. Provider instances are attached separately through the providers API.",
|
|
16861
|
+
security: "management",
|
|
16862
|
+
request: ProviderGatewayCreateRequestSchema,
|
|
16863
|
+
status: 201,
|
|
16864
|
+
response: ProviderGatewayResponseSchema,
|
|
16865
|
+
responseDescription: "Created provider gateway.",
|
|
16866
|
+
receipt: true
|
|
16867
|
+
},
|
|
16868
|
+
testProviderGateway: {
|
|
16869
|
+
method: "POST",
|
|
16870
|
+
path: "/v1/admin/provider-gateways/test",
|
|
16871
|
+
tags: ["Admin provider gateways"],
|
|
16872
|
+
summary: "Probe a gateway connection without storing it",
|
|
16873
|
+
description: "Calls the gateway with a token, and reports what it answered. Nothing is stored, and no other endpoint runs this check: a write stores what it is given. Unlike the providers API, a refused token is reported as reason: rejected rather than raised as provider_key_invalid, because the same 401 means both a wrong token and a gateway that is not finished being set up.",
|
|
16874
|
+
security: "management",
|
|
16875
|
+
request: ProviderGatewayTestRequestSchema,
|
|
16876
|
+
response: ProviderGatewayTestResponseSchema,
|
|
16877
|
+
responseDescription: "Probe outcome."
|
|
16878
|
+
},
|
|
16879
|
+
updateProviderGateway: {
|
|
16880
|
+
method: "PATCH",
|
|
16881
|
+
path: "/v1/admin/provider-gateways/{id}",
|
|
16882
|
+
tags: ["Admin provider gateways"],
|
|
16883
|
+
summary: "Rename a provider gateway",
|
|
16884
|
+
description: "Requires the revision returned by the gateway read this rename is based on; a stale revision returns 409 conflict.",
|
|
16885
|
+
security: "management",
|
|
16886
|
+
params: { id: { example: "b0a1\u2026" } },
|
|
16887
|
+
request: ProviderGatewayUpdateRequestSchema,
|
|
16888
|
+
response: ProviderGatewayResponseSchema,
|
|
16889
|
+
responseDescription: "Updated provider gateway."
|
|
16890
|
+
},
|
|
16891
|
+
rotateProviderGateway: {
|
|
16892
|
+
method: "POST",
|
|
16893
|
+
path: "/v1/admin/provider-gateways/{id}/rotate",
|
|
16894
|
+
tags: ["Admin provider gateways"],
|
|
16895
|
+
summary: "Rotate a shared provider gateway token",
|
|
16896
|
+
description: "Requires the revision returned by the gateway read this rotation is based on; a stale revision returns 409 conflict. Re-encrypts the token once for every provider instance referencing this gateway. The new token is stored as given, never probed.",
|
|
16897
|
+
security: "management",
|
|
16898
|
+
params: { id: { example: "b0a1\u2026" } },
|
|
16899
|
+
request: ProviderGatewayRotateRequestSchema,
|
|
16900
|
+
response: ProviderGatewayResponseSchema,
|
|
16901
|
+
responseDescription: "Rotated provider gateway."
|
|
16902
|
+
},
|
|
16903
|
+
deleteProviderGateway: {
|
|
16904
|
+
method: "DELETE",
|
|
16905
|
+
path: "/v1/admin/provider-gateways/{id}",
|
|
16906
|
+
tags: ["Admin provider gateways"],
|
|
16907
|
+
summary: "Delete an unused provider gateway",
|
|
16908
|
+
security: "management",
|
|
16909
|
+
params: { id: { example: "b0a1\u2026" } },
|
|
16910
|
+
response: ProviderGatewayDeleteResponseSchema,
|
|
16911
|
+
responseDescription: "Provider gateway deleted.",
|
|
16912
|
+
errors: {
|
|
16913
|
+
409: "Provider instances still reference this gateway. Disabled rows are retained for re-enabling and block deletion too; see referencedCount."
|
|
16914
|
+
}
|
|
16915
|
+
},
|
|
16916
|
+
getAdminSession: {
|
|
16917
|
+
method: "GET",
|
|
16918
|
+
path: "/v1/admin/session",
|
|
16919
|
+
tags: ["Admin organizations"],
|
|
16920
|
+
summary: "Get the caller's identity, current organization and role",
|
|
16921
|
+
description: "The console needs the caller's role and active organization to gate its UI; the sign-in session endpoint reports neither.",
|
|
16922
|
+
security: "management",
|
|
16923
|
+
response: IdentitySessionSchema,
|
|
16924
|
+
responseDescription: "Resolved session."
|
|
16925
|
+
},
|
|
16926
|
+
listOrganizations: {
|
|
16927
|
+
method: "GET",
|
|
16928
|
+
path: "/v1/admin/organizations",
|
|
16929
|
+
tags: ["Admin organizations"],
|
|
16930
|
+
summary: "List the organizations the caller belongs to",
|
|
16931
|
+
security: "management",
|
|
16932
|
+
response: OrganizationListResponseSchema,
|
|
16933
|
+
responseDescription: "Memberships ordered by organization creation time."
|
|
16934
|
+
},
|
|
16935
|
+
selectOrganization: {
|
|
16936
|
+
method: "POST",
|
|
16937
|
+
path: "/v1/admin/organizations/select",
|
|
16938
|
+
tags: ["Admin organizations"],
|
|
16939
|
+
summary: "Switch the caller's active organization",
|
|
16940
|
+
description: "Available to every member, including read-only members, of the target organization.",
|
|
16941
|
+
security: "management",
|
|
16942
|
+
// Switching the active organization re-signs the cookie naming which
|
|
16943
|
+
// tenant the caller reads; gating it behind owner/admin would strand a
|
|
16944
|
+
// read-only member in one organization, and gating it behind setup access
|
|
16945
|
+
// would strand them in an account whose trial has ended.
|
|
16946
|
+
policy: { role: "member", access: "read" },
|
|
16947
|
+
request: OrganizationSelectRequestSchema,
|
|
16948
|
+
response: IdentitySessionSchema,
|
|
16949
|
+
responseDescription: "Session rescoped to the selected organization."
|
|
16950
|
+
},
|
|
16951
|
+
listAppEvents: {
|
|
16952
|
+
method: "GET",
|
|
16953
|
+
path: "/v1/admin/apps/{app}/events",
|
|
16954
|
+
tags: ["Admin operations"],
|
|
16955
|
+
summary: "List application usage events",
|
|
16956
|
+
security: "management",
|
|
16957
|
+
params: APP_PARAM,
|
|
16958
|
+
query: external_exports.object({
|
|
16959
|
+
limit: PageLimitSchema,
|
|
16960
|
+
status: external_exports.enum(USAGE_STATUSES).optional(),
|
|
16961
|
+
provider: external_exports.string().optional(),
|
|
16962
|
+
user: external_exports.string().optional(),
|
|
16963
|
+
model: external_exports.string().optional(),
|
|
16964
|
+
before_id: BeforeIdSchema
|
|
16965
|
+
}),
|
|
16966
|
+
response: UsageEventListSchema,
|
|
16967
|
+
responseDescription: "Paginated application usage events."
|
|
16968
|
+
},
|
|
16969
|
+
getAppAuthEventSummary: {
|
|
16970
|
+
method: "GET",
|
|
16971
|
+
path: "/v1/admin/apps/{app}/auth-events/summary",
|
|
16972
|
+
tags: ["Admin operations"],
|
|
16973
|
+
summary: "Summarize application authentication outcomes",
|
|
16974
|
+
description: "Daily authentication outcomes and reasons, non-ok proxied requests, token-exchange success rate, entitlement-claim propagation delays, and how many users are waiting on a claim right now.",
|
|
16975
|
+
security: "management",
|
|
16976
|
+
params: APP_PARAM,
|
|
16977
|
+
query: external_exports.object({
|
|
16978
|
+
days: external_exports.coerce.number().int({ error: "days must be an integer between 1 and 365" }).min(1, { error: "days must be an integer between 1 and 365" }).max(365, { error: "days must be an integer between 1 and 365" }).default(30).meta({ description: "Trailing window in days, ending today. Defaults to 30." })
|
|
16979
|
+
}),
|
|
16980
|
+
response: AuthEventSummarySchema,
|
|
16981
|
+
responseDescription: "Authentication activity for the window."
|
|
16982
|
+
},
|
|
16983
|
+
listAppAuthEvents: {
|
|
16984
|
+
method: "GET",
|
|
16985
|
+
path: "/v1/admin/apps/{app}/auth-events",
|
|
16986
|
+
tags: ["Admin operations"],
|
|
16987
|
+
summary: "List application authentication events",
|
|
16988
|
+
security: "management",
|
|
16989
|
+
params: APP_PARAM,
|
|
16990
|
+
query: external_exports.object({
|
|
16991
|
+
limit: PageLimitSchema,
|
|
16992
|
+
outcome: external_exports.string().optional(),
|
|
16993
|
+
event: external_exports.enum(["token_exchange", "register"]).optional(),
|
|
16994
|
+
user: external_exports.string().optional(),
|
|
16995
|
+
before_id: BeforeIdSchema
|
|
16996
|
+
}),
|
|
16997
|
+
response: AuthEventListSchema,
|
|
16998
|
+
responseDescription: "Paginated authentication attempts, newest first."
|
|
16999
|
+
},
|
|
17000
|
+
listAppKeys: {
|
|
17001
|
+
method: "GET",
|
|
17002
|
+
path: "/v1/admin/apps/{app}/keys",
|
|
17003
|
+
tags: ["Admin operations"],
|
|
17004
|
+
summary: "List application API keys",
|
|
17005
|
+
security: "management",
|
|
17006
|
+
params: APP_PARAM,
|
|
17007
|
+
response: ApiKeyListResponseSchema,
|
|
17008
|
+
responseDescription: "Application API keys without plaintext tokens."
|
|
17009
|
+
},
|
|
17010
|
+
createAppKey: {
|
|
17011
|
+
method: "POST",
|
|
17012
|
+
path: "/v1/admin/apps/{app}/keys",
|
|
17013
|
+
tags: ["Admin operations"],
|
|
17014
|
+
summary: "Create an application API key",
|
|
17015
|
+
security: "management",
|
|
17016
|
+
params: APP_PARAM,
|
|
17017
|
+
request: ApiKeyCreateRequestSchema,
|
|
17018
|
+
status: 201,
|
|
17019
|
+
response: CreatedApiKeySchema,
|
|
17020
|
+
responseDescription: "One-time plaintext application API key.",
|
|
17021
|
+
receipt: true
|
|
17022
|
+
},
|
|
17023
|
+
revokeAppKey: {
|
|
17024
|
+
method: "POST",
|
|
17025
|
+
path: "/v1/admin/apps/{app}/keys/{key}/revoke",
|
|
17026
|
+
tags: ["Admin operations"],
|
|
17027
|
+
summary: "Revoke an application API key",
|
|
17028
|
+
security: "management",
|
|
17029
|
+
params: { app: { example: "my-app" }, key: { example: "key_123" } },
|
|
17030
|
+
response: ApiKeyRevokeResponseSchema,
|
|
17031
|
+
responseDescription: "Revoked application API key."
|
|
17032
|
+
},
|
|
17033
|
+
listAppUsers: {
|
|
17034
|
+
method: "GET",
|
|
17035
|
+
path: "/v1/admin/apps/{app}/users",
|
|
17036
|
+
tags: ["Admin operations"],
|
|
17037
|
+
summary: "List application users",
|
|
17038
|
+
security: "management",
|
|
17039
|
+
params: APP_PARAM,
|
|
17040
|
+
query: external_exports.object({
|
|
17041
|
+
month: MonthSchema.optional().meta({ description: "YYYY-MM; defaults to the current UTC month." }),
|
|
17042
|
+
query: external_exports.string().optional().meta({ description: "Substring match on the user id." }),
|
|
17043
|
+
status: external_exports.enum(["active", "blocked"]).optional(),
|
|
17044
|
+
limit: PageLimitSchema,
|
|
17045
|
+
offset: external_exports.coerce.number().int({ error: "offset must be a non-negative integer" }).min(0, { error: "offset must be a non-negative integer" }).default(0)
|
|
17046
|
+
}),
|
|
17047
|
+
response: UserListResponseSchema,
|
|
17048
|
+
responseDescription: "Application users and their usage for the month."
|
|
17049
|
+
},
|
|
17050
|
+
getAppUser: {
|
|
17051
|
+
method: "GET",
|
|
17052
|
+
path: "/v1/admin/apps/{app}/users/{user}",
|
|
17053
|
+
tags: ["Admin operations"],
|
|
17054
|
+
summary: "Get an application user",
|
|
17055
|
+
security: "management",
|
|
17056
|
+
params: { app: { example: "my-app" }, user: { example: "user-123" } },
|
|
17057
|
+
query: MonthQuerySchema,
|
|
17058
|
+
response: UserResponseSchema,
|
|
17059
|
+
responseDescription: "One application user and their usage for the month."
|
|
17060
|
+
},
|
|
17061
|
+
blockAppUser: {
|
|
17062
|
+
method: "POST",
|
|
17063
|
+
path: "/v1/admin/apps/{app}/users/{user}/block",
|
|
17064
|
+
tags: ["Admin operations"],
|
|
17065
|
+
summary: "Block an application user",
|
|
17066
|
+
security: "management",
|
|
17067
|
+
params: { app: { example: "my-app" }, user: { example: "user-123" } },
|
|
17068
|
+
response: UserBlockResponseSchema,
|
|
17069
|
+
responseDescription: "The user's new block state."
|
|
17070
|
+
},
|
|
17071
|
+
unblockAppUser: {
|
|
17072
|
+
method: "POST",
|
|
17073
|
+
path: "/v1/admin/apps/{app}/users/{user}/unblock",
|
|
17074
|
+
tags: ["Admin operations"],
|
|
17075
|
+
summary: "Unblock an application user",
|
|
17076
|
+
security: "management",
|
|
17077
|
+
params: { app: { example: "my-app" }, user: { example: "user-123" } },
|
|
17078
|
+
response: UserBlockResponseSchema,
|
|
17079
|
+
responseDescription: "The user's new block state."
|
|
17080
|
+
},
|
|
17081
|
+
getAppUsage: {
|
|
17082
|
+
method: "GET",
|
|
17083
|
+
path: "/v1/admin/apps/{app}/usage",
|
|
17084
|
+
tags: ["Admin operations"],
|
|
17085
|
+
summary: "Get application usage totals",
|
|
17086
|
+
security: "management",
|
|
17087
|
+
params: APP_PARAM,
|
|
17088
|
+
query: MonthQuerySchema,
|
|
17089
|
+
response: MonthlyUsageResponseSchema,
|
|
17090
|
+
responseDescription: "One month's totals for one application."
|
|
17091
|
+
},
|
|
17092
|
+
repriceAppUsage: {
|
|
17093
|
+
method: "POST",
|
|
17094
|
+
path: "/v1/admin/apps/{app}/usage/reprice",
|
|
17095
|
+
tags: ["Admin operations"],
|
|
17096
|
+
summary: "Preview or apply current catalog prices to stored usage",
|
|
17097
|
+
security: "management",
|
|
17098
|
+
params: APP_PARAM,
|
|
17099
|
+
request: UsageRepriceRequestSchema,
|
|
17100
|
+
response: UsageRepriceResponseSchema,
|
|
17101
|
+
responseDescription: "What repricing would change, or did."
|
|
17102
|
+
},
|
|
17103
|
+
getAppUsageTimeseries: {
|
|
17104
|
+
method: "GET",
|
|
17105
|
+
path: "/v1/admin/apps/{app}/usage/timeseries",
|
|
17106
|
+
tags: ["Admin operations"],
|
|
17107
|
+
summary: "Get application usage over time",
|
|
17108
|
+
security: "management",
|
|
17109
|
+
params: APP_PARAM,
|
|
17110
|
+
query: RangeQuerySchema,
|
|
17111
|
+
response: TimeseriesResponseSchema,
|
|
17112
|
+
responseDescription: "Daily buckets split by provider."
|
|
17113
|
+
},
|
|
17114
|
+
getAppUsageBreakdown: {
|
|
17115
|
+
method: "GET",
|
|
17116
|
+
path: "/v1/admin/apps/{app}/usage/breakdown",
|
|
17117
|
+
tags: ["Admin operations"],
|
|
17118
|
+
summary: "Get grouped application usage",
|
|
17119
|
+
security: "management",
|
|
17120
|
+
params: APP_PARAM,
|
|
17121
|
+
query: RangeQuerySchema.extend({
|
|
17122
|
+
by: external_exports.enum(USAGE_BREAKDOWN_DIMENSIONS).default("model").meta({ description: "Which dimension to group by. Defaults to model." }),
|
|
17123
|
+
limit: PageLimitSchema
|
|
17124
|
+
}),
|
|
17125
|
+
response: BreakdownResponseSchema,
|
|
17126
|
+
responseDescription: "One dimension's totals, largest first."
|
|
17127
|
+
},
|
|
17128
|
+
listModelPrices: {
|
|
17129
|
+
method: "GET",
|
|
17130
|
+
path: "/v1/admin/prices",
|
|
17131
|
+
tags: ["Admin models"],
|
|
17132
|
+
summary: "List known model prices",
|
|
17133
|
+
security: "management",
|
|
17134
|
+
response: PricesResponseSchema,
|
|
17135
|
+
responseDescription: "The priced model catalog this deployment enforces."
|
|
17136
|
+
},
|
|
17137
|
+
getCliCapabilities: {
|
|
17138
|
+
method: "GET",
|
|
17139
|
+
path: "/v1/cli/capabilities",
|
|
17140
|
+
tags: ["CLI"],
|
|
17141
|
+
summary: "Discover deployment identity and provider capabilities",
|
|
17142
|
+
security: "public",
|
|
17143
|
+
response: CliCapabilitiesResponseSchema,
|
|
17144
|
+
responseDescription: "Public deployment capabilities. No credentials or inference calls.",
|
|
17145
|
+
errors: "none"
|
|
17146
|
+
},
|
|
17147
|
+
bootstrapCliAccount: {
|
|
17148
|
+
method: "POST",
|
|
17149
|
+
path: "/v1/cli/bootstrap",
|
|
17150
|
+
tags: ["CLI"],
|
|
17151
|
+
summary: "Initialize a recoverable CLI account",
|
|
17152
|
+
description: "Persist both random proofs before sending. An identical retry returns the same account and protected credential during its exchange window. Cloud initialization is public and rate limited. Self-hosted initialization is public too and creates the deployment's single initial account, so whoever initializes an empty deployment first owns it, exactly as its first console registration does; it is not rate limited, because the only thing a limit could refuse on a deployment nobody owns yet is its own installer retrying, and every caller after the first is refused permanently anyway. All responses are no-store.",
|
|
17153
|
+
security: "public",
|
|
17154
|
+
request: CliBootstrapRequestSchema,
|
|
17155
|
+
response: CliBootstrapResponseSchema,
|
|
17156
|
+
responseDescription: "Initial account and credential. Never print or log the credential.",
|
|
17157
|
+
errors: CLI_ERRORS
|
|
17158
|
+
},
|
|
17159
|
+
createCliOperation: {
|
|
17160
|
+
method: "POST",
|
|
17161
|
+
path: "/v1/cli/operations",
|
|
17162
|
+
tags: ["CLI"],
|
|
17163
|
+
summary: "Create or recover a browser handoff",
|
|
17164
|
+
description: "Persist pollToken before initiation. Repeating the same proof and payload recovers the same operation. A current account key is required. Claims require interactive human sign-in and explicit consent. Provider handoffs require the browser URL proof and show the exact resource configuration before secret submission. Handoffs expire after 15 minutes.",
|
|
17165
|
+
security: "management",
|
|
17166
|
+
// Admin, like every write; read access because the claim is the one kind
|
|
17167
|
+
// an account past its free window may still open. Every resource kind then
|
|
17168
|
+
// asks for setup access of its own, in the handler, from its kind entry.
|
|
17169
|
+
policy: { role: "admin", access: "read" },
|
|
17170
|
+
request: CliOperationRequestSchema,
|
|
17171
|
+
response: CliOperationResponseSchema,
|
|
17172
|
+
responseDescription: "Browser URL for the pending handoff.",
|
|
17173
|
+
errors: CLI_ERRORS
|
|
17174
|
+
},
|
|
17175
|
+
pollCliOperation: {
|
|
17176
|
+
method: "GET",
|
|
17177
|
+
path: "/v1/cli/operations/{id}",
|
|
17178
|
+
tags: ["CLI"],
|
|
17179
|
+
summary: "Poll a browser handoff",
|
|
17180
|
+
description: "Only the original polling proof can recover the result. Completed claims report the account they landed on; the CLI keeps the access it already had. Provider secrets are never returned.",
|
|
17181
|
+
security: "cliPoll",
|
|
17182
|
+
params: { id: {} },
|
|
17183
|
+
response: CliPollResponseSchema,
|
|
17184
|
+
responseDescription: "Current operation state and nonsecret result.",
|
|
17185
|
+
errors: CLI_ERRORS
|
|
17186
|
+
},
|
|
17187
|
+
getCliAccount: {
|
|
17188
|
+
method: "GET",
|
|
17189
|
+
path: "/v1/cli/account",
|
|
17190
|
+
tags: ["CLI"],
|
|
17191
|
+
summary: "Read account lifecycle and current access",
|
|
17192
|
+
security: "management",
|
|
17193
|
+
response: CliAccountResponseSchema,
|
|
17194
|
+
responseDescription: "Account deadlines, effective access and current request count.",
|
|
17195
|
+
errors: CLI_ERRORS
|
|
17196
|
+
},
|
|
17197
|
+
getCliUsage: {
|
|
17198
|
+
method: "GET",
|
|
17199
|
+
path: "/v1/cli/usage",
|
|
17200
|
+
tags: ["CLI"],
|
|
17201
|
+
summary: "Read retained account usage for a UTC month",
|
|
17202
|
+
description: "Includes retained usage for deleted apps, with durable account attribution. Historical rows whose owner was already unknown when attribution was introduced cannot be counted. Coverage describes this limitation without disclosing other accounts' data.",
|
|
17203
|
+
security: "management",
|
|
17204
|
+
query: external_exports.object({
|
|
17205
|
+
month: MonthSchema.optional().meta({ description: "YYYY-MM; defaults to the current UTC month." })
|
|
17206
|
+
}),
|
|
17207
|
+
response: CliUsageResponseSchema,
|
|
17208
|
+
responseDescription: "Account totals, per-app totals and attribution coverage.",
|
|
17209
|
+
errors: CLI_ERRORS
|
|
17210
|
+
},
|
|
17211
|
+
/*
|
|
17212
|
+
* The human half of a handoff. There is no page to fetch here: the console
|
|
17213
|
+
* renders the approval screen at `/cli/approve/{id}` from its own bundle, and
|
|
17214
|
+
* these four are the API it calls with the proof it read from the URL
|
|
17215
|
+
* fragment. Every one of them carries that proof in its body, so they are
|
|
17216
|
+
* POSTs with no readable URL of their own.
|
|
17217
|
+
*/
|
|
17218
|
+
cliBrowserDetails: {
|
|
17219
|
+
method: "POST",
|
|
17220
|
+
path: "/v1/cli/browser/{id}/details",
|
|
17221
|
+
tags: ["CLI"],
|
|
17222
|
+
summary: "Browser handoff: details",
|
|
17223
|
+
description: BROWSER_HANDOFF_DESCRIPTION,
|
|
17224
|
+
security: "public",
|
|
17225
|
+
params: { id: {} },
|
|
17226
|
+
request: CliSubmissionRequestSchema,
|
|
17227
|
+
response: CliBrowserDetailsResponseSchema,
|
|
17228
|
+
responseDescription: "The pending action, its configuration, the account it lands on, the signed-in human who would approve it, if any, and what stands between this browser and approving it.",
|
|
17229
|
+
errors: CLI_ERRORS
|
|
17230
|
+
},
|
|
17231
|
+
cliBrowserSubmit: {
|
|
17232
|
+
method: "POST",
|
|
17233
|
+
path: "/v1/cli/browser/{id}/submit",
|
|
17234
|
+
tags: ["CLI"],
|
|
17235
|
+
summary: "Browser handoff: submit",
|
|
17236
|
+
description: BROWSER_HANDOFF_DESCRIPTION,
|
|
17237
|
+
security: "public",
|
|
17238
|
+
params: { id: {} },
|
|
17239
|
+
request: CliSubmissionRequestSchema,
|
|
17240
|
+
response: CliBrowserSubmitResponseSchema,
|
|
17241
|
+
responseDescription: "The handoff is approved and consumed. No submitted secret is ever echoed.",
|
|
17242
|
+
errors: CLI_ERRORS
|
|
17243
|
+
},
|
|
17244
|
+
cliBrowserRegister: {
|
|
17245
|
+
method: "POST",
|
|
17246
|
+
path: "/v1/cli/browser/{id}/register",
|
|
17247
|
+
tags: ["CLI"],
|
|
17248
|
+
summary: "Browser handoff: register",
|
|
17249
|
+
description: BROWSER_HANDOFF_DESCRIPTION,
|
|
17250
|
+
security: "public",
|
|
17251
|
+
params: { id: {} },
|
|
17252
|
+
request: CliSubmissionRequestSchema,
|
|
17253
|
+
response: CliBrowserRegisterResponseSchema,
|
|
17254
|
+
responseDescription: "A new human identity for a pending claim, with its session set as a cookie.",
|
|
17255
|
+
errors: CLI_ERRORS
|
|
17256
|
+
},
|
|
17257
|
+
cliBrowserGoogle: {
|
|
17258
|
+
method: "POST",
|
|
17259
|
+
path: "/v1/cli/browser/{id}/google",
|
|
17260
|
+
tags: ["CLI"],
|
|
17261
|
+
summary: "Browser handoff: google",
|
|
17262
|
+
description: BROWSER_HANDOFF_DESCRIPTION,
|
|
17263
|
+
security: "public",
|
|
17264
|
+
params: { id: {} },
|
|
17265
|
+
request: CliSubmissionRequestSchema,
|
|
17266
|
+
response: CliBrowserGoogleResponseSchema,
|
|
17267
|
+
responseDescription: "Where to send the browser to start Google consent for a pending claim.",
|
|
17268
|
+
errors: CLI_ERRORS
|
|
17269
|
+
}
|
|
17270
|
+
};
|
|
16010
17271
|
function searchSuffix(params) {
|
|
16011
17272
|
const search = new URLSearchParams();
|
|
16012
17273
|
for (const [key, value] of Object.entries(params)) {
|
|
@@ -16015,200 +17276,15 @@ function searchSuffix(params) {
|
|
|
16015
17276
|
const encoded = search.toString();
|
|
16016
17277
|
return encoded ? `?${encoded}` : "";
|
|
16017
17278
|
}
|
|
16018
|
-
|
|
16019
|
-
|
|
16020
|
-
|
|
17279
|
+
function operationPath(name, ...rest) {
|
|
17280
|
+
const [params, query] = rest;
|
|
17281
|
+
const filled = CATALOG[name].path.replace(/\{(\w+)\}/gu, (_match, key) => {
|
|
17282
|
+
const value = params?.[key];
|
|
17283
|
+
if (value === void 0) throw new Error(`${String(name)} needs a "${key}" path parameter`);
|
|
17284
|
+
return encodeURIComponent(value);
|
|
17285
|
+
});
|
|
17286
|
+
return query === void 0 ? filled : filled + searchSuffix(query);
|
|
16021
17287
|
}
|
|
16022
|
-
var operations = {
|
|
16023
|
-
// Deployment and identity
|
|
16024
|
-
getConsoleCapabilities: operation()(
|
|
16025
|
-
"GET",
|
|
16026
|
-
() => "/v1/console/capabilities"
|
|
16027
|
-
),
|
|
16028
|
-
getAdminSession: operation()(
|
|
16029
|
-
"GET",
|
|
16030
|
-
() => "/v1/admin/session"
|
|
16031
|
-
),
|
|
16032
|
-
listOrganizations: operation()(
|
|
16033
|
-
"GET",
|
|
16034
|
-
() => "/v1/admin/organizations"
|
|
16035
|
-
),
|
|
16036
|
-
selectOrganization: operation()(
|
|
16037
|
-
"POST",
|
|
16038
|
-
() => "/v1/admin/organizations/select"
|
|
16039
|
-
),
|
|
16040
|
-
// Management keys
|
|
16041
|
-
listManagementKeys: operation()(
|
|
16042
|
-
"GET",
|
|
16043
|
-
() => "/v1/admin/keys"
|
|
16044
|
-
),
|
|
16045
|
-
createManagementKey: operation()(
|
|
16046
|
-
"POST",
|
|
16047
|
-
() => "/v1/admin/keys"
|
|
16048
|
-
),
|
|
16049
|
-
revokeManagementKey: operation()(
|
|
16050
|
-
"POST",
|
|
16051
|
-
(id) => `/v1/admin/keys/${seg(id)}/revoke`
|
|
16052
|
-
),
|
|
16053
|
-
// Providers
|
|
16054
|
-
listProviders: operation()(
|
|
16055
|
-
"GET",
|
|
16056
|
-
() => "/v1/admin/providers"
|
|
16057
|
-
),
|
|
16058
|
-
createProvider: operation()(
|
|
16059
|
-
"POST",
|
|
16060
|
-
() => "/v1/admin/providers"
|
|
16061
|
-
),
|
|
16062
|
-
testProvider: operation()(
|
|
16063
|
-
"POST",
|
|
16064
|
-
() => "/v1/admin/providers/test"
|
|
16065
|
-
),
|
|
16066
|
-
updateProvider: operation()(
|
|
16067
|
-
"PUT",
|
|
16068
|
-
(id) => `/v1/admin/providers/${seg(id)}`
|
|
16069
|
-
),
|
|
16070
|
-
deleteProvider: operation()(
|
|
16071
|
-
"DELETE",
|
|
16072
|
-
(id) => `/v1/admin/providers/${seg(id)}`
|
|
16073
|
-
),
|
|
16074
|
-
// Provider gateways
|
|
16075
|
-
listProviderGateways: operation()(
|
|
16076
|
-
"GET",
|
|
16077
|
-
() => "/v1/admin/provider-gateways"
|
|
16078
|
-
),
|
|
16079
|
-
createProviderGateway: operation()("POST", () => "/v1/admin/provider-gateways"),
|
|
16080
|
-
testProviderGateway: operation()("POST", () => "/v1/admin/provider-gateways/test"),
|
|
16081
|
-
updateProviderGateway: operation()("PATCH", (id) => `/v1/admin/provider-gateways/${seg(id)}`),
|
|
16082
|
-
rotateProviderGateway: operation()("POST", (id) => `/v1/admin/provider-gateways/${seg(id)}/rotate`),
|
|
16083
|
-
deleteProviderGateway: operation()(
|
|
16084
|
-
"DELETE",
|
|
16085
|
-
(id) => `/v1/admin/provider-gateways/${seg(id)}`
|
|
16086
|
-
),
|
|
16087
|
-
// Applications
|
|
16088
|
-
listApps: operation()(
|
|
16089
|
-
"GET",
|
|
16090
|
-
(query = {}) => `/v1/admin/apps${searchSuffix(query)}`
|
|
16091
|
-
),
|
|
16092
|
-
createApp: operation()(
|
|
16093
|
-
"POST",
|
|
16094
|
-
() => "/v1/admin/apps"
|
|
16095
|
-
),
|
|
16096
|
-
getApp: operation()(
|
|
16097
|
-
"GET",
|
|
16098
|
-
(appId) => `/v1/admin/apps/${seg(appId)}`
|
|
16099
|
-
),
|
|
16100
|
-
updateApp: operation()(
|
|
16101
|
-
"PUT",
|
|
16102
|
-
(appId) => `/v1/admin/apps/${seg(appId)}`
|
|
16103
|
-
),
|
|
16104
|
-
deleteApp: operation()(
|
|
16105
|
-
"DELETE",
|
|
16106
|
-
(appId) => `/v1/admin/apps/${seg(appId)}${searchSuffix({ confirm: appId })}`
|
|
16107
|
-
),
|
|
16108
|
-
validateApp: operation()(
|
|
16109
|
-
"POST",
|
|
16110
|
-
(appId) => `/v1/admin/apps/${seg(appId)}/validate`
|
|
16111
|
-
),
|
|
16112
|
-
// Application keys
|
|
16113
|
-
listAppKeys: operation()(
|
|
16114
|
-
"GET",
|
|
16115
|
-
(appId) => `/v1/admin/apps/${seg(appId)}/keys`
|
|
16116
|
-
),
|
|
16117
|
-
createAppKey: operation()(
|
|
16118
|
-
"POST",
|
|
16119
|
-
(appId) => `/v1/admin/apps/${seg(appId)}/keys`
|
|
16120
|
-
),
|
|
16121
|
-
revokeAppKey: operation()(
|
|
16122
|
-
"POST",
|
|
16123
|
-
(appId, keyId) => `/v1/admin/apps/${seg(appId)}/keys/${seg(keyId)}/revoke`
|
|
16124
|
-
),
|
|
16125
|
-
// Application users
|
|
16126
|
-
listAppUsers: operation()(
|
|
16127
|
-
"GET",
|
|
16128
|
-
(appId, query) => `/v1/admin/apps/${seg(appId)}/users${searchSuffix(query)}`
|
|
16129
|
-
),
|
|
16130
|
-
setAppUserBlocked: operation()(
|
|
16131
|
-
"POST",
|
|
16132
|
-
(appId, userId, blocked) => `/v1/admin/apps/${seg(appId)}/users/${seg(userId)}/${blocked ? "block" : "unblock"}`
|
|
16133
|
-
),
|
|
16134
|
-
// Application usage
|
|
16135
|
-
getAppUsage: operation()(
|
|
16136
|
-
"GET",
|
|
16137
|
-
(appId, query = {}) => `/v1/admin/apps/${seg(appId)}/usage${searchSuffix(query)}`
|
|
16138
|
-
),
|
|
16139
|
-
getAppUsageTimeseries: operation()(
|
|
16140
|
-
"GET",
|
|
16141
|
-
(appId, query) => `/v1/admin/apps/${seg(appId)}/usage/timeseries${searchSuffix(query)}`
|
|
16142
|
-
),
|
|
16143
|
-
getAppUsageBreakdown: operation()(
|
|
16144
|
-
"GET",
|
|
16145
|
-
(appId, query) => `/v1/admin/apps/${seg(appId)}/usage/breakdown${searchSuffix(query)}`
|
|
16146
|
-
),
|
|
16147
|
-
listAppEvents: operation()(
|
|
16148
|
-
"GET",
|
|
16149
|
-
(appId, query) => `/v1/admin/apps/${seg(appId)}/events${searchSuffix(query)}`
|
|
16150
|
-
),
|
|
16151
|
-
getAppAuthEventSummary: operation()(
|
|
16152
|
-
"GET",
|
|
16153
|
-
(appId, query) => `/v1/admin/apps/${seg(appId)}/auth-events/summary${searchSuffix(query)}`
|
|
16154
|
-
),
|
|
16155
|
-
listAppAuthEvents: operation()(
|
|
16156
|
-
"GET",
|
|
16157
|
-
(appId, query) => `/v1/admin/apps/${seg(appId)}/auth-events${searchSuffix(query)}`
|
|
16158
|
-
),
|
|
16159
|
-
listModelPrices: operation()(
|
|
16160
|
-
"GET",
|
|
16161
|
-
() => "/v1/admin/prices"
|
|
16162
|
-
),
|
|
16163
|
-
// CLI surface
|
|
16164
|
-
getCliCapabilities: operation()(
|
|
16165
|
-
"GET",
|
|
16166
|
-
() => "/v1/cli/capabilities"
|
|
16167
|
-
),
|
|
16168
|
-
bootstrapCliAccount: operation()(
|
|
16169
|
-
"POST",
|
|
16170
|
-
() => "/v1/cli/bootstrap"
|
|
16171
|
-
),
|
|
16172
|
-
createCliOperation: operation()(
|
|
16173
|
-
"POST",
|
|
16174
|
-
() => "/v1/cli/operations"
|
|
16175
|
-
),
|
|
16176
|
-
pollCliOperation: operation()(
|
|
16177
|
-
"GET",
|
|
16178
|
-
(id) => `/v1/cli/operations/${seg(id)}`
|
|
16179
|
-
),
|
|
16180
|
-
getCliAccount: operation()(
|
|
16181
|
-
"GET",
|
|
16182
|
-
() => "/v1/cli/account"
|
|
16183
|
-
),
|
|
16184
|
-
getCliUsage: operation()(
|
|
16185
|
-
"GET",
|
|
16186
|
-
(query = {}) => `/v1/cli/usage${searchSuffix(query)}`
|
|
16187
|
-
),
|
|
16188
|
-
/*
|
|
16189
|
-
* The browser half of a handoff, called by the console's approval page rather
|
|
16190
|
-
* than by the CLI. Every one of them carries the submission proof from the
|
|
16191
|
-
* URL fragment in its body, so they are POSTs with no readable URL of their
|
|
16192
|
-
* own; routing them through here is what keeps the page from writing those
|
|
16193
|
-
* paths by hand.
|
|
16194
|
-
*/
|
|
16195
|
-
cliBrowserDetails: operation()(
|
|
16196
|
-
"POST",
|
|
16197
|
-
(id) => `/v1/cli/browser/${seg(id)}/details`
|
|
16198
|
-
),
|
|
16199
|
-
cliBrowserSubmit: operation()(
|
|
16200
|
-
"POST",
|
|
16201
|
-
(id) => `/v1/cli/browser/${seg(id)}/submit`
|
|
16202
|
-
),
|
|
16203
|
-
cliBrowserRegister: operation()(
|
|
16204
|
-
"POST",
|
|
16205
|
-
(id) => `/v1/cli/browser/${seg(id)}/register`
|
|
16206
|
-
),
|
|
16207
|
-
cliBrowserGoogle: operation()(
|
|
16208
|
-
"POST",
|
|
16209
|
-
(id) => `/v1/cli/browser/${seg(id)}/google`
|
|
16210
|
-
)
|
|
16211
|
-
};
|
|
16212
17288
|
|
|
16213
17289
|
// src/common.ts
|
|
16214
17290
|
import { randomBytes } from "node:crypto";
|
|
@@ -16579,14 +17655,14 @@ async function required2(flags, key, label = key) {
|
|
|
16579
17655
|
return typeof value === "string" ? value : await prompt(label, flags);
|
|
16580
17656
|
}
|
|
16581
17657
|
async function resolveProvider(ctx, id) {
|
|
16582
|
-
const { data } = await ctx.call("listProviders"
|
|
17658
|
+
const { data } = await ctx.call("listProviders");
|
|
16583
17659
|
const matches = data.providers.filter((p) => p.id === id || p.slug === id);
|
|
16584
17660
|
if (matches.length !== 1 || !matches[0])
|
|
16585
17661
|
fail("provider_not_found", "Choose one exact provider ID or slug.");
|
|
16586
17662
|
return matches[0];
|
|
16587
17663
|
}
|
|
16588
17664
|
async function resolveGateway(ctx, id) {
|
|
16589
|
-
const { data } = await ctx.call("listProviderGateways"
|
|
17665
|
+
const { data } = await ctx.call("listProviderGateways");
|
|
16590
17666
|
const value = data.gateways.find((g) => g.id === id);
|
|
16591
17667
|
if (!value)
|
|
16592
17668
|
fail("provider_gateway_not_found", "Provider gateway ID was not found.");
|
|
@@ -16596,14 +17672,14 @@ async function resourceCommand(ctx, command, args, flags) {
|
|
|
16596
17672
|
const [group, action] = command.split(" ");
|
|
16597
17673
|
const gateway = group === "provider-gateway";
|
|
16598
17674
|
if (action === "types") {
|
|
16599
|
-
const { data } = await ctx.publicCall("getCliCapabilities"
|
|
17675
|
+
const { data } = await ctx.publicCall("getCliCapabilities");
|
|
16600
17676
|
return gateway ? data.providerGateways.map((entry) => ({
|
|
16601
17677
|
...entry,
|
|
16602
17678
|
type: entry.type === "cf_aig" ? "cloudflare" : entry.type
|
|
16603
17679
|
})) : data.providers;
|
|
16604
17680
|
}
|
|
16605
17681
|
if (action === "list")
|
|
16606
|
-
return gateway ? (await ctx.call("listProviderGateways"
|
|
17682
|
+
return gateway ? (await ctx.call("listProviderGateways")).data : (await ctx.call("listProviders")).data;
|
|
16607
17683
|
let existingProvider;
|
|
16608
17684
|
let existingGateway;
|
|
16609
17685
|
const target = args[0];
|
|
@@ -16623,14 +17699,14 @@ async function resourceCommand(ctx, command, args, flags) {
|
|
|
16623
17699
|
`Delete ${group} ${existingGateway.name} (${existingGateway.id})? Referenced by ${existingGateway.referencedCount} providers; deletion is refused until all references are removed.`,
|
|
16624
17700
|
flags
|
|
16625
17701
|
);
|
|
16626
|
-
return (await ctx.call("deleteProviderGateway",
|
|
17702
|
+
return (await ctx.call("deleteProviderGateway", { params: { id: existingGateway.id } })).data;
|
|
16627
17703
|
}
|
|
16628
17704
|
if (!existingProvider) fail("invalid_arguments", "Supply the resource to remove.");
|
|
16629
17705
|
await confirm(
|
|
16630
17706
|
`Delete ${group} ${existingProvider.name} (${existingProvider.id})? Apps referencing this provider lose access through this slug.`,
|
|
16631
17707
|
flags
|
|
16632
17708
|
);
|
|
16633
|
-
return (await ctx.call("deleteProvider",
|
|
17709
|
+
return (await ctx.call("deleteProvider", { params: { id: existingProvider.id } })).data;
|
|
16634
17710
|
}
|
|
16635
17711
|
if (action === "add") {
|
|
16636
17712
|
const type = await required2(
|
|
@@ -16664,7 +17740,7 @@ async function resourceCommand(ctx, command, args, flags) {
|
|
|
16664
17740
|
await ctx.bootstrap();
|
|
16665
17741
|
if (flags.browser)
|
|
16666
17742
|
return ctx.operation("provider-gateway.add", { ...draft2 });
|
|
16667
|
-
const created2 = await ctx.create("createProviderGateway",
|
|
17743
|
+
const created2 = await ctx.create("createProviderGateway", { body: body2 });
|
|
16668
17744
|
await created2.complete();
|
|
16669
17745
|
return created2.data;
|
|
16670
17746
|
}
|
|
@@ -16696,8 +17772,9 @@ async function resourceCommand(ctx, command, args, flags) {
|
|
|
16696
17772
|
let body;
|
|
16697
17773
|
if (!flags.browser) body = validate(ProviderCreateRequestSchema, draft);
|
|
16698
17774
|
await ctx.bootstrap();
|
|
16699
|
-
if (flags.browser)
|
|
16700
|
-
|
|
17775
|
+
if (flags.browser)
|
|
17776
|
+
return ctx.operation("provider.add", validate(HandoffProviderAddPayloadSchema, draft));
|
|
17777
|
+
const created = await ctx.create("createProvider", { body });
|
|
16701
17778
|
await created.complete();
|
|
16702
17779
|
return created.data;
|
|
16703
17780
|
}
|
|
@@ -16707,7 +17784,8 @@ async function resourceCommand(ctx, command, args, flags) {
|
|
|
16707
17784
|
const value2 = await secret(flags, "Gateway token");
|
|
16708
17785
|
if (flags.browser)
|
|
16709
17786
|
return ctx.operation("provider-gateway.rotate-key", { id: existingGateway.id, revision: existingGateway.revision });
|
|
16710
|
-
return (await ctx.call("rotateProviderGateway",
|
|
17787
|
+
return (await ctx.call("rotateProviderGateway", {
|
|
17788
|
+
params: { id: existingGateway.id },
|
|
16711
17789
|
body: { token: value2, revision: existingGateway.revision }
|
|
16712
17790
|
})).data;
|
|
16713
17791
|
}
|
|
@@ -16720,7 +17798,8 @@ async function resourceCommand(ctx, command, args, flags) {
|
|
|
16720
17798
|
const value = await secret(flags, "Provider API key");
|
|
16721
17799
|
if (flags.browser)
|
|
16722
17800
|
return ctx.operation("provider.rotate-key", { id: existingProvider.id, revision: existingProvider.revision });
|
|
16723
|
-
return (await ctx.call("updateProvider",
|
|
17801
|
+
return (await ctx.call("updateProvider", {
|
|
17802
|
+
params: { id: existingProvider.id },
|
|
16724
17803
|
body: { secret: value, revision: existingProvider.revision }
|
|
16725
17804
|
})).data;
|
|
16726
17805
|
}
|
|
@@ -16731,7 +17810,7 @@ async function resourceCommand(ctx, command, args, flags) {
|
|
|
16731
17810
|
name: await required2(flags, "name"),
|
|
16732
17811
|
revision: existingGateway.revision
|
|
16733
17812
|
});
|
|
16734
|
-
return (await ctx.call("updateProviderGateway",
|
|
17813
|
+
return (await ctx.call("updateProviderGateway", { params: { id: existingGateway.id }, body: body2 })).data;
|
|
16735
17814
|
}
|
|
16736
17815
|
if (!existingProvider) fail("invalid_arguments", "Supply the provider to update.");
|
|
16737
17816
|
const draft = {};
|
|
@@ -16774,13 +17853,20 @@ async function resourceCommand(ctx, command, args, flags) {
|
|
|
16774
17853
|
...flags.browser ? { secret: "validation-placeholder" } : {}
|
|
16775
17854
|
});
|
|
16776
17855
|
if (flags.browser)
|
|
16777
|
-
return ctx.operation(
|
|
16778
|
-
|
|
17856
|
+
return ctx.operation(
|
|
17857
|
+
"provider.update",
|
|
17858
|
+
validate(HandoffProviderUpdatePayloadSchema, {
|
|
17859
|
+
id: existingProvider.id,
|
|
17860
|
+
revision: existingProvider.revision,
|
|
17861
|
+
...draft
|
|
17862
|
+
})
|
|
17863
|
+
);
|
|
17864
|
+
return (await ctx.call("updateProvider", { params: { id: existingProvider.id }, body })).data;
|
|
16779
17865
|
}
|
|
16780
17866
|
fail("unknown_command", "Unknown command.");
|
|
16781
17867
|
}
|
|
16782
17868
|
async function assertSupportedType(ctx, gateway, type) {
|
|
16783
|
-
const { data: capabilities } = await ctx.publicCall("getCliCapabilities"
|
|
17869
|
+
const { data: capabilities } = await ctx.publicCall("getCliCapabilities");
|
|
16784
17870
|
const supported = gateway ? capabilities.providerGateways : capabilities.providers;
|
|
16785
17871
|
if (!supported.some((p) => p.type === type))
|
|
16786
17872
|
fail(
|
|
@@ -16805,6 +17891,95 @@ import { join, resolve } from "node:path";
|
|
|
16805
17891
|
import { homedir, hostname as hostname3, platform } from "node:os";
|
|
16806
17892
|
import { setTimeout as delay } from "node:timers/promises";
|
|
16807
17893
|
var posixPermissions = () => process.platform !== "win32";
|
|
17894
|
+
var StoredOrigin = external_exports.string().refine(
|
|
17895
|
+
(value) => {
|
|
17896
|
+
try {
|
|
17897
|
+
origin(value);
|
|
17898
|
+
return true;
|
|
17899
|
+
} catch {
|
|
17900
|
+
return false;
|
|
17901
|
+
}
|
|
17902
|
+
},
|
|
17903
|
+
"must be a deployment origin without credentials, path, query or fragment"
|
|
17904
|
+
);
|
|
17905
|
+
var StoredConnectionSchema = external_exports.looseObject({
|
|
17906
|
+
url: StoredOrigin,
|
|
17907
|
+
authenticated: external_exports.boolean(),
|
|
17908
|
+
credential: external_exports.string().optional(),
|
|
17909
|
+
account: external_exports.looseObject({}).optional(),
|
|
17910
|
+
deployment: external_exports.looseObject({}).optional()
|
|
17911
|
+
}).refine(
|
|
17912
|
+
(connection) => !connection.authenticated || typeof connection.credential === "string",
|
|
17913
|
+
"an authenticated connection must carry its credential"
|
|
17914
|
+
);
|
|
17915
|
+
var StoredOperationSchema = external_exports.looseObject({
|
|
17916
|
+
url: StoredOrigin,
|
|
17917
|
+
pollToken: external_exports.string(),
|
|
17918
|
+
kind: external_exports.string(),
|
|
17919
|
+
phase: external_exports.enum(["initiating", "pending"]).optional(),
|
|
17920
|
+
payload: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
|
|
17921
|
+
generation: external_exports.number().optional(),
|
|
17922
|
+
completed: external_exports.boolean().optional()
|
|
17923
|
+
});
|
|
17924
|
+
var StoredMutationSchema = external_exports.looseObject({
|
|
17925
|
+
id: external_exports.string(),
|
|
17926
|
+
proof: external_exports.string(),
|
|
17927
|
+
requestHash: external_exports.string(),
|
|
17928
|
+
url: external_exports.string(),
|
|
17929
|
+
accountId: external_exports.string().nullable(),
|
|
17930
|
+
path: external_exports.string(),
|
|
17931
|
+
createdAt: external_exports.string(),
|
|
17932
|
+
completedAt: external_exports.string().optional()
|
|
17933
|
+
});
|
|
17934
|
+
var StoredInstallationSchema = external_exports.looseObject({
|
|
17935
|
+
id: external_exports.string(),
|
|
17936
|
+
name: external_exports.string(),
|
|
17937
|
+
accountId: external_exports.string(),
|
|
17938
|
+
version: external_exports.string(),
|
|
17939
|
+
phase: external_exports.enum(["prepared", "deployed", "ready"]),
|
|
17940
|
+
databaseId: external_exports.string().optional(),
|
|
17941
|
+
databaseName: external_exports.string().optional(),
|
|
17942
|
+
url: external_exports.string().optional(),
|
|
17943
|
+
vars: external_exports.record(external_exports.string(), external_exports.string()).optional(),
|
|
17944
|
+
domains: external_exports.array(external_exports.string()).optional(),
|
|
17945
|
+
pendingDomain: external_exports.string().optional(),
|
|
17946
|
+
bootstrap: external_exports.looseObject({ idempotencyKey: external_exports.string(), pollToken: external_exports.string() }).optional(),
|
|
17947
|
+
secrets: external_exports.record(external_exports.string(), external_exports.string()).optional()
|
|
17948
|
+
});
|
|
17949
|
+
var CliStateSchema = external_exports.object({
|
|
17950
|
+
schemaVersion: external_exports.literal(1),
|
|
17951
|
+
active: StoredConnectionSchema.nullable(),
|
|
17952
|
+
previous: StoredConnectionSchema.optional(),
|
|
17953
|
+
operations: external_exports.record(external_exports.string(), StoredOperationSchema),
|
|
17954
|
+
generation: external_exports.number().optional(),
|
|
17955
|
+
bootstrap: external_exports.looseObject({
|
|
17956
|
+
createdAt: external_exports.string().optional(),
|
|
17957
|
+
idempotencyKey: external_exports.string(),
|
|
17958
|
+
pollToken: external_exports.string()
|
|
17959
|
+
}).optional(),
|
|
17960
|
+
mutations: external_exports.record(external_exports.string(), StoredMutationSchema).optional(),
|
|
17961
|
+
installations: external_exports.record(external_exports.string(), StoredInstallationSchema).optional()
|
|
17962
|
+
});
|
|
17963
|
+
var RECOVER_STATE = "Recover the state file; it will not be treated as a new account.";
|
|
17964
|
+
function parseState(path, text2) {
|
|
17965
|
+
let value;
|
|
17966
|
+
try {
|
|
17967
|
+
value = JSON.parse(text2);
|
|
17968
|
+
} catch {
|
|
17969
|
+
fail("invalid_state", `Connection state at ${path} is not valid JSON.`, RECOVER_STATE, 4);
|
|
17970
|
+
}
|
|
17971
|
+
try {
|
|
17972
|
+
return validate(CliStateSchema, value);
|
|
17973
|
+
} catch (error51) {
|
|
17974
|
+
const first = error51 instanceof CliError ? error51.message.split("; ")[0] : void 0;
|
|
17975
|
+
fail(
|
|
17976
|
+
"invalid_state",
|
|
17977
|
+
`Unsupported or malformed connection state at ${path}${first ? `: ${first}` : ""}.`,
|
|
17978
|
+
RECOVER_STATE,
|
|
17979
|
+
4
|
|
17980
|
+
);
|
|
17981
|
+
}
|
|
17982
|
+
}
|
|
16808
17983
|
function stateDirectory() {
|
|
16809
17984
|
if (platform() === "win32")
|
|
16810
17985
|
return join(
|
|
@@ -16929,7 +18104,6 @@ async function breakAbandonedLock(path) {
|
|
|
16929
18104
|
return true;
|
|
16930
18105
|
}
|
|
16931
18106
|
var same = (a, b) => JSON.stringify(a ?? null) === JSON.stringify(b ?? null);
|
|
16932
|
-
var records = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
16933
18107
|
function mergeRecords(baseline, local, disk) {
|
|
16934
18108
|
const merged = { ...disk };
|
|
16935
18109
|
for (const [id, value] of Object.entries(local)) {
|
|
@@ -16941,8 +18115,7 @@ function mergeRecords(baseline, local, disk) {
|
|
|
16941
18115
|
return merged;
|
|
16942
18116
|
}
|
|
16943
18117
|
function mergeState(baseline, local, disk) {
|
|
16944
|
-
if (!disk
|
|
16945
|
-
return local;
|
|
18118
|
+
if (!disk) return local;
|
|
16946
18119
|
const connection = baseline && same(baseline.active, local.active) && same(baseline.previous, local.previous) && same(baseline.generation, local.generation) && same(baseline.bootstrap, local.bootstrap) ? disk : local;
|
|
16947
18120
|
const merged = {
|
|
16948
18121
|
schemaVersion: 1,
|
|
@@ -16960,13 +18133,13 @@ function mergeState(baseline, local, disk) {
|
|
|
16960
18133
|
const mutations = mergeRecords(
|
|
16961
18134
|
baseline?.mutations ?? {},
|
|
16962
18135
|
local.mutations ?? {},
|
|
16963
|
-
|
|
18136
|
+
disk.mutations ?? {}
|
|
16964
18137
|
);
|
|
16965
18138
|
if (Object.keys(mutations).length) merged.mutations = mutations;
|
|
16966
18139
|
const installations = mergeRecords(
|
|
16967
18140
|
baseline?.installations ?? {},
|
|
16968
18141
|
local.installations ?? {},
|
|
16969
|
-
|
|
18142
|
+
disk.installations ?? {}
|
|
16970
18143
|
);
|
|
16971
18144
|
if (Object.keys(installations).length) merged.installations = installations;
|
|
16972
18145
|
return merged;
|
|
@@ -17030,50 +18203,32 @@ var StateStore = class {
|
|
|
17030
18203
|
});
|
|
17031
18204
|
}
|
|
17032
18205
|
}
|
|
18206
|
+
/**
|
|
18207
|
+
* The state this command starts from: what the file holds, or a fresh state
|
|
18208
|
+
* when there is no file.
|
|
18209
|
+
*
|
|
18210
|
+
* Only a missing file is a fresh state. A file that is present and cannot be
|
|
18211
|
+
* read — wrong owner, wrong mode, unparseable, or a shape this release does
|
|
18212
|
+
* not know — is refused, because every one of those would otherwise start a
|
|
18213
|
+
* command as though this machine had never held an account.
|
|
18214
|
+
*/
|
|
17033
18215
|
async read() {
|
|
17034
|
-
|
|
17035
|
-
|
|
17036
|
-
|
|
17037
|
-
|
|
17038
|
-
|
|
17039
|
-
"Connection state must be owned by you with mode 0600."
|
|
17040
|
-
);
|
|
17041
|
-
const state = JSON.parse(await readFile2(this.path, "utf8"));
|
|
17042
|
-
if (state.schemaVersion !== 1 || !Object.hasOwn(state, "active") || !state.operations || typeof state.operations !== "object" || Array.isArray(state.operations))
|
|
17043
|
-
fail("invalid_state", "Unsupported or malformed connection state.");
|
|
17044
|
-
if (state.active !== null) {
|
|
17045
|
-
const a = state.active;
|
|
17046
|
-
if (!a || typeof a !== "object" || typeof a.url !== "string" || typeof a.authenticated !== "boolean" || a.authenticated && typeof a.credential !== "string")
|
|
17047
|
-
fail(
|
|
17048
|
-
"invalid_state",
|
|
17049
|
-
"Malformed prior connection; recovery is required."
|
|
17050
|
-
);
|
|
17051
|
-
origin(a.url);
|
|
17052
|
-
}
|
|
17053
|
-
for (const op of Object.values(state.operations)) {
|
|
17054
|
-
if (!op || typeof op.url !== "string" || typeof op.pollToken !== "string" || typeof op.kind !== "string")
|
|
17055
|
-
fail(
|
|
17056
|
-
"invalid_state",
|
|
17057
|
-
"Malformed operation state; recovery is required."
|
|
17058
|
-
);
|
|
17059
|
-
origin(op.url);
|
|
17060
|
-
}
|
|
17061
|
-
this.baseline = structuredClone(state);
|
|
17062
|
-
return state;
|
|
17063
|
-
} catch (e) {
|
|
17064
|
-
if (errorCode(e) === "ENOENT") {
|
|
17065
|
-
const empty = { schemaVersion: 1, active: null, operations: {} };
|
|
17066
|
-
this.baseline = structuredClone(empty);
|
|
17067
|
-
return empty;
|
|
17068
|
-
}
|
|
17069
|
-
if (e instanceof CliError) throw e;
|
|
18216
|
+
const held = await lstat(this.path).catch((error51) => {
|
|
18217
|
+
if (errorCode(error51) === "ENOENT") return null;
|
|
18218
|
+
fail("invalid_state", "Cannot read connection state.", RECOVER_STATE, 4);
|
|
18219
|
+
});
|
|
18220
|
+
if (held && (!held.isFile() || held.isSymbolicLink() || posixPermissions() && (process.getuid && held.uid !== process.getuid() || (held.mode & 63) !== 0)))
|
|
17070
18221
|
fail(
|
|
17071
|
-
"
|
|
17072
|
-
"
|
|
17073
|
-
"Recover the state file; it will not be treated as a new account.",
|
|
17074
|
-
4
|
|
18222
|
+
"unsafe_storage",
|
|
18223
|
+
"Connection state must be owned by you with mode 0600."
|
|
17075
18224
|
);
|
|
17076
|
-
|
|
18225
|
+
const state = held && await this.stored() || {
|
|
18226
|
+
schemaVersion: 1,
|
|
18227
|
+
active: null,
|
|
18228
|
+
operations: {}
|
|
18229
|
+
};
|
|
18230
|
+
this.baseline = structuredClone(state);
|
|
18231
|
+
return state;
|
|
17077
18232
|
}
|
|
17078
18233
|
/**
|
|
17079
18234
|
* Persists the state, merged with whatever another command wrote meanwhile.
|
|
@@ -17125,13 +18280,23 @@ var StateStore = class {
|
|
|
17125
18280
|
else state.generation = merged.generation;
|
|
17126
18281
|
}
|
|
17127
18282
|
}
|
|
17128
|
-
/**
|
|
18283
|
+
/**
|
|
18284
|
+
* The state file as it stands, or null when there is no file at all.
|
|
18285
|
+
*
|
|
18286
|
+
* A file that will not parse is refused here rather than treated as nothing
|
|
18287
|
+
* to merge with: this is what a write reads to keep another command's
|
|
18288
|
+
* receipts, and answering "nothing" would replace a file whose contents
|
|
18289
|
+
* could not be understood with this command's own state.
|
|
18290
|
+
*/
|
|
17129
18291
|
async stored() {
|
|
18292
|
+
let text2;
|
|
17130
18293
|
try {
|
|
17131
|
-
|
|
17132
|
-
} catch {
|
|
17133
|
-
return null;
|
|
18294
|
+
text2 = await readFile2(this.path, "utf8");
|
|
18295
|
+
} catch (error51) {
|
|
18296
|
+
if (errorCode(error51) === "ENOENT") return null;
|
|
18297
|
+
fail("invalid_state", "Cannot read connection state.", RECOVER_STATE, 4);
|
|
17134
18298
|
}
|
|
18299
|
+
return parseState(this.path, text2);
|
|
17135
18300
|
}
|
|
17136
18301
|
async replace(state) {
|
|
17137
18302
|
const temp = join(this.directory, `.state-${randomToken()}`);
|
|
@@ -17210,15 +18375,12 @@ var EXAMPLE_GAP_NOTES = {
|
|
|
17210
18375
|
body: `This app allows no path whose request shape is known here, so ${BODY_PLACEHOLDER} stands in for the body the provider documents.`
|
|
17211
18376
|
};
|
|
17212
18377
|
function examplePath(type, { gatewayRouted = false, model } = {}) {
|
|
17213
|
-
|
|
17214
|
-
if (
|
|
17215
|
-
|
|
17216
|
-
|
|
17217
|
-
|
|
17218
|
-
|
|
17219
|
-
if (type === "fireworks") return "inference/v1/chat/completions";
|
|
17220
|
-
if (["deepseek", "perplexity", "bytedance"].includes(type)) return "chat/completions";
|
|
17221
|
-
return "v1/chat/completions";
|
|
18378
|
+
const descriptor = isProviderType(type) ? providerDescriptor(type) : void 0;
|
|
18379
|
+
if (descriptor?.modelInPath) {
|
|
18380
|
+
return model ? API_STYLE_PATHS.gemini_native.replace("{model}", model) : void 0;
|
|
18381
|
+
}
|
|
18382
|
+
const own = descriptor?.examplePath ?? API_STYLE_PATHS.chat_completions;
|
|
18383
|
+
return gatewayRouted && own.endsWith("chat/completions") ? API_STYLE_PATHS.chat_completions : own;
|
|
17222
18384
|
}
|
|
17223
18385
|
function bodyFor(path, model) {
|
|
17224
18386
|
const messages = [{ role: "user", content: "Say hello." }];
|
|
@@ -17229,8 +18391,8 @@ function bodyFor(path, model) {
|
|
|
17229
18391
|
return { contents: [{ parts: [{ text: "Say hello." }] }] };
|
|
17230
18392
|
return null;
|
|
17231
18393
|
}
|
|
17232
|
-
function modelsFor(provider,
|
|
17233
|
-
if (
|
|
18394
|
+
function modelsFor(provider, policy, prices) {
|
|
18395
|
+
if (policy?.allowed_models?.length) return policy.allowed_models;
|
|
17234
18396
|
return [
|
|
17235
18397
|
...Object.keys(provider.pricing ?? {}),
|
|
17236
18398
|
...Object.entries(prices[provider.type] ?? {}).filter(([, price]) => price.output !== void 0).map(([model]) => model)
|
|
@@ -17238,18 +18400,17 @@ function modelsFor(provider, policy2, prices) {
|
|
|
17238
18400
|
}
|
|
17239
18401
|
function firstRequest(routing, providers, prices) {
|
|
17240
18402
|
let fallback;
|
|
17241
|
-
|
|
17242
|
-
|
|
17243
|
-
const
|
|
17244
|
-
|
|
17245
|
-
const models = modelsFor(provider, policy2, prices);
|
|
18403
|
+
const candidates = routing ? reachableProviders(routing, providers) : providers.filter((provider) => provider.status === "active");
|
|
18404
|
+
for (const provider of candidates) {
|
|
18405
|
+
const policy = routing?.providers.mode === "selected" ? providerPolicyFor(routing, provider.slug) : void 0;
|
|
18406
|
+
const models = modelsFor(provider, policy, prices);
|
|
17246
18407
|
const model = models[0] ?? MODEL_PLACEHOLDER;
|
|
17247
18408
|
const gaps = models.length ? [] : ["model"];
|
|
17248
18409
|
const fallbackPath = examplePath(provider.type, {
|
|
17249
18410
|
gatewayRouted: Boolean(provider.providerGatewayId),
|
|
17250
18411
|
model
|
|
17251
18412
|
});
|
|
17252
|
-
const paths =
|
|
18413
|
+
const paths = policy?.allowed_paths?.length ? policy.allowed_paths : fallbackPath ? [fallbackPath] : [];
|
|
17253
18414
|
for (const entry of paths) {
|
|
17254
18415
|
const path = typeof entry === "string" ? entry : entry.path;
|
|
17255
18416
|
if (path.includes("*")) continue;
|
|
@@ -17298,8 +18459,16 @@ function curlSnippet(options) {
|
|
|
17298
18459
|
` + (example.anthropic ? " -H 'anthropic-version: 2023-06-01' \\\n" : "") + ` -d ${shellQuote(body)}
|
|
17299
18460
|
`;
|
|
17300
18461
|
}
|
|
18462
|
+
var ISSUER_TOKEN_NOTE = "Replace yourIdentitySDK.currentIDToken(forceRefresh: forceRefresh) with your configured issuer integration.";
|
|
18463
|
+
function swiftSignsInUsers(authentication) {
|
|
18464
|
+
return authentication?.type === "apple_app_attest" && authentication.end_user.source === "issuer";
|
|
18465
|
+
}
|
|
18466
|
+
function swiftAuthMode(authentication) {
|
|
18467
|
+
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";
|
|
18468
|
+
}
|
|
17301
18469
|
function swiftSnippet(options) {
|
|
17302
|
-
const { baseUrl, appId, example, notes: notes2,
|
|
18470
|
+
const { baseUrl, appId, example, notes: notes2, authentication } = options;
|
|
18471
|
+
const authMode = swiftAuthMode(authentication);
|
|
17303
18472
|
const target = "endpoint" in example.target ? `endpointSlug: ${JSON.stringify(example.target.endpoint)}` : `provider: ${JSON.stringify(example.target.provider)},
|
|
17304
18473
|
providerPath: ${JSON.stringify(example.target.path)}`;
|
|
17305
18474
|
const body = example.body ? `request.httpBody = Data(${JSON.stringify(JSON.stringify(example.body))}.utf8)` : `// Supply the provider-native JSON body.
|
|
@@ -17323,33 +18492,54 @@ print(String(decoding: data, as: UTF8.self))
|
|
|
17323
18492
|
`;
|
|
17324
18493
|
}
|
|
17325
18494
|
|
|
18495
|
+
// ../src/shared/app-defaults.ts
|
|
18496
|
+
function unlimitedScope() {
|
|
18497
|
+
return {
|
|
18498
|
+
requests: { per_minute: null, per_day: null },
|
|
18499
|
+
spending: { monthly_usd: null }
|
|
18500
|
+
};
|
|
18501
|
+
}
|
|
18502
|
+
function mobileDefaultLimits() {
|
|
18503
|
+
return {
|
|
18504
|
+
per_user: { requests: { per_minute: 10, per_day: 300 }, spending: { monthly_usd: null } },
|
|
18505
|
+
per_app: unlimitedScope()
|
|
18506
|
+
};
|
|
18507
|
+
}
|
|
18508
|
+
function emptyPolicy() {
|
|
18509
|
+
return { allowed_paths: [], allowed_models: [] };
|
|
18510
|
+
}
|
|
18511
|
+
function newAppConfig(input) {
|
|
18512
|
+
const routing = { providers: { mode: "all" }, model_rewrites: {} };
|
|
18513
|
+
if (input.type === "api_key") {
|
|
18514
|
+
return {
|
|
18515
|
+
authentication: {
|
|
18516
|
+
type: "api_key",
|
|
18517
|
+
...input.endUser ? { end_user: input.endUser } : {}
|
|
18518
|
+
},
|
|
18519
|
+
routing
|
|
18520
|
+
};
|
|
18521
|
+
}
|
|
18522
|
+
return {
|
|
18523
|
+
authentication: {
|
|
18524
|
+
type: "apple_app_attest",
|
|
18525
|
+
app_attest: {
|
|
18526
|
+
team_id: input.teamId,
|
|
18527
|
+
bundle_id: input.bundleId,
|
|
18528
|
+
...input.environments ? { environments: [...input.environments] } : {}
|
|
18529
|
+
},
|
|
18530
|
+
end_user: input.endUser ?? { source: "app_install" }
|
|
18531
|
+
},
|
|
18532
|
+
routing,
|
|
18533
|
+
limits: mobileDefaultLimits()
|
|
18534
|
+
};
|
|
18535
|
+
}
|
|
18536
|
+
|
|
17326
18537
|
// src/apps.ts
|
|
17327
|
-
var unlimited = () => ({
|
|
17328
|
-
requests: { per_minute: null, per_day: null },
|
|
17329
|
-
spending: { monthly_usd: null }
|
|
17330
|
-
});
|
|
17331
|
-
var policy = () => ({ allowed_paths: [], allowed_models: [] });
|
|
17332
18538
|
function documentOf(app2) {
|
|
17333
18539
|
return localApp({ name: app2.name, config: app2.config, status: app2.status });
|
|
17334
18540
|
}
|
|
17335
18541
|
function localApp(value) {
|
|
17336
|
-
|
|
17337
|
-
const auth = doc.config.authentication;
|
|
17338
|
-
if (auth.type === "apple_app_attest") {
|
|
17339
|
-
if (!/^[A-Z0-9]{10}$/.test(auth.app_attest.team_id))
|
|
17340
|
-
fail(
|
|
17341
|
-
"invalid_input",
|
|
17342
|
-
"App Attest team_id must contain ten uppercase letters or digits."
|
|
17343
|
-
);
|
|
17344
|
-
if (!/^[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+$/.test(auth.app_attest.bundle_id))
|
|
17345
|
-
fail(
|
|
17346
|
-
"invalid_input",
|
|
17347
|
-
"App Attest bundle_id must be a reverse DNS identifier."
|
|
17348
|
-
);
|
|
17349
|
-
}
|
|
17350
|
-
if (auth.type === "api_key" && !auth.end_user && doc.config.limits?.per_user && (doc.config.limits.per_user.requests.per_minute !== null || doc.config.limits.per_user.requests.per_day !== null || doc.config.limits.per_user.spending.monthly_usd !== null))
|
|
17351
|
-
fail("invalid_input", "User limits require an end-user identity source.");
|
|
17352
|
-
return doc;
|
|
18542
|
+
return validate(AppWriteSchema, value);
|
|
17353
18543
|
}
|
|
17354
18544
|
async function appDocument(flags, current) {
|
|
17355
18545
|
if (flags.file) {
|
|
@@ -17383,27 +18573,22 @@ async function appDocument(flags, current) {
|
|
|
17383
18573
|
doc = localApp({
|
|
17384
18574
|
name: flags.name ?? (ios && bundle ? bundle.split(".").slice(-2).join(" ") : await required2(flags, "name", "App name")),
|
|
17385
18575
|
status: flags.status ?? "active",
|
|
17386
|
-
config: {
|
|
17387
|
-
|
|
17388
|
-
|
|
17389
|
-
|
|
17390
|
-
|
|
17391
|
-
|
|
17392
|
-
|
|
17393
|
-
|
|
17394
|
-
|
|
17395
|
-
|
|
17396
|
-
|
|
17397
|
-
|
|
17398
|
-
|
|
17399
|
-
|
|
17400
|
-
|
|
17401
|
-
|
|
17402
|
-
},
|
|
17403
|
-
per_app: unlimited()
|
|
17404
|
-
}
|
|
17405
|
-
} : {}
|
|
17406
|
-
}
|
|
18576
|
+
config: ios ? newAppConfig({
|
|
18577
|
+
type: "apple_app_attest",
|
|
18578
|
+
teamId: await required2(flags, "team-id", "Apple team ID"),
|
|
18579
|
+
// Asked for above, because `ios` is exactly when there is one.
|
|
18580
|
+
bundleId: bundle ?? "",
|
|
18581
|
+
/*
|
|
18582
|
+
* Both environments, which is `agw`'s own default rather than the
|
|
18583
|
+
* schema's: someone reaching for the CLI to create an iOS app is
|
|
18584
|
+
* building it, and a development-signed build is what they have in
|
|
18585
|
+
* hand. Passed through unmapped, so `localApp` below is what
|
|
18586
|
+
* refuses a name that is neither.
|
|
18587
|
+
*/
|
|
18588
|
+
environments: attestEnvironments(
|
|
18589
|
+
flags["attest-environments"] ?? "production,development"
|
|
18590
|
+
)
|
|
18591
|
+
}) : newAppConfig({ type: "api_key" })
|
|
17407
18592
|
});
|
|
17408
18593
|
}
|
|
17409
18594
|
if (flags.name) doc.name = flags.name;
|
|
@@ -17417,22 +18602,25 @@ async function appDocument(flags, current) {
|
|
|
17417
18602
|
if (flags["team-id"]) auth.app_attest.team_id = flags["team-id"];
|
|
17418
18603
|
if (flags["bundle-id"]) auth.app_attest.bundle_id = flags["bundle-id"];
|
|
17419
18604
|
if (flags["attest-environments"])
|
|
17420
|
-
auth.app_attest.environments = flags["attest-environments"]
|
|
18605
|
+
auth.app_attest.environments = attestEnvironments(flags["attest-environments"]);
|
|
17421
18606
|
}
|
|
17422
18607
|
const selectedProviders = flagList(flags.provider);
|
|
17423
18608
|
if (selectedProviders.length) {
|
|
17424
|
-
const previous = doc.config.routing
|
|
18609
|
+
const previous = selectedProviderPolicies(doc.config.routing);
|
|
17425
18610
|
doc.config.routing.providers = {
|
|
17426
18611
|
mode: "selected",
|
|
17427
18612
|
selected: Object.fromEntries(
|
|
17428
|
-
selectedProviders.map((slug) => [slug, previous[slug] ??
|
|
18613
|
+
selectedProviders.map((slug) => [slug, previous[slug] ?? emptyPolicy()])
|
|
17429
18614
|
)
|
|
17430
18615
|
};
|
|
17431
18616
|
}
|
|
17432
18617
|
if (flags["all-providers"]) doc.config.routing.providers = { mode: "all" };
|
|
17433
18618
|
return localApp(doc);
|
|
17434
18619
|
}
|
|
17435
|
-
|
|
18620
|
+
function attestEnvironments(value) {
|
|
18621
|
+
return value.split(",").map((name) => name.trim()).filter((name) => name.length > 0);
|
|
18622
|
+
}
|
|
18623
|
+
async function remoteValidation(ctx, doc, id) {
|
|
17436
18624
|
if (!ctx.active?.credential)
|
|
17437
18625
|
return {
|
|
17438
18626
|
local: true,
|
|
@@ -17441,7 +18629,7 @@ async function remoteValidation(ctx, doc, id = "validation-preview") {
|
|
|
17441
18629
|
"Provider references, pricing, and saved configuration checks require login."
|
|
17442
18630
|
]
|
|
17443
18631
|
};
|
|
17444
|
-
const { data } = await ctx.call("validateApp",
|
|
18632
|
+
const { data } = id === void 0 ? await ctx.call("validateAppDraft", { body: doc }) : await ctx.call("validateApp", { params: { app: id }, body: doc });
|
|
17445
18633
|
return { local: true, remote: true, ...data };
|
|
17446
18634
|
}
|
|
17447
18635
|
function mintedKey(value) {
|
|
@@ -17465,7 +18653,7 @@ async function saveKey(ctx, keyRecord, output, appId) {
|
|
|
17465
18653
|
}
|
|
17466
18654
|
let revoked = false;
|
|
17467
18655
|
try {
|
|
17468
|
-
await ctx.call("revokeAppKey",
|
|
18656
|
+
await ctx.call("revokeAppKey", { params: { app: appId, key: keyRecord.id } });
|
|
17469
18657
|
revoked = true;
|
|
17470
18658
|
} catch {
|
|
17471
18659
|
}
|
|
@@ -17495,7 +18683,7 @@ async function saveKey(ctx, keyRecord, output, appId) {
|
|
|
17495
18683
|
}
|
|
17496
18684
|
async function appCommand(ctx, command, args, flags) {
|
|
17497
18685
|
const action = command.slice(4);
|
|
17498
|
-
if (action === "list") return (await ctx.call("listApps"
|
|
18686
|
+
if (action === "list") return (await ctx.call("listApps")).data;
|
|
17499
18687
|
if (action === "validate") {
|
|
17500
18688
|
const doc2 = localApp(await jsonFile(await required2(flags, "file")));
|
|
17501
18689
|
return { definition: doc2, validation: await remoteValidation(ctx, doc2) };
|
|
@@ -17505,7 +18693,7 @@ async function appCommand(ctx, command, args, flags) {
|
|
|
17505
18693
|
let revision = 0;
|
|
17506
18694
|
const appId2 = args[0] ?? "";
|
|
17507
18695
|
if (action === "update") {
|
|
17508
|
-
const r = await ctx.call("getApp",
|
|
18696
|
+
const r = await ctx.call("getApp", { params: { app: appId2 } });
|
|
17509
18697
|
current = documentOf(r.data.app);
|
|
17510
18698
|
revision = r.data.app.revision;
|
|
17511
18699
|
if (!revision && !flags["dry-run"])
|
|
@@ -17529,16 +18717,15 @@ async function appCommand(ctx, command, args, flags) {
|
|
|
17529
18717
|
definition: doc2,
|
|
17530
18718
|
validation: await remoteValidation(ctx, doc2, appId2 || void 0)
|
|
17531
18719
|
};
|
|
17532
|
-
const output = action === "add" && doc2.config.authentication.type === "api_key" ? await ctx.keyOutput(
|
|
18720
|
+
const output = action === "add" && doc2.config.authentication.type === "api_key" ? await ctx.keyOutput(operationPath("createApp"), doc2, flags["key-output"]) : null;
|
|
17533
18721
|
try {
|
|
17534
18722
|
let app2;
|
|
17535
|
-
let resolved;
|
|
17536
18723
|
let configError;
|
|
17537
18724
|
let key;
|
|
17538
18725
|
if (action === "add") {
|
|
17539
18726
|
await ctx.bootstrap();
|
|
17540
|
-
const created = await ctx.create("createApp",
|
|
17541
|
-
({ app: app2,
|
|
18727
|
+
const created = await ctx.create("createApp", { body: doc2 });
|
|
18728
|
+
({ app: app2, config_error: configError } = created.data);
|
|
17542
18729
|
if (output) {
|
|
17543
18730
|
if (created.keyMetadata) key = created.keyMetadata;
|
|
17544
18731
|
else {
|
|
@@ -17556,10 +18743,11 @@ async function appCommand(ctx, command, args, flags) {
|
|
|
17556
18743
|
}
|
|
17557
18744
|
await created.complete();
|
|
17558
18745
|
} else {
|
|
17559
|
-
const updated = await ctx.call("updateApp",
|
|
18746
|
+
const updated = await ctx.call("updateApp", {
|
|
18747
|
+
params: { app: appId2 },
|
|
17560
18748
|
body: { ...doc2, revision }
|
|
17561
18749
|
});
|
|
17562
|
-
({ app: app2,
|
|
18750
|
+
({ app: app2, config_error: configError } = updated.data);
|
|
17563
18751
|
}
|
|
17564
18752
|
let snippet;
|
|
17565
18753
|
try {
|
|
@@ -17574,7 +18762,6 @@ ${snippet}`;
|
|
|
17574
18762
|
return {
|
|
17575
18763
|
...snippet ? { snippet } : {},
|
|
17576
18764
|
app: app2,
|
|
17577
|
-
resolved,
|
|
17578
18765
|
config_error: configError,
|
|
17579
18766
|
...key ? { applicationKey: key } : {},
|
|
17580
18767
|
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.`
|
|
@@ -17586,31 +18773,31 @@ ${snippet}`;
|
|
|
17586
18773
|
if (action.startsWith("key ")) {
|
|
17587
18774
|
const sub = action.slice(4);
|
|
17588
18775
|
const appId2 = args[0] ?? "";
|
|
17589
|
-
const { data: app2 } = await ctx.call("getApp",
|
|
18776
|
+
const { data: app2 } = await ctx.call("getApp", { params: { app: appId2 } });
|
|
17590
18777
|
if (documentOf(app2.app).config.authentication.type !== "api_key")
|
|
17591
18778
|
fail(
|
|
17592
18779
|
"invalid_input",
|
|
17593
18780
|
"Only server applications support application keys."
|
|
17594
18781
|
);
|
|
17595
|
-
if (sub === "list") return (await ctx.call("listAppKeys",
|
|
18782
|
+
if (sub === "list") return (await ctx.call("listAppKeys", { params: { app: appId2 } })).data;
|
|
17596
18783
|
if (sub === "revoke") {
|
|
17597
18784
|
const keyId = args[1] ?? "";
|
|
17598
18785
|
await confirm(
|
|
17599
18786
|
`Revoke key ${keyId} for app ${appId2}? Clients using it will lose access.`,
|
|
17600
18787
|
flags
|
|
17601
18788
|
);
|
|
17602
|
-
return (await ctx.call("revokeAppKey",
|
|
18789
|
+
return (await ctx.call("revokeAppKey", { params: { app: appId2, key: keyId } })).data;
|
|
17603
18790
|
}
|
|
17604
18791
|
const name = await required2(flags, "name");
|
|
17605
18792
|
if (!name.trim() || name.length > 100)
|
|
17606
18793
|
fail("invalid_input", "Key name must be 1\u2013100 characters.");
|
|
17607
18794
|
const output = await ctx.keyOutput(
|
|
17608
|
-
|
|
18795
|
+
operationPath("createAppKey", { app: appId2 }),
|
|
17609
18796
|
{ name },
|
|
17610
18797
|
flags["key-output"]
|
|
17611
18798
|
);
|
|
17612
18799
|
try {
|
|
17613
|
-
const created = await ctx.create("createAppKey",
|
|
18800
|
+
const created = await ctx.create("createAppKey", { params: { app: appId2 }, body: { name } });
|
|
17614
18801
|
let applicationKey = created.keyMetadata;
|
|
17615
18802
|
if (!applicationKey) {
|
|
17616
18803
|
const minted = mintedKey(created.data);
|
|
@@ -17631,7 +18818,7 @@ ${snippet}`;
|
|
|
17631
18818
|
}
|
|
17632
18819
|
}
|
|
17633
18820
|
const appId = args[0] ?? "";
|
|
17634
|
-
const { data } = await ctx.call("getApp",
|
|
18821
|
+
const { data } = await ctx.call("getApp", { params: { app: appId } });
|
|
17635
18822
|
if (action === "show") return data;
|
|
17636
18823
|
const doc = documentOf(data.app);
|
|
17637
18824
|
if (action === "remove") {
|
|
@@ -17639,13 +18826,13 @@ ${snippet}`;
|
|
|
17639
18826
|
`Delete app ${data.app.name} (${appId})? Its keys, users and authentication state will be removed and clients will lose access.`,
|
|
17640
18827
|
flags
|
|
17641
18828
|
);
|
|
17642
|
-
return (await ctx.call("deleteApp",
|
|
18829
|
+
return (await ctx.call("deleteApp", { params: { app: appId }, query: { confirm: appId } })).data;
|
|
17643
18830
|
}
|
|
17644
18831
|
if (action === "check") {
|
|
17645
18832
|
const validation = await remoteValidation(ctx, doc, appId);
|
|
17646
|
-
const { data: providers } = await ctx.call("listProviders"
|
|
17647
|
-
const selected =
|
|
17648
|
-
(p) =>
|
|
18833
|
+
const { data: providers } = await ctx.call("listProviders");
|
|
18834
|
+
const selected = providers.providers.filter(
|
|
18835
|
+
(p) => providerPolicyFor(doc.config.routing, p.slug) !== void 0
|
|
17649
18836
|
);
|
|
17650
18837
|
return {
|
|
17651
18838
|
appId,
|
|
@@ -17656,7 +18843,7 @@ ${snippet}`;
|
|
|
17656
18843
|
slug: p.slug,
|
|
17657
18844
|
status: p.status
|
|
17658
18845
|
})),
|
|
17659
|
-
ready: doc.status === "active" && selected
|
|
18846
|
+
ready: doc.status === "active" && reachableProviders(doc.config.routing, selected).length > 0 && !data.config_error,
|
|
17660
18847
|
limitations: [
|
|
17661
18848
|
"No inference was sent.",
|
|
17662
18849
|
"Physical device attestation, issuer login, subscription entitlement and upstream credentials were not exercised."
|
|
@@ -17677,7 +18864,7 @@ ${snippet}`;
|
|
|
17677
18864
|
const notes2 = [];
|
|
17678
18865
|
let example;
|
|
17679
18866
|
if (flags.endpoint) {
|
|
17680
|
-
const endpoint = doc.config.endpoints
|
|
18867
|
+
const endpoint = doc.config.endpoints[flags.endpoint];
|
|
17681
18868
|
if (!endpoint)
|
|
17682
18869
|
fail("endpoint_not_found", "Choose an existing named endpoint.");
|
|
17683
18870
|
const responses = endpoint.api_style === "responses";
|
|
@@ -17688,14 +18875,9 @@ ${snippet}`;
|
|
|
17688
18875
|
gaps: responses ? [] : ["body"]
|
|
17689
18876
|
};
|
|
17690
18877
|
} else {
|
|
17691
|
-
const routing =
|
|
17692
|
-
|
|
17693
|
-
|
|
17694
|
-
};
|
|
17695
|
-
const { data: all } = await ctx.call("listProviders", []);
|
|
17696
|
-
const reachable = all.providers.filter(
|
|
17697
|
-
(p) => p.status === "active" && (routing.providerMode === "all" || Object.hasOwn(routing.providers ?? {}, p.slug))
|
|
17698
|
-
);
|
|
18878
|
+
const routing = doc.config.routing;
|
|
18879
|
+
const { data: all } = await ctx.call("listProviders");
|
|
18880
|
+
const reachable = reachableProviders(routing, all.providers);
|
|
17699
18881
|
const requested = typeof flags.provider === "string" ? flags.provider : void 0;
|
|
17700
18882
|
if (requested && !reachable.some((p) => p.slug === requested))
|
|
17701
18883
|
fail(
|
|
@@ -17703,7 +18885,7 @@ ${snippet}`;
|
|
|
17703
18885
|
all.providers.some((p) => p.slug === requested) ? "That provider is disabled or outside this app's proxy policy." : "No provider has that slug.",
|
|
17704
18886
|
"Run agw provider list for the slugs, and agw app show <id> for the policy."
|
|
17705
18887
|
);
|
|
17706
|
-
const { data: catalog } = await ctx.call("listModelPrices"
|
|
18888
|
+
const { data: catalog } = await ctx.call("listModelPrices");
|
|
17707
18889
|
const providers = requested ? reachable.filter((p) => p.slug === requested) : reachable;
|
|
17708
18890
|
example = firstRequest(routing, providers, catalog.prices);
|
|
17709
18891
|
if (!requested && reachable.length > 1)
|
|
@@ -17713,17 +18895,12 @@ ${snippet}`;
|
|
|
17713
18895
|
const clientUrl = ctx.active?.deployment?.apiUrl ?? ctx.url;
|
|
17714
18896
|
let snippet;
|
|
17715
18897
|
if (ios) {
|
|
17716
|
-
|
|
17717
|
-
const issuer = endUser?.source === "issuer";
|
|
17718
|
-
if (issuer)
|
|
17719
|
-
notes2.push(
|
|
17720
|
-
"Replace yourIdentitySDK.currentIDToken(forceRefresh: forceRefresh) with your configured issuer integration."
|
|
17721
|
-
);
|
|
18898
|
+
if (swiftSignsInUsers(doc.config.authentication)) notes2.push(ISSUER_TOKEN_NOTE);
|
|
17722
18899
|
snippet = swiftSnippet({
|
|
17723
18900
|
baseUrl: clientUrl,
|
|
17724
18901
|
appId,
|
|
17725
18902
|
example,
|
|
17726
|
-
|
|
18903
|
+
authentication: doc.config.authentication,
|
|
17727
18904
|
notes: [
|
|
17728
18905
|
"Swift package: https://github.com/maxceem/app-ai-gateway-swift (from: 1.0.0)",
|
|
17729
18906
|
"Enable App Attest and test on a supported physical device.",
|
|
@@ -17769,6 +18946,7 @@ var RECORDED_RESULT = {
|
|
|
17769
18946
|
createProviderGateway: ProviderGatewayResponseSchema
|
|
17770
18947
|
};
|
|
17771
18948
|
var recordedResultFor = RECORDED_RESULT;
|
|
18949
|
+
var pathFor = operationPath;
|
|
17772
18950
|
var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 425, 429]);
|
|
17773
18951
|
var RECEIPT_BOUND_STATUS = /* @__PURE__ */ new Set([409, 410]);
|
|
17774
18952
|
function definitiveRefusal(error51) {
|
|
@@ -17806,13 +18984,12 @@ var Context = class {
|
|
|
17806
18984
|
* this is what keeps an undocumented server field — or anything an unexpected
|
|
17807
18985
|
* response carries — out of the values the commands go on to print.
|
|
17808
18986
|
*/
|
|
17809
|
-
async publicCall(name, params,
|
|
17810
|
-
const descriptor = operations[name];
|
|
18987
|
+
async publicCall(name, { params, query, body, key, url: url2, headers } = {}) {
|
|
17811
18988
|
const wire = await this.transport.request(
|
|
17812
18989
|
url2 ?? this.url,
|
|
17813
|
-
|
|
18990
|
+
pathFor(name, params, query),
|
|
17814
18991
|
{
|
|
17815
|
-
method:
|
|
18992
|
+
method: CATALOG[name].method,
|
|
17816
18993
|
...body === void 0 ? {} : { body },
|
|
17817
18994
|
...key === void 0 ? {} : { key },
|
|
17818
18995
|
...headers === void 0 ? {} : { headers }
|
|
@@ -17821,7 +18998,7 @@ var Context = class {
|
|
|
17821
18998
|
return { data: this.parse(name, wire.data) };
|
|
17822
18999
|
}
|
|
17823
19000
|
parse(name, data) {
|
|
17824
|
-
const parsed =
|
|
19001
|
+
const parsed = CATALOG[name].response.safeParse(data);
|
|
17825
19002
|
if (!parsed.success)
|
|
17826
19003
|
fail(
|
|
17827
19004
|
"invalid_response",
|
|
@@ -17844,11 +19021,8 @@ var Context = class {
|
|
|
17844
19021
|
return parsed.data;
|
|
17845
19022
|
}
|
|
17846
19023
|
/** The same call, with the connection's management credential attached. */
|
|
17847
|
-
async call(name,
|
|
17848
|
-
return this.publicCall(name,
|
|
17849
|
-
...options,
|
|
17850
|
-
key: options.key ?? this.credential(name)
|
|
17851
|
-
});
|
|
19024
|
+
async call(name, options = {}) {
|
|
19025
|
+
return this.publicCall(name, { ...options, key: options.key ?? this.credential(name) });
|
|
17852
19026
|
}
|
|
17853
19027
|
/**
|
|
17854
19028
|
* The current management credential, or the refusal that names the way back.
|
|
@@ -17931,7 +19105,7 @@ var Context = class {
|
|
|
17931
19105
|
* The receipt in protected state holds the original server answer until the
|
|
17932
19106
|
* command has printed it; `complete` is what releases it.
|
|
17933
19107
|
*/
|
|
17934
|
-
async create(name, params, body) {
|
|
19108
|
+
async create(name, { params, body }) {
|
|
17935
19109
|
if (!this.active?.credential)
|
|
17936
19110
|
fail(
|
|
17937
19111
|
"login_required",
|
|
@@ -17939,8 +19113,7 @@ var Context = class {
|
|
|
17939
19113
|
"Run agw account login.",
|
|
17940
19114
|
4
|
|
17941
19115
|
);
|
|
17942
|
-
const
|
|
17943
|
-
const path = descriptor.path(...params);
|
|
19116
|
+
const path = pathFor(name, params);
|
|
17944
19117
|
const mutation2 = await this.prepareCreate(path, body);
|
|
17945
19118
|
if (mutation2.accountId === null) {
|
|
17946
19119
|
mutation2.accountId = this.active.account?.id ?? null;
|
|
@@ -17979,7 +19152,7 @@ var Context = class {
|
|
|
17979
19152
|
const keyRecord = recoveredKey(name, recovered);
|
|
17980
19153
|
if (keyRecord) {
|
|
17981
19154
|
const appId = recoveredAppId(name, recovered, params);
|
|
17982
|
-
const { data: existing } = await this.call("listAppKeys",
|
|
19155
|
+
const { data: existing } = await this.call("listAppKeys", { params: { app: appId } });
|
|
17983
19156
|
if (!existing.keys.some(
|
|
17984
19157
|
(key) => key.id === keyRecord.id && key.status === "active"
|
|
17985
19158
|
)) {
|
|
@@ -17997,7 +19170,8 @@ var Context = class {
|
|
|
17997
19170
|
}
|
|
17998
19171
|
data = recovered;
|
|
17999
19172
|
} else {
|
|
18000
|
-
const response = await this.call(name,
|
|
19173
|
+
const response = await this.call(name, {
|
|
19174
|
+
...params === void 0 ? {} : { params },
|
|
18001
19175
|
body,
|
|
18002
19176
|
headers: {
|
|
18003
19177
|
"Idempotency-Key": mutation2.id,
|
|
@@ -18082,11 +19256,10 @@ var Context = class {
|
|
|
18082
19256
|
4
|
|
18083
19257
|
);
|
|
18084
19258
|
const { idempotencyKey, pollToken } = reservation;
|
|
18085
|
-
const { data } = await this.publicCall(
|
|
18086
|
-
|
|
18087
|
-
|
|
18088
|
-
|
|
18089
|
-
).catch(async (error51) => {
|
|
19259
|
+
const { data } = await this.publicCall("bootstrapCliAccount", {
|
|
19260
|
+
body: { idempotencyKey, pollToken },
|
|
19261
|
+
url: CLOUD
|
|
19262
|
+
}).catch(async (error51) => {
|
|
18090
19263
|
if (definitiveRefusal(error51)) {
|
|
18091
19264
|
delete this.state.bootstrap;
|
|
18092
19265
|
await this.save().catch(() => {
|
|
@@ -18097,7 +19270,7 @@ var Context = class {
|
|
|
18097
19270
|
await this.select(CLOUD, data);
|
|
18098
19271
|
this.onboarding = {
|
|
18099
19272
|
account: data.account,
|
|
18100
|
-
|
|
19273
|
+
unclaimedAccess: data.unclaimedAccess,
|
|
18101
19274
|
deployment: data.deployment
|
|
18102
19275
|
};
|
|
18103
19276
|
delete this.state.bootstrap;
|
|
@@ -18133,7 +19306,7 @@ var Context = class {
|
|
|
18133
19306
|
const token = await secret(this.flags, "Management API key");
|
|
18134
19307
|
if (!token)
|
|
18135
19308
|
fail("input_required", "A management API key is required.", "Supply it with --key-stdin.");
|
|
18136
|
-
const { data } = await this.publicCall("getCliAccount",
|
|
19309
|
+
const { data } = await this.publicCall("getCliAccount", { key: token, url: url2 });
|
|
18137
19310
|
await this.select(url2, { ...data, credential: { token } });
|
|
18138
19311
|
return { connected: true, ...data };
|
|
18139
19312
|
}
|
|
@@ -18161,12 +19334,14 @@ var Context = class {
|
|
|
18161
19334
|
};
|
|
18162
19335
|
await this.save();
|
|
18163
19336
|
}
|
|
18164
|
-
const
|
|
18165
|
-
const { data: capabilities } = await this.publicCall("getCliCapabilities",
|
|
19337
|
+
const operation2 = this.state.operations[localId];
|
|
19338
|
+
const { data: capabilities } = await this.publicCall("getCliCapabilities", {
|
|
18166
19339
|
url: target
|
|
18167
19340
|
});
|
|
18168
|
-
const { data } = await this.publicCall("createCliOperation",
|
|
18169
|
-
|
|
19341
|
+
const { data } = await this.publicCall("createCliOperation", {
|
|
19342
|
+
// One member of the per-kind union: `payload` was typed by `kind` above,
|
|
19343
|
+
// which is the correlation the compiler cannot follow through a generic.
|
|
19344
|
+
body: { kind, payload, pollToken: operation2.pollToken },
|
|
18170
19345
|
url: target,
|
|
18171
19346
|
...authenticated ? { key: this.active?.credential } : {}
|
|
18172
19347
|
});
|
|
@@ -18179,7 +19354,7 @@ var Context = class {
|
|
|
18179
19354
|
"invalid_response",
|
|
18180
19355
|
"The handoff URL is not on this deployment\u2019s trusted console origin."
|
|
18181
19356
|
);
|
|
18182
|
-
this.state.operations[data.id] = { ...
|
|
19357
|
+
this.state.operations[data.id] = { ...operation2, phase: "pending" };
|
|
18183
19358
|
delete this.state.operations[localId];
|
|
18184
19359
|
await this.save();
|
|
18185
19360
|
if (!this.flags["no-open"]) await openBrowser(data.url);
|
|
@@ -18193,34 +19368,35 @@ Operation: ${data.id}
|
|
|
18193
19368
|
return this.wait(data.id, 300);
|
|
18194
19369
|
}
|
|
18195
19370
|
async poll(id) {
|
|
18196
|
-
const
|
|
18197
|
-
if (!
|
|
19371
|
+
const operation2 = this.state.operations[id];
|
|
19372
|
+
if (!operation2)
|
|
18198
19373
|
fail(
|
|
18199
19374
|
"operation_unknown",
|
|
18200
19375
|
"This operation has no locally stored polling authorization.",
|
|
18201
19376
|
"Resume it from the machine that initiated the operation.",
|
|
18202
19377
|
4
|
|
18203
19378
|
);
|
|
18204
|
-
if (this.active && this.active.url !==
|
|
19379
|
+
if (this.active && this.active.url !== operation2.url)
|
|
18205
19380
|
fail(
|
|
18206
19381
|
"operation_context",
|
|
18207
19382
|
"This operation belongs to another selected deployment.",
|
|
18208
19383
|
"Reconnect explicitly to the operation\u2019s originating deployment.",
|
|
18209
19384
|
4
|
|
18210
19385
|
);
|
|
18211
|
-
const { data } = await this.publicCall("pollCliOperation",
|
|
18212
|
-
|
|
18213
|
-
|
|
19386
|
+
const { data } = await this.publicCall("pollCliOperation", {
|
|
19387
|
+
params: { id },
|
|
19388
|
+
key: operation2.pollToken,
|
|
19389
|
+
url: operation2.url
|
|
18214
19390
|
});
|
|
18215
|
-
if (data.state === "completed" && !
|
|
18216
|
-
if ((this.state.generation ?? 0) !==
|
|
19391
|
+
if (data.state === "completed" && !operation2.completed && operation2.kind === "claim") {
|
|
19392
|
+
if ((this.state.generation ?? 0) !== operation2.generation)
|
|
18217
19393
|
fail(
|
|
18218
19394
|
"operation_context",
|
|
18219
19395
|
"The active connection changed after this operation began.",
|
|
18220
19396
|
"Start a new login for the intended connection.",
|
|
18221
19397
|
4
|
|
18222
19398
|
);
|
|
18223
|
-
|
|
19399
|
+
operation2.completed = true;
|
|
18224
19400
|
if (this.active && data.account) this.active.account = data.account;
|
|
18225
19401
|
await this.save();
|
|
18226
19402
|
}
|
|
@@ -18260,7 +19436,7 @@ function recoveredKey(name, data) {
|
|
|
18260
19436
|
}
|
|
18261
19437
|
function recoveredAppId(name, data, params) {
|
|
18262
19438
|
if (name === "createApp") return data.app.id;
|
|
18263
|
-
return String(params
|
|
19439
|
+
return String(params?.app);
|
|
18264
19440
|
}
|
|
18265
19441
|
async function openBrowser(url2) {
|
|
18266
19442
|
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "rundll32" : "xdg-open";
|
|
@@ -18281,14 +19457,9 @@ async function openBrowser(url2) {
|
|
|
18281
19457
|
}
|
|
18282
19458
|
|
|
18283
19459
|
// src/deployment.ts
|
|
18284
|
-
import { spawn as spawn2 } from "node:child_process";
|
|
18285
|
-
import { stripVTControlCharacters } from "node:util";
|
|
18286
|
-
import { createRequire as createRequire2 } from "node:module";
|
|
18287
19460
|
import { randomUUID } from "node:crypto";
|
|
18288
|
-
import {
|
|
18289
|
-
import {
|
|
18290
|
-
import { join as join3 } from "node:path";
|
|
18291
|
-
import { setTimeout as delay3 } from "node:timers/promises";
|
|
19461
|
+
import { writeFile as writeFile3, unlink as unlink2 } from "node:fs/promises";
|
|
19462
|
+
import { join as join4 } from "node:path";
|
|
18292
19463
|
|
|
18293
19464
|
// src/release.ts
|
|
18294
19465
|
import { readFile as readFile3, lstat as lstat2, mkdir as mkdir2, rename as rename2, rm, writeFile } from "node:fs/promises";
|
|
@@ -18530,7 +19701,12 @@ async function release(requested = VERSION, base = dirname(fileURLToPath(import.
|
|
|
18530
19701
|
};
|
|
18531
19702
|
}
|
|
18532
19703
|
|
|
18533
|
-
// src/
|
|
19704
|
+
// src/cloudflare.ts
|
|
19705
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
19706
|
+
import { stripVTControlCharacters } from "node:util";
|
|
19707
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
19708
|
+
import { tmpdir } from "node:os";
|
|
19709
|
+
import { setTimeout as delay3 } from "node:timers/promises";
|
|
18534
19710
|
var require2 = createRequire2(import.meta.url);
|
|
18535
19711
|
var OUTPUT_TAIL = 5e3;
|
|
18536
19712
|
function wranglerFailure(args, code, output, redact = []) {
|
|
@@ -18751,11 +19927,57 @@ async function resultOf(cf, path, options) {
|
|
|
18751
19927
|
if (!data) fail("cloudflare_error", "Cloudflare returned no result.");
|
|
18752
19928
|
return data.result;
|
|
18753
19929
|
}
|
|
18754
|
-
function
|
|
18755
|
-
|
|
18756
|
-
if (
|
|
18757
|
-
|
|
18758
|
-
return
|
|
19930
|
+
function transient(error51) {
|
|
19931
|
+
if (!(error51 instanceof CliError)) return false;
|
|
19932
|
+
if (error51.code === "connection_failed") return true;
|
|
19933
|
+
const status2 = error51.details?.status;
|
|
19934
|
+
return typeof status2 === "number" && status2 >= 500;
|
|
19935
|
+
}
|
|
19936
|
+
async function whileWarming(run, sleep = delay3, report = (line) => process.stderr.write(line + "\n")) {
|
|
19937
|
+
let wait = 1e3;
|
|
19938
|
+
for (let attempt = 0; ; attempt++) {
|
|
19939
|
+
try {
|
|
19940
|
+
return await run();
|
|
19941
|
+
} catch (error51) {
|
|
19942
|
+
if (attempt === 5 || !transient(error51)) throw error51;
|
|
19943
|
+
const { code, details } = error51;
|
|
19944
|
+
const status2 = details?.status;
|
|
19945
|
+
report(
|
|
19946
|
+
`Deployment is still coming up (${code}${status2 === void 0 ? "" : ` ${status2}`}); retrying in ${wait / 1e3}s.`
|
|
19947
|
+
);
|
|
19948
|
+
await sleep(wait);
|
|
19949
|
+
wait = Math.min(wait * 2, 8e3);
|
|
19950
|
+
}
|
|
19951
|
+
}
|
|
19952
|
+
}
|
|
19953
|
+
|
|
19954
|
+
// src/installation.ts
|
|
19955
|
+
import { cp, writeFile as writeFile2 } from "node:fs/promises";
|
|
19956
|
+
import { join as join3 } from "node:path";
|
|
19957
|
+
import { setTimeout as delay4 } from "node:timers/promises";
|
|
19958
|
+
async function advance(ctx, journal, phase, patch = {}) {
|
|
19959
|
+
const fields = journal;
|
|
19960
|
+
for (const [field, value] of Object.entries(patch)) {
|
|
19961
|
+
if (value === void 0) delete fields[field];
|
|
19962
|
+
else fields[field] = value;
|
|
19963
|
+
}
|
|
19964
|
+
journal.phase = phase;
|
|
19965
|
+
await ctx.save();
|
|
19966
|
+
}
|
|
19967
|
+
async function prepare(ctx, installations, draft) {
|
|
19968
|
+
const journal = { ...draft, phase: "prepared" };
|
|
19969
|
+
installations[draft.id] = journal;
|
|
19970
|
+
await ctx.save();
|
|
19971
|
+
return journal;
|
|
19972
|
+
}
|
|
19973
|
+
function selectedInstallation(ctx) {
|
|
19974
|
+
if (ctx.active?.deployment?.mode !== "self_hosted")
|
|
19975
|
+
fail("not_self_hosted", "This command requires a selected self-hosted deployment.");
|
|
19976
|
+
return {
|
|
19977
|
+
url: ctx.url,
|
|
19978
|
+
deploymentId: ctx.active.deployment.id,
|
|
19979
|
+
installations: ctx.state.installations ??= {}
|
|
19980
|
+
};
|
|
18759
19981
|
}
|
|
18760
19982
|
async function configFile(ctx, journal, artifact) {
|
|
18761
19983
|
const directory = join3(ctx.store.directory, "deployments", journal.id);
|
|
@@ -18792,33 +20014,10 @@ async function configFile(ctx, journal, artifact) {
|
|
|
18792
20014
|
await writeFile2(path, JSON.stringify(config2, null, 2), { mode: 384 });
|
|
18793
20015
|
return { path, directory, config: config2 };
|
|
18794
20016
|
}
|
|
18795
|
-
function transient(error51) {
|
|
18796
|
-
if (!(error51 instanceof CliError)) return false;
|
|
18797
|
-
if (error51.code === "connection_failed") return true;
|
|
18798
|
-
const status2 = error51.details?.status;
|
|
18799
|
-
return typeof status2 === "number" && status2 >= 500;
|
|
18800
|
-
}
|
|
18801
|
-
async function whileWarming(run, sleep = delay3, report = (line) => process.stderr.write(line + "\n")) {
|
|
18802
|
-
let wait = 1e3;
|
|
18803
|
-
for (let attempt = 0; ; attempt++) {
|
|
18804
|
-
try {
|
|
18805
|
-
return await run();
|
|
18806
|
-
} catch (error51) {
|
|
18807
|
-
if (attempt === 5 || !transient(error51)) throw error51;
|
|
18808
|
-
const { code, details } = error51;
|
|
18809
|
-
const status2 = details?.status;
|
|
18810
|
-
report(
|
|
18811
|
-
`Deployment is still coming up (${code}${status2 === void 0 ? "" : ` ${status2}`}); retrying in ${wait / 1e3}s.`
|
|
18812
|
-
);
|
|
18813
|
-
await sleep(wait);
|
|
18814
|
-
wait = Math.min(wait * 2, 8e3);
|
|
18815
|
-
}
|
|
18816
|
-
}
|
|
18817
|
-
}
|
|
18818
20017
|
async function verifyDeployment(ctx, url2, id) {
|
|
18819
20018
|
for (let attempt = 0; attempt < 20; attempt++) {
|
|
18820
20019
|
try {
|
|
18821
|
-
const { data } = await ctx.publicCall("getCliCapabilities",
|
|
20020
|
+
const { data } = await ctx.publicCall("getCliCapabilities", { url: url2 });
|
|
18822
20021
|
if (data.deployment.id !== id)
|
|
18823
20022
|
fail(
|
|
18824
20023
|
"deployment_identity_mismatch",
|
|
@@ -18828,7 +20027,7 @@ async function verifyDeployment(ctx, url2, id) {
|
|
|
18828
20027
|
} catch (error51) {
|
|
18829
20028
|
if (error51 instanceof CliError && error51.code === "deployment_identity_mismatch") throw error51;
|
|
18830
20029
|
if (attempt === 19) throw error51;
|
|
18831
|
-
await
|
|
20030
|
+
await delay4(1500);
|
|
18832
20031
|
}
|
|
18833
20032
|
}
|
|
18834
20033
|
fail("deployment_identity_mismatch", "The endpoint never identified the expected deployment.");
|
|
@@ -18852,10 +20051,10 @@ async function checkDomain(cf, journal, domain2) {
|
|
|
18852
20051
|
if (current && current.service !== journal.name)
|
|
18853
20052
|
fail("domain_in_use", "The hostname is attached to a different Worker.");
|
|
18854
20053
|
if (!current) {
|
|
18855
|
-
const
|
|
20054
|
+
const records = await cf.all(
|
|
18856
20055
|
"/zones/" + zone.id + "/dns_records?name=" + encodeURIComponent(domain2)
|
|
18857
20056
|
);
|
|
18858
|
-
if (
|
|
20057
|
+
if (records.length)
|
|
18859
20058
|
fail("domain_in_use", "The hostname has existing DNS records; no records were changed.");
|
|
18860
20059
|
}
|
|
18861
20060
|
return zone;
|
|
@@ -18885,9 +20084,10 @@ async function attachDomain(ctx, cf, journal, domain2, artifact, dryRun) {
|
|
|
18885
20084
|
zone_id: zone.id
|
|
18886
20085
|
}
|
|
18887
20086
|
});
|
|
18888
|
-
|
|
18889
|
-
|
|
18890
|
-
|
|
20087
|
+
await advance(ctx, journal, journal.phase, {
|
|
20088
|
+
domains: [.../* @__PURE__ */ new Set([...journal.domains ?? [], domain2])],
|
|
20089
|
+
vars: { ...journal.vars, CLI_CONSOLE_ORIGIN: "https://" + domain2 }
|
|
20090
|
+
});
|
|
18891
20091
|
const file2 = await configFile(ctx, journal, artifact);
|
|
18892
20092
|
await cf.run(["deploy", "--config", file2.path, "--keep-vars"], {
|
|
18893
20093
|
cwd: file2.directory
|
|
@@ -18954,14 +20154,11 @@ async function retireInstallationSecrets(ctx, journal, flags) {
|
|
|
18954
20154
|
if (flags["dry-run"] || journal.phase !== "ready" || !journal.secrets) return;
|
|
18955
20155
|
const kek = journal.secrets["SECRET_VAULT_LOCAL_KEK_V1"];
|
|
18956
20156
|
if (kek) await ctx.store.vaultKey(journal.id, kek);
|
|
18957
|
-
|
|
18958
|
-
await ctx.save();
|
|
20157
|
+
await advance(ctx, journal, journal.phase, { secrets: void 0 });
|
|
18959
20158
|
}
|
|
18960
|
-
async function matchExisting(ctx, cf, flags) {
|
|
18961
|
-
|
|
18962
|
-
|
|
18963
|
-
const installations = ctx.state.installations ??= {};
|
|
18964
|
-
let journal = installations[ctx.active.deployment.id];
|
|
20159
|
+
async function matchExisting(ctx, cf, flags, target) {
|
|
20160
|
+
const { installations } = target;
|
|
20161
|
+
let journal = installations[target.deploymentId];
|
|
18965
20162
|
if (journal) {
|
|
18966
20163
|
const settings2 = await resultOf(
|
|
18967
20164
|
cf,
|
|
@@ -18972,7 +20169,7 @@ async function matchExisting(ctx, cf, flags) {
|
|
|
18972
20169
|
"deployment_identity_mismatch",
|
|
18973
20170
|
"The Cloudflare Worker no longer matches this installation journal."
|
|
18974
20171
|
);
|
|
18975
|
-
const capabilities2 = await verifyDeployment(ctx,
|
|
20172
|
+
const capabilities2 = await verifyDeployment(ctx, target.url, journal.id);
|
|
18976
20173
|
const inventory2 = await currentInventory(
|
|
18977
20174
|
cf,
|
|
18978
20175
|
journal.accountId,
|
|
@@ -19002,7 +20199,7 @@ async function matchExisting(ctx, cf, flags) {
|
|
|
19002
20199
|
const deploymentId = settings.bindings?.find(
|
|
19003
20200
|
(b) => b.name === "DEPLOYMENT_ID" && b.type === "plain_text"
|
|
19004
20201
|
)?.text;
|
|
19005
|
-
if (deploymentId !==
|
|
20202
|
+
if (deploymentId !== target.deploymentId)
|
|
19006
20203
|
fail(
|
|
19007
20204
|
"deployment_identity_mismatch",
|
|
19008
20205
|
"Cloudflare Worker identity does not match the selected deployment."
|
|
@@ -19014,7 +20211,9 @@ async function matchExisting(ctx, cf, flags) {
|
|
|
19014
20211
|
`Bind deployment ${deploymentId} to Cloudflare account ${accountId}, Worker ${name} and D1 ${db.id}?`,
|
|
19015
20212
|
flags
|
|
19016
20213
|
);
|
|
19017
|
-
const { data: capabilities } = await ctx.publicCall("getCliCapabilities",
|
|
20214
|
+
const { data: capabilities } = await ctx.publicCall("getCliCapabilities", {
|
|
20215
|
+
url: target.url
|
|
20216
|
+
});
|
|
19018
20217
|
const inventory = await currentInventory(cf, accountId, name, settings);
|
|
19019
20218
|
journal = {
|
|
19020
20219
|
id: deploymentId,
|
|
@@ -19031,52 +20230,170 @@ async function matchExisting(ctx, cf, flags) {
|
|
|
19031
20230
|
}
|
|
19032
20231
|
return journal;
|
|
19033
20232
|
}
|
|
19034
|
-
|
|
20233
|
+
|
|
20234
|
+
// src/deployment.ts
|
|
20235
|
+
function hostname4(value) {
|
|
20236
|
+
const u = origin("https://" + value);
|
|
20237
|
+
if (new URL(u).hostname !== value || !value.includes(".") || value.includes(":"))
|
|
20238
|
+
fail("invalid_input", "Supply a hostname without protocol, path or port.");
|
|
20239
|
+
return value;
|
|
20240
|
+
}
|
|
20241
|
+
async function authorized(flags, cf, loadRelease) {
|
|
19035
20242
|
const artifact = await loadRelease(flags.version, void 0, {
|
|
19036
20243
|
archive: flags["release-archive"]
|
|
19037
20244
|
});
|
|
19038
20245
|
await cf.authenticate(flags);
|
|
19039
|
-
|
|
19040
|
-
|
|
19041
|
-
|
|
19042
|
-
|
|
19043
|
-
|
|
19044
|
-
|
|
19045
|
-
|
|
19046
|
-
flags
|
|
19047
|
-
);
|
|
19048
|
-
return attachDomain(ctx, cf, journal2, domain3, artifact, Boolean(flags["dry-run"]));
|
|
19049
|
-
}
|
|
19050
|
-
if (!artifact.manifest.upgradeFrom.includes(journal2.version))
|
|
19051
|
-
fail(
|
|
19052
|
-
"unsupported_upgrade",
|
|
19053
|
-
"This release cannot upgrade the selected database schema.",
|
|
19054
|
-
"Use a supported migration release; no data was changed."
|
|
19055
|
-
);
|
|
19056
|
-
const plan2 = {
|
|
19057
|
-
worker: journal2.name,
|
|
19058
|
-
accountId: journal2.accountId,
|
|
19059
|
-
databaseId: journal2.databaseId,
|
|
19060
|
-
fromVersion: journal2.version,
|
|
19061
|
-
toVersion: artifact.manifest.version
|
|
19062
|
-
};
|
|
19063
|
-
if (flags["dry-run"]) return { dryRun: true, ...plan2 };
|
|
20246
|
+
return artifact;
|
|
20247
|
+
}
|
|
20248
|
+
async function deploymentDomain(ctx, flags, cf = new Cloudflare(), loadRelease = release) {
|
|
20249
|
+
const artifact = await authorized(flags, cf, loadRelease);
|
|
20250
|
+
const journal = await matchExisting(ctx, cf, flags, selectedInstallation(ctx));
|
|
20251
|
+
const domain2 = hostname4(flags.hostname ?? await prompt("Custom hostname", flags));
|
|
20252
|
+
if (!flags["dry-run"])
|
|
19064
20253
|
await confirm(
|
|
19065
|
-
`
|
|
20254
|
+
`Attach ${domain2} to ${journal.name} in Cloudflare account ${journal.accountId}?`,
|
|
19066
20255
|
flags
|
|
19067
20256
|
);
|
|
19068
|
-
|
|
19069
|
-
|
|
19070
|
-
|
|
20257
|
+
return attachDomain(ctx, cf, journal, domain2, artifact, Boolean(flags["dry-run"]));
|
|
20258
|
+
}
|
|
20259
|
+
async function deploymentUpdate(ctx, flags, cf = new Cloudflare(), loadRelease = release) {
|
|
20260
|
+
const artifact = await authorized(flags, cf, loadRelease);
|
|
20261
|
+
const journal = await matchExisting(ctx, cf, flags, selectedInstallation(ctx));
|
|
20262
|
+
if (!artifact.manifest.upgradeFrom.includes(journal.version))
|
|
20263
|
+
fail(
|
|
20264
|
+
"unsupported_upgrade",
|
|
20265
|
+
"This release cannot upgrade the selected database schema.",
|
|
20266
|
+
"Use a supported migration release; no data was changed."
|
|
20267
|
+
);
|
|
20268
|
+
const plan = {
|
|
20269
|
+
worker: journal.name,
|
|
20270
|
+
accountId: journal.accountId,
|
|
20271
|
+
databaseId: journal.databaseId,
|
|
20272
|
+
fromVersion: journal.version,
|
|
20273
|
+
toVersion: artifact.manifest.version
|
|
20274
|
+
};
|
|
20275
|
+
if (flags["dry-run"]) return { dryRun: true, ...plan };
|
|
20276
|
+
await confirm(
|
|
20277
|
+
`Update ${journal.name} to ${artifact.manifest.version}? Database migrations cannot be rolled back automatically.`,
|
|
20278
|
+
flags
|
|
20279
|
+
);
|
|
20280
|
+
const file2 = await configFile(ctx, journal, artifact);
|
|
20281
|
+
await cf.run(["d1", "migrations", "apply", "DB", "--remote", "--config", file2.path], {
|
|
20282
|
+
cwd: file2.directory
|
|
20283
|
+
});
|
|
20284
|
+
await cf.run(["deploy", "--config", file2.path, "--keep-vars"], {
|
|
20285
|
+
cwd: file2.directory
|
|
20286
|
+
});
|
|
20287
|
+
await verifyDeployment(ctx, ctx.url, journal.id);
|
|
20288
|
+
await advance(ctx, journal, journal.phase, { version: artifact.manifest.version });
|
|
20289
|
+
return { updated: true, ...plan };
|
|
20290
|
+
}
|
|
20291
|
+
function newInstallation(name, accountId, url2, version2) {
|
|
20292
|
+
const id = randomUUID();
|
|
20293
|
+
return {
|
|
20294
|
+
id,
|
|
20295
|
+
name,
|
|
20296
|
+
accountId,
|
|
20297
|
+
databaseName: name + "-" + id.slice(0, 8),
|
|
20298
|
+
version: version2,
|
|
20299
|
+
url: url2,
|
|
20300
|
+
vars: { CLI_CONSOLE_ORIGIN: url2 },
|
|
20301
|
+
bootstrap: { idempotencyKey: randomToken(), pollToken: randomToken() },
|
|
20302
|
+
// Held only until the Worker has them and the install is ready; the vault
|
|
20303
|
+
// key is the one that outlives setup, and it lives in its own file.
|
|
20304
|
+
secrets: {
|
|
20305
|
+
JWT_SECRET: randomToken(),
|
|
20306
|
+
BETTER_AUTH_SECRET: randomToken()
|
|
20307
|
+
}
|
|
20308
|
+
};
|
|
20309
|
+
}
|
|
20310
|
+
async function completeInstallation(ctx, cf, flags, ready, artifact, installations) {
|
|
20311
|
+
await retireInstallationSecrets(ctx, ready, flags);
|
|
20312
|
+
let journal = ready;
|
|
20313
|
+
if (journal.pendingDomain) {
|
|
20314
|
+
journal = await matchExisting(ctx, cf, flags, {
|
|
20315
|
+
url: journal.url ?? ctx.url,
|
|
20316
|
+
deploymentId: journal.id,
|
|
20317
|
+
installations
|
|
20318
|
+
});
|
|
20319
|
+
if (flags["dry-run"])
|
|
20320
|
+
return {
|
|
20321
|
+
installed: true,
|
|
20322
|
+
pendingDomain: journal.pendingDomain,
|
|
20323
|
+
dryRun: true
|
|
20324
|
+
};
|
|
20325
|
+
await attachDomain(ctx, cf, journal, journal.pendingDomain, artifact, false);
|
|
20326
|
+
await advance(ctx, journal, journal.phase, { pendingDomain: void 0 });
|
|
20327
|
+
}
|
|
20328
|
+
await verifyDeployment(ctx, journal.url ?? ctx.url, journal.id);
|
|
20329
|
+
return {
|
|
20330
|
+
installed: true,
|
|
20331
|
+
deploymentId: journal.id,
|
|
20332
|
+
name: journal.name,
|
|
20333
|
+
accountId: journal.accountId,
|
|
20334
|
+
url: journal.url ?? ctx.url,
|
|
20335
|
+
note: "This installation is already complete. Use deployment connect to authenticate or deployment update to change its release."
|
|
20336
|
+
};
|
|
20337
|
+
}
|
|
20338
|
+
async function install(ctx, cf, journal, artifact, url2, domain2) {
|
|
20339
|
+
if (!journal.databaseId) {
|
|
20340
|
+
const databases = await cf.all(
|
|
20341
|
+
"/accounts/" + journal.accountId + "/d1/database"
|
|
20342
|
+
);
|
|
20343
|
+
let database = databases.find((d) => d.name === journal.databaseName);
|
|
20344
|
+
if (!database)
|
|
20345
|
+
database = await resultOf(
|
|
20346
|
+
cf,
|
|
20347
|
+
"/accounts/" + journal.accountId + "/d1/database",
|
|
20348
|
+
{
|
|
20349
|
+
method: "POST",
|
|
20350
|
+
body: { name: journal.databaseName }
|
|
20351
|
+
}
|
|
20352
|
+
);
|
|
20353
|
+
await advance(ctx, journal, journal.phase, { databaseId: database.uuid });
|
|
20354
|
+
}
|
|
20355
|
+
const file2 = await configFile(ctx, journal, artifact);
|
|
20356
|
+
await cf.run(["d1", "migrations", "apply", "DB", "--remote", "--config", file2.path], {
|
|
20357
|
+
cwd: file2.directory
|
|
20358
|
+
});
|
|
20359
|
+
const secrets = {
|
|
20360
|
+
...journal.secrets,
|
|
20361
|
+
SECRET_VAULT_LOCAL_KEK_V1: await ctx.store.vaultKey(journal.id)
|
|
20362
|
+
};
|
|
20363
|
+
const secretPath = join4(file2.directory, "secrets.json");
|
|
20364
|
+
await writeFile3(secretPath, JSON.stringify(secrets), {
|
|
20365
|
+
mode: 384
|
|
20366
|
+
});
|
|
20367
|
+
try {
|
|
20368
|
+
await cf.run(["deploy", "--config", file2.path, "--secrets-file", secretPath], {
|
|
20369
|
+
cwd: file2.directory,
|
|
20370
|
+
redact: Object.values(secrets)
|
|
19071
20371
|
});
|
|
19072
|
-
|
|
19073
|
-
|
|
20372
|
+
} finally {
|
|
20373
|
+
await unlink2(secretPath).catch(() => {
|
|
19074
20374
|
});
|
|
19075
|
-
await verifyDeployment(ctx, ctx.url, journal2.id);
|
|
19076
|
-
journal2.version = artifact.manifest.version;
|
|
19077
|
-
await ctx.save();
|
|
19078
|
-
return { updated: true, ...plan2 };
|
|
19079
20375
|
}
|
|
20376
|
+
await advance(ctx, journal, "deployed");
|
|
20377
|
+
await verifyDeployment(ctx, url2, journal.id);
|
|
20378
|
+
const proofs = {
|
|
20379
|
+
idempotencyKey: journal.bootstrap.idempotencyKey,
|
|
20380
|
+
pollToken: journal.bootstrap.pollToken
|
|
20381
|
+
};
|
|
20382
|
+
const { data } = await whileWarming(
|
|
20383
|
+
() => ctx.publicCall("bootstrapCliAccount", { body: proofs, url: url2 })
|
|
20384
|
+
);
|
|
20385
|
+
await ctx.select(url2, data);
|
|
20386
|
+
await advance(ctx, journal, "ready", {
|
|
20387
|
+
...domain2 ? { pendingDomain: domain2 } : {},
|
|
20388
|
+
secrets: void 0
|
|
20389
|
+
});
|
|
20390
|
+
if (domain2) {
|
|
20391
|
+
await attachDomain(ctx, cf, journal, domain2, artifact, false);
|
|
20392
|
+
await advance(ctx, journal, journal.phase, { pendingDomain: void 0 });
|
|
20393
|
+
}
|
|
20394
|
+
}
|
|
20395
|
+
async function deploymentSetup(ctx, flags, cf = new Cloudflare(), loadRelease = release) {
|
|
20396
|
+
const artifact = await authorized(flags, cf, loadRelease);
|
|
19080
20397
|
const name = flags.name ?? await prompt("Worker name", flags, { defaultValue: "app-ai-gateway" });
|
|
19081
20398
|
if (!/^[a-z][a-z0-9-]{0,62}$/.test(name))
|
|
19082
20399
|
fail("invalid_input", "Worker name must be a lowercase slug of at most 63 characters.");
|
|
@@ -19084,58 +20401,29 @@ async function deploymentCommand(ctx, command, flags, cf = new Cloudflare(), loa
|
|
|
19084
20401
|
const accountId = await cf.account(flags);
|
|
19085
20402
|
const workers = await cf.all("/accounts/" + accountId + "/workers/scripts");
|
|
19086
20403
|
const installations = ctx.state.installations ??= {};
|
|
19087
|
-
|
|
20404
|
+
const recorded = Object.values(installations).find(
|
|
19088
20405
|
(j) => j.accountId === accountId && j.name === name
|
|
19089
20406
|
);
|
|
19090
20407
|
const exists = workers.some((w) => w.id === name);
|
|
19091
|
-
if (exists && !
|
|
20408
|
+
if (exists && !recorded)
|
|
19092
20409
|
fail(
|
|
19093
20410
|
"worker_name_collision",
|
|
19094
20411
|
"A Worker already uses this name.",
|
|
19095
20412
|
"Choose a different --name; setup never takes over an existing installation."
|
|
19096
20413
|
);
|
|
19097
|
-
if (exists &&
|
|
20414
|
+
if (exists && recorded) {
|
|
19098
20415
|
const settings = await resultOf(
|
|
19099
20416
|
cf,
|
|
19100
20417
|
`/accounts/${accountId}/workers/scripts/${name}/settings`
|
|
19101
20418
|
);
|
|
19102
|
-
if (settings.bindings?.find((b) => b.name === "DEPLOYMENT_ID")?.text !==
|
|
20419
|
+
if (settings.bindings?.find((b) => b.name === "DEPLOYMENT_ID")?.text !== recorded.id)
|
|
19103
20420
|
fail(
|
|
19104
20421
|
"deployment_identity_mismatch",
|
|
19105
20422
|
"Existing Worker does not match the saved installation journal."
|
|
19106
20423
|
);
|
|
19107
20424
|
}
|
|
19108
|
-
if (
|
|
19109
|
-
|
|
19110
|
-
if (journal.pendingDomain) {
|
|
19111
|
-
const inventoryContext = Object.create(ctx);
|
|
19112
|
-
Object.defineProperty(inventoryContext, "active", {
|
|
19113
|
-
value: { deployment: { id: journal.id, mode: "self_hosted" } }
|
|
19114
|
-
});
|
|
19115
|
-
Object.defineProperty(inventoryContext, "url", {
|
|
19116
|
-
value: journal.url ?? ctx.url
|
|
19117
|
-
});
|
|
19118
|
-
journal = await matchExisting(inventoryContext, cf, flags);
|
|
19119
|
-
if (flags["dry-run"])
|
|
19120
|
-
return {
|
|
19121
|
-
installed: true,
|
|
19122
|
-
pendingDomain: journal.pendingDomain,
|
|
19123
|
-
dryRun: true
|
|
19124
|
-
};
|
|
19125
|
-
await attachDomain(ctx, cf, journal, journal.pendingDomain, artifact, false);
|
|
19126
|
-
delete journal.pendingDomain;
|
|
19127
|
-
await ctx.save();
|
|
19128
|
-
}
|
|
19129
|
-
await verifyDeployment(ctx, journal.url ?? ctx.url, journal.id);
|
|
19130
|
-
return {
|
|
19131
|
-
installed: true,
|
|
19132
|
-
deploymentId: journal.id,
|
|
19133
|
-
name,
|
|
19134
|
-
accountId,
|
|
19135
|
-
url: journal.url ?? ctx.url,
|
|
19136
|
-
note: "This installation is already complete. Use deployment connect to authenticate or deployment update to change its release."
|
|
19137
|
-
};
|
|
19138
|
-
}
|
|
20425
|
+
if (recorded?.phase === "ready")
|
|
20426
|
+
return completeInstallation(ctx, cf, flags, recorded, artifact, installations);
|
|
19139
20427
|
const subdomain = (await resultOf(cf, "/accounts/" + accountId + "/workers/subdomain")).subdomain;
|
|
19140
20428
|
if (!subdomain)
|
|
19141
20429
|
fail(
|
|
@@ -19157,81 +20445,13 @@ async function deploymentCommand(ctx, command, flags, cf = new Cloudflare(), loa
|
|
|
19157
20445
|
`Install ${name} in Cloudflare account ${accountId} at ${url2}${domain2 ? " with " + domain2 : ""}?`,
|
|
19158
20446
|
flags
|
|
19159
20447
|
);
|
|
19160
|
-
|
|
19161
|
-
|
|
19162
|
-
|
|
19163
|
-
|
|
19164
|
-
|
|
19165
|
-
accountId,
|
|
19166
|
-
databaseName: name + "-" + id.slice(0, 8),
|
|
19167
|
-
version: artifact.manifest.version,
|
|
19168
|
-
phase: "prepared",
|
|
19169
|
-
url: url2,
|
|
19170
|
-
vars: { CLI_CONSOLE_ORIGIN: url2 },
|
|
19171
|
-
bootstrap: { idempotencyKey: randomToken(), pollToken: randomToken() },
|
|
19172
|
-
// Held only until the Worker has them and the install is ready; the vault
|
|
19173
|
-
// key is the one that outlives setup, and it lives in its own file.
|
|
19174
|
-
secrets: {
|
|
19175
|
-
JWT_SECRET: randomToken(),
|
|
19176
|
-
BETTER_AUTH_SECRET: randomToken()
|
|
19177
|
-
}
|
|
19178
|
-
};
|
|
19179
|
-
installations[id] = journal;
|
|
19180
|
-
await ctx.save();
|
|
19181
|
-
}
|
|
20448
|
+
const journal = recorded ?? await prepare(
|
|
20449
|
+
ctx,
|
|
20450
|
+
installations,
|
|
20451
|
+
newInstallation(name, accountId, url2, artifact.manifest.version)
|
|
20452
|
+
);
|
|
19182
20453
|
try {
|
|
19183
|
-
|
|
19184
|
-
const databases = await cf.all("/accounts/" + accountId + "/d1/database");
|
|
19185
|
-
let database = databases.find((d) => d.name === journal.databaseName);
|
|
19186
|
-
if (!database)
|
|
19187
|
-
database = await resultOf(cf, "/accounts/" + accountId + "/d1/database", {
|
|
19188
|
-
method: "POST",
|
|
19189
|
-
body: { name: journal.databaseName }
|
|
19190
|
-
});
|
|
19191
|
-
journal.databaseId = database.uuid;
|
|
19192
|
-
await ctx.save();
|
|
19193
|
-
}
|
|
19194
|
-
const file2 = await configFile(ctx, journal, artifact);
|
|
19195
|
-
await cf.run(["d1", "migrations", "apply", "DB", "--remote", "--config", file2.path], {
|
|
19196
|
-
cwd: file2.directory
|
|
19197
|
-
});
|
|
19198
|
-
const secrets = {
|
|
19199
|
-
...journal.secrets,
|
|
19200
|
-
SECRET_VAULT_LOCAL_KEK_V1: await ctx.store.vaultKey(journal.id)
|
|
19201
|
-
};
|
|
19202
|
-
const secretPath = join3(file2.directory, "secrets.json");
|
|
19203
|
-
await writeFile2(secretPath, JSON.stringify(secrets), {
|
|
19204
|
-
mode: 384
|
|
19205
|
-
});
|
|
19206
|
-
try {
|
|
19207
|
-
await cf.run(["deploy", "--config", file2.path, "--secrets-file", secretPath], {
|
|
19208
|
-
cwd: file2.directory,
|
|
19209
|
-
redact: Object.values(secrets)
|
|
19210
|
-
});
|
|
19211
|
-
} finally {
|
|
19212
|
-
await unlink2(secretPath).catch(() => {
|
|
19213
|
-
});
|
|
19214
|
-
}
|
|
19215
|
-
journal.phase = "deployed";
|
|
19216
|
-
await ctx.save();
|
|
19217
|
-
await verifyDeployment(ctx, url2, journal.id);
|
|
19218
|
-
const proofs = {
|
|
19219
|
-
idempotencyKey: journal.bootstrap.idempotencyKey,
|
|
19220
|
-
pollToken: journal.bootstrap.pollToken
|
|
19221
|
-
};
|
|
19222
|
-
const { data } = await whileWarming(
|
|
19223
|
-
() => ctx.publicCall("bootstrapCliAccount", [], { body: proofs, url: url2 })
|
|
19224
|
-
);
|
|
19225
|
-
await ctx.select(url2, data);
|
|
19226
|
-
journal.phase = "ready";
|
|
19227
|
-
if (domain2) journal.pendingDomain = domain2;
|
|
19228
|
-
delete journal.secrets;
|
|
19229
|
-
await ctx.save();
|
|
19230
|
-
if (domain2) {
|
|
19231
|
-
await attachDomain(ctx, cf, journal, domain2, artifact, false);
|
|
19232
|
-
delete journal.pendingDomain;
|
|
19233
|
-
await ctx.save();
|
|
19234
|
-
}
|
|
20454
|
+
await install(ctx, cf, journal, artifact, url2, domain2);
|
|
19235
20455
|
return {
|
|
19236
20456
|
installed: true,
|
|
19237
20457
|
...plan,
|
|
@@ -19250,6 +20470,12 @@ async function deploymentCommand(ctx, command, flags, cf = new Cloudflare(), loa
|
|
|
19250
20470
|
throw error51;
|
|
19251
20471
|
}
|
|
19252
20472
|
}
|
|
20473
|
+
async function deploymentCommand(ctx, command, flags, cf = new Cloudflare(), loadRelease = release) {
|
|
20474
|
+
if (command === "deployment setup") return deploymentSetup(ctx, flags, cf, loadRelease);
|
|
20475
|
+
if (command === "deployment domain") return deploymentDomain(ctx, flags, cf, loadRelease);
|
|
20476
|
+
if (command === "deployment update") return deploymentUpdate(ctx, flags, cf, loadRelease);
|
|
20477
|
+
fail("unknown_command", "Unknown command.");
|
|
20478
|
+
}
|
|
19253
20479
|
|
|
19254
20480
|
// src/style.ts
|
|
19255
20481
|
import { Writable } from "node:stream";
|
|
@@ -19339,8 +20565,8 @@ function humanResult(command, result, context, style2 = plain) {
|
|
|
19339
20565
|
return style2.code(result.snippet, language).join("\n") + "\n";
|
|
19340
20566
|
}
|
|
19341
20567
|
const lines = render(command, result, context, style2);
|
|
19342
|
-
if ("
|
|
19343
|
-
lines.push(`Free access ends: ${result.
|
|
20568
|
+
if ("unclaimedAccess" in result && result.unclaimedAccess)
|
|
20569
|
+
lines.push(`Free access ends: ${result.unclaimedAccess.endsAt}`);
|
|
19344
20570
|
if ("account" in result && result.account?.expiresAt)
|
|
19345
20571
|
lines.push(`Recovery deadline: ${result.account.expiresAt}`);
|
|
19346
20572
|
return lines.join("\n") + "\n";
|
|
@@ -19388,7 +20614,7 @@ function render(command, result, context, style2) {
|
|
|
19388
20614
|
return usage(command, result, style2);
|
|
19389
20615
|
case "operation status":
|
|
19390
20616
|
case "operation wait":
|
|
19391
|
-
return
|
|
20617
|
+
return operation(result, style2);
|
|
19392
20618
|
case "app add":
|
|
19393
20619
|
case "app update":
|
|
19394
20620
|
case "app validate":
|
|
@@ -19434,7 +20660,7 @@ function status(command, result, context, style2) {
|
|
|
19434
20660
|
style2
|
|
19435
20661
|
)
|
|
19436
20662
|
];
|
|
19437
|
-
if ("state" in result) return [...did, ...
|
|
20663
|
+
if ("state" in result) return [...did, ...operation(result, style2)];
|
|
19438
20664
|
return [...did, style2.json(result)];
|
|
19439
20665
|
}
|
|
19440
20666
|
function list(command, result, style2) {
|
|
@@ -19670,7 +20896,7 @@ function usage(command, result, style2) {
|
|
|
19670
20896
|
);
|
|
19671
20897
|
return [style2.json(result)];
|
|
19672
20898
|
}
|
|
19673
|
-
function
|
|
20899
|
+
function operation(result, style2) {
|
|
19674
20900
|
if (!("state" in result)) return [style2.json(result)];
|
|
19675
20901
|
const stored = "result" in result ? result.result : void 0;
|
|
19676
20902
|
return kv(
|
|
@@ -19716,7 +20942,9 @@ function app(command, result, context, style2) {
|
|
|
19716
20942
|
["Gateway", context.url],
|
|
19717
20943
|
["Status", result.definition.status],
|
|
19718
20944
|
["Authentication", result.definition.config.authentication.type],
|
|
19719
|
-
|
|
20945
|
+
// Only an edit of an application that exists has an id to show; a
|
|
20946
|
+
// new one is judged as a draft and has none yet.
|
|
20947
|
+
["App ID", result.validation.remote && "app_id" in result.validation ? result.validation.app_id : void 0]
|
|
19720
20948
|
],
|
|
19721
20949
|
style2
|
|
19722
20950
|
),
|
|
@@ -19942,8 +21170,9 @@ var Transport = class {
|
|
|
19942
21170
|
};
|
|
19943
21171
|
|
|
19944
21172
|
// src/usage.ts
|
|
21173
|
+
var CLI_BREAKDOWNS = ["provider", "model", "status"];
|
|
19945
21174
|
function month(value) {
|
|
19946
|
-
if (
|
|
21175
|
+
if (!MONTH_PATTERN.test(value))
|
|
19947
21176
|
fail(
|
|
19948
21177
|
"invalid_input",
|
|
19949
21178
|
"Month must use YYYY-MM with a valid calendar month."
|
|
@@ -19966,11 +21195,11 @@ function positive(value, max = 3600) {
|
|
|
19966
21195
|
async function usageCommand(ctx, command, flags) {
|
|
19967
21196
|
if (command === "usage show") {
|
|
19968
21197
|
const m = month(flags.month ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 7));
|
|
19969
|
-
return flags.app ? (await ctx.call("getAppUsage",
|
|
21198
|
+
return flags.app ? (await ctx.call("getAppUsage", { params: { app: flags.app }, query: { month: m } })).data : (await ctx.call("getCliUsage", { query: { month: m } })).data;
|
|
19970
21199
|
}
|
|
19971
21200
|
const app2 = await required2(flags, "app");
|
|
19972
|
-
const by = flags.by ?? "model";
|
|
19973
|
-
if (
|
|
21201
|
+
const by = CLI_BREAKDOWNS.find((dimension) => dimension === (flags.by ?? "model"));
|
|
21202
|
+
if (by === void 0)
|
|
19974
21203
|
fail("invalid_input", "--by must be provider, model or status.");
|
|
19975
21204
|
if (Boolean(flags.from) !== Boolean(flags.to))
|
|
19976
21205
|
fail("invalid_input", "Supply both --from and --to or neither.");
|
|
@@ -19980,7 +21209,10 @@ async function usageCommand(ctx, command, flags) {
|
|
|
19980
21209
|
if (from > to) fail("invalid_input", "--from must not be later than --to.");
|
|
19981
21210
|
const limit = positive(flags.limit ?? 50, 200);
|
|
19982
21211
|
return {
|
|
19983
|
-
...(await ctx.call("getAppUsageBreakdown",
|
|
21212
|
+
...(await ctx.call("getAppUsageBreakdown", {
|
|
21213
|
+
params: { app: app2 },
|
|
21214
|
+
query: { from, to, by, limit }
|
|
21215
|
+
})).data,
|
|
19984
21216
|
coverage: {
|
|
19985
21217
|
from,
|
|
19986
21218
|
to,
|
|
@@ -19998,7 +21230,7 @@ async function execute(parsed, ctx) {
|
|
|
19998
21230
|
if (command.startsWith("usage ")) return usageCommand(ctx, command, flags);
|
|
19999
21231
|
if (command.startsWith("operation "))
|
|
20000
21232
|
return command === "operation wait" ? ctx.wait(args[0] ?? "", positive(flags.timeout ?? 300)) : ctx.poll(args[0] ?? "");
|
|
20001
|
-
if (command === "account status") return (await ctx.call("getCliAccount"
|
|
21233
|
+
if (command === "account status") return (await ctx.call("getCliAccount")).data;
|
|
20002
21234
|
if (command === "account login") return ctx.login();
|
|
20003
21235
|
if (command === "account claim") return ctx.operation("claim", {});
|
|
20004
21236
|
if (command === "account logout") {
|
|
@@ -20021,7 +21253,7 @@ async function execute(parsed, ctx) {
|
|
|
20021
21253
|
return ctx.login(url2);
|
|
20022
21254
|
}
|
|
20023
21255
|
if (command === "deployment status") {
|
|
20024
|
-
const { data } = await ctx.publicCall("getCliCapabilities"
|
|
21256
|
+
const { data } = await ctx.publicCall("getCliCapabilities");
|
|
20025
21257
|
return {
|
|
20026
21258
|
url: ctx.url,
|
|
20027
21259
|
authenticated: Boolean(ctx.active?.credential),
|