pikibun 0.23.1 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/{chunk-NAPYFPGG.js → chunk-3IXKVGAD.js} +3177 -1120
- package/dist/chunks/{cli-DRWVFHEJ.js → cli-COW3WXMB.js} +208 -47
- package/dist/cli.js +10 -9
- package/dist/migrations/postgres/0045_connector_version.sql +6 -0
- package/dist/migrations/postgres/0046_session_tunnels.sql +12 -0
- package/dist/migrations/postgres/0047_native_page_callback.sql +4 -0
- package/dist/migrations/sqlite/0052_connector_version.sql +4 -0
- package/dist/migrations/sqlite/0053_session_tunnels.sql +10 -0
- package/dist/migrations/sqlite/0054_native_page_callback.sql +4 -0
- package/dist/web/app.eb3752a36c.js +14 -0
- package/dist/web/app.eb3752a36c.js.br +0 -0
- package/dist/web/app.eb3752a36c.js.gz +0 -0
- package/dist/web/assets/src/assets/logo.5872d6d67c.png +0 -0
- package/dist/web/browser.6333ad295d/LICENSES.txt +83 -0
- package/dist/web/browser.6333ad295d/LICENSES.txt.br +0 -0
- package/dist/web/browser.6333ad295d/LICENSES.txt.gz +0 -0
- package/dist/web/browser.6333ad295d/animation.js +1 -0
- package/dist/web/browser.6333ad295d/animation.js.br +0 -0
- package/dist/web/browser.6333ad295d/animation.js.gz +0 -0
- package/dist/web/browser.6333ad295d/bootstrap.js +1 -0
- package/dist/web/browser.6333ad295d/buffer.js +1 -0
- package/dist/web/browser.6333ad295d/buffer.js.br +0 -0
- package/dist/web/browser.6333ad295d/buffer.js.gz +0 -0
- package/dist/web/browser.6333ad295d/canvas.js +1 -0
- package/dist/web/browser.6333ad295d/canvas.js.br +0 -0
- package/dist/web/browser.6333ad295d/canvas.js.gz +0 -0
- package/dist/web/browser.6333ad295d/components.js +1 -0
- package/dist/web/browser.6333ad295d/components.js.br +0 -0
- package/dist/web/browser.6333ad295d/components.js.gz +0 -0
- package/dist/web/browser.6333ad295d/dom.js +1 -0
- package/dist/web/browser.6333ad295d/dom.js.br +0 -0
- package/dist/web/browser.6333ad295d/dom.js.gz +0 -0
- package/dist/web/browser.6333ad295d/environment.js +1 -0
- package/dist/web/browser.6333ad295d/environment.js.br +0 -0
- package/dist/web/browser.6333ad295d/environment.js.gz +0 -0
- package/dist/web/browser.6333ad295d/gesture.js +1 -0
- package/dist/web/browser.6333ad295d/gesture.js.br +0 -0
- package/dist/web/browser.6333ad295d/gesture.js.gz +0 -0
- package/dist/web/browser.6333ad295d/host.js +1 -0
- package/dist/web/browser.6333ad295d/host.js.br +0 -0
- package/dist/web/browser.6333ad295d/host.js.gz +0 -0
- package/dist/web/browser.6333ad295d/hot.js +1 -0
- package/dist/web/browser.6333ad295d/hot.js.br +0 -0
- package/dist/web/browser.6333ad295d/hot.js.gz +0 -0
- package/dist/web/browser.6333ad295d/keymap.js +1 -0
- package/dist/web/browser.6333ad295d/keymap.js.br +0 -0
- package/dist/web/browser.6333ad295d/keymap.js.gz +0 -0
- package/dist/web/browser.6333ad295d/list.js +1 -0
- package/dist/web/browser.6333ad295d/list.js.br +0 -0
- package/dist/web/browser.6333ad295d/list.js.gz +0 -0
- package/dist/web/browser.6333ad295d/loader.js +1 -0
- package/dist/web/browser.6333ad295d/modules.js +1 -0
- package/dist/web/browser.6333ad295d/modules.js.br +0 -0
- package/dist/web/browser.6333ad295d/modules.js.gz +0 -0
- package/dist/web/browser.6333ad295d/navigation.js +1 -0
- package/dist/web/browser.6333ad295d/navigation.js.br +0 -0
- package/dist/web/browser.6333ad295d/navigation.js.gz +0 -0
- package/dist/web/browser.6333ad295d/path.js +1 -0
- package/dist/web/browser.6333ad295d/path.js.br +0 -0
- package/dist/web/browser.6333ad295d/path.js.gz +0 -0
- package/dist/web/browser.6333ad295d/reactive.js +1 -0
- package/dist/web/browser.6333ad295d/reactive.js.br +0 -0
- package/dist/web/browser.6333ad295d/reactive.js.gz +0 -0
- package/dist/web/browser.6333ad295d/realm.js +1 -0
- package/dist/web/browser.6333ad295d/resource.js +1 -0
- package/dist/web/browser.6333ad295d/resource.js.br +0 -0
- package/dist/web/browser.6333ad295d/resource.js.gz +0 -0
- package/dist/web/browser.6333ad295d/runtime.js +2 -0
- package/dist/web/browser.6333ad295d/runtime.js.br +0 -0
- package/dist/web/browser.6333ad295d/runtime.js.gz +0 -0
- package/dist/web/browser.6333ad295d/scope.js +1 -0
- package/dist/web/browser.6333ad295d/scope.js.br +0 -0
- package/dist/web/browser.6333ad295d/scope.js.gz +0 -0
- package/dist/web/browser.6333ad295d/standard.js +1 -0
- package/dist/web/browser.6333ad295d/standard.js.br +0 -0
- package/dist/web/browser.6333ad295d/standard.js.gz +0 -0
- package/dist/web/browser.6333ad295d/testing.js +1 -0
- package/dist/web/browser.6333ad295d/testing.js.br +0 -0
- package/dist/web/browser.6333ad295d/testing.js.gz +0 -0
- package/dist/web/browser.6333ad295d/values.js +1 -0
- package/dist/web/browser.6333ad295d/values.js.br +0 -0
- package/dist/web/browser.6333ad295d/values.js.gz +0 -0
- package/dist/web/host.19652b6282.js +80 -0
- package/dist/web/host.19652b6282.js.br +0 -0
- package/dist/web/host.19652b6282.js.gz +0 -0
- package/dist/web/icons/180.c8c643a608.png +0 -0
- package/dist/web/icons/192.c099d3e1ed.png +0 -0
- package/dist/web/icons/32.caf706bbd1.png +0 -0
- package/dist/web/icons/512.e2c2dd9ba7.png +0 -0
- package/dist/web/index.html +129 -14
- package/dist/web/index.html.br +0 -0
- package/dist/web/index.html.gz +0 -0
- package/dist/web/manifest.webmanifest +22 -0
- package/dist/web/nsh-math.7ad3060fbc.wasm +0 -0
- package/dist/web/nsh-math.7ad3060fbc.wasm.br +0 -0
- package/dist/web/nsh-math.7ad3060fbc.wasm.gz +0 -0
- package/dist/web/nsh-math.cc869361ca.bin +0 -0
- package/dist/web/nsh-syntaxes.ea0da56d04.bin +0 -0
- package/dist/web/nsh-syntaxes.ea0da56d04.bin.br +0 -0
- package/dist/web/nsh-syntaxes.ea0da56d04.bin.gz +0 -0
- package/dist/web/nsh.5be0579ad8.wasm +0 -0
- package/dist/web/nsh.5be0579ad8.wasm.br +0 -0
- package/dist/web/nsh.5be0579ad8.wasm.gz +0 -0
- package/dist/web/nsh.6b31ba26e0.js +259 -0
- package/dist/web/nsh.6b31ba26e0.js.br +0 -0
- package/dist/web/nsh.6b31ba26e0.js.gz +0 -0
- package/npm-shrinkwrap.json +251 -220
- package/package.json +8 -7
- package/dist/web/assets/index-BChj4uOJ.js +0 -5
- package/dist/web/assets/index-CEoUvOKr.js +0 -64
- package/dist/web/assets/index-TpNsUNeA.css +0 -1
- package/dist/web/favicon.svg +0 -1
- package/dist/web/sw.js +0 -61
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
API_CREDENTIAL_CACHE_SECONDS,
|
|
3
3
|
COMPACTION_FAILURES,
|
|
4
|
+
CONNECTOR_VERSION_HEADER,
|
|
4
5
|
ConnectorSetupError,
|
|
5
6
|
CronError,
|
|
6
7
|
GITHUB_ISSUE_BODY_LIMIT,
|
|
@@ -23,7 +24,9 @@ import {
|
|
|
23
24
|
base64Bytes,
|
|
24
25
|
bindingRemote,
|
|
25
26
|
boundedStream,
|
|
27
|
+
buildVersion,
|
|
26
28
|
builtinExtensionNames,
|
|
29
|
+
bundledToolDefinitions,
|
|
27
30
|
checkBrowseResult,
|
|
28
31
|
checkPrerequisites,
|
|
29
32
|
confirmUnsandboxed,
|
|
@@ -39,10 +42,13 @@ import {
|
|
|
39
42
|
decidedAsHostCommand,
|
|
40
43
|
defaultHostBrowserConfig,
|
|
41
44
|
defaultHostConfig,
|
|
45
|
+
defaultHostTunnelConfig,
|
|
42
46
|
defaultNetworkPolicy,
|
|
43
47
|
defaultWorkspaceExtensions,
|
|
44
48
|
documentKind,
|
|
45
49
|
documentKindNames,
|
|
50
|
+
documentKindsOff,
|
|
51
|
+
documentSourceExtensionOf,
|
|
46
52
|
drainStream,
|
|
47
53
|
dropboxItem,
|
|
48
54
|
egressGitServices,
|
|
@@ -100,15 +106,19 @@ import {
|
|
|
100
106
|
parseHostCompaction,
|
|
101
107
|
parseHostConfigChange,
|
|
102
108
|
parseHostExtensionsReport,
|
|
109
|
+
parseHostTunnelConfig,
|
|
103
110
|
parseLfsBatchRequest,
|
|
104
111
|
parseLfsObject,
|
|
105
112
|
parseNetworkPolicy,
|
|
106
113
|
parseRequiredExtensions,
|
|
114
|
+
parseSessionTunnelReport,
|
|
107
115
|
parseUploadChunk,
|
|
108
116
|
parseWorkspaceExtension,
|
|
109
117
|
permissionNeeds,
|
|
110
118
|
permissions,
|
|
111
119
|
pikiExtension,
|
|
120
|
+
pikiExtensionOn,
|
|
121
|
+
pikiExtensions,
|
|
112
122
|
pikiExtensionsFor,
|
|
113
123
|
pikiSessionToolExtensions,
|
|
114
124
|
plainRepositoryPath,
|
|
@@ -157,12 +167,13 @@ import {
|
|
|
157
167
|
validRepositoryId,
|
|
158
168
|
validRepositoryName,
|
|
159
169
|
validSourceItem,
|
|
170
|
+
validVersion,
|
|
160
171
|
validateUserEnv,
|
|
161
172
|
verifyToken,
|
|
162
173
|
workspaceDatabaseTlsModes,
|
|
163
174
|
workspaceManagerRoles,
|
|
164
175
|
workspaceSessionPermissions
|
|
165
|
-
} from "./chunk-
|
|
176
|
+
} from "./chunk-3IXKVGAD.js";
|
|
166
177
|
import {
|
|
167
178
|
ConstraintError,
|
|
168
179
|
migrationsDirectory,
|
|
@@ -566,7 +577,9 @@ function sessionRecord(row, grants) {
|
|
|
566
577
|
...row.tool_output === "brief" ? { toolOutput: "brief" } : {},
|
|
567
578
|
...row.parent_session_id ? { parentSessionId: row.parent_session_id } : {},
|
|
568
579
|
...row.packed_at ? { packed: true } : {},
|
|
569
|
-
...pullRequest2 ? { pullRequest: pullRequest2 } : {}
|
|
580
|
+
...pullRequest2 ? { pullRequest: pullRequest2 } : {},
|
|
581
|
+
// An archived session is never online, whatever a host that was away when it was archived said.
|
|
582
|
+
...row.tunnel_url && row.tunnel_expires_at && row.status !== "archived" && Date.parse(row.tunnel_expires_at) > Date.now() ? { tunnel: { url: row.tunnel_url, expiresAt: row.tunnel_expires_at } } : {}
|
|
570
583
|
};
|
|
571
584
|
}
|
|
572
585
|
function runnerSessionRecord(row) {
|
|
@@ -580,6 +593,7 @@ function runnerSessionRecord(row) {
|
|
|
580
593
|
nextScheduledAt: _nextScheduledAt,
|
|
581
594
|
parentSessionId: _parentSessionId,
|
|
582
595
|
packed: _packed,
|
|
596
|
+
tunnel: _tunnel,
|
|
583
597
|
...record4
|
|
584
598
|
} = sessionRecord(row);
|
|
585
599
|
return record4;
|
|
@@ -705,6 +719,7 @@ var NATIVE_CODE_TTL = 2 * 60 * 1e3;
|
|
|
705
719
|
var OPAQUE_TOKEN = /^[A-Za-z0-9_-]{43}$/;
|
|
706
720
|
var CALLBACK_PATH = "/api/auth/github/callback";
|
|
707
721
|
var NATIVE_CALLBACK = "pikibun://auth";
|
|
722
|
+
var NATIVE_PAGE_PATH = "/api/auth/native/done";
|
|
708
723
|
var VERIFIER = /^[A-Za-z0-9_-]{43,128}$/;
|
|
709
724
|
var APP_STATE = /^[A-Za-z0-9_-]{16,128}$/;
|
|
710
725
|
var PROFILE_FIELDS = { displayName: true, gitName: true, gitEmail: true, env: true };
|
|
@@ -947,8 +962,18 @@ async function githubAuthorization(c, native, inviteTokenHash) {
|
|
|
947
962
|
const state = randomToken();
|
|
948
963
|
const verifier = randomToken();
|
|
949
964
|
const expiresAt2 = Date.now() + STATE_TTL;
|
|
950
|
-
await dbResult(c.env.DB.query(`INSERT INTO oauth_states
|
|
951
|
-
|
|
965
|
+
await dbResult(c.env.DB.query(`INSERT INTO oauth_states
|
|
966
|
+
(state_hash, verifier, expires_at, created_at, native_challenge, native_state, native_callback, invite_token_hash)
|
|
967
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).bind(
|
|
968
|
+
await sha256(state),
|
|
969
|
+
verifier,
|
|
970
|
+
expiresAt2,
|
|
971
|
+
now(),
|
|
972
|
+
native?.challenge ?? null,
|
|
973
|
+
native?.state ?? null,
|
|
974
|
+
native?.callback === "page" ? "page" : null,
|
|
975
|
+
inviteTokenHash ?? null
|
|
976
|
+
).run());
|
|
952
977
|
const cookieConfig = cookies(c.env);
|
|
953
978
|
setCookie(c, cookieConfig.state, state, { ...cookieConfig.options, maxAge: STATE_TTL / 1e3, expires: new Date(expiresAt2) });
|
|
954
979
|
const authorization2 = new URL("https://github.com/login/oauth/authorize");
|
|
@@ -996,18 +1021,24 @@ async function githubSignup(c, githubId, inviteTokenHash, native) {
|
|
|
996
1021
|
function nativeStart(c) {
|
|
997
1022
|
const challenges = c.req.queries("challenge");
|
|
998
1023
|
const states = c.req.queries("state");
|
|
1024
|
+
const callbacks = c.req.queries("callback");
|
|
999
1025
|
const challenge = challenges?.length === 1 ? challenges[0] : void 0;
|
|
1000
1026
|
const state = states?.length === 1 ? states[0] : void 0;
|
|
1001
1027
|
if (!challenge || !OPAQUE_TOKEN.test(challenge) || !state || !APP_STATE.test(state)) fail(400, "invalid_native_request", "Invalid native sign-in request.");
|
|
1002
|
-
|
|
1028
|
+
if (callbacks !== void 0 && (callbacks.length !== 1 || callbacks[0] !== "page")) fail(400, "invalid_native_request", "Invalid native sign-in request.");
|
|
1029
|
+
return githubStart(c, { challenge, state, callback: callbacks ? "page" : "scheme" });
|
|
1003
1030
|
}
|
|
1004
|
-
function nativeCallback(params) {
|
|
1005
|
-
|
|
1031
|
+
function nativeCallback(c, native, params) {
|
|
1032
|
+
const query = new URLSearchParams({ ...params, state: native.state }).toString();
|
|
1033
|
+
return native.callback === "page" ? `${appOrigin(c.env)}${NATIVE_PAGE_PATH}?${query}` : `${NATIVE_CALLBACK}?${query}`;
|
|
1006
1034
|
}
|
|
1007
|
-
async function nativeFailure(c, error,
|
|
1035
|
+
async function nativeFailure(c, error, native) {
|
|
1008
1036
|
if (!(error instanceof HTTPException2)) throw error;
|
|
1009
1037
|
const body = await error.getResponse().json().catch(() => null);
|
|
1010
|
-
return c.redirect(nativeCallback({ error: typeof body?.code === "string" ? body.code : "sign_in_failed"
|
|
1038
|
+
return c.redirect(nativeCallback(c, native, { error: typeof body?.code === "string" ? body.code : "sign_in_failed" }));
|
|
1039
|
+
}
|
|
1040
|
+
function nativeDone(c) {
|
|
1041
|
+
return c.html('<!doctype html><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>Pikibun</title><p style="font:16px system-ui,sans-serif;text-align:center;margin-top:30vh">You can return to Pikibun.</p>');
|
|
1011
1042
|
}
|
|
1012
1043
|
async function githubUser(c, config, code, verifier, inviteTokenHash = null, native = false) {
|
|
1013
1044
|
const exchange = await githubJson("https://github.com/login/oauth/access_token", {
|
|
@@ -1118,13 +1149,13 @@ async function githubCallback(c) {
|
|
|
1118
1149
|
fail(400, "invalid_oauth_state", "Invalid or expired OAuth state.");
|
|
1119
1150
|
}
|
|
1120
1151
|
const saved = await dbResult(c.env.DB.query(
|
|
1121
|
-
"DELETE FROM oauth_states WHERE state_hash = ? RETURNING verifier, expires_at, native_challenge, native_state, invite_token_hash"
|
|
1152
|
+
"DELETE FROM oauth_states WHERE state_hash = ? RETURNING verifier, expires_at, native_challenge, native_state, native_callback, invite_token_hash"
|
|
1122
1153
|
).bind(await sha256(state)).first());
|
|
1123
1154
|
expireCookie(c, "state");
|
|
1124
1155
|
if (!saved || !Number.isSafeInteger(saved.expires_at) || saved.expires_at <= Date.now() || !OPAQUE_TOKEN.test(saved.verifier)) {
|
|
1125
1156
|
fail(400, "invalid_oauth_state", "Invalid or expired OAuth state.");
|
|
1126
1157
|
}
|
|
1127
|
-
const native = saved.native_challenge !== null && saved.native_state !== null ? { challenge: saved.native_challenge, state: saved.native_state } : void 0;
|
|
1158
|
+
const native = saved.native_challenge !== null && saved.native_state !== null ? { challenge: saved.native_challenge, state: saved.native_state, callback: saved.native_callback === "page" ? "page" : "scheme" } : void 0;
|
|
1128
1159
|
let signedIn;
|
|
1129
1160
|
try {
|
|
1130
1161
|
const codes = c.req.queries("code");
|
|
@@ -1135,9 +1166,9 @@ async function githubCallback(c) {
|
|
|
1135
1166
|
signedIn = await githubUser(c, config, code, saved.verifier, saved.invite_token_hash, !!native);
|
|
1136
1167
|
} catch (error) {
|
|
1137
1168
|
if (error instanceof SignupRefusal) {
|
|
1138
|
-
return c.redirect(native ? nativeCallback({ error: error.code
|
|
1169
|
+
return c.redirect(native ? nativeCallback(c, native, { error: error.code }) : `${appOrigin(c.env)}/?${new URLSearchParams({ error: error.message }).toString()}`);
|
|
1139
1170
|
}
|
|
1140
|
-
if (native) return nativeFailure(c, error, native
|
|
1171
|
+
if (native) return nativeFailure(c, error, native);
|
|
1141
1172
|
throw error;
|
|
1142
1173
|
}
|
|
1143
1174
|
const { user, tokenStatement } = signedIn;
|
|
@@ -1151,9 +1182,9 @@ async function githubCallback(c) {
|
|
|
1151
1182
|
c.env.DB.query("INSERT INTO native_auth_codes (code_hash, user_id, challenge, expires_at, created_at) VALUES (?, ?, ?, ?, ?)").bind(await sha256(code), user.id, native.challenge, Date.now() + NATIVE_CODE_TTL, now())
|
|
1152
1183
|
]));
|
|
1153
1184
|
} catch (error) {
|
|
1154
|
-
return nativeFailure(c, error, native
|
|
1185
|
+
return nativeFailure(c, error, native);
|
|
1155
1186
|
}
|
|
1156
|
-
return c.redirect(nativeCallback(
|
|
1187
|
+
return c.redirect(nativeCallback(c, native, { code }));
|
|
1157
1188
|
}
|
|
1158
1189
|
const session = await newSession(c.env, user.id, "github");
|
|
1159
1190
|
await dbResult(c.env.DB.batch([tokenStatement, session.statement]));
|
|
@@ -1232,6 +1263,7 @@ function registerAuth(app2) {
|
|
|
1232
1263
|
app2.get(CALLBACK_PATH, privateResponse, githubCallback);
|
|
1233
1264
|
app2.get("/api/auth/local/claim", privateResponse, localClaim);
|
|
1234
1265
|
app2.get("/api/auth/native/start", privateResponse, nativeStart);
|
|
1266
|
+
app2.get(NATIVE_PAGE_PATH, privateResponse, nativeDone);
|
|
1235
1267
|
app2.post("/api/auth/native/token", privateResponse, nativeToken);
|
|
1236
1268
|
app2.post("/api/auth/test", privateResponse, csrfGuard, testLogin);
|
|
1237
1269
|
app2.get("/api/auth/csrf", privateResponse, authenticate, (c) => c.json({ csrfToken: c.get("auth").csrfToken }));
|
|
@@ -1887,7 +1919,8 @@ var actionPermissions = {
|
|
|
1887
1919
|
pack: "manage",
|
|
1888
1920
|
unpack: "manage",
|
|
1889
1921
|
withdraw: "ask",
|
|
1890
|
-
upload: "write"
|
|
1922
|
+
upload: "write",
|
|
1923
|
+
tunnel: "write"
|
|
1891
1924
|
};
|
|
1892
1925
|
var connectorPermissions = {
|
|
1893
1926
|
create: "write",
|
|
@@ -1907,7 +1940,8 @@ var connectorPermissions = {
|
|
|
1907
1940
|
pack: "manage",
|
|
1908
1941
|
unpack: "manage",
|
|
1909
1942
|
withdraw: "ask",
|
|
1910
|
-
upload: "write"
|
|
1943
|
+
upload: "write",
|
|
1944
|
+
tunnel: "write"
|
|
1911
1945
|
};
|
|
1912
1946
|
var ownerActions = ["create", "restart", "restore", "backup", "fork", "pack", "unpack"];
|
|
1913
1947
|
function requireArchivedAction(row, action) {
|
|
@@ -2056,6 +2090,8 @@ async function authorizeSession(c, connectorId) {
|
|
|
2056
2090
|
}
|
|
2057
2091
|
|
|
2058
2092
|
// ../../apps/server/src/authority/operations.ts
|
|
2093
|
+
var programTools = /* @__PURE__ */ new Set([...programToolNames, ...bundledToolDefinitions.filter((item2) => item2.kind === "program").map((item2) => item2.name)]);
|
|
2094
|
+
var knownTools = /* @__PURE__ */ new Set([...toolNames, ...bundledToolDefinitions.map((item2) => item2.name)]);
|
|
2059
2095
|
function validRepositoryPath(value) {
|
|
2060
2096
|
return typeof value === "string" && value.length <= 512 && /^[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+){1,63}$/.test(value) && !value.split("/").some((part) => [".", "..", ".git"].includes(part.toLowerCase()));
|
|
2061
2097
|
}
|
|
@@ -2096,7 +2132,7 @@ function parseOperation(input2) {
|
|
|
2096
2132
|
case "bash":
|
|
2097
2133
|
return { kind: value.kind, command: text(value.command, "command", 8192) };
|
|
2098
2134
|
case "program":
|
|
2099
|
-
if (Object.keys(value).length !== 3 || !
|
|
2135
|
+
if (Object.keys(value).length !== 3 || typeof value.tool !== "string" || !programTools.has(value.tool)) fail(400, "invalid_operation", "Unknown program tool.");
|
|
2100
2136
|
return { kind: value.kind, tool: value.tool, command: text(value.command, "command", 8192) };
|
|
2101
2137
|
case "github.comment":
|
|
2102
2138
|
return { kind: value.kind, repository: githubRepositoryName(value.repository), issue: issueNumber(value.issue), body: text(value.body, "comment body", 6e4) };
|
|
@@ -2132,7 +2168,7 @@ function parseOperation(input2) {
|
|
|
2132
2168
|
}
|
|
2133
2169
|
case "permission": {
|
|
2134
2170
|
const needs = value.needs;
|
|
2135
|
-
if (!
|
|
2171
|
+
if (typeof value.tool !== "string" || !knownTools.has(value.tool)) fail(400, "invalid_operation", "Unknown tool.");
|
|
2136
2172
|
if (!Array.isArray(needs) || !needs.length || needs.join() !== permissionNeeds.filter((need) => needs.includes(need)).join()) {
|
|
2137
2173
|
fail(400, "invalid_operation", "Invalid permission needs.");
|
|
2138
2174
|
}
|
|
@@ -4862,6 +4898,9 @@ async function sourceRead(c, connectorId) {
|
|
|
4862
4898
|
await requireToolCall(c.env, access.row, "source_read", { id: callId, requestedBy: actorId, admission, target: item2 === null ? sourceId : `${sourceId}: ${item2}` });
|
|
4863
4899
|
const source2 = await c.env.DB.query(`${documentSourceSelect} WHERE d.source_id = ? AND d.workspace_id = ?`).bind(sourceId, access.row.workspace_id).first();
|
|
4864
4900
|
if (!source2) fail(404, "source_not_found", "This workspace has no such source, or it is a repository (clone those with Git).");
|
|
4901
|
+
if (documentKindsOff(await workspaceExtensions(c.env, access.row.workspace_id)).has(source2.kind)) {
|
|
4902
|
+
fail(403, "source_kind_off", `Reading ${documentKindNames[source2.kind]} sources is turned off in this workspace (${documentSourceExtensionOf(source2.kind)?.source ?? "its extension"}). Its owners and admins turn it on under Settings \u2192 Workspaces \u2192 Extensions.`);
|
|
4903
|
+
}
|
|
4865
4904
|
return { row: access.row, source: source2 };
|
|
4866
4905
|
};
|
|
4867
4906
|
const { row, source } = await authorize();
|
|
@@ -8414,6 +8453,7 @@ function connectorPath(action, sessionId, suffix = "") {
|
|
|
8414
8453
|
case "unpack":
|
|
8415
8454
|
case "withdraw":
|
|
8416
8455
|
case "upload":
|
|
8456
|
+
case "tunnel":
|
|
8417
8457
|
return `${base}/${action}`;
|
|
8418
8458
|
default:
|
|
8419
8459
|
return fail(400, "invalid_action", "Invalid connector action.");
|
|
@@ -9370,7 +9410,8 @@ var stringVars = [
|
|
|
9370
9410
|
"TENCENT_DOCS_CLIENT_ID",
|
|
9371
9411
|
"TENCENT_DOCS_CLIENT_SECRET",
|
|
9372
9412
|
"WEB_PUSH_CONTACT",
|
|
9373
|
-
"EXPO_ACCESS_TOKEN"
|
|
9413
|
+
"EXPO_ACCESS_TOKEN",
|
|
9414
|
+
"PIKIBUN_VERSION"
|
|
9374
9415
|
];
|
|
9375
9416
|
var LOOPBACK = ["127.0.0.1", "::1", "localhost"];
|
|
9376
9417
|
async function loadConfig(processEnv = process.env) {
|
|
@@ -9509,7 +9550,8 @@ async function actorRequest(c, actorId, row, action, suffix = "", body, pin) {
|
|
|
9509
9550
|
pack: `/v1/sessions/${row.id}/pack`,
|
|
9510
9551
|
unpack: `/v1/sessions/${row.id}/unpack`,
|
|
9511
9552
|
withdraw: `/v1/sessions/${row.id}/withdraw`,
|
|
9512
|
-
upload: `/v1/sessions/${row.id}/upload
|
|
9553
|
+
upload: `/v1/sessions/${row.id}/upload`,
|
|
9554
|
+
tunnel: `/v1/sessions/${row.id}/tunnel`
|
|
9513
9555
|
};
|
|
9514
9556
|
const serialized = body === void 0 ? "" : JSON.stringify(body);
|
|
9515
9557
|
const issuedAt = Math.floor(Date.now() / 1e3);
|
|
@@ -10061,8 +10103,8 @@ function redactEntry(entry2) {
|
|
|
10061
10103
|
// ../../apps/server/src/host-config.ts
|
|
10062
10104
|
async function connectorHostConfig(env, connectorId) {
|
|
10063
10105
|
const row = await env.DB.query(`SELECT command_timeout_seconds, gpu, git_credentials, repository_cache, host_sessions, compaction_json,
|
|
10064
|
-
builtin_extensions_json, required_extensions_json, browser_json FROM connector_configs WHERE connector_id = ?`).bind(connectorId).first();
|
|
10065
|
-
if (!row) return { ...defaultHostConfig, builtinExtensions: [], requiredExtensions: [], browser: { ...defaultHostBrowserConfig } };
|
|
10106
|
+
builtin_extensions_json, required_extensions_json, browser_json, service_lifetime_seconds, tunnel_json FROM connector_configs WHERE connector_id = ?`).bind(connectorId).first();
|
|
10107
|
+
if (!row) return { ...defaultHostConfig, builtinExtensions: [], requiredExtensions: [], browser: { ...defaultHostBrowserConfig }, tunnel: { ...defaultHostTunnelConfig } };
|
|
10066
10108
|
return {
|
|
10067
10109
|
commandTimeoutSeconds: Number(row.command_timeout_seconds),
|
|
10068
10110
|
gpu: Number(row.gpu) === 1,
|
|
@@ -10072,9 +10114,23 @@ async function connectorHostConfig(env, connectorId) {
|
|
|
10072
10114
|
compaction: storedCompaction(row.compaction_json),
|
|
10073
10115
|
builtinExtensions: storedBuiltinExtensions(row.builtin_extensions_json),
|
|
10074
10116
|
requiredExtensions: storedRequiredExtensions(row.required_extensions_json),
|
|
10075
|
-
browser: storedBrowser(row.browser_json)
|
|
10117
|
+
browser: storedBrowser(row.browser_json),
|
|
10118
|
+
serviceLifetimeSeconds: storedLifetime(row.service_lifetime_seconds),
|
|
10119
|
+
tunnel: storedTunnel(row.tunnel_json)
|
|
10076
10120
|
};
|
|
10077
10121
|
}
|
|
10122
|
+
function storedLifetime(value) {
|
|
10123
|
+
const seconds = Number(value);
|
|
10124
|
+
const { min, max } = hostConfigLimits.serviceLifetimeSeconds;
|
|
10125
|
+
return Number.isSafeInteger(seconds) && seconds >= min && seconds <= max ? seconds : defaultHostConfig.serviceLifetimeSeconds;
|
|
10126
|
+
}
|
|
10127
|
+
function storedTunnel(json2) {
|
|
10128
|
+
try {
|
|
10129
|
+
return parseHostTunnelConfig(JSON.parse(json2));
|
|
10130
|
+
} catch {
|
|
10131
|
+
return { ...defaultHostTunnelConfig };
|
|
10132
|
+
}
|
|
10133
|
+
}
|
|
10078
10134
|
function storedBrowser(json2) {
|
|
10079
10135
|
try {
|
|
10080
10136
|
return parseHostBrowserConfig(JSON.parse(json2));
|
|
@@ -10122,12 +10178,14 @@ var columns = Object.freeze({
|
|
|
10122
10178
|
compaction: "compaction_json",
|
|
10123
10179
|
builtinExtensions: "builtin_extensions_json",
|
|
10124
10180
|
requiredExtensions: "required_extensions_json",
|
|
10125
|
-
browser: "browser_json"
|
|
10181
|
+
browser: "browser_json",
|
|
10182
|
+
serviceLifetimeSeconds: "service_lifetime_seconds",
|
|
10183
|
+
tunnel: "tunnel_json"
|
|
10126
10184
|
});
|
|
10127
10185
|
function upsert(settings) {
|
|
10128
10186
|
return `INSERT INTO connector_configs (connector_id, command_timeout_seconds, gpu, git_credentials, repository_cache, host_sessions, compaction_json,
|
|
10129
|
-
builtin_extensions_json, required_extensions_json, browser_json, updated_by, updated_at)
|
|
10130
|
-
SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? WHERE EXISTS (SELECT 1 FROM connectors WHERE id = ? AND revoked_at IS NULL AND removed_at IS NULL)
|
|
10187
|
+
builtin_extensions_json, required_extensions_json, browser_json, service_lifetime_seconds, tunnel_json, updated_by, updated_at)
|
|
10188
|
+
SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? WHERE EXISTS (SELECT 1 FROM connectors WHERE id = ? AND revoked_at IS NULL AND removed_at IS NULL)
|
|
10131
10189
|
ON CONFLICT (connector_id) DO UPDATE SET ${settings.map((key) => `${columns[key]} = excluded.${columns[key]}, `).join("")}
|
|
10132
10190
|
updated_by = excluded.updated_by, updated_at = excluded.updated_at
|
|
10133
10191
|
RETURNING connector_id`;
|
|
@@ -10151,7 +10209,7 @@ function registerHostConfig(app2) {
|
|
|
10151
10209
|
let settings = Object.keys(change);
|
|
10152
10210
|
if (row.role !== "owner") {
|
|
10153
10211
|
if (settings.some((key) => ownerOnlyHostConfig.includes(key) && JSON.stringify(change[key]) !== JSON.stringify(current[key]))) {
|
|
10154
|
-
fail(403, "host_owner_required", "Only the host's owner decides whether it uses its Git credentials, keeps a repository cache, runs sessions without the sandbox, requires extensions of every session on it
|
|
10212
|
+
fail(403, "host_owner_required", "Only the host's owner decides whether it uses its Git credentials, keeps a repository cache, runs sessions without the sandbox, requires extensions of every session on it, where its browser comes from or whether its sessions may put servers online through tunnels.");
|
|
10155
10213
|
}
|
|
10156
10214
|
settings = settings.filter((key) => !ownerOnlyHostConfig.includes(key));
|
|
10157
10215
|
if (!settings.length) return c.json({ config: current });
|
|
@@ -10168,6 +10226,8 @@ function registerHostConfig(app2) {
|
|
|
10168
10226
|
JSON.stringify(config.builtinExtensions),
|
|
10169
10227
|
JSON.stringify(config.requiredExtensions),
|
|
10170
10228
|
JSON.stringify(config.browser),
|
|
10229
|
+
config.serviceLifetimeSeconds,
|
|
10230
|
+
JSON.stringify(config.tunnel),
|
|
10171
10231
|
actorId,
|
|
10172
10232
|
now(),
|
|
10173
10233
|
row.id
|
|
@@ -10388,6 +10448,7 @@ async function versionBatch(env, statements, versionIndex) {
|
|
|
10388
10448
|
throw error;
|
|
10389
10449
|
}
|
|
10390
10450
|
}
|
|
10451
|
+
var agentToolNames = [...toolNames, ...bundledToolDefinitions.map((item2) => item2.name).filter((name2) => !toolNames.includes(name2))];
|
|
10391
10452
|
function carries(capabilities, tool) {
|
|
10392
10453
|
if (retiredToolNames.includes(tool)) return false;
|
|
10393
10454
|
if (tool === "git_push") return capabilities.gitPush === true;
|
|
@@ -10401,7 +10462,7 @@ function carries(capabilities, tool) {
|
|
|
10401
10462
|
async function chatAgentVersion(env, ownerId, model, capabilities) {
|
|
10402
10463
|
const agentId = id();
|
|
10403
10464
|
const createdAt = now();
|
|
10404
|
-
const tools =
|
|
10465
|
+
const tools = agentToolNames.filter((tool) => carries(capabilities, tool));
|
|
10405
10466
|
const input2 = { name: model.name, description: "", instructions: "", toolsJson: JSON.stringify(tools), modelId: model.id };
|
|
10406
10467
|
return versionBatch(env, [
|
|
10407
10468
|
env.DB.query(`INSERT INTO agents (id, author_id, created_at, chat)
|
|
@@ -10415,7 +10476,7 @@ async function migratedAgentVersion(env, ownerId, source, model, capabilities) {
|
|
|
10415
10476
|
const agentId = id();
|
|
10416
10477
|
const createdAt = now();
|
|
10417
10478
|
const wanted = JSON.parse(source.tools_json);
|
|
10418
|
-
const tools =
|
|
10479
|
+
const tools = agentToolNames.filter((tool) => wanted.includes(tool) && carries(capabilities, tool));
|
|
10419
10480
|
const input2 = {
|
|
10420
10481
|
name: source.name,
|
|
10421
10482
|
description: source.description,
|
|
@@ -10980,6 +11041,40 @@ function redactedEntry(entry2) {
|
|
|
10980
11041
|
return shown;
|
|
10981
11042
|
}
|
|
10982
11043
|
|
|
11044
|
+
// ../../apps/server/src/session-tunnels.ts
|
|
11045
|
+
async function reportSessionTunnel(c, connectorId) {
|
|
11046
|
+
let report;
|
|
11047
|
+
try {
|
|
11048
|
+
report = parseSessionTunnelReport(await jsonObject(c, 4096));
|
|
11049
|
+
} catch (error) {
|
|
11050
|
+
return fail(400, "invalid_tunnel", error instanceof Error ? error.message : "Invalid tunnel report.");
|
|
11051
|
+
}
|
|
11052
|
+
const row = await c.env.DB.query("SELECT * FROM sessions WHERE id = ?").bind(report.sessionId).first();
|
|
11053
|
+
if (!row) fail(404, "not_found", "Session not found.");
|
|
11054
|
+
await requireCallbackScope(c, row, connectorId);
|
|
11055
|
+
const actor = report.actorId && await c.env.DB.query("SELECT 1 FROM workspace_members WHERE workspace_id = ? AND user_id = ?").bind(row.workspace_id, report.actorId).first() ? report.actorId : null;
|
|
11056
|
+
const detail = { connectorId, url: report.url, ...report.expiresAt ? { expiresAt: report.expiresAt } : {}, ...report.reason ? { reason: report.reason } : {} };
|
|
11057
|
+
await c.env.DB.batch([
|
|
11058
|
+
report.event === "started" ? c.env.DB.query("UPDATE sessions SET tunnel_url = ?, tunnel_expires_at = ? WHERE id = ? AND connector_id = ? AND status <> 'archived'").bind(report.url, report.expiresAt, row.id, connectorId) : c.env.DB.query("UPDATE sessions SET tunnel_url = NULL, tunnel_expires_at = NULL WHERE id = ? AND connector_id = ? AND tunnel_url = ?").bind(row.id, connectorId, report.url),
|
|
11059
|
+
auditStatement(c.env, actor, `session.tunnel.${report.event}`, row.workspace_id, row.id, detail)
|
|
11060
|
+
]);
|
|
11061
|
+
invalidateSessionView(c, row.id);
|
|
11062
|
+
return c.json({ ok: true });
|
|
11063
|
+
}
|
|
11064
|
+
async function stopSessionTunnel(c, row, reason) {
|
|
11065
|
+
const result3 = await runnerJSON(await runnerRequest(c, row, "tunnel", "", { action: "stop", ...reason ? { reason } : {} }));
|
|
11066
|
+
invalidateSessionView(c, row.id);
|
|
11067
|
+
if (typeof result3?.stopped !== "boolean") fail(502, "invalid_runner_response", "The runner returned an invalid tunnel result.");
|
|
11068
|
+
return result3.stopped;
|
|
11069
|
+
}
|
|
11070
|
+
function registerSessionTunnels(app2) {
|
|
11071
|
+
app2.post("/api/sessions/:id/tunnel/stop", async (c) => {
|
|
11072
|
+
const { row } = await requireSession(c, identifier(c.req.param("id")), "write");
|
|
11073
|
+
if (!row.connector_id) fail(409, "tunnel_unsupported", "Only sessions on a connected host put servers online.");
|
|
11074
|
+
return c.json({ stopped: await stopSessionTunnel(c, row) });
|
|
11075
|
+
});
|
|
11076
|
+
}
|
|
11077
|
+
|
|
10983
11078
|
// ../../apps/server/src/transcript-settings.ts
|
|
10984
11079
|
var adminSql6 = `EXISTS (SELECT 1 FROM workspace_members WHERE workspace_id = ? AND user_id = ? AND role IN ('owner','admin'))`;
|
|
10985
11080
|
function settingsOf2(row) {
|
|
@@ -11358,7 +11453,9 @@ async function archiveSession(c, sessionId, audit = {}) {
|
|
|
11358
11453
|
const access = await requireSessionManager(c, sessionId, "manage", "Only the session owner, or the workspace's owner or an admin, can archive it.");
|
|
11359
11454
|
const { row } = access;
|
|
11360
11455
|
const actorId = c.get("auth").userId;
|
|
11361
|
-
|
|
11456
|
+
if (row.tunnel_url && row.connector_id) await stopSessionTunnel(c, row, "archived").catch(() => false);
|
|
11457
|
+
const archived = await c.env.DB.query(`UPDATE sessions SET archived_status = status, status = 'archived', policy_version = policy_version + 1,
|
|
11458
|
+
tunnel_url = NULL, tunnel_expires_at = NULL
|
|
11362
11459
|
WHERE id = ? AND owner_id = ? AND policy_version = ? AND status <> 'archived' AND ${sessionManagerSQL("sessions")}
|
|
11363
11460
|
RETURNING *`).bind(row.id, row.owner_id, row.policy_version, actorId).first();
|
|
11364
11461
|
if (!archived) fail(409, "policy_changed", "Session status or permissions changed; reload and retry.");
|
|
@@ -14335,6 +14432,7 @@ async function connectorInfo(env, row, viewer) {
|
|
|
14335
14432
|
revokedAt: row.revoked_at,
|
|
14336
14433
|
online,
|
|
14337
14434
|
models,
|
|
14435
|
+
...row.connector_version ? { version: row.connector_version } : {},
|
|
14338
14436
|
workspaceIds: workspaceIds.filter(visible),
|
|
14339
14437
|
connectedWorkspaceIds: connectedWorkspaceIds.filter(visible),
|
|
14340
14438
|
capabilities,
|
|
@@ -14444,8 +14542,9 @@ function registerConnectorCredentials(app2) {
|
|
|
14444
14542
|
const input2 = await jsonObject(c, 128 * 1024);
|
|
14445
14543
|
onlyFields2(input2, ["models", "capabilities"]);
|
|
14446
14544
|
const manifest = withHostToolCapabilities(input2, (base) => parseManifest(base, c.env));
|
|
14447
|
-
const
|
|
14448
|
-
|
|
14545
|
+
const version = validVersion(c.req.header(CONNECTOR_VERSION_HEADER)) ?? null;
|
|
14546
|
+
const updated = await c.env.DB.query(`UPDATE connectors SET models_json = ?, capabilities_json = ?, connector_version = ? WHERE id = ?
|
|
14547
|
+
AND revoked_at IS NULL AND connection_hash = ? RETURNING *`).bind(JSON.stringify(manifest.models), JSON.stringify(manifest.capabilities), version, row.id, row.connection_hash).first();
|
|
14449
14548
|
if (!updated) fail(401, "stale_connection", "Connection has been replaced.");
|
|
14450
14549
|
await syncModels(c.env, updated, null);
|
|
14451
14550
|
return c.json({ ok: true });
|
|
@@ -14453,11 +14552,16 @@ function registerConnectorCredentials(app2) {
|
|
|
14453
14552
|
app2.post("/api/connectors/self/authorize", async (c) => authorizeSession(c, (await authenticateConnector(c)).id));
|
|
14454
14553
|
app2.post("/api/connectors/self/activity", async (c) => reportSessionActivity(c, (await authenticateConnector(c)).id));
|
|
14455
14554
|
app2.post("/api/connectors/self/branches", async (c) => reportSessionBranches(c, (await authenticateConnector(c)).id));
|
|
14555
|
+
app2.post("/api/connectors/self/tunnel", async (c) => reportSessionTunnel(c, (await authenticateConnector(c)).id));
|
|
14456
14556
|
app2.post("/api/connectors/self/github/comment", async (c) => githubComment(c, (await authenticateConnector(c)).id));
|
|
14457
14557
|
app2.post("/api/connectors/self/github/read", async (c) => githubRead(c, (await authenticateConnector(c)).id));
|
|
14458
14558
|
app2.post("/api/connectors/self/github/merge", async (c) => githubMerge(c, (await authenticateConnector(c)).id));
|
|
14459
14559
|
app2.post("/api/connectors/self/github/issue", async (c) => githubIssue(c, (await authenticateConnector(c)).id));
|
|
14460
14560
|
app2.post("/api/connectors/self/sources/read", async (c) => sourceRead(c, (await authenticateConnector(c)).id));
|
|
14561
|
+
app2.post("/api/connectors/self/tools", async (c) => {
|
|
14562
|
+
await authenticateConnector(c);
|
|
14563
|
+
return c.json({ tools: bundledToolDefinitions });
|
|
14564
|
+
});
|
|
14461
14565
|
app2.post("/api/connectors/self/schedules", async (c) => agentSchedules(c, (await authenticateConnector(c)).id));
|
|
14462
14566
|
app2.post("/api/connectors/self/sessions", async (c) => agentSessions(c, (await authenticateConnector(c)).id));
|
|
14463
14567
|
app2.post("/api/connectors/self/web/search", async (c) => {
|
|
@@ -14592,7 +14696,7 @@ function registerConnectors(app2) {
|
|
|
14592
14696
|
const timestamp2 = now();
|
|
14593
14697
|
await c.env.DB.batch([
|
|
14594
14698
|
c.env.DB.query(`UPDATE connectors SET removed_at = ?, revoked_at = COALESCE(revoked_at, ?),
|
|
14595
|
-
connection_hash = NULL, credential_hash = ?, name = 'Removed host', models_json = '[]', capabilities_json = '{}'
|
|
14699
|
+
connection_hash = NULL, credential_hash = ?, name = 'Removed host', models_json = '[]', capabilities_json = '{}', connector_version = NULL
|
|
14596
14700
|
WHERE id = ?`).bind(timestamp2, timestamp2, await sha256(randomToken()), row.id),
|
|
14597
14701
|
c.env.DB.query("DELETE FROM connector_shares WHERE connector_id = ?").bind(row.id),
|
|
14598
14702
|
c.env.DB.query("DELETE FROM connector_workspaces WHERE connector_id = ?").bind(row.id),
|
|
@@ -14929,14 +15033,31 @@ function availability(extensions, installed) {
|
|
|
14929
15033
|
function shownReport(report, ownerView) {
|
|
14930
15034
|
if (ownerView) return report;
|
|
14931
15035
|
const browser = report.browser && (({ executable: _, error: __, ...rest }) => rest)(report.browser);
|
|
14932
|
-
|
|
15036
|
+
const tunnel = report.tunnel && (({ executable: _, error: __, ...rest }) => rest)(report.tunnel);
|
|
15037
|
+
return { ...report, installed: report.installed.filter((item2) => !item2.dev), ...browser ? { browser } : {}, ...tunnel ? { tunnel } : {} };
|
|
14933
15038
|
}
|
|
14934
15039
|
var coreSources = pikiSessionToolExtensions().filter((item2) => item2.core).map((item2) => item2.source);
|
|
14935
15040
|
var adminSql8 = `EXISTS (SELECT 1 FROM workspace_members WHERE workspace_id = ? AND user_id = ? AND role IN ('owner','admin'))`;
|
|
14936
|
-
function connectorWorkspaceExtensions(extensions, installed) {
|
|
15041
|
+
function connectorWorkspaceExtensions(extensions, installed, options = {}) {
|
|
14937
15042
|
const has = new Set((installed ?? []).map((item2) => item2.source));
|
|
14938
15043
|
const knowsPiki = [...has].some(isPikiExtensionSource);
|
|
14939
|
-
|
|
15044
|
+
if (options.toolDefinitions && knowsPiki) for (const definition of bundledToolDefinitions) for (const extension of definition.extensions) has.add(extension);
|
|
15045
|
+
const shown = extensions.filter((item2) => !isPikiExtensionSource(item2.source) || knowsPiki && (!sessionToolSources.includes(item2.source) || has.has(item2.source)));
|
|
15046
|
+
return knowsPiki ? withReplacedSwitches(shown, extensions, has) : shown;
|
|
15047
|
+
}
|
|
15048
|
+
function withReplacedSwitches(shown, extensions, has) {
|
|
15049
|
+
let result3 = [...shown];
|
|
15050
|
+
const replaced = new Set(pikiExtensions.flatMap((item2) => item2.replaces ? [item2.replaces] : []));
|
|
15051
|
+
for (const source of replaced) {
|
|
15052
|
+
const successors = pikiExtensions.filter((item2) => item2.replaces === source);
|
|
15053
|
+
if (!has.has(source) || successors.some((item2) => has.has(item2.source))) continue;
|
|
15054
|
+
const on = successors.some((item2) => pikiExtensionOn(item2, extensions));
|
|
15055
|
+
const own = result3.find((item2) => item2.source === source);
|
|
15056
|
+
if (on) result3 = result3.filter((item2) => item2.source !== source);
|
|
15057
|
+
else if (own) result3 = result3.map((item2) => item2.source === source ? { ...item2, enabled: false } : item2);
|
|
15058
|
+
else result3.push({ source, enabled: false, tools: "approval", settings: {} });
|
|
15059
|
+
}
|
|
15060
|
+
return result3;
|
|
14940
15061
|
}
|
|
14941
15062
|
function registerConnectorExtensions(app2) {
|
|
14942
15063
|
app2.post("/api/connectors/self/extensions", async (c) => {
|
|
@@ -14977,12 +15098,13 @@ function registerConnectorExtensions(app2) {
|
|
|
14977
15098
|
const row = await authenticateConnector(c);
|
|
14978
15099
|
const input2 = await jsonObject(c, 4096);
|
|
14979
15100
|
const workspaceId = identifier(input2.workspaceId, "workspaceId");
|
|
15101
|
+
const options = { toolDefinitions: input2.toolDefinitions === true };
|
|
14980
15102
|
const connected = await c.env.DB.query(`SELECT cw.connector_id FROM connector_workspaces cw
|
|
14981
15103
|
JOIN workspace_members m ON m.workspace_id = cw.workspace_id AND m.user_id = ?
|
|
14982
15104
|
WHERE cw.connector_id = ? AND cw.workspace_id = ?`).bind(row.owner_id, row.id, workspaceId).first();
|
|
14983
15105
|
if (!connected) fail(404, "not_found", "Workspace not found.");
|
|
14984
15106
|
const extensions = await workspaceExtensions(c.env, workspaceId);
|
|
14985
|
-
const shown = extensions.some((item2) => isPikiExtensionSource(item2.source)) ? connectorWorkspaceExtensions(extensions, (await hostReport(c.env, row.id))?.report.installed) : extensions;
|
|
15107
|
+
const shown = extensions.some((item2) => isPikiExtensionSource(item2.source)) ? connectorWorkspaceExtensions(extensions, (await hostReport(c.env, row.id))?.report.installed, options) : extensions;
|
|
14986
15108
|
return c.json({ extensions: shown, automaticTools: await automaticTools(c.env, row.id) });
|
|
14987
15109
|
});
|
|
14988
15110
|
}
|
|
@@ -16002,6 +16124,8 @@ app.get("/api/health", async (c) => {
|
|
|
16002
16124
|
}
|
|
16003
16125
|
return c.json({
|
|
16004
16126
|
ok: true,
|
|
16127
|
+
// The release it runs, such as 0.24.0; null in development. Shown in the web app's settings.
|
|
16128
|
+
version: validVersion(c.env.PIKIBUN_VERSION) ?? buildVersion ?? null,
|
|
16005
16129
|
githubConfigured: !!c.env.GITHUB_CLIENT_ID && !!c.env.GITHUB_CLIENT_SECRET && vaultReady,
|
|
16006
16130
|
runnerConfigured: !!c.env.RUNNER_URL && !!c.env.RUNNER_SHARED_SECRET && c.env.RUNNER_SHARED_SECRET.length >= 32,
|
|
16007
16131
|
databaseConfigured: true,
|
|
@@ -16045,6 +16169,7 @@ registerSessions(app);
|
|
|
16045
16169
|
registerSessionLinks(app);
|
|
16046
16170
|
registerBackups(app);
|
|
16047
16171
|
registerSessionFiles(app);
|
|
16172
|
+
registerSessionTunnels(app);
|
|
16048
16173
|
registerFileUploads(app);
|
|
16049
16174
|
registerScheduledPrompts(app);
|
|
16050
16175
|
registerCrossSession(app);
|
|
@@ -16062,7 +16187,7 @@ app.all("/api/*", () => fail(404, "not_found", "API route not found."));
|
|
|
16062
16187
|
|
|
16063
16188
|
// ../../apps/server/src/static.ts
|
|
16064
16189
|
import { createReadStream } from "node:fs";
|
|
16065
|
-
import { stat } from "node:fs/promises";
|
|
16190
|
+
import { readFile as readFile3, stat } from "node:fs/promises";
|
|
16066
16191
|
import { extname, join as join2, resolve as resolve3, sep as sep2 } from "node:path";
|
|
16067
16192
|
import { Readable } from "node:stream";
|
|
16068
16193
|
var contentTypes = {
|
|
@@ -16070,9 +16195,11 @@ var contentTypes = {
|
|
|
16070
16195
|
".js": "text/javascript; charset=utf-8",
|
|
16071
16196
|
".mjs": "text/javascript; charset=utf-8",
|
|
16072
16197
|
".css": "text/css; charset=utf-8",
|
|
16198
|
+
".wasm": "application/wasm",
|
|
16073
16199
|
".json": "application/json; charset=utf-8",
|
|
16074
16200
|
".webmanifest": "application/manifest+json",
|
|
16075
16201
|
".map": "application/json",
|
|
16202
|
+
".bin": "application/octet-stream",
|
|
16076
16203
|
".svg": "image/svg+xml",
|
|
16077
16204
|
".png": "image/png",
|
|
16078
16205
|
".jpg": "image/jpeg",
|
|
@@ -16083,12 +16210,30 @@ var contentTypes = {
|
|
|
16083
16210
|
".woff": "font/woff",
|
|
16084
16211
|
".woff2": "font/woff2",
|
|
16085
16212
|
".ttf": "font/ttf",
|
|
16213
|
+
".otf": "font/otf",
|
|
16086
16214
|
".txt": "text/plain; charset=utf-8"
|
|
16087
16215
|
};
|
|
16216
|
+
var hashedName = /\.[0-9a-f]{10}\.[a-z0-9]+$/;
|
|
16217
|
+
function pageContentSecurityPolicy() {
|
|
16218
|
+
return [
|
|
16219
|
+
"default-src 'self'",
|
|
16220
|
+
"script-src 'self' blob: 'unsafe-inline' 'wasm-unsafe-eval'",
|
|
16221
|
+
"style-src 'self' 'unsafe-inline'",
|
|
16222
|
+
"img-src 'self' data: blob: https://avatars.githubusercontent.com",
|
|
16223
|
+
"font-src 'self' data:",
|
|
16224
|
+
// The runtime reads its own blobs (a pasted image, a picked photo) with fetch.
|
|
16225
|
+
"connect-src 'self' blob: data:",
|
|
16226
|
+
"worker-src 'self' blob:",
|
|
16227
|
+
"object-src 'none'",
|
|
16228
|
+
"base-uri 'self'",
|
|
16229
|
+
"frame-ancestors 'none'",
|
|
16230
|
+
"form-action 'self'"
|
|
16231
|
+
].join("; ");
|
|
16232
|
+
}
|
|
16088
16233
|
function createStaticHandler(root) {
|
|
16089
16234
|
const base = resolve3(root);
|
|
16090
16235
|
const index = join2(base, "index.html");
|
|
16091
|
-
async function
|
|
16236
|
+
async function file2(path2, request2) {
|
|
16092
16237
|
let info2;
|
|
16093
16238
|
try {
|
|
16094
16239
|
info2 = await stat(path2);
|
|
@@ -16100,11 +16245,24 @@ function createStaticHandler(root) {
|
|
|
16100
16245
|
"Content-Type": contentTypes[extname(path2).toLowerCase()] ?? "application/octet-stream",
|
|
16101
16246
|
"Content-Length": String(info2.size),
|
|
16102
16247
|
"Last-Modified": info2.mtime.toUTCString(),
|
|
16103
|
-
"Cache-Control":
|
|
16248
|
+
"Cache-Control": hashedName.test(path2) ? "public, max-age=31536000, immutable" : "no-cache"
|
|
16104
16249
|
};
|
|
16105
16250
|
if (request2.method === "HEAD") return new Response(null, { status: 200, headers: headers3 });
|
|
16106
16251
|
return new Response(Readable.toWeb(createReadStream(path2)), { status: 200, headers: headers3 });
|
|
16107
16252
|
}
|
|
16253
|
+
async function page(request2) {
|
|
16254
|
+
let html;
|
|
16255
|
+
try {
|
|
16256
|
+
html = await readFile3(index, "utf8");
|
|
16257
|
+
} catch {
|
|
16258
|
+
return Response.json({ error: "The web app is not built: run `pnpm build`.", code: "not_found" }, { status: 404 });
|
|
16259
|
+
}
|
|
16260
|
+
if (!/<base\s/i.test(html)) html = html.replace(/<head>/i, '<head>\n<base href="/">');
|
|
16261
|
+
return new Response(request2.method === "HEAD" ? null : html, {
|
|
16262
|
+
status: 200,
|
|
16263
|
+
headers: { "Content-Type": contentTypes[".html"], "Cache-Control": "no-cache", "Content-Security-Policy": pageContentSecurityPolicy() }
|
|
16264
|
+
});
|
|
16265
|
+
}
|
|
16108
16266
|
return async (request2) => {
|
|
16109
16267
|
let pathname;
|
|
16110
16268
|
try {
|
|
@@ -16114,12 +16272,12 @@ function createStaticHandler(root) {
|
|
|
16114
16272
|
}
|
|
16115
16273
|
if (!pathname.includes("\0") && pathname !== "/") {
|
|
16116
16274
|
const path2 = resolve3(base, `.${pathname}`);
|
|
16117
|
-
if (path2.startsWith(base + sep2)) {
|
|
16118
|
-
const response = await
|
|
16275
|
+
if (path2.startsWith(base + sep2) && path2 !== index) {
|
|
16276
|
+
const response = await file2(path2, request2);
|
|
16119
16277
|
if (response) return response;
|
|
16120
16278
|
}
|
|
16121
16279
|
}
|
|
16122
|
-
return
|
|
16280
|
+
return page(request2);
|
|
16123
16281
|
};
|
|
16124
16282
|
}
|
|
16125
16283
|
|
|
@@ -16184,8 +16342,11 @@ async function startServer(config) {
|
|
|
16184
16342
|
if (c.req.method !== "GET" && c.req.method !== "HEAD" || c.req.path.startsWith("/api/")) {
|
|
16185
16343
|
return c.json({ error: "Not found.", code: "not_found" }, 404);
|
|
16186
16344
|
}
|
|
16187
|
-
const
|
|
16188
|
-
|
|
16345
|
+
const served = await serveStatic(c.req.raw);
|
|
16346
|
+
const response = c.newResponse(served.body, served);
|
|
16347
|
+
const policy = served.headers.get("Content-Security-Policy");
|
|
16348
|
+
if (policy) response.headers.set("Content-Security-Policy", policy);
|
|
16349
|
+
return response;
|
|
16189
16350
|
});
|
|
16190
16351
|
const server = createAdaptorServer({
|
|
16191
16352
|
overrideGlobalObjects: false,
|
|
@@ -16435,7 +16596,7 @@ function defaultStaticDir() {
|
|
|
16435
16596
|
let directory = dirname3(fileURLToPath(import.meta.url));
|
|
16436
16597
|
for (let depth = 0; depth < 6; depth++) {
|
|
16437
16598
|
for (const candidate of [join4(directory, "web"), join4(directory, "dist", "web")]) {
|
|
16438
|
-
if (existsSync(join4(candidate, "index.html")) && existsSync(join4(candidate, "
|
|
16599
|
+
if (existsSync(join4(candidate, "index.html")) && existsSync(join4(candidate, "manifest.webmanifest"))) return candidate;
|
|
16439
16600
|
}
|
|
16440
16601
|
directory = dirname3(directory);
|
|
16441
16602
|
}
|