@sentry/junior 0.64.0 → 0.65.1
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/app.d.ts +3 -0
- package/dist/app.js +3299 -1343
- package/dist/chat/agent-dispatch/heartbeat.d.ts +8 -0
- package/dist/chat/agent-dispatch/store.d.ts +2 -2
- package/dist/chat/agent-dispatch/types.d.ts +8 -22
- package/dist/chat/agent-dispatch/validation.d.ts +3 -1
- package/dist/chat/app/production.d.ts +11 -5
- package/dist/chat/capabilities/factory.d.ts +2 -1
- package/dist/chat/capabilities/router.d.ts +3 -2
- package/dist/chat/config.d.ts +5 -0
- package/dist/chat/credentials/broker.d.ts +2 -1
- package/dist/chat/credentials/context.d.ts +28 -0
- package/dist/chat/credentials/subject.d.ts +20 -0
- package/dist/chat/ingress/slack-webhook.d.ts +19 -0
- package/dist/chat/mcp/errors.d.ts +1 -1
- package/dist/chat/plugins/command-env.d.ts +1 -1
- package/dist/chat/plugins/github-permissions.d.ts +11 -0
- package/dist/chat/plugins/types.d.ts +3 -0
- package/dist/chat/prompt.d.ts +2 -0
- package/dist/chat/respond-helpers.d.ts +4 -2
- package/dist/chat/respond.d.ts +23 -13
- package/dist/chat/runtime/processing-reaction.d.ts +1 -0
- package/dist/chat/runtime/reply-executor.d.ts +5 -0
- package/dist/chat/runtime/request-deadline.d.ts +8 -0
- package/dist/chat/runtime/slack-resume.d.ts +4 -5
- package/dist/chat/runtime/slack-runtime.d.ts +10 -0
- package/dist/chat/runtime/thread-context.d.ts +5 -2
- package/dist/chat/runtime/timeout-resume-runner.d.ts +19 -0
- package/dist/chat/runtime/turn-preparation.d.ts +1 -0
- package/dist/chat/runtime/turn.d.ts +13 -0
- package/dist/chat/sandbox/egress-policy.d.ts +1 -1
- package/dist/chat/sandbox/egress-session.d.ts +13 -12
- package/dist/chat/sandbox/sandbox.d.ts +2 -3
- package/dist/chat/services/timeout-resume.d.ts +9 -4
- package/dist/chat/services/turn-session-record.d.ts +19 -2
- package/dist/chat/slack/adapter-context.d.ts +25 -0
- package/dist/chat/slack/conversation-context.d.ts +28 -0
- package/dist/chat/state/turn-session.d.ts +1 -1
- package/dist/chat/task-execution/heartbeat.d.ts +13 -0
- package/dist/chat/task-execution/queue-signing.d.ts +12 -0
- package/dist/chat/task-execution/queue.d.ts +13 -0
- package/dist/chat/task-execution/slack-work.d.ts +32 -0
- package/dist/chat/task-execution/store.d.ts +153 -0
- package/dist/chat/task-execution/vercel-callback.d.ts +21 -0
- package/dist/chat/task-execution/vercel-queue.d.ts +16 -0
- package/dist/chat/task-execution/worker.d.ts +28 -0
- package/dist/{chunk-WDPWFMCE.js → chunk-657CJCUO.js} +244 -68
- package/dist/chunk-6YY4Q3D4.js +12 -0
- package/dist/chunk-75UZ4JLC.js +213 -0
- package/dist/{chunk-D23WCM66.js → chunk-F57QSMOJ.js} +1 -1
- package/dist/{chunk-WZFQQ6SP.js → chunk-HRQQS63X.js} +2 -2
- package/dist/{chunk-SCQPBJAU.js → chunk-QUXPUKBH.js} +1 -7
- package/dist/{chunk-4CRYMG7M.js → chunk-X2HJLKQT.js} +368 -13
- package/dist/{chunk-IGVHCX2U.js → chunk-ZOV3XJAH.js} +3 -2
- package/dist/cli/check.js +115 -2
- package/dist/cli/init.js +13 -1
- package/dist/cli/snapshot-warmup.js +3 -3
- package/dist/deployment.d.ts +4 -0
- package/dist/handlers/heartbeat.d.ts +5 -1
- package/dist/handlers/turn-resume.d.ts +3 -2
- package/dist/handlers/webhooks.d.ts +11 -9
- package/dist/nitro.d.ts +3 -1
- package/dist/nitro.js +48 -4
- package/dist/reporting.js +13 -16
- package/dist/vercel.d.ts +1 -1
- package/dist/vercel.js +1 -1
- package/package.json +5 -4
- package/dist/chat/services/turn-continuation-response.d.ts +0 -2
- package/dist/chat/slack/turn-continuation-notice.d.ts +0 -8
- package/dist/chunk-5VDO6LSG.js +0 -104
|
@@ -1363,6 +1363,11 @@ function inlineCredentialsSource(credentials) {
|
|
|
1363
1363
|
setDefined(result, "app-id-env", credentials.appIdEnv);
|
|
1364
1364
|
setDefined(result, "private-key-env", credentials.privateKeyEnv);
|
|
1365
1365
|
setDefined(result, "installation-id-env", credentials.installationIdEnv);
|
|
1366
|
+
setDefined(
|
|
1367
|
+
result,
|
|
1368
|
+
"system-read-permissions",
|
|
1369
|
+
credentials.systemReadPermissions
|
|
1370
|
+
);
|
|
1366
1371
|
}
|
|
1367
1372
|
return result;
|
|
1368
1373
|
}
|
|
@@ -1441,6 +1446,103 @@ function inlineManifestSource(manifest) {
|
|
|
1441
1446
|
return result;
|
|
1442
1447
|
}
|
|
1443
1448
|
|
|
1449
|
+
// src/chat/plugins/github-permissions.ts
|
|
1450
|
+
var KNOWN_GITHUB_PERMISSION_SCOPES = /* @__PURE__ */ new Set([
|
|
1451
|
+
"actions",
|
|
1452
|
+
"administration",
|
|
1453
|
+
"checks",
|
|
1454
|
+
"codespaces",
|
|
1455
|
+
"contents",
|
|
1456
|
+
"deployments",
|
|
1457
|
+
"environments",
|
|
1458
|
+
"issues",
|
|
1459
|
+
"metadata",
|
|
1460
|
+
"packages",
|
|
1461
|
+
"pages",
|
|
1462
|
+
"pull_requests",
|
|
1463
|
+
"repository_hooks",
|
|
1464
|
+
"repository_projects",
|
|
1465
|
+
"secret_scanning_alerts",
|
|
1466
|
+
"secrets",
|
|
1467
|
+
"security_events",
|
|
1468
|
+
"statuses",
|
|
1469
|
+
"vulnerability_alerts",
|
|
1470
|
+
"workflows"
|
|
1471
|
+
]);
|
|
1472
|
+
var DEFAULT_GITHUB_SYSTEM_READ_SCOPES = /* @__PURE__ */ new Set([
|
|
1473
|
+
"actions",
|
|
1474
|
+
"checks",
|
|
1475
|
+
"contents",
|
|
1476
|
+
"issues",
|
|
1477
|
+
"metadata",
|
|
1478
|
+
"pull_requests",
|
|
1479
|
+
"statuses"
|
|
1480
|
+
]);
|
|
1481
|
+
function normalizeGitHubPermissionScope(rawScope) {
|
|
1482
|
+
return rawScope.trim().replace(/-/g, "_");
|
|
1483
|
+
}
|
|
1484
|
+
function normalizeGitHubSystemReadPermissionScopes(scopes, context) {
|
|
1485
|
+
return scopes.map((rawScope) => {
|
|
1486
|
+
const scope = normalizeGitHubPermissionScope(rawScope);
|
|
1487
|
+
if (!KNOWN_GITHUB_PERMISSION_SCOPES.has(scope)) {
|
|
1488
|
+
throw new Error(`${context} contains unsupported scope "${rawScope}"`);
|
|
1489
|
+
}
|
|
1490
|
+
return scope;
|
|
1491
|
+
});
|
|
1492
|
+
}
|
|
1493
|
+
function githubCapabilitiesToPermissions(capabilities, pluginName) {
|
|
1494
|
+
const permissions = {};
|
|
1495
|
+
const prefix = `${pluginName}.`;
|
|
1496
|
+
for (const capability of capabilities) {
|
|
1497
|
+
if (!capability.startsWith(prefix)) {
|
|
1498
|
+
throw new Error(`Unsupported GitHub capability: ${capability}`);
|
|
1499
|
+
}
|
|
1500
|
+
const suffix = capability.slice(prefix.length);
|
|
1501
|
+
const lastDot = suffix.lastIndexOf(".");
|
|
1502
|
+
if (lastDot === -1) {
|
|
1503
|
+
throw new Error(`Unsupported GitHub capability: ${capability}`);
|
|
1504
|
+
}
|
|
1505
|
+
const scopeRaw = suffix.slice(0, lastDot);
|
|
1506
|
+
const level = suffix.slice(lastDot + 1);
|
|
1507
|
+
if (level !== "read" && level !== "write") {
|
|
1508
|
+
throw new Error(`Unsupported GitHub capability: ${capability}`);
|
|
1509
|
+
}
|
|
1510
|
+
const scope = normalizeGitHubPermissionScope(scopeRaw);
|
|
1511
|
+
if (!KNOWN_GITHUB_PERMISSION_SCOPES.has(scope)) {
|
|
1512
|
+
throw new Error(`Unsupported GitHub capability: ${capability}`);
|
|
1513
|
+
}
|
|
1514
|
+
const existing = permissions[scope];
|
|
1515
|
+
permissions[scope] = existing === "write" || level === "write" ? "write" : "read";
|
|
1516
|
+
}
|
|
1517
|
+
return permissions;
|
|
1518
|
+
}
|
|
1519
|
+
function githubSystemReadPermissionsFromScopes(scopes) {
|
|
1520
|
+
const readOnly = {
|
|
1521
|
+
metadata: "read"
|
|
1522
|
+
};
|
|
1523
|
+
for (const scope of normalizeGitHubSystemReadPermissionScopes(
|
|
1524
|
+
scopes,
|
|
1525
|
+
"GitHub system read permissions"
|
|
1526
|
+
)) {
|
|
1527
|
+
readOnly[scope] = "read";
|
|
1528
|
+
}
|
|
1529
|
+
return readOnly;
|
|
1530
|
+
}
|
|
1531
|
+
function githubInstallationReadPermissions(permissions, allowedScopes) {
|
|
1532
|
+
const readOnly = {
|
|
1533
|
+
metadata: "read"
|
|
1534
|
+
};
|
|
1535
|
+
for (const [scope, level] of Object.entries(permissions ?? {})) {
|
|
1536
|
+
if (!allowedScopes.has(scope) || !KNOWN_GITHUB_PERMISSION_SCOPES.has(scope)) {
|
|
1537
|
+
continue;
|
|
1538
|
+
}
|
|
1539
|
+
if (level === "read" || level === "write" || level === "admin") {
|
|
1540
|
+
readOnly[scope] = "read";
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
return readOnly;
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1444
1546
|
// src/chat/plugins/manifest.ts
|
|
1445
1547
|
var PLUGIN_NAME_RE = /^[a-z][a-z0-9-]*$/;
|
|
1446
1548
|
var SHORT_CAPABILITY_RE = /^[a-z0-9-]+(\.[a-z0-9-]+)*$/;
|
|
@@ -1580,7 +1682,10 @@ var githubAppCredentialsSchema = baseCredentialsSchema.extend({
|
|
|
1580
1682
|
type: z.literal("github-app"),
|
|
1581
1683
|
"app-id-env": envVarString,
|
|
1582
1684
|
"private-key-env": envVarString,
|
|
1583
|
-
"installation-id-env": envVarString
|
|
1685
|
+
"installation-id-env": envVarString,
|
|
1686
|
+
"system-read-permissions": nonEmptyStringArraySchema(
|
|
1687
|
+
"system-read-permissions"
|
|
1688
|
+
).optional()
|
|
1584
1689
|
});
|
|
1585
1690
|
var runtimeDependencyEntrySchema = z.object({
|
|
1586
1691
|
type: z.enum(["npm", "system"]),
|
|
@@ -1704,6 +1809,11 @@ function manifestConfigPatch(config) {
|
|
|
1704
1809
|
"installation-id-env",
|
|
1705
1810
|
config.credentials.installationIdEnv
|
|
1706
1811
|
);
|
|
1812
|
+
setDefined2(
|
|
1813
|
+
credentials,
|
|
1814
|
+
"system-read-permissions",
|
|
1815
|
+
config.credentials.systemReadPermissions
|
|
1816
|
+
);
|
|
1707
1817
|
result.credentials = credentials;
|
|
1708
1818
|
}
|
|
1709
1819
|
}
|
|
@@ -1913,6 +2023,21 @@ function assertCommandEnvDoesNotExposeHostSecretRefs(commandEnv, apiHeaders, cre
|
|
|
1913
2023
|
}
|
|
1914
2024
|
}
|
|
1915
2025
|
}
|
|
2026
|
+
function assertCommandEnvHostRefsAreExplicitlyExposed(commandEnv, envVars, pluginName) {
|
|
2027
|
+
if (!commandEnv) {
|
|
2028
|
+
return;
|
|
2029
|
+
}
|
|
2030
|
+
for (const [key, value] of Object.entries(commandEnv)) {
|
|
2031
|
+
for (const name of envReferences(value)) {
|
|
2032
|
+
const declaration = envVars[name];
|
|
2033
|
+
if (declaration && declaration.default === void 0 && declaration.exposeToCommandEnv !== true) {
|
|
2034
|
+
throw new Error(
|
|
2035
|
+
`Plugin ${pluginName} command-env.${key} references env var ${name}, but env-vars.${name} must set expose-to-command-env: true before host env can be exposed to sandbox`
|
|
2036
|
+
);
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
1916
2041
|
function normalizeCredentials(data, name) {
|
|
1917
2042
|
const schema = data.type === "oauth-bearer" ? oauthBearerCredentialsSchema : data.type === "github-app" ? githubAppCredentialsSchema : void 0;
|
|
1918
2043
|
if (!schema) {
|
|
@@ -1947,6 +2072,10 @@ function normalizeCredentials(data, name) {
|
|
|
1947
2072
|
`Plugin ${name} credentials.api-headers`,
|
|
1948
2073
|
{ forbiddenKeys: FORBIDDEN_API_HEADER_NAMES }
|
|
1949
2074
|
) : void 0;
|
|
2075
|
+
const systemReadPermissions = result.data["system-read-permissions"] ? normalizeGitHubSystemReadPermissionScopes(
|
|
2076
|
+
result.data["system-read-permissions"],
|
|
2077
|
+
`Plugin ${name} credentials.system-read-permissions`
|
|
2078
|
+
) : void 0;
|
|
1950
2079
|
return {
|
|
1951
2080
|
type: "github-app",
|
|
1952
2081
|
domains,
|
|
@@ -1955,7 +2084,8 @@ function normalizeCredentials(data, name) {
|
|
|
1955
2084
|
...result.data["auth-token-placeholder"] ? { authTokenPlaceholder: result.data["auth-token-placeholder"] } : {},
|
|
1956
2085
|
appIdEnv: result.data["app-id-env"],
|
|
1957
2086
|
privateKeyEnv: result.data["private-key-env"],
|
|
1958
|
-
installationIdEnv: result.data["installation-id-env"]
|
|
2087
|
+
installationIdEnv: result.data["installation-id-env"],
|
|
2088
|
+
...systemReadPermissions ? { systemReadPermissions } : {}
|
|
1959
2089
|
};
|
|
1960
2090
|
}
|
|
1961
2091
|
function normalizeRuntimeDependencies(entries, name) {
|
|
@@ -2087,7 +2217,9 @@ function normalizeRuntimePostinstall(commands, name) {
|
|
|
2087
2217
|
var envVarDeclarationSchema = z.preprocess(
|
|
2088
2218
|
(value) => value === null || value === void 0 ? {} : value,
|
|
2089
2219
|
z.object({
|
|
2090
|
-
default: z.string().optional()
|
|
2220
|
+
default: z.string().optional(),
|
|
2221
|
+
"expose-to-command-env": z.boolean().optional(),
|
|
2222
|
+
exposeToCommandEnv: z.boolean().optional()
|
|
2091
2223
|
}).strict()
|
|
2092
2224
|
);
|
|
2093
2225
|
function normalizeEnvVars(data, pluginName) {
|
|
@@ -2109,6 +2241,9 @@ function normalizeEnvVars(data, pluginName) {
|
|
|
2109
2241
|
if (parsed.data.default !== void 0) {
|
|
2110
2242
|
decl.default = parsed.data.default;
|
|
2111
2243
|
}
|
|
2244
|
+
if (parsed.data["expose-to-command-env"] === true || parsed.data.exposeToCommandEnv === true) {
|
|
2245
|
+
decl.exposeToCommandEnv = true;
|
|
2246
|
+
}
|
|
2112
2247
|
normalized[name] = decl;
|
|
2113
2248
|
}
|
|
2114
2249
|
return normalized;
|
|
@@ -2267,11 +2402,6 @@ function parseManifestSource(parsedSource, dir, config) {
|
|
|
2267
2402
|
envVars
|
|
2268
2403
|
) : void 0;
|
|
2269
2404
|
const credentials = data.credentials ? normalizeCredentials(data.credentials, data.name) : void 0;
|
|
2270
|
-
if (commandEnv && !credentials && !apiHeaders) {
|
|
2271
|
-
throw new Error(
|
|
2272
|
-
`Plugin ${data.name} command-env requires credentials or api-headers`
|
|
2273
|
-
);
|
|
2274
|
-
}
|
|
2275
2405
|
const runtimeDependencies = data["runtime-dependencies"] ? normalizeRuntimeDependencies(data["runtime-dependencies"], data.name) : void 0;
|
|
2276
2406
|
const runtimePostinstall = data["runtime-postinstall"] ? normalizeRuntimePostinstall(data["runtime-postinstall"], data.name) : void 0;
|
|
2277
2407
|
const mcp = data.mcp ? normalizeMcp(data.mcp, envVars, data.name) : void 0;
|
|
@@ -2334,6 +2464,11 @@ function parseManifestSource(parsedSource, dir, config) {
|
|
|
2334
2464
|
manifest.oauth,
|
|
2335
2465
|
data.name
|
|
2336
2466
|
);
|
|
2467
|
+
assertCommandEnvHostRefsAreExplicitlyExposed(
|
|
2468
|
+
data["command-env"],
|
|
2469
|
+
envVars,
|
|
2470
|
+
data.name
|
|
2471
|
+
);
|
|
2337
2472
|
if (data.target) {
|
|
2338
2473
|
const result = targetSourceSchema.safeParse(data.target);
|
|
2339
2474
|
if (!result.success) {
|
|
@@ -2600,54 +2735,6 @@ function resolveGitHubApiDomain(credentials) {
|
|
|
2600
2735
|
}
|
|
2601
2736
|
return apiDomain;
|
|
2602
2737
|
}
|
|
2603
|
-
var KNOWN_SCOPES = /* @__PURE__ */ new Set([
|
|
2604
|
-
"actions",
|
|
2605
|
-
"administration",
|
|
2606
|
-
"checks",
|
|
2607
|
-
"codespaces",
|
|
2608
|
-
"contents",
|
|
2609
|
-
"deployments",
|
|
2610
|
-
"environments",
|
|
2611
|
-
"issues",
|
|
2612
|
-
"metadata",
|
|
2613
|
-
"packages",
|
|
2614
|
-
"pages",
|
|
2615
|
-
"pull_requests",
|
|
2616
|
-
"repository_hooks",
|
|
2617
|
-
"repository_projects",
|
|
2618
|
-
"secret_scanning_alerts",
|
|
2619
|
-
"secrets",
|
|
2620
|
-
"security_events",
|
|
2621
|
-
"statuses",
|
|
2622
|
-
"vulnerability_alerts",
|
|
2623
|
-
"workflows"
|
|
2624
|
-
]);
|
|
2625
|
-
function capabilitiesToPermissions(capabilities, pluginName) {
|
|
2626
|
-
const permissions = {};
|
|
2627
|
-
const prefix = `${pluginName}.`;
|
|
2628
|
-
for (const capability of capabilities) {
|
|
2629
|
-
if (!capability.startsWith(prefix)) {
|
|
2630
|
-
throw new Error(`Unsupported GitHub capability: ${capability}`);
|
|
2631
|
-
}
|
|
2632
|
-
const suffix = capability.slice(prefix.length);
|
|
2633
|
-
const lastDot = suffix.lastIndexOf(".");
|
|
2634
|
-
if (lastDot === -1) {
|
|
2635
|
-
throw new Error(`Unsupported GitHub capability: ${capability}`);
|
|
2636
|
-
}
|
|
2637
|
-
const scopeRaw = suffix.slice(0, lastDot);
|
|
2638
|
-
const level = suffix.slice(lastDot + 1);
|
|
2639
|
-
if (level !== "read" && level !== "write") {
|
|
2640
|
-
throw new Error(`Unsupported GitHub capability: ${capability}`);
|
|
2641
|
-
}
|
|
2642
|
-
const scope = scopeRaw.replace(/-/g, "_");
|
|
2643
|
-
if (!KNOWN_SCOPES.has(scope)) {
|
|
2644
|
-
throw new Error(`Unsupported GitHub capability: ${capability}`);
|
|
2645
|
-
}
|
|
2646
|
-
const existing = permissions[scope];
|
|
2647
|
-
permissions[scope] = existing === "write" || level === "write" ? "write" : "read";
|
|
2648
|
-
}
|
|
2649
|
-
return permissions;
|
|
2650
|
-
}
|
|
2651
2738
|
function createGitHubAppBroker(manifest, credentials) {
|
|
2652
2739
|
const provider = manifest.name;
|
|
2653
2740
|
const {
|
|
@@ -2674,7 +2761,23 @@ function createGitHubAppBroker(manifest, credentials) {
|
|
|
2674
2761
|
const normalizedApiDomain = apiDomain.toLowerCase();
|
|
2675
2762
|
return normalizedDomain === "github.com" || normalizedApiDomain.startsWith("api.") && normalizedDomain === normalizedApiDomain.slice("api.".length);
|
|
2676
2763
|
}
|
|
2677
|
-
const permissions = manifest.capabilities?.length ?
|
|
2764
|
+
const permissions = manifest.capabilities?.length ? githubCapabilitiesToPermissions(manifest.capabilities, provider) : void 0;
|
|
2765
|
+
const systemReadPermissions = credentials.systemReadPermissions?.length ? githubSystemReadPermissionsFromScopes(credentials.systemReadPermissions) : void 0;
|
|
2766
|
+
async function resolveTokenPermissions(params) {
|
|
2767
|
+
if (!params.systemActor) {
|
|
2768
|
+
return permissions;
|
|
2769
|
+
}
|
|
2770
|
+
if (systemReadPermissions) {
|
|
2771
|
+
return systemReadPermissions;
|
|
2772
|
+
}
|
|
2773
|
+
const installation = await githubRequest(apiBase, `/app/installations/${params.installationId}`, {
|
|
2774
|
+
token: params.appJwt
|
|
2775
|
+
});
|
|
2776
|
+
return githubInstallationReadPermissions(
|
|
2777
|
+
installation.permissions,
|
|
2778
|
+
DEFAULT_GITHUB_SYSTEM_READ_SCOPES
|
|
2779
|
+
);
|
|
2780
|
+
}
|
|
2678
2781
|
function createLease(params) {
|
|
2679
2782
|
return {
|
|
2680
2783
|
id: randomUUID2(),
|
|
@@ -2714,9 +2817,14 @@ function createGitHubAppBroker(manifest, credentials) {
|
|
|
2714
2817
|
return {
|
|
2715
2818
|
async issue(input) {
|
|
2716
2819
|
const installationId = resolveInstallationId();
|
|
2717
|
-
const tokenRequestBody = permissions ? { permissions } : {};
|
|
2718
2820
|
const appId = resolveAppId(appIdEnv);
|
|
2719
2821
|
const appJwt = createAppJwt(appId, privateKeyEnv);
|
|
2822
|
+
const tokenPermissions = await resolveTokenPermissions({
|
|
2823
|
+
appJwt,
|
|
2824
|
+
installationId,
|
|
2825
|
+
systemActor: input.context.actor.type === "system"
|
|
2826
|
+
});
|
|
2827
|
+
const tokenRequestBody = tokenPermissions ? { permissions: tokenPermissions } : {};
|
|
2720
2828
|
const accessTokenResponse = await githubRequest(apiBase, `/app/installations/${installationId}/access_tokens`, {
|
|
2721
2829
|
method: "POST",
|
|
2722
2830
|
token: appJwt,
|
|
@@ -2750,6 +2858,79 @@ var CredentialUnavailableError = class extends Error {
|
|
|
2750
2858
|
}
|
|
2751
2859
|
};
|
|
2752
2860
|
|
|
2861
|
+
// src/chat/credentials/context.ts
|
|
2862
|
+
function credentialUserSubjectId(context) {
|
|
2863
|
+
if (context.actor.type === "user") {
|
|
2864
|
+
return context.actor.userId;
|
|
2865
|
+
}
|
|
2866
|
+
return context.subject?.userId;
|
|
2867
|
+
}
|
|
2868
|
+
function parseCredentialContext(value) {
|
|
2869
|
+
if (!value || typeof value !== "object") {
|
|
2870
|
+
return void 0;
|
|
2871
|
+
}
|
|
2872
|
+
const record = value;
|
|
2873
|
+
const actor = parseActor(record.actor);
|
|
2874
|
+
if (!actor) {
|
|
2875
|
+
return void 0;
|
|
2876
|
+
}
|
|
2877
|
+
if (actor.type === "user") {
|
|
2878
|
+
if ("subject" in record && record.subject !== void 0) {
|
|
2879
|
+
return void 0;
|
|
2880
|
+
}
|
|
2881
|
+
return { actor };
|
|
2882
|
+
}
|
|
2883
|
+
if (!("subject" in record) || record.subject === void 0) {
|
|
2884
|
+
return { actor };
|
|
2885
|
+
}
|
|
2886
|
+
const subject = parseSubject(record.subject);
|
|
2887
|
+
if (!subject) {
|
|
2888
|
+
return void 0;
|
|
2889
|
+
}
|
|
2890
|
+
return {
|
|
2891
|
+
actor,
|
|
2892
|
+
subject
|
|
2893
|
+
};
|
|
2894
|
+
}
|
|
2895
|
+
function parseActor(value) {
|
|
2896
|
+
if (value && typeof value === "object") {
|
|
2897
|
+
const record = value;
|
|
2898
|
+
if (record.type === "user" && typeof record.userId === "string" && record.userId) {
|
|
2899
|
+
return { type: "user", userId: record.userId };
|
|
2900
|
+
}
|
|
2901
|
+
if (record.type === "system" && typeof record.id === "string" && record.id) {
|
|
2902
|
+
return { type: "system", id: record.id };
|
|
2903
|
+
}
|
|
2904
|
+
}
|
|
2905
|
+
return void 0;
|
|
2906
|
+
}
|
|
2907
|
+
function parseSubject(value) {
|
|
2908
|
+
if (value && typeof value === "object") {
|
|
2909
|
+
const record = value;
|
|
2910
|
+
if (record.type === "user" && typeof record.userId === "string" && record.userId && record.allowedWhen === "private-direct-conversation") {
|
|
2911
|
+
if (!record.binding || typeof record.binding !== "object") {
|
|
2912
|
+
return void 0;
|
|
2913
|
+
}
|
|
2914
|
+
const binding = record.binding;
|
|
2915
|
+
if (binding.type !== "slack-direct-conversation" || typeof binding.teamId !== "string" || !binding.teamId || typeof binding.channelId !== "string" || !binding.channelId || typeof binding.signature !== "string" || !binding.signature) {
|
|
2916
|
+
return void 0;
|
|
2917
|
+
}
|
|
2918
|
+
return {
|
|
2919
|
+
type: "user",
|
|
2920
|
+
userId: record.userId,
|
|
2921
|
+
allowedWhen: "private-direct-conversation",
|
|
2922
|
+
binding: {
|
|
2923
|
+
type: "slack-direct-conversation",
|
|
2924
|
+
teamId: binding.teamId,
|
|
2925
|
+
channelId: binding.channelId,
|
|
2926
|
+
signature: binding.signature
|
|
2927
|
+
}
|
|
2928
|
+
};
|
|
2929
|
+
}
|
|
2930
|
+
}
|
|
2931
|
+
return void 0;
|
|
2932
|
+
}
|
|
2933
|
+
|
|
2753
2934
|
// src/chat/credentials/oauth-scope.ts
|
|
2754
2935
|
function parseScope(scope) {
|
|
2755
2936
|
if (!scope) {
|
|
@@ -2911,6 +3092,7 @@ function createOAuthBearerBroker(manifest, credentials, deps) {
|
|
|
2911
3092
|
async issue(input) {
|
|
2912
3093
|
const envToken = process.env[authTokenEnv]?.trim();
|
|
2913
3094
|
const oauth = manifest.oauth;
|
|
3095
|
+
const userSubjectId = credentialUserSubjectId(input.context);
|
|
2914
3096
|
if (!oauth) {
|
|
2915
3097
|
if (envToken) {
|
|
2916
3098
|
return buildLease(envToken, Date.now() + MAX_LEASE_MS3, input.reason);
|
|
@@ -2920,11 +3102,8 @@ function createOAuthBearerBroker(manifest, credentials, deps) {
|
|
|
2920
3102
|
`No ${provider} credentials available.`
|
|
2921
3103
|
);
|
|
2922
3104
|
}
|
|
2923
|
-
if (
|
|
2924
|
-
const stored = await deps.userTokenStore.get(
|
|
2925
|
-
input.requesterId,
|
|
2926
|
-
provider
|
|
2927
|
-
);
|
|
3105
|
+
if (userSubjectId) {
|
|
3106
|
+
const stored = await deps.userTokenStore.get(userSubjectId, provider);
|
|
2928
3107
|
if (stored) {
|
|
2929
3108
|
if (!hasRequiredOAuthScope(stored.scope, oauth.scope)) {
|
|
2930
3109
|
throw new CredentialUnavailableError(
|
|
@@ -2946,11 +3125,7 @@ function createOAuthBearerBroker(manifest, credentials, deps) {
|
|
|
2946
3125
|
`Your ${provider} connection needs to be reauthorized.`
|
|
2947
3126
|
);
|
|
2948
3127
|
}
|
|
2949
|
-
await deps.userTokenStore.set(
|
|
2950
|
-
input.requesterId,
|
|
2951
|
-
provider,
|
|
2952
|
-
refreshed
|
|
2953
|
-
);
|
|
3128
|
+
await deps.userTokenStore.set(userSubjectId, provider, refreshed);
|
|
2954
3129
|
return buildLease(
|
|
2955
3130
|
refreshed.accessToken,
|
|
2956
3131
|
getLeaseExpiry(refreshed.expiresAt),
|
|
@@ -3442,6 +3617,7 @@ export {
|
|
|
3442
3617
|
resolveAuthTokenPlaceholder,
|
|
3443
3618
|
parsePluginManifest,
|
|
3444
3619
|
CredentialUnavailableError,
|
|
3620
|
+
parseCredentialContext,
|
|
3445
3621
|
hasRequiredOAuthScope,
|
|
3446
3622
|
buildOAuthTokenRequest,
|
|
3447
3623
|
parseOAuthTokenResponse,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// src/deployment.ts
|
|
2
|
+
var JUNIOR_HEARTBEAT_ROUTE = "/api/internal/heartbeat";
|
|
3
|
+
var JUNIOR_HEARTBEAT_CRON_SCHEDULE = "* * * * *";
|
|
4
|
+
var JUNIOR_CONVERSATION_WORK_CALLBACK_ROUTE = "/api/internal/agent/continue";
|
|
5
|
+
var LEGACY_JUNIOR_CONVERSATION_WORK_FUNCTION = "api/internal/agent/continue.ts";
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
JUNIOR_HEARTBEAT_ROUTE,
|
|
9
|
+
JUNIOR_HEARTBEAT_CRON_SCHEDULE,
|
|
10
|
+
JUNIOR_CONVERSATION_WORK_CALLBACK_ROUTE,
|
|
11
|
+
LEGACY_JUNIOR_CONVERSATION_WORK_FUNCTION
|
|
12
|
+
};
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
// src/plugins.ts
|
|
2
|
+
function cloneManifests(manifests) {
|
|
3
|
+
return manifests ? structuredClone(manifests) : void 0;
|
|
4
|
+
}
|
|
5
|
+
function cloneInlineManifests(registrations) {
|
|
6
|
+
const inlineManifests = registrations.flatMap(
|
|
7
|
+
(plugin) => plugin.manifest ? [
|
|
8
|
+
{
|
|
9
|
+
manifest: {
|
|
10
|
+
...structuredClone(plugin.manifest),
|
|
11
|
+
capabilities: plugin.manifest.capabilities?.map(
|
|
12
|
+
(capability) => capability.includes(".") ? capability : `${plugin.manifest.name}.${capability}`
|
|
13
|
+
) ?? [],
|
|
14
|
+
configKeys: plugin.manifest.configKeys?.map(
|
|
15
|
+
(key) => key.includes(".") ? key : `${plugin.manifest.name}.${key}`
|
|
16
|
+
) ?? [],
|
|
17
|
+
...plugin.manifest.target ? {
|
|
18
|
+
target: {
|
|
19
|
+
...plugin.manifest.target,
|
|
20
|
+
configKey: plugin.manifest.target.configKey.includes(".") ? plugin.manifest.target.configKey : `${plugin.manifest.name}.${plugin.manifest.target.configKey}`
|
|
21
|
+
}
|
|
22
|
+
} : {}
|
|
23
|
+
},
|
|
24
|
+
...plugin.packageName ? { packageName: plugin.packageName } : {}
|
|
25
|
+
}
|
|
26
|
+
] : []
|
|
27
|
+
);
|
|
28
|
+
return inlineManifests.length > 0 ? inlineManifests : void 0;
|
|
29
|
+
}
|
|
30
|
+
function assertUniquePluginNames(registrations) {
|
|
31
|
+
const seen = /* @__PURE__ */ new Set();
|
|
32
|
+
for (const plugin of registrations) {
|
|
33
|
+
if (seen.has(plugin.name)) {
|
|
34
|
+
throw new Error(`Duplicate plugin registration name "${plugin.name}"`);
|
|
35
|
+
}
|
|
36
|
+
seen.add(plugin.name);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function assertUniquePackageNames(packageNames) {
|
|
40
|
+
const seen = /* @__PURE__ */ new Set();
|
|
41
|
+
for (const packageName of packageNames) {
|
|
42
|
+
if (seen.has(packageName)) {
|
|
43
|
+
throw new Error(`Duplicate plugin package name "${packageName}"`);
|
|
44
|
+
}
|
|
45
|
+
seen.add(packageName);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function normalizePluginInput(input) {
|
|
49
|
+
if (typeof input === "string") {
|
|
50
|
+
return { packageName: input };
|
|
51
|
+
}
|
|
52
|
+
return { registration: input };
|
|
53
|
+
}
|
|
54
|
+
function defineJuniorPlugins(inputs, options = {}) {
|
|
55
|
+
const normalized = inputs.map(normalizePluginInput);
|
|
56
|
+
const packageNames = normalized.flatMap(
|
|
57
|
+
(input) => input.packageName ? [input.packageName] : []
|
|
58
|
+
);
|
|
59
|
+
const registrations = normalized.flatMap(
|
|
60
|
+
(input) => input.registration ? [input.registration] : []
|
|
61
|
+
);
|
|
62
|
+
assertUniquePackageNames(packageNames);
|
|
63
|
+
assertUniquePluginNames(registrations);
|
|
64
|
+
const manifests = cloneManifests(options.manifests);
|
|
65
|
+
return {
|
|
66
|
+
packageNames,
|
|
67
|
+
registrations: registrations.map((plugin) => ({ ...plugin })),
|
|
68
|
+
...manifests ? { manifests } : {}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function pluginCatalogConfigFromPluginSet(pluginSet) {
|
|
72
|
+
if (!pluginSet) {
|
|
73
|
+
return void 0;
|
|
74
|
+
}
|
|
75
|
+
const packages = [
|
|
76
|
+
.../* @__PURE__ */ new Set([
|
|
77
|
+
...pluginSet.packageNames,
|
|
78
|
+
...pluginSet.registrations.flatMap(
|
|
79
|
+
(plugin) => plugin.packageName ? [plugin.packageName] : []
|
|
80
|
+
)
|
|
81
|
+
])
|
|
82
|
+
];
|
|
83
|
+
const manifests = cloneManifests(pluginSet.manifests);
|
|
84
|
+
const inlineManifests = cloneInlineManifests(pluginSet.registrations);
|
|
85
|
+
if (packages.length === 0 && !manifests && !inlineManifests) {
|
|
86
|
+
return void 0;
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
...inlineManifests ? { inlineManifests } : {},
|
|
90
|
+
...packages.length > 0 ? { packages } : {},
|
|
91
|
+
...manifests ? { manifests } : {}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function trustedPluginRegistrationsFromPluginSet(pluginSet) {
|
|
95
|
+
return pluginSet?.registrations.filter(
|
|
96
|
+
(plugin) => plugin.hooks || plugin.legacyStatePrefixes
|
|
97
|
+
) ?? [];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// src/chat/task-execution/vercel-queue.ts
|
|
101
|
+
import { QueueClient } from "@vercel/queue";
|
|
102
|
+
|
|
103
|
+
// src/chat/task-execution/queue-signing.ts
|
|
104
|
+
import { createHmac, timingSafeEqual } from "crypto";
|
|
105
|
+
var CONVERSATION_WORK_QUEUE_SIGNATURE_CONTEXT = "junior.conversation_work_queue.v1";
|
|
106
|
+
var CONVERSATION_WORK_QUEUE_SIGNATURE_VERSION = "v1";
|
|
107
|
+
var CONVERSATION_WORK_QUEUE_SIGNATURE_MAX_SKEW_MS = 60 * 60 * 1e3;
|
|
108
|
+
function getConversationWorkQueueSecret() {
|
|
109
|
+
return process.env.JUNIOR_SECRET?.trim() || void 0;
|
|
110
|
+
}
|
|
111
|
+
function buildSignedPayload(message, signedAtMs) {
|
|
112
|
+
return [
|
|
113
|
+
CONVERSATION_WORK_QUEUE_SIGNATURE_CONTEXT,
|
|
114
|
+
signedAtMs,
|
|
115
|
+
message.conversationId
|
|
116
|
+
].join(":");
|
|
117
|
+
}
|
|
118
|
+
function signPayload(message, signedAtMs, secret) {
|
|
119
|
+
return createHmac("sha256", secret).update(buildSignedPayload(message, signedAtMs)).digest("hex");
|
|
120
|
+
}
|
|
121
|
+
function timingSafeMatch(expected, actual) {
|
|
122
|
+
const expectedBuffer = Buffer.from(expected);
|
|
123
|
+
const actualBuffer = Buffer.from(actual);
|
|
124
|
+
if (expectedBuffer.length !== actualBuffer.length) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
return timingSafeEqual(expectedBuffer, actualBuffer);
|
|
128
|
+
}
|
|
129
|
+
function parseSignedConversationQueueMessage(value) {
|
|
130
|
+
if (!value || typeof value !== "object") {
|
|
131
|
+
return void 0;
|
|
132
|
+
}
|
|
133
|
+
const record = value;
|
|
134
|
+
if (typeof record.conversationId !== "string" || !record.conversationId.trim() || record.signatureVersion !== CONVERSATION_WORK_QUEUE_SIGNATURE_VERSION || typeof record.signedAtMs !== "number" || !Number.isFinite(record.signedAtMs) || typeof record.signature !== "string" || !record.signature.trim()) {
|
|
135
|
+
return void 0;
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
conversationId: record.conversationId,
|
|
139
|
+
signature: record.signature,
|
|
140
|
+
signatureVersion: CONVERSATION_WORK_QUEUE_SIGNATURE_VERSION,
|
|
141
|
+
signedAtMs: record.signedAtMs
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function signConversationQueueMessage(message, nowMs = Date.now()) {
|
|
145
|
+
const secret = getConversationWorkQueueSecret();
|
|
146
|
+
if (!secret) {
|
|
147
|
+
throw new Error(
|
|
148
|
+
"Cannot sign conversation queue message without JUNIOR_SECRET"
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
...message,
|
|
153
|
+
signedAtMs: nowMs,
|
|
154
|
+
signatureVersion: CONVERSATION_WORK_QUEUE_SIGNATURE_VERSION,
|
|
155
|
+
signature: signPayload(message, nowMs, secret)
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
function verifySignedConversationQueueMessage(value, nowMs = Date.now()) {
|
|
159
|
+
const message = parseSignedConversationQueueMessage(value);
|
|
160
|
+
const secret = getConversationWorkQueueSecret();
|
|
161
|
+
if (!message || !secret || !Number.isFinite(nowMs) || Math.abs(nowMs - message.signedAtMs) > CONVERSATION_WORK_QUEUE_SIGNATURE_MAX_SKEW_MS) {
|
|
162
|
+
return void 0;
|
|
163
|
+
}
|
|
164
|
+
const expected = signPayload(message, message.signedAtMs, secret);
|
|
165
|
+
if (!timingSafeMatch(expected, message.signature)) {
|
|
166
|
+
return void 0;
|
|
167
|
+
}
|
|
168
|
+
return { conversationId: message.conversationId };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// src/chat/task-execution/vercel-queue.ts
|
|
172
|
+
var DEFAULT_CONVERSATION_WORK_QUEUE_TOPIC = "junior_conversation_work";
|
|
173
|
+
var defaultQueue;
|
|
174
|
+
function getTopic(options) {
|
|
175
|
+
return options.topic || process.env.JUNIOR_CONVERSATION_WORK_QUEUE_TOPIC?.trim() || DEFAULT_CONVERSATION_WORK_QUEUE_TOPIC;
|
|
176
|
+
}
|
|
177
|
+
function toDelaySeconds(options) {
|
|
178
|
+
if (!options?.delayMs || options.delayMs <= 0) {
|
|
179
|
+
return void 0;
|
|
180
|
+
}
|
|
181
|
+
return Math.ceil(options.delayMs / 1e3);
|
|
182
|
+
}
|
|
183
|
+
function createVercelConversationWorkQueue(options = {}) {
|
|
184
|
+
const topic = getTopic(options);
|
|
185
|
+
const client = options.client ?? new QueueClient();
|
|
186
|
+
return {
|
|
187
|
+
async send(message, sendOptions) {
|
|
188
|
+
const result = await client.send(
|
|
189
|
+
topic,
|
|
190
|
+
signConversationQueueMessage(message),
|
|
191
|
+
{
|
|
192
|
+
idempotencyKey: sendOptions?.idempotencyKey,
|
|
193
|
+
delaySeconds: toDelaySeconds(sendOptions),
|
|
194
|
+
retentionSeconds: options.retentionSeconds
|
|
195
|
+
}
|
|
196
|
+
);
|
|
197
|
+
return result.messageId ? { messageId: result.messageId } : {};
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function getVercelConversationWorkQueue() {
|
|
202
|
+
defaultQueue ??= createVercelConversationWorkQueue();
|
|
203
|
+
return defaultQueue;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export {
|
|
207
|
+
defineJuniorPlugins,
|
|
208
|
+
pluginCatalogConfigFromPluginSet,
|
|
209
|
+
trustedPluginRegistrationsFromPluginSet,
|
|
210
|
+
verifySignedConversationQueueMessage,
|
|
211
|
+
DEFAULT_CONVERSATION_WORK_QUEUE_TOPIC,
|
|
212
|
+
getVercelConversationWorkQueue
|
|
213
|
+
};
|
|
@@ -2,12 +2,12 @@ import {
|
|
|
2
2
|
SANDBOX_WORKSPACE_ROOT,
|
|
3
3
|
getStateAdapter,
|
|
4
4
|
toOptionalTrimmed
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-ZOV3XJAH.js";
|
|
6
6
|
import {
|
|
7
7
|
getPluginRuntimeDependencies,
|
|
8
8
|
getPluginRuntimePostinstall,
|
|
9
9
|
withSpan
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-657CJCUO.js";
|
|
11
11
|
|
|
12
12
|
// src/chat/sandbox/runtime-dependency-snapshots.ts
|
|
13
13
|
import { createHash } from "crypto";
|