@ganju/utils 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/accessToken.d.ts +25 -0
- package/dist/accessToken.d.ts.map +1 -0
- package/dist/accessToken.js +65 -0
- package/dist/constants.d.ts +15 -2
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +94 -12
- package/dist/customCodeToken.d.ts +40 -5
- package/dist/customCodeToken.d.ts.map +1 -1
- package/dist/customCodeToken.js +77 -6
- package/dist/index.d.ts +45 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/schema.d.ts +22 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +46 -0
- package/package.json +1 -1
- package/src/accessToken.ts +79 -0
- package/src/constants.ts +106 -12
- package/src/customCodeToken.ts +85 -5
- package/src/index.ts +17 -1
- package/src/schema.ts +50 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** The SHA-256 of a presented token, in the encoding the column stores. */
|
|
2
|
+
export declare const hashAccessToken: (token: string) => Promise<string>;
|
|
3
|
+
/**
|
|
4
|
+
* Cheap enough to run before the database is touched: a bearer token that does
|
|
5
|
+
* not carry the prefix is an OAuth token, and belongs on the other path.
|
|
6
|
+
*/
|
|
7
|
+
export declare const isAccessToken: (token: string) => boolean;
|
|
8
|
+
/**
|
|
9
|
+
* What the dashboard shows beside a token's name.
|
|
10
|
+
*
|
|
11
|
+
* Enough to tell two rows apart when someone is deciding which to revoke, and
|
|
12
|
+
* deliberately taken from the *front* of the secret rather than the end: a
|
|
13
|
+
* prefix narrows a brute-force search by exactly as much as a suffix would, and
|
|
14
|
+
* a value people are used to seeing truncated at the end reads as complete when
|
|
15
|
+
* it is the end that is shown.
|
|
16
|
+
*/
|
|
17
|
+
export declare const accessTokenHint: (token: string) => string;
|
|
18
|
+
export interface MintedAccessToken {
|
|
19
|
+
/** The only time this value exists. Returned to the caller, never stored. */
|
|
20
|
+
token: string;
|
|
21
|
+
tokenHash: string;
|
|
22
|
+
hint: string;
|
|
23
|
+
}
|
|
24
|
+
export declare const mintAccessToken: () => Promise<MintedAccessToken>;
|
|
25
|
+
//# sourceMappingURL=accessToken.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accessToken.d.ts","sourceRoot":"","sources":["../src/accessToken.ts"],"names":[],"mappings":"AA+BA,2EAA2E;AAC3E,eAAO,MAAM,eAAe,GAAU,OAAO,MAAM,KAAG,OAAO,CAAC,MAAM,CAMnE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,aAAa,GAAI,OAAO,MAAM,KAAG,OACG,CAAC;AAElD;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,GAAI,OAAO,MAAM,KAAG,MAI1B,CAAC;AAEvB,MAAM,WAAW,iBAAiB;IAChC,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,eAAO,MAAM,eAAe,QAAa,OAAO,CAAC,iBAAiB,CASjE,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mintAccessToken = exports.accessTokenHint = exports.isAccessToken = exports.hashAccessToken = void 0;
|
|
4
|
+
const constants_1 = require("./constants");
|
|
5
|
+
/**
|
|
6
|
+
* Minting and recognising a personal access token.
|
|
7
|
+
*
|
|
8
|
+
* The value exists in plaintext exactly once — in the response to the request
|
|
9
|
+
* that created it — and what the database holds is its SHA-256. That is the
|
|
10
|
+
* property the whole credential rests on: a leaked backup, a stray log line, or
|
|
11
|
+
* a support engineer reading the row learns nothing they could present as the
|
|
12
|
+
* token, and there is no path in the product that can print one back, because
|
|
13
|
+
* there is nothing to print.
|
|
14
|
+
*
|
|
15
|
+
* SHA-256 rather than a password hash on purpose. A password is a low-entropy
|
|
16
|
+
* secret a person chose, so the cost of hashing it is what stands between a
|
|
17
|
+
* stolen table and the passwords in it; this is 32 bytes from a CSPRNG, where
|
|
18
|
+
* that cost buys nothing and would be paid on every authenticated request. It
|
|
19
|
+
* is also the hash `oauth_client.client_secret` already uses in this system, so
|
|
20
|
+
* there is one answer here to "how is a machine credential stored".
|
|
21
|
+
*/
|
|
22
|
+
const HINT_SEPARATOR = '…';
|
|
23
|
+
const base64url = (bytes) => {
|
|
24
|
+
let binary = '';
|
|
25
|
+
for (const byte of bytes)
|
|
26
|
+
binary += String.fromCharCode(byte);
|
|
27
|
+
return btoa(binary)
|
|
28
|
+
.replace(/\+/g, '-')
|
|
29
|
+
.replace(/\//g, '_')
|
|
30
|
+
.replace(/=+$/, '');
|
|
31
|
+
};
|
|
32
|
+
/** The SHA-256 of a presented token, in the encoding the column stores. */
|
|
33
|
+
const hashAccessToken = async (token) => {
|
|
34
|
+
const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(token));
|
|
35
|
+
return base64url(new Uint8Array(digest));
|
|
36
|
+
};
|
|
37
|
+
exports.hashAccessToken = hashAccessToken;
|
|
38
|
+
/**
|
|
39
|
+
* Cheap enough to run before the database is touched: a bearer token that does
|
|
40
|
+
* not carry the prefix is an OAuth token, and belongs on the other path.
|
|
41
|
+
*/
|
|
42
|
+
const isAccessToken = (token) => token.startsWith(constants_1.constants.ACCESS_TOKEN_PREFIX);
|
|
43
|
+
exports.isAccessToken = isAccessToken;
|
|
44
|
+
/**
|
|
45
|
+
* What the dashboard shows beside a token's name.
|
|
46
|
+
*
|
|
47
|
+
* Enough to tell two rows apart when someone is deciding which to revoke, and
|
|
48
|
+
* deliberately taken from the *front* of the secret rather than the end: a
|
|
49
|
+
* prefix narrows a brute-force search by exactly as much as a suffix would, and
|
|
50
|
+
* a value people are used to seeing truncated at the end reads as complete when
|
|
51
|
+
* it is the end that is shown.
|
|
52
|
+
*/
|
|
53
|
+
const accessTokenHint = (token) => `${token.slice(0, constants_1.constants.ACCESS_TOKEN_PREFIX.length + constants_1.constants.ACCESS_TOKEN_HINT_CHARS)}${HINT_SEPARATOR}`;
|
|
54
|
+
exports.accessTokenHint = accessTokenHint;
|
|
55
|
+
const mintAccessToken = async () => {
|
|
56
|
+
const bytes = new Uint8Array(constants_1.constants.ACCESS_TOKEN_BYTES);
|
|
57
|
+
crypto.getRandomValues(bytes);
|
|
58
|
+
const token = `${constants_1.constants.ACCESS_TOKEN_PREFIX}${base64url(bytes)}`;
|
|
59
|
+
return {
|
|
60
|
+
token,
|
|
61
|
+
tokenHash: await (0, exports.hashAccessToken)(token),
|
|
62
|
+
hint: (0, exports.accessTokenHint)(token)
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
exports.mintAccessToken = mintAccessToken;
|
package/dist/constants.d.ts
CHANGED
|
@@ -519,6 +519,10 @@ export declare const constants: {
|
|
|
519
519
|
CUSTOM_CODE_MAX_FILE_PATH: number;
|
|
520
520
|
CUSTOM_CODE_VERSION_STATUSES: ("draft" | "published" | "archived")[];
|
|
521
521
|
CUSTOM_CODE_SCRIPT_NAME_PREFIX: string;
|
|
522
|
+
CUSTOM_CODE_SCRIPT_NAME_MAX: number;
|
|
523
|
+
CUSTOM_CODE_UPLOAD_SUFFIX_CHARS: number;
|
|
524
|
+
CUSTOM_CODE_SWEEP_GRACE_MS: number;
|
|
525
|
+
CUSTOM_CODE_SWEEP_MAX_DELETES: number;
|
|
522
526
|
CUSTOM_CODE_PREVIEW_SCRIPT_SUFFIX: string;
|
|
523
527
|
CUSTOM_CODE_PREVIEW_TOKEN_TTL_MS: number;
|
|
524
528
|
CUSTOM_CODE_TEST_TIMEOUT_MS: number;
|
|
@@ -560,8 +564,8 @@ export declare const constants: {
|
|
|
560
564
|
CUSTOM_CODE_BROKER_SERVICE_ENV: string;
|
|
561
565
|
CUSTOM_CODE_COMPATIBILITY_DATE: string;
|
|
562
566
|
CUSTOM_CODE_SCRIPT_CPU_MS: number;
|
|
563
|
-
|
|
564
|
-
|
|
567
|
+
CUSTOM_CODE_REGISTER_TIMEOUT_MS: number;
|
|
568
|
+
CUSTOM_CODE_REGISTER_INTERVAL_MS: number;
|
|
565
569
|
CUSTOM_CODE_MAX_LOGS: number;
|
|
566
570
|
CUSTOM_CODE_MAX_LOG_LENGTH: number;
|
|
567
571
|
CUSTOM_CODE_SEND_FILE_TARGET_GMAIL: "gmail";
|
|
@@ -614,6 +618,15 @@ export declare const constants: {
|
|
|
614
618
|
CLI_OAUTH_REDIRECT_PORTS: number[];
|
|
615
619
|
CLI_OAUTH_SCOPES: string[];
|
|
616
620
|
CLI_TOKEN_REFRESH_SKEW_SECONDS: number;
|
|
621
|
+
ACCESS_TOKEN_PREFIX: string;
|
|
622
|
+
ACCESS_TOKEN_BYTES: number;
|
|
623
|
+
ACCESS_TOKEN_HINT_CHARS: number;
|
|
624
|
+
ACCESS_TOKEN_NAME_MAX: number;
|
|
625
|
+
ACCESS_TOKEN_MAX_PER_PROJECT: number;
|
|
626
|
+
ACCESS_TOKEN_MAX_EXPIRY_DAYS: number;
|
|
627
|
+
ACCESS_TOKEN_LAST_USED_INTERVAL_MS: number;
|
|
628
|
+
ACCESS_TOKEN_UNSCOPED_PATHS: string[];
|
|
629
|
+
ACCESS_TOKEN_SCOPE_MESSAGE: string;
|
|
617
630
|
CUSTOM_CODE_LOGS_DEFAULT_LIMIT: number;
|
|
618
631
|
CUSTOM_CODE_LOGS_MAX_LIMIT: number;
|
|
619
632
|
BOT_GRANT_TYPE: string;
|
package/dist/constants.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AA6JA,QAAA,MAAM,yBAAyB,EAAiB,UAAU,CAAC;AAC3D,QAAA,MAAM,+BAA+B,EAAuB,gBAAgB,CAAC;AAC7E,QAAA,MAAM,8BAA8B,EAAsB,eAAe,CAAC;AAC1E,QAAA,MAAM,wBAAwB,EAAgB,SAAS,CAAC;AAuCxD,QAAA,MAAM,sBAAsB,EAAiB,UAAU,CAAC;AACxD,QAAA,MAAM,4BAA4B,EAAuB,gBAAgB,CAAC;AAC1E,QAAA,MAAM,oBAAoB,EAAe,QAAQ,CAAC;AAClD,QAAA,MAAM,oBAAoB,EAAe,QAAQ,CAAC;AAmYlD,QAAA,MAAM,sBAAsB,EAAkB,WAAW,CAAC;AAC1D,QAAA,MAAM,mBAAmB,EAAe,QAAQ,CAAC;AAGjD,QAAA,MAAM,mBAAmB,EAAe,QAAQ,CAAC;AAkTjD,MAAM,MAAM,mBAAmB,GAC3B;IACE,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,QAAQ,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GACD;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3D;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9D;IACE,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GACD;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"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"}
|
package/dist/constants.js
CHANGED
|
@@ -1041,9 +1041,36 @@ const CUSTOM_CODE_MAIN_MODULE = 'index.js';
|
|
|
1041
1041
|
// object read on every deploy.
|
|
1042
1042
|
const CUSTOM_CODE_MAX_FILES = 25;
|
|
1043
1043
|
const CUSTOM_CODE_MAX_FILE_PATH = 100;
|
|
1044
|
-
// WfP script name: `artifact_<artifactId>`. The id, never the slug —
|
|
1045
|
-
// user-editable and a rename would orphan the deployed script.
|
|
1044
|
+
// WfP script name: `artifact_<artifactId>_<upload>`. The id, never the slug —
|
|
1045
|
+
// slugs are user-editable and a rename would orphan the deployed script.
|
|
1046
|
+
//
|
|
1047
|
+
// The trailing segment is minted per upload rather than derived from anything,
|
|
1048
|
+
// which is the whole point: uploading over a name that already exists is not
|
|
1049
|
+
// read-your-writes, so a deploy that replaces a script can serve the previous
|
|
1050
|
+
// edition for up to half a minute. A name nothing has ever used cannot, and
|
|
1051
|
+
// costs nothing to mint. Everything a publish used to do to survive that race —
|
|
1052
|
+
// waiting on an edition marker, refusing with a 503, putting the previous bundle
|
|
1053
|
+
// back when validation failed — went with the reuse that caused it.
|
|
1046
1054
|
const CUSTOM_CODE_SCRIPT_NAME_PREFIX = 'artifact_';
|
|
1055
|
+
// Worker names cap at 63 characters, and `artifact_<uuid>` already spends 45.
|
|
1056
|
+
// That leaves 17 for a separator and a suffix, so a second uuid does not fit and
|
|
1057
|
+
// neither does a hex-32 digest. Twelve hex characters is 48 bits against a
|
|
1058
|
+
// namespace holding at most a few hundred names for any one artifact — not a
|
|
1059
|
+
// collision worth checking for, and an upload to a name in use would fail loudly
|
|
1060
|
+
// rather than quietly serve the wrong code.
|
|
1061
|
+
const CUSTOM_CODE_SCRIPT_NAME_MAX = 63;
|
|
1062
|
+
const CUSTOM_CODE_UPLOAD_SUFFIX_CHARS = 12;
|
|
1063
|
+
// How long a superseded script stays in the namespace before the hourly sweep
|
|
1064
|
+
// may collect it.
|
|
1065
|
+
//
|
|
1066
|
+
// Deleting at publish time would race the thing it deletes: a tool call that
|
|
1067
|
+
// resolved the old pointer a moment earlier is still in flight, and the pointer
|
|
1068
|
+
// moving does not recall it. An hour is far longer than any call can take, and
|
|
1069
|
+
// the wait costs $0.02 per script per month against an allowance of 1,000.
|
|
1070
|
+
const CUSTOM_CODE_SWEEP_GRACE_MS = 60 * 60 * 1_000;
|
|
1071
|
+
// Deletes per sweep. A backlog drains over several hourly runs rather than
|
|
1072
|
+
// making one run unbounded — the same shape the retention purge uses.
|
|
1073
|
+
const CUSTOM_CODE_SWEEP_MAX_DELETES = 200;
|
|
1047
1074
|
// A second script per artifact, `artifact_<id>_preview`, that the Test panel
|
|
1048
1075
|
// deploys a draft into and calls.
|
|
1049
1076
|
//
|
|
@@ -1228,15 +1255,17 @@ const CUSTOM_CODE_COMPATIBILITY_DATE = '2025-11-17';
|
|
|
1228
1255
|
// tighter than our own workers' 30s — this is the technical cap that bounds
|
|
1229
1256
|
// what one adversarial call can cost us, so an infinite loop in a customer's
|
|
1230
1257
|
// tool is billed as five seconds rather than as whatever it wanted.
|
|
1231
|
-
// How long
|
|
1232
|
-
//
|
|
1258
|
+
// How long a deploy waits for a freshly minted script name to become
|
|
1259
|
+
// dispatchable, and how often it asks.
|
|
1233
1260
|
//
|
|
1234
|
-
//
|
|
1235
|
-
//
|
|
1236
|
-
//
|
|
1237
|
-
//
|
|
1238
|
-
|
|
1239
|
-
|
|
1261
|
+
// Every upload goes to a name that has never been used, which is
|
|
1262
|
+
// read-your-writes: ~2s end to end against the deployed namespace, against the
|
|
1263
|
+
// 20-41s a replacement could take. So this bounds how long a brand-new name
|
|
1264
|
+
// takes to register, never how long an old edition takes to stop answering —
|
|
1265
|
+
// there is no old edition. It is short for that reason, and a script that
|
|
1266
|
+
// answers with the wrong edition now fails outright instead of being waited on.
|
|
1267
|
+
const CUSTOM_CODE_REGISTER_TIMEOUT_MS = 8_000;
|
|
1268
|
+
const CUSTOM_CODE_REGISTER_INTERVAL_MS = 500;
|
|
1240
1269
|
const CUSTOM_CODE_SCRIPT_CPU_MS = 5_000;
|
|
1241
1270
|
// The ctx.log() caps also live in ./sdkConstants — the buffer that enforces them
|
|
1242
1271
|
// runs inside the isolate.
|
|
@@ -1410,6 +1439,46 @@ const ARTIFACT_SCOPE_PREFIX = 'artifact:';
|
|
|
1410
1439
|
// loopback matching, where the port is ignored for a 127.0.0.0/8 redirect, but
|
|
1411
1440
|
// that is one library's behaviour and this is a login that has already opened
|
|
1412
1441
|
// someone's browser by the time it would fail.
|
|
1442
|
+
// A personal access token — the durable credential a machine with no browser
|
|
1443
|
+
// uses, where an OAuth access token's one hour is not enough. Bound to one
|
|
1444
|
+
// project, because that is the unit a deploy pipeline works on: one repository,
|
|
1445
|
+
// one artifact, one credential in its CI settings.
|
|
1446
|
+
//
|
|
1447
|
+
// The prefix is part of the value rather than decoration: it is what lets the
|
|
1448
|
+
// middleware tell one of these from an OAuth token before it decides which
|
|
1449
|
+
// lookup to make, and it is what secret scanners match on when one leaks into a
|
|
1450
|
+
// repository. The rest is 32 random bytes, base64url — the token is the only
|
|
1451
|
+
// place the value ever exists in plaintext, since what is stored is its hash.
|
|
1452
|
+
const ACCESS_TOKEN_PREFIX = 'ganju_pat_';
|
|
1453
|
+
const ACCESS_TOKEN_BYTES = 32;
|
|
1454
|
+
// Enough of the secret to recognise a row by, and not enough to be worth
|
|
1455
|
+
// stealing. Shown in the dashboard and by `ganju token list` beside the name.
|
|
1456
|
+
const ACCESS_TOKEN_HINT_CHARS = 6;
|
|
1457
|
+
const ACCESS_TOKEN_NAME_MAX = 100;
|
|
1458
|
+
// A ceiling per project, so a compromised session cannot quietly mint an
|
|
1459
|
+
// unbounded set of credentials that each survive the session being ended.
|
|
1460
|
+
const ACCESS_TOKEN_MAX_PER_PROJECT = 20;
|
|
1461
|
+
// An expiry is optional, because a scheduled deploy that dies on a date nobody
|
|
1462
|
+
// wrote down is its own kind of outage — but a year is the longest we will
|
|
1463
|
+
// write one for.
|
|
1464
|
+
const ACCESS_TOKEN_MAX_EXPIRY_DAYS = 365;
|
|
1465
|
+
// `last_used_at` is a convenience, not an audit log, so it is written at most
|
|
1466
|
+
// this often per token rather than on every request. The question it answers —
|
|
1467
|
+
// "is anything still using this, or can I revoke it" — does not get a better
|
|
1468
|
+
// answer from minute-level precision, and the write would otherwise land on the
|
|
1469
|
+
// hot path of every CI request.
|
|
1470
|
+
const ACCESS_TOKEN_LAST_USED_INTERVAL_MS = 5 * 60 * 1000;
|
|
1471
|
+
// The only path a personal access token may reach without naming the project it
|
|
1472
|
+
// is scoped to, and only on GET. `/me` reports who the token is, which is how
|
|
1473
|
+
// the CLI confirms a machine is authenticated at all, and tells it nothing it
|
|
1474
|
+
// does not already hold.
|
|
1475
|
+
//
|
|
1476
|
+
// Everything else is refused, organization routes included — billing, members,
|
|
1477
|
+
// the model configs and the other projects are not what a deploy credential is
|
|
1478
|
+
// for. The list is deliberately this short: a route added later is closed by
|
|
1479
|
+
// omission rather than open by it.
|
|
1480
|
+
const ACCESS_TOKEN_UNSCOPED_PATHS = ['/me'];
|
|
1481
|
+
const ACCESS_TOKEN_SCOPE_MESSAGE = 'This token is scoped to a different project';
|
|
1413
1482
|
// Recent custom-tool invocations, as `ganju logs` reads them.
|
|
1414
1483
|
const CUSTOM_CODE_LOGS_DEFAULT_LIMIT = 20;
|
|
1415
1484
|
const CUSTOM_CODE_LOGS_MAX_LIMIT = 100;
|
|
@@ -2139,6 +2208,10 @@ exports.constants = {
|
|
|
2139
2208
|
CUSTOM_CODE_MAX_FILE_PATH,
|
|
2140
2209
|
CUSTOM_CODE_VERSION_STATUSES,
|
|
2141
2210
|
CUSTOM_CODE_SCRIPT_NAME_PREFIX,
|
|
2211
|
+
CUSTOM_CODE_SCRIPT_NAME_MAX,
|
|
2212
|
+
CUSTOM_CODE_UPLOAD_SUFFIX_CHARS,
|
|
2213
|
+
CUSTOM_CODE_SWEEP_GRACE_MS,
|
|
2214
|
+
CUSTOM_CODE_SWEEP_MAX_DELETES,
|
|
2142
2215
|
CUSTOM_CODE_PREVIEW_SCRIPT_SUFFIX,
|
|
2143
2216
|
CUSTOM_CODE_PREVIEW_TOKEN_TTL_MS,
|
|
2144
2217
|
CUSTOM_CODE_TEST_TIMEOUT_MS,
|
|
@@ -2180,8 +2253,8 @@ exports.constants = {
|
|
|
2180
2253
|
CUSTOM_CODE_BROKER_SERVICE_ENV,
|
|
2181
2254
|
CUSTOM_CODE_COMPATIBILITY_DATE,
|
|
2182
2255
|
CUSTOM_CODE_SCRIPT_CPU_MS,
|
|
2183
|
-
|
|
2184
|
-
|
|
2256
|
+
CUSTOM_CODE_REGISTER_TIMEOUT_MS,
|
|
2257
|
+
CUSTOM_CODE_REGISTER_INTERVAL_MS,
|
|
2185
2258
|
CUSTOM_CODE_MAX_LOGS: sdkConstants_1.CUSTOM_CODE_MAX_LOGS,
|
|
2186
2259
|
CUSTOM_CODE_MAX_LOG_LENGTH: sdkConstants_1.CUSTOM_CODE_MAX_LOG_LENGTH,
|
|
2187
2260
|
CUSTOM_CODE_SEND_FILE_TARGET_GMAIL,
|
|
@@ -2234,6 +2307,15 @@ exports.constants = {
|
|
|
2234
2307
|
CLI_OAUTH_REDIRECT_PORTS: cliConstants_1.CLI_OAUTH_REDIRECT_PORTS,
|
|
2235
2308
|
CLI_OAUTH_SCOPES: cliConstants_1.CLI_OAUTH_SCOPES,
|
|
2236
2309
|
CLI_TOKEN_REFRESH_SKEW_SECONDS: cliConstants_1.CLI_TOKEN_REFRESH_SKEW_SECONDS,
|
|
2310
|
+
ACCESS_TOKEN_PREFIX,
|
|
2311
|
+
ACCESS_TOKEN_BYTES,
|
|
2312
|
+
ACCESS_TOKEN_HINT_CHARS,
|
|
2313
|
+
ACCESS_TOKEN_NAME_MAX,
|
|
2314
|
+
ACCESS_TOKEN_MAX_PER_PROJECT,
|
|
2315
|
+
ACCESS_TOKEN_MAX_EXPIRY_DAYS,
|
|
2316
|
+
ACCESS_TOKEN_LAST_USED_INTERVAL_MS,
|
|
2317
|
+
ACCESS_TOKEN_UNSCOPED_PATHS,
|
|
2318
|
+
ACCESS_TOKEN_SCOPE_MESSAGE,
|
|
2237
2319
|
CUSTOM_CODE_LOGS_DEFAULT_LIMIT,
|
|
2238
2320
|
CUSTOM_CODE_LOGS_MAX_LIMIT,
|
|
2239
2321
|
BOT_GRANT_TYPE,
|
|
@@ -24,18 +24,53 @@ export declare const mintCustomCodeToken: (payload: Omit<CustomCodeTokenPayload,
|
|
|
24
24
|
*/
|
|
25
25
|
export declare const verifyCustomCodeToken: (token: string, secret: string, now?: number) => Promise<CustomCodeTokenPayload | null>;
|
|
26
26
|
/**
|
|
27
|
-
* The dispatch-namespace script name for an artifact: `artifact_<id>`.
|
|
27
|
+
* The legacy dispatch-namespace script name for an artifact: `artifact_<id>`.
|
|
28
28
|
*
|
|
29
29
|
* The id, never the slug — slugs are user-editable and a rename would orphan the
|
|
30
30
|
* deployed script while the database still pointed at a live version.
|
|
31
|
+
*
|
|
32
|
+
* Nothing uploads to this name any more; every deploy mints its own. It survives
|
|
33
|
+
* as the fallback for a version published before `script_name` existed, whose
|
|
34
|
+
* bundle really is sitting under this name. Tightening a rule must never stop an
|
|
35
|
+
* already-published version from serving, because that failure is invisible to
|
|
36
|
+
* whoever owns it — the same reason the boot loop still accepts a stored tool key
|
|
37
|
+
* the current catalog no longer offers.
|
|
31
38
|
*/
|
|
32
39
|
export declare const customCodeScriptName: (artifactId: string) => string;
|
|
33
40
|
/**
|
|
34
|
-
* The script name
|
|
41
|
+
* The legacy preview script name: `artifact_<id>_preview`.
|
|
35
42
|
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
43
|
+
* Kept for the same reason as the one above, and for one more: it is the prefix
|
|
44
|
+
* the sweep matches to recognise a preview script left behind by a test run that
|
|
45
|
+
* did not clean up after itself.
|
|
39
46
|
*/
|
|
40
47
|
export declare const customCodePreviewScriptName: (artifactId: string) => string;
|
|
48
|
+
/**
|
|
49
|
+
* A dispatch-namespace name no upload has ever used:
|
|
50
|
+
* `artifact_<id>_<12 hex chars>`.
|
|
51
|
+
*
|
|
52
|
+
* Minted rather than derived, and that is the entire design. Uploading over an
|
|
53
|
+
* existing name is not read-your-writes — a replacement can serve the previous
|
|
54
|
+
* edition for tens of seconds — so a deploy that always writes to a new name is
|
|
55
|
+
* correct by construction rather than by waiting to see whether it worked.
|
|
56
|
+
*
|
|
57
|
+
* The suffix deliberately carries no meaning. The two candidates that did are
|
|
58
|
+
* both wrong: the bundle digest collides whenever a deploy reverts to bytes that
|
|
59
|
+
* shipped before, which is exactly what a rollback is, and the version id is one
|
|
60
|
+
* string across every re-upload of a single draft, which is every test run of it.
|
|
61
|
+
*
|
|
62
|
+
* Twelve hex characters is what fits. Worker names cap at 63 and
|
|
63
|
+
* `artifact_<uuid>` spends 45 of them.
|
|
64
|
+
*/
|
|
65
|
+
export declare const customCodeUploadName: (artifactId: string) => string;
|
|
66
|
+
/**
|
|
67
|
+
* A preview name no test run has ever used:
|
|
68
|
+
* `artifact_<id>_preview_<12 hex chars>`.
|
|
69
|
+
*
|
|
70
|
+
* The sharper version of the same race. Every test used to deploy over one
|
|
71
|
+
* preview name, so a test could report the run before it — which reads as "my
|
|
72
|
+
* edit did nothing" from the one tool whose whole job is to say what an edit
|
|
73
|
+
* does. Nothing stores this: it is minted, used, and deleted inside one request.
|
|
74
|
+
*/
|
|
75
|
+
export declare const customCodePreviewUploadName: (artifactId: string) => string;
|
|
41
76
|
//# sourceMappingURL=customCodeToken.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"customCodeToken.d.ts","sourceRoot":"","sources":["../src/customCodeToken.ts"],"names":[],"mappings":"AAuBA,MAAM,WAAW,sBAAsB;IAGrC,CAAC,EAAE,MAAM,CAAC;IACV,UAAU,EAAE,MAAM,CAAC;IAGnB,SAAS,EAAE,MAAM,CAAC;IAIlB,GAAG,EAAE,MAAM,CAAC;IAOZ,OAAO,CAAC,EAAE,OAAO,CAAC;IAGlB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AA0BD;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,GAC9B,SAAS,IAAI,CAAC,sBAAsB,EAAE,GAAG,GAAG,KAAK,GAAG,KAAK,CAAC,GAAG;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,EACD,QAAQ,MAAM,EACd,WAAU,MAAmB,KAC5B,OAAO,CAAC,MAAM,CAmBhB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,GAChC,OAAO,MAAM,EACb,QAAQ,MAAM,EACd,MAAM,MAAM,KACX,OAAO,CAAC,sBAAsB,GAAG,IAAI,CA+DvC,CAAC;AAEF
|
|
1
|
+
{"version":3,"file":"customCodeToken.d.ts","sourceRoot":"","sources":["../src/customCodeToken.ts"],"names":[],"mappings":"AAuBA,MAAM,WAAW,sBAAsB;IAGrC,CAAC,EAAE,MAAM,CAAC;IACV,UAAU,EAAE,MAAM,CAAC;IAGnB,SAAS,EAAE,MAAM,CAAC;IAIlB,GAAG,EAAE,MAAM,CAAC;IAOZ,OAAO,CAAC,EAAE,OAAO,CAAC;IAGlB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AA0BD;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,GAC9B,SAAS,IAAI,CAAC,sBAAsB,EAAE,GAAG,GAAG,KAAK,GAAG,KAAK,CAAC,GAAG;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,EACD,QAAQ,MAAM,EACd,WAAU,MAAmB,KAC5B,OAAO,CAAC,MAAM,CAmBhB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,GAChC,OAAO,MAAM,EACb,QAAQ,MAAM,EACd,MAAM,MAAM,KACX,OAAO,CAAC,sBAAsB,GAAG,IAAI,CA+DvC,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,oBAAoB,GAAI,YAAY,MAAM,KAAG,MACE,CAAC;AAE7D;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,GAAI,YAAY,MAAM,KAAG,MACoB,CAAC;AAEtF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,oBAAoB,GAAI,YAAY,MAAM,KAAG,MACR,CAAC;AAEnD;;;;;;;;GAQG;AACH,eAAO,MAAM,2BAA2B,GAAI,YAAY,MAAM,KAAG,MACR,CAAC"}
|
package/dist/customCodeToken.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.customCodePreviewScriptName = exports.customCodeScriptName = exports.verifyCustomCodeToken = exports.mintCustomCodeToken = void 0;
|
|
3
|
+
exports.customCodePreviewUploadName = exports.customCodeUploadName = exports.customCodePreviewScriptName = exports.customCodeScriptName = exports.verifyCustomCodeToken = exports.mintCustomCodeToken = void 0;
|
|
4
4
|
const base64_1 = require("./base64");
|
|
5
5
|
const constants_1 = require("./constants");
|
|
6
6
|
const encoder = new TextEncoder();
|
|
@@ -97,19 +97,90 @@ const verifyCustomCodeToken = async (token, secret, now) => {
|
|
|
97
97
|
};
|
|
98
98
|
exports.verifyCustomCodeToken = verifyCustomCodeToken;
|
|
99
99
|
/**
|
|
100
|
-
* The dispatch-namespace script name for an artifact: `artifact_<id>`.
|
|
100
|
+
* The legacy dispatch-namespace script name for an artifact: `artifact_<id>`.
|
|
101
101
|
*
|
|
102
102
|
* The id, never the slug — slugs are user-editable and a rename would orphan the
|
|
103
103
|
* deployed script while the database still pointed at a live version.
|
|
104
|
+
*
|
|
105
|
+
* Nothing uploads to this name any more; every deploy mints its own. It survives
|
|
106
|
+
* as the fallback for a version published before `script_name` existed, whose
|
|
107
|
+
* bundle really is sitting under this name. Tightening a rule must never stop an
|
|
108
|
+
* already-published version from serving, because that failure is invisible to
|
|
109
|
+
* whoever owns it — the same reason the boot loop still accepts a stored tool key
|
|
110
|
+
* the current catalog no longer offers.
|
|
104
111
|
*/
|
|
105
112
|
const customCodeScriptName = (artifactId) => `${constants_1.constants.CUSTOM_CODE_SCRIPT_NAME_PREFIX}${artifactId}`;
|
|
106
113
|
exports.customCodeScriptName = customCodeScriptName;
|
|
107
114
|
/**
|
|
108
|
-
* The script name
|
|
115
|
+
* The legacy preview script name: `artifact_<id>_preview`.
|
|
109
116
|
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
117
|
+
* Kept for the same reason as the one above, and for one more: it is the prefix
|
|
118
|
+
* the sweep matches to recognise a preview script left behind by a test run that
|
|
119
|
+
* did not clean up after itself.
|
|
113
120
|
*/
|
|
114
121
|
const customCodePreviewScriptName = (artifactId) => `${(0, exports.customCodeScriptName)(artifactId)}${constants_1.constants.CUSTOM_CODE_PREVIEW_SCRIPT_SUFFIX}`;
|
|
115
122
|
exports.customCodePreviewScriptName = customCodePreviewScriptName;
|
|
123
|
+
/**
|
|
124
|
+
* A dispatch-namespace name no upload has ever used:
|
|
125
|
+
* `artifact_<id>_<12 hex chars>`.
|
|
126
|
+
*
|
|
127
|
+
* Minted rather than derived, and that is the entire design. Uploading over an
|
|
128
|
+
* existing name is not read-your-writes — a replacement can serve the previous
|
|
129
|
+
* edition for tens of seconds — so a deploy that always writes to a new name is
|
|
130
|
+
* correct by construction rather than by waiting to see whether it worked.
|
|
131
|
+
*
|
|
132
|
+
* The suffix deliberately carries no meaning. The two candidates that did are
|
|
133
|
+
* both wrong: the bundle digest collides whenever a deploy reverts to bytes that
|
|
134
|
+
* shipped before, which is exactly what a rollback is, and the version id is one
|
|
135
|
+
* string across every re-upload of a single draft, which is every test run of it.
|
|
136
|
+
*
|
|
137
|
+
* Twelve hex characters is what fits. Worker names cap at 63 and
|
|
138
|
+
* `artifact_<uuid>` spends 45 of them.
|
|
139
|
+
*/
|
|
140
|
+
const customCodeUploadName = (artifactId) => mintUploadName((0, exports.customCodeScriptName)(artifactId));
|
|
141
|
+
exports.customCodeUploadName = customCodeUploadName;
|
|
142
|
+
/**
|
|
143
|
+
* A preview name no test run has ever used:
|
|
144
|
+
* `artifact_<id>_preview_<12 hex chars>`.
|
|
145
|
+
*
|
|
146
|
+
* The sharper version of the same race. Every test used to deploy over one
|
|
147
|
+
* preview name, so a test could report the run before it — which reads as "my
|
|
148
|
+
* edit did nothing" from the one tool whose whole job is to say what an edit
|
|
149
|
+
* does. Nothing stores this: it is minted, used, and deleted inside one request.
|
|
150
|
+
*/
|
|
151
|
+
const customCodePreviewUploadName = (artifactId) => mintUploadName((0, exports.customCodePreviewScriptName)(artifactId));
|
|
152
|
+
exports.customCodePreviewUploadName = customCodePreviewUploadName;
|
|
153
|
+
/**
|
|
154
|
+
* Append `_<hex>` to a base name, spending whatever the 63-character ceiling
|
|
155
|
+
* leaves and no more.
|
|
156
|
+
*
|
|
157
|
+
* The budget is genuinely tight, and the two names spend it differently:
|
|
158
|
+
*
|
|
159
|
+
* | name | base | separator | suffix | total |
|
|
160
|
+
* |---|---|---|---|---|
|
|
161
|
+
* | live | `artifact_<uuid>` = 45 | 1 | 12 | 58 |
|
|
162
|
+
* | preview | + `_preview` = 53 | 1 | 8 | 62 |
|
|
163
|
+
*
|
|
164
|
+
* Twelve hex characters is 48 bits and eight is 32, against a namespace holding
|
|
165
|
+
* a few hundred names for any one artifact — and a preview name lives for the
|
|
166
|
+
* seconds one test run takes. Neither is a collision worth checking for, and an
|
|
167
|
+
* upload to a name already in use fails loudly rather than quietly serving the
|
|
168
|
+
* wrong code, which is the failure that matters.
|
|
169
|
+
*
|
|
170
|
+
* The ceiling is asserted rather than assumed: it is one number away from being
|
|
171
|
+
* silently exceeded by a longer prefix, and a name Cloudflare refuses would
|
|
172
|
+
* surface as a failed publish with nothing explaining why.
|
|
173
|
+
*/
|
|
174
|
+
const mintUploadName = (base) => {
|
|
175
|
+
const available = constants_1.constants.CUSTOM_CODE_SCRIPT_NAME_MAX - base.length - 1;
|
|
176
|
+
// Even, because each byte renders as two hex characters.
|
|
177
|
+
const chars = Math.min(constants_1.constants.CUSTOM_CODE_UPLOAD_SUFFIX_CHARS, available) & ~1;
|
|
178
|
+
if (chars < 4) {
|
|
179
|
+
throw new Error(`A dispatch script name based on "${base}" leaves no room for a unique suffix.`);
|
|
180
|
+
}
|
|
181
|
+
const bytes = new Uint8Array(chars / 2);
|
|
182
|
+
crypto.getRandomValues(bytes);
|
|
183
|
+
return `${base}_${Array.from(bytes)
|
|
184
|
+
.map(byte => byte.toString(16).padStart(2, '0'))
|
|
185
|
+
.join('')}`;
|
|
186
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ import type { OAuthProviderConfig } from './oauthProviders';
|
|
|
15
15
|
import type { ExposableResource } from './exposedResource';
|
|
16
16
|
import type { CustomCodeProject, ProjectPathIssue } from './customCodeProject';
|
|
17
17
|
import type { AttachmentResource, ResolvedAttachment, ResolveAttachmentResult } from './attachment';
|
|
18
|
+
import type { MintedAccessToken } from './accessToken';
|
|
18
19
|
import type { CatalogGroup, CatalogTool, CatalogToolDescriptor, ToolGroupKey, ToolKey } from './toolCatalog';
|
|
19
20
|
import type { Separator, ChunkMetadata, PreparedChunk } from './chunking';
|
|
20
21
|
import type { RateLimitRetryOptions } from './retry';
|
|
@@ -868,6 +869,28 @@ export declare const utils: {
|
|
|
868
869
|
userId: import("zod").ZodUUID;
|
|
869
870
|
organizationId: import("zod").ZodUUID;
|
|
870
871
|
}, import("zod/v4/core").$strip>;
|
|
872
|
+
ACCESS_TOKEN_CREATE: import("zod").ZodObject<{
|
|
873
|
+
name: import("zod").ZodString;
|
|
874
|
+
expiresInDays: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodNumber>>;
|
|
875
|
+
userId: import("zod").ZodUUID;
|
|
876
|
+
organizationId: import("zod").ZodUUID;
|
|
877
|
+
projectId: import("zod").ZodUUID;
|
|
878
|
+
}, import("zod/v4/core").$strip>;
|
|
879
|
+
ACCESS_TOKEN_CREATE_VIEW: import("zod").ZodObject<{
|
|
880
|
+
name: import("zod").ZodString;
|
|
881
|
+
expiresInDays: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodNumber>>;
|
|
882
|
+
}, import("zod/v4/core").$strip>;
|
|
883
|
+
ACCESS_TOKEN_LIST: import("zod").ZodObject<{
|
|
884
|
+
userId: import("zod").ZodUUID;
|
|
885
|
+
organizationId: import("zod").ZodUUID;
|
|
886
|
+
projectId: import("zod").ZodUUID;
|
|
887
|
+
}, import("zod/v4/core").$strip>;
|
|
888
|
+
ACCESS_TOKEN_REMOVE: import("zod").ZodObject<{
|
|
889
|
+
tokenId: import("zod").ZodUUID;
|
|
890
|
+
userId: import("zod").ZodUUID;
|
|
891
|
+
organizationId: import("zod").ZodUUID;
|
|
892
|
+
projectId: import("zod").ZodUUID;
|
|
893
|
+
}, import("zod/v4/core").$strip>;
|
|
871
894
|
CHANNEL_CONFIG: import("zod").ZodObject<{
|
|
872
895
|
debounceMs: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
873
896
|
}, import("zod/v4/core").$loose>;
|
|
@@ -2206,6 +2229,10 @@ export declare const utils: {
|
|
|
2206
2229
|
CUSTOM_CODE_MAX_FILE_PATH: number;
|
|
2207
2230
|
CUSTOM_CODE_VERSION_STATUSES: ("draft" | "published" | "archived")[];
|
|
2208
2231
|
CUSTOM_CODE_SCRIPT_NAME_PREFIX: string;
|
|
2232
|
+
CUSTOM_CODE_SCRIPT_NAME_MAX: number;
|
|
2233
|
+
CUSTOM_CODE_UPLOAD_SUFFIX_CHARS: number;
|
|
2234
|
+
CUSTOM_CODE_SWEEP_GRACE_MS: number;
|
|
2235
|
+
CUSTOM_CODE_SWEEP_MAX_DELETES: number;
|
|
2209
2236
|
CUSTOM_CODE_PREVIEW_SCRIPT_SUFFIX: string;
|
|
2210
2237
|
CUSTOM_CODE_PREVIEW_TOKEN_TTL_MS: number;
|
|
2211
2238
|
CUSTOM_CODE_TEST_TIMEOUT_MS: number;
|
|
@@ -2247,8 +2274,8 @@ export declare const utils: {
|
|
|
2247
2274
|
CUSTOM_CODE_BROKER_SERVICE_ENV: string;
|
|
2248
2275
|
CUSTOM_CODE_COMPATIBILITY_DATE: string;
|
|
2249
2276
|
CUSTOM_CODE_SCRIPT_CPU_MS: number;
|
|
2250
|
-
|
|
2251
|
-
|
|
2277
|
+
CUSTOM_CODE_REGISTER_TIMEOUT_MS: number;
|
|
2278
|
+
CUSTOM_CODE_REGISTER_INTERVAL_MS: number;
|
|
2252
2279
|
CUSTOM_CODE_MAX_LOGS: number;
|
|
2253
2280
|
CUSTOM_CODE_MAX_LOG_LENGTH: number;
|
|
2254
2281
|
CUSTOM_CODE_SEND_FILE_TARGET_GMAIL: "gmail";
|
|
@@ -2301,6 +2328,15 @@ export declare const utils: {
|
|
|
2301
2328
|
CLI_OAUTH_REDIRECT_PORTS: number[];
|
|
2302
2329
|
CLI_OAUTH_SCOPES: string[];
|
|
2303
2330
|
CLI_TOKEN_REFRESH_SKEW_SECONDS: number;
|
|
2331
|
+
ACCESS_TOKEN_PREFIX: string;
|
|
2332
|
+
ACCESS_TOKEN_BYTES: number;
|
|
2333
|
+
ACCESS_TOKEN_HINT_CHARS: number;
|
|
2334
|
+
ACCESS_TOKEN_NAME_MAX: number;
|
|
2335
|
+
ACCESS_TOKEN_MAX_PER_PROJECT: number;
|
|
2336
|
+
ACCESS_TOKEN_MAX_EXPIRY_DAYS: number;
|
|
2337
|
+
ACCESS_TOKEN_LAST_USED_INTERVAL_MS: number;
|
|
2338
|
+
ACCESS_TOKEN_UNSCOPED_PATHS: string[];
|
|
2339
|
+
ACCESS_TOKEN_SCOPE_MESSAGE: string;
|
|
2304
2340
|
CUSTOM_CODE_LOGS_DEFAULT_LIMIT: number;
|
|
2305
2341
|
CUSTOM_CODE_LOGS_MAX_LIMIT: number;
|
|
2306
2342
|
BOT_GRANT_TYPE: string;
|
|
@@ -2353,6 +2389,8 @@ export declare const utils: {
|
|
|
2353
2389
|
verifyCustomCodeToken: (token: string, secret: string, now?: number) => Promise<CustomCodeTokenPayload | null>;
|
|
2354
2390
|
customCodeScriptName: (artifactId: string) => string;
|
|
2355
2391
|
customCodePreviewScriptName: (artifactId: string) => string;
|
|
2392
|
+
customCodeUploadName: (artifactId: string) => string;
|
|
2393
|
+
customCodePreviewUploadName: (artifactId: string) => string;
|
|
2356
2394
|
oauthProviders: Record<string, OAuthProviderConfig>;
|
|
2357
2395
|
resolveAttachment: (resource: AttachmentResource, readObject: (key: string) => Promise<ArrayBuffer | null>, verb?: "attach" | "upload") => Promise<ResolveAttachmentResult>;
|
|
2358
2396
|
isExposedResource: (resource: ExposableResource) => boolean;
|
|
@@ -2380,6 +2418,10 @@ export declare const utils: {
|
|
|
2380
2418
|
languageFromHeader: (header?: string | null) => string;
|
|
2381
2419
|
slugifyTitle: (title: string) => string;
|
|
2382
2420
|
resourceUriFromTitle: (title: string) => string;
|
|
2421
|
+
accessTokenHint: (token: string) => string;
|
|
2422
|
+
hashAccessToken: (token: string) => Promise<string>;
|
|
2423
|
+
isAccessToken: (token: string) => boolean;
|
|
2424
|
+
mintAccessToken: () => Promise<MintedAccessToken>;
|
|
2383
2425
|
TOOL_CATALOG: readonly [{
|
|
2384
2426
|
readonly key: "gmail";
|
|
2385
2427
|
readonly title: "Gmail";
|
|
@@ -2839,5 +2881,5 @@ export declare const utils: {
|
|
|
2839
2881
|
PlanLimitError: typeof PlanLimitError;
|
|
2840
2882
|
isPlanLimitError: (error: unknown) => error is PlanLimitError;
|
|
2841
2883
|
};
|
|
2842
|
-
export type { CustomCodeProject, ProjectPathIssue, CatalogGroup, CatalogTool, CatalogToolDescriptor, ToolGroupKey, ToolKey, CalendarConfigField, JsonSchema, SchemaViolation, MimeAttachment, MimeMessageInput, GmailOperation, GmailSendRequest, GmailSendResponse, OutlookOperation, OutlookSendRequest, OutlookSendResponse, SlackOperation, SlackSendRequest, SlackSendResponse, SlackSendRemoteResourceRequest, TelegramSendRequest, TelegramSendResponse, TelegramSendRemoteResourceRequest, DiscordSendRequest, DiscordSendResponse, DiscordSendRemoteResourceRequest, WhatsappSendRequest, WhatsappSendResponse, WhatsappSendRemoteResourceRequest, EnvSource, Separator, ChunkMetadata, PreparedChunk, ExtractedDocument, ExtractedDocumentMetadata, ExtractedDocumentSource, RateLimitRetryOptions, QueueBatchLike, QueueMessageLike, ProcessQueueBatchHandlers, ChannelNotifier, ToolStatusEvent, BufferedChannelMessage, ChannelBufferEnvelope, ChannelBufferFlush, Source, ResourceUrlContext, SourceButton, RefreshOAuthTokenInput, RefreshedOAuthToken, HttpEndpointToolConfig, McpProxyToolConfig, McpProxyDiscoveredTool, McpProxyDiscoveredResource, McpProxyDiscoveredPrompt, McpProxyDiscovery, CustomCodeToolConfig, CustomCodeToolManifest, CustomCodeManifest, CustomCodeSendFile, CustomCodeCreateResource, CustomCodeTokenPayload, OAuthProviderConfig, AttachmentResource, ResolvedAttachment, ResolveAttachmentResult, ExposableResource, PlanLimitDetails, PlanLimits };
|
|
2884
|
+
export type { MintedAccessToken, CustomCodeProject, ProjectPathIssue, CatalogGroup, CatalogTool, CatalogToolDescriptor, ToolGroupKey, ToolKey, CalendarConfigField, JsonSchema, SchemaViolation, MimeAttachment, MimeMessageInput, GmailOperation, GmailSendRequest, GmailSendResponse, OutlookOperation, OutlookSendRequest, OutlookSendResponse, SlackOperation, SlackSendRequest, SlackSendResponse, SlackSendRemoteResourceRequest, TelegramSendRequest, TelegramSendResponse, TelegramSendRemoteResourceRequest, DiscordSendRequest, DiscordSendResponse, DiscordSendRemoteResourceRequest, WhatsappSendRequest, WhatsappSendResponse, WhatsappSendRemoteResourceRequest, EnvSource, Separator, ChunkMetadata, PreparedChunk, ExtractedDocument, ExtractedDocumentMetadata, ExtractedDocumentSource, RateLimitRetryOptions, QueueBatchLike, QueueMessageLike, ProcessQueueBatchHandlers, ChannelNotifier, ToolStatusEvent, BufferedChannelMessage, ChannelBufferEnvelope, ChannelBufferFlush, Source, ResourceUrlContext, SourceButton, RefreshOAuthTokenInput, RefreshedOAuthToken, HttpEndpointToolConfig, McpProxyToolConfig, McpProxyDiscoveredTool, McpProxyDiscoveredResource, McpProxyDiscoveredPrompt, McpProxyDiscovery, CustomCodeToolConfig, CustomCodeToolManifest, CustomCodeManifest, CustomCodeSendFile, CustomCodeCreateResource, CustomCodeTokenPayload, OAuthProviderConfig, AttachmentResource, ResolvedAttachment, ResolveAttachmentResult, ExposableResource, PlanLimitDetails, PlanLimits };
|
|
2843
2885
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,sBAAsB,EACtB,kBAAkB,EAClB,sBAAsB,EACtB,0BAA0B,EAC1B,wBAAwB,EACxB,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,EACtB,kBAAkB,EAClB,kBAAkB,EAClB,wBAAwB,EACzB,MAAM,UAAU,CAAC;AAElB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAyCvD,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,KAAK,EACV,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EACV,mBAAmB,EACnB,oBAAoB,EACpB,iCAAiC,EAClC,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EACV,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EACV,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,8BAA8B,EAC/B,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EACnB,gCAAgC,EACjC,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EACV,mBAAmB,EACnB,oBAAoB,EACpB,iCAAiC,EAClC,MAAM,gBAAgB,CAAC;AAKxB,OAAO,EACL,UAAU,EAGX,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aA5Gf,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;kBAhHiB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBA0DF,CAAC;oBAEd,CAAC;;;;aA0BkB,CAAC;mBAA8B,CAAC;WACvC,CAAC;gBACf,CAAC;gBAA4B,CAAA;;;mBAoBK,CAAC;oBACjC,CAAC;;;;;;;;;;;;;;;kBA3Dc,CAAC;;;;;;;sBAqCR,CAAC;;;;;;;;;CAsPf,CAAC;AAEF,YAAY,EACV,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,YAAY,EACZ,OAAO,EACP,mBAAmB,EACnB,UAAU,EACV,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,8BAA8B,EAC9B,mBAAmB,EACnB,oBAAoB,EACpB,iCAAiC,EACjC,kBAAkB,EAClB,mBAAmB,EACnB,gCAAgC,EAChC,mBAAmB,EACnB,oBAAoB,EACpB,iCAAiC,EACjC,SAAS,EACT,SAAS,EACT,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,yBAAyB,EACzB,uBAAuB,EACvB,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,yBAAyB,EACzB,eAAe,EACf,eAAe,EACf,sBAAsB,EACtB,qBAAqB,EACrB,kBAAkB,EAClB,MAAM,EACN,kBAAkB,EAClB,YAAY,EACZ,sBAAsB,EACtB,mBAAmB,EACnB,sBAAsB,EACtB,kBAAkB,EAClB,sBAAsB,EACtB,0BAA0B,EAC1B,wBAAwB,EACxB,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,EACtB,kBAAkB,EAClB,kBAAkB,EAClB,wBAAwB,EACxB,sBAAsB,EACtB,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,EACvB,iBAAiB,EACjB,gBAAgB,EAChB,UAAU,EACX,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -31,6 +31,7 @@ const languageCookieDomain_1 = require("./languageCookieDomain");
|
|
|
31
31
|
const localizeZodIssue_1 = require("./localizeZodIssue");
|
|
32
32
|
const slugifyTitle_1 = require("./slugifyTitle");
|
|
33
33
|
const resourceUri_1 = require("./resourceUri");
|
|
34
|
+
const accessToken_1 = require("./accessToken");
|
|
34
35
|
const toolCatalog_1 = require("./toolCatalog");
|
|
35
36
|
const tallyUsageKinds_1 = require("./tallyUsageKinds");
|
|
36
37
|
const formatFilename_1 = require("./formatFilename");
|
|
@@ -89,6 +90,8 @@ exports.utils = {
|
|
|
89
90
|
verifyCustomCodeToken: customCodeToken_1.verifyCustomCodeToken,
|
|
90
91
|
customCodeScriptName: customCodeToken_1.customCodeScriptName,
|
|
91
92
|
customCodePreviewScriptName: customCodeToken_1.customCodePreviewScriptName,
|
|
93
|
+
customCodeUploadName: customCodeToken_1.customCodeUploadName,
|
|
94
|
+
customCodePreviewUploadName: customCodeToken_1.customCodePreviewUploadName,
|
|
92
95
|
oauthProviders: oauthProviders_1.oauthProviders,
|
|
93
96
|
resolveAttachment: attachment_1.resolveAttachment,
|
|
94
97
|
isExposedResource: exposedResource_1.isExposedResource,
|
|
@@ -111,6 +114,10 @@ exports.utils = {
|
|
|
111
114
|
languageFromHeader: localizeZodIssue_1.languageFromHeader,
|
|
112
115
|
slugifyTitle: slugifyTitle_1.slugifyTitle,
|
|
113
116
|
resourceUriFromTitle: resourceUri_1.resourceUriFromTitle,
|
|
117
|
+
accessTokenHint: accessToken_1.accessTokenHint,
|
|
118
|
+
hashAccessToken: accessToken_1.hashAccessToken,
|
|
119
|
+
isAccessToken: accessToken_1.isAccessToken,
|
|
120
|
+
mintAccessToken: accessToken_1.mintAccessToken,
|
|
114
121
|
TOOL_CATALOG: toolCatalog_1.TOOL_CATALOG,
|
|
115
122
|
TOOL_KEYS: toolCatalog_1.TOOL_KEYS,
|
|
116
123
|
describeCatalogTool: toolCatalog_1.describeCatalogTool,
|
package/dist/schema.d.ts
CHANGED
|
@@ -1255,6 +1255,28 @@ export declare const Schema: {
|
|
|
1255
1255
|
userId: z.ZodUUID;
|
|
1256
1256
|
organizationId: z.ZodUUID;
|
|
1257
1257
|
}, z.core.$strip>;
|
|
1258
|
+
ACCESS_TOKEN_CREATE: z.ZodObject<{
|
|
1259
|
+
name: z.ZodString;
|
|
1260
|
+
expiresInDays: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
1261
|
+
userId: z.ZodUUID;
|
|
1262
|
+
organizationId: z.ZodUUID;
|
|
1263
|
+
projectId: z.ZodUUID;
|
|
1264
|
+
}, z.core.$strip>;
|
|
1265
|
+
ACCESS_TOKEN_CREATE_VIEW: z.ZodObject<{
|
|
1266
|
+
name: z.ZodString;
|
|
1267
|
+
expiresInDays: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
1268
|
+
}, z.core.$strip>;
|
|
1269
|
+
ACCESS_TOKEN_LIST: z.ZodObject<{
|
|
1270
|
+
userId: z.ZodUUID;
|
|
1271
|
+
organizationId: z.ZodUUID;
|
|
1272
|
+
projectId: z.ZodUUID;
|
|
1273
|
+
}, z.core.$strip>;
|
|
1274
|
+
ACCESS_TOKEN_REMOVE: z.ZodObject<{
|
|
1275
|
+
tokenId: z.ZodUUID;
|
|
1276
|
+
userId: z.ZodUUID;
|
|
1277
|
+
organizationId: z.ZodUUID;
|
|
1278
|
+
projectId: z.ZodUUID;
|
|
1279
|
+
}, z.core.$strip>;
|
|
1258
1280
|
CHANNEL_CONFIG: z.ZodObject<{
|
|
1259
1281
|
debounceMs: z.ZodOptional<z.ZodNumber>;
|
|
1260
1282
|
}, z.core.$loose>;
|
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA6tBxB,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0FrB,CAAC;AAyDN,QAAA,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;iBAmCpB,CAAC;AAWH,QAAA,MAAM,kBAAkB;;;;;;;;;;iBA+CtB,CAAC;AAiCH,QAAA,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAapB,CAAC;AAYH,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgCxB,CAAC;AA4LH,QAAA,MAAM,kCAAkC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA4DpC,CAAC;AA4BL,QAAA,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBAkDzB,CAAC;AAwGH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0GlB,CAAC;AAGF,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAG1E,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAIlE,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAMtE,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAGtE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAMtE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAIvE,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAC5C,OAAO,kCAAkC,CAC1C,CAAC;AAKF,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;CACtB;AAKD,MAAM,WAAW,0BAA0B;IACzC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAKD,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;CAC1E;AAMD,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACjD,KAAK,EAAE,sBAAsB,EAAE,CAAC;IAChC,SAAS,CAAC,EAAE,0BAA0B,EAAE,CAAC;IACzC,OAAO,CAAC,EAAE,wBAAwB,EAAE,CAAC;CACtC"}
|
package/dist/schema.js
CHANGED
|
@@ -519,6 +519,48 @@ const ORGANIZATION_REMOVE_LLM = zod_1.z.object({
|
|
|
519
519
|
userId: zod_1.z.uuid(),
|
|
520
520
|
organizationId: zod_1.z.uuid()
|
|
521
521
|
});
|
|
522
|
+
// Personal access tokens. `name` is required rather than optional because it is
|
|
523
|
+
// the only thing anyone has to go on when deciding whether revoking a row will
|
|
524
|
+
// break a deploy, and a list of unnamed credentials is a list nobody prunes.
|
|
525
|
+
//
|
|
526
|
+
// `expiresInDays` is what the caller sends; `expiresAt` is what the row stores.
|
|
527
|
+
// A duration is what someone actually decides ("ninety days"), and computing the
|
|
528
|
+
// date on the server means a clock the client got wrong cannot mint a token that
|
|
529
|
+
// outlives what was asked for. Null is a token that does not expire, which is a
|
|
530
|
+
// real answer for a scheduled deploy and is why it is spelled explicitly rather
|
|
531
|
+
// than reached by leaving the field out.
|
|
532
|
+
const ACCESS_TOKEN_CREATE = zod_1.z.object({
|
|
533
|
+
name: zod_1.z.string().trim().min(1).max(constants_1.constants.ACCESS_TOKEN_NAME_MAX),
|
|
534
|
+
expiresInDays: zod_1.z
|
|
535
|
+
.number()
|
|
536
|
+
.int()
|
|
537
|
+
.min(1)
|
|
538
|
+
.max(constants_1.constants.ACCESS_TOKEN_MAX_EXPIRY_DAYS)
|
|
539
|
+
.nullable()
|
|
540
|
+
.optional(),
|
|
541
|
+
userId: zod_1.z.uuid(),
|
|
542
|
+
organizationId: zod_1.z.uuid(),
|
|
543
|
+
projectId: zod_1.z.uuid()
|
|
544
|
+
});
|
|
545
|
+
const ACCESS_TOKEN_LIST = zod_1.z.object({
|
|
546
|
+
userId: zod_1.z.uuid(),
|
|
547
|
+
organizationId: zod_1.z.uuid(),
|
|
548
|
+
projectId: zod_1.z.uuid()
|
|
549
|
+
});
|
|
550
|
+
const ACCESS_TOKEN_REMOVE = zod_1.z.object({
|
|
551
|
+
tokenId: zod_1.z.uuid(),
|
|
552
|
+
userId: zod_1.z.uuid(),
|
|
553
|
+
organizationId: zod_1.z.uuid(),
|
|
554
|
+
projectId: zod_1.z.uuid()
|
|
555
|
+
});
|
|
556
|
+
// The ids the route already carries come off: the dashboard reads them from the
|
|
557
|
+
// URL it is on, so a form asking for them again is a second place for them to be
|
|
558
|
+
// wrong.
|
|
559
|
+
const ACCESS_TOKEN_CREATE_VIEW = ACCESS_TOKEN_CREATE.omit({
|
|
560
|
+
userId: true,
|
|
561
|
+
organizationId: true,
|
|
562
|
+
projectId: true
|
|
563
|
+
});
|
|
522
564
|
const ORGANIZATION_CREATE_LLM_VIEW = ORGANIZATION_CREATE_LLM.omit({
|
|
523
565
|
userId: true,
|
|
524
566
|
organizationId: true
|
|
@@ -1345,6 +1387,10 @@ exports.Schema = {
|
|
|
1345
1387
|
ORGANIZATION_UPDATE_LLM,
|
|
1346
1388
|
ORGANIZATION_UPDATE_LLM_VIEW,
|
|
1347
1389
|
ORGANIZATION_REMOVE_LLM,
|
|
1390
|
+
ACCESS_TOKEN_CREATE,
|
|
1391
|
+
ACCESS_TOKEN_CREATE_VIEW,
|
|
1392
|
+
ACCESS_TOKEN_LIST,
|
|
1393
|
+
ACCESS_TOKEN_REMOVE,
|
|
1348
1394
|
CHANNEL_CONFIG,
|
|
1349
1395
|
CHANNEL_CREATE,
|
|
1350
1396
|
CHANNEL_CREATE_VIEW,
|
package/package.json
CHANGED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { constants } from './constants';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Minting and recognising a personal access token.
|
|
5
|
+
*
|
|
6
|
+
* The value exists in plaintext exactly once — in the response to the request
|
|
7
|
+
* that created it — and what the database holds is its SHA-256. That is the
|
|
8
|
+
* property the whole credential rests on: a leaked backup, a stray log line, or
|
|
9
|
+
* a support engineer reading the row learns nothing they could present as the
|
|
10
|
+
* token, and there is no path in the product that can print one back, because
|
|
11
|
+
* there is nothing to print.
|
|
12
|
+
*
|
|
13
|
+
* SHA-256 rather than a password hash on purpose. A password is a low-entropy
|
|
14
|
+
* secret a person chose, so the cost of hashing it is what stands between a
|
|
15
|
+
* stolen table and the passwords in it; this is 32 bytes from a CSPRNG, where
|
|
16
|
+
* that cost buys nothing and would be paid on every authenticated request. It
|
|
17
|
+
* is also the hash `oauth_client.client_secret` already uses in this system, so
|
|
18
|
+
* there is one answer here to "how is a machine credential stored".
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const HINT_SEPARATOR = '…';
|
|
22
|
+
|
|
23
|
+
const base64url = (bytes: Uint8Array): string => {
|
|
24
|
+
let binary = '';
|
|
25
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
26
|
+
return btoa(binary)
|
|
27
|
+
.replace(/\+/g, '-')
|
|
28
|
+
.replace(/\//g, '_')
|
|
29
|
+
.replace(/=+$/, '');
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/** The SHA-256 of a presented token, in the encoding the column stores. */
|
|
33
|
+
export const hashAccessToken = async (token: string): Promise<string> => {
|
|
34
|
+
const digest = await crypto.subtle.digest(
|
|
35
|
+
'SHA-256',
|
|
36
|
+
new TextEncoder().encode(token)
|
|
37
|
+
);
|
|
38
|
+
return base64url(new Uint8Array(digest));
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Cheap enough to run before the database is touched: a bearer token that does
|
|
43
|
+
* not carry the prefix is an OAuth token, and belongs on the other path.
|
|
44
|
+
*/
|
|
45
|
+
export const isAccessToken = (token: string): boolean =>
|
|
46
|
+
token.startsWith(constants.ACCESS_TOKEN_PREFIX);
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* What the dashboard shows beside a token's name.
|
|
50
|
+
*
|
|
51
|
+
* Enough to tell two rows apart when someone is deciding which to revoke, and
|
|
52
|
+
* deliberately taken from the *front* of the secret rather than the end: a
|
|
53
|
+
* prefix narrows a brute-force search by exactly as much as a suffix would, and
|
|
54
|
+
* a value people are used to seeing truncated at the end reads as complete when
|
|
55
|
+
* it is the end that is shown.
|
|
56
|
+
*/
|
|
57
|
+
export const accessTokenHint = (token: string): string =>
|
|
58
|
+
`${token.slice(
|
|
59
|
+
0,
|
|
60
|
+
constants.ACCESS_TOKEN_PREFIX.length + constants.ACCESS_TOKEN_HINT_CHARS
|
|
61
|
+
)}${HINT_SEPARATOR}`;
|
|
62
|
+
|
|
63
|
+
export interface MintedAccessToken {
|
|
64
|
+
/** The only time this value exists. Returned to the caller, never stored. */
|
|
65
|
+
token: string;
|
|
66
|
+
tokenHash: string;
|
|
67
|
+
hint: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export const mintAccessToken = async (): Promise<MintedAccessToken> => {
|
|
71
|
+
const bytes = new Uint8Array(constants.ACCESS_TOKEN_BYTES);
|
|
72
|
+
crypto.getRandomValues(bytes);
|
|
73
|
+
const token = `${constants.ACCESS_TOKEN_PREFIX}${base64url(bytes)}`;
|
|
74
|
+
return {
|
|
75
|
+
token,
|
|
76
|
+
tokenHash: await hashAccessToken(token),
|
|
77
|
+
hint: accessTokenHint(token)
|
|
78
|
+
};
|
|
79
|
+
};
|
package/src/constants.ts
CHANGED
|
@@ -1239,10 +1239,40 @@ const CUSTOM_CODE_MAIN_MODULE = 'index.js';
|
|
|
1239
1239
|
const CUSTOM_CODE_MAX_FILES = 25;
|
|
1240
1240
|
const CUSTOM_CODE_MAX_FILE_PATH = 100;
|
|
1241
1241
|
|
|
1242
|
-
// WfP script name: `artifact_<artifactId>`. The id, never the slug —
|
|
1243
|
-
// user-editable and a rename would orphan the deployed script.
|
|
1242
|
+
// WfP script name: `artifact_<artifactId>_<upload>`. The id, never the slug —
|
|
1243
|
+
// slugs are user-editable and a rename would orphan the deployed script.
|
|
1244
|
+
//
|
|
1245
|
+
// The trailing segment is minted per upload rather than derived from anything,
|
|
1246
|
+
// which is the whole point: uploading over a name that already exists is not
|
|
1247
|
+
// read-your-writes, so a deploy that replaces a script can serve the previous
|
|
1248
|
+
// edition for up to half a minute. A name nothing has ever used cannot, and
|
|
1249
|
+
// costs nothing to mint. Everything a publish used to do to survive that race —
|
|
1250
|
+
// waiting on an edition marker, refusing with a 503, putting the previous bundle
|
|
1251
|
+
// back when validation failed — went with the reuse that caused it.
|
|
1244
1252
|
const CUSTOM_CODE_SCRIPT_NAME_PREFIX = 'artifact_';
|
|
1245
1253
|
|
|
1254
|
+
// Worker names cap at 63 characters, and `artifact_<uuid>` already spends 45.
|
|
1255
|
+
// That leaves 17 for a separator and a suffix, so a second uuid does not fit and
|
|
1256
|
+
// neither does a hex-32 digest. Twelve hex characters is 48 bits against a
|
|
1257
|
+
// namespace holding at most a few hundred names for any one artifact — not a
|
|
1258
|
+
// collision worth checking for, and an upload to a name in use would fail loudly
|
|
1259
|
+
// rather than quietly serve the wrong code.
|
|
1260
|
+
const CUSTOM_CODE_SCRIPT_NAME_MAX = 63;
|
|
1261
|
+
const CUSTOM_CODE_UPLOAD_SUFFIX_CHARS = 12;
|
|
1262
|
+
|
|
1263
|
+
// How long a superseded script stays in the namespace before the hourly sweep
|
|
1264
|
+
// may collect it.
|
|
1265
|
+
//
|
|
1266
|
+
// Deleting at publish time would race the thing it deletes: a tool call that
|
|
1267
|
+
// resolved the old pointer a moment earlier is still in flight, and the pointer
|
|
1268
|
+
// moving does not recall it. An hour is far longer than any call can take, and
|
|
1269
|
+
// the wait costs $0.02 per script per month against an allowance of 1,000.
|
|
1270
|
+
const CUSTOM_CODE_SWEEP_GRACE_MS = 60 * 60 * 1_000;
|
|
1271
|
+
|
|
1272
|
+
// Deletes per sweep. A backlog drains over several hourly runs rather than
|
|
1273
|
+
// making one run unbounded — the same shape the retention purge uses.
|
|
1274
|
+
const CUSTOM_CODE_SWEEP_MAX_DELETES = 200;
|
|
1275
|
+
|
|
1246
1276
|
// A second script per artifact, `artifact_<id>_preview`, that the Test panel
|
|
1247
1277
|
// deploys a draft into and calls.
|
|
1248
1278
|
//
|
|
@@ -1453,15 +1483,17 @@ const CUSTOM_CODE_COMPATIBILITY_DATE = '2025-11-17';
|
|
|
1453
1483
|
// tighter than our own workers' 30s — this is the technical cap that bounds
|
|
1454
1484
|
// what one adversarial call can cost us, so an infinite loop in a customer's
|
|
1455
1485
|
// tool is billed as five seconds rather than as whatever it wanted.
|
|
1456
|
-
// How long
|
|
1457
|
-
//
|
|
1486
|
+
// How long a deploy waits for a freshly minted script name to become
|
|
1487
|
+
// dispatchable, and how often it asks.
|
|
1458
1488
|
//
|
|
1459
|
-
//
|
|
1460
|
-
//
|
|
1461
|
-
//
|
|
1462
|
-
//
|
|
1463
|
-
|
|
1464
|
-
|
|
1489
|
+
// Every upload goes to a name that has never been used, which is
|
|
1490
|
+
// read-your-writes: ~2s end to end against the deployed namespace, against the
|
|
1491
|
+
// 20-41s a replacement could take. So this bounds how long a brand-new name
|
|
1492
|
+
// takes to register, never how long an old edition takes to stop answering —
|
|
1493
|
+
// there is no old edition. It is short for that reason, and a script that
|
|
1494
|
+
// answers with the wrong edition now fails outright instead of being waited on.
|
|
1495
|
+
const CUSTOM_CODE_REGISTER_TIMEOUT_MS = 8_000;
|
|
1496
|
+
const CUSTOM_CODE_REGISTER_INTERVAL_MS = 500;
|
|
1465
1497
|
|
|
1466
1498
|
const CUSTOM_CODE_SCRIPT_CPU_MS = 5_000;
|
|
1467
1499
|
|
|
@@ -1663,6 +1695,55 @@ const ARTIFACT_SCOPE_PREFIX = 'artifact:';
|
|
|
1663
1695
|
// that is one library's behaviour and this is a login that has already opened
|
|
1664
1696
|
// someone's browser by the time it would fail.
|
|
1665
1697
|
|
|
1698
|
+
// A personal access token — the durable credential a machine with no browser
|
|
1699
|
+
// uses, where an OAuth access token's one hour is not enough. Bound to one
|
|
1700
|
+
// project, because that is the unit a deploy pipeline works on: one repository,
|
|
1701
|
+
// one artifact, one credential in its CI settings.
|
|
1702
|
+
//
|
|
1703
|
+
// The prefix is part of the value rather than decoration: it is what lets the
|
|
1704
|
+
// middleware tell one of these from an OAuth token before it decides which
|
|
1705
|
+
// lookup to make, and it is what secret scanners match on when one leaks into a
|
|
1706
|
+
// repository. The rest is 32 random bytes, base64url — the token is the only
|
|
1707
|
+
// place the value ever exists in plaintext, since what is stored is its hash.
|
|
1708
|
+
const ACCESS_TOKEN_PREFIX = 'ganju_pat_';
|
|
1709
|
+
const ACCESS_TOKEN_BYTES = 32;
|
|
1710
|
+
|
|
1711
|
+
// Enough of the secret to recognise a row by, and not enough to be worth
|
|
1712
|
+
// stealing. Shown in the dashboard and by `ganju token list` beside the name.
|
|
1713
|
+
const ACCESS_TOKEN_HINT_CHARS = 6;
|
|
1714
|
+
|
|
1715
|
+
const ACCESS_TOKEN_NAME_MAX = 100;
|
|
1716
|
+
|
|
1717
|
+
// A ceiling per project, so a compromised session cannot quietly mint an
|
|
1718
|
+
// unbounded set of credentials that each survive the session being ended.
|
|
1719
|
+
const ACCESS_TOKEN_MAX_PER_PROJECT = 20;
|
|
1720
|
+
|
|
1721
|
+
// An expiry is optional, because a scheduled deploy that dies on a date nobody
|
|
1722
|
+
// wrote down is its own kind of outage — but a year is the longest we will
|
|
1723
|
+
// write one for.
|
|
1724
|
+
const ACCESS_TOKEN_MAX_EXPIRY_DAYS = 365;
|
|
1725
|
+
|
|
1726
|
+
// `last_used_at` is a convenience, not an audit log, so it is written at most
|
|
1727
|
+
// this often per token rather than on every request. The question it answers —
|
|
1728
|
+
// "is anything still using this, or can I revoke it" — does not get a better
|
|
1729
|
+
// answer from minute-level precision, and the write would otherwise land on the
|
|
1730
|
+
// hot path of every CI request.
|
|
1731
|
+
const ACCESS_TOKEN_LAST_USED_INTERVAL_MS = 5 * 60 * 1000;
|
|
1732
|
+
|
|
1733
|
+
// The only path a personal access token may reach without naming the project it
|
|
1734
|
+
// is scoped to, and only on GET. `/me` reports who the token is, which is how
|
|
1735
|
+
// the CLI confirms a machine is authenticated at all, and tells it nothing it
|
|
1736
|
+
// does not already hold.
|
|
1737
|
+
//
|
|
1738
|
+
// Everything else is refused, organization routes included — billing, members,
|
|
1739
|
+
// the model configs and the other projects are not what a deploy credential is
|
|
1740
|
+
// for. The list is deliberately this short: a route added later is closed by
|
|
1741
|
+
// omission rather than open by it.
|
|
1742
|
+
const ACCESS_TOKEN_UNSCOPED_PATHS = ['/me'];
|
|
1743
|
+
|
|
1744
|
+
const ACCESS_TOKEN_SCOPE_MESSAGE =
|
|
1745
|
+
'This token is scoped to a different project';
|
|
1746
|
+
|
|
1666
1747
|
// Recent custom-tool invocations, as `ganju logs` reads them.
|
|
1667
1748
|
const CUSTOM_CODE_LOGS_DEFAULT_LIMIT = 20;
|
|
1668
1749
|
const CUSTOM_CODE_LOGS_MAX_LIMIT = 100;
|
|
@@ -2465,6 +2546,10 @@ export const constants = {
|
|
|
2465
2546
|
CUSTOM_CODE_MAX_FILE_PATH,
|
|
2466
2547
|
CUSTOM_CODE_VERSION_STATUSES,
|
|
2467
2548
|
CUSTOM_CODE_SCRIPT_NAME_PREFIX,
|
|
2549
|
+
CUSTOM_CODE_SCRIPT_NAME_MAX,
|
|
2550
|
+
CUSTOM_CODE_UPLOAD_SUFFIX_CHARS,
|
|
2551
|
+
CUSTOM_CODE_SWEEP_GRACE_MS,
|
|
2552
|
+
CUSTOM_CODE_SWEEP_MAX_DELETES,
|
|
2468
2553
|
CUSTOM_CODE_PREVIEW_SCRIPT_SUFFIX,
|
|
2469
2554
|
CUSTOM_CODE_PREVIEW_TOKEN_TTL_MS,
|
|
2470
2555
|
CUSTOM_CODE_TEST_TIMEOUT_MS,
|
|
@@ -2506,8 +2591,8 @@ export const constants = {
|
|
|
2506
2591
|
CUSTOM_CODE_BROKER_SERVICE_ENV,
|
|
2507
2592
|
CUSTOM_CODE_COMPATIBILITY_DATE,
|
|
2508
2593
|
CUSTOM_CODE_SCRIPT_CPU_MS,
|
|
2509
|
-
|
|
2510
|
-
|
|
2594
|
+
CUSTOM_CODE_REGISTER_TIMEOUT_MS,
|
|
2595
|
+
CUSTOM_CODE_REGISTER_INTERVAL_MS,
|
|
2511
2596
|
CUSTOM_CODE_MAX_LOGS,
|
|
2512
2597
|
CUSTOM_CODE_MAX_LOG_LENGTH,
|
|
2513
2598
|
CUSTOM_CODE_SEND_FILE_TARGET_GMAIL,
|
|
@@ -2560,6 +2645,15 @@ export const constants = {
|
|
|
2560
2645
|
CLI_OAUTH_REDIRECT_PORTS,
|
|
2561
2646
|
CLI_OAUTH_SCOPES,
|
|
2562
2647
|
CLI_TOKEN_REFRESH_SKEW_SECONDS,
|
|
2648
|
+
ACCESS_TOKEN_PREFIX,
|
|
2649
|
+
ACCESS_TOKEN_BYTES,
|
|
2650
|
+
ACCESS_TOKEN_HINT_CHARS,
|
|
2651
|
+
ACCESS_TOKEN_NAME_MAX,
|
|
2652
|
+
ACCESS_TOKEN_MAX_PER_PROJECT,
|
|
2653
|
+
ACCESS_TOKEN_MAX_EXPIRY_DAYS,
|
|
2654
|
+
ACCESS_TOKEN_LAST_USED_INTERVAL_MS,
|
|
2655
|
+
ACCESS_TOKEN_UNSCOPED_PATHS,
|
|
2656
|
+
ACCESS_TOKEN_SCOPE_MESSAGE,
|
|
2563
2657
|
CUSTOM_CODE_LOGS_DEFAULT_LIMIT,
|
|
2564
2658
|
CUSTOM_CODE_LOGS_MAX_LIMIT,
|
|
2565
2659
|
BOT_GRANT_TYPE,
|
package/src/customCodeToken.ts
CHANGED
|
@@ -179,20 +179,100 @@ export const verifyCustomCodeToken = async (
|
|
|
179
179
|
};
|
|
180
180
|
|
|
181
181
|
/**
|
|
182
|
-
* The dispatch-namespace script name for an artifact: `artifact_<id>`.
|
|
182
|
+
* The legacy dispatch-namespace script name for an artifact: `artifact_<id>`.
|
|
183
183
|
*
|
|
184
184
|
* The id, never the slug — slugs are user-editable and a rename would orphan the
|
|
185
185
|
* deployed script while the database still pointed at a live version.
|
|
186
|
+
*
|
|
187
|
+
* Nothing uploads to this name any more; every deploy mints its own. It survives
|
|
188
|
+
* as the fallback for a version published before `script_name` existed, whose
|
|
189
|
+
* bundle really is sitting under this name. Tightening a rule must never stop an
|
|
190
|
+
* already-published version from serving, because that failure is invisible to
|
|
191
|
+
* whoever owns it — the same reason the boot loop still accepts a stored tool key
|
|
192
|
+
* the current catalog no longer offers.
|
|
186
193
|
*/
|
|
187
194
|
export const customCodeScriptName = (artifactId: string): string =>
|
|
188
195
|
`${constants.CUSTOM_CODE_SCRIPT_NAME_PREFIX}${artifactId}`;
|
|
189
196
|
|
|
190
197
|
/**
|
|
191
|
-
* The script name
|
|
198
|
+
* The legacy preview script name: `artifact_<id>_preview`.
|
|
192
199
|
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
200
|
+
* Kept for the same reason as the one above, and for one more: it is the prefix
|
|
201
|
+
* the sweep matches to recognise a preview script left behind by a test run that
|
|
202
|
+
* did not clean up after itself.
|
|
196
203
|
*/
|
|
197
204
|
export const customCodePreviewScriptName = (artifactId: string): string =>
|
|
198
205
|
`${customCodeScriptName(artifactId)}${constants.CUSTOM_CODE_PREVIEW_SCRIPT_SUFFIX}`;
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* A dispatch-namespace name no upload has ever used:
|
|
209
|
+
* `artifact_<id>_<12 hex chars>`.
|
|
210
|
+
*
|
|
211
|
+
* Minted rather than derived, and that is the entire design. Uploading over an
|
|
212
|
+
* existing name is not read-your-writes — a replacement can serve the previous
|
|
213
|
+
* edition for tens of seconds — so a deploy that always writes to a new name is
|
|
214
|
+
* correct by construction rather than by waiting to see whether it worked.
|
|
215
|
+
*
|
|
216
|
+
* The suffix deliberately carries no meaning. The two candidates that did are
|
|
217
|
+
* both wrong: the bundle digest collides whenever a deploy reverts to bytes that
|
|
218
|
+
* shipped before, which is exactly what a rollback is, and the version id is one
|
|
219
|
+
* string across every re-upload of a single draft, which is every test run of it.
|
|
220
|
+
*
|
|
221
|
+
* Twelve hex characters is what fits. Worker names cap at 63 and
|
|
222
|
+
* `artifact_<uuid>` spends 45 of them.
|
|
223
|
+
*/
|
|
224
|
+
export const customCodeUploadName = (artifactId: string): string =>
|
|
225
|
+
mintUploadName(customCodeScriptName(artifactId));
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* A preview name no test run has ever used:
|
|
229
|
+
* `artifact_<id>_preview_<12 hex chars>`.
|
|
230
|
+
*
|
|
231
|
+
* The sharper version of the same race. Every test used to deploy over one
|
|
232
|
+
* preview name, so a test could report the run before it — which reads as "my
|
|
233
|
+
* edit did nothing" from the one tool whose whole job is to say what an edit
|
|
234
|
+
* does. Nothing stores this: it is minted, used, and deleted inside one request.
|
|
235
|
+
*/
|
|
236
|
+
export const customCodePreviewUploadName = (artifactId: string): string =>
|
|
237
|
+
mintUploadName(customCodePreviewScriptName(artifactId));
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Append `_<hex>` to a base name, spending whatever the 63-character ceiling
|
|
241
|
+
* leaves and no more.
|
|
242
|
+
*
|
|
243
|
+
* The budget is genuinely tight, and the two names spend it differently:
|
|
244
|
+
*
|
|
245
|
+
* | name | base | separator | suffix | total |
|
|
246
|
+
* |---|---|---|---|---|
|
|
247
|
+
* | live | `artifact_<uuid>` = 45 | 1 | 12 | 58 |
|
|
248
|
+
* | preview | + `_preview` = 53 | 1 | 8 | 62 |
|
|
249
|
+
*
|
|
250
|
+
* Twelve hex characters is 48 bits and eight is 32, against a namespace holding
|
|
251
|
+
* a few hundred names for any one artifact — and a preview name lives for the
|
|
252
|
+
* seconds one test run takes. Neither is a collision worth checking for, and an
|
|
253
|
+
* upload to a name already in use fails loudly rather than quietly serving the
|
|
254
|
+
* wrong code, which is the failure that matters.
|
|
255
|
+
*
|
|
256
|
+
* The ceiling is asserted rather than assumed: it is one number away from being
|
|
257
|
+
* silently exceeded by a longer prefix, and a name Cloudflare refuses would
|
|
258
|
+
* surface as a failed publish with nothing explaining why.
|
|
259
|
+
*/
|
|
260
|
+
const mintUploadName = (base: string): string => {
|
|
261
|
+
const available = constants.CUSTOM_CODE_SCRIPT_NAME_MAX - base.length - 1;
|
|
262
|
+
// Even, because each byte renders as two hex characters.
|
|
263
|
+
const chars =
|
|
264
|
+
Math.min(constants.CUSTOM_CODE_UPLOAD_SUFFIX_CHARS, available) & ~1;
|
|
265
|
+
|
|
266
|
+
if (chars < 4) {
|
|
267
|
+
throw new Error(
|
|
268
|
+
`A dispatch script name based on "${base}" leaves no room for a unique suffix.`
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const bytes = new Uint8Array(chars / 2);
|
|
273
|
+
crypto.getRandomValues(bytes);
|
|
274
|
+
|
|
275
|
+
return `${base}_${Array.from(bytes)
|
|
276
|
+
.map(byte => byte.toString(16).padStart(2, '0'))
|
|
277
|
+
.join('')}`;
|
|
278
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -102,7 +102,9 @@ import {
|
|
|
102
102
|
mintCustomCodeToken,
|
|
103
103
|
verifyCustomCodeToken,
|
|
104
104
|
customCodeScriptName,
|
|
105
|
-
customCodePreviewScriptName
|
|
105
|
+
customCodePreviewScriptName,
|
|
106
|
+
customCodeUploadName,
|
|
107
|
+
customCodePreviewUploadName
|
|
106
108
|
} from './customCodeToken';
|
|
107
109
|
import type { CustomCodeTokenPayload } from './customCodeToken';
|
|
108
110
|
import { oauthProviders } from './oauthProviders';
|
|
@@ -133,6 +135,13 @@ import { languageCookieDomain } from './languageCookieDomain';
|
|
|
133
135
|
import { localizeZodIssue, languageFromHeader } from './localizeZodIssue';
|
|
134
136
|
import { slugifyTitle } from './slugifyTitle';
|
|
135
137
|
import { resourceUriFromTitle } from './resourceUri';
|
|
138
|
+
import {
|
|
139
|
+
accessTokenHint,
|
|
140
|
+
hashAccessToken,
|
|
141
|
+
isAccessToken,
|
|
142
|
+
mintAccessToken
|
|
143
|
+
} from './accessToken';
|
|
144
|
+
import type { MintedAccessToken } from './accessToken';
|
|
136
145
|
import {
|
|
137
146
|
TOOL_CATALOG,
|
|
138
147
|
TOOL_KEYS,
|
|
@@ -257,6 +266,8 @@ export const utils = {
|
|
|
257
266
|
verifyCustomCodeToken,
|
|
258
267
|
customCodeScriptName,
|
|
259
268
|
customCodePreviewScriptName,
|
|
269
|
+
customCodeUploadName,
|
|
270
|
+
customCodePreviewUploadName,
|
|
260
271
|
oauthProviders,
|
|
261
272
|
resolveAttachment,
|
|
262
273
|
isExposedResource,
|
|
@@ -279,6 +290,10 @@ export const utils = {
|
|
|
279
290
|
languageFromHeader,
|
|
280
291
|
slugifyTitle,
|
|
281
292
|
resourceUriFromTitle,
|
|
293
|
+
accessTokenHint,
|
|
294
|
+
hashAccessToken,
|
|
295
|
+
isAccessToken,
|
|
296
|
+
mintAccessToken,
|
|
282
297
|
TOOL_CATALOG,
|
|
283
298
|
TOOL_KEYS,
|
|
284
299
|
describeCatalogTool,
|
|
@@ -323,6 +338,7 @@ export const utils = {
|
|
|
323
338
|
};
|
|
324
339
|
|
|
325
340
|
export type {
|
|
341
|
+
MintedAccessToken,
|
|
326
342
|
CustomCodeProject,
|
|
327
343
|
ProjectPathIssue,
|
|
328
344
|
CatalogGroup,
|
package/src/schema.ts
CHANGED
|
@@ -603,6 +603,52 @@ const ORGANIZATION_REMOVE_LLM = z.object({
|
|
|
603
603
|
organizationId: z.uuid()
|
|
604
604
|
});
|
|
605
605
|
|
|
606
|
+
// Personal access tokens. `name` is required rather than optional because it is
|
|
607
|
+
// the only thing anyone has to go on when deciding whether revoking a row will
|
|
608
|
+
// break a deploy, and a list of unnamed credentials is a list nobody prunes.
|
|
609
|
+
//
|
|
610
|
+
// `expiresInDays` is what the caller sends; `expiresAt` is what the row stores.
|
|
611
|
+
// A duration is what someone actually decides ("ninety days"), and computing the
|
|
612
|
+
// date on the server means a clock the client got wrong cannot mint a token that
|
|
613
|
+
// outlives what was asked for. Null is a token that does not expire, which is a
|
|
614
|
+
// real answer for a scheduled deploy and is why it is spelled explicitly rather
|
|
615
|
+
// than reached by leaving the field out.
|
|
616
|
+
const ACCESS_TOKEN_CREATE = z.object({
|
|
617
|
+
name: z.string().trim().min(1).max(constants.ACCESS_TOKEN_NAME_MAX),
|
|
618
|
+
expiresInDays: z
|
|
619
|
+
.number()
|
|
620
|
+
.int()
|
|
621
|
+
.min(1)
|
|
622
|
+
.max(constants.ACCESS_TOKEN_MAX_EXPIRY_DAYS)
|
|
623
|
+
.nullable()
|
|
624
|
+
.optional(),
|
|
625
|
+
userId: z.uuid(),
|
|
626
|
+
organizationId: z.uuid(),
|
|
627
|
+
projectId: z.uuid()
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
const ACCESS_TOKEN_LIST = z.object({
|
|
631
|
+
userId: z.uuid(),
|
|
632
|
+
organizationId: z.uuid(),
|
|
633
|
+
projectId: z.uuid()
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
const ACCESS_TOKEN_REMOVE = z.object({
|
|
637
|
+
tokenId: z.uuid(),
|
|
638
|
+
userId: z.uuid(),
|
|
639
|
+
organizationId: z.uuid(),
|
|
640
|
+
projectId: z.uuid()
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
// The ids the route already carries come off: the dashboard reads them from the
|
|
644
|
+
// URL it is on, so a form asking for them again is a second place for them to be
|
|
645
|
+
// wrong.
|
|
646
|
+
const ACCESS_TOKEN_CREATE_VIEW = ACCESS_TOKEN_CREATE.omit({
|
|
647
|
+
userId: true,
|
|
648
|
+
organizationId: true,
|
|
649
|
+
projectId: true
|
|
650
|
+
});
|
|
651
|
+
|
|
606
652
|
const ORGANIZATION_CREATE_LLM_VIEW = ORGANIZATION_CREATE_LLM.omit({
|
|
607
653
|
userId: true,
|
|
608
654
|
organizationId: true
|
|
@@ -1513,6 +1559,10 @@ export const Schema = {
|
|
|
1513
1559
|
ORGANIZATION_UPDATE_LLM,
|
|
1514
1560
|
ORGANIZATION_UPDATE_LLM_VIEW,
|
|
1515
1561
|
ORGANIZATION_REMOVE_LLM,
|
|
1562
|
+
ACCESS_TOKEN_CREATE,
|
|
1563
|
+
ACCESS_TOKEN_CREATE_VIEW,
|
|
1564
|
+
ACCESS_TOKEN_LIST,
|
|
1565
|
+
ACCESS_TOKEN_REMOVE,
|
|
1516
1566
|
CHANNEL_CONFIG,
|
|
1517
1567
|
CHANNEL_CREATE,
|
|
1518
1568
|
CHANNEL_CREATE_VIEW,
|