@agent-team-foundation/first-tree-hub 0.6.3 → 0.7.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.
@@ -550,7 +550,7 @@ function isTokenExpired(token) {
550
550
  if (parts.length !== 3 || !parts[1]) return true;
551
551
  const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString());
552
552
  if (!payload.exp) return false;
553
- return payload.exp * 1e3 < Date.now() - 3e4;
553
+ return payload.exp * 1e3 < Date.now() + 3e4;
554
554
  } catch {
555
555
  return true;
556
556
  }
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { C as setConfigValue, S as serverConfigSchema, _ as loadAgents, b as resetConfigMeta, c as saveCredentials, f as agentConfigSchema, g as initConfig, h as getConfigValue, 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, v as readConfigFile, y as resetConfig } from "../bootstrap-DNL1cEwv.mjs";
3
- import { A as SdkError, D as createOwner, E as ClientRuntime, M as cleanWorkspaces, T as stopPostgres, _ as checkServerHealth, a as formatCheckReport, b as printResults, c as onboardCreate, d as checkAgentConfigs, f as checkClientConfig, g as checkServerConfig, h as checkNodeVersion, i as promptMissingFields, j as SessionRegistry, k as FirstTreeHubSDK, l as saveOnboardState, m as checkDocker, n as isInteractive, o as loadOnboardState, p as checkDatabase, r as promptAddAgent, s as onboardCheck, t as startServer, u as runMigrations, v as checkServerReachable, y as checkWebSocket } from "../core-B10jgThe.mjs";
4
- import { n as bindFeishuUser, t as bindFeishuBot } from "../feishu-BoMJHlOv.mjs";
2
+ import { C as setConfigValue, S as serverConfigSchema, _ as loadAgents, b as resetConfigMeta, c as saveCredentials, f as agentConfigSchema, g as initConfig, h as getConfigValue, 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, v as readConfigFile, y as resetConfig } from "../bootstrap-CRDR6NwE.mjs";
3
+ import { A as SdkError, D as createOwner, E as ClientRuntime, M as cleanWorkspaces, T as stopPostgres, _ as checkServerHealth, a as formatCheckReport, b as printResults, c as onboardCreate, d as checkAgentConfigs, f as checkClientConfig, g as checkServerConfig, h as checkNodeVersion, i as promptMissingFields, j as SessionRegistry, k as FirstTreeHubSDK, l as saveOnboardState, m as checkDocker, n as isInteractive, o as loadOnboardState, p as checkDatabase, r as promptAddAgent, s as onboardCheck, t as startServer, u as runMigrations, v as checkServerReachable, y as checkWebSocket } from "../core-4nvleGlC.mjs";
4
+ import { n as bindFeishuUser, t as bindFeishuBot } from "../feishu-CJ08ntOD.mjs";
5
5
  import { createRequire } from "node:module";
6
6
  import { Command } from "commander";
7
7
  import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync } from "node:fs";
@@ -439,7 +439,31 @@ function registerAgentCommands(program) {
439
439
  }
440
440
  process.stderr.write(` ${totalRemoved} workspace(s) cleaned.\n`);
441
441
  });
442
- const bind = agent.command("bind").description("Bind external IM accounts to agents");
442
+ const bind = agent.command("bind").description("Bind an agent to a client machine or external IM account");
443
+ bind.command("client <agentName>").description("Bind an unbound agent to a client machine (first-time bind only — ID is immutable once set)").requiredOption("--client-id <id>", "Client (machine) ID — must be owned by you. Run `first-tree-hub client connect` on that machine first.").option("--server <url>", "Hub server URL").action(async (agentName, options) => {
444
+ try {
445
+ const serverUrl = resolveServerUrl(options.server);
446
+ const accessToken = await ensureFreshAccessToken();
447
+ const target = await resolveAgent(serverUrl, accessToken, agentName);
448
+ const patchRes = await fetch(`${serverUrl}/api/v1/admin/agents/${target.uuid}`, {
449
+ method: "PATCH",
450
+ headers: {
451
+ Authorization: `Bearer ${accessToken}`,
452
+ "Content-Type": "application/json"
453
+ },
454
+ body: JSON.stringify({ clientId: options.clientId }),
455
+ signal: AbortSignal.timeout(1e4)
456
+ });
457
+ if (!patchRes.ok) fail("BIND_CLIENT_ERROR", (await patchRes.json().catch(() => ({}))).error ?? `Bind failed (HTTP ${patchRes.status})`, 1);
458
+ process.stderr.write(` \u2713 Bound "${target.name ?? target.uuid}" to client ${options.clientId}.\n`);
459
+ success({
460
+ agentId: target.uuid,
461
+ clientId: options.clientId
462
+ });
463
+ } catch (error) {
464
+ fail("BIND_CLIENT_ERROR", error instanceof Error ? error.message : String(error));
465
+ }
466
+ });
443
467
  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) => {
444
468
  try {
445
469
  if (options.platform !== "feishu") fail("UNSUPPORTED_PLATFORM", `Platform "${options.platform}" is not supported. Use "feishu".`);
@@ -899,21 +923,21 @@ function registerClientCommands(program) {
899
923
  process.stderr.write(" No agents directory found.\n");
900
924
  }
901
925
  });
902
- client.command("hub-list").description("List connected clients on the Hub server").option("--server <url>", "Hub server URL").action(async (options) => {
926
+ client.command("hub-list").description("List clients on the Hub server").option("--server <url>", "Hub server URL").action(async (options) => {
903
927
  try {
904
928
  const serverUrl = resolveServerUrl(options.server);
905
929
  const token = await ensureFreshAccessToken();
906
- const response = await fetch(`${serverUrl}/api/v1/admin/clients`, {
930
+ const response = await fetch(`${serverUrl}/api/v1/clients`, {
907
931
  headers: { Authorization: `Bearer ${token}` },
908
932
  signal: AbortSignal.timeout(1e4)
909
933
  });
910
934
  if (!response.ok) fail("FETCH_ERROR", `Server returned ${response.status}`, 1);
911
935
  const clients = await response.json();
912
936
  if (clients.length === 0) {
913
- process.stderr.write(" No connected clients.\n");
937
+ process.stderr.write(" No clients.\n");
914
938
  return;
915
939
  }
916
- process.stderr.write(`\n Connected Clients: ${clients.length}\n\n`);
940
+ process.stderr.write(`\n Clients: ${clients.length}\n\n`);
917
941
  const header = ` ${"CLIENT".padEnd(20)} ${"HOST".padEnd(25)} ${"AGENTS".padEnd(8)} CONNECTED`;
918
942
  process.stderr.write(`${header}\n`);
919
943
  process.stderr.write(` ${"─".repeat(header.length - 2)}\n`);
@@ -930,7 +954,7 @@ function registerClientCommands(program) {
930
954
  try {
931
955
  const serverUrl = resolveServerUrl(options.server);
932
956
  const token = await ensureFreshAccessToken();
933
- const response = await fetch(`${serverUrl}/api/v1/admin/clients/${clientId}/disconnect`, {
957
+ const response = await fetch(`${serverUrl}/api/v1/clients/${clientId}/disconnect`, {
934
958
  method: "POST",
935
959
  headers: { Authorization: `Bearer ${token}` },
936
960
  signal: AbortSignal.timeout(1e4)
@@ -1049,13 +1073,13 @@ function isSecretField(schema, dotPath) {
1049
1073
  //#region src/commands/onboard.ts
1050
1074
  async function promptMissing(args) {
1051
1075
  if (!args.server) try {
1052
- const { resolveServerUrl } = await import("../bootstrap-DNL1cEwv.mjs").then((n) => n.t);
1076
+ const { resolveServerUrl } = await import("../bootstrap-CRDR6NwE.mjs").then((n) => n.t);
1053
1077
  resolveServerUrl();
1054
1078
  } catch {
1055
1079
  args.server = await input({ message: "Hub server URL:" });
1056
1080
  saveOnboardState(args);
1057
1081
  }
1058
- const { loadCredentials } = await import("../bootstrap-DNL1cEwv.mjs").then((n) => n.t);
1082
+ const { loadCredentials } = await import("../bootstrap-CRDR6NwE.mjs").then((n) => n.t);
1059
1083
  if (!loadCredentials()) throw new Error("No saved credentials. Run `first-tree-hub client connect <server-url>` before onboarding.");
1060
1084
  if (!args.id) {
1061
1085
  args.id = await input({ message: "Agent ID:" });