@agent-team-foundation/first-tree-hub 0.2.0 → 0.3.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/bootstrap-B9JsJR3Z.mjs +583 -0
- package/dist/cli/index.mjs +220 -3
- package/dist/{core-CD3xEbyB.mjs → core-D-c9r7D6.mjs} +1956 -941
- package/dist/drizzle/0006_agent_tree_path.sql +1 -0
- package/dist/drizzle/meta/_journal.json +7 -0
- package/dist/feishu-Y4m2zFc3.mjs +51 -0
- package/dist/index.mjs +4 -2
- package/dist/rolldown-runtime-twds-ZHy.mjs +14 -0
- package/dist/web/assets/index-B1dQmYGJ.js +234 -0
- package/dist/web/assets/index-BURu6jt9.css +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/index-CHZINY3I.js +0 -229
- package/dist/web/assets/index-Drt799Rs.css +0 -1
package/dist/cli/index.mjs
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { _ as resetConfig, b as serverConfigSchema, c as DEFAULT_CONFIG_DIR, d as clientConfigSchema, g as readConfigFile, h as loadAgents, l as DEFAULT_DATA_DIR, m as initConfig, o as resolveAgentToken, p as getConfigValue, s as resolveServerUrl, t as bootstrapToken, u as agentConfigSchema, v as resetConfigMeta, x as setConfigValue } from "../bootstrap-B9JsJR3Z.mjs";
|
|
3
|
+
import { A as ClientRuntime, B as registerBuiltinHandlers, C as checkWebSocket, F as SdkError, I as SessionRegistry, L as cleanWorkspaces, M as AgentSlot, N as DEFAULT_WORKSPACE_TTL_MS, P as FirstTreeHubSDK, R as getHandlerFactory, S as checkServerReachable, V as createAdminUser, _ as checkDocker, a as formatCheckReport, b as checkServerConfig, c as onboardContinue, d as runMigrations, f as checkAgentConfigs, g as checkDatabase, h as checkContextTreeRepo, i as promptMissingFields, j as AgentRuntime, k as stopPostgres, l as onboardCreate, m as checkClientConfig, n as isInteractive, o as loadOnboardState, p as checkAgentTokens, r as promptAddAgent, s as onboardCheck, t as startServer, u as saveOnboardState, v as checkGitHubToken, w as printResults, x as checkServerHealth, y as checkNodeVersion, z as loadRuntimeConfig } from "../core-D-c9r7D6.mjs";
|
|
4
|
+
import { n as bindFeishuUser, t as bindFeishuBot } from "../feishu-Y4m2zFc3.mjs";
|
|
3
5
|
import { createRequire } from "node:module";
|
|
4
6
|
import { Command } from "commander";
|
|
5
|
-
import { existsSync, mkdirSync, rmSync } from "node:fs";
|
|
7
|
+
import { existsSync, mkdirSync, readdirSync, rmSync } from "node:fs";
|
|
6
8
|
import { join } from "node:path";
|
|
9
|
+
import { confirm, input, select } from "@inquirer/prompts";
|
|
7
10
|
//#region src/commands/admin.ts
|
|
8
11
|
function registerAdminCommands(program) {
|
|
9
12
|
program.command("admin").description("Admin user management").command("create").description("Create an admin user").option("-u, --username <name>", "Admin username", "admin").option("-p, --password <pass>", "Admin password (auto-generated if omitted)").action(async (options) => {
|
|
@@ -84,6 +87,37 @@ function registerAgentCommands(program) {
|
|
|
84
87
|
});
|
|
85
88
|
}
|
|
86
89
|
//#endregion
|
|
90
|
+
//#region src/commands/bind.ts
|
|
91
|
+
function registerBindCommands(program) {
|
|
92
|
+
program.command("bind-bot").description("Bind a Feishu bot to this agent (self-service)").requiredOption("--platform <platform>", "Platform: feishu").requiredOption("--app-id <id>", "Feishu bot App ID").requiredOption("--app-secret <secret>", "Feishu bot App Secret").option("--server <url>", "Hub server URL").action(async (options) => {
|
|
93
|
+
try {
|
|
94
|
+
if (options.platform !== "feishu") fail("UNSUPPORTED_PLATFORM", `Platform "${options.platform}" is not supported. Use "feishu".`);
|
|
95
|
+
await bindFeishuBot(resolveServerUrl(options.server), resolveAgentToken(), options.appId, options.appSecret);
|
|
96
|
+
process.stderr.write("Feishu bot bound successfully.\n");
|
|
97
|
+
success({
|
|
98
|
+
platform: "feishu",
|
|
99
|
+
bound: true
|
|
100
|
+
});
|
|
101
|
+
} catch (error) {
|
|
102
|
+
fail("BIND_BOT_ERROR", error instanceof Error ? error.message : String(error));
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
program.command("bind-user <humanAgentId>").description("Bind a Feishu user to a human agent (via delegate_mention)").requiredOption("--platform <platform>", "Platform: feishu").requiredOption("--feishu-id <id>", "Feishu user ID (ou_xxx)").option("--server <url>", "Hub server URL").action(async (humanAgentId, options) => {
|
|
106
|
+
try {
|
|
107
|
+
if (options.platform !== "feishu") fail("UNSUPPORTED_PLATFORM", `Platform "${options.platform}" is not supported. Use "feishu".`);
|
|
108
|
+
await bindFeishuUser(resolveServerUrl(options.server), resolveAgentToken(), humanAgentId, options.feishuId);
|
|
109
|
+
process.stderr.write(`Feishu user ${options.feishuId} bound to ${humanAgentId}.\n`);
|
|
110
|
+
success({
|
|
111
|
+
platform: "feishu",
|
|
112
|
+
humanAgentId,
|
|
113
|
+
feishuUserId: options.feishuId
|
|
114
|
+
});
|
|
115
|
+
} catch (error) {
|
|
116
|
+
fail("BIND_USER_ERROR", error instanceof Error ? error.message : String(error));
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
//#endregion
|
|
87
121
|
//#region src/cli/util.ts
|
|
88
122
|
function resolveConfig() {
|
|
89
123
|
const token = process.env.FIRST_TREE_HUB_TOKEN;
|
|
@@ -237,7 +271,7 @@ function registerClientCommands(program) {
|
|
|
237
271
|
process.exit(1);
|
|
238
272
|
}
|
|
239
273
|
});
|
|
240
|
-
client.command("remove <name>").description("Remove an agent instance").action((name) => {
|
|
274
|
+
client.command("remove <name>").description("Remove an agent instance and its runtime data").action((name) => {
|
|
241
275
|
const agentDir = join(DEFAULT_CONFIG_DIR, "agents", name);
|
|
242
276
|
if (!existsSync(agentDir)) {
|
|
243
277
|
process.stderr.write(` Agent "${name}" not found.\n`);
|
|
@@ -247,6 +281,11 @@ function registerClientCommands(program) {
|
|
|
247
281
|
recursive: true,
|
|
248
282
|
force: true
|
|
249
283
|
});
|
|
284
|
+
rmSync(join(DEFAULT_DATA_DIR, "workspaces", name), {
|
|
285
|
+
recursive: true,
|
|
286
|
+
force: true
|
|
287
|
+
});
|
|
288
|
+
rmSync(join(DEFAULT_DATA_DIR, "sessions", `${name}.json`), { force: true });
|
|
250
289
|
process.stderr.write(` Agent "${name}" removed.\n`);
|
|
251
290
|
});
|
|
252
291
|
client.command("list").description("List configured agents").action(() => {
|
|
@@ -268,6 +307,28 @@ function registerClientCommands(program) {
|
|
|
268
307
|
process.stderr.write(" No agents configured.\n");
|
|
269
308
|
}
|
|
270
309
|
});
|
|
310
|
+
client.command("workspace").description("Manage agent workspaces").command("clean [agent-name]").description("Remove stale workspace directories (older than TTL with no active session)").option("--ttl <days>", "TTL in days", String(DEFAULT_WORKSPACE_TTL_MS / (1440 * 60 * 1e3))).action((agentName, options) => {
|
|
311
|
+
const defaultDays = DEFAULT_WORKSPACE_TTL_MS / (1440 * 60 * 1e3);
|
|
312
|
+
const ttlMs = Number.parseInt(options?.ttl ?? String(defaultDays), 10) * 24 * 60 * 60 * 1e3;
|
|
313
|
+
const workspacesDir = join(DEFAULT_DATA_DIR, "workspaces");
|
|
314
|
+
if (!existsSync(workspacesDir)) {
|
|
315
|
+
process.stderr.write(" No workspaces found.\n");
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
const agentNames = agentName ? [agentName] : readdirSync(workspacesDir);
|
|
319
|
+
let totalRemoved = 0;
|
|
320
|
+
for (const name of agentNames) {
|
|
321
|
+
const agentWorkspaceRoot = join(workspacesDir, name);
|
|
322
|
+
if (!existsSync(agentWorkspaceRoot)) continue;
|
|
323
|
+
const persisted = new SessionRegistry(join(DEFAULT_DATA_DIR, "sessions", `${name}.json`)).load();
|
|
324
|
+
const activeChatIds = /* @__PURE__ */ new Set();
|
|
325
|
+
for (const [chatId, data] of persisted) if (data.status !== "evicted") activeChatIds.add(chatId);
|
|
326
|
+
const removed = cleanWorkspaces(agentWorkspaceRoot, activeChatIds, ttlMs);
|
|
327
|
+
totalRemoved += removed.length;
|
|
328
|
+
for (const chatId of removed) process.stderr.write(` Removed: ${name}/${chatId}\n`);
|
|
329
|
+
}
|
|
330
|
+
process.stderr.write(` ${totalRemoved} workspace(s) cleaned.\n`);
|
|
331
|
+
});
|
|
271
332
|
}
|
|
272
333
|
//#endregion
|
|
273
334
|
//#region src/commands/config.ts
|
|
@@ -395,6 +456,142 @@ function registerHistoryCommand(program) {
|
|
|
395
456
|
});
|
|
396
457
|
}
|
|
397
458
|
//#endregion
|
|
459
|
+
//#region src/commands/onboard.ts
|
|
460
|
+
async function promptMissing(args) {
|
|
461
|
+
let ghUsername = null;
|
|
462
|
+
try {
|
|
463
|
+
const { getGitHubUsername } = await import("../bootstrap-B9JsJR3Z.mjs").then((n) => n.n);
|
|
464
|
+
ghUsername = getGitHubUsername();
|
|
465
|
+
} catch {}
|
|
466
|
+
if (!args.id) {
|
|
467
|
+
args.id = await input({
|
|
468
|
+
message: "Member ID (directory name):",
|
|
469
|
+
default: ghUsername ?? void 0
|
|
470
|
+
});
|
|
471
|
+
saveOnboardState(args);
|
|
472
|
+
}
|
|
473
|
+
if (!args.type) {
|
|
474
|
+
args.type = await select({
|
|
475
|
+
message: "Agent type:",
|
|
476
|
+
choices: [
|
|
477
|
+
{
|
|
478
|
+
name: "human",
|
|
479
|
+
value: "human"
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
name: "personal_assistant",
|
|
483
|
+
value: "personal_assistant"
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
name: "autonomous_agent",
|
|
487
|
+
value: "autonomous_agent"
|
|
488
|
+
}
|
|
489
|
+
]
|
|
490
|
+
});
|
|
491
|
+
saveOnboardState(args);
|
|
492
|
+
}
|
|
493
|
+
if (!args.role) {
|
|
494
|
+
args.role = await input({ message: "Role:" });
|
|
495
|
+
saveOnboardState(args);
|
|
496
|
+
}
|
|
497
|
+
if (!args.domains) {
|
|
498
|
+
args.domains = await input({ message: "Domains (comma-separated):" });
|
|
499
|
+
saveOnboardState(args);
|
|
500
|
+
}
|
|
501
|
+
if (!args.displayName) {
|
|
502
|
+
const name = await input({ message: `Display name (Enter to use "${args.id}"):` });
|
|
503
|
+
if (name) {
|
|
504
|
+
args.displayName = name;
|
|
505
|
+
saveOnboardState(args);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
if (!args.assistant) {
|
|
509
|
+
if (await confirm({
|
|
510
|
+
message: "Create a personal assistant?",
|
|
511
|
+
default: false
|
|
512
|
+
})) {
|
|
513
|
+
args.assistant = await input({
|
|
514
|
+
message: "Assistant ID:",
|
|
515
|
+
default: `${args.id}-assistant`
|
|
516
|
+
});
|
|
517
|
+
saveOnboardState(args);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
if (!args.server) try {
|
|
521
|
+
const { resolveServerUrl } = await import("../bootstrap-B9JsJR3Z.mjs").then((n) => n.n);
|
|
522
|
+
resolveServerUrl();
|
|
523
|
+
} catch {
|
|
524
|
+
args.server = await input({ message: "Hub server URL:" });
|
|
525
|
+
saveOnboardState(args);
|
|
526
|
+
}
|
|
527
|
+
if (!args.feishuBotAppId) {
|
|
528
|
+
if (await confirm({
|
|
529
|
+
message: "Bind Feishu bot?",
|
|
530
|
+
default: false
|
|
531
|
+
})) {
|
|
532
|
+
args.feishuBotAppId = await input({ message: "Feishu App ID:" });
|
|
533
|
+
args.feishuBotAppSecret = await input({ message: "Feishu App Secret:" });
|
|
534
|
+
saveOnboardState(args);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
function registerOnboardCommand(program) {
|
|
539
|
+
program.command("onboard").description("Onboard a new member to First Tree Hub (end-to-end)").option("--id <id>", "Member ID (directory name)").option("--type <type>", "Agent type: human | personal_assistant | autonomous_agent").option("--display-name <name>", "Display name (defaults to id)").option("--role <role>", "Role description").option("--domains <domains>", "Comma-separated domains").option("--assistant <id>", "Also create a personal_assistant with this ID").option("--delegate-mention <id>", "Set delegate_mention field").option("--server <url>", "Hub server URL").option("--feishu-bot-app-id <id>", "Feishu bot App ID").option("--feishu-bot-app-secret <secret>", "Feishu bot App Secret").option("--check", "Dry-run: show readiness checklist without executing").option("--continue", "Resume after PR merge (Phase 2)").action(async (options) => {
|
|
540
|
+
try {
|
|
541
|
+
const args = {
|
|
542
|
+
...loadOnboardState() ?? {},
|
|
543
|
+
...options.id && { id: options.id },
|
|
544
|
+
...options.type && { type: options.type },
|
|
545
|
+
...options.displayName && { displayName: options.displayName },
|
|
546
|
+
...options.role && { role: options.role },
|
|
547
|
+
...options.domains && { domains: options.domains },
|
|
548
|
+
...options.assistant && { assistant: options.assistant },
|
|
549
|
+
...options.delegateMention && { delegateMention: options.delegateMention },
|
|
550
|
+
...options.server && { server: options.server },
|
|
551
|
+
...options.feishuBotAppId && { feishuBotAppId: options.feishuBotAppId },
|
|
552
|
+
...options.feishuBotAppSecret && { feishuBotAppSecret: options.feishuBotAppSecret },
|
|
553
|
+
check: options.check,
|
|
554
|
+
continue: options.continue
|
|
555
|
+
};
|
|
556
|
+
if (!args.feishuBotAppId && process.env.FEISHU_APP_ID) args.feishuBotAppId = process.env.FEISHU_APP_ID;
|
|
557
|
+
if (!args.feishuBotAppSecret && process.env.FEISHU_APP_SECRET) args.feishuBotAppSecret = process.env.FEISHU_APP_SECRET;
|
|
558
|
+
if (args.continue) {
|
|
559
|
+
await onboardContinue(args);
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
if (args.check) {
|
|
563
|
+
const items = await onboardCheck(args);
|
|
564
|
+
const report = formatCheckReport(items);
|
|
565
|
+
process.stderr.write(`\nOnboard Check: ${args.id ?? "(no id)"}\n\n${report}\n\n`);
|
|
566
|
+
if (items.some((i) => i.status === "missing_required" || i.status === "error")) process.exit(1);
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
if (isInteractive()) await promptMissing(args);
|
|
570
|
+
const items = await onboardCheck(args);
|
|
571
|
+
if (items.some((i) => i.status === "missing_required" || i.status === "error")) {
|
|
572
|
+
const report = formatCheckReport(items);
|
|
573
|
+
process.stderr.write(`\nOnboard Check: ${args.id ?? "(no id)"}\n\n${report}\n\n`);
|
|
574
|
+
fail("MISSING_PARAMS", "Required parameters are missing. See checklist above.");
|
|
575
|
+
}
|
|
576
|
+
const result = await onboardCreate(args);
|
|
577
|
+
process.stderr.write(`\nPR created: ${result.prUrl}\n`);
|
|
578
|
+
process.stderr.write("Review and merge the PR, then run:\n");
|
|
579
|
+
process.stderr.write(" first-tree-hub onboard --continue\n\n");
|
|
580
|
+
success({
|
|
581
|
+
phase: "create",
|
|
582
|
+
prUrl: result.prUrl
|
|
583
|
+
});
|
|
584
|
+
} catch (error) {
|
|
585
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
586
|
+
if (isInteractive()) {
|
|
587
|
+
process.stderr.write(`\n❌ ${msg}\n\n`);
|
|
588
|
+
process.exit(1);
|
|
589
|
+
}
|
|
590
|
+
fail("ONBOARD_ERROR", msg);
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
//#endregion
|
|
398
595
|
//#region src/commands/send.ts
|
|
399
596
|
const MAX_STDIN_BYTES = 10 * 1024 * 1024;
|
|
400
597
|
/** Read all of stdin as a string. Returns null if stdin is a TTY. */
|
|
@@ -555,6 +752,23 @@ function formatUptime(seconds) {
|
|
|
555
752
|
return `${mins}m`;
|
|
556
753
|
}
|
|
557
754
|
//#endregion
|
|
755
|
+
//#region src/commands/token.ts
|
|
756
|
+
function registerTokenCommands(program) {
|
|
757
|
+
program.command("token").description("Agent token management").command("bootstrap <agentId>").description("Bootstrap a token using GitHub identity (requires gh CLI)").option("--save-to <target>", "Save token to: \"agent\" (default) or a file path", "agent").option("--server <url>", "Hub server URL").action(async (agentId, options) => {
|
|
758
|
+
try {
|
|
759
|
+
const result = await bootstrapToken(resolveServerUrl(options.server), agentId, { saveTo: options.saveTo });
|
|
760
|
+
if (options.saveTo === "agent") process.stderr.write(`Token saved to ~/.first-tree-hub/agents/${agentId}/agent.yaml\n`);
|
|
761
|
+
else process.stderr.write(`Token saved to ${options.saveTo}\n`);
|
|
762
|
+
success({
|
|
763
|
+
agentId: result.agentId,
|
|
764
|
+
tokenSaved: true
|
|
765
|
+
});
|
|
766
|
+
} catch (error) {
|
|
767
|
+
fail("BOOTSTRAP_ERROR", error instanceof Error ? error.message : String(error));
|
|
768
|
+
}
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
//#endregion
|
|
558
772
|
//#region src/cli/connect.ts
|
|
559
773
|
function registerConnectCommand(program) {
|
|
560
774
|
program.command("connect").description("Connect a single agent to server and process messages").option("-t, --type <type>", "Handler type", "claude-code").option("--concurrency <n>", "Max parallel message processing", "5").option("--server <url>", "Override FIRST_TREE_HUB_SERVER").action(async (options) => {
|
|
@@ -628,6 +842,9 @@ registerStatusCommand(program);
|
|
|
628
842
|
registerConnectCommand(program);
|
|
629
843
|
registerStartCommand(program);
|
|
630
844
|
registerAgentCommands(program);
|
|
845
|
+
registerOnboardCommand(program);
|
|
846
|
+
registerTokenCommands(program);
|
|
847
|
+
registerBindCommands(program);
|
|
631
848
|
registerSendCommand(program);
|
|
632
849
|
registerChatsCommand(program);
|
|
633
850
|
registerHistoryCommand(program);
|