@agent-team-foundation/first-tree-hub 0.9.10 → 0.10.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.
@@ -1,5 +1,5 @@
1
1
  import { d as __exportAll } from "./esm-CYu4tXXn.mjs";
2
- import { c as logFormatSchema, l as logLevelSchema } from "./logger-core-BTmvdflj-DhdipBkV.mjs";
2
+ import { o as logFormatSchema, s as logLevelSchema } from "./logger-core-BTmvdflj-DjW8FM4T.mjs";
3
3
  import { z } from "zod";
4
4
  import { dirname, join } from "node:path";
5
5
  import { homedir } from "node:os";
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import "../observability-DV_fQKqV-CuLWzBxQ.mjs";
3
- import { $ as configureClientLoggerForService, A as checkServerHealth, B as createOwner, C as runMigrations, D as checkDocker, E as checkDatabase, G as setJsonMode, H as resolveReplyToFromEnv, I as stopPostgres, J as FirstTreeHubSDK, L as ClientRuntime, M as checkWebSocket, N as printResults, O as checkNodeVersion, Q as applyClientLoggerConfig, R as handleClientOrgMismatch, S as uninstallClientService, T as checkClientConfig, W as print, X as SessionRegistry, Y as SdkError, Z as cleanWorkspaces, _ as runHomeMigration, a as showServiceLogs, b as isServiceSupported, c as COMMAND_VERSION, d as promptMissingFields, f as formatCheckReport, g as saveOnboardState, h as onboardCreate, i as parseDuration, j as checkServerReachable, k as checkServerConfig, l as isInteractive, m as onboardCheck, n as declineUpdate, o as validateLevel, p as loadOnboardState, q as ClientOrgMismatchError, r as promptUpdate, s as startServer, t as createExecuteUpdate, u as promptAddAgent, v as getClientServiceStatus, w as checkAgentConfigs, y as installClientService } from "../core-BWaSYpXv.mjs";
4
- import "../logger-core-BTmvdflj-DhdipBkV.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-hh_PkTu6.mjs";
6
- import { n as bindFeishuUser, t as bindFeishuBot } from "../feishu-BJaN64iR.mjs";
2
+ import "../observability-DV_fQKqV-oxfXX6Z2.mjs";
3
+ import { $ as configureClientLoggerForService, A as installClientService, B as createOwner, C as checkNodeVersion, D as checkWebSocket, E as checkServerReachable, G as setJsonMode, H as resolveReplyToFromEnv, I as stopPostgres, J as FirstTreeHubSDK, L as ClientRuntime, O as printResults, Q as applyClientLoggerConfig, R as handleClientOrgMismatch, S as checkDocker, T as checkServerHealth, W as print, X as SessionRegistry, Y as SdkError, Z as cleanWorkspaces, _ as runMigrations, a as COMMAND_VERSION, b as checkClientConfig, c as promptMissingFields, d as onboardCheck, f as onboardCreate, g as migrateLocalAgentDirs, h as createApiNameResolver, i as startServer, j as isServiceSupported, k as getClientServiceStatus, l as formatCheckReport, m as runHomeMigration, n as declineUpdate, o as isInteractive, p as saveOnboardState, q as ClientOrgMismatchError, r as promptUpdate, s as promptAddAgent, t as createExecuteUpdate, u as loadOnboardState, v as checkAgentConfigs, w as checkServerConfig, x as checkDatabase, y as checkBackgroundService } from "../core-BgiFGT7Y.mjs";
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-CtVqQA8a.mjs";
6
+ import { n as bindFeishuUser, t as bindFeishuBot } from "../feishu-DEmwoNn_.mjs";
7
7
  import { join } from "node:path";
8
8
  import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync } from "node:fs";
9
9
  import { Command } from "commander";
@@ -295,16 +295,10 @@ async function resolveAgent(serverUrl, adminToken, agentName) {
295
295
  function registerAgentCommands(program) {
296
296
  const agent = program.command("agent").description("Agent management — config, bindings, messaging");
297
297
  registerAgentConfigCommands(agent);
298
- agent.command("add [name]").description("Register a local alias for an existing Hub agent (stores agentId)").option("--agent-id <id>", "Agent UUID on the Hub").action(async (name, options) => {
298
+ agent.command("add").description("Register an existing Hub agent on this client (uses the agent name from the Hub)").option("--agent-id <id>", "Agent UUID on the Hub").action(async (options) => {
299
299
  try {
300
- let agentName = name;
301
- let agentId = options?.agentId;
302
- if (!agentName || !agentId) {
303
- const result = await promptAddAgent();
304
- agentName = agentName ?? result.name;
305
- agentId = agentId ?? result.agentId;
306
- }
307
- if (!agentName || !agentId) fail("MISSING_AGENT_ARGS", "Both agent name and agent-id are required.", 2);
300
+ const { name: agentName, agentId } = await promptAddAgent({ agentId: options?.agentId });
301
+ if (!agentName || !agentId) fail("MISSING_AGENT_ARGS", "Agent UUID (and a hub name for that UUID) are required.", 2);
308
302
  const agentDir = join(DEFAULT_CONFIG_DIR, "agents", agentName);
309
303
  mkdirSync(agentDir, {
310
304
  recursive: true,
@@ -323,7 +317,7 @@ function registerAgentCommands(program) {
323
317
  process.exit(1);
324
318
  }
325
319
  });
326
- agent.command("remove <name>").description("Remove a local agent alias and its runtime data").action((name) => {
320
+ agent.command("remove <name>").description("Remove an agent from this client and delete its local runtime data (config dir, workspace, session state)").action((name) => {
327
321
  const agentDir = join(DEFAULT_CONFIG_DIR, "agents", name);
328
322
  if (!existsSync(agentDir)) {
329
323
  print.line(` Agent "${name}" not found.\n`);
@@ -458,7 +452,7 @@ function registerAgentCommands(program) {
458
452
  fail("BIND_CLIENT_ERROR", error instanceof Error ? error.message : String(error));
459
453
  }
460
454
  });
461
- bind.command("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("--agent <name>", "Local agent alias (default: first configured)").option("--server <url>", "Hub server URL").action(async (options) => {
455
+ bind.command("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("--agent <name>", "Agent name on the Hub (default: first configured on this client)").option("--server <url>", "Hub server URL").action(async (options) => {
462
456
  try {
463
457
  if (options.platform !== "feishu") fail("UNSUPPORTED_PLATFORM", `Platform "${options.platform}" is not supported. Use "feishu".`);
464
458
  const serverUrl = resolveServerUrl(options.server);
@@ -473,7 +467,7 @@ function registerAgentCommands(program) {
473
467
  fail("BIND_BOT_ERROR", error instanceof Error ? error.message : String(error));
474
468
  }
475
469
  });
476
- bind.command("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("--agent <name>", "Local agent alias (default: first configured)").option("--server <url>", "Hub server URL").action(async (humanAgentId, options) => {
470
+ bind.command("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("--agent <name>", "Agent name on the Hub (default: first configured on this client)").option("--server <url>", "Hub server URL").action(async (humanAgentId, options) => {
477
471
  try {
478
472
  if (options.platform !== "feishu") fail("UNSUPPORTED_PLATFORM", `Platform "${options.platform}" is not supported. Use "feishu".`);
479
473
  const serverUrl = resolveServerUrl(options.server);
@@ -489,7 +483,7 @@ function registerAgentCommands(program) {
489
483
  fail("BIND_USER_ERROR", error instanceof Error ? error.message : String(error));
490
484
  }
491
485
  });
492
- agent.command("send <target> [message]").description("Send a message to an agent or chat").option("-f, --format <format>", "Message format (text|markdown|card)", "text").option("--chat", "Treat target as chat ID instead of agent ID").option("-m, --metadata <json>", "JSON metadata to attach").option("--reply-to <messageId>", "Message ID to reply to").option("--reply-to-inbox <inboxId>", "Cross-chat reply target inbox").option("--reply-to-chat <chatId>", "Cross-chat reply target chat").option("--agent <name>", "Local agent alias (default: first configured)").action(async (target, message, options) => {
486
+ agent.command("send <target> [message]").description("Send a message to an agent or chat").option("-f, --format <format>", "Message format (text|markdown|card)", "text").option("--chat", "Treat target as chat ID instead of agent ID").option("-m, --metadata <json>", "JSON metadata to attach").option("--reply-to <messageId>", "Message ID to reply to").option("--reply-to-inbox <inboxId>", "Cross-chat reply target inbox").option("--reply-to-chat <chatId>", "Cross-chat reply target chat").option("--agent <name>", "Agent name on the Hub (default: first configured on this client)").action(async (target, message, options) => {
493
487
  try {
494
488
  const content = message ?? await readStdin();
495
489
  if (!content) fail("NO_MESSAGE", "No message provided. Pass as argument or pipe via stdin.", 2);
@@ -523,7 +517,7 @@ function registerAgentCommands(program) {
523
517
  handleSdkError(error);
524
518
  }
525
519
  });
526
- agent.command("chats").description("List chats this agent participates in").option("-l, --limit <number>", "Maximum chats to return (1-100)", "20").option("--cursor <cursor>", "Pagination cursor from previous response").option("--agent <name>", "Local agent alias (default: first configured)").action(async (options) => {
520
+ agent.command("chats").description("List chats this agent participates in").option("-l, --limit <number>", "Maximum chats to return (1-100)", "20").option("--cursor <cursor>", "Pagination cursor from previous response").option("--agent <name>", "Agent name on the Hub (default: first configured on this client)").action(async (options) => {
527
521
  try {
528
522
  const limit = parseLimit(options.limit, 100);
529
523
  success(await createSdk(options.agent).listChats({
@@ -534,7 +528,7 @@ function registerAgentCommands(program) {
534
528
  handleSdkError(error);
535
529
  }
536
530
  });
537
- agent.command("history <chatId>").description("View message history in a chat").option("-l, --limit <number>", "Maximum messages to return (1-100)", "20").option("--cursor <cursor>", "Pagination cursor from previous response").option("--agent <name>", "Local agent alias (default: first configured)").action(async (chatId, options) => {
531
+ agent.command("history <chatId>").description("View message history in a chat").option("-l, --limit <number>", "Maximum messages to return (1-100)", "20").option("--cursor <cursor>", "Pagination cursor from previous response").option("--agent <name>", "Agent name on the Hub (default: first configured on this client)").action(async (chatId, options) => {
538
532
  try {
539
533
  const limit = parseLimit(options.limit, 100);
540
534
  success(await createSdk(options.agent).listMessages(chatId, {
@@ -738,14 +732,14 @@ function registerAgentCommands(program) {
738
732
  fail("CHAT_ERROR", error instanceof Error ? error.message : String(error));
739
733
  }
740
734
  });
741
- agent.command("register").description("Register this agent and return identity info").option("--agent <name>", "Local agent alias (default: first configured)").action(async (options) => {
735
+ agent.command("register").description("Register this agent and return identity info").option("--agent <name>", "Agent name on the Hub (default: first configured on this client)").action(async (options) => {
742
736
  try {
743
737
  success(await createSdk(options.agent).register());
744
738
  } catch (error) {
745
739
  handleSdkError(error);
746
740
  }
747
741
  });
748
- agent.command("pull").description("Pull pending messages from inbox").option("-l, --limit <number>", "Maximum entries to return", "10").option("-a, --ack", "Automatically ACK entries after pulling").option("--agent <name>", "Local agent alias (default: first configured)").action(async (options) => {
742
+ agent.command("pull").description("Pull pending messages from inbox").option("-l, --limit <number>", "Maximum entries to return", "10").option("-a, --ack", "Automatically ACK entries after pulling").option("--agent <name>", "Agent name on the Hub (default: first configured on this client)").action(async (options) => {
749
743
  try {
750
744
  const sdk = createSdk(options.agent);
751
745
  const limit = parseLimit(options.limit, 50);
@@ -922,6 +916,17 @@ function registerConnectCommand(parent) {
922
916
  if (options.service === false) print.line(" (--no-service) running inline — Ctrl+C to stop\n");
923
917
  else print.line(` Background service not supported on ${process.platform}; running inline.\n`);
924
918
  const agentsDir = join(DEFAULT_CONFIG_DIR, "agents");
919
+ try {
920
+ await migrateLocalAgentDirs({
921
+ agentsDir,
922
+ workspacesDir: join(DEFAULT_DATA_DIR, "workspaces"),
923
+ sessionsDir: join(DEFAULT_DATA_DIR, "sessions"),
924
+ resolver: createApiNameResolver(config.server.url, () => ensureFreshAccessToken())
925
+ });
926
+ } catch (err) {
927
+ const msg = err instanceof Error ? err.message : String(err);
928
+ print.status("⚠️", `agent-dir migration skipped: ${msg}`);
929
+ }
925
930
  const agents = loadAgents({
926
931
  schema: agentConfigSchema,
927
932
  agentsDir
@@ -984,6 +989,17 @@ function registerClientCommands(program) {
984
989
  applyClientLoggerConfig({ level: config.logLevel });
985
990
  if (process.env.FIRST_TREE_HUB_SERVICE_MODE === "1") configureClientLoggerForService(join(DEFAULT_HOME_DIR, "logs"));
986
991
  const agentsDir = join(DEFAULT_CONFIG_DIR, "agents");
992
+ try {
993
+ await migrateLocalAgentDirs({
994
+ agentsDir,
995
+ workspacesDir: join(DEFAULT_DATA_DIR, "workspaces"),
996
+ sessionsDir: join(DEFAULT_DATA_DIR, "sessions"),
997
+ resolver: createApiNameResolver(config.server.url, () => ensureFreshAccessToken())
998
+ });
999
+ } catch (err) {
1000
+ const msg = err instanceof Error ? err.message : String(err);
1001
+ print.status("⚠️", `agent-dir migration skipped: ${msg}`);
1002
+ }
987
1003
  const agents = loadAgents({
988
1004
  schema: agentConfigSchema,
989
1005
  agentsDir
@@ -1031,7 +1047,8 @@ function registerClientCommands(program) {
1031
1047
  checkClientConfig(),
1032
1048
  await checkServerReachable(),
1033
1049
  checkAgentConfigs(),
1034
- await checkWebSocket()
1050
+ await checkWebSocket(),
1051
+ checkBackgroundService()
1035
1052
  ]);
1036
1053
  });
1037
1054
  client.command("stop").description("Stop the client (sends SIGTERM to running process)").action(() => {
@@ -1056,62 +1073,6 @@ function registerClientCommands(program) {
1056
1073
  print.line(" No agents directory found.\n");
1057
1074
  }
1058
1075
  });
1059
- const service = client.command("service").description("Install/uninstall the background service that keeps this computer online");
1060
- service.command("install").description("Install as a background service — auto-starts on login/boot").action(() => {
1061
- if (!isServiceSupported()) {
1062
- print.line(` Background service is not supported on ${process.platform}.\n Run \`first-tree-hub client start\` manually to keep the computer online.
1063
- `);
1064
- process.exit(1);
1065
- }
1066
- try {
1067
- const info = installClientService();
1068
- print.line(`\n \u2713 Installed as a background service (${info.platform}).\n`);
1069
- print.line(` Unit: ${info.unitPath}\n`);
1070
- print.line(` Logs: ${info.logDir}\n`);
1071
- if (info.state === "active") print.line(` State: running${info.detail ? ` (${info.detail})` : ""}\n`);
1072
- else print.line(` State: ${info.state}${info.detail ? ` (${info.detail})` : ""}\n`);
1073
- print.line("\n You can close this terminal — the computer stays online.\n");
1074
- } catch (error) {
1075
- fail("SERVICE_INSTALL_ERROR", error instanceof Error ? error.message : String(error));
1076
- }
1077
- });
1078
- service.command("status").description("Show background service state").action(() => {
1079
- const info = getClientServiceStatus();
1080
- if (info.platform === "unsupported") {
1081
- print.line(` Not supported on ${process.platform}.\n`);
1082
- return;
1083
- }
1084
- print.line(`\n ${info.platform}: ${info.label}\n`);
1085
- print.line(` Unit: ${info.unitPath}\n`);
1086
- print.line(` Logs: ${info.logDir}\n`);
1087
- print.line(` State: ${info.state}${info.detail ? ` (${info.detail})` : ""}\n\n`);
1088
- });
1089
- service.command("uninstall").description("Stop and remove the background service").action(() => {
1090
- if (!isServiceSupported()) {
1091
- print.line(` Not supported on ${process.platform}.\n`);
1092
- return;
1093
- }
1094
- try {
1095
- const info = uninstallClientService();
1096
- print.line(`\n \u2713 Uninstalled background service (${info.platform}).\n\n`);
1097
- } catch (error) {
1098
- fail("SERVICE_UNINSTALL_ERROR", error instanceof Error ? error.message : String(error));
1099
- }
1100
- });
1101
- service.command("logs").description("Read background-service logs (pretty by default)").option("-f, --tail", "follow new lines as they arrive (Ctrl+C to stop)", false).option("--since <duration>", "only show records newer than duration (e.g. 10s, 5m, 2h, 1d)").option("--level <level>", "minimum level (trace|debug|info|warn|error|fatal)").option("--json", "emit raw NDJSON lines instead of pretty formatting", false).action(async (options) => {
1102
- try {
1103
- const level = validateLevel(options.level);
1104
- const sinceMs = options.since ? parseDuration(options.since) : void 0;
1105
- await showServiceLogs({
1106
- tail: options.tail === true,
1107
- level,
1108
- sinceMs,
1109
- json: options.json === true
1110
- });
1111
- } catch (error) {
1112
- fail("SERVICE_LOGS_ERROR", error instanceof Error ? error.message : String(error));
1113
- }
1114
- });
1115
1076
  client.command("hub-list").description("List clients on the Hub server").option("--server <url>", "Hub server URL").action(async (options) => {
1116
1077
  try {
1117
1078
  const serverUrl = resolveServerUrl(options.server);
@@ -1262,13 +1223,13 @@ function isSecretField(schema, dotPath) {
1262
1223
  //#region src/commands/onboard.ts
1263
1224
  async function promptMissing(args) {
1264
1225
  if (!args.server) try {
1265
- const { resolveServerUrl } = await import("../bootstrap-hh_PkTu6.mjs").then((n) => n.t);
1226
+ const { resolveServerUrl } = await import("../bootstrap-CtVqQA8a.mjs").then((n) => n.t);
1266
1227
  resolveServerUrl();
1267
1228
  } catch {
1268
1229
  args.server = await input({ message: "Hub server URL:" });
1269
1230
  saveOnboardState(args);
1270
1231
  }
1271
- const { loadCredentials } = await import("../bootstrap-hh_PkTu6.mjs").then((n) => n.t);
1232
+ const { loadCredentials } = await import("../bootstrap-CtVqQA8a.mjs").then((n) => n.t);
1272
1233
  if (!loadCredentials()) throw new Error("No saved credentials. Run `first-tree-hub client connect <server-url>` before onboarding.");
1273
1234
  if (!args.id) {
1274
1235
  args.id = await input({ message: "Agent ID:" });