@route_33/opencode 0.4.7-rc.7 → 0.4.7-rc.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -20
- package/dist/cli/production-smoke/production-smoke.js +28 -20
- package/dist/index.js +1005 -743
- package/dist/tui-entry/index.js +1061 -637
- package/dist/v2-entry/index.js +105 -77
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,19 +1,219 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
// src/auth/auth-error/auth-error.ts
|
|
2
|
+
var AuthError = class extends Error {
|
|
3
|
+
constructor(code, options) {
|
|
4
|
+
super(
|
|
5
|
+
"Route 33 authorization failed",
|
|
6
|
+
options?.cause === void 0 ? void 0 : { cause: options.cause }
|
|
7
|
+
);
|
|
8
|
+
this.code = code;
|
|
9
|
+
this.name = "AuthError";
|
|
10
|
+
this.statusCode = options?.statusCode;
|
|
11
|
+
}
|
|
12
|
+
code;
|
|
13
|
+
statusCode;
|
|
5
14
|
};
|
|
6
15
|
|
|
7
|
-
// src/
|
|
8
|
-
var
|
|
9
|
-
const
|
|
10
|
-
|
|
16
|
+
// src/auth/build-authorize-url/build-authorize-url.ts
|
|
17
|
+
var buildAuthorizeUrl = (issuerUrl2, request) => {
|
|
18
|
+
const url = new URL("/authorize", issuerUrl2);
|
|
19
|
+
url.search = new URLSearchParams({
|
|
20
|
+
client_id: request.clientId,
|
|
21
|
+
code_challenge: request.codeChallenge,
|
|
22
|
+
code_challenge_method: "S256",
|
|
23
|
+
redirect_uri: request.redirectUri,
|
|
24
|
+
response_type: "code",
|
|
25
|
+
scope: request.scope,
|
|
26
|
+
state: request.state
|
|
27
|
+
}).toString();
|
|
28
|
+
return url;
|
|
11
29
|
};
|
|
12
30
|
|
|
13
|
-
// src/
|
|
14
|
-
var
|
|
15
|
-
|
|
16
|
-
|
|
31
|
+
// src/contract/adapters/contract-parse-error/contract-parse-error.ts
|
|
32
|
+
var ContractParseError = class extends Error {
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// src/contract/adapters/gateway-api-key/gateway-api-key.ts
|
|
36
|
+
var GatewayApiKey = class {
|
|
37
|
+
value;
|
|
38
|
+
constructor(value) {
|
|
39
|
+
this.value = value;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// src/contract/adapters/management-jwt/management-jwt.ts
|
|
44
|
+
var ManagementJwt = class {
|
|
45
|
+
value;
|
|
46
|
+
constructor(value) {
|
|
47
|
+
this.value = value;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// src/contract/adapters/parse-gateway-key/parse-gateway-key.ts
|
|
52
|
+
var parseGatewayKey = (value) => {
|
|
53
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
54
|
+
throw new Error("gateway key must be non-empty");
|
|
55
|
+
}
|
|
56
|
+
return new GatewayApiKey(value);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// src/contract/adapters/parse-management-jwt/parse-management-jwt.ts
|
|
60
|
+
var parseManagementJwt = (value) => {
|
|
61
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
62
|
+
throw new Error("management token must be non-empty");
|
|
63
|
+
}
|
|
64
|
+
return new ManagementJwt(value);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// src/contract/predicates/has-string-fields/has-string-fields.ts
|
|
68
|
+
var hasStringFields = (record, fields) => fields.every((field) => typeof record[field] === "string");
|
|
69
|
+
|
|
70
|
+
// src/contract/predicates/is-nullable-string/is-nullable-string.ts
|
|
71
|
+
var isNullableString = (value) => typeof value === "string" || value === null;
|
|
72
|
+
|
|
73
|
+
// src/contract/predicates/is-owner-type/is-owner-type.ts
|
|
74
|
+
var isOwnerType = (value) => value === "project" || value === "user_personal" || value === "user_project";
|
|
75
|
+
|
|
76
|
+
// src/contract/predicates/is-record/is-record.ts
|
|
77
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
78
|
+
|
|
79
|
+
// src/contract/adapters/parse-management-subject/parse-management-subject.ts
|
|
80
|
+
var parseManagementSubject = (credential) => {
|
|
81
|
+
const encodedPayload = credential.value.split(".")[1];
|
|
82
|
+
if (encodedPayload === void 0 || encodedPayload.length === 0) {
|
|
83
|
+
throw new ContractParseError("management token carries no usable subject");
|
|
84
|
+
}
|
|
85
|
+
let payload;
|
|
86
|
+
try {
|
|
87
|
+
payload = JSON.parse(
|
|
88
|
+
Buffer.from(encodedPayload, "base64url").toString("utf8")
|
|
89
|
+
);
|
|
90
|
+
} catch {
|
|
91
|
+
throw new ContractParseError("management token payload is not JSON");
|
|
92
|
+
}
|
|
93
|
+
if (!isRecord(payload)) {
|
|
94
|
+
throw new ContractParseError("management token payload is not an object");
|
|
95
|
+
}
|
|
96
|
+
const subject = payload["sub"];
|
|
97
|
+
if (typeof subject !== "string" || subject.length === 0) {
|
|
98
|
+
throw new ContractParseError("management token carries no usable subject");
|
|
99
|
+
}
|
|
100
|
+
return subject;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// src/contract/auth-session/parse-token-set/parse-token-set.ts
|
|
104
|
+
var parseTokenSet = (value) => {
|
|
105
|
+
if (!isRecord(value)) {
|
|
106
|
+
throw new ContractParseError("invalid token response");
|
|
107
|
+
}
|
|
108
|
+
if (typeof value["access_token"] !== "string" || typeof value["expires_in"] !== "number" || value["token_type"] !== "Bearer") {
|
|
109
|
+
throw new ContractParseError("invalid token response");
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
access_token: value["access_token"],
|
|
113
|
+
expires_in: value["expires_in"],
|
|
114
|
+
token_type: "Bearer",
|
|
115
|
+
...typeof value["id_token"] === "string" ? { id_token: value["id_token"] } : {},
|
|
116
|
+
...typeof value["refresh_token"] === "string" ? { refresh_token: value["refresh_token"] } : {},
|
|
117
|
+
...typeof value["scope"] === "string" ? { scope: value["scope"] } : {}
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// src/auth/exchange-code-and-parse/exchange-code-and-parse.ts
|
|
122
|
+
var exchangeCodeAndParse = async (options) => {
|
|
123
|
+
const body = new URLSearchParams({
|
|
124
|
+
client_id: options.clientId,
|
|
125
|
+
code: options.input.code,
|
|
126
|
+
code_verifier: options.input.codeVerifier,
|
|
127
|
+
grant_type: "authorization_code",
|
|
128
|
+
redirect_uri: options.input.redirectUri
|
|
129
|
+
});
|
|
130
|
+
let response;
|
|
131
|
+
try {
|
|
132
|
+
response = await (options.fetchImpl ?? fetch)(options.tokenUrl, {
|
|
133
|
+
body,
|
|
134
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
135
|
+
method: "POST"
|
|
136
|
+
});
|
|
137
|
+
} catch (cause) {
|
|
138
|
+
throw new AuthError("AUTH_CODE_EXCHANGE_FAILED", { cause });
|
|
139
|
+
}
|
|
140
|
+
if (!response.ok) {
|
|
141
|
+
throw new AuthError("AUTH_CODE_EXCHANGE_FAILED", {
|
|
142
|
+
statusCode: response.status
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
let payload;
|
|
146
|
+
try {
|
|
147
|
+
payload = await response.json();
|
|
148
|
+
} catch (cause) {
|
|
149
|
+
throw new AuthError("AUTH_CODE_EXCHANGE_FAILED", { cause });
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
return parseTokenSet(payload);
|
|
153
|
+
} catch (cause) {
|
|
154
|
+
if (cause instanceof ContractParseError) {
|
|
155
|
+
throw new AuthError("AUTH_CODE_EXCHANGE_FAILED", { cause });
|
|
156
|
+
}
|
|
157
|
+
throw cause;
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// src/auth/refresh-session/refresh-session.ts
|
|
162
|
+
var refreshSession = async (options) => {
|
|
163
|
+
const body = new URLSearchParams({
|
|
164
|
+
client_id: options.clientId,
|
|
165
|
+
grant_type: "refresh_token",
|
|
166
|
+
refresh_token: options.refreshToken
|
|
167
|
+
});
|
|
168
|
+
let response;
|
|
169
|
+
try {
|
|
170
|
+
response = await (options.fetchImpl ?? fetch)(options.tokenUrl, {
|
|
171
|
+
body,
|
|
172
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
173
|
+
method: "POST"
|
|
174
|
+
});
|
|
175
|
+
} catch (cause) {
|
|
176
|
+
throw new AuthError("AUTH_REAUTHENTICATION_REQUIRED", { cause });
|
|
177
|
+
}
|
|
178
|
+
if (!response.ok) {
|
|
179
|
+
throw new AuthError("AUTH_REAUTHENTICATION_REQUIRED", {
|
|
180
|
+
statusCode: response.status
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
let payload;
|
|
184
|
+
try {
|
|
185
|
+
payload = await response.json();
|
|
186
|
+
} catch (cause) {
|
|
187
|
+
throw new AuthError("AUTH_REAUTHENTICATION_REQUIRED", { cause });
|
|
188
|
+
}
|
|
189
|
+
try {
|
|
190
|
+
return parseTokenSet(payload);
|
|
191
|
+
} catch (cause) {
|
|
192
|
+
if (cause instanceof ContractParseError) {
|
|
193
|
+
throw new AuthError("AUTH_REAUTHENTICATION_REQUIRED", { cause });
|
|
194
|
+
}
|
|
195
|
+
throw cause;
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// src/auth/create-auth-session/create-auth-session.ts
|
|
200
|
+
var createAuthSession = (options) => {
|
|
201
|
+
const tokenUrl = new URL("/oauth/token", options.issuerUrl);
|
|
202
|
+
return {
|
|
203
|
+
authorizationUrl: (request) => buildAuthorizeUrl(options.issuerUrl, request),
|
|
204
|
+
exchangeCode: (input) => exchangeCodeAndParse({
|
|
205
|
+
clientId: options.clientId,
|
|
206
|
+
...options.fetchImpl === void 0 ? {} : { fetchImpl: options.fetchImpl },
|
|
207
|
+
input,
|
|
208
|
+
tokenUrl
|
|
209
|
+
}),
|
|
210
|
+
refresh: (refreshToken) => refreshSession({
|
|
211
|
+
clientId: options.clientId,
|
|
212
|
+
...options.fetchImpl === void 0 ? {} : { fetchImpl: options.fetchImpl },
|
|
213
|
+
refreshToken,
|
|
214
|
+
tokenUrl
|
|
215
|
+
})
|
|
216
|
+
};
|
|
17
217
|
};
|
|
18
218
|
|
|
19
219
|
// src/shared/default-auth-issuer-url/default-auth-issuer-url.ts
|
|
@@ -61,164 +261,406 @@ var resolveCacheDir = (env) => {
|
|
|
61
261
|
);
|
|
62
262
|
};
|
|
63
263
|
|
|
64
|
-
// src/
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
// src/cli/read-env-catalog-ttl-ms/read-env-catalog-ttl-ms.ts
|
|
68
|
-
var readEnvCatalogTtlMs = (env = process.env) => {
|
|
69
|
-
const value = Number(env["ROUTE33_CATALOG_TTL_MS"]);
|
|
70
|
-
return Number.isFinite(value) && value > 0 ? value : DEFAULT_CATALOG_TTL_MS;
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
// src/cli/read-env-client-id/read-env-client-id.ts
|
|
74
|
-
var readEnvClientId = (env = process.env) => {
|
|
75
|
-
const value = env["ROUTE33_CLIENT_ID"]?.trim();
|
|
76
|
-
return value === "" ? void 0 : value;
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
// src/cli/read-env-direct-api-key/read-env-direct-api-key.ts
|
|
80
|
-
var readEnvDirectApiKey = (env = process.env) => {
|
|
81
|
-
const value = env["ROUTE33_API_KEY"]?.trim();
|
|
82
|
-
return value === "" ? void 0 : value;
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
// src/cli/read-env-disable-id-shim/read-env-disable-id-shim.ts
|
|
86
|
-
var readEnvDisableIdShim = (env = process.env) => env["ROUTE33_DISABLE_ID_SHIM"] === "1";
|
|
87
|
-
|
|
88
|
-
// src/cli/read-env-discovery/read-env-discovery.ts
|
|
89
|
-
var readEnvDiscovery = (env = process.env) => env["ROUTE33_DISCOVERY"] !== "0";
|
|
264
|
+
// src/auth/open-browser-and-capture/open-browser-and-capture.ts
|
|
265
|
+
import { timingSafeEqual } from "node:crypto";
|
|
90
266
|
|
|
91
|
-
// src/
|
|
92
|
-
|
|
267
|
+
// src/auth/loopback-callback/loopback-callback.ts
|
|
268
|
+
import { createServer } from "node:http";
|
|
93
269
|
|
|
94
|
-
// src/
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
270
|
+
// src/auth/loopback-callback/callback-page/callback-page.ts
|
|
271
|
+
var MARK_PATHS = [
|
|
272
|
+
"M310.6,175.8c0-6.1-2.7-11.8-7.2-15.9l-127.6-114.4V7h-43.5v48.3c0,6.2,2.6,12.1,7.2,16.2l92.5,82.9H0v43.5h234.7l-95.6,91.4c-4.3,4.1-6.7,9.8-6.7,15.7v48.3h43.5v-39l128.1-122.4c4.4-4.2,6.8-10,6.7-16.1h0Z",
|
|
273
|
+
"M457.2,175.9v-2.6c18.4-4.9,33-14.1,44-27.7,11-13.6,16.5-30.3,16.5-50.3s-4.5-32.9-13.4-47.5c-9-14.5-21.6-26.1-38-34.9-16.4-8.7-35.8-13.1-58.3-13.1s-41.1,4-58.8,12.1-31.8,19.3-42.4,33.7-16.1,31-16.6,50h51.1c.3-10.8,3.6-20,9.8-27.7,6.2-7.6,14.2-13.5,24.1-17.5,9.9-4.1,20.6-6.1,32-6.1s22.3,2.2,31.1,6.7c8.8,4.5,15.6,10.8,20.5,18.8,4.9,8.1,7.3,17.5,7.3,28.4s-2.8,21.1-8.4,29.4-13.3,14.8-23.2,19.5-21.3,7.1-34.2,7.1h-29.9v43.3h29.9c16.2,0,30,2.5,41.5,7.5,11.5,5,20.2,12,26.1,21.1,6,9,9,19.4,9,31.2s-3,21.2-9.1,29.8-14.4,15.3-25.1,20.1c-10.7,4.9-22.9,7.3-36.7,7.3s-24.2-2.1-34.5-6.2-18.6-10-24.8-17.5c-6.3-7.5-9.8-16.5-10.6-26.8h-53.2c.6,19,6.3,35.7,17,50.2,10.7,14.5,25.2,25.7,43.6,33.8,18.4,8.1,39.4,12.1,63.1,12.1s45.1-4.4,63.7-13.2c18.6-8.8,33.3-20.8,44-36,10.8-15.2,16.1-32.5,16.1-51.8s-6.4-40.7-19.2-55.6c-12.8-14.9-30.8-24.2-54-27.8h0Z",
|
|
274
|
+
"M763,203.7c-12.8-14.9-30.8-24.2-54-27.8v-2.6c18.4-4.9,33-14.1,44-27.7,11-13.6,16.5-30.3,16.5-50.3s-4.5-32.9-13.4-47.5c-8.9-14.5-21.6-26.1-38-34.9-16.4-8.7-35.8-13.1-58.3-13.1s-41.1,4-58.8,12.1-31.8,19.3-42.4,33.7-16.1,31-16.6,50h51.1c.3-10.8,3.6-20,9.8-27.7,6.2-7.6,14.2-13.5,24.1-17.5,9.9-4.1,20.6-6.1,32-6.1s22.3,2.2,31.1,6.7c8.8,4.5,15.6,10.8,20.5,18.8,4.9,8.1,7.3,17.5,7.3,28.4s-2.8,21.1-8.4,29.4c-5.6,8.3-13.3,14.8-23.2,19.5s-21.3,7.1-34.2,7.1h-29.9v43.3h29.9c16.2,0,30,2.5,41.5,7.5,11.5,5,20.2,12,26.1,21.1,6,9,8.9,19.4,8.9,31.2s-3,21.2-9.1,29.8c-6.1,8.6-14.4,15.3-25.1,20.1-10.7,4.9-22.9,7.3-36.7,7.3s-24.2-2.1-34.5-6.2-18.6-10-24.8-17.5c-6.3-7.5-9.8-16.5-10.6-26.8h-53.2c.6,19,6.3,35.7,17,50.2,10.7,14.5,25.2,25.7,43.6,33.8,18.4,8.1,39.4,12.1,63.1,12.1s45.1-4.4,63.7-13.2,33.3-20.8,44-36c10.8-15.2,16.1-32.5,16.1-51.8s-6.4-40.7-19.2-55.6h0Z"
|
|
275
|
+
];
|
|
276
|
+
var BRAND_MARK = `<svg class="r33-mark" viewBox="0 0 782.2 360.3" fill="currentColor" role="img" aria-label="route33.ai" xmlns="http://www.w3.org/2000/svg"><path d="${MARK_PATHS[0]}"/><path d="${MARK_PATHS[1]}"/><path d="${MARK_PATHS[2]}"/></svg>`;
|
|
277
|
+
var PAGE_STYLE = `:root{color-scheme:dark;--r33-euro-blue:#0000FF;--r33-off-white:#F1F1F1;--r33-off-black:#121212;--r33-grey-300:#C9CBCF;--r33-radius-fillet:4px;--r33-space-2:8px;--r33-space-3:16px;--r33-space-4:24px;--r33-space-6:64px;--r33-font-body:"Geist","Helvetica Neue",Arial,sans-serif;--r33-font-mono:"Geist Mono","SFMono-Regular",Menlo,monospace;--r33-surface:var(--r33-off-black);--r33-text-primary:var(--r33-off-white);--r33-text-secondary:var(--r33-grey-300);--r33-fill-accent:var(--r33-euro-blue);--r33-on-fill-accent:var(--r33-off-white)}
|
|
278
|
+
*{box-sizing:border-box}
|
|
279
|
+
html,body{margin:0}
|
|
280
|
+
.r33-page{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:var(--r33-space-6);background:var(--r33-surface);color:var(--r33-text-primary);font-family:var(--r33-font-body);-webkit-font-smoothing:antialiased}
|
|
281
|
+
.r33-panel{display:grid;gap:var(--r33-space-4);max-width:460px;width:100%}
|
|
282
|
+
.r33-mark{height:40px;width:auto;color:var(--r33-text-primary)}
|
|
283
|
+
.r33-status{display:inline-flex;align-items:center;gap:var(--r33-space-2);font-size:11px;letter-spacing:0.08em;text-transform:uppercase;color:var(--r33-text-secondary)}
|
|
284
|
+
.r33-status__dot{width:8px;height:8px;background:var(--r33-fill-accent)}
|
|
285
|
+
.r33-status__dot--error{background:transparent;border:1px solid var(--r33-text-primary);border-radius:50%}
|
|
286
|
+
.r33-headline{margin:0;font-size:48px;line-height:0.85;letter-spacing:-0.02em;font-weight:600;color:var(--r33-text-primary);text-wrap:balance}
|
|
287
|
+
.r33-subline{margin:0;font-size:16px;line-height:1.45;color:var(--r33-text-secondary);max-width:40ch;text-wrap:pretty}
|
|
288
|
+
.r33-code{margin:0;font-size:13px;letter-spacing:0.04em;color:var(--r33-text-secondary)}
|
|
289
|
+
.r33-code__value{font-family:var(--r33-font-mono);text-transform:uppercase;color:var(--r33-text-primary)}
|
|
290
|
+
@media (prefers-reduced-motion:reduce){*{animation-duration:1ms!important;transition-duration:1ms!important}}`;
|
|
291
|
+
var buildCallbackPage = (state, errorCode) => {
|
|
292
|
+
const isError = state === "error";
|
|
293
|
+
const safeCode = (errorCode ?? "").replace(/[^a-z0-9_-]/gi, "");
|
|
294
|
+
const title = isError ? "Sign-in failed" : "Signed in";
|
|
295
|
+
const headline = isError ? "Sign-in failed" : "Signed in to route33";
|
|
296
|
+
const subline = isError ? "Return to OpenCode and retry." : "Return to OpenCode \u2014 one more step may wait there: if it asks which budget covers usage, pick a project. Then this window can be closed.";
|
|
297
|
+
const statusLabel = isError ? "Authentication error" : "Authenticated";
|
|
298
|
+
const dotClass = isError ? "r33-status__dot r33-status__dot--error" : "r33-status__dot";
|
|
299
|
+
const codeLine = isError && safeCode.length > 0 ? `<p class="r33-code">Error code: <span class="r33-code__value">${safeCode}</span></p>` : "";
|
|
300
|
+
return `<!doctype html>
|
|
301
|
+
<html lang="en" data-theme="dark">
|
|
302
|
+
<head>
|
|
303
|
+
<meta charset="utf-8">
|
|
304
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
305
|
+
<title>${title} \xB7 route33.ai</title>
|
|
306
|
+
<style>${PAGE_STYLE}</style>
|
|
307
|
+
</head>
|
|
308
|
+
<body>
|
|
309
|
+
<main class="r33-page">
|
|
310
|
+
<div class="r33-panel">
|
|
311
|
+
${BRAND_MARK}
|
|
312
|
+
<span class="r33-status"><span class="${dotClass}"></span>${statusLabel}</span>
|
|
313
|
+
<h1 class="r33-headline">${headline}</h1>
|
|
314
|
+
<p class="r33-subline">${subline}</p>
|
|
315
|
+
${codeLine}
|
|
316
|
+
</div>
|
|
317
|
+
</main>
|
|
318
|
+
</body>
|
|
319
|
+
</html>`;
|
|
112
320
|
};
|
|
113
321
|
|
|
114
|
-
// src/
|
|
115
|
-
var
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
322
|
+
// src/auth/loopback-callback/loopback-callback.ts
|
|
323
|
+
var createAuthLoopbackCallback = async () => {
|
|
324
|
+
let redirectUri = "http://127.0.0.1/callback";
|
|
325
|
+
let resolveCapture;
|
|
326
|
+
const captured = new Promise((resolve2) => {
|
|
327
|
+
resolveCapture = resolve2;
|
|
328
|
+
});
|
|
329
|
+
const server = createServer((request, response) => {
|
|
330
|
+
const url = new URL(request.url ?? "/", redirectUri);
|
|
331
|
+
if (request.method !== "GET" || url.pathname !== "/callback") {
|
|
332
|
+
response.writeHead(404).end();
|
|
333
|
+
return;
|
|
124
334
|
}
|
|
125
|
-
|
|
335
|
+
const resolve2 = resolveCapture;
|
|
336
|
+
resolveCapture = void 0;
|
|
337
|
+
resolve2?.(url);
|
|
338
|
+
const error = url.searchParams.get("error");
|
|
339
|
+
const page = error === null || error.length === 0 ? buildCallbackPage("success") : buildCallbackPage("error", error);
|
|
340
|
+
response.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
341
|
+
response.end(page);
|
|
342
|
+
});
|
|
343
|
+
await new Promise((resolve2, reject) => {
|
|
344
|
+
const onError = (cause) => {
|
|
345
|
+
reject(new AuthError("AUTH_CALLBACK_LISTEN_FAILED", { cause }));
|
|
346
|
+
};
|
|
347
|
+
server.once("error", onError);
|
|
348
|
+
server.listen(0, "127.0.0.1", () => {
|
|
349
|
+
server.off("error", onError);
|
|
350
|
+
resolve2();
|
|
351
|
+
});
|
|
352
|
+
});
|
|
353
|
+
const address = server.address();
|
|
354
|
+
if (address === null || typeof address === "string") {
|
|
355
|
+
server.close();
|
|
356
|
+
throw new AuthError("AUTH_CALLBACK_LISTEN_FAILED");
|
|
357
|
+
}
|
|
358
|
+
redirectUri = `http://127.0.0.1:${String(address.port)}/callback`;
|
|
359
|
+
return {
|
|
360
|
+
redirectUri,
|
|
361
|
+
close: () => {
|
|
362
|
+
if (!server.listening) {
|
|
363
|
+
return Promise.resolve();
|
|
364
|
+
}
|
|
365
|
+
return new Promise((resolve2, reject) => {
|
|
366
|
+
server.close((cause) => {
|
|
367
|
+
if (cause === void 0) {
|
|
368
|
+
resolve2();
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
reject(new AuthError("AUTH_CALLBACK_CLEANUP_FAILED", { cause }));
|
|
372
|
+
});
|
|
373
|
+
});
|
|
374
|
+
},
|
|
375
|
+
wait: () => captured
|
|
126
376
|
};
|
|
127
|
-
|
|
128
|
-
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
// src/auth/open-system-browser/open-system-browser.ts
|
|
380
|
+
import { spawn } from "node:child_process";
|
|
381
|
+
var openSystemBrowser = async (url, options = {}) => new Promise((resolve2, reject) => {
|
|
382
|
+
let command = "xdg-open";
|
|
383
|
+
let args = [url.href];
|
|
384
|
+
if (process.platform === "darwin") {
|
|
385
|
+
command = "open";
|
|
386
|
+
} else if (process.platform === "win32") {
|
|
387
|
+
command = "cmd";
|
|
388
|
+
args = ["/c", "start", "", url.href];
|
|
389
|
+
}
|
|
390
|
+
if (options.spawnUrl !== void 0) {
|
|
391
|
+
options.spawnUrl(command, args, { detached: true, stdio: "ignore" });
|
|
392
|
+
resolve2();
|
|
129
393
|
return;
|
|
130
394
|
}
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
|
|
395
|
+
const child = spawn(command, args, {
|
|
396
|
+
detached: true,
|
|
397
|
+
stdio: "ignore"
|
|
398
|
+
});
|
|
399
|
+
child.once("error", reject);
|
|
400
|
+
child.once("spawn", () => {
|
|
401
|
+
child.unref();
|
|
137
402
|
resolve2();
|
|
138
|
-
}
|
|
139
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
403
|
+
});
|
|
140
404
|
});
|
|
141
405
|
|
|
142
|
-
// src/
|
|
143
|
-
var
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
// src/config/parse-exhaustion-envelope/parse-exhaustion-envelope.ts
|
|
158
|
-
var parseExhaustionEnvelope = async (response) => {
|
|
159
|
-
let body;
|
|
406
|
+
// src/auth/open-browser-and-capture/open-browser-and-capture.ts
|
|
407
|
+
var openBrowserAndCapture = async (options) => {
|
|
408
|
+
const callback = await createAuthLoopbackCallback();
|
|
409
|
+
const authorizationUrl = options.authorizationUrl(callback.redirectUri);
|
|
410
|
+
const browserRunner = options.browserRunner ?? ((url) => openSystemBrowser(url));
|
|
411
|
+
let timer;
|
|
412
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
413
|
+
timer = setTimeout(() => {
|
|
414
|
+
reject(new AuthError("AUTH_CALLBACK_TIMEOUT"));
|
|
415
|
+
}, options.timeoutMs ?? 12e4);
|
|
416
|
+
});
|
|
417
|
+
const launch = browserRunner(authorizationUrl).catch((cause) => {
|
|
418
|
+
throw new AuthError("AUTH_BROWSER_LAUNCH_FAILED", { cause });
|
|
419
|
+
});
|
|
160
420
|
try {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
421
|
+
const completed = await Promise.race([
|
|
422
|
+
Promise.all([launch, callback.wait()]),
|
|
423
|
+
timeout
|
|
424
|
+
]);
|
|
425
|
+
const callbackUrl = completed[1];
|
|
426
|
+
const actualState = callbackUrl.searchParams.get("state");
|
|
427
|
+
if (actualState === null) {
|
|
428
|
+
throw new AuthError("AUTH_CALLBACK_RESPONSE_INVALID");
|
|
165
429
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
const nestedError = body["error"];
|
|
172
|
-
const nestedCode = isRecord(nestedError) ? nestedError["code"] : void 0;
|
|
173
|
-
if (body["code"] !== "PROVIDER_LANES_EXHAUSTED" && nestedCode !== "PROVIDER_LANES_EXHAUSTED") {
|
|
174
|
-
return void 0;
|
|
175
|
-
}
|
|
176
|
-
const nestedId = isRecord(nestedError) ? nestedError["correlationId"] : void 0;
|
|
177
|
-
const directId = body["correlationId"];
|
|
178
|
-
let correlationId;
|
|
179
|
-
if (typeof directId === "string") {
|
|
180
|
-
correlationId = directId;
|
|
181
|
-
} else if (typeof nestedId === "string") {
|
|
182
|
-
correlationId = nestedId;
|
|
183
|
-
}
|
|
184
|
-
return { correlationId };
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
// src/config/request-model/request-model.ts
|
|
188
|
-
var requestModel = (body) => {
|
|
189
|
-
if (typeof body !== "string") {
|
|
190
|
-
return void 0;
|
|
191
|
-
}
|
|
192
|
-
let parsed;
|
|
193
|
-
try {
|
|
194
|
-
parsed = JSON.parse(body);
|
|
195
|
-
} catch (error) {
|
|
196
|
-
if (error instanceof SyntaxError) {
|
|
197
|
-
return void 0;
|
|
430
|
+
const expectedState = Buffer.from(options.expectedState);
|
|
431
|
+
const receivedState = Buffer.from(actualState);
|
|
432
|
+
if (expectedState.length !== receivedState.length || !timingSafeEqual(expectedState, receivedState)) {
|
|
433
|
+
throw new AuthError("AUTH_CALLBACK_STATE_MISMATCH");
|
|
198
434
|
}
|
|
199
|
-
|
|
435
|
+
const code = callbackUrl.searchParams.get("code");
|
|
436
|
+
if (code === null || code.length === 0) {
|
|
437
|
+
throw new AuthError("AUTH_CALLBACK_RESPONSE_INVALID");
|
|
438
|
+
}
|
|
439
|
+
return { code, redirectUri: callback.redirectUri };
|
|
440
|
+
} finally {
|
|
441
|
+
if (timer !== void 0) {
|
|
442
|
+
clearTimeout(timer);
|
|
443
|
+
}
|
|
444
|
+
await callback.close();
|
|
200
445
|
}
|
|
201
|
-
return isRecord(parsed) && typeof parsed["model"] === "string" ? parsed["model"] : void 0;
|
|
202
446
|
};
|
|
203
447
|
|
|
204
|
-
// src/
|
|
205
|
-
|
|
206
|
-
const advised = options.retryAfter !== null && /^\d+$/.test(options.retryAfter) ? Number(options.retryAfter) : void 0;
|
|
207
|
-
return advised !== void 0 && advised > 0 ? Math.min(advised, options.maxWaitSeconds) : Math.min(
|
|
208
|
-
options.baseSeconds * 2 ** (options.attempt - 1),
|
|
209
|
-
options.maxWaitSeconds
|
|
210
|
-
);
|
|
211
|
-
};
|
|
448
|
+
// src/auth/pkce-verifier-and-challenge/pkce-verifier-and-challenge.ts
|
|
449
|
+
import { randomBytes } from "node:crypto";
|
|
212
450
|
|
|
213
|
-
// src/
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
451
|
+
// src/auth/pkce-challenge/pkce-challenge.ts
|
|
452
|
+
import { createHash } from "node:crypto";
|
|
453
|
+
var pkceChallenge = (verifier) => createHash("sha256").update(verifier).digest("base64url");
|
|
454
|
+
|
|
455
|
+
// src/auth/pkce-verifier-and-challenge/pkce-verifier-and-challenge.ts
|
|
456
|
+
var createPkceVerifierAndChallenge = () => {
|
|
457
|
+
const codeVerifier = randomBytes(32).toString("base64url");
|
|
458
|
+
return {
|
|
459
|
+
codeChallenge: pkceChallenge(codeVerifier),
|
|
460
|
+
codeVerifier,
|
|
461
|
+
state: randomBytes(32).toString("base64url")
|
|
462
|
+
};
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
// src/auth/requested-scopes/requested-scopes.ts
|
|
466
|
+
var REQUESTED_OAUTH_SCOPES = "openid offline_access projects:read keys:read keys:write";
|
|
467
|
+
|
|
468
|
+
// src/auth/login-flow/login-flow.ts
|
|
469
|
+
var loginFlow = async (options) => {
|
|
470
|
+
const log = options.logger ?? logger;
|
|
471
|
+
log.info("auth.login.started");
|
|
472
|
+
const pkce = createPkceVerifierAndChallenge();
|
|
473
|
+
const captured = await openBrowserAndCapture({
|
|
474
|
+
authorizationUrl: (redirectUri) => options.session.authorizationUrl({
|
|
475
|
+
clientId: options.clientId,
|
|
476
|
+
codeChallenge: pkce.codeChallenge,
|
|
477
|
+
redirectUri,
|
|
478
|
+
scope: REQUESTED_OAUTH_SCOPES,
|
|
479
|
+
state: pkce.state
|
|
480
|
+
}),
|
|
481
|
+
...options.browserRunner === void 0 ? {} : { browserRunner: options.browserRunner },
|
|
482
|
+
expectedState: pkce.state,
|
|
483
|
+
...options.timeoutMs === void 0 ? {} : { timeoutMs: options.timeoutMs }
|
|
484
|
+
});
|
|
485
|
+
const tokenSet = await options.session.exchangeCode({
|
|
486
|
+
code: captured.code,
|
|
487
|
+
codeVerifier: pkce.codeVerifier,
|
|
488
|
+
redirectUri: captured.redirectUri
|
|
489
|
+
});
|
|
490
|
+
let credential;
|
|
491
|
+
try {
|
|
492
|
+
credential = parseManagementJwt(tokenSet.access_token);
|
|
493
|
+
} catch (cause) {
|
|
494
|
+
throw new AuthError("AUTH_TOKEN_RESPONSE_INVALID", { cause });
|
|
495
|
+
}
|
|
496
|
+
log.info("auth.login.completed");
|
|
497
|
+
return { credential, tokenSet };
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
// src/cli/read-env-base-url/read-env-base-url.ts
|
|
501
|
+
var readEnvBaseUrl = (env = process.env) => {
|
|
502
|
+
const value = env["ROUTE33_BASE_URL"]?.trim();
|
|
503
|
+
return value === "" ? void 0 : value;
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
// src/cli/read-env-api-base/read-env-api-base.ts
|
|
507
|
+
var readEnvApiBase = (env = process.env) => {
|
|
508
|
+
const value = env["ROUTE33_API_BASE"]?.trim();
|
|
509
|
+
return value === "" ? void 0 : value;
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
// src/cli/read-env-auth-issuer/read-env-auth-issuer.ts
|
|
513
|
+
var readEnvAuthIssuer = (env = process.env) => {
|
|
514
|
+
const value = env["ROUTE33_AUTH_ISSUER"]?.trim();
|
|
515
|
+
return value === "" ? void 0 : value;
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
// src/cli/read-env-cache-dir/read-env-cache-dir.ts
|
|
519
|
+
var readEnvCacheDir = (env = process.env) => resolveCacheDir(env);
|
|
520
|
+
|
|
521
|
+
// src/cli/read-env-catalog-ttl-ms/read-env-catalog-ttl-ms.ts
|
|
522
|
+
var readEnvCatalogTtlMs = (env = process.env) => {
|
|
523
|
+
const value = Number(env["ROUTE33_CATALOG_TTL_MS"]);
|
|
524
|
+
return Number.isFinite(value) && value > 0 ? value : DEFAULT_CATALOG_TTL_MS;
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
// src/cli/read-env-client-id/read-env-client-id.ts
|
|
528
|
+
var readEnvClientId = (env = process.env) => {
|
|
529
|
+
const value = env["ROUTE33_CLIENT_ID"]?.trim();
|
|
530
|
+
return value === "" ? void 0 : value;
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
// src/cli/read-env-direct-api-key/read-env-direct-api-key.ts
|
|
534
|
+
var readEnvDirectApiKey = (env = process.env) => {
|
|
535
|
+
const value = env["ROUTE33_API_KEY"]?.trim();
|
|
536
|
+
return value === "" ? void 0 : value;
|
|
537
|
+
};
|
|
538
|
+
|
|
539
|
+
// src/cli/read-env-disable-id-shim/read-env-disable-id-shim.ts
|
|
540
|
+
var readEnvDisableIdShim = (env = process.env) => env["ROUTE33_DISABLE_ID_SHIM"] === "1";
|
|
541
|
+
|
|
542
|
+
// src/cli/read-env-discovery/read-env-discovery.ts
|
|
543
|
+
var readEnvDiscovery = (env = process.env) => env["ROUTE33_DISCOVERY"] !== "0";
|
|
544
|
+
|
|
545
|
+
// src/cli/read-env-strict/read-env-strict.ts
|
|
546
|
+
var readEnvStrict = (env = process.env) => env["ROUTE33_STRICT"] !== "0";
|
|
547
|
+
|
|
548
|
+
// src/cli/resolve-v1-secret-store-directory/resolve-v1-secret-store-directory.ts
|
|
549
|
+
import * as os2 from "node:os";
|
|
550
|
+
import * as path2 from "node:path";
|
|
551
|
+
var resolveV1SecretStoreDirectory = (options = {}) => {
|
|
552
|
+
const explicit = options.directory?.trim();
|
|
553
|
+
if (explicit !== void 0 && explicit.length > 0) {
|
|
554
|
+
return path2.resolve(explicit);
|
|
555
|
+
}
|
|
556
|
+
const hostConfig = (options.env ?? process.env)["OPENCODE_CONFIG_DIR"]?.trim();
|
|
557
|
+
if (hostConfig !== void 0 && hostConfig.length > 0) {
|
|
558
|
+
return path2.join(path2.resolve(hostConfig), "route33");
|
|
559
|
+
}
|
|
560
|
+
return path2.join(
|
|
561
|
+
options.homeDirectory ?? os2.homedir(),
|
|
562
|
+
".config",
|
|
563
|
+
"opencode",
|
|
564
|
+
"route33"
|
|
565
|
+
);
|
|
566
|
+
};
|
|
567
|
+
|
|
568
|
+
// src/config/abortable-sleep/abortable-sleep.ts
|
|
569
|
+
var abortableSleep = (milliseconds, signal) => new Promise((resolve2, reject) => {
|
|
570
|
+
if (signal === void 0) {
|
|
571
|
+
setTimeout(resolve2, milliseconds);
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
const abortError = () => {
|
|
575
|
+
const reason = signal.reason;
|
|
576
|
+
if (reason instanceof Error) {
|
|
577
|
+
return reason;
|
|
578
|
+
}
|
|
579
|
+
return new Error(String(reason), { cause: reason });
|
|
580
|
+
};
|
|
581
|
+
if (signal.aborted) {
|
|
582
|
+
reject(abortError());
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
const onAbort = () => {
|
|
586
|
+
clearTimeout(timer);
|
|
587
|
+
reject(abortError());
|
|
588
|
+
};
|
|
589
|
+
const timer = setTimeout(() => {
|
|
590
|
+
signal.removeEventListener("abort", onAbort);
|
|
591
|
+
resolve2();
|
|
592
|
+
}, milliseconds);
|
|
593
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
// src/config/is-resendable-body/is-resendable-body.ts
|
|
597
|
+
var isResendableBody = (body) => body === void 0 || typeof body === "string" || body instanceof ArrayBuffer || ArrayBuffer.isView(body) || body instanceof Blob || body instanceof URLSearchParams || body instanceof FormData;
|
|
598
|
+
|
|
599
|
+
// src/config/parse-exhaustion-envelope/parse-exhaustion-envelope.ts
|
|
600
|
+
var parseExhaustionEnvelope = async (response) => {
|
|
601
|
+
let body;
|
|
602
|
+
try {
|
|
603
|
+
body = await response.clone().json();
|
|
604
|
+
} catch (error) {
|
|
605
|
+
if (error instanceof Error) {
|
|
606
|
+
return void 0;
|
|
607
|
+
}
|
|
608
|
+
throw error;
|
|
609
|
+
}
|
|
610
|
+
if (!isRecord(body)) {
|
|
611
|
+
return void 0;
|
|
612
|
+
}
|
|
613
|
+
const nestedError = body["error"];
|
|
614
|
+
const nestedCode = isRecord(nestedError) ? nestedError["code"] : void 0;
|
|
615
|
+
if (body["code"] !== "PROVIDER_LANES_EXHAUSTED" && nestedCode !== "PROVIDER_LANES_EXHAUSTED") {
|
|
616
|
+
return void 0;
|
|
617
|
+
}
|
|
618
|
+
const nestedId = isRecord(nestedError) ? nestedError["correlationId"] : void 0;
|
|
619
|
+
const directId = body["correlationId"];
|
|
620
|
+
let correlationId;
|
|
621
|
+
if (typeof directId === "string") {
|
|
622
|
+
correlationId = directId;
|
|
623
|
+
} else if (typeof nestedId === "string") {
|
|
624
|
+
correlationId = nestedId;
|
|
625
|
+
}
|
|
626
|
+
return { correlationId };
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
// src/config/request-model/request-model.ts
|
|
630
|
+
var requestModel = (body) => {
|
|
631
|
+
if (typeof body !== "string") {
|
|
632
|
+
return void 0;
|
|
633
|
+
}
|
|
634
|
+
let parsed;
|
|
635
|
+
try {
|
|
636
|
+
parsed = JSON.parse(body);
|
|
637
|
+
} catch (error) {
|
|
638
|
+
if (error instanceof SyntaxError) {
|
|
639
|
+
return void 0;
|
|
640
|
+
}
|
|
641
|
+
throw error;
|
|
642
|
+
}
|
|
643
|
+
return isRecord(parsed) && typeof parsed["model"] === "string" ? parsed["model"] : void 0;
|
|
644
|
+
};
|
|
645
|
+
|
|
646
|
+
// src/config/retry-wait-seconds/retry-wait-seconds.ts
|
|
647
|
+
var retryWaitSeconds = (options) => {
|
|
648
|
+
const advised = options.retryAfter !== null && /^\d+$/.test(options.retryAfter) ? Number(options.retryAfter) : void 0;
|
|
649
|
+
return advised !== void 0 && advised > 0 ? Math.min(advised, options.maxWaitSeconds) : Math.min(
|
|
650
|
+
options.baseSeconds * 2 ** (options.attempt - 1),
|
|
651
|
+
options.maxWaitSeconds
|
|
652
|
+
);
|
|
653
|
+
};
|
|
654
|
+
|
|
655
|
+
// src/config/exhaustion-retry-fetch/exhaustion-retry-fetch.ts
|
|
656
|
+
var buildExhaustionRetryFetch = (innerFetch, options = {}) => {
|
|
657
|
+
const sleep = options.sleep ?? abortableSleep;
|
|
658
|
+
const maxWaitSeconds = options.maxWaitSeconds ?? 1200;
|
|
659
|
+
const baseSeconds = options.baseSeconds ?? 5;
|
|
660
|
+
const parseRetryableExhaustion = async (response) => {
|
|
661
|
+
if (response.status !== 429) {
|
|
662
|
+
return void 0;
|
|
663
|
+
}
|
|
222
664
|
const contentType = response.headers.get("content-type") ?? "";
|
|
223
665
|
if (!/(?:^|\/)json(?:;|$)|\+json(?:;|$)/i.test(contentType)) {
|
|
224
666
|
return void 0;
|
|
@@ -443,8 +885,8 @@ var catalogMemory = /* @__PURE__ */ new Map();
|
|
|
443
885
|
var CATALOG_CACHE_VERSION = 2;
|
|
444
886
|
|
|
445
887
|
// src/catalog/catalog-cache/hash-key/hash-key.ts
|
|
446
|
-
import { createHash } from "node:crypto";
|
|
447
|
-
var hashKey = (baseURL, gatewayKey) =>
|
|
888
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
889
|
+
var hashKey = (baseURL, gatewayKey) => createHash2("sha256").update(`${baseURL}
|
|
448
890
|
${gatewayKey.value}`).digest("hex").slice(0, 12);
|
|
449
891
|
|
|
450
892
|
// src/catalog/catalog-cache/is-catalog-model-record/is-catalog-model-record.ts
|
|
@@ -548,86 +990,26 @@ var catalogModelsUrl = (baseURL) => {
|
|
|
548
990
|
// src/catalog/default-fetch-timeout-ms/default-fetch-timeout-ms.ts
|
|
549
991
|
var DEFAULT_FETCH_TIMEOUT_MS = 5e3;
|
|
550
992
|
|
|
551
|
-
// src/contract/
|
|
552
|
-
var
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
// src/contract/adapters/gateway-api-key/gateway-api-key.ts
|
|
556
|
-
var GatewayApiKey = class {
|
|
557
|
-
value;
|
|
558
|
-
constructor(value) {
|
|
559
|
-
this.value = value;
|
|
993
|
+
// src/contract/gateway-catalog/parse-catalog-result/parse-catalog-result.ts
|
|
994
|
+
var parseCatalogResult = (value) => {
|
|
995
|
+
if (!isRecord(value) || !Array.isArray(value["data"])) {
|
|
996
|
+
throw new ContractParseError("invalid catalog response");
|
|
560
997
|
}
|
|
998
|
+
return value;
|
|
561
999
|
};
|
|
562
1000
|
|
|
563
|
-
// src/
|
|
564
|
-
var
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
this.value = value;
|
|
1001
|
+
// src/catalog/parse-catalog-entries/parse-catalog-entries.ts
|
|
1002
|
+
var parseCatalogEntries = (payload) => {
|
|
1003
|
+
if (Array.isArray(payload)) {
|
|
1004
|
+
return payload;
|
|
568
1005
|
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
return new GatewayApiKey(value);
|
|
577
|
-
};
|
|
578
|
-
|
|
579
|
-
// src/contract/adapters/parse-management-jwt/parse-management-jwt.ts
|
|
580
|
-
var parseManagementJwt = (value) => {
|
|
581
|
-
if (typeof value !== "string" || value.length === 0) {
|
|
582
|
-
throw new Error("management token must be non-empty");
|
|
583
|
-
}
|
|
584
|
-
return new ManagementJwt(value);
|
|
585
|
-
};
|
|
586
|
-
|
|
587
|
-
// src/contract/adapters/parse-management-subject/parse-management-subject.ts
|
|
588
|
-
var parseManagementSubject = (credential) => {
|
|
589
|
-
const encodedPayload = credential.value.split(".")[1];
|
|
590
|
-
if (encodedPayload === void 0 || encodedPayload.length === 0) {
|
|
591
|
-
throw new ContractParseError("management token carries no usable subject");
|
|
592
|
-
}
|
|
593
|
-
let payload;
|
|
594
|
-
try {
|
|
595
|
-
payload = JSON.parse(
|
|
596
|
-
Buffer.from(encodedPayload, "base64url").toString("utf8")
|
|
597
|
-
);
|
|
598
|
-
} catch {
|
|
599
|
-
throw new ContractParseError("management token payload is not JSON");
|
|
600
|
-
}
|
|
601
|
-
if (!isRecord(payload)) {
|
|
602
|
-
throw new ContractParseError("management token payload is not an object");
|
|
603
|
-
}
|
|
604
|
-
const subject = payload["sub"];
|
|
605
|
-
if (typeof subject !== "string" || subject.length === 0) {
|
|
606
|
-
throw new ContractParseError("management token carries no usable subject");
|
|
607
|
-
}
|
|
608
|
-
return subject;
|
|
609
|
-
};
|
|
610
|
-
|
|
611
|
-
// src/contract/gateway-catalog/parse-catalog-result/parse-catalog-result.ts
|
|
612
|
-
var parseCatalogResult = (value) => {
|
|
613
|
-
if (!isRecord(value) || !Array.isArray(value["data"])) {
|
|
614
|
-
throw new ContractParseError("invalid catalog response");
|
|
615
|
-
}
|
|
616
|
-
return value;
|
|
617
|
-
};
|
|
618
|
-
|
|
619
|
-
// src/catalog/parse-catalog-entries/parse-catalog-entries.ts
|
|
620
|
-
var parseCatalogEntries = (payload) => {
|
|
621
|
-
if (Array.isArray(payload)) {
|
|
622
|
-
return payload;
|
|
623
|
-
}
|
|
624
|
-
try {
|
|
625
|
-
return parseCatalogResult(payload).data;
|
|
626
|
-
} catch (error) {
|
|
627
|
-
if (error instanceof ContractParseError) {
|
|
628
|
-
throw new CatalogFetchError("parse", error.message);
|
|
629
|
-
}
|
|
630
|
-
throw error;
|
|
1006
|
+
try {
|
|
1007
|
+
return parseCatalogResult(payload).data;
|
|
1008
|
+
} catch (error) {
|
|
1009
|
+
if (error instanceof ContractParseError) {
|
|
1010
|
+
throw new CatalogFetchError("parse", error.message);
|
|
1011
|
+
}
|
|
1012
|
+
throw error;
|
|
631
1013
|
}
|
|
632
1014
|
};
|
|
633
1015
|
|
|
@@ -2453,508 +2835,134 @@ var createLeaseInstallationFactory = (options) => {
|
|
|
2453
2835
|
const installation = {
|
|
2454
2836
|
keyName: `route33:lease:${scopeId}`,
|
|
2455
2837
|
secretStore: options.createSecretStore(scopeId),
|
|
2456
|
-
...options.createStateStore === void 0 ? {} : { stateStore: options.createStateStore(scopeId) },
|
|
2457
|
-
state: LeaseState.restore(void 0)
|
|
2458
|
-
};
|
|
2459
|
-
installations.set(scopeId, installation);
|
|
2460
|
-
return installation;
|
|
2461
|
-
};
|
|
2462
|
-
};
|
|
2463
|
-
|
|
2464
|
-
// src/key-lease/scope-id/scope-id.ts
|
|
2465
|
-
import { createHash as createHash2 } from "node:crypto";
|
|
2466
|
-
var deriveScopeId = (input) => {
|
|
2467
|
-
let identity;
|
|
2468
|
-
switch (input.attribution.kind) {
|
|
2469
|
-
case "personal":
|
|
2470
|
-
identity = ["route33-lease", "personal"];
|
|
2471
|
-
break;
|
|
2472
|
-
case "project":
|
|
2473
|
-
if (input.folderRealpath === void 0 || input.folderRealpath.length === 0) {
|
|
2474
|
-
throw new TypeError("Project lease scope requires a real folder path");
|
|
2475
|
-
}
|
|
2476
|
-
identity = [
|
|
2477
|
-
"route33-lease",
|
|
2478
|
-
"project",
|
|
2479
|
-
input.attribution.projectId,
|
|
2480
|
-
input.folderRealpath
|
|
2481
|
-
];
|
|
2482
|
-
break;
|
|
2483
|
-
default: {
|
|
2484
|
-
const unexpected = input.attribution;
|
|
2485
|
-
throw new TypeError(`Unexpected attribution: ${String(unexpected)}`);
|
|
2486
|
-
}
|
|
2487
|
-
}
|
|
2488
|
-
return createHash2("sha256").update(JSON.stringify(identity)).digest("hex");
|
|
2489
|
-
};
|
|
2490
|
-
|
|
2491
|
-
// src/requested-client-id/requested-client-id.ts
|
|
2492
|
-
var DEFAULT_ROUTE33_CLIENT_ID = false ? "" : "oa_platform_203623de";
|
|
2493
|
-
|
|
2494
|
-
// src/commands/change-attribution/change-attribution.ts
|
|
2495
|
-
var changeAttribution = async (options) => {
|
|
2496
|
-
await options.clear();
|
|
2497
|
-
return options.prompt();
|
|
2498
|
-
};
|
|
2499
|
-
|
|
2500
|
-
// src/commands/inspect-session/inspect-session.ts
|
|
2501
|
-
var inspectSession = (options) => {
|
|
2502
|
-
const attribution = options.readAttribution();
|
|
2503
|
-
const lease = options.readLease();
|
|
2504
|
-
return {
|
|
2505
|
-
attribution,
|
|
2506
|
-
...lease === void 0 ? {} : { lease: { expiresAt: lease.expiresAt, keyId: lease.keyId } }
|
|
2507
|
-
};
|
|
2508
|
-
};
|
|
2509
|
-
|
|
2510
|
-
// src/commands/reset-attribution/reset-attribution.ts
|
|
2511
|
-
var resetAttribution = async (options) => {
|
|
2512
|
-
await options.clear();
|
|
2513
|
-
};
|
|
2514
|
-
|
|
2515
|
-
// src/commands/rotate-now/rotate-now.ts
|
|
2516
|
-
var rotateNow = async (options) => {
|
|
2517
|
-
const rotated = await options.lease({ renewalRatio: Number.EPSILON });
|
|
2518
|
-
return rotated.lease;
|
|
2519
|
-
};
|
|
2520
|
-
|
|
2521
|
-
// src/ux/attribution-prompt/attribution-prompt.ts
|
|
2522
|
-
var createAttributionPrompt = (projects) => {
|
|
2523
|
-
const choicesBySelection = {
|
|
2524
|
-
personal: { kind: "personal" }
|
|
2525
|
-
};
|
|
2526
|
-
const projectOptions = projects.map((project) => {
|
|
2527
|
-
const selection = `project:${project.id}`;
|
|
2528
|
-
choicesBySelection[selection] = {
|
|
2529
|
-
kind: "project",
|
|
2530
|
-
projectId: project.id
|
|
2531
|
-
};
|
|
2532
|
-
return {
|
|
2533
|
-
label: `${project.name} (${project.organisation.name})`,
|
|
2534
|
-
selection
|
|
2535
|
-
};
|
|
2536
|
-
});
|
|
2537
|
-
return {
|
|
2538
|
-
choicesBySelection,
|
|
2539
|
-
options: [
|
|
2540
|
-
...projectOptions,
|
|
2541
|
-
{ label: "Use my personal key", selection: "personal" }
|
|
2542
|
-
],
|
|
2543
|
-
title: "Choose where Route 33 should manage this key"
|
|
2544
|
-
};
|
|
2545
|
-
};
|
|
2546
|
-
|
|
2547
|
-
// src/ux/attribution-prompt-error/attribution-prompt-error.ts
|
|
2548
|
-
var MESSAGES = {
|
|
2549
|
-
ATTRIBUTION_PRESENTER_UNAVAILABLE: "Route 33 needs OpenCode v2 to assign a budget for this folder. Update to OpenCode v2, run /connect, choose Route 33, and follow the steps.",
|
|
2550
|
-
ATTRIBUTION_SELECTION_INVALID: "That Route 33 option is no longer available. Connect again and pick another."
|
|
2551
|
-
};
|
|
2552
|
-
var AttributionPromptError = class extends GatewayCredentialError {
|
|
2553
|
-
code;
|
|
2554
|
-
constructor(code) {
|
|
2555
|
-
super(MESSAGES[code]);
|
|
2556
|
-
this.name = "AttributionPromptError";
|
|
2557
|
-
this.code = code;
|
|
2558
|
-
}
|
|
2559
|
-
};
|
|
2560
|
-
|
|
2561
|
-
// src/ux/prompt-flow/prompt-flow.ts
|
|
2562
|
-
var resolveAttribution = async (options) => {
|
|
2563
|
-
const remembered = readAttribution(options.state);
|
|
2564
|
-
if (remembered !== void 0) {
|
|
2565
|
-
return remembered;
|
|
2566
|
-
}
|
|
2567
|
-
if (options.presenter === void 0) {
|
|
2568
|
-
throw new AttributionPromptError("ATTRIBUTION_PRESENTER_UNAVAILABLE");
|
|
2569
|
-
}
|
|
2570
|
-
const plan = createAttributionPrompt(await options.loadProjects());
|
|
2571
|
-
const selection = await options.presenter.pick(plan);
|
|
2572
|
-
const attribution = plan.choicesBySelection[selection];
|
|
2573
|
-
if (attribution === void 0) {
|
|
2574
|
-
throw new AttributionPromptError("ATTRIBUTION_SELECTION_INVALID");
|
|
2575
|
-
}
|
|
2576
|
-
writeAttribution(options.state, attribution);
|
|
2577
|
-
return attribution;
|
|
2578
|
-
};
|
|
2579
|
-
|
|
2580
|
-
// src/ux/trusted-party-copy/trusted-party-copy.ts
|
|
2581
|
-
var TRUSTED_PARTY_COPY = {
|
|
2582
|
-
message: "Route 33 wants to manage your keys for this OpenCode installation.",
|
|
2583
|
-
title: "Connect Route 33"
|
|
2584
|
-
};
|
|
2585
|
-
|
|
2586
|
-
// src/auth/auth-error/auth-error.ts
|
|
2587
|
-
var AuthError = class extends Error {
|
|
2588
|
-
constructor(code, options) {
|
|
2589
|
-
super(
|
|
2590
|
-
"Route 33 authorization failed",
|
|
2591
|
-
options?.cause === void 0 ? void 0 : { cause: options.cause }
|
|
2592
|
-
);
|
|
2593
|
-
this.code = code;
|
|
2594
|
-
this.name = "AuthError";
|
|
2595
|
-
this.statusCode = options?.statusCode;
|
|
2596
|
-
}
|
|
2597
|
-
code;
|
|
2598
|
-
statusCode;
|
|
2599
|
-
};
|
|
2600
|
-
|
|
2601
|
-
// src/auth/build-authorize-url/build-authorize-url.ts
|
|
2602
|
-
var buildAuthorizeUrl = (issuerUrl, request) => {
|
|
2603
|
-
const url = new URL("/authorize", issuerUrl);
|
|
2604
|
-
url.search = new URLSearchParams({
|
|
2605
|
-
client_id: request.clientId,
|
|
2606
|
-
code_challenge: request.codeChallenge,
|
|
2607
|
-
code_challenge_method: "S256",
|
|
2608
|
-
redirect_uri: request.redirectUri,
|
|
2609
|
-
response_type: "code",
|
|
2610
|
-
scope: request.scope,
|
|
2611
|
-
state: request.state
|
|
2612
|
-
}).toString();
|
|
2613
|
-
return url;
|
|
2614
|
-
};
|
|
2615
|
-
|
|
2616
|
-
// src/contract/auth-session/parse-token-set/parse-token-set.ts
|
|
2617
|
-
var parseTokenSet = (value) => {
|
|
2618
|
-
if (!isRecord(value)) {
|
|
2619
|
-
throw new ContractParseError("invalid token response");
|
|
2620
|
-
}
|
|
2621
|
-
if (typeof value["access_token"] !== "string" || typeof value["expires_in"] !== "number" || value["token_type"] !== "Bearer") {
|
|
2622
|
-
throw new ContractParseError("invalid token response");
|
|
2623
|
-
}
|
|
2624
|
-
return {
|
|
2625
|
-
access_token: value["access_token"],
|
|
2626
|
-
expires_in: value["expires_in"],
|
|
2627
|
-
token_type: "Bearer",
|
|
2628
|
-
...typeof value["id_token"] === "string" ? { id_token: value["id_token"] } : {},
|
|
2629
|
-
...typeof value["refresh_token"] === "string" ? { refresh_token: value["refresh_token"] } : {},
|
|
2630
|
-
...typeof value["scope"] === "string" ? { scope: value["scope"] } : {}
|
|
2631
|
-
};
|
|
2632
|
-
};
|
|
2633
|
-
|
|
2634
|
-
// src/auth/exchange-code-and-parse/exchange-code-and-parse.ts
|
|
2635
|
-
var exchangeCodeAndParse = async (options) => {
|
|
2636
|
-
const body = new URLSearchParams({
|
|
2637
|
-
client_id: options.clientId,
|
|
2638
|
-
code: options.input.code,
|
|
2639
|
-
code_verifier: options.input.codeVerifier,
|
|
2640
|
-
grant_type: "authorization_code",
|
|
2641
|
-
redirect_uri: options.input.redirectUri
|
|
2642
|
-
});
|
|
2643
|
-
let response;
|
|
2644
|
-
try {
|
|
2645
|
-
response = await (options.fetchImpl ?? fetch)(options.tokenUrl, {
|
|
2646
|
-
body,
|
|
2647
|
-
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
2648
|
-
method: "POST"
|
|
2649
|
-
});
|
|
2650
|
-
} catch (cause) {
|
|
2651
|
-
throw new AuthError("AUTH_CODE_EXCHANGE_FAILED", { cause });
|
|
2652
|
-
}
|
|
2653
|
-
if (!response.ok) {
|
|
2654
|
-
throw new AuthError("AUTH_CODE_EXCHANGE_FAILED", {
|
|
2655
|
-
statusCode: response.status
|
|
2656
|
-
});
|
|
2657
|
-
}
|
|
2658
|
-
let payload;
|
|
2659
|
-
try {
|
|
2660
|
-
payload = await response.json();
|
|
2661
|
-
} catch (cause) {
|
|
2662
|
-
throw new AuthError("AUTH_CODE_EXCHANGE_FAILED", { cause });
|
|
2663
|
-
}
|
|
2664
|
-
try {
|
|
2665
|
-
return parseTokenSet(payload);
|
|
2666
|
-
} catch (cause) {
|
|
2667
|
-
if (cause instanceof ContractParseError) {
|
|
2668
|
-
throw new AuthError("AUTH_CODE_EXCHANGE_FAILED", { cause });
|
|
2669
|
-
}
|
|
2670
|
-
throw cause;
|
|
2671
|
-
}
|
|
2672
|
-
};
|
|
2673
|
-
|
|
2674
|
-
// src/auth/refresh-session/refresh-session.ts
|
|
2675
|
-
var refreshSession = async (options) => {
|
|
2676
|
-
const body = new URLSearchParams({
|
|
2677
|
-
client_id: options.clientId,
|
|
2678
|
-
grant_type: "refresh_token",
|
|
2679
|
-
refresh_token: options.refreshToken
|
|
2680
|
-
});
|
|
2681
|
-
let response;
|
|
2682
|
-
try {
|
|
2683
|
-
response = await (options.fetchImpl ?? fetch)(options.tokenUrl, {
|
|
2684
|
-
body,
|
|
2685
|
-
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
2686
|
-
method: "POST"
|
|
2687
|
-
});
|
|
2688
|
-
} catch (cause) {
|
|
2689
|
-
throw new AuthError("AUTH_REAUTHENTICATION_REQUIRED", { cause });
|
|
2690
|
-
}
|
|
2691
|
-
if (!response.ok) {
|
|
2692
|
-
throw new AuthError("AUTH_REAUTHENTICATION_REQUIRED", {
|
|
2693
|
-
statusCode: response.status
|
|
2694
|
-
});
|
|
2695
|
-
}
|
|
2696
|
-
let payload;
|
|
2697
|
-
try {
|
|
2698
|
-
payload = await response.json();
|
|
2699
|
-
} catch (cause) {
|
|
2700
|
-
throw new AuthError("AUTH_REAUTHENTICATION_REQUIRED", { cause });
|
|
2701
|
-
}
|
|
2702
|
-
try {
|
|
2703
|
-
return parseTokenSet(payload);
|
|
2704
|
-
} catch (cause) {
|
|
2705
|
-
if (cause instanceof ContractParseError) {
|
|
2706
|
-
throw new AuthError("AUTH_REAUTHENTICATION_REQUIRED", { cause });
|
|
2707
|
-
}
|
|
2708
|
-
throw cause;
|
|
2709
|
-
}
|
|
2710
|
-
};
|
|
2711
|
-
|
|
2712
|
-
// src/auth/create-auth-session/create-auth-session.ts
|
|
2713
|
-
var createAuthSession = (options) => {
|
|
2714
|
-
const tokenUrl = new URL("/oauth/token", options.issuerUrl);
|
|
2715
|
-
return {
|
|
2716
|
-
authorizationUrl: (request) => buildAuthorizeUrl(options.issuerUrl, request),
|
|
2717
|
-
exchangeCode: (input) => exchangeCodeAndParse({
|
|
2718
|
-
clientId: options.clientId,
|
|
2719
|
-
...options.fetchImpl === void 0 ? {} : { fetchImpl: options.fetchImpl },
|
|
2720
|
-
input,
|
|
2721
|
-
tokenUrl
|
|
2722
|
-
}),
|
|
2723
|
-
refresh: (refreshToken) => refreshSession({
|
|
2724
|
-
clientId: options.clientId,
|
|
2725
|
-
...options.fetchImpl === void 0 ? {} : { fetchImpl: options.fetchImpl },
|
|
2726
|
-
refreshToken,
|
|
2727
|
-
tokenUrl
|
|
2728
|
-
})
|
|
2729
|
-
};
|
|
2730
|
-
};
|
|
2731
|
-
|
|
2732
|
-
// src/auth/open-browser-and-capture/open-browser-and-capture.ts
|
|
2733
|
-
import { spawn } from "node:child_process";
|
|
2734
|
-
import { timingSafeEqual } from "node:crypto";
|
|
2735
|
-
|
|
2736
|
-
// src/auth/loopback-callback/loopback-callback.ts
|
|
2737
|
-
import { createServer } from "node:http";
|
|
2738
|
-
|
|
2739
|
-
// src/auth/loopback-callback/callback-page/callback-page.ts
|
|
2740
|
-
var MARK_PATHS = [
|
|
2741
|
-
"M310.6,175.8c0-6.1-2.7-11.8-7.2-15.9l-127.6-114.4V7h-43.5v48.3c0,6.2,2.6,12.1,7.2,16.2l92.5,82.9H0v43.5h234.7l-95.6,91.4c-4.3,4.1-6.7,9.8-6.7,15.7v48.3h43.5v-39l128.1-122.4c4.4-4.2,6.8-10,6.7-16.1h0Z",
|
|
2742
|
-
"M457.2,175.9v-2.6c18.4-4.9,33-14.1,44-27.7,11-13.6,16.5-30.3,16.5-50.3s-4.5-32.9-13.4-47.5c-9-14.5-21.6-26.1-38-34.9-16.4-8.7-35.8-13.1-58.3-13.1s-41.1,4-58.8,12.1-31.8,19.3-42.4,33.7-16.1,31-16.6,50h51.1c.3-10.8,3.6-20,9.8-27.7,6.2-7.6,14.2-13.5,24.1-17.5,9.9-4.1,20.6-6.1,32-6.1s22.3,2.2,31.1,6.7c8.8,4.5,15.6,10.8,20.5,18.8,4.9,8.1,7.3,17.5,7.3,28.4s-2.8,21.1-8.4,29.4-13.3,14.8-23.2,19.5-21.3,7.1-34.2,7.1h-29.9v43.3h29.9c16.2,0,30,2.5,41.5,7.5,11.5,5,20.2,12,26.1,21.1,6,9,9,19.4,9,31.2s-3,21.2-9.1,29.8-14.4,15.3-25.1,20.1c-10.7,4.9-22.9,7.3-36.7,7.3s-24.2-2.1-34.5-6.2-18.6-10-24.8-17.5c-6.3-7.5-9.8-16.5-10.6-26.8h-53.2c.6,19,6.3,35.7,17,50.2,10.7,14.5,25.2,25.7,43.6,33.8,18.4,8.1,39.4,12.1,63.1,12.1s45.1-4.4,63.7-13.2c18.6-8.8,33.3-20.8,44-36,10.8-15.2,16.1-32.5,16.1-51.8s-6.4-40.7-19.2-55.6c-12.8-14.9-30.8-24.2-54-27.8h0Z",
|
|
2743
|
-
"M763,203.7c-12.8-14.9-30.8-24.2-54-27.8v-2.6c18.4-4.9,33-14.1,44-27.7,11-13.6,16.5-30.3,16.5-50.3s-4.5-32.9-13.4-47.5c-8.9-14.5-21.6-26.1-38-34.9-16.4-8.7-35.8-13.1-58.3-13.1s-41.1,4-58.8,12.1-31.8,19.3-42.4,33.7-16.1,31-16.6,50h51.1c.3-10.8,3.6-20,9.8-27.7,6.2-7.6,14.2-13.5,24.1-17.5,9.9-4.1,20.6-6.1,32-6.1s22.3,2.2,31.1,6.7c8.8,4.5,15.6,10.8,20.5,18.8,4.9,8.1,7.3,17.5,7.3,28.4s-2.8,21.1-8.4,29.4c-5.6,8.3-13.3,14.8-23.2,19.5s-21.3,7.1-34.2,7.1h-29.9v43.3h29.9c16.2,0,30,2.5,41.5,7.5,11.5,5,20.2,12,26.1,21.1,6,9,8.9,19.4,8.9,31.2s-3,21.2-9.1,29.8c-6.1,8.6-14.4,15.3-25.1,20.1-10.7,4.9-22.9,7.3-36.7,7.3s-24.2-2.1-34.5-6.2-18.6-10-24.8-17.5c-6.3-7.5-9.8-16.5-10.6-26.8h-53.2c.6,19,6.3,35.7,17,50.2,10.7,14.5,25.2,25.7,43.6,33.8,18.4,8.1,39.4,12.1,63.1,12.1s45.1-4.4,63.7-13.2,33.3-20.8,44-36c10.8-15.2,16.1-32.5,16.1-51.8s-6.4-40.7-19.2-55.6h0Z"
|
|
2744
|
-
];
|
|
2745
|
-
var BRAND_MARK = `<svg class="r33-mark" viewBox="0 0 782.2 360.3" fill="currentColor" role="img" aria-label="route33.ai" xmlns="http://www.w3.org/2000/svg"><path d="${MARK_PATHS[0]}"/><path d="${MARK_PATHS[1]}"/><path d="${MARK_PATHS[2]}"/></svg>`;
|
|
2746
|
-
var PAGE_STYLE = `:root{color-scheme:dark;--r33-euro-blue:#0000FF;--r33-off-white:#F1F1F1;--r33-off-black:#121212;--r33-grey-300:#C9CBCF;--r33-radius-fillet:4px;--r33-space-2:8px;--r33-space-3:16px;--r33-space-4:24px;--r33-space-6:64px;--r33-font-body:"Geist","Helvetica Neue",Arial,sans-serif;--r33-font-mono:"Geist Mono","SFMono-Regular",Menlo,monospace;--r33-surface:var(--r33-off-black);--r33-text-primary:var(--r33-off-white);--r33-text-secondary:var(--r33-grey-300);--r33-fill-accent:var(--r33-euro-blue);--r33-on-fill-accent:var(--r33-off-white)}
|
|
2747
|
-
*{box-sizing:border-box}
|
|
2748
|
-
html,body{margin:0}
|
|
2749
|
-
.r33-page{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:var(--r33-space-6);background:var(--r33-surface);color:var(--r33-text-primary);font-family:var(--r33-font-body);-webkit-font-smoothing:antialiased}
|
|
2750
|
-
.r33-panel{display:grid;gap:var(--r33-space-4);max-width:460px;width:100%}
|
|
2751
|
-
.r33-mark{height:40px;width:auto;color:var(--r33-text-primary)}
|
|
2752
|
-
.r33-status{display:inline-flex;align-items:center;gap:var(--r33-space-2);font-size:11px;letter-spacing:0.08em;text-transform:uppercase;color:var(--r33-text-secondary)}
|
|
2753
|
-
.r33-status__dot{width:8px;height:8px;background:var(--r33-fill-accent)}
|
|
2754
|
-
.r33-status__dot--error{background:transparent;border:1px solid var(--r33-text-primary);border-radius:50%}
|
|
2755
|
-
.r33-headline{margin:0;font-size:48px;line-height:0.85;letter-spacing:-0.02em;font-weight:600;color:var(--r33-text-primary);text-wrap:balance}
|
|
2756
|
-
.r33-subline{margin:0;font-size:16px;line-height:1.45;color:var(--r33-text-secondary);max-width:40ch;text-wrap:pretty}
|
|
2757
|
-
.r33-code{margin:0;font-size:13px;letter-spacing:0.04em;color:var(--r33-text-secondary)}
|
|
2758
|
-
.r33-code__value{font-family:var(--r33-font-mono);text-transform:uppercase;color:var(--r33-text-primary)}
|
|
2759
|
-
@media (prefers-reduced-motion:reduce){*{animation-duration:1ms!important;transition-duration:1ms!important}}`;
|
|
2760
|
-
var buildCallbackPage = (state, errorCode) => {
|
|
2761
|
-
const isError = state === "error";
|
|
2762
|
-
const safeCode = (errorCode ?? "").replace(/[^a-z0-9_-]/gi, "");
|
|
2763
|
-
const title = isError ? "Sign-in failed" : "Signed in";
|
|
2764
|
-
const headline = isError ? "Sign-in failed" : "Signed in to route33";
|
|
2765
|
-
const subline = isError ? "Return to OpenCode and retry." : "Return to OpenCode \u2014 one more step may wait there: if it asks which budget covers usage, pick a project. Then this window can be closed.";
|
|
2766
|
-
const statusLabel = isError ? "Authentication error" : "Authenticated";
|
|
2767
|
-
const dotClass = isError ? "r33-status__dot r33-status__dot--error" : "r33-status__dot";
|
|
2768
|
-
const codeLine = isError && safeCode.length > 0 ? `<p class="r33-code">Error code: <span class="r33-code__value">${safeCode}</span></p>` : "";
|
|
2769
|
-
return `<!doctype html>
|
|
2770
|
-
<html lang="en" data-theme="dark">
|
|
2771
|
-
<head>
|
|
2772
|
-
<meta charset="utf-8">
|
|
2773
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
2774
|
-
<title>${title} \xB7 route33.ai</title>
|
|
2775
|
-
<style>${PAGE_STYLE}</style>
|
|
2776
|
-
</head>
|
|
2777
|
-
<body>
|
|
2778
|
-
<main class="r33-page">
|
|
2779
|
-
<div class="r33-panel">
|
|
2780
|
-
${BRAND_MARK}
|
|
2781
|
-
<span class="r33-status"><span class="${dotClass}"></span>${statusLabel}</span>
|
|
2782
|
-
<h1 class="r33-headline">${headline}</h1>
|
|
2783
|
-
<p class="r33-subline">${subline}</p>
|
|
2784
|
-
${codeLine}
|
|
2785
|
-
</div>
|
|
2786
|
-
</main>
|
|
2787
|
-
</body>
|
|
2788
|
-
</html>`;
|
|
2789
|
-
};
|
|
2790
|
-
|
|
2791
|
-
// src/auth/loopback-callback/loopback-callback.ts
|
|
2792
|
-
var createAuthLoopbackCallback = async () => {
|
|
2793
|
-
let redirectUri = "http://127.0.0.1/callback";
|
|
2794
|
-
let resolveCapture;
|
|
2795
|
-
const captured = new Promise((resolve2) => {
|
|
2796
|
-
resolveCapture = resolve2;
|
|
2797
|
-
});
|
|
2798
|
-
const server = createServer((request, response) => {
|
|
2799
|
-
const url = new URL(request.url ?? "/", redirectUri);
|
|
2800
|
-
if (request.method !== "GET" || url.pathname !== "/callback") {
|
|
2801
|
-
response.writeHead(404).end();
|
|
2802
|
-
return;
|
|
2803
|
-
}
|
|
2804
|
-
const resolve2 = resolveCapture;
|
|
2805
|
-
resolveCapture = void 0;
|
|
2806
|
-
resolve2?.(url);
|
|
2807
|
-
const error = url.searchParams.get("error");
|
|
2808
|
-
const page = error === null || error.length === 0 ? buildCallbackPage("success") : buildCallbackPage("error", error);
|
|
2809
|
-
response.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
2810
|
-
response.end(page);
|
|
2811
|
-
});
|
|
2812
|
-
await new Promise((resolve2, reject) => {
|
|
2813
|
-
const onError = (cause) => {
|
|
2814
|
-
reject(new AuthError("AUTH_CALLBACK_LISTEN_FAILED", { cause }));
|
|
2815
|
-
};
|
|
2816
|
-
server.once("error", onError);
|
|
2817
|
-
server.listen(0, "127.0.0.1", () => {
|
|
2818
|
-
server.off("error", onError);
|
|
2819
|
-
resolve2();
|
|
2820
|
-
});
|
|
2821
|
-
});
|
|
2822
|
-
const address = server.address();
|
|
2823
|
-
if (address === null || typeof address === "string") {
|
|
2824
|
-
server.close();
|
|
2825
|
-
throw new AuthError("AUTH_CALLBACK_LISTEN_FAILED");
|
|
2826
|
-
}
|
|
2827
|
-
redirectUri = `http://127.0.0.1:${String(address.port)}/callback`;
|
|
2828
|
-
return {
|
|
2829
|
-
redirectUri,
|
|
2830
|
-
close: () => {
|
|
2831
|
-
if (!server.listening) {
|
|
2832
|
-
return Promise.resolve();
|
|
2833
|
-
}
|
|
2834
|
-
return new Promise((resolve2, reject) => {
|
|
2835
|
-
server.close((cause) => {
|
|
2836
|
-
if (cause === void 0) {
|
|
2837
|
-
resolve2();
|
|
2838
|
-
return;
|
|
2839
|
-
}
|
|
2840
|
-
reject(new AuthError("AUTH_CALLBACK_CLEANUP_FAILED", { cause }));
|
|
2841
|
-
});
|
|
2842
|
-
});
|
|
2843
|
-
},
|
|
2844
|
-
wait: () => captured
|
|
2838
|
+
...options.createStateStore === void 0 ? {} : { stateStore: options.createStateStore(scopeId) },
|
|
2839
|
+
state: LeaseState.restore(void 0)
|
|
2840
|
+
};
|
|
2841
|
+
installations.set(scopeId, installation);
|
|
2842
|
+
return installation;
|
|
2845
2843
|
};
|
|
2846
2844
|
};
|
|
2847
2845
|
|
|
2848
|
-
// src/
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
}));
|
|
2871
|
-
let timer;
|
|
2872
|
-
const timeout = new Promise((_resolve, reject) => {
|
|
2873
|
-
timer = setTimeout(() => {
|
|
2874
|
-
reject(new AuthError("AUTH_CALLBACK_TIMEOUT"));
|
|
2875
|
-
}, options.timeoutMs ?? 12e4);
|
|
2876
|
-
});
|
|
2877
|
-
const launch = browserRunner(authorizationUrl).catch((cause) => {
|
|
2878
|
-
throw new AuthError("AUTH_BROWSER_LAUNCH_FAILED", { cause });
|
|
2879
|
-
});
|
|
2880
|
-
try {
|
|
2881
|
-
const completed = await Promise.race([
|
|
2882
|
-
Promise.all([launch, callback.wait()]),
|
|
2883
|
-
timeout
|
|
2884
|
-
]);
|
|
2885
|
-
const callbackUrl = completed[1];
|
|
2886
|
-
const actualState = callbackUrl.searchParams.get("state");
|
|
2887
|
-
if (actualState === null) {
|
|
2888
|
-
throw new AuthError("AUTH_CALLBACK_RESPONSE_INVALID");
|
|
2889
|
-
}
|
|
2890
|
-
const expectedState = Buffer.from(options.expectedState);
|
|
2891
|
-
const receivedState = Buffer.from(actualState);
|
|
2892
|
-
if (expectedState.length !== receivedState.length || !timingSafeEqual(expectedState, receivedState)) {
|
|
2893
|
-
throw new AuthError("AUTH_CALLBACK_STATE_MISMATCH");
|
|
2894
|
-
}
|
|
2895
|
-
const code = callbackUrl.searchParams.get("code");
|
|
2896
|
-
if (code === null || code.length === 0) {
|
|
2897
|
-
throw new AuthError("AUTH_CALLBACK_RESPONSE_INVALID");
|
|
2898
|
-
}
|
|
2899
|
-
return { code, redirectUri: callback.redirectUri };
|
|
2900
|
-
} finally {
|
|
2901
|
-
if (timer !== void 0) {
|
|
2902
|
-
clearTimeout(timer);
|
|
2846
|
+
// src/key-lease/scope-id/scope-id.ts
|
|
2847
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
2848
|
+
var deriveScopeId = (input) => {
|
|
2849
|
+
let identity;
|
|
2850
|
+
switch (input.attribution.kind) {
|
|
2851
|
+
case "personal":
|
|
2852
|
+
identity = ["route33-lease", "personal"];
|
|
2853
|
+
break;
|
|
2854
|
+
case "project":
|
|
2855
|
+
if (input.folderRealpath === void 0 || input.folderRealpath.length === 0) {
|
|
2856
|
+
throw new TypeError("Project lease scope requires a real folder path");
|
|
2857
|
+
}
|
|
2858
|
+
identity = [
|
|
2859
|
+
"route33-lease",
|
|
2860
|
+
"project",
|
|
2861
|
+
input.attribution.projectId,
|
|
2862
|
+
input.folderRealpath
|
|
2863
|
+
];
|
|
2864
|
+
break;
|
|
2865
|
+
default: {
|
|
2866
|
+
const unexpected = input.attribution;
|
|
2867
|
+
throw new TypeError(`Unexpected attribution: ${String(unexpected)}`);
|
|
2903
2868
|
}
|
|
2904
|
-
await callback.close();
|
|
2905
2869
|
}
|
|
2870
|
+
return createHash3("sha256").update(JSON.stringify(identity)).digest("hex");
|
|
2906
2871
|
};
|
|
2907
2872
|
|
|
2908
|
-
// src/
|
|
2909
|
-
|
|
2873
|
+
// src/requested-client-id/requested-client-id.ts
|
|
2874
|
+
var DEFAULT_ROUTE33_CLIENT_ID = false ? "" : "oa_platform_203623de";
|
|
2910
2875
|
|
|
2911
|
-
// src/
|
|
2912
|
-
|
|
2913
|
-
|
|
2876
|
+
// src/commands/change-attribution/change-attribution.ts
|
|
2877
|
+
var changeAttribution = async (options) => {
|
|
2878
|
+
await options.clear();
|
|
2879
|
+
return options.prompt();
|
|
2880
|
+
};
|
|
2914
2881
|
|
|
2915
|
-
// src/
|
|
2916
|
-
var
|
|
2917
|
-
const
|
|
2882
|
+
// src/commands/inspect-session/inspect-session.ts
|
|
2883
|
+
var inspectSession = (options) => {
|
|
2884
|
+
const attribution = options.readAttribution();
|
|
2885
|
+
const lease = options.readLease();
|
|
2918
2886
|
return {
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
state: randomBytes(32).toString("base64url")
|
|
2887
|
+
attribution,
|
|
2888
|
+
...lease === void 0 ? {} : { lease: { expiresAt: lease.expiresAt, keyId: lease.keyId } }
|
|
2922
2889
|
};
|
|
2923
2890
|
};
|
|
2924
2891
|
|
|
2925
|
-
// src/
|
|
2926
|
-
var
|
|
2892
|
+
// src/commands/reset-attribution/reset-attribution.ts
|
|
2893
|
+
var resetAttribution = async (options) => {
|
|
2894
|
+
await options.clear();
|
|
2895
|
+
};
|
|
2927
2896
|
|
|
2928
|
-
// src/
|
|
2929
|
-
var
|
|
2930
|
-
const
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
}
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2897
|
+
// src/commands/rotate-now/rotate-now.ts
|
|
2898
|
+
var rotateNow = async (options) => {
|
|
2899
|
+
const rotated = await options.lease({ renewalRatio: Number.EPSILON });
|
|
2900
|
+
return rotated.lease;
|
|
2901
|
+
};
|
|
2902
|
+
|
|
2903
|
+
// src/ux/attribution-prompt/attribution-prompt.ts
|
|
2904
|
+
var createAttributionPrompt = (projects) => {
|
|
2905
|
+
const choicesBySelection = {
|
|
2906
|
+
personal: { kind: "personal" }
|
|
2907
|
+
};
|
|
2908
|
+
const projectOptions = projects.map((project) => {
|
|
2909
|
+
const selection = `project:${project.id}`;
|
|
2910
|
+
choicesBySelection[selection] = {
|
|
2911
|
+
kind: "project",
|
|
2912
|
+
projectId: project.id
|
|
2913
|
+
};
|
|
2914
|
+
return {
|
|
2915
|
+
label: `${project.name} (${project.organisation.name})`,
|
|
2916
|
+
selection
|
|
2917
|
+
};
|
|
2949
2918
|
});
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2919
|
+
return {
|
|
2920
|
+
choicesBySelection,
|
|
2921
|
+
options: [
|
|
2922
|
+
...projectOptions,
|
|
2923
|
+
{ label: "Use my personal key", selection: "personal" }
|
|
2924
|
+
],
|
|
2925
|
+
title: "Choose where Route 33 should manage this key"
|
|
2926
|
+
};
|
|
2927
|
+
};
|
|
2928
|
+
|
|
2929
|
+
// src/ux/attribution-prompt-error/attribution-prompt-error.ts
|
|
2930
|
+
var MESSAGES = {
|
|
2931
|
+
ATTRIBUTION_PRESENTER_UNAVAILABLE: "Route 33 needs OpenCode v2 to assign a budget for this folder. Update to OpenCode v2, run /connect, choose Route 33, and follow the steps.",
|
|
2932
|
+
ATTRIBUTION_SELECTION_INVALID: "That Route 33 option is no longer available. Connect again and pick another."
|
|
2933
|
+
};
|
|
2934
|
+
var AttributionPromptError = class extends GatewayCredentialError {
|
|
2935
|
+
code;
|
|
2936
|
+
constructor(code) {
|
|
2937
|
+
super(MESSAGES[code]);
|
|
2938
|
+
this.name = "AttributionPromptError";
|
|
2939
|
+
this.code = code;
|
|
2955
2940
|
}
|
|
2956
|
-
|
|
2957
|
-
|
|
2941
|
+
};
|
|
2942
|
+
|
|
2943
|
+
// src/ux/prompt-flow/prompt-flow.ts
|
|
2944
|
+
var resolveAttribution = async (options) => {
|
|
2945
|
+
const remembered = readAttribution(options.state);
|
|
2946
|
+
if (remembered !== void 0) {
|
|
2947
|
+
return remembered;
|
|
2948
|
+
}
|
|
2949
|
+
if (options.presenter === void 0) {
|
|
2950
|
+
throw new AttributionPromptError("ATTRIBUTION_PRESENTER_UNAVAILABLE");
|
|
2951
|
+
}
|
|
2952
|
+
const plan = createAttributionPrompt(await options.loadProjects());
|
|
2953
|
+
const selection = await options.presenter.pick(plan);
|
|
2954
|
+
const attribution = plan.choicesBySelection[selection];
|
|
2955
|
+
if (attribution === void 0) {
|
|
2956
|
+
throw new AttributionPromptError("ATTRIBUTION_SELECTION_INVALID");
|
|
2957
|
+
}
|
|
2958
|
+
writeAttribution(options.state, attribution);
|
|
2959
|
+
return attribution;
|
|
2960
|
+
};
|
|
2961
|
+
|
|
2962
|
+
// src/ux/trusted-party-copy/trusted-party-copy.ts
|
|
2963
|
+
var TRUSTED_PARTY_COPY = {
|
|
2964
|
+
message: "Route 33 wants to manage your keys for this OpenCode installation.",
|
|
2965
|
+
title: "Connect Route 33"
|
|
2958
2966
|
};
|
|
2959
2967
|
|
|
2960
2968
|
// src/route33-runtime/login-session/login-session.ts
|
|
@@ -3161,7 +3169,7 @@ var Route33Runtime = class {
|
|
|
3161
3169
|
if (typeof client !== "function") {
|
|
3162
3170
|
return client;
|
|
3163
3171
|
}
|
|
3164
|
-
this.#managementClient ??= client(
|
|
3172
|
+
this.#managementClient ??= client();
|
|
3165
3173
|
return this.#managementClient;
|
|
3166
3174
|
}
|
|
3167
3175
|
#subject(credential) {
|
|
@@ -3268,7 +3276,7 @@ var createWorkspaceKeyHeaders = ({ resolveKey, session }) => async (input, outpu
|
|
|
3268
3276
|
// src/create-route33-plugin/create-route33-plugin.ts
|
|
3269
3277
|
var createRoute33Plugin = (options = {}) => {
|
|
3270
3278
|
const baseURL = readEnvBaseUrl();
|
|
3271
|
-
const
|
|
3279
|
+
const clientId2 = options.clientId ?? readEnvClientId() ?? DEFAULT_ROUTE33_CLIENT_ID;
|
|
3272
3280
|
const directKey = readEnvDirectApiKey();
|
|
3273
3281
|
const resolvedConfig = {
|
|
3274
3282
|
...baseURL === void 0 ? {} : { baseURL },
|
|
@@ -3289,19 +3297,19 @@ var createRoute33Plugin = (options = {}) => {
|
|
|
3289
3297
|
})
|
|
3290
3298
|
});
|
|
3291
3299
|
return (input) => {
|
|
3292
|
-
const
|
|
3300
|
+
const runtime2 = runtimeOptions === void 0 || installation === void 0 ? void 0 : new Route33Runtime({
|
|
3293
3301
|
...runtimeOptions,
|
|
3294
|
-
auth: { ...runtimeOptions.auth, clientId },
|
|
3302
|
+
auth: { ...runtimeOptions.auth, clientId: clientId2 },
|
|
3295
3303
|
installation,
|
|
3296
3304
|
state: {
|
|
3297
3305
|
directory: input.worktree,
|
|
3298
3306
|
outsideWorktree: "return-undefined"
|
|
3299
3307
|
}
|
|
3300
3308
|
});
|
|
3301
|
-
if (
|
|
3302
|
-
options.bindRuntime?.(
|
|
3309
|
+
if (runtime2 !== void 0) {
|
|
3310
|
+
options.bindRuntime?.(runtime2);
|
|
3303
3311
|
if (options.registerCommands !== void 0) {
|
|
3304
|
-
options.registerCommands(
|
|
3312
|
+
options.registerCommands(runtime2.commandHandlers());
|
|
3305
3313
|
}
|
|
3306
3314
|
}
|
|
3307
3315
|
let credentialProvider;
|
|
@@ -3309,14 +3317,14 @@ var createRoute33Plugin = (options = {}) => {
|
|
|
3309
3317
|
let promptGate;
|
|
3310
3318
|
if (directKey !== void 0) {
|
|
3311
3319
|
credentialProvider = () => Promise.resolve(parseGatewayKey(directKey));
|
|
3312
|
-
} else if (
|
|
3320
|
+
} else if (runtime2 === void 0) {
|
|
3313
3321
|
credentialProvider = () => Promise.reject(
|
|
3314
3322
|
new AttributionPromptError("ATTRIBUTION_PRESENTER_UNAVAILABLE")
|
|
3315
3323
|
);
|
|
3316
3324
|
} else {
|
|
3317
|
-
credentialProvider = () =>
|
|
3325
|
+
credentialProvider = () => runtime2.resolveCredential();
|
|
3318
3326
|
workspaceHeaders = createWorkspaceKeyHeaders({
|
|
3319
|
-
resolveKey: (directory) =>
|
|
3327
|
+
resolveKey: (directory) => runtime2.resolveWorkspaceCredential(directory),
|
|
3320
3328
|
session: {
|
|
3321
3329
|
get: async ({ sessionID }) => {
|
|
3322
3330
|
const session = await options.readSession?.(sessionID);
|
|
@@ -3751,20 +3759,20 @@ var HttpManagementKeys = class {
|
|
|
3751
3759
|
|
|
3752
3760
|
// src/lazy-management-keys/lazy-management-keys.ts
|
|
3753
3761
|
var lazyManagementKeys = (options) => {
|
|
3754
|
-
const client =
|
|
3762
|
+
const client = (accessToken) => new HttpManagementKeys({
|
|
3755
3763
|
baseUrl: options.apiBaseUrl,
|
|
3756
|
-
credential:
|
|
3764
|
+
credential: accessToken,
|
|
3757
3765
|
...options.fetchImpl === void 0 ? {} : { fetchImpl: options.fetchImpl }
|
|
3758
3766
|
});
|
|
3759
3767
|
return {
|
|
3760
|
-
create: async (accessToken, input) =>
|
|
3761
|
-
delete: async (accessToken, id) =>
|
|
3762
|
-
get: async (accessToken, id) =>
|
|
3763
|
-
list: async (accessToken) =>
|
|
3764
|
-
listOrganizations: async (accessToken) =>
|
|
3765
|
-
listProjects: async (accessToken, organisationId) =>
|
|
3766
|
-
regenerate: async (accessToken, id, expiresAt) =>
|
|
3767
|
-
update: async (accessToken, id, input) =>
|
|
3768
|
+
create: async (accessToken, input) => client(accessToken).create(accessToken, input),
|
|
3769
|
+
delete: async (accessToken, id) => client(accessToken).delete(accessToken, id),
|
|
3770
|
+
get: async (accessToken, id) => client(accessToken).get(accessToken, id),
|
|
3771
|
+
list: async (accessToken) => client(accessToken).list(accessToken),
|
|
3772
|
+
listOrganizations: async (accessToken) => client(accessToken).listOrganizations(accessToken),
|
|
3773
|
+
listProjects: async (accessToken, organisationId) => client(accessToken).listProjects(accessToken, organisationId),
|
|
3774
|
+
regenerate: async (accessToken, id, expiresAt) => client(accessToken).regenerate(accessToken, id, expiresAt),
|
|
3775
|
+
update: async (accessToken, id, input) => client(accessToken).update(accessToken, id, input)
|
|
3768
3776
|
};
|
|
3769
3777
|
};
|
|
3770
3778
|
|
|
@@ -3780,14 +3788,217 @@ var buildProductionRuntimeOptions = (input) => ({
|
|
|
3780
3788
|
timeoutMs: LOGIN_BUDGET_MS
|
|
3781
3789
|
},
|
|
3782
3790
|
management: {
|
|
3783
|
-
client: (
|
|
3784
|
-
apiBaseUrl: input.apiBaseUrl,
|
|
3785
|
-
obtainCredential
|
|
3786
|
-
})
|
|
3791
|
+
client: () => lazyManagementKeys({ apiBaseUrl: input.apiBaseUrl })
|
|
3787
3792
|
},
|
|
3788
3793
|
...input.tokenStore === void 0 ? {} : { tokenStore: input.tokenStore }
|
|
3789
3794
|
});
|
|
3790
3795
|
|
|
3796
|
+
// src/v1-entry/host-auth-bridge/host-auth-bridge.ts
|
|
3797
|
+
var createHostAuthBridge = () => {
|
|
3798
|
+
let refresh;
|
|
3799
|
+
return {
|
|
3800
|
+
hydrate: (auth) => {
|
|
3801
|
+
if (typeof auth.refresh === "string" && auth.refresh !== "") {
|
|
3802
|
+
refresh = auth.refresh;
|
|
3803
|
+
}
|
|
3804
|
+
},
|
|
3805
|
+
tokenStore: {
|
|
3806
|
+
get: () => Promise.resolve(refresh),
|
|
3807
|
+
set: (token) => {
|
|
3808
|
+
refresh = token;
|
|
3809
|
+
return Promise.resolve();
|
|
3810
|
+
}
|
|
3811
|
+
}
|
|
3812
|
+
};
|
|
3813
|
+
};
|
|
3814
|
+
|
|
3815
|
+
// src/auth/authorization-url-stash/authorization-url-stash.ts
|
|
3816
|
+
var FALLBACK_URL = "https://route33.ai";
|
|
3817
|
+
var SIGN_IN_INSTRUCTIONS = "Open the Route 33 sign-in link \u2014 your browser will finish at a confirmation page.";
|
|
3818
|
+
var FINISHING_INSTRUCTIONS = "Finishing Route 33 setup\u2026";
|
|
3819
|
+
var URL_WAIT_BUDGET_MS = 3e4;
|
|
3820
|
+
var createAuthorizationUrlStash = (options = {}) => {
|
|
3821
|
+
const open = options.open ?? ((url) => openSystemBrowser(url));
|
|
3822
|
+
const waitBudgetMs = options.waitBudgetMs ?? URL_WAIT_BUDGET_MS;
|
|
3823
|
+
let resolveUrl;
|
|
3824
|
+
const nextUrl = new Promise((resolve2) => {
|
|
3825
|
+
resolveUrl = resolve2;
|
|
3826
|
+
});
|
|
3827
|
+
const browserRunner = (url) => {
|
|
3828
|
+
resolveUrl?.(url);
|
|
3829
|
+
return open(url);
|
|
3830
|
+
};
|
|
3831
|
+
const urlForConnect = async (callback) => {
|
|
3832
|
+
let decide;
|
|
3833
|
+
const decided = new Promise((resolve2) => {
|
|
3834
|
+
decide = resolve2;
|
|
3835
|
+
});
|
|
3836
|
+
const timer = setTimeout(() => {
|
|
3837
|
+
decide({ instructions: SIGN_IN_INSTRUCTIONS, url: FALLBACK_URL });
|
|
3838
|
+
}, waitBudgetMs);
|
|
3839
|
+
void nextUrl.then((url) => {
|
|
3840
|
+
clearTimeout(timer);
|
|
3841
|
+
decide({ instructions: SIGN_IN_INSTRUCTIONS, url: url.toString() });
|
|
3842
|
+
});
|
|
3843
|
+
void Promise.resolve(callback).catch(() => void 0).then(() => {
|
|
3844
|
+
clearTimeout(timer);
|
|
3845
|
+
decide({ instructions: FINISHING_INSTRUCTIONS, url: FALLBACK_URL });
|
|
3846
|
+
});
|
|
3847
|
+
return decided;
|
|
3848
|
+
};
|
|
3849
|
+
return { browserRunner, urlForConnect };
|
|
3850
|
+
};
|
|
3851
|
+
|
|
3852
|
+
// src/v1-entry/route33-auth-hook/route33-auth-hook.ts
|
|
3853
|
+
var createRoute33AuthHook = (deps) => ({
|
|
3854
|
+
loader: async (auth) => {
|
|
3855
|
+
deps.bridge.hydrate(await auth());
|
|
3856
|
+
return buildOptions(deps.providerBaseURL);
|
|
3857
|
+
},
|
|
3858
|
+
methods: [
|
|
3859
|
+
{
|
|
3860
|
+
authorize: async () => {
|
|
3861
|
+
const stash = createAuthorizationUrlStash();
|
|
3862
|
+
const login = deps.startLogin(stash.browserRunner);
|
|
3863
|
+
const decision = await stash.urlForConnect(login.then(() => void 0));
|
|
3864
|
+
return {
|
|
3865
|
+
callback: async () => {
|
|
3866
|
+
try {
|
|
3867
|
+
const { tokenSet } = await login;
|
|
3868
|
+
deps.bridge.hydrate({
|
|
3869
|
+
refresh: tokenSet.refresh_token,
|
|
3870
|
+
type: "oauth"
|
|
3871
|
+
});
|
|
3872
|
+
return {
|
|
3873
|
+
access: tokenSet.access_token,
|
|
3874
|
+
expires: Date.now() + tokenSet.expires_in * 1e3,
|
|
3875
|
+
provider: "route_33",
|
|
3876
|
+
refresh: tokenSet.refresh_token ?? "",
|
|
3877
|
+
type: "success"
|
|
3878
|
+
};
|
|
3879
|
+
} catch {
|
|
3880
|
+
return { type: "failed" };
|
|
3881
|
+
}
|
|
3882
|
+
},
|
|
3883
|
+
instructions: decision.instructions,
|
|
3884
|
+
method: "auto",
|
|
3885
|
+
url: decision.url
|
|
3886
|
+
};
|
|
3887
|
+
},
|
|
3888
|
+
label: "Route 33 (browser sign-in)",
|
|
3889
|
+
type: "oauth"
|
|
3890
|
+
}
|
|
3891
|
+
],
|
|
3892
|
+
provider: "route_33"
|
|
3893
|
+
});
|
|
3894
|
+
|
|
3895
|
+
// src/v1-entry/host-modality-flags/host-modality-flags.ts
|
|
3896
|
+
var hostModalityFlags = (modalities) => ({
|
|
3897
|
+
audio: modalities.includes("audio"),
|
|
3898
|
+
image: modalities.includes("image"),
|
|
3899
|
+
pdf: modalities.includes("pdf"),
|
|
3900
|
+
text: modalities.includes("text"),
|
|
3901
|
+
video: modalities.includes("video")
|
|
3902
|
+
});
|
|
3903
|
+
|
|
3904
|
+
// src/v1-entry/host-model-capabilities/host-model-capabilities.ts
|
|
3905
|
+
var hostModelCapabilities = (meta, forceReasoning) => {
|
|
3906
|
+
const modalities = meta.modalities;
|
|
3907
|
+
const capabilities = meta.capabilities;
|
|
3908
|
+
const attachment = meta.attachment ?? capabilities.attachment ?? false;
|
|
3909
|
+
return {
|
|
3910
|
+
attachment,
|
|
3911
|
+
input: hostModalityFlags(modalities?.input ?? ["text"]),
|
|
3912
|
+
interleaved: false,
|
|
3913
|
+
output: hostModalityFlags(modalities?.output ?? ["text"]),
|
|
3914
|
+
reasoning: capabilities.reasoning === true || forceReasoning,
|
|
3915
|
+
temperature: capabilities.temperature === true,
|
|
3916
|
+
toolcall: capabilities.tool_call === true
|
|
3917
|
+
};
|
|
3918
|
+
};
|
|
3919
|
+
|
|
3920
|
+
// src/v1-entry/host-model-quantities/host-model-quantities.ts
|
|
3921
|
+
var ZERO_COST = {
|
|
3922
|
+
cache: { read: 0, write: 0 },
|
|
3923
|
+
input: 0,
|
|
3924
|
+
output: 0
|
|
3925
|
+
};
|
|
3926
|
+
var DEFAULT_LIMIT = { context: 2e5, output: 64e3 };
|
|
3927
|
+
var hostModelQuantities = (meta) => {
|
|
3928
|
+
const cost = meta.cost;
|
|
3929
|
+
const limit = meta.limit;
|
|
3930
|
+
return {
|
|
3931
|
+
cost: cost === void 0 ? ZERO_COST : {
|
|
3932
|
+
cache: {
|
|
3933
|
+
read: cost.cache_read ?? 0,
|
|
3934
|
+
write: cost.cache_write ?? 0
|
|
3935
|
+
},
|
|
3936
|
+
input: cost.input,
|
|
3937
|
+
output: cost.output
|
|
3938
|
+
},
|
|
3939
|
+
limit: limit === void 0 ? DEFAULT_LIMIT : {
|
|
3940
|
+
context: limit.context,
|
|
3941
|
+
output: limit.output
|
|
3942
|
+
}
|
|
3943
|
+
};
|
|
3944
|
+
};
|
|
3945
|
+
|
|
3946
|
+
// src/v1-entry/host-model-vendor/host-model-vendor.ts
|
|
3947
|
+
var hostModelVendor = (slug) => {
|
|
3948
|
+
const separator = slug.indexOf("/");
|
|
3949
|
+
const vendor = separator === -1 ? slug : slug.slice(0, separator);
|
|
3950
|
+
return {
|
|
3951
|
+
forceReasoning: vendor === "anthropic",
|
|
3952
|
+
npm: NATIVE_SDK_BY_VENDOR[vendor] ?? "@ai-sdk/openai-compatible"
|
|
3953
|
+
};
|
|
3954
|
+
};
|
|
3955
|
+
|
|
3956
|
+
// src/v1-entry/host-models/host-models.ts
|
|
3957
|
+
var toHostModels = (input) => {
|
|
3958
|
+
const models = {};
|
|
3959
|
+
for (const [slug, meta] of Object.entries(input.enriched)) {
|
|
3960
|
+
const vendor = hostModelVendor(slug);
|
|
3961
|
+
const capabilities = hostModelCapabilities(meta, vendor.forceReasoning);
|
|
3962
|
+
const quantities = hostModelQuantities(meta);
|
|
3963
|
+
models[slug] = {
|
|
3964
|
+
api: { id: slug, npm: vendor.npm, url: input.baseURL },
|
|
3965
|
+
attachment: capabilities.attachment,
|
|
3966
|
+
capabilities,
|
|
3967
|
+
cost: quantities.cost,
|
|
3968
|
+
headers: {},
|
|
3969
|
+
id: slug,
|
|
3970
|
+
limit: quantities.limit,
|
|
3971
|
+
name: meta.name,
|
|
3972
|
+
options: {},
|
|
3973
|
+
providerID: "route_33",
|
|
3974
|
+
release_date: "",
|
|
3975
|
+
status: "active"
|
|
3976
|
+
};
|
|
3977
|
+
}
|
|
3978
|
+
return models;
|
|
3979
|
+
};
|
|
3980
|
+
|
|
3981
|
+
// src/v1-entry/route33-provider-models/route33-provider-models.ts
|
|
3982
|
+
var createRoute33ProviderModels = (deps) => async (provider) => {
|
|
3983
|
+
const fallback = provider.models;
|
|
3984
|
+
const key = await deps.resolveKey();
|
|
3985
|
+
if (key === void 0) return fallback;
|
|
3986
|
+
const [catalog, modelsDev] = await Promise.all([
|
|
3987
|
+
(deps.loadCatalog ?? getCatalog)({
|
|
3988
|
+
...deps.catalog,
|
|
3989
|
+
cacheDir: deps.cacheDir,
|
|
3990
|
+
gatewayKey: key
|
|
3991
|
+
}),
|
|
3992
|
+
deps.loadModelsDev()
|
|
3993
|
+
]);
|
|
3994
|
+
const enriched = enrichCatalog(catalog, modelsDev);
|
|
3995
|
+
const models = toHostModels({
|
|
3996
|
+
baseURL: deps.catalog.baseURL,
|
|
3997
|
+
enriched
|
|
3998
|
+
});
|
|
3999
|
+
return Object.keys(models).length === 0 ? fallback : models;
|
|
4000
|
+
};
|
|
4001
|
+
|
|
3791
4002
|
// src/contract/fixtures/fixtures.ts
|
|
3792
4003
|
var contractFixtures = {
|
|
3793
4004
|
apiKey: {
|
|
@@ -3835,13 +4046,64 @@ var parseContractFixtures = (fixtures) => ({
|
|
|
3835
4046
|
});
|
|
3836
4047
|
|
|
3837
4048
|
// src/index.ts
|
|
3838
|
-
var
|
|
4049
|
+
var clientId = readEnvClientId() ?? DEFAULT_ROUTE33_CLIENT_ID;
|
|
4050
|
+
var issuerUrl = readEnvAuthIssuer() ?? DEFAULT_AUTH_ISSUER_URL;
|
|
4051
|
+
var authBridge = createHostAuthBridge();
|
|
4052
|
+
var startNativeLogin = (browserRunner) => loginFlow({
|
|
4053
|
+
browserRunner,
|
|
4054
|
+
clientId,
|
|
4055
|
+
session: createAuthSession({ clientId, issuerUrl }),
|
|
4056
|
+
timeoutMs: LOGIN_BUDGET_MS
|
|
4057
|
+
});
|
|
4058
|
+
var route33Plugin = createRoute33Plugin({
|
|
4059
|
+
bindRuntime: (instance) => {
|
|
4060
|
+
runtime = instance;
|
|
4061
|
+
},
|
|
3839
4062
|
runtime: buildProductionRuntimeOptions({
|
|
3840
4063
|
apiBaseUrl: readEnvApiBase() ?? DEFAULT_MANAGEMENT_API_BASE_URL,
|
|
3841
|
-
clientId
|
|
3842
|
-
issuerUrl
|
|
4064
|
+
clientId,
|
|
4065
|
+
issuerUrl,
|
|
4066
|
+
tokenStore: authBridge.tokenStore
|
|
3843
4067
|
})
|
|
3844
4068
|
});
|
|
4069
|
+
var runtime;
|
|
4070
|
+
var modelsDevOnce;
|
|
4071
|
+
var Route33Plugin = async (input) => {
|
|
4072
|
+
const hooks = await route33Plugin(input);
|
|
4073
|
+
const native = readEnvDirectApiKey();
|
|
4074
|
+
return {
|
|
4075
|
+
...hooks,
|
|
4076
|
+
...native !== void 0 ? {} : {
|
|
4077
|
+
auth: createRoute33AuthHook({
|
|
4078
|
+
bridge: authBridge,
|
|
4079
|
+
providerBaseURL: readEnvBaseUrl() ?? DEFAULT_BASE_URL,
|
|
4080
|
+
startLogin: startNativeLogin
|
|
4081
|
+
}),
|
|
4082
|
+
provider: {
|
|
4083
|
+
id: "route_33",
|
|
4084
|
+
models: createRoute33ProviderModels({
|
|
4085
|
+
cacheDir: readEnvCacheDir(),
|
|
4086
|
+
catalog: {
|
|
4087
|
+
baseURL: readEnvBaseUrl() ?? DEFAULT_BASE_URL,
|
|
4088
|
+
catalogTtlMs: readEnvCatalogTtlMs(),
|
|
4089
|
+
disableIdShim: readEnvDisableIdShim(),
|
|
4090
|
+
discoveryEnabled: readEnvDiscovery()
|
|
4091
|
+
},
|
|
4092
|
+
loadModelsDev: () => {
|
|
4093
|
+
modelsDevOnce ??= loadModelsDev({
|
|
4094
|
+
cacheDir: readEnvCacheDir()
|
|
4095
|
+
});
|
|
4096
|
+
return modelsDevOnce;
|
|
4097
|
+
},
|
|
4098
|
+
resolveKey: () => {
|
|
4099
|
+
const active = runtime;
|
|
4100
|
+
return active === void 0 ? Promise.resolve(void 0) : active.resolveWorkspaceCredential(input.worktree);
|
|
4101
|
+
}
|
|
4102
|
+
})
|
|
4103
|
+
}
|
|
4104
|
+
}
|
|
4105
|
+
};
|
|
4106
|
+
};
|
|
3845
4107
|
export {
|
|
3846
4108
|
AttributionPromptError,
|
|
3847
4109
|
AuthError,
|