@agent-team-foundation/first-tree-hub 0.10.1 → 0.10.3
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/{bootstrap-CtVqQA8a.mjs → bootstrap-CBAVWQUT.mjs} +9 -1
- package/dist/cli/index.mjs +63 -20
- package/dist/{feishu-DEmwoNn_.mjs → dist-DUCelK3Z.mjs} +202 -62
- package/dist/drizzle/0026_saas_onboarding.sql +153 -0
- package/dist/drizzle/0027_runtime_provider.sql +10 -0
- package/dist/drizzle/0028_auth_identity_user_github_unique.sql +12 -0
- package/dist/drizzle/meta/_journal.json +21 -0
- package/dist/feishu-Boy3n8CT.mjs +52 -0
- package/dist/{getMachineId-bsd-BB-fnFLA.mjs → getMachineId-bsd-D0w3uAZa.mjs} +1 -1
- package/dist/{getMachineId-darwin-DAYWNsYK.mjs → getMachineId-darwin-DOoYFb2_.mjs} +1 -1
- package/dist/{getMachineId-win-H5RT49ov.mjs → getMachineId-win-B6hY8edq.mjs} +1 -1
- package/dist/index.mjs +7 -5
- package/dist/invitation-BTlGMy0o-Coj07kYi.mjs +3 -0
- package/dist/invitation-C_zAhB8x-8Khychlu.mjs +258 -0
- package/dist/{observability-DDkJwSKv.mjs → observability-C08jUFsJ.mjs} +1 -1
- package/dist/{observability-DV_fQKqV-oxfXX6Z2.mjs → observability-DPyf745N-BSc8QNcR.mjs} +6 -6
- package/dist/{core-BgiFGT7Y.mjs → saas-connect-3p-vBkuY.mjs} +2459 -430
- package/dist/web/assets/index-CHoaSIzI.js +21 -0
- package/dist/web/assets/index-CP8uLPyO.css +1 -0
- package/dist/web/assets/index-D7OzKrI2.js +387 -0
- package/dist/web/index.html +2 -2
- package/package.json +3 -2
- package/dist/web/assets/index-Cd290Lq6.css +0 -1
- package/dist/web/assets/index-xi7JmCtW.js +0 -361
- /package/dist/{execAsync-CP8iWV5b.mjs → execAsync-XMc-nFn-.mjs} +0 -0
- /package/dist/{getMachineId-linux-BU7Fi6S0.mjs → getMachineId-linux-MlY63Zsw.mjs} +0 -0
- /package/dist/{getMachineId-unsupported-BhWCxKBo.mjs → getMachineId-unsupported-BS652RIy.mjs} +0 -0
|
@@ -548,7 +548,8 @@ const serverConfigSchema = defineConfig({
|
|
|
548
548
|
},
|
|
549
549
|
server: {
|
|
550
550
|
port: field(z.number().default(8e3), { env: "FIRST_TREE_HUB_PORT" }),
|
|
551
|
-
host: field(z.string().default("127.0.0.1"), { env: "FIRST_TREE_HUB_HOST" })
|
|
551
|
+
host: field(z.string().default("127.0.0.1"), { env: "FIRST_TREE_HUB_HOST" }),
|
|
552
|
+
publicUrl: field(z.string().optional(), { env: "FIRST_TREE_HUB_PUBLIC_URL" })
|
|
552
553
|
},
|
|
553
554
|
secrets: {
|
|
554
555
|
jwtSecret: field(z.string(), {
|
|
@@ -576,6 +577,13 @@ const serverConfigSchema = defineConfig({
|
|
|
576
577
|
}),
|
|
577
578
|
allowedOrg: field(z.string().optional(), { env: "FIRST_TREE_HUB_GITHUB_ALLOWED_ORG" })
|
|
578
579
|
},
|
|
580
|
+
oauth: optional({ github: optional({
|
|
581
|
+
clientId: field(z.string(), { env: "FIRST_TREE_HUB_GITHUB_OAUTH_CLIENT_ID" }),
|
|
582
|
+
clientSecret: field(z.string(), {
|
|
583
|
+
env: "FIRST_TREE_HUB_GITHUB_OAUTH_CLIENT_SECRET",
|
|
584
|
+
secret: true
|
|
585
|
+
})
|
|
586
|
+
}) }),
|
|
579
587
|
cors: optional({ origin: field(z.string(), { env: "FIRST_TREE_HUB_CORS_ORIGIN" }) }),
|
|
580
588
|
rateLimit: optional({
|
|
581
589
|
max: field(z.number().default(100), { env: "FIRST_TREE_HUB_RATE_LIMIT_MAX" }),
|
package/dist/cli/index.mjs
CHANGED
|
@@ -1,26 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import "../observability-
|
|
3
|
-
import {
|
|
2
|
+
import "../observability-DPyf745N-BSc8QNcR.mjs";
|
|
3
|
+
import { A as checkServerHealth, C as checkAgentConfigs, D as checkDocker, E as checkDatabase, F as installClientService, G as createOwner, H as ClientRuntime, I as isServiceSupported, J as fail, M as checkWebSocket, N as printResults, O as checkNodeVersion, P as getClientServiceStatus, Q as setJsonMode, S as runMigrations, T as checkClientConfig, U as handleClientOrgMismatch, V as stopPostgres, Y as success, Z as print, _ as onboardCreate, a as declineUpdate, at as probeCapabilities, b as createApiNameResolver, c as COMMAND_VERSION, d as isInteractive, et as ClientOrgMismatchError, f as promptAddAgent, g as onboardCheck, h as loadOnboardState, i as createExecuteUpdate, it as cleanWorkspaces, j as checkServerReachable, k as checkServerConfig, l as reconcileLocalRuntimeProviders, m as formatCheckReport, nt as SdkError, o as promptUpdate, ot as applyClientLoggerConfig, p as promptMissingFields, q as resolveReplyToFromEnv, r as registerSaaSConnectCommand, rt as SessionRegistry, s as startServer, st as configureClientLoggerForService, tt as FirstTreeHubSDK, u as uploadClientCapabilities, v as saveOnboardState, w as checkBackgroundService, x as migrateLocalAgentDirs, y as runHomeMigration } from "../saas-connect-3p-vBkuY.mjs";
|
|
4
4
|
import "../logger-core-BTmvdflj-DjW8FM4T.mjs";
|
|
5
|
-
import { C as serverConfigSchema, _ as loadAgents, b as resetConfig, c as saveCredentials, d as DEFAULT_HOME_DIR, f as agentConfigSchema, g as initConfig, h as getConfigValue, i as loadCredentials, l as DEFAULT_CONFIG_DIR, n as ensureFreshAccessToken, o as resolveServerUrl, p as clientConfigSchema, r as ensureFreshAdminToken, s as saveAgentConfig, u as DEFAULT_DATA_DIR, w as setConfigValue, x as resetConfigMeta, y as readConfigFile } from "../bootstrap-
|
|
6
|
-
import
|
|
5
|
+
import { C as serverConfigSchema, _ as loadAgents, b as resetConfig, c as saveCredentials, d as DEFAULT_HOME_DIR, f as agentConfigSchema, g as initConfig, h as getConfigValue, i as loadCredentials, l as DEFAULT_CONFIG_DIR, n as ensureFreshAccessToken, o as resolveServerUrl, p as clientConfigSchema, r as ensureFreshAdminToken, s as saveAgentConfig, u as DEFAULT_DATA_DIR, w as setConfigValue, x as resetConfigMeta, y as readConfigFile } from "../bootstrap-CBAVWQUT.mjs";
|
|
6
|
+
import "../dist-DUCelK3Z.mjs";
|
|
7
|
+
import { n as bindFeishuUser, t as bindFeishuBot } from "../feishu-Boy3n8CT.mjs";
|
|
8
|
+
import "../invitation-C_zAhB8x-8Khychlu.mjs";
|
|
7
9
|
import { join } from "node:path";
|
|
8
10
|
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync } from "node:fs";
|
|
9
11
|
import { Command } from "commander";
|
|
10
12
|
import { confirm, input, password, select } from "@inquirer/prompts";
|
|
11
|
-
//#region src/cli/output.ts
|
|
12
|
-
/**
|
|
13
|
-
* CLI output re-exports. The underlying implementation lives in
|
|
14
|
-
* `core/output.ts` (the Print layer). Keep these thin wrappers so callers that
|
|
15
|
-
* only depend on `cli/output.ts` keep working during the migration.
|
|
16
|
-
*/
|
|
17
|
-
function success(data) {
|
|
18
|
-
print.result(data);
|
|
19
|
-
}
|
|
20
|
-
function fail(code, message, exitCode = 1) {
|
|
21
|
-
return print.fail(code, message, exitCode);
|
|
22
|
-
}
|
|
23
|
-
//#endregion
|
|
24
13
|
//#region src/commands/agent-config.ts
|
|
25
14
|
async function resolveAgentRecord(serverUrl, adminToken, agentName) {
|
|
26
15
|
const res = await fetch(`${serverUrl}/api/v1/admin/agents?limit=100`, {
|
|
@@ -360,7 +349,8 @@ function registerAgentCommands(program) {
|
|
|
360
349
|
const createBody = {
|
|
361
350
|
name,
|
|
362
351
|
type: options.type,
|
|
363
|
-
clientId: options.clientId
|
|
352
|
+
clientId: options.clientId,
|
|
353
|
+
runtimeProvider: options.runtime
|
|
364
354
|
};
|
|
365
355
|
if (options.displayName) createBody.displayName = options.displayName;
|
|
366
356
|
const createRes = await fetch(`${serverUrl}/api/v1/admin/agents`, {
|
|
@@ -927,6 +917,20 @@ function registerConnectCommand(parent) {
|
|
|
927
917
|
const msg = err instanceof Error ? err.message : String(err);
|
|
928
918
|
print.status("⚠️", `agent-dir migration skipped: ${msg}`);
|
|
929
919
|
}
|
|
920
|
+
let probedCapabilities = null;
|
|
921
|
+
try {
|
|
922
|
+
const accessToken = await ensureFreshAccessToken();
|
|
923
|
+
probedCapabilities = await probeCapabilities();
|
|
924
|
+
await reconcileLocalRuntimeProviders({
|
|
925
|
+
serverUrl: config.server.url,
|
|
926
|
+
accessToken,
|
|
927
|
+
agentsDir,
|
|
928
|
+
log: (level, msg) => print.status(level === "warn" ? "⚠️" : "•", msg)
|
|
929
|
+
});
|
|
930
|
+
} catch (err) {
|
|
931
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
932
|
+
print.status("⚠️", `runtime-provider reconcile skipped: ${msg}`);
|
|
933
|
+
}
|
|
930
934
|
const agents = loadAgents({
|
|
931
935
|
schema: agentConfigSchema,
|
|
932
936
|
agentsDir
|
|
@@ -941,6 +945,18 @@ function registerConnectCommand(parent) {
|
|
|
941
945
|
});
|
|
942
946
|
for (const [name, agentConfig] of agents) runtime.addAgent(name, agentConfig);
|
|
943
947
|
await runtime.start();
|
|
948
|
+
if (probedCapabilities) try {
|
|
949
|
+
const accessToken = await ensureFreshAccessToken();
|
|
950
|
+
await uploadClientCapabilities({
|
|
951
|
+
serverUrl: config.server.url,
|
|
952
|
+
accessToken,
|
|
953
|
+
clientId: config.client.id,
|
|
954
|
+
capabilities: probedCapabilities
|
|
955
|
+
});
|
|
956
|
+
} catch (err) {
|
|
957
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
958
|
+
print.status("⚠️", `capabilities upload skipped: ${msg}`);
|
|
959
|
+
}
|
|
944
960
|
runtime.watchAgentsDir(agentsDir);
|
|
945
961
|
const shutdown = async () => {
|
|
946
962
|
print.line("\n Shutting down...\n");
|
|
@@ -1000,6 +1016,20 @@ function registerClientCommands(program) {
|
|
|
1000
1016
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1001
1017
|
print.status("⚠️", `agent-dir migration skipped: ${msg}`);
|
|
1002
1018
|
}
|
|
1019
|
+
let probedCapabilities = null;
|
|
1020
|
+
try {
|
|
1021
|
+
const accessToken = await ensureFreshAccessToken();
|
|
1022
|
+
probedCapabilities = await probeCapabilities();
|
|
1023
|
+
await reconcileLocalRuntimeProviders({
|
|
1024
|
+
serverUrl: config.server.url,
|
|
1025
|
+
accessToken,
|
|
1026
|
+
agentsDir,
|
|
1027
|
+
log: (level, msg) => print.status(level === "warn" ? "⚠️" : "•", msg)
|
|
1028
|
+
});
|
|
1029
|
+
} catch (err) {
|
|
1030
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1031
|
+
print.status("⚠️", `runtime-provider reconcile skipped: ${msg}`);
|
|
1032
|
+
}
|
|
1003
1033
|
const agents = loadAgents({
|
|
1004
1034
|
schema: agentConfigSchema,
|
|
1005
1035
|
agentsDir
|
|
@@ -1016,6 +1046,18 @@ function registerClientCommands(program) {
|
|
|
1016
1046
|
});
|
|
1017
1047
|
for (const [name, agentConfig] of agents) runtime.addAgent(name, agentConfig);
|
|
1018
1048
|
await runtime.start();
|
|
1049
|
+
if (probedCapabilities) try {
|
|
1050
|
+
const accessToken = await ensureFreshAccessToken();
|
|
1051
|
+
await uploadClientCapabilities({
|
|
1052
|
+
serverUrl: config.server.url,
|
|
1053
|
+
accessToken,
|
|
1054
|
+
clientId: config.client.id,
|
|
1055
|
+
capabilities: probedCapabilities
|
|
1056
|
+
});
|
|
1057
|
+
} catch (err) {
|
|
1058
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1059
|
+
print.status("⚠️", `capabilities upload skipped: ${msg}`);
|
|
1060
|
+
}
|
|
1019
1061
|
runtime.watchAgentsDir(agentsDir);
|
|
1020
1062
|
const shutdown = async () => {
|
|
1021
1063
|
print.line("\n Shutting down...\n");
|
|
@@ -1223,13 +1265,13 @@ function isSecretField(schema, dotPath) {
|
|
|
1223
1265
|
//#region src/commands/onboard.ts
|
|
1224
1266
|
async function promptMissing(args) {
|
|
1225
1267
|
if (!args.server) try {
|
|
1226
|
-
const { resolveServerUrl } = await import("../bootstrap-
|
|
1268
|
+
const { resolveServerUrl } = await import("../bootstrap-CBAVWQUT.mjs").then((n) => n.t);
|
|
1227
1269
|
resolveServerUrl();
|
|
1228
1270
|
} catch {
|
|
1229
1271
|
args.server = await input({ message: "Hub server URL:" });
|
|
1230
1272
|
saveOnboardState(args);
|
|
1231
1273
|
}
|
|
1232
|
-
const { loadCredentials } = await import("../bootstrap-
|
|
1274
|
+
const { loadCredentials } = await import("../bootstrap-CBAVWQUT.mjs").then((n) => n.t);
|
|
1233
1275
|
if (!loadCredentials()) throw new Error("No saved credentials. Run `first-tree-hub client connect <server-url>` before onboarding.");
|
|
1234
1276
|
if (!args.id) {
|
|
1235
1277
|
args.id = await input({ message: "Agent ID:" });
|
|
@@ -1506,6 +1548,7 @@ program.name("first-tree-hub").description("First Tree Hub — centralized colla
|
|
|
1506
1548
|
});
|
|
1507
1549
|
else applyClientLoggerConfig({ level: "warn" });
|
|
1508
1550
|
});
|
|
1551
|
+
registerSaaSConnectCommand(program);
|
|
1509
1552
|
registerServerCommands(program);
|
|
1510
1553
|
registerClientCommands(program);
|
|
1511
1554
|
registerAgentCommands(program);
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { d as __exportAll } from "./esm-CYu4tXXn.mjs";
|
|
2
1
|
import { z } from "zod";
|
|
3
2
|
//#region ../shared/dist/index.mjs
|
|
4
3
|
const MENTION_REGEX = /(?<![A-Za-z0-9_.@-])@([A-Za-z0-9][A-Za-z0-9_-]{0,63})\b/g;
|
|
@@ -38,6 +37,35 @@ function scanMentionTokens(content) {
|
|
|
38
37
|
}
|
|
39
38
|
return tokens;
|
|
40
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Single source of truth for "is this string safe to redirect to after a
|
|
42
|
+
* successful OAuth callback".
|
|
43
|
+
*
|
|
44
|
+
* Both the server (`/auth/github/start` validates `?next=` before signing
|
|
45
|
+
* the state JWT) and the web client (the fragment-consumer page validates
|
|
46
|
+
* before navigating) must agree on the regex — drift here is what enables
|
|
47
|
+
* open-redirect bugs. The server is authoritative; the client check is a
|
|
48
|
+
* defense-in-depth.
|
|
49
|
+
*
|
|
50
|
+
* Allowed: a path that begins with exactly one `/` and is not the start of
|
|
51
|
+
* an authority component (`//`, `/\`). Permits typical SPA paths with
|
|
52
|
+
* query strings and fragments. Anything else (absolute URLs, scheme-less
|
|
53
|
+
* authority components, `javascript:`) falls through to the safe default.
|
|
54
|
+
*/
|
|
55
|
+
const SAFE_NEXT_PATH = /^\/(?![/\\])[A-Za-z0-9_\-./?=&%#]*$/;
|
|
56
|
+
/**
|
|
57
|
+
* Return `next` if it is a syntactically safe relative path, otherwise the
|
|
58
|
+
* default landing path. The check is deliberately conservative — the
|
|
59
|
+
* intent is to reject anything that could be parsed as an absolute URL by
|
|
60
|
+
* a browser navigation. Length is capped at 256 chars to defang
|
|
61
|
+
* pathological inputs.
|
|
62
|
+
*/
|
|
63
|
+
function safeRedirectPath(next) {
|
|
64
|
+
if (!next || typeof next !== "string") return "/";
|
|
65
|
+
if (next.length > 256) return "/";
|
|
66
|
+
if (!SAFE_NEXT_PATH.test(next)) return "/";
|
|
67
|
+
return next;
|
|
68
|
+
}
|
|
41
69
|
const adapterPlatformSchema = z.enum([
|
|
42
70
|
"feishu",
|
|
43
71
|
"slack",
|
|
@@ -132,14 +160,16 @@ const AGENT_BIND_REJECT_REASONS = {
|
|
|
132
160
|
NOT_OWNED: "not_owned",
|
|
133
161
|
AGENT_SUSPENDED: "agent_suspended",
|
|
134
162
|
WRONG_ORG: "wrong_org",
|
|
135
|
-
UNKNOWN_AGENT: "unknown_agent"
|
|
163
|
+
UNKNOWN_AGENT: "unknown_agent",
|
|
164
|
+
RUNTIME_PROVIDER_MISMATCH: "runtime_provider_mismatch"
|
|
136
165
|
};
|
|
137
166
|
z.enum([
|
|
138
167
|
"wrong_client",
|
|
139
168
|
"not_owned",
|
|
140
169
|
"agent_suspended",
|
|
141
170
|
"wrong_org",
|
|
142
|
-
"unknown_agent"
|
|
171
|
+
"unknown_agent",
|
|
172
|
+
"runtime_provider_mismatch"
|
|
143
173
|
]);
|
|
144
174
|
/** Header used on agent-scoped HTTP calls to select which managed agent the JWT acts as. */
|
|
145
175
|
const AGENT_SELECTOR_HEADER = "x-agent-id";
|
|
@@ -167,6 +197,8 @@ z.object({
|
|
|
167
197
|
}),
|
|
168
198
|
clients: z.number().int()
|
|
169
199
|
});
|
|
200
|
+
const runtimeProviderSchema = z.enum(["claude-code", "codex"]);
|
|
201
|
+
const DEFAULT_RUNTIME_PROVIDER = "claude-code";
|
|
170
202
|
const AGENT_TYPES = {
|
|
171
203
|
HUMAN: "human",
|
|
172
204
|
PERSONAL_ASSISTANT: "personal_assistant",
|
|
@@ -226,7 +258,8 @@ const createAgentSchema = z.object({
|
|
|
226
258
|
visibility: agentVisibilitySchema.optional(),
|
|
227
259
|
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
228
260
|
managerId: z.string().optional(),
|
|
229
|
-
clientId: z.string().min(1).max(100).optional()
|
|
261
|
+
clientId: z.string().min(1).max(100).optional(),
|
|
262
|
+
runtimeProvider: runtimeProviderSchema.optional()
|
|
230
263
|
});
|
|
231
264
|
const updateAgentSchema = z.object({
|
|
232
265
|
type: agentTypeSchema.optional(),
|
|
@@ -237,6 +270,18 @@ const updateAgentSchema = z.object({
|
|
|
237
270
|
managerId: z.string().nullable().optional(),
|
|
238
271
|
clientId: z.string().min(1).max(100).nullable().optional()
|
|
239
272
|
});
|
|
273
|
+
/**
|
|
274
|
+
* Service-level rebind input. Admin / owner re-binds an agent to a new
|
|
275
|
+
* client and/or a new runtime provider in one atomic operation.
|
|
276
|
+
*
|
|
277
|
+
* `force` bypasses the capability-match check (e.g. when the client is
|
|
278
|
+
* offline and capabilities are stale).
|
|
279
|
+
*/
|
|
280
|
+
const rebindAgentSchema = z.object({
|
|
281
|
+
clientId: z.string().min(1).max(100),
|
|
282
|
+
runtimeProvider: runtimeProviderSchema,
|
|
283
|
+
force: z.boolean().optional()
|
|
284
|
+
});
|
|
240
285
|
z.object({
|
|
241
286
|
uuid: z.string(),
|
|
242
287
|
name: z.string().nullable(),
|
|
@@ -251,6 +296,7 @@ z.object({
|
|
|
251
296
|
metadata: z.record(z.string(), z.unknown()),
|
|
252
297
|
managerId: z.string().nullable(),
|
|
253
298
|
clientId: z.string().nullable(),
|
|
299
|
+
runtimeProvider: runtimeProviderSchema,
|
|
254
300
|
presenceStatus: presenceStatusSchema.optional(),
|
|
255
301
|
createdAt: z.string(),
|
|
256
302
|
updatedAt: z.string()
|
|
@@ -270,14 +316,16 @@ const agentPinnedMessageSchema = z.object({
|
|
|
270
316
|
agentId: z.string(),
|
|
271
317
|
name: z.string().nullable(),
|
|
272
318
|
displayName: z.string(),
|
|
273
|
-
agentType: agentTypeSchema
|
|
319
|
+
agentType: agentTypeSchema,
|
|
320
|
+
runtimeProvider: runtimeProviderSchema
|
|
274
321
|
});
|
|
275
322
|
/**
|
|
276
|
-
* Agent runtime configuration
|
|
323
|
+
* Agent runtime configuration.
|
|
277
324
|
*
|
|
278
325
|
* Defines the 5 user-tunable field groups that the Hub centrally manages
|
|
279
326
|
* and pushes down to the client runtime: prompt append, model, MCP servers,
|
|
280
|
-
* env vars, and Git repos.
|
|
327
|
+
* env vars, and Git repos. Tagged by `kind` (a runtime provider) so future
|
|
328
|
+
* provider-specific fields can land on a dedicated variant.
|
|
281
329
|
*
|
|
282
330
|
* NOTE: do not co-locate with `packages/shared/src/config/` — that namespace
|
|
283
331
|
* is reserved for the local YAML config (`agent.yaml` / server / client) and
|
|
@@ -321,9 +369,11 @@ const gitRepoSchema = z.object({
|
|
|
321
369
|
localPath: z.string().min(1).optional()
|
|
322
370
|
});
|
|
323
371
|
/**
|
|
324
|
-
*
|
|
325
|
-
*
|
|
326
|
-
*
|
|
372
|
+
* Untagged base shape — 5 user-tunable fields, no `kind` discriminator.
|
|
373
|
+
* Used for `.partial()` derivations on the PATCH side, where `kind` is
|
|
374
|
+
* pinned to `agents.runtime_provider` and never changes via config PATCH.
|
|
375
|
+
* Zod 4 forbids `.partial()` on a refined object, so we keep refinements
|
|
376
|
+
* on the tagged schema below.
|
|
327
377
|
*/
|
|
328
378
|
const agentRuntimeConfigPayloadShape = z.object({
|
|
329
379
|
prompt: promptConfigSchema.default({ append: "" }),
|
|
@@ -332,6 +382,17 @@ const agentRuntimeConfigPayloadShape = z.object({
|
|
|
332
382
|
env: z.array(envEntrySchema).default([]),
|
|
333
383
|
gitRepos: z.array(gitRepoSchema).default([])
|
|
334
384
|
});
|
|
385
|
+
/**
|
|
386
|
+
* Tagged variants — read-side, full payload including `kind`. Adding a new
|
|
387
|
+
* provider means adding a variant here, plus a handler factory and a
|
|
388
|
+
* capability probe module on the client side.
|
|
389
|
+
*
|
|
390
|
+
* Provider-specific fields (e.g. codex `sandboxMode`) belong on the
|
|
391
|
+
* matching variant, not on the base shape.
|
|
392
|
+
*/
|
|
393
|
+
const claudeRuntimeConfigPayloadShape = agentRuntimeConfigPayloadShape.extend({ kind: z.literal("claude-code") });
|
|
394
|
+
const codexRuntimeConfigPayloadShape = agentRuntimeConfigPayloadShape.extend({ kind: z.literal("codex") });
|
|
395
|
+
const taggedPayloadUnion = z.discriminatedUnion("kind", [claudeRuntimeConfigPayloadShape, codexRuntimeConfigPayloadShape]);
|
|
335
396
|
const payloadDuplicatesRefinement = (payload, ctx) => {
|
|
336
397
|
const seenMcp = /* @__PURE__ */ new Set();
|
|
337
398
|
payload.mcpServers.forEach((server, idx) => {
|
|
@@ -376,15 +437,54 @@ const payloadDuplicatesRefinement = (payload, ctx) => {
|
|
|
376
437
|
seenPaths.add(path);
|
|
377
438
|
});
|
|
378
439
|
};
|
|
379
|
-
|
|
380
|
-
|
|
440
|
+
/**
|
|
441
|
+
* Read-side full payload schema. Rows persisted before 0026 do not carry
|
|
442
|
+
* `kind`; `z.preprocess` injects `"claude-code"` so they parse cleanly into
|
|
443
|
+
* the claude variant. The service layer separately enforces
|
|
444
|
+
* `payload.kind === agents.runtime_provider` on writes.
|
|
445
|
+
*/
|
|
446
|
+
const agentRuntimeConfigPayloadSchema = z.preprocess((input) => {
|
|
447
|
+
if (input && typeof input === "object" && !Array.isArray(input) && !("kind" in input)) return {
|
|
448
|
+
...input,
|
|
449
|
+
kind: "claude-code"
|
|
450
|
+
};
|
|
451
|
+
return input;
|
|
452
|
+
}, taggedPayloadUnion).superRefine((payload, ctx) => {
|
|
453
|
+
payloadDuplicatesRefinement(payload, ctx);
|
|
454
|
+
});
|
|
455
|
+
/** Default payload used when creating a fresh claude-code agent. */
|
|
381
456
|
const DEFAULT_AGENT_RUNTIME_CONFIG_PAYLOAD = {
|
|
457
|
+
kind: "claude-code",
|
|
382
458
|
prompt: { append: "" },
|
|
383
459
|
model: "opus",
|
|
384
460
|
mcpServers: [],
|
|
385
461
|
env: [],
|
|
386
462
|
gitRepos: []
|
|
387
463
|
};
|
|
464
|
+
/**
|
|
465
|
+
* Default payload for a fresh codex agent. Same 5 fields as claude-code.
|
|
466
|
+
* `model` is left empty by default so the Codex CLI picks one matching the
|
|
467
|
+
* user's auth mode — `gpt-5-codex` is rejected by ChatGPT-account auth, while
|
|
468
|
+
* an empty string lets the SDK fall through to its built-in default.
|
|
469
|
+
*/
|
|
470
|
+
const DEFAULT_CODEX_RUNTIME_CONFIG_PAYLOAD = {
|
|
471
|
+
kind: "codex",
|
|
472
|
+
prompt: { append: "" },
|
|
473
|
+
model: "",
|
|
474
|
+
mcpServers: [],
|
|
475
|
+
env: [],
|
|
476
|
+
gitRepos: []
|
|
477
|
+
};
|
|
478
|
+
/**
|
|
479
|
+
* Default payload selector by runtime provider.
|
|
480
|
+
*/
|
|
481
|
+
function defaultRuntimeConfigPayload(provider) {
|
|
482
|
+
switch (provider) {
|
|
483
|
+
case "codex": return { ...DEFAULT_CODEX_RUNTIME_CONFIG_PAYLOAD };
|
|
484
|
+
case "claude-code": return { ...DEFAULT_AGENT_RUNTIME_CONFIG_PAYLOAD };
|
|
485
|
+
default: return { ...DEFAULT_AGENT_RUNTIME_CONFIG_PAYLOAD };
|
|
486
|
+
}
|
|
487
|
+
}
|
|
388
488
|
const agentRuntimeConfigSchema = z.object({
|
|
389
489
|
agentId: z.string(),
|
|
390
490
|
version: z.number().int().positive(),
|
|
@@ -503,6 +603,37 @@ const clientRegisterSchema = z.object({
|
|
|
503
603
|
os: z.string().max(50).optional(),
|
|
504
604
|
sdkVersion: z.string().max(50).optional()
|
|
505
605
|
});
|
|
606
|
+
const capabilityStateSchema = z.enum([
|
|
607
|
+
"ok",
|
|
608
|
+
"missing",
|
|
609
|
+
"unauthenticated",
|
|
610
|
+
"error"
|
|
611
|
+
]);
|
|
612
|
+
const capabilityAuthMethodSchema = z.enum([
|
|
613
|
+
"api_key",
|
|
614
|
+
"oauth",
|
|
615
|
+
"auth_json",
|
|
616
|
+
"none"
|
|
617
|
+
]);
|
|
618
|
+
const capabilityEntrySchema = z.object({
|
|
619
|
+
state: capabilityStateSchema,
|
|
620
|
+
available: z.boolean(),
|
|
621
|
+
authenticated: z.boolean(),
|
|
622
|
+
sdkVersion: z.string().nullable().optional(),
|
|
623
|
+
authMethod: capabilityAuthMethodSchema,
|
|
624
|
+
error: z.string().nullable().optional(),
|
|
625
|
+
detectedAt: z.string()
|
|
626
|
+
});
|
|
627
|
+
/**
|
|
628
|
+
* Capabilities snapshot keyed by runtime provider name. Recorded as a plain
|
|
629
|
+
* `Record<string, CapabilityEntry>` — every entry is optional (a client may
|
|
630
|
+
* report only the runtimes it actually probed) and the key set evolves
|
|
631
|
+
* naturally as new providers ship without a schema migration. Service-layer
|
|
632
|
+
* lookups (`agents.runtime_provider ∈ keys(capabilities)`) treat the keys
|
|
633
|
+
* as `RuntimeProvider` strings.
|
|
634
|
+
*/
|
|
635
|
+
const clientCapabilitiesSchema = z.record(z.string(), capabilityEntrySchema);
|
|
636
|
+
const updateClientCapabilitiesSchema = z.object({ capabilities: clientCapabilitiesSchema });
|
|
506
637
|
const paginationQuerySchema = z.object({
|
|
507
638
|
limit: z.coerce.number().int().min(1).max(100).default(20),
|
|
508
639
|
cursor: z.string().optional()
|
|
@@ -667,6 +798,42 @@ z.object({
|
|
|
667
798
|
ackedAt: z.string().nullable()
|
|
668
799
|
}).extend({ message: clientMessageSchema });
|
|
669
800
|
const inboxPollQuerySchema = z.object({ limit: z.coerce.number().int().min(1).max(50).default(10) });
|
|
801
|
+
z.object({
|
|
802
|
+
organizationId: z.string(),
|
|
803
|
+
organizationName: z.string(),
|
|
804
|
+
organizationDisplayName: z.string(),
|
|
805
|
+
role: z.string()
|
|
806
|
+
});
|
|
807
|
+
z.object({
|
|
808
|
+
id: z.string(),
|
|
809
|
+
organizationId: z.string(),
|
|
810
|
+
token: z.string(),
|
|
811
|
+
inviteUrl: z.string(),
|
|
812
|
+
role: z.string(),
|
|
813
|
+
createdAt: z.string(),
|
|
814
|
+
expiresAt: z.string().nullable()
|
|
815
|
+
});
|
|
816
|
+
/** Body for joining via invite token. */
|
|
817
|
+
const joinByInvitationSchema = z.object({ token: z.string().min(1) });
|
|
818
|
+
z.object({}).optional();
|
|
819
|
+
z.enum([
|
|
820
|
+
"connect",
|
|
821
|
+
"create_agent",
|
|
822
|
+
"completed"
|
|
823
|
+
]);
|
|
824
|
+
z.object({
|
|
825
|
+
id: z.string(),
|
|
826
|
+
name: z.string(),
|
|
827
|
+
displayName: z.string(),
|
|
828
|
+
role: z.enum(["admin", "member"])
|
|
829
|
+
});
|
|
830
|
+
/** Body for `POST /me/organizations` — operator wants to create another team. */
|
|
831
|
+
const createOrgFromMeSchema = z.object({
|
|
832
|
+
name: z.string().min(2).max(50).regex(/^[a-z0-9][a-z0-9-]*$/),
|
|
833
|
+
displayName: z.string().min(1).max(200)
|
|
834
|
+
});
|
|
835
|
+
/** Body for `POST /auth/switch-org`. */
|
|
836
|
+
const switchOrgSchema = z.object({ organizationId: z.string().min(1) });
|
|
670
837
|
const memberRoleSchema = z.enum(["admin", "member"]);
|
|
671
838
|
const memberSchema = z.object({
|
|
672
839
|
id: z.string(),
|
|
@@ -724,6 +891,28 @@ const notificationQuerySchema = z.object({
|
|
|
724
891
|
read: z.enum(["true", "false"]).transform((v) => v === "true").optional(),
|
|
725
892
|
agentId: z.string().optional()
|
|
726
893
|
});
|
|
894
|
+
/**
|
|
895
|
+
* `GET /api/v1/auth/github/start` query — `next` is the post-login landing
|
|
896
|
+
* path. It is validated again before signing the state JWT (see
|
|
897
|
+
* `safe-redirect.ts`); the schema only enforces the syntactic upper bound
|
|
898
|
+
* so over-long paths bounce with a Zod error rather than silently truncate.
|
|
899
|
+
*/
|
|
900
|
+
const githubStartQuerySchema = z.object({ next: z.string().max(256).optional() });
|
|
901
|
+
const githubCallbackQuerySchema = z.object({
|
|
902
|
+
code: z.string().min(1),
|
|
903
|
+
state: z.string().min(1)
|
|
904
|
+
});
|
|
905
|
+
/**
|
|
906
|
+
* Dev-only callback to bypass the GitHub round-trip — sign in as a stub
|
|
907
|
+
* Github user. Gated by NODE_ENV !== 'production'; production always 404s.
|
|
908
|
+
*/
|
|
909
|
+
const githubDevCallbackQuerySchema = z.object({
|
|
910
|
+
githubId: z.string().min(1),
|
|
911
|
+
login: z.string().min(1),
|
|
912
|
+
email: z.string().email().optional(),
|
|
913
|
+
displayName: z.string().optional(),
|
|
914
|
+
next: z.string().max(256).optional()
|
|
915
|
+
});
|
|
727
916
|
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
728
917
|
const createOrganizationSchema = z.object({
|
|
729
918
|
name: z.string().min(2).max(50).regex(/^[a-z0-9][a-z0-9-]*$/, "Must start with a letter or digit and contain only lowercase alphanumeric and hyphens").refine((v) => !UUID_PATTERN.test(v), "Name must not be a UUID format"),
|
|
@@ -1061,53 +1250,4 @@ z.object({
|
|
|
1061
1250
|
serverTimeMs: z.number().int().nonnegative()
|
|
1062
1251
|
}).passthrough();
|
|
1063
1252
|
//#endregion
|
|
1064
|
-
|
|
1065
|
-
var feishu_exports = /* @__PURE__ */ __exportAll({
|
|
1066
|
-
bindFeishuBot: () => bindFeishuBot,
|
|
1067
|
-
bindFeishuUser: () => bindFeishuUser
|
|
1068
|
-
});
|
|
1069
|
-
/**
|
|
1070
|
-
* Feishu-related core operations: bind-bot, bind-user.
|
|
1071
|
-
*
|
|
1072
|
-
* All agent-scoped calls carry both the member access JWT (Authorization)
|
|
1073
|
-
* and the acting agent UUID (X-Agent-Id); the server's agent-selector
|
|
1074
|
-
* middleware enforces Rule R-RUN.
|
|
1075
|
-
*/
|
|
1076
|
-
async function bindFeishuBot(serverUrl, accessToken, agentId, appId, appSecret) {
|
|
1077
|
-
const res = await fetch(`${serverUrl}/api/v1/agent/me/feishu-bot`, {
|
|
1078
|
-
method: "PUT",
|
|
1079
|
-
headers: {
|
|
1080
|
-
Authorization: `Bearer ${accessToken}`,
|
|
1081
|
-
[AGENT_SELECTOR_HEADER]: agentId,
|
|
1082
|
-
"Content-Type": "application/json"
|
|
1083
|
-
},
|
|
1084
|
-
body: JSON.stringify({
|
|
1085
|
-
appId,
|
|
1086
|
-
appSecret
|
|
1087
|
-
})
|
|
1088
|
-
});
|
|
1089
|
-
if (!res.ok) {
|
|
1090
|
-
const body = await res.json().catch(() => ({}));
|
|
1091
|
-
throw new Error(body.error ?? `Bind Feishu bot failed: HTTP ${res.status}`);
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
async function bindFeishuUser(serverUrl, accessToken, agentId, humanAgentId, feishuUserId, displayName) {
|
|
1095
|
-
const res = await fetch(`${serverUrl}/api/v1/agent/delegated/${encodeURIComponent(humanAgentId)}/feishu-user`, {
|
|
1096
|
-
method: "POST",
|
|
1097
|
-
headers: {
|
|
1098
|
-
Authorization: `Bearer ${accessToken}`,
|
|
1099
|
-
[AGENT_SELECTOR_HEADER]: agentId,
|
|
1100
|
-
"Content-Type": "application/json"
|
|
1101
|
-
},
|
|
1102
|
-
body: JSON.stringify({
|
|
1103
|
-
feishuUserId,
|
|
1104
|
-
displayName
|
|
1105
|
-
})
|
|
1106
|
-
});
|
|
1107
|
-
if (!res.ok) {
|
|
1108
|
-
const body = await res.json().catch(() => ({}));
|
|
1109
|
-
throw new Error(body.error ?? `Bind Feishu user failed: HTTP ${res.status}`);
|
|
1110
|
-
}
|
|
1111
|
-
}
|
|
1112
|
-
//#endregion
|
|
1113
|
-
export { sessionEventMessageSchema as $, createChatSchema as A, isReservedAgentName as B, agentRuntimeConfigPayloadSchema as C, createAdapterConfigSchema as D, connectTokenExchangeSchema as E, dryRunAgentRuntimeConfigSchema as F, paginationQuerySchema as G, loginSchema as H, extractMentions as I, scanMentionTokens as J, refreshTokenSchema as K, imageInlineContentSchema as L, createOrganizationSchema as M, createTaskSchema as N, createAdapterMappingSchema as O, delegateFeishuUserSchema as P, sessionCompletionMessageSchema as Q, inboxPollQuerySchema as R, agentPinnedMessageSchema as S, clientRegisterSchema as T, messageSourceSchema as U, linkTaskChatSchema as V, notificationQuerySchema as W, sendMessageSchema as X, selfServiceFeishuBotSchema as Y, sendToAgentSchema as Z, WS_AUTH_FRAME_TIMEOUT_MS as _, AGENT_NAME_REGEX as a, updateAgentRuntimeConfigSchema as at, adminUpdateTaskSchema as b, AGENT_STATUSES as c, updateMemberSchema as ct, DEFAULT_AGENT_RUNTIME_CONFIG_PAYLOAD as d, updateTaskStatusSchema as dt, sessionEventSchema as et, SYSTEM_CONFIG_DEFAULTS as f, wsAuthFrameSchema as ft, TASK_TERMINAL_STATUSES as g, TASK_STATUSES as h, AGENT_BIND_REJECT_REASONS as i, updateAdapterConfigSchema as it, createMemberSchema as j, createAgentSchema as k, AGENT_TYPES as l, updateOrganizationSchema as lt, TASK_HEALTH_SIGNALS as m, bindFeishuUser as n, sessionStateMessageSchema as nt, AGENT_SELECTOR_HEADER as o, updateAgentSchema as ot, TASK_CREATOR_TYPES as p, runtimeStateMessageSchema as q, feishu_exports as r, taskListQuerySchema as rt, AGENT_SOURCES as s, updateChatSchema as st, bindFeishuBot as t, sessionReconcileRequestSchema as tt, AGENT_VISIBILITY as u, updateSystemConfigSchema as ut, addParticipantSchema as v, agentTypeSchema as w, agentBindRequestSchema as x, adminCreateTaskSchema as y, isRedactedEnvValue as z };
|
|
1253
|
+
export { safeRedirectPath as $, createOrgFromMeSchema as A, imageInlineContentSchema as B, clientRegisterSchema as C, createAgentSchema as D, createAdapterMappingSchema as E, dryRunAgentRuntimeConfigSchema as F, linkTaskChatSchema as G, isRedactedEnvValue as H, extractMentions as I, notificationQuerySchema as J, loginSchema as K, githubCallbackQuerySchema as L, createTaskSchema as M, defaultRuntimeConfigPayload as N, createChatSchema as O, delegateFeishuUserSchema as P, runtimeStateMessageSchema as Q, githubDevCallbackQuerySchema as R, clientCapabilitiesSchema as S, createAdapterConfigSchema as T, isReservedAgentName as U, inboxPollQuerySchema as V, joinByInvitationSchema as W, rebindAgentSchema as X, paginationQuerySchema as Y, refreshTokenSchema as Z, adminUpdateTaskSchema as _, updateOrganizationSchema as _t, AGENT_STATUSES as a, sessionEventMessageSchema as at, agentRuntimeConfigPayloadSchema as b, wsAuthFrameSchema as bt, DEFAULT_RUNTIME_PROVIDER as c, sessionStateMessageSchema as ct, TASK_HEALTH_SIGNALS as d, updateAdapterConfigSchema as dt, scanMentionTokens as et, TASK_STATUSES as f, updateAgentRuntimeConfigSchema as ft, adminCreateTaskSchema as g, updateMemberSchema as gt, addParticipantSchema as h, updateClientCapabilitiesSchema as ht, AGENT_SOURCES as i, sessionCompletionMessageSchema as it, createOrganizationSchema as j, createMemberSchema as k, SYSTEM_CONFIG_DEFAULTS as l, switchOrgSchema as lt, WS_AUTH_FRAME_TIMEOUT_MS as m, updateChatSchema as mt, AGENT_NAME_REGEX as n, sendMessageSchema as nt, AGENT_TYPES as o, sessionEventSchema as ot, TASK_TERMINAL_STATUSES as p, updateAgentSchema as pt, messageSourceSchema as q, AGENT_SELECTOR_HEADER as r, sendToAgentSchema as rt, AGENT_VISIBILITY as s, sessionReconcileRequestSchema as st, AGENT_BIND_REJECT_REASONS as t, selfServiceFeishuBotSchema as tt, TASK_CREATOR_TYPES as u, taskListQuerySchema as ut, agentBindRequestSchema as v, updateSystemConfigSchema as vt, connectTokenExchangeSchema as w, agentTypeSchema as x, agentPinnedMessageSchema as y, updateTaskStatusSchema as yt, githubStartQuerySchema as z };
|