@route_33/opencode 0.4.7-rc.7 → 0.4.7-rc.8
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/index.js +1021 -766
- package/dist/tui-entry/index.js +1216 -820
- package/dist/v2-entry/index.js +63 -42
- package/package.json +1 -1
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,148 +261,382 @@ 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 { spawn } from "node:child_process";
|
|
266
|
+
import { timingSafeEqual } from "node:crypto";
|
|
90
267
|
|
|
91
|
-
// src/
|
|
92
|
-
|
|
268
|
+
// src/auth/loopback-callback/loopback-callback.ts
|
|
269
|
+
import { createServer } from "node:http";
|
|
93
270
|
|
|
94
|
-
// src/
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
271
|
+
// src/auth/loopback-callback/callback-page/callback-page.ts
|
|
272
|
+
var MARK_PATHS = [
|
|
273
|
+
"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",
|
|
274
|
+
"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",
|
|
275
|
+
"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"
|
|
276
|
+
];
|
|
277
|
+
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>`;
|
|
278
|
+
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)}
|
|
279
|
+
*{box-sizing:border-box}
|
|
280
|
+
html,body{margin:0}
|
|
281
|
+
.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}
|
|
282
|
+
.r33-panel{display:grid;gap:var(--r33-space-4);max-width:460px;width:100%}
|
|
283
|
+
.r33-mark{height:40px;width:auto;color:var(--r33-text-primary)}
|
|
284
|
+
.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)}
|
|
285
|
+
.r33-status__dot{width:8px;height:8px;background:var(--r33-fill-accent)}
|
|
286
|
+
.r33-status__dot--error{background:transparent;border:1px solid var(--r33-text-primary);border-radius:50%}
|
|
287
|
+
.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}
|
|
288
|
+
.r33-subline{margin:0;font-size:16px;line-height:1.45;color:var(--r33-text-secondary);max-width:40ch;text-wrap:pretty}
|
|
289
|
+
.r33-code{margin:0;font-size:13px;letter-spacing:0.04em;color:var(--r33-text-secondary)}
|
|
290
|
+
.r33-code__value{font-family:var(--r33-font-mono);text-transform:uppercase;color:var(--r33-text-primary)}
|
|
291
|
+
@media (prefers-reduced-motion:reduce){*{animation-duration:1ms!important;transition-duration:1ms!important}}`;
|
|
292
|
+
var buildCallbackPage = (state, errorCode) => {
|
|
293
|
+
const isError = state === "error";
|
|
294
|
+
const safeCode = (errorCode ?? "").replace(/[^a-z0-9_-]/gi, "");
|
|
295
|
+
const title = isError ? "Sign-in failed" : "Signed in";
|
|
296
|
+
const headline = isError ? "Sign-in failed" : "Signed in to route33";
|
|
297
|
+
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.";
|
|
298
|
+
const statusLabel = isError ? "Authentication error" : "Authenticated";
|
|
299
|
+
const dotClass = isError ? "r33-status__dot r33-status__dot--error" : "r33-status__dot";
|
|
300
|
+
const codeLine = isError && safeCode.length > 0 ? `<p class="r33-code">Error code: <span class="r33-code__value">${safeCode}</span></p>` : "";
|
|
301
|
+
return `<!doctype html>
|
|
302
|
+
<html lang="en" data-theme="dark">
|
|
303
|
+
<head>
|
|
304
|
+
<meta charset="utf-8">
|
|
305
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
306
|
+
<title>${title} \xB7 route33.ai</title>
|
|
307
|
+
<style>${PAGE_STYLE}</style>
|
|
308
|
+
</head>
|
|
309
|
+
<body>
|
|
310
|
+
<main class="r33-page">
|
|
311
|
+
<div class="r33-panel">
|
|
312
|
+
${BRAND_MARK}
|
|
313
|
+
<span class="r33-status"><span class="${dotClass}"></span>${statusLabel}</span>
|
|
314
|
+
<h1 class="r33-headline">${headline}</h1>
|
|
315
|
+
<p class="r33-subline">${subline}</p>
|
|
316
|
+
${codeLine}
|
|
317
|
+
</div>
|
|
318
|
+
</main>
|
|
319
|
+
</body>
|
|
320
|
+
</html>`;
|
|
112
321
|
};
|
|
113
322
|
|
|
114
|
-
// src/
|
|
115
|
-
var
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
323
|
+
// src/auth/loopback-callback/loopback-callback.ts
|
|
324
|
+
var createAuthLoopbackCallback = async () => {
|
|
325
|
+
let redirectUri = "http://127.0.0.1/callback";
|
|
326
|
+
let resolveCapture;
|
|
327
|
+
const captured = new Promise((resolve2) => {
|
|
328
|
+
resolveCapture = resolve2;
|
|
329
|
+
});
|
|
330
|
+
const server = createServer((request, response) => {
|
|
331
|
+
const url = new URL(request.url ?? "/", redirectUri);
|
|
332
|
+
if (request.method !== "GET" || url.pathname !== "/callback") {
|
|
333
|
+
response.writeHead(404).end();
|
|
334
|
+
return;
|
|
124
335
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
336
|
+
const resolve2 = resolveCapture;
|
|
337
|
+
resolveCapture = void 0;
|
|
338
|
+
resolve2?.(url);
|
|
339
|
+
const error = url.searchParams.get("error");
|
|
340
|
+
const page = error === null || error.length === 0 ? buildCallbackPage("success") : buildCallbackPage("error", error);
|
|
341
|
+
response.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
342
|
+
response.end(page);
|
|
343
|
+
});
|
|
344
|
+
await new Promise((resolve2, reject) => {
|
|
345
|
+
const onError = (cause) => {
|
|
346
|
+
reject(new AuthError("AUTH_CALLBACK_LISTEN_FAILED", { cause }));
|
|
347
|
+
};
|
|
348
|
+
server.once("error", onError);
|
|
349
|
+
server.listen(0, "127.0.0.1", () => {
|
|
350
|
+
server.off("error", onError);
|
|
351
|
+
resolve2();
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
const address = server.address();
|
|
355
|
+
if (address === null || typeof address === "string") {
|
|
356
|
+
server.close();
|
|
357
|
+
throw new AuthError("AUTH_CALLBACK_LISTEN_FAILED");
|
|
130
358
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
359
|
+
redirectUri = `http://127.0.0.1:${String(address.port)}/callback`;
|
|
360
|
+
return {
|
|
361
|
+
redirectUri,
|
|
362
|
+
close: () => {
|
|
363
|
+
if (!server.listening) {
|
|
364
|
+
return Promise.resolve();
|
|
365
|
+
}
|
|
366
|
+
return new Promise((resolve2, reject) => {
|
|
367
|
+
server.close((cause) => {
|
|
368
|
+
if (cause === void 0) {
|
|
369
|
+
resolve2();
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
reject(new AuthError("AUTH_CALLBACK_CLEANUP_FAILED", { cause }));
|
|
373
|
+
});
|
|
374
|
+
});
|
|
375
|
+
},
|
|
376
|
+
wait: () => captured
|
|
134
377
|
};
|
|
135
|
-
const timer = setTimeout(() => {
|
|
136
|
-
signal.removeEventListener("abort", onAbort);
|
|
137
|
-
resolve2();
|
|
138
|
-
}, milliseconds);
|
|
139
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
// src/config/is-resendable-body/is-resendable-body.ts
|
|
143
|
-
var isResendableBody = (body) => body === void 0 || typeof body === "string" || body instanceof ArrayBuffer || ArrayBuffer.isView(body) || body instanceof Blob || body instanceof URLSearchParams || body instanceof FormData;
|
|
144
|
-
|
|
145
|
-
// src/contract/predicates/has-string-fields/has-string-fields.ts
|
|
146
|
-
var hasStringFields = (record, fields) => fields.every((field) => typeof record[field] === "string");
|
|
147
|
-
|
|
148
|
-
// src/contract/predicates/is-nullable-string/is-nullable-string.ts
|
|
149
|
-
var isNullableString = (value) => typeof value === "string" || value === null;
|
|
150
|
-
|
|
151
|
-
// src/contract/predicates/is-owner-type/is-owner-type.ts
|
|
152
|
-
var isOwnerType = (value) => value === "project" || value === "user_personal" || value === "user_project";
|
|
153
|
-
|
|
154
|
-
// src/contract/predicates/is-record/is-record.ts
|
|
155
|
-
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
156
|
-
|
|
157
|
-
// src/config/parse-exhaustion-envelope/parse-exhaustion-envelope.ts
|
|
158
|
-
var parseExhaustionEnvelope = async (response) => {
|
|
159
|
-
let body;
|
|
160
|
-
try {
|
|
161
|
-
body = await response.clone().json();
|
|
162
|
-
} catch (error) {
|
|
163
|
-
if (error instanceof Error) {
|
|
164
|
-
return void 0;
|
|
165
|
-
}
|
|
166
|
-
throw error;
|
|
167
|
-
}
|
|
168
|
-
if (!isRecord(body)) {
|
|
169
|
-
return void 0;
|
|
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
378
|
};
|
|
186
379
|
|
|
187
|
-
// src/
|
|
188
|
-
var
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
380
|
+
// src/auth/open-browser-and-capture/open-browser-and-capture.ts
|
|
381
|
+
var openBrowserAndCapture = async (options) => {
|
|
382
|
+
const callback = await createAuthLoopbackCallback();
|
|
383
|
+
const authorizationUrl = options.authorizationUrl(callback.redirectUri);
|
|
384
|
+
const browserRunner = options.browserRunner ?? ((url) => new Promise((resolve2, reject) => {
|
|
385
|
+
let command = "xdg-open";
|
|
386
|
+
let args = [url.href];
|
|
387
|
+
if (process.platform === "darwin") {
|
|
388
|
+
command = "open";
|
|
389
|
+
} else if (process.platform === "win32") {
|
|
390
|
+
command = "cmd";
|
|
391
|
+
args = ["/c", "start", "", url.href];
|
|
392
|
+
}
|
|
393
|
+
const child = spawn(command, args, {
|
|
394
|
+
detached: true,
|
|
395
|
+
stdio: "ignore"
|
|
396
|
+
});
|
|
397
|
+
child.once("error", reject);
|
|
398
|
+
child.once("spawn", () => {
|
|
399
|
+
child.unref();
|
|
400
|
+
resolve2();
|
|
401
|
+
});
|
|
402
|
+
}));
|
|
403
|
+
let timer;
|
|
404
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
405
|
+
timer = setTimeout(() => {
|
|
406
|
+
reject(new AuthError("AUTH_CALLBACK_TIMEOUT"));
|
|
407
|
+
}, options.timeoutMs ?? 12e4);
|
|
408
|
+
});
|
|
409
|
+
const launch = browserRunner(authorizationUrl).catch((cause) => {
|
|
410
|
+
throw new AuthError("AUTH_BROWSER_LAUNCH_FAILED", { cause });
|
|
411
|
+
});
|
|
193
412
|
try {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
413
|
+
const completed = await Promise.race([
|
|
414
|
+
Promise.all([launch, callback.wait()]),
|
|
415
|
+
timeout
|
|
416
|
+
]);
|
|
417
|
+
const callbackUrl = completed[1];
|
|
418
|
+
const actualState = callbackUrl.searchParams.get("state");
|
|
419
|
+
if (actualState === null) {
|
|
420
|
+
throw new AuthError("AUTH_CALLBACK_RESPONSE_INVALID");
|
|
198
421
|
}
|
|
199
|
-
|
|
422
|
+
const expectedState = Buffer.from(options.expectedState);
|
|
423
|
+
const receivedState = Buffer.from(actualState);
|
|
424
|
+
if (expectedState.length !== receivedState.length || !timingSafeEqual(expectedState, receivedState)) {
|
|
425
|
+
throw new AuthError("AUTH_CALLBACK_STATE_MISMATCH");
|
|
426
|
+
}
|
|
427
|
+
const code = callbackUrl.searchParams.get("code");
|
|
428
|
+
if (code === null || code.length === 0) {
|
|
429
|
+
throw new AuthError("AUTH_CALLBACK_RESPONSE_INVALID");
|
|
430
|
+
}
|
|
431
|
+
return { code, redirectUri: callback.redirectUri };
|
|
432
|
+
} finally {
|
|
433
|
+
if (timer !== void 0) {
|
|
434
|
+
clearTimeout(timer);
|
|
435
|
+
}
|
|
436
|
+
await callback.close();
|
|
200
437
|
}
|
|
201
|
-
return isRecord(parsed) && typeof parsed["model"] === "string" ? parsed["model"] : void 0;
|
|
202
438
|
};
|
|
203
439
|
|
|
204
|
-
// src/
|
|
205
|
-
|
|
440
|
+
// src/auth/pkce-verifier-and-challenge/pkce-verifier-and-challenge.ts
|
|
441
|
+
import { randomBytes } from "node:crypto";
|
|
442
|
+
|
|
443
|
+
// src/auth/pkce-challenge/pkce-challenge.ts
|
|
444
|
+
import { createHash } from "node:crypto";
|
|
445
|
+
var pkceChallenge = (verifier) => createHash("sha256").update(verifier).digest("base64url");
|
|
446
|
+
|
|
447
|
+
// src/auth/pkce-verifier-and-challenge/pkce-verifier-and-challenge.ts
|
|
448
|
+
var createPkceVerifierAndChallenge = () => {
|
|
449
|
+
const codeVerifier = randomBytes(32).toString("base64url");
|
|
450
|
+
return {
|
|
451
|
+
codeChallenge: pkceChallenge(codeVerifier),
|
|
452
|
+
codeVerifier,
|
|
453
|
+
state: randomBytes(32).toString("base64url")
|
|
454
|
+
};
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
// src/auth/requested-scopes/requested-scopes.ts
|
|
458
|
+
var REQUESTED_OAUTH_SCOPES = "openid offline_access projects:read keys:read keys:write";
|
|
459
|
+
|
|
460
|
+
// src/auth/login-flow/login-flow.ts
|
|
461
|
+
var loginFlow = async (options) => {
|
|
462
|
+
const log = options.logger ?? logger;
|
|
463
|
+
log.info("auth.login.started");
|
|
464
|
+
const pkce = createPkceVerifierAndChallenge();
|
|
465
|
+
const captured = await openBrowserAndCapture({
|
|
466
|
+
authorizationUrl: (redirectUri) => options.session.authorizationUrl({
|
|
467
|
+
clientId: options.clientId,
|
|
468
|
+
codeChallenge: pkce.codeChallenge,
|
|
469
|
+
redirectUri,
|
|
470
|
+
scope: REQUESTED_OAUTH_SCOPES,
|
|
471
|
+
state: pkce.state
|
|
472
|
+
}),
|
|
473
|
+
...options.browserRunner === void 0 ? {} : { browserRunner: options.browserRunner },
|
|
474
|
+
expectedState: pkce.state,
|
|
475
|
+
...options.timeoutMs === void 0 ? {} : { timeoutMs: options.timeoutMs }
|
|
476
|
+
});
|
|
477
|
+
const tokenSet = await options.session.exchangeCode({
|
|
478
|
+
code: captured.code,
|
|
479
|
+
codeVerifier: pkce.codeVerifier,
|
|
480
|
+
redirectUri: captured.redirectUri
|
|
481
|
+
});
|
|
482
|
+
let credential;
|
|
483
|
+
try {
|
|
484
|
+
credential = parseManagementJwt(tokenSet.access_token);
|
|
485
|
+
} catch (cause) {
|
|
486
|
+
throw new AuthError("AUTH_TOKEN_RESPONSE_INVALID", { cause });
|
|
487
|
+
}
|
|
488
|
+
log.info("auth.login.completed");
|
|
489
|
+
return { credential, tokenSet };
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
// src/cli/read-env-base-url/read-env-base-url.ts
|
|
493
|
+
var readEnvBaseUrl = (env = process.env) => {
|
|
494
|
+
const value = env["ROUTE33_BASE_URL"]?.trim();
|
|
495
|
+
return value === "" ? void 0 : value;
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
// src/cli/read-env-api-base/read-env-api-base.ts
|
|
499
|
+
var readEnvApiBase = (env = process.env) => {
|
|
500
|
+
const value = env["ROUTE33_API_BASE"]?.trim();
|
|
501
|
+
return value === "" ? void 0 : value;
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
// src/cli/read-env-auth-issuer/read-env-auth-issuer.ts
|
|
505
|
+
var readEnvAuthIssuer = (env = process.env) => {
|
|
506
|
+
const value = env["ROUTE33_AUTH_ISSUER"]?.trim();
|
|
507
|
+
return value === "" ? void 0 : value;
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
// src/cli/read-env-cache-dir/read-env-cache-dir.ts
|
|
511
|
+
var readEnvCacheDir = (env = process.env) => resolveCacheDir(env);
|
|
512
|
+
|
|
513
|
+
// src/cli/read-env-catalog-ttl-ms/read-env-catalog-ttl-ms.ts
|
|
514
|
+
var readEnvCatalogTtlMs = (env = process.env) => {
|
|
515
|
+
const value = Number(env["ROUTE33_CATALOG_TTL_MS"]);
|
|
516
|
+
return Number.isFinite(value) && value > 0 ? value : DEFAULT_CATALOG_TTL_MS;
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
// src/cli/read-env-client-id/read-env-client-id.ts
|
|
520
|
+
var readEnvClientId = (env = process.env) => {
|
|
521
|
+
const value = env["ROUTE33_CLIENT_ID"]?.trim();
|
|
522
|
+
return value === "" ? void 0 : value;
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
// src/cli/read-env-direct-api-key/read-env-direct-api-key.ts
|
|
526
|
+
var readEnvDirectApiKey = (env = process.env) => {
|
|
527
|
+
const value = env["ROUTE33_API_KEY"]?.trim();
|
|
528
|
+
return value === "" ? void 0 : value;
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
// src/cli/read-env-disable-id-shim/read-env-disable-id-shim.ts
|
|
532
|
+
var readEnvDisableIdShim = (env = process.env) => env["ROUTE33_DISABLE_ID_SHIM"] === "1";
|
|
533
|
+
|
|
534
|
+
// src/cli/read-env-discovery/read-env-discovery.ts
|
|
535
|
+
var readEnvDiscovery = (env = process.env) => env["ROUTE33_DISCOVERY"] !== "0";
|
|
536
|
+
|
|
537
|
+
// src/cli/read-env-strict/read-env-strict.ts
|
|
538
|
+
var readEnvStrict = (env = process.env) => env["ROUTE33_STRICT"] !== "0";
|
|
539
|
+
|
|
540
|
+
// src/cli/resolve-v1-secret-store-directory/resolve-v1-secret-store-directory.ts
|
|
541
|
+
import * as os2 from "node:os";
|
|
542
|
+
import * as path2 from "node:path";
|
|
543
|
+
var resolveV1SecretStoreDirectory = (options = {}) => {
|
|
544
|
+
const explicit = options.directory?.trim();
|
|
545
|
+
if (explicit !== void 0 && explicit.length > 0) {
|
|
546
|
+
return path2.resolve(explicit);
|
|
547
|
+
}
|
|
548
|
+
const hostConfig = (options.env ?? process.env)["OPENCODE_CONFIG_DIR"]?.trim();
|
|
549
|
+
if (hostConfig !== void 0 && hostConfig.length > 0) {
|
|
550
|
+
return path2.join(path2.resolve(hostConfig), "route33");
|
|
551
|
+
}
|
|
552
|
+
return path2.join(
|
|
553
|
+
options.homeDirectory ?? os2.homedir(),
|
|
554
|
+
".config",
|
|
555
|
+
"opencode",
|
|
556
|
+
"route33"
|
|
557
|
+
);
|
|
558
|
+
};
|
|
559
|
+
|
|
560
|
+
// src/config/abortable-sleep/abortable-sleep.ts
|
|
561
|
+
var abortableSleep = (milliseconds, signal) => new Promise((resolve2, reject) => {
|
|
562
|
+
if (signal === void 0) {
|
|
563
|
+
setTimeout(resolve2, milliseconds);
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
const abortError = () => {
|
|
567
|
+
const reason = signal.reason;
|
|
568
|
+
if (reason instanceof Error) {
|
|
569
|
+
return reason;
|
|
570
|
+
}
|
|
571
|
+
return new Error(String(reason), { cause: reason });
|
|
572
|
+
};
|
|
573
|
+
if (signal.aborted) {
|
|
574
|
+
reject(abortError());
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
const onAbort = () => {
|
|
578
|
+
clearTimeout(timer);
|
|
579
|
+
reject(abortError());
|
|
580
|
+
};
|
|
581
|
+
const timer = setTimeout(() => {
|
|
582
|
+
signal.removeEventListener("abort", onAbort);
|
|
583
|
+
resolve2();
|
|
584
|
+
}, milliseconds);
|
|
585
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
// src/config/is-resendable-body/is-resendable-body.ts
|
|
589
|
+
var isResendableBody = (body) => body === void 0 || typeof body === "string" || body instanceof ArrayBuffer || ArrayBuffer.isView(body) || body instanceof Blob || body instanceof URLSearchParams || body instanceof FormData;
|
|
590
|
+
|
|
591
|
+
// src/config/parse-exhaustion-envelope/parse-exhaustion-envelope.ts
|
|
592
|
+
var parseExhaustionEnvelope = async (response) => {
|
|
593
|
+
let body;
|
|
594
|
+
try {
|
|
595
|
+
body = await response.clone().json();
|
|
596
|
+
} catch (error) {
|
|
597
|
+
if (error instanceof Error) {
|
|
598
|
+
return void 0;
|
|
599
|
+
}
|
|
600
|
+
throw error;
|
|
601
|
+
}
|
|
602
|
+
if (!isRecord(body)) {
|
|
603
|
+
return void 0;
|
|
604
|
+
}
|
|
605
|
+
const nestedError = body["error"];
|
|
606
|
+
const nestedCode = isRecord(nestedError) ? nestedError["code"] : void 0;
|
|
607
|
+
if (body["code"] !== "PROVIDER_LANES_EXHAUSTED" && nestedCode !== "PROVIDER_LANES_EXHAUSTED") {
|
|
608
|
+
return void 0;
|
|
609
|
+
}
|
|
610
|
+
const nestedId = isRecord(nestedError) ? nestedError["correlationId"] : void 0;
|
|
611
|
+
const directId = body["correlationId"];
|
|
612
|
+
let correlationId;
|
|
613
|
+
if (typeof directId === "string") {
|
|
614
|
+
correlationId = directId;
|
|
615
|
+
} else if (typeof nestedId === "string") {
|
|
616
|
+
correlationId = nestedId;
|
|
617
|
+
}
|
|
618
|
+
return { correlationId };
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
// src/config/request-model/request-model.ts
|
|
622
|
+
var requestModel = (body) => {
|
|
623
|
+
if (typeof body !== "string") {
|
|
624
|
+
return void 0;
|
|
625
|
+
}
|
|
626
|
+
let parsed;
|
|
627
|
+
try {
|
|
628
|
+
parsed = JSON.parse(body);
|
|
629
|
+
} catch (error) {
|
|
630
|
+
if (error instanceof SyntaxError) {
|
|
631
|
+
return void 0;
|
|
632
|
+
}
|
|
633
|
+
throw error;
|
|
634
|
+
}
|
|
635
|
+
return isRecord(parsed) && typeof parsed["model"] === "string" ? parsed["model"] : void 0;
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
// src/config/retry-wait-seconds/retry-wait-seconds.ts
|
|
639
|
+
var retryWaitSeconds = (options) => {
|
|
206
640
|
const advised = options.retryAfter !== null && /^\d+$/.test(options.retryAfter) ? Number(options.retryAfter) : void 0;
|
|
207
641
|
return advised !== void 0 && advised > 0 ? Math.min(advised, options.maxWaitSeconds) : Math.min(
|
|
208
642
|
options.baseSeconds * 2 ** (options.attempt - 1),
|
|
@@ -443,8 +877,8 @@ var catalogMemory = /* @__PURE__ */ new Map();
|
|
|
443
877
|
var CATALOG_CACHE_VERSION = 2;
|
|
444
878
|
|
|
445
879
|
// src/catalog/catalog-cache/hash-key/hash-key.ts
|
|
446
|
-
import { createHash } from "node:crypto";
|
|
447
|
-
var hashKey = (baseURL, gatewayKey) =>
|
|
880
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
881
|
+
var hashKey = (baseURL, gatewayKey) => createHash2("sha256").update(`${baseURL}
|
|
448
882
|
${gatewayKey.value}`).digest("hex").slice(0, 12);
|
|
449
883
|
|
|
450
884
|
// src/catalog/catalog-cache/is-catalog-model-record/is-catalog-model-record.ts
|
|
@@ -548,103 +982,43 @@ var catalogModelsUrl = (baseURL) => {
|
|
|
548
982
|
// src/catalog/default-fetch-timeout-ms/default-fetch-timeout-ms.ts
|
|
549
983
|
var DEFAULT_FETCH_TIMEOUT_MS = 5e3;
|
|
550
984
|
|
|
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;
|
|
985
|
+
// src/contract/gateway-catalog/parse-catalog-result/parse-catalog-result.ts
|
|
986
|
+
var parseCatalogResult = (value) => {
|
|
987
|
+
if (!isRecord(value) || !Array.isArray(value["data"])) {
|
|
988
|
+
throw new ContractParseError("invalid catalog response");
|
|
560
989
|
}
|
|
990
|
+
return value;
|
|
561
991
|
};
|
|
562
992
|
|
|
563
|
-
// src/
|
|
564
|
-
var
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
this.value = value;
|
|
993
|
+
// src/catalog/parse-catalog-entries/parse-catalog-entries.ts
|
|
994
|
+
var parseCatalogEntries = (payload) => {
|
|
995
|
+
if (Array.isArray(payload)) {
|
|
996
|
+
return payload;
|
|
568
997
|
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
998
|
+
try {
|
|
999
|
+
return parseCatalogResult(payload).data;
|
|
1000
|
+
} catch (error) {
|
|
1001
|
+
if (error instanceof ContractParseError) {
|
|
1002
|
+
throw new CatalogFetchError("parse", error.message);
|
|
1003
|
+
}
|
|
1004
|
+
throw error;
|
|
575
1005
|
}
|
|
576
|
-
return new GatewayApiKey(value);
|
|
577
1006
|
};
|
|
578
1007
|
|
|
579
|
-
// src/
|
|
580
|
-
var
|
|
581
|
-
if (
|
|
582
|
-
|
|
1008
|
+
// src/catalog/parse-model-capabilities/parse-model-capabilities.ts
|
|
1009
|
+
var parseModelCapabilities = (raw) => {
|
|
1010
|
+
if (!isRecord(raw)) {
|
|
1011
|
+
return void 0;
|
|
583
1012
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
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;
|
|
631
|
-
}
|
|
632
|
-
};
|
|
633
|
-
|
|
634
|
-
// src/catalog/parse-model-capabilities/parse-model-capabilities.ts
|
|
635
|
-
var parseModelCapabilities = (raw) => {
|
|
636
|
-
if (!isRecord(raw)) {
|
|
637
|
-
return void 0;
|
|
638
|
-
}
|
|
639
|
-
const reasoning = raw["reasoning"] === true;
|
|
640
|
-
const toolCall = raw["tool_support"] === true;
|
|
641
|
-
if (!reasoning && !toolCall) {
|
|
642
|
-
return void 0;
|
|
643
|
-
}
|
|
644
|
-
return {
|
|
645
|
-
...reasoning ? { reasoning: true } : {},
|
|
646
|
-
...toolCall ? { tool_call: true } : {}
|
|
647
|
-
};
|
|
1013
|
+
const reasoning = raw["reasoning"] === true;
|
|
1014
|
+
const toolCall = raw["tool_support"] === true;
|
|
1015
|
+
if (!reasoning && !toolCall) {
|
|
1016
|
+
return void 0;
|
|
1017
|
+
}
|
|
1018
|
+
return {
|
|
1019
|
+
...reasoning ? { reasoning: true } : {},
|
|
1020
|
+
...toolCall ? { tool_call: true } : {}
|
|
1021
|
+
};
|
|
648
1022
|
};
|
|
649
1023
|
|
|
650
1024
|
// src/catalog/parse-model-limit/parse-model-limit.ts
|
|
@@ -2417,544 +2791,170 @@ var LeaseState = class _LeaseState {
|
|
|
2417
2791
|
/** Atomically swaps metadata when the expected snapshot is still current.
|
|
2418
2792
|
* @param expected - Snapshot observed before asynchronous renewal.
|
|
2419
2793
|
* @param next - Fresh metadata to commit.
|
|
2420
|
-
* @returns Whether the swap committed.
|
|
2421
|
-
*/
|
|
2422
|
-
compareAndSwap(expected, next) {
|
|
2423
|
-
if (this.#current !== expected) {
|
|
2424
|
-
return false;
|
|
2425
|
-
}
|
|
2426
|
-
this.#current = next;
|
|
2427
|
-
return true;
|
|
2428
|
-
}
|
|
2429
|
-
/** Clears metadata only while the expected snapshot remains current.
|
|
2430
|
-
* @param expected - Snapshot whose lease is being invalidated.
|
|
2431
|
-
* @returns Whether the state was cleared.
|
|
2432
|
-
*/
|
|
2433
|
-
clear(expected) {
|
|
2434
|
-
if (this.#current !== expected) {
|
|
2435
|
-
return false;
|
|
2436
|
-
}
|
|
2437
|
-
this.#current = void 0;
|
|
2438
|
-
return true;
|
|
2439
|
-
}
|
|
2440
|
-
static #unexpectedAttribution(value) {
|
|
2441
|
-
throw new TypeError(`Unexpected attribution: ${String(value)}`);
|
|
2442
|
-
}
|
|
2443
|
-
};
|
|
2444
|
-
|
|
2445
|
-
// src/key-lease/lease-installation-factory/lease-installation-factory.ts
|
|
2446
|
-
var createLeaseInstallationFactory = (options) => {
|
|
2447
|
-
const installations = /* @__PURE__ */ new Map();
|
|
2448
|
-
return (scopeId) => {
|
|
2449
|
-
const existing = installations.get(scopeId);
|
|
2450
|
-
if (existing !== void 0) {
|
|
2451
|
-
return existing;
|
|
2452
|
-
}
|
|
2453
|
-
const installation = {
|
|
2454
|
-
keyName: `route33:lease:${scopeId}`,
|
|
2455
|
-
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
|
|
2845
|
-
};
|
|
2846
|
-
};
|
|
2847
|
-
|
|
2848
|
-
// src/auth/open-browser-and-capture/open-browser-and-capture.ts
|
|
2849
|
-
var openBrowserAndCapture = async (options) => {
|
|
2850
|
-
const callback = await createAuthLoopbackCallback();
|
|
2851
|
-
const authorizationUrl = options.authorizationUrl(callback.redirectUri);
|
|
2852
|
-
const browserRunner = options.browserRunner ?? ((url) => new Promise((resolve2, reject) => {
|
|
2853
|
-
let command = "xdg-open";
|
|
2854
|
-
let args = [url.href];
|
|
2855
|
-
if (process.platform === "darwin") {
|
|
2856
|
-
command = "open";
|
|
2857
|
-
} else if (process.platform === "win32") {
|
|
2858
|
-
command = "cmd";
|
|
2859
|
-
args = ["/c", "start", "", url.href];
|
|
2860
|
-
}
|
|
2861
|
-
const child = spawn(command, args, {
|
|
2862
|
-
detached: true,
|
|
2863
|
-
stdio: "ignore"
|
|
2864
|
-
});
|
|
2865
|
-
child.once("error", reject);
|
|
2866
|
-
child.once("spawn", () => {
|
|
2867
|
-
child.unref();
|
|
2868
|
-
resolve2();
|
|
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");
|
|
2794
|
+
* @returns Whether the swap committed.
|
|
2795
|
+
*/
|
|
2796
|
+
compareAndSwap(expected, next) {
|
|
2797
|
+
if (this.#current !== expected) {
|
|
2798
|
+
return false;
|
|
2898
2799
|
}
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2800
|
+
this.#current = next;
|
|
2801
|
+
return true;
|
|
2802
|
+
}
|
|
2803
|
+
/** Clears metadata only while the expected snapshot remains current.
|
|
2804
|
+
* @param expected - Snapshot whose lease is being invalidated.
|
|
2805
|
+
* @returns Whether the state was cleared.
|
|
2806
|
+
*/
|
|
2807
|
+
clear(expected) {
|
|
2808
|
+
if (this.#current !== expected) {
|
|
2809
|
+
return false;
|
|
2903
2810
|
}
|
|
2904
|
-
|
|
2811
|
+
this.#current = void 0;
|
|
2812
|
+
return true;
|
|
2813
|
+
}
|
|
2814
|
+
static #unexpectedAttribution(value) {
|
|
2815
|
+
throw new TypeError(`Unexpected attribution: ${String(value)}`);
|
|
2905
2816
|
}
|
|
2906
2817
|
};
|
|
2907
2818
|
|
|
2908
|
-
// src/
|
|
2909
|
-
|
|
2819
|
+
// src/key-lease/lease-installation-factory/lease-installation-factory.ts
|
|
2820
|
+
var createLeaseInstallationFactory = (options) => {
|
|
2821
|
+
const installations = /* @__PURE__ */ new Map();
|
|
2822
|
+
return (scopeId) => {
|
|
2823
|
+
const existing = installations.get(scopeId);
|
|
2824
|
+
if (existing !== void 0) {
|
|
2825
|
+
return existing;
|
|
2826
|
+
}
|
|
2827
|
+
const installation = {
|
|
2828
|
+
keyName: `route33:lease:${scopeId}`,
|
|
2829
|
+
secretStore: options.createSecretStore(scopeId),
|
|
2830
|
+
...options.createStateStore === void 0 ? {} : { stateStore: options.createStateStore(scopeId) },
|
|
2831
|
+
state: LeaseState.restore(void 0)
|
|
2832
|
+
};
|
|
2833
|
+
installations.set(scopeId, installation);
|
|
2834
|
+
return installation;
|
|
2835
|
+
};
|
|
2836
|
+
};
|
|
2910
2837
|
|
|
2911
|
-
// src/
|
|
2838
|
+
// src/key-lease/scope-id/scope-id.ts
|
|
2912
2839
|
import { createHash as createHash3 } from "node:crypto";
|
|
2913
|
-
var
|
|
2840
|
+
var deriveScopeId = (input) => {
|
|
2841
|
+
let identity;
|
|
2842
|
+
switch (input.attribution.kind) {
|
|
2843
|
+
case "personal":
|
|
2844
|
+
identity = ["route33-lease", "personal"];
|
|
2845
|
+
break;
|
|
2846
|
+
case "project":
|
|
2847
|
+
if (input.folderRealpath === void 0 || input.folderRealpath.length === 0) {
|
|
2848
|
+
throw new TypeError("Project lease scope requires a real folder path");
|
|
2849
|
+
}
|
|
2850
|
+
identity = [
|
|
2851
|
+
"route33-lease",
|
|
2852
|
+
"project",
|
|
2853
|
+
input.attribution.projectId,
|
|
2854
|
+
input.folderRealpath
|
|
2855
|
+
];
|
|
2856
|
+
break;
|
|
2857
|
+
default: {
|
|
2858
|
+
const unexpected = input.attribution;
|
|
2859
|
+
throw new TypeError(`Unexpected attribution: ${String(unexpected)}`);
|
|
2860
|
+
}
|
|
2861
|
+
}
|
|
2862
|
+
return createHash3("sha256").update(JSON.stringify(identity)).digest("hex");
|
|
2863
|
+
};
|
|
2914
2864
|
|
|
2915
|
-
// src/
|
|
2916
|
-
var
|
|
2917
|
-
|
|
2865
|
+
// src/requested-client-id/requested-client-id.ts
|
|
2866
|
+
var DEFAULT_ROUTE33_CLIENT_ID = false ? "" : "oa_platform_203623de";
|
|
2867
|
+
|
|
2868
|
+
// src/commands/change-attribution/change-attribution.ts
|
|
2869
|
+
var changeAttribution = async (options) => {
|
|
2870
|
+
await options.clear();
|
|
2871
|
+
return options.prompt();
|
|
2872
|
+
};
|
|
2873
|
+
|
|
2874
|
+
// src/commands/inspect-session/inspect-session.ts
|
|
2875
|
+
var inspectSession = (options) => {
|
|
2876
|
+
const attribution = options.readAttribution();
|
|
2877
|
+
const lease = options.readLease();
|
|
2918
2878
|
return {
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
state: randomBytes(32).toString("base64url")
|
|
2879
|
+
attribution,
|
|
2880
|
+
...lease === void 0 ? {} : { lease: { expiresAt: lease.expiresAt, keyId: lease.keyId } }
|
|
2922
2881
|
};
|
|
2923
2882
|
};
|
|
2924
2883
|
|
|
2925
|
-
// src/
|
|
2926
|
-
var
|
|
2884
|
+
// src/commands/reset-attribution/reset-attribution.ts
|
|
2885
|
+
var resetAttribution = async (options) => {
|
|
2886
|
+
await options.clear();
|
|
2887
|
+
};
|
|
2927
2888
|
|
|
2928
|
-
// src/
|
|
2929
|
-
var
|
|
2930
|
-
const
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
}
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2889
|
+
// src/commands/rotate-now/rotate-now.ts
|
|
2890
|
+
var rotateNow = async (options) => {
|
|
2891
|
+
const rotated = await options.lease({ renewalRatio: Number.EPSILON });
|
|
2892
|
+
return rotated.lease;
|
|
2893
|
+
};
|
|
2894
|
+
|
|
2895
|
+
// src/ux/attribution-prompt/attribution-prompt.ts
|
|
2896
|
+
var createAttributionPrompt = (projects) => {
|
|
2897
|
+
const choicesBySelection = {
|
|
2898
|
+
personal: { kind: "personal" }
|
|
2899
|
+
};
|
|
2900
|
+
const projectOptions = projects.map((project) => {
|
|
2901
|
+
const selection = `project:${project.id}`;
|
|
2902
|
+
choicesBySelection[selection] = {
|
|
2903
|
+
kind: "project",
|
|
2904
|
+
projectId: project.id
|
|
2905
|
+
};
|
|
2906
|
+
return {
|
|
2907
|
+
label: `${project.name} (${project.organisation.name})`,
|
|
2908
|
+
selection
|
|
2909
|
+
};
|
|
2949
2910
|
});
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2911
|
+
return {
|
|
2912
|
+
choicesBySelection,
|
|
2913
|
+
options: [
|
|
2914
|
+
...projectOptions,
|
|
2915
|
+
{ label: "Use my personal key", selection: "personal" }
|
|
2916
|
+
],
|
|
2917
|
+
title: "Choose where Route 33 should manage this key"
|
|
2918
|
+
};
|
|
2919
|
+
};
|
|
2920
|
+
|
|
2921
|
+
// src/ux/attribution-prompt-error/attribution-prompt-error.ts
|
|
2922
|
+
var MESSAGES = {
|
|
2923
|
+
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.",
|
|
2924
|
+
ATTRIBUTION_SELECTION_INVALID: "That Route 33 option is no longer available. Connect again and pick another."
|
|
2925
|
+
};
|
|
2926
|
+
var AttributionPromptError = class extends GatewayCredentialError {
|
|
2927
|
+
code;
|
|
2928
|
+
constructor(code) {
|
|
2929
|
+
super(MESSAGES[code]);
|
|
2930
|
+
this.name = "AttributionPromptError";
|
|
2931
|
+
this.code = code;
|
|
2955
2932
|
}
|
|
2956
|
-
|
|
2957
|
-
|
|
2933
|
+
};
|
|
2934
|
+
|
|
2935
|
+
// src/ux/prompt-flow/prompt-flow.ts
|
|
2936
|
+
var resolveAttribution = async (options) => {
|
|
2937
|
+
const remembered = readAttribution(options.state);
|
|
2938
|
+
if (remembered !== void 0) {
|
|
2939
|
+
return remembered;
|
|
2940
|
+
}
|
|
2941
|
+
if (options.presenter === void 0) {
|
|
2942
|
+
throw new AttributionPromptError("ATTRIBUTION_PRESENTER_UNAVAILABLE");
|
|
2943
|
+
}
|
|
2944
|
+
const plan = createAttributionPrompt(await options.loadProjects());
|
|
2945
|
+
const selection = await options.presenter.pick(plan);
|
|
2946
|
+
const attribution = plan.choicesBySelection[selection];
|
|
2947
|
+
if (attribution === void 0) {
|
|
2948
|
+
throw new AttributionPromptError("ATTRIBUTION_SELECTION_INVALID");
|
|
2949
|
+
}
|
|
2950
|
+
writeAttribution(options.state, attribution);
|
|
2951
|
+
return attribution;
|
|
2952
|
+
};
|
|
2953
|
+
|
|
2954
|
+
// src/ux/trusted-party-copy/trusted-party-copy.ts
|
|
2955
|
+
var TRUSTED_PARTY_COPY = {
|
|
2956
|
+
message: "Route 33 wants to manage your keys for this OpenCode installation.",
|
|
2957
|
+
title: "Connect Route 33"
|
|
2958
2958
|
};
|
|
2959
2959
|
|
|
2960
2960
|
// src/route33-runtime/login-session/login-session.ts
|
|
@@ -3268,7 +3268,7 @@ var createWorkspaceKeyHeaders = ({ resolveKey, session }) => async (input, outpu
|
|
|
3268
3268
|
// src/create-route33-plugin/create-route33-plugin.ts
|
|
3269
3269
|
var createRoute33Plugin = (options = {}) => {
|
|
3270
3270
|
const baseURL = readEnvBaseUrl();
|
|
3271
|
-
const
|
|
3271
|
+
const clientId2 = options.clientId ?? readEnvClientId() ?? DEFAULT_ROUTE33_CLIENT_ID;
|
|
3272
3272
|
const directKey = readEnvDirectApiKey();
|
|
3273
3273
|
const resolvedConfig = {
|
|
3274
3274
|
...baseURL === void 0 ? {} : { baseURL },
|
|
@@ -3289,19 +3289,19 @@ var createRoute33Plugin = (options = {}) => {
|
|
|
3289
3289
|
})
|
|
3290
3290
|
});
|
|
3291
3291
|
return (input) => {
|
|
3292
|
-
const
|
|
3292
|
+
const runtime2 = runtimeOptions === void 0 || installation === void 0 ? void 0 : new Route33Runtime({
|
|
3293
3293
|
...runtimeOptions,
|
|
3294
|
-
auth: { ...runtimeOptions.auth, clientId },
|
|
3294
|
+
auth: { ...runtimeOptions.auth, clientId: clientId2 },
|
|
3295
3295
|
installation,
|
|
3296
3296
|
state: {
|
|
3297
3297
|
directory: input.worktree,
|
|
3298
3298
|
outsideWorktree: "return-undefined"
|
|
3299
3299
|
}
|
|
3300
3300
|
});
|
|
3301
|
-
if (
|
|
3302
|
-
options.bindRuntime?.(
|
|
3301
|
+
if (runtime2 !== void 0) {
|
|
3302
|
+
options.bindRuntime?.(runtime2);
|
|
3303
3303
|
if (options.registerCommands !== void 0) {
|
|
3304
|
-
options.registerCommands(
|
|
3304
|
+
options.registerCommands(runtime2.commandHandlers());
|
|
3305
3305
|
}
|
|
3306
3306
|
}
|
|
3307
3307
|
let credentialProvider;
|
|
@@ -3309,14 +3309,14 @@ var createRoute33Plugin = (options = {}) => {
|
|
|
3309
3309
|
let promptGate;
|
|
3310
3310
|
if (directKey !== void 0) {
|
|
3311
3311
|
credentialProvider = () => Promise.resolve(parseGatewayKey(directKey));
|
|
3312
|
-
} else if (
|
|
3312
|
+
} else if (runtime2 === void 0) {
|
|
3313
3313
|
credentialProvider = () => Promise.reject(
|
|
3314
3314
|
new AttributionPromptError("ATTRIBUTION_PRESENTER_UNAVAILABLE")
|
|
3315
3315
|
);
|
|
3316
3316
|
} else {
|
|
3317
|
-
credentialProvider = () =>
|
|
3317
|
+
credentialProvider = () => runtime2.resolveCredential();
|
|
3318
3318
|
workspaceHeaders = createWorkspaceKeyHeaders({
|
|
3319
|
-
resolveKey: (directory) =>
|
|
3319
|
+
resolveKey: (directory) => runtime2.resolveWorkspaceCredential(directory),
|
|
3320
3320
|
session: {
|
|
3321
3321
|
get: async ({ sessionID }) => {
|
|
3322
3322
|
const session = await options.readSession?.(sessionID);
|
|
@@ -3788,6 +3788,210 @@ var buildProductionRuntimeOptions = (input) => ({
|
|
|
3788
3788
|
...input.tokenStore === void 0 ? {} : { tokenStore: input.tokenStore }
|
|
3789
3789
|
});
|
|
3790
3790
|
|
|
3791
|
+
// src/v1-entry/host-auth-bridge/host-auth-bridge.ts
|
|
3792
|
+
var createHostAuthBridge = () => {
|
|
3793
|
+
let refresh;
|
|
3794
|
+
return {
|
|
3795
|
+
hydrate: (auth) => {
|
|
3796
|
+
if (typeof auth.refresh === "string" && auth.refresh !== "") {
|
|
3797
|
+
refresh = auth.refresh;
|
|
3798
|
+
}
|
|
3799
|
+
},
|
|
3800
|
+
tokenStore: {
|
|
3801
|
+
get: () => Promise.resolve(refresh),
|
|
3802
|
+
set: (token) => {
|
|
3803
|
+
refresh = token;
|
|
3804
|
+
return Promise.resolve();
|
|
3805
|
+
}
|
|
3806
|
+
}
|
|
3807
|
+
};
|
|
3808
|
+
};
|
|
3809
|
+
|
|
3810
|
+
// src/auth/authorization-url-stash/authorization-url-stash.ts
|
|
3811
|
+
var FALLBACK_URL = "https://route33.ai";
|
|
3812
|
+
var SIGN_IN_INSTRUCTIONS = "Open the Route 33 sign-in link \u2014 your browser will finish at a confirmation page.";
|
|
3813
|
+
var FINISHING_INSTRUCTIONS = "Finishing Route 33 setup\u2026";
|
|
3814
|
+
var URL_WAIT_BUDGET_MS = 3e4;
|
|
3815
|
+
var createAuthorizationUrlStash = () => {
|
|
3816
|
+
let resolveUrl;
|
|
3817
|
+
const nextUrl = new Promise((resolve2) => {
|
|
3818
|
+
resolveUrl = resolve2;
|
|
3819
|
+
});
|
|
3820
|
+
const browserRunner = (url) => {
|
|
3821
|
+
resolveUrl?.(url);
|
|
3822
|
+
return Promise.resolve();
|
|
3823
|
+
};
|
|
3824
|
+
const urlForConnect = async (callback) => {
|
|
3825
|
+
let decide;
|
|
3826
|
+
const decided = new Promise((resolve2) => {
|
|
3827
|
+
decide = resolve2;
|
|
3828
|
+
});
|
|
3829
|
+
const timer = setTimeout(() => {
|
|
3830
|
+
decide({ instructions: SIGN_IN_INSTRUCTIONS, url: FALLBACK_URL });
|
|
3831
|
+
}, URL_WAIT_BUDGET_MS);
|
|
3832
|
+
void nextUrl.then((url) => {
|
|
3833
|
+
clearTimeout(timer);
|
|
3834
|
+
decide({ instructions: SIGN_IN_INSTRUCTIONS, url: url.toString() });
|
|
3835
|
+
});
|
|
3836
|
+
void Promise.resolve(callback).catch(() => void 0).then(() => {
|
|
3837
|
+
clearTimeout(timer);
|
|
3838
|
+
decide({ instructions: FINISHING_INSTRUCTIONS, url: FALLBACK_URL });
|
|
3839
|
+
});
|
|
3840
|
+
return decided;
|
|
3841
|
+
};
|
|
3842
|
+
return { browserRunner, urlForConnect };
|
|
3843
|
+
};
|
|
3844
|
+
|
|
3845
|
+
// src/v1-entry/route33-auth-hook/route33-auth-hook.ts
|
|
3846
|
+
var createRoute33AuthHook = (deps) => ({
|
|
3847
|
+
loader: async (auth) => {
|
|
3848
|
+
deps.bridge.hydrate(await auth());
|
|
3849
|
+
return buildOptions(deps.providerBaseURL);
|
|
3850
|
+
},
|
|
3851
|
+
methods: [
|
|
3852
|
+
{
|
|
3853
|
+
authorize: async () => {
|
|
3854
|
+
const stash = createAuthorizationUrlStash();
|
|
3855
|
+
const login = deps.startLogin(stash.browserRunner);
|
|
3856
|
+
const decision = await stash.urlForConnect(login.then(() => void 0));
|
|
3857
|
+
return {
|
|
3858
|
+
callback: async () => {
|
|
3859
|
+
try {
|
|
3860
|
+
const { tokenSet } = await login;
|
|
3861
|
+
deps.bridge.hydrate({
|
|
3862
|
+
refresh: tokenSet.refresh_token,
|
|
3863
|
+
type: "oauth"
|
|
3864
|
+
});
|
|
3865
|
+
return {
|
|
3866
|
+
access: tokenSet.access_token,
|
|
3867
|
+
expires: Date.now() + tokenSet.expires_in * 1e3,
|
|
3868
|
+
provider: "route_33",
|
|
3869
|
+
refresh: tokenSet.refresh_token ?? "",
|
|
3870
|
+
type: "success"
|
|
3871
|
+
};
|
|
3872
|
+
} catch {
|
|
3873
|
+
return { type: "failed" };
|
|
3874
|
+
}
|
|
3875
|
+
},
|
|
3876
|
+
instructions: decision.instructions,
|
|
3877
|
+
method: "auto",
|
|
3878
|
+
url: decision.url
|
|
3879
|
+
};
|
|
3880
|
+
},
|
|
3881
|
+
label: "Route 33 (browser sign-in)",
|
|
3882
|
+
type: "oauth"
|
|
3883
|
+
}
|
|
3884
|
+
],
|
|
3885
|
+
provider: "route_33"
|
|
3886
|
+
});
|
|
3887
|
+
|
|
3888
|
+
// src/v1-entry/host-modality-flags/host-modality-flags.ts
|
|
3889
|
+
var hostModalityFlags = (modalities) => ({
|
|
3890
|
+
audio: modalities.includes("audio"),
|
|
3891
|
+
image: modalities.includes("image"),
|
|
3892
|
+
pdf: modalities.includes("pdf"),
|
|
3893
|
+
text: modalities.includes("text"),
|
|
3894
|
+
video: modalities.includes("video")
|
|
3895
|
+
});
|
|
3896
|
+
|
|
3897
|
+
// src/v1-entry/host-model-capabilities/host-model-capabilities.ts
|
|
3898
|
+
var hostModelCapabilities = (meta, forceReasoning) => {
|
|
3899
|
+
const modalities = meta.modalities;
|
|
3900
|
+
const capabilities = meta.capabilities;
|
|
3901
|
+
const attachment = meta.attachment ?? capabilities.attachment ?? false;
|
|
3902
|
+
return {
|
|
3903
|
+
attachment,
|
|
3904
|
+
input: hostModalityFlags(modalities?.input ?? ["text"]),
|
|
3905
|
+
interleaved: false,
|
|
3906
|
+
output: hostModalityFlags(modalities?.output ?? ["text"]),
|
|
3907
|
+
reasoning: capabilities.reasoning === true || forceReasoning,
|
|
3908
|
+
temperature: capabilities.temperature === true,
|
|
3909
|
+
toolcall: capabilities.tool_call === true
|
|
3910
|
+
};
|
|
3911
|
+
};
|
|
3912
|
+
|
|
3913
|
+
// src/v1-entry/host-model-quantities/host-model-quantities.ts
|
|
3914
|
+
var ZERO_COST = {
|
|
3915
|
+
cache: { read: 0, write: 0 },
|
|
3916
|
+
input: 0,
|
|
3917
|
+
output: 0
|
|
3918
|
+
};
|
|
3919
|
+
var DEFAULT_LIMIT = { context: 2e5, output: 64e3 };
|
|
3920
|
+
var hostModelQuantities = (meta) => {
|
|
3921
|
+
const cost = meta.cost;
|
|
3922
|
+
const limit = meta.limit;
|
|
3923
|
+
return {
|
|
3924
|
+
cost: cost === void 0 ? ZERO_COST : {
|
|
3925
|
+
cache: {
|
|
3926
|
+
read: cost.cache_read ?? 0,
|
|
3927
|
+
write: cost.cache_write ?? 0
|
|
3928
|
+
},
|
|
3929
|
+
input: cost.input,
|
|
3930
|
+
output: cost.output
|
|
3931
|
+
},
|
|
3932
|
+
limit: limit === void 0 ? DEFAULT_LIMIT : {
|
|
3933
|
+
context: limit.context,
|
|
3934
|
+
output: limit.output
|
|
3935
|
+
}
|
|
3936
|
+
};
|
|
3937
|
+
};
|
|
3938
|
+
|
|
3939
|
+
// src/v1-entry/host-model-vendor/host-model-vendor.ts
|
|
3940
|
+
var hostModelVendor = (slug) => {
|
|
3941
|
+
const separator = slug.indexOf("/");
|
|
3942
|
+
const vendor = separator === -1 ? slug : slug.slice(0, separator);
|
|
3943
|
+
return {
|
|
3944
|
+
forceReasoning: vendor === "anthropic",
|
|
3945
|
+
npm: NATIVE_SDK_BY_VENDOR[vendor] ?? "@ai-sdk/openai-compatible"
|
|
3946
|
+
};
|
|
3947
|
+
};
|
|
3948
|
+
|
|
3949
|
+
// src/v1-entry/host-models/host-models.ts
|
|
3950
|
+
var toHostModels = (input) => {
|
|
3951
|
+
const models = {};
|
|
3952
|
+
for (const [slug, meta] of Object.entries(input.enriched)) {
|
|
3953
|
+
const vendor = hostModelVendor(slug);
|
|
3954
|
+
const capabilities = hostModelCapabilities(meta, vendor.forceReasoning);
|
|
3955
|
+
const quantities = hostModelQuantities(meta);
|
|
3956
|
+
models[slug] = {
|
|
3957
|
+
api: { id: slug, npm: vendor.npm, url: input.baseURL },
|
|
3958
|
+
attachment: capabilities.attachment,
|
|
3959
|
+
capabilities,
|
|
3960
|
+
cost: quantities.cost,
|
|
3961
|
+
headers: {},
|
|
3962
|
+
id: slug,
|
|
3963
|
+
limit: quantities.limit,
|
|
3964
|
+
name: meta.name,
|
|
3965
|
+
options: {},
|
|
3966
|
+
providerID: "route_33",
|
|
3967
|
+
release_date: "",
|
|
3968
|
+
status: "active"
|
|
3969
|
+
};
|
|
3970
|
+
}
|
|
3971
|
+
return models;
|
|
3972
|
+
};
|
|
3973
|
+
|
|
3974
|
+
// src/v1-entry/route33-provider-models/route33-provider-models.ts
|
|
3975
|
+
var createRoute33ProviderModels = (deps) => async (provider) => {
|
|
3976
|
+
const fallback = provider.models;
|
|
3977
|
+
const key = await deps.resolveKey();
|
|
3978
|
+
if (key === void 0) return fallback;
|
|
3979
|
+
const [catalog, modelsDev] = await Promise.all([
|
|
3980
|
+
(deps.loadCatalog ?? getCatalog)({
|
|
3981
|
+
...deps.catalog,
|
|
3982
|
+
cacheDir: deps.cacheDir,
|
|
3983
|
+
gatewayKey: key
|
|
3984
|
+
}),
|
|
3985
|
+
deps.loadModelsDev()
|
|
3986
|
+
]);
|
|
3987
|
+
const enriched = enrichCatalog(catalog, modelsDev);
|
|
3988
|
+
const models = toHostModels({
|
|
3989
|
+
baseURL: deps.catalog.baseURL,
|
|
3990
|
+
enriched
|
|
3991
|
+
});
|
|
3992
|
+
return Object.keys(models).length === 0 ? fallback : models;
|
|
3993
|
+
};
|
|
3994
|
+
|
|
3791
3995
|
// src/contract/fixtures/fixtures.ts
|
|
3792
3996
|
var contractFixtures = {
|
|
3793
3997
|
apiKey: {
|
|
@@ -3835,13 +4039,64 @@ var parseContractFixtures = (fixtures) => ({
|
|
|
3835
4039
|
});
|
|
3836
4040
|
|
|
3837
4041
|
// src/index.ts
|
|
3838
|
-
var
|
|
4042
|
+
var clientId = readEnvClientId() ?? DEFAULT_ROUTE33_CLIENT_ID;
|
|
4043
|
+
var issuerUrl = readEnvAuthIssuer() ?? DEFAULT_AUTH_ISSUER_URL;
|
|
4044
|
+
var authBridge = createHostAuthBridge();
|
|
4045
|
+
var startNativeLogin = (browserRunner) => loginFlow({
|
|
4046
|
+
browserRunner,
|
|
4047
|
+
clientId,
|
|
4048
|
+
session: createAuthSession({ clientId, issuerUrl }),
|
|
4049
|
+
timeoutMs: LOGIN_BUDGET_MS
|
|
4050
|
+
});
|
|
4051
|
+
var route33Plugin = createRoute33Plugin({
|
|
4052
|
+
bindRuntime: (instance) => {
|
|
4053
|
+
runtime = instance;
|
|
4054
|
+
},
|
|
3839
4055
|
runtime: buildProductionRuntimeOptions({
|
|
3840
4056
|
apiBaseUrl: readEnvApiBase() ?? DEFAULT_MANAGEMENT_API_BASE_URL,
|
|
3841
|
-
clientId
|
|
3842
|
-
issuerUrl
|
|
4057
|
+
clientId,
|
|
4058
|
+
issuerUrl,
|
|
4059
|
+
tokenStore: authBridge.tokenStore
|
|
3843
4060
|
})
|
|
3844
4061
|
});
|
|
4062
|
+
var runtime;
|
|
4063
|
+
var modelsDevOnce;
|
|
4064
|
+
var Route33Plugin = async (input) => {
|
|
4065
|
+
const hooks = await route33Plugin(input);
|
|
4066
|
+
const native = readEnvDirectApiKey();
|
|
4067
|
+
return {
|
|
4068
|
+
...hooks,
|
|
4069
|
+
...native !== void 0 ? {} : {
|
|
4070
|
+
auth: createRoute33AuthHook({
|
|
4071
|
+
bridge: authBridge,
|
|
4072
|
+
providerBaseURL: readEnvBaseUrl() ?? DEFAULT_BASE_URL,
|
|
4073
|
+
startLogin: startNativeLogin
|
|
4074
|
+
}),
|
|
4075
|
+
provider: {
|
|
4076
|
+
id: "route_33",
|
|
4077
|
+
models: createRoute33ProviderModels({
|
|
4078
|
+
cacheDir: readEnvCacheDir(),
|
|
4079
|
+
catalog: {
|
|
4080
|
+
baseURL: readEnvBaseUrl() ?? DEFAULT_BASE_URL,
|
|
4081
|
+
catalogTtlMs: readEnvCatalogTtlMs(),
|
|
4082
|
+
disableIdShim: readEnvDisableIdShim(),
|
|
4083
|
+
discoveryEnabled: readEnvDiscovery()
|
|
4084
|
+
},
|
|
4085
|
+
loadModelsDev: () => {
|
|
4086
|
+
modelsDevOnce ??= loadModelsDev({
|
|
4087
|
+
cacheDir: readEnvCacheDir()
|
|
4088
|
+
});
|
|
4089
|
+
return modelsDevOnce;
|
|
4090
|
+
},
|
|
4091
|
+
resolveKey: () => {
|
|
4092
|
+
const active = runtime;
|
|
4093
|
+
return active === void 0 ? Promise.resolve(void 0) : active.resolveWorkspaceCredential(input.worktree);
|
|
4094
|
+
}
|
|
4095
|
+
})
|
|
4096
|
+
}
|
|
4097
|
+
}
|
|
4098
|
+
};
|
|
4099
|
+
};
|
|
3845
4100
|
export {
|
|
3846
4101
|
AttributionPromptError,
|
|
3847
4102
|
AuthError,
|