@agent-team-foundation/first-tree-hub 0.11.2 → 0.11.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.
Files changed (36) hide show
  1. package/dist/{bootstrap-B-FRMuvL.mjs → bootstrap-D4rdqM2F.mjs} +2 -1
  2. package/dist/cli/index.mjs +44 -41
  3. package/dist/cli-fetch--tiwKm5S.mjs +167 -0
  4. package/dist/client-By1K4VVT-C5K7WZo6.mjs +4 -0
  5. package/dist/{client-CLdRbuml-B416INrm.mjs → client-CLdRbuml-BRtalKpQ.mjs} +2 -2
  6. package/dist/{dist-BLY7Bu-l.mjs → dist-BQtAQNRD.mjs} +1 -1
  7. package/dist/{feishu-GvFABWW5.mjs → feishu-Th_-ivJ7.mjs} +4 -3
  8. package/dist/{getMachineId-bsd-DjLgZlll.mjs → getMachineId-bsd-DyySs8xz.mjs} +2 -2
  9. package/dist/{getMachineId-bsd-DR4-Dysy.mjs → getMachineId-bsd-c2VImogj.mjs} +2 -2
  10. package/dist/{getMachineId-darwin-CaD2juTg.mjs → getMachineId-darwin-Cl7TSzgO.mjs} +2 -2
  11. package/dist/{getMachineId-darwin-B6WCAhc4.mjs → getMachineId-darwin-DKgI8b1d.mjs} +2 -2
  12. package/dist/{getMachineId-linux-Dk3gWdQK.mjs → getMachineId-linux-1OIMWfdh.mjs} +1 -1
  13. package/dist/{getMachineId-linux-BeWHG1gK.mjs → getMachineId-linux-cT7EbP10.mjs} +1 -1
  14. package/dist/{getMachineId-unsupported-BMJQItvF.mjs → getMachineId-unsupported-CkX-YOG1.mjs} +1 -1
  15. package/dist/{getMachineId-unsupported-Bgz_Je1J.mjs → getMachineId-unsupported-CmVlhzIo.mjs} +1 -1
  16. package/dist/{getMachineId-win-vJ6VfDRI.mjs → getMachineId-win-C2cM60YT.mjs} +2 -2
  17. package/dist/{getMachineId-win-CdgcrzCW.mjs → getMachineId-win-Chl03TYe.mjs} +2 -2
  18. package/dist/index.mjs +10 -9
  19. package/dist/invitation-DWlyNb8x-D3zjZSwI.mjs +4 -0
  20. package/dist/{invitation-Dnn5gGGX-Ce7zbZpn.mjs → invitation-Dnn5gGGX-DXryyvRG.mjs} +1 -1
  21. package/dist/{multipart-parser-BIksYTkk.mjs → multipart-parser-QRu3OKK4.mjs} +1 -1
  22. package/dist/{observability-C3nY6Jcz-Bk7FX689.mjs → observability-BAScT_5S-gw1ODB_o.mjs} +140 -17
  23. package/dist/observability-CYsdAcoF.mjs +5 -0
  24. package/dist/{saas-connect-Df2CVAGp.mjs → saas-connect-gcT6Q10z.mjs} +78 -158
  25. package/dist/{src-CzQ5KF6D.mjs → src-DFlbpJfU.mjs} +2 -2
  26. package/package.json +1 -1
  27. package/dist/client-By1K4VVT-nVOhsXBy.mjs +0 -4
  28. package/dist/invitation-DWlyNb8x-BEgoZ9k1.mjs +0 -4
  29. package/dist/observability-DttujCqj.mjs +0 -5
  30. /package/dist/{dist-FuUBFTEB.mjs → dist-BAqGZkco.mjs} +0 -0
  31. /package/dist/{errors-BmyRwN0Y-CIZZ_sDc.mjs → errors-BmyRwN0Y-Dad3eV8F.mjs} +0 -0
  32. /package/dist/{esm-iadMkGbV.mjs → esm-Ci8E1Gtj.mjs} +0 -0
  33. /package/dist/{execAsync-pImxPKN5.mjs → execAsync-DUfRkc4a.mjs} +0 -0
  34. /package/dist/{execAsync-CCyouKZM.mjs → execAsync-YbEZSOYd.mjs} +0 -0
  35. /package/dist/{from-CaD373S1.mjs → from-DQ7eNRwu.mjs} +0 -0
  36. /package/dist/{src-DNBS5Yjj.mjs → src-aJMV60mR.mjs} +0 -0
@@ -1,5 +1,6 @@
1
1
  import { r as __exportAll } from "./chunk-BSw8zbkd.mjs";
2
2
  import { o as logFormatSchema, s as logLevelSchema } from "./logger-core-BTmvdflj-DjW8FM4T.mjs";
3
+ import { t as cliFetch } from "./cli-fetch--tiwKm5S.mjs";
3
4
  import { z } from "zod";
4
5
  import { dirname, join } from "node:path";
5
6
  import { homedir } from "node:os";
@@ -784,7 +785,7 @@ async function ensureFreshAccessToken(opts) {
784
785
  if (!isTokenStale(creds.accessToken, minValidityMs)) return creds.accessToken;
785
786
  if (inflightRefresh) return inflightRefresh;
786
787
  inflightRefresh = (async () => {
787
- const res = await fetch(`${creds.serverUrl}/api/v1/auth/refresh`, {
788
+ const res = await cliFetch(`${creds.serverUrl}/api/v1/auth/refresh`, {
788
789
  method: "POST",
789
790
  headers: { "Content-Type": "application/json" },
790
791
  body: JSON.stringify({ refreshToken: creds.refreshToken }),
@@ -1,14 +1,15 @@
1
1
  #!/usr/bin/env node
2
- import "../observability-C3nY6Jcz-Bk7FX689.mjs";
3
- import { $ as findStaleAliases, A as checkDatabase, B as installClientService, C as runHomeMigration, D as checkAgentConfigs, E as runMigrations, F as checkServerReachable, G as stopClientService, I as checkWebSocket, L as printResults, M as checkNodeVersion, N as checkServerConfig, O as checkBackgroundService, P as checkServerHealth, R as reconcileAgentConfigs, S as saveOnboardState, T as migrateLocalAgentDirs, U as restartClientService, V as isServiceSupported, W as startClientService, X as ClientRuntime, Y as stopPostgres, Z as handleClientOrgMismatch, _ as promptMissingFields, _t as probeCapabilities, a as declineUpdate, at as fail, b as onboardCheck, c as detectInstallMode, ct as print, d as startServer, dt as ClientOrgMismatchError, et as formatStaleReason, f as COMMAND_VERSION, ft as ClientUserMismatchError, g as promptAddAgent, gt as cleanWorkspaces, h as isInteractive, ht as SessionRegistry, i as createExecuteUpdate, it as resolveReplyToFromEnv, j as checkDocker, k as checkClientConfig, l as fetchLatestVersion, lt as setJsonMode, m as uploadClientCapabilities, mt as SdkError, nt as createOwner, o as promptUpdate, ot as success, p as reconcileLocalRuntimeProviders, pt as FirstTreeHubSDK, r as registerSaaSConnectCommand, s as PACKAGE_NAME, tt as removeLocalAgent, u as installGlobalLatest, v as formatCheckReport, vt as applyClientLoggerConfig, w as createApiNameResolver, x as onboardCreate, y as loadOnboardState, yt as configureClientLoggerForService, z as getClientServiceStatus } from "../saas-connect-Df2CVAGp.mjs";
2
+ import "../observability-BAScT_5S-gw1ODB_o.mjs";
3
+ import { $ as formatStaleReason, A as checkDocker, B as isServiceSupported, C as createApiNameResolver, D as checkBackgroundService, E as checkAgentConfigs, F as checkWebSocket, H as restartClientService, I as printResults, J as stopPostgres, L as reconcileAgentConfigs, M as checkServerConfig, N as checkServerHealth, O as checkClientConfig, P as checkServerReachable, Q as findStaleAliases, R as getClientServiceStatus, S as runHomeMigration, T as runMigrations, U as startClientService, W as stopClientService, X as handleClientOrgMismatch, Y as ClientRuntime, _ as formatCheckReport, a as declineUpdate, at as success, b as onboardCreate, c as detectInstallMode, ct as FirstTreeHubSDK, d as startServer, dt as cleanWorkspaces, et as removeLocalAgent, f as reconcileLocalRuntimeProviders, ft as probeCapabilities, g as promptMissingFields, h as promptAddAgent, i as createExecuteUpdate, it as fail, j as checkNodeVersion, k as checkDatabase, l as fetchLatestVersion, lt as SdkError, m as isInteractive, mt as configureClientLoggerForService, o as promptUpdate, ot as ClientOrgMismatchError, p as uploadClientCapabilities, pt as applyClientLoggerConfig, r as registerSaaSConnectCommand, rt as resolveReplyToFromEnv, s as PACKAGE_NAME, st as ClientUserMismatchError, tt as createOwner, u as installGlobalLatest, ut as SessionRegistry, v as loadOnboardState, w as migrateLocalAgentDirs, x as saveOnboardState, y as onboardCheck, z as installClientService } from "../saas-connect-gcT6Q10z.mjs";
4
4
  import "../logger-core-BTmvdflj-DjW8FM4T.mjs";
5
- import { C as resetConfigMeta, E as setConfigValue, S as resetConfig, T as serverConfigSchema, _ as getConfigValue, a as ensureFreshAdminToken, c as resolveServerUrl, d as DEFAULT_CONFIG_DIR, f as DEFAULT_DATA_DIR, h as clientConfigSchema, i as ensureFreshAccessToken, l as saveAgentConfig, m as agentConfigSchema, o as loadCredentials, p as DEFAULT_HOME_DIR, u as saveCredentials, v as initConfig, w as resolveConfigReadonly, x as readConfigFile, y as loadAgents } from "../bootstrap-B-FRMuvL.mjs";
6
- import "../dist-FuUBFTEB.mjs";
7
- import { n as bindFeishuUser, t as bindFeishuBot } from "../feishu-GvFABWW5.mjs";
8
- import "../errors-BmyRwN0Y-CIZZ_sDc.mjs";
9
- import "../client-CLdRbuml-B416INrm.mjs";
10
- import "../src-DNBS5Yjj.mjs";
11
- import "../invitation-Dnn5gGGX-Ce7zbZpn.mjs";
5
+ import { C as resetConfigMeta, E as setConfigValue, S as resetConfig, T as serverConfigSchema, _ as getConfigValue, a as ensureFreshAdminToken, c as resolveServerUrl, d as DEFAULT_CONFIG_DIR, f as DEFAULT_DATA_DIR, h as clientConfigSchema, i as ensureFreshAccessToken, l as saveAgentConfig, m as agentConfigSchema, o as loadCredentials, p as DEFAULT_HOME_DIR, u as saveCredentials, v as initConfig, w as resolveConfigReadonly, x as readConfigFile, y as loadAgents } from "../bootstrap-D4rdqM2F.mjs";
6
+ import { a as print, n as CLI_USER_AGENT, o as setJsonMode, r as COMMAND_VERSION, t as cliFetch } from "../cli-fetch--tiwKm5S.mjs";
7
+ import "../dist-BAqGZkco.mjs";
8
+ import { n as bindFeishuUser, t as bindFeishuBot } from "../feishu-Th_-ivJ7.mjs";
9
+ import "../errors-BmyRwN0Y-Dad3eV8F.mjs";
10
+ import "../client-CLdRbuml-BRtalKpQ.mjs";
11
+ import "../src-aJMV60mR.mjs";
12
+ import "../invitation-Dnn5gGGX-DXryyvRG.mjs";
12
13
  import { join } from "node:path";
13
14
  import { existsSync, mkdirSync, readFileSync, readdirSync } from "node:fs";
14
15
  import * as semver from "semver";
@@ -16,7 +17,7 @@ import { Command } from "commander";
16
17
  import { confirm, input, password, select } from "@inquirer/prompts";
17
18
  //#region src/commands/agent-config.ts
18
19
  async function resolveAgentRecord(serverUrl, adminToken, agentName) {
19
- const res = await fetch(`${serverUrl}/api/v1/me/managed-agents`, {
20
+ const res = await cliFetch(`${serverUrl}/api/v1/me/managed-agents`, {
20
21
  headers: { Authorization: `Bearer ${adminToken}` },
21
22
  signal: AbortSignal.timeout(1e4)
22
23
  });
@@ -27,7 +28,7 @@ async function resolveAgentRecord(serverUrl, adminToken, agentName) {
27
28
  }
28
29
  async function adminFetch(url, init) {
29
30
  const { adminToken, headers, ...rest } = init;
30
- const res = await fetch(url, {
31
+ const res = await cliFetch(url, {
31
32
  ...rest,
32
33
  headers: {
33
34
  Authorization: `Bearer ${adminToken}`,
@@ -240,7 +241,8 @@ function createSdk(agentName) {
240
241
  return new FirstTreeHubSDK({
241
242
  serverUrl,
242
243
  getAccessToken: (opts) => ensureFreshAccessToken(opts),
243
- agentId
244
+ agentId,
245
+ userAgent: CLI_USER_AGENT
244
246
  });
245
247
  }
246
248
  function handleSdkError(error) {
@@ -276,7 +278,7 @@ function readStdin() {
276
278
  });
277
279
  }
278
280
  async function resolveAgent(serverUrl, adminToken, agentName) {
279
- const res = await fetch(`${serverUrl}/api/v1/me/managed-agents`, {
281
+ const res = await cliFetch(`${serverUrl}/api/v1/me/managed-agents`, {
280
282
  headers: { Authorization: `Bearer ${adminToken}` },
281
283
  signal: AbortSignal.timeout(1e4)
282
284
  });
@@ -339,7 +341,8 @@ function registerAgentCommands(program) {
339
341
  const clientId = readClientId();
340
342
  const sdk = new FirstTreeHubSDK({
341
343
  serverUrl,
342
- getAccessToken: (opts) => ensureFreshAccessToken(opts)
344
+ getAccessToken: (opts) => ensureFreshAccessToken(opts),
345
+ userAgent: CLI_USER_AGENT
343
346
  });
344
347
  const stale = await findStaleAliases({
345
348
  clientId,
@@ -406,7 +409,7 @@ function registerAgentCommands(program) {
406
409
  try {
407
410
  const serverUrl = resolveServerUrl(options.server);
408
411
  const token = await ensureFreshAccessToken();
409
- const res = await fetch(`${serverUrl}/api/v1/me/managed-agents`, {
412
+ const res = await cliFetch(`${serverUrl}/api/v1/me/managed-agents`, {
410
413
  headers: { Authorization: `Bearer ${token}` },
411
414
  signal: AbortSignal.timeout(1e4)
412
415
  });
@@ -433,7 +436,7 @@ function registerAgentCommands(program) {
433
436
  Authorization: `Bearer ${adminToken}`,
434
437
  "Content-Type": "application/json"
435
438
  };
436
- const meRes = await fetch(`${serverUrl}/api/v1/me`, {
439
+ const meRes = await cliFetch(`${serverUrl}/api/v1/me`, {
437
440
  headers: { Authorization: `Bearer ${adminToken}` },
438
441
  signal: AbortSignal.timeout(1e4)
439
442
  });
@@ -456,7 +459,7 @@ function registerAgentCommands(program) {
456
459
  runtimeProvider: options.runtime
457
460
  };
458
461
  if (options.displayName) createBody.displayName = options.displayName;
459
- const createRes = await fetch(`${serverUrl}/api/v1/orgs/${encodeURIComponent(orgId)}/agents`, {
462
+ const createRes = await cliFetch(`${serverUrl}/api/v1/orgs/${encodeURIComponent(orgId)}/agents`, {
460
463
  method: "POST",
461
464
  headers,
462
465
  body: JSON.stringify(createBody),
@@ -476,14 +479,14 @@ function registerAgentCommands(program) {
476
479
  try {
477
480
  const serverUrl = resolveServerUrl(options.server);
478
481
  const accessToken = await ensureFreshAccessToken();
479
- const meRes = await fetch(`${serverUrl}/api/v1/me`, {
482
+ const meRes = await cliFetch(`${serverUrl}/api/v1/me`, {
480
483
  headers: { Authorization: `Bearer ${accessToken}` },
481
484
  signal: AbortSignal.timeout(1e4)
482
485
  });
483
486
  if (!meRes.ok) fail("ME_ERROR", `Failed to fetch current member (HTTP ${meRes.status})`, 1);
484
487
  const me = await meRes.json();
485
488
  const target = await resolveAgent(serverUrl, accessToken, agentName);
486
- const patchRes = await fetch(`${serverUrl}/api/v1/agents/${target.uuid}`, {
489
+ const patchRes = await cliFetch(`${serverUrl}/api/v1/agents/${target.uuid}`, {
487
490
  method: "PATCH",
488
491
  headers: {
489
492
  Authorization: `Bearer ${accessToken}`,
@@ -526,7 +529,7 @@ function registerAgentCommands(program) {
526
529
  const serverUrl = resolveServerUrl(options.server);
527
530
  const accessToken = await ensureFreshAccessToken();
528
531
  const target = await resolveAgent(serverUrl, accessToken, agentName);
529
- const patchRes = await fetch(`${serverUrl}/api/v1/agents/${target.uuid}`, {
532
+ const patchRes = await cliFetch(`${serverUrl}/api/v1/agents/${target.uuid}`, {
530
533
  method: "PATCH",
531
534
  headers: {
532
535
  Authorization: `Bearer ${accessToken}`,
@@ -636,7 +639,7 @@ function registerAgentCommands(program) {
636
639
  try {
637
640
  const serverUrl = resolveServerUrl(options?.server);
638
641
  const accessToken = await ensureFreshAccessToken();
639
- const meRes = await fetch(`${serverUrl}/api/v1/me`, {
642
+ const meRes = await cliFetch(`${serverUrl}/api/v1/me`, {
640
643
  headers: { Authorization: `Bearer ${accessToken}` },
641
644
  signal: AbortSignal.timeout(1e4)
642
645
  });
@@ -655,7 +658,7 @@ function registerAgentCommands(program) {
655
658
  agents: []
656
659
  };
657
660
  for (const m of me.memberships) {
658
- const r = await fetch(`${serverUrl}/api/v1/orgs/${encodeURIComponent(m.organizationId)}/activity`, {
661
+ const r = await cliFetch(`${serverUrl}/api/v1/orgs/${encodeURIComponent(m.organizationId)}/activity`, {
659
662
  headers: { Authorization: `Bearer ${accessToken}` },
660
663
  signal: AbortSignal.timeout(1e4)
661
664
  });
@@ -704,8 +707,7 @@ function registerAgentCommands(program) {
704
707
  });
705
708
  agent.command("reset <name>").description("Reset agent error state to idle").option("--server <url>", "Hub server URL").action(async (name, options) => {
706
709
  try {
707
- const serverUrl = resolveServerUrl(options.server);
708
- const response = await fetch(`${serverUrl}/api/v1/agents/${name}/reset-activity`, {
710
+ const response = await cliFetch(`${resolveServerUrl(options.server)}/api/v1/agents/${name}/reset-activity`, {
709
711
  method: "POST",
710
712
  headers: { Authorization: `Bearer ${await ensureFreshAccessToken()}` },
711
713
  signal: AbortSignal.timeout(1e4)
@@ -721,8 +723,7 @@ function registerAgentCommands(program) {
721
723
  const serverUrl = resolveServerUrl(options.server);
722
724
  const adminToken = await ensureFreshAccessToken();
723
725
  const agentId = (await resolveAgent(serverUrl, adminToken, agentName)).uuid;
724
- const qs = options.state ? `?state=${options.state}` : "";
725
- const response = await fetch(`${serverUrl}/api/v1/agents/${agentId}/sessions${qs}`, {
726
+ const response = await cliFetch(`${serverUrl}/api/v1/agents/${agentId}/sessions${options.state ? `?state=${options.state}` : ""}`, {
726
727
  headers: { Authorization: `Bearer ${adminToken}` },
727
728
  signal: AbortSignal.timeout(1e4)
728
729
  });
@@ -751,7 +752,7 @@ function registerAgentCommands(program) {
751
752
  const serverUrl = resolveServerUrl(options.server);
752
753
  const adminToken = await ensureFreshAccessToken();
753
754
  const agentId = (await resolveAgent(serverUrl, adminToken, agentName)).uuid;
754
- const response = await fetch(`${serverUrl}/api/v1/agents/${agentId}/sessions/${chatId}/${cmd}`, {
755
+ const response = await cliFetch(`${serverUrl}/api/v1/agents/${agentId}/sessions/${chatId}/${cmd}`, {
755
756
  method: "POST",
756
757
  headers: { Authorization: `Bearer ${adminToken}` },
757
758
  signal: AbortSignal.timeout(1e4)
@@ -774,7 +775,7 @@ function registerAgentCommands(program) {
774
775
  "Content-Type": "application/json"
775
776
  };
776
777
  const targetAgent = await resolveAgent(serverUrl, adminToken, agentName);
777
- const dmRes = await fetch(`${serverUrl}/api/v1/agents/${targetAgent.uuid}/chats`, {
778
+ const dmRes = await cliFetch(`${serverUrl}/api/v1/agents/${targetAgent.uuid}/chats`, {
778
779
  method: "POST",
779
780
  headers,
780
781
  signal: AbortSignal.timeout(1e4)
@@ -796,7 +797,7 @@ function registerAgentCommands(program) {
796
797
  const pollMessages = async () => {
797
798
  try {
798
799
  const qs = lastSeenAt ? `?limit=50` : `?limit=10`;
799
- const msgRes = await fetch(`${serverUrl}/api/v1/chats/${dm.id}/messages${qs}`, {
800
+ const msgRes = await cliFetch(`${serverUrl}/api/v1/chats/${dm.id}/messages${qs}`, {
800
801
  headers,
801
802
  signal: AbortSignal.timeout(1e4)
802
803
  });
@@ -827,7 +828,7 @@ function registerAgentCommands(program) {
827
828
  return;
828
829
  }
829
830
  try {
830
- const sendRes = await fetch(`${serverUrl}/api/v1/chats/${dm.id}/messages`, {
831
+ const sendRes = await cliFetch(`${serverUrl}/api/v1/chats/${dm.id}/messages`, {
831
832
  method: "POST",
832
833
  headers,
833
834
  body: JSON.stringify({
@@ -963,7 +964,7 @@ function printIsolationGuide(newServerUrl) {
963
964
  * Authenticate via connect token — exchange for full JWT credentials.
964
965
  */
965
966
  async function authenticateWithToken(url, token) {
966
- const res = await fetch(`${url}/api/v1/auth/connect-token`, {
967
+ const res = await cliFetch(`${url}/api/v1/auth/connect-token`, {
967
968
  method: "POST",
968
969
  headers: { "Content-Type": "application/json" },
969
970
  body: JSON.stringify({ token }),
@@ -979,7 +980,7 @@ async function authenticateInteractive(url) {
979
980
  print.line("\n Log in to Hub:\n");
980
981
  const username = await input({ message: " Username:" });
981
982
  const pw = await password({ message: " Password:" });
982
- const loginRes = await fetch(`${url}/api/v1/auth/login`, {
983
+ const loginRes = await cliFetch(`${url}/api/v1/auth/login`, {
983
984
  method: "POST",
984
985
  headers: { "Content-Type": "application/json" },
985
986
  body: JSON.stringify({
@@ -1270,7 +1271,8 @@ function registerClientCommands(program) {
1270
1271
  });
1271
1272
  const sdk = new FirstTreeHubSDK({
1272
1273
  serverUrl,
1273
- getAccessToken: (opts) => ensureFreshAccessToken(opts)
1274
+ getAccessToken: (opts) => ensureFreshAccessToken(opts),
1275
+ userAgent: CLI_USER_AGENT
1274
1276
  });
1275
1277
  agentCheck = await reconcileAgentConfigs({
1276
1278
  clientId: cfg.client.id,
@@ -1396,7 +1398,7 @@ function registerClientCommands(program) {
1396
1398
  try {
1397
1399
  const serverUrl = resolveServerUrl(options.server);
1398
1400
  const token = await ensureFreshAccessToken();
1399
- const meRes = await fetch(`${serverUrl}/api/v1/me`, {
1401
+ const meRes = await cliFetch(`${serverUrl}/api/v1/me`, {
1400
1402
  headers: { Authorization: `Bearer ${token}` },
1401
1403
  signal: AbortSignal.timeout(1e4)
1402
1404
  });
@@ -1404,7 +1406,7 @@ function registerClientCommands(program) {
1404
1406
  const adminOrgs = (await meRes.json()).memberships.filter((m) => m.role === "admin");
1405
1407
  const clients = [];
1406
1408
  for (const m of adminOrgs) {
1407
- const r = await fetch(`${serverUrl}/api/v1/orgs/${encodeURIComponent(m.organizationId)}/clients`, {
1409
+ const r = await cliFetch(`${serverUrl}/api/v1/orgs/${encodeURIComponent(m.organizationId)}/clients`, {
1408
1410
  headers: { Authorization: `Bearer ${token}` },
1409
1411
  signal: AbortSignal.timeout(1e4)
1410
1412
  });
@@ -1453,7 +1455,7 @@ function registerClientCommands(program) {
1453
1455
  }
1454
1456
  }
1455
1457
  const token = await ensureFreshAccessToken();
1456
- const response = await fetch(`${serverUrl}/api/v1/clients/${encodeURIComponent(clientId)}/claim`, {
1458
+ const response = await cliFetch(`${serverUrl}/api/v1/clients/${encodeURIComponent(clientId)}/claim`, {
1457
1459
  method: "POST",
1458
1460
  headers: {
1459
1461
  Authorization: `Bearer ${token}`,
@@ -1471,7 +1473,8 @@ function registerClientCommands(program) {
1471
1473
  try {
1472
1474
  const sdk = new FirstTreeHubSDK({
1473
1475
  serverUrl,
1474
- getAccessToken: (opts) => ensureFreshAccessToken(opts)
1476
+ getAccessToken: (opts) => ensureFreshAccessToken(opts),
1477
+ userAgent: CLI_USER_AGENT
1475
1478
  });
1476
1479
  const stale = await findStaleAliases({
1477
1480
  clientId,
@@ -1520,7 +1523,7 @@ function registerClientCommands(program) {
1520
1523
  try {
1521
1524
  const serverUrl = resolveServerUrl(options.server);
1522
1525
  const token = await ensureFreshAccessToken();
1523
- const response = await fetch(`${serverUrl}/api/v1/clients/${encodeURIComponent(clientId)}/disconnect`, {
1526
+ const response = await cliFetch(`${serverUrl}/api/v1/clients/${encodeURIComponent(clientId)}/disconnect`, {
1524
1527
  method: "POST",
1525
1528
  headers: { Authorization: `Bearer ${token}` },
1526
1529
  signal: AbortSignal.timeout(1e4)
@@ -1667,13 +1670,13 @@ function isSecretField(schema, dotPath) {
1667
1670
  //#region src/commands/onboard.ts
1668
1671
  async function promptMissing(args) {
1669
1672
  if (!args.server) try {
1670
- const { resolveServerUrl } = await import("../bootstrap-B-FRMuvL.mjs").then((n) => n.r);
1673
+ const { resolveServerUrl } = await import("../bootstrap-D4rdqM2F.mjs").then((n) => n.r);
1671
1674
  resolveServerUrl();
1672
1675
  } catch {
1673
1676
  args.server = await input({ message: "Hub server URL:" });
1674
1677
  saveOnboardState(args);
1675
1678
  }
1676
- const { loadCredentials } = await import("../bootstrap-B-FRMuvL.mjs").then((n) => n.r);
1679
+ const { loadCredentials } = await import("../bootstrap-D4rdqM2F.mjs").then((n) => n.r);
1677
1680
  if (!loadCredentials()) throw new Error("No saved credentials. Run `first-tree-hub client connect <server-url>` before onboarding.");
1678
1681
  if (!args.id) {
1679
1682
  args.id = await input({ message: "Agent ID:" });
@@ -1833,7 +1836,7 @@ function registerServerCommands(program) {
1833
1836
  server.command("status").description("Show server health and status").action(async () => {
1834
1837
  const url = process.env.FIRST_TREE_HUB_SERVER_URL ?? "http://localhost:8000";
1835
1838
  try {
1836
- const res = await fetch(`${url}/api/v1/health`);
1839
+ const res = await cliFetch(`${url}/api/v1/health`);
1837
1840
  if (res.ok) {
1838
1841
  const data = await res.json();
1839
1842
  process.stdout.write(`${JSON.stringify(data, null, 2)}\n`);
@@ -0,0 +1,167 @@
1
+ import { dirname, resolve } from "node:path";
2
+ import { readFileSync } from "node:fs";
3
+ import { fileURLToPath } from "node:url";
4
+ //#region src/core/output.ts
5
+ /**
6
+ * Print layer — the only place CLI code should write to stdout/stderr.
7
+ *
8
+ * Contract:
9
+ * - `print.result(data)` / `print.fail(...)` emit machine-readable JSON on
10
+ * stdout / stderr respectively. Scripts pipe into `jq` and expect a clean
11
+ * envelope, so nothing else may touch stdout.
12
+ * - `print.status` / `print.check` / `print.blank` / `print.line` are
13
+ * human-friendly and go to stderr so they never pollute a redirected stdout.
14
+ * In `--json` mode they are silenced — scripted consumers only care about
15
+ * the envelope.
16
+ */
17
+ let jsonMode = false;
18
+ function setJsonMode(enabled) {
19
+ jsonMode = enabled;
20
+ }
21
+ function result(data) {
22
+ process.stdout.write(`${JSON.stringify({
23
+ ok: true,
24
+ data
25
+ })}\n`);
26
+ }
27
+ function fail(code, message, exitCode = 1) {
28
+ process.stderr.write(`${JSON.stringify({
29
+ ok: false,
30
+ error: {
31
+ code,
32
+ message
33
+ }
34
+ })}\n`);
35
+ process.exit(exitCode);
36
+ }
37
+ function status(label, message) {
38
+ if (jsonMode) return;
39
+ process.stderr.write(` ${label.padEnd(20)} ${message}\n`);
40
+ }
41
+ function check(pass, label, detail = "") {
42
+ if (jsonMode) return;
43
+ const icon = pass ? "✓" : "✗";
44
+ const tail = detail ? ` ${detail}` : "";
45
+ process.stderr.write(` ${icon} ${label.padEnd(22)}${tail}\n`);
46
+ }
47
+ function blank() {
48
+ if (jsonMode) return;
49
+ process.stderr.write("\n");
50
+ }
51
+ /**
52
+ * Generic stderr writer for pre-formatted human text (multi-line tables,
53
+ * interactive prompts). Prefer `status` / `check` when the text fits; this
54
+ * exists so the `--json` mode gate can silence arbitrary human chatter.
55
+ */
56
+ function line(text) {
57
+ if (jsonMode) return;
58
+ process.stderr.write(text);
59
+ }
60
+ const print = {
61
+ result,
62
+ fail,
63
+ status,
64
+ check,
65
+ blank,
66
+ line
67
+ };
68
+ //#endregion
69
+ //#region src/core/version.ts
70
+ /**
71
+ * Version of the consumer-facing `@agent-team-foundation/first-tree-hub`
72
+ * package. Read once at module load so the CLI, client runtime, and server
73
+ * bootstrap all quote the same string.
74
+ *
75
+ * Path-based lookups (`require("../../package.json")`) do not survive the
76
+ * tsdown bundle: the source lives at `src/core/version.ts` but every
77
+ * emitted chunk lands in `dist/` — shifting the relative depth by one and
78
+ * pointing at `packages/package.json` instead of our own manifest (the
79
+ * v0.9.1 "Cannot find module ../../package.json" crash). Walk up from this
80
+ * module's URL and accept the first `package.json` whose `name` matches, so
81
+ * dev runs (`tsx src/cli/index.ts`) and the published bundle
82
+ * (`dist/cli/index.mjs`) both resolve the same file.
83
+ */
84
+ const PACKAGE_NAME = "@agent-team-foundation/first-tree-hub";
85
+ /**
86
+ * Sentinel returned when the walker exhausts every parent directory without
87
+ * finding our manifest. Deliberately NOT valid SemVer so the client-side
88
+ * `UpdateManager` drops into its `semver.valid(current) === false` warn-and-
89
+ * skip branch instead of treating it as `< target` and triggering a spurious
90
+ * self-update loop (the scenario where the startup crash this module fixes
91
+ * would otherwise quietly reincarnate as repeated `npm install -g @latest`).
92
+ */
93
+ const UNRESOLVED_VERSION = "unknown";
94
+ /**
95
+ * Exported for tests. Walks up from `moduleUrl`'s directory looking for a
96
+ * `package.json` whose `name` field equals {@link PACKAGE_NAME}. Returns
97
+ * {@link UNRESOLVED_VERSION} as a last-resort fallback so the CLI never
98
+ * crashes on a missing manifest.
99
+ */
100
+ function resolveCommandVersion(moduleUrl = import.meta.url) {
101
+ let dir = dirname(fileURLToPath(moduleUrl));
102
+ for (let i = 0; i < 10; i++) {
103
+ try {
104
+ const pkg = JSON.parse(readFileSync(resolve(dir, "package.json"), "utf8"));
105
+ if (pkg.name === PACKAGE_NAME && typeof pkg.version === "string" && pkg.version.length > 0) return pkg.version;
106
+ } catch (err) {
107
+ const code = err.code;
108
+ if (code !== "ENOENT" && code !== "ENOTDIR") {
109
+ const message = err instanceof Error ? err.message : String(err);
110
+ print.line(`[first-tree-hub] warning: could not read ${dir}/package.json: ${message}\n`);
111
+ }
112
+ }
113
+ const parent = dirname(dir);
114
+ if (parent === dir) break;
115
+ dir = parent;
116
+ }
117
+ return UNRESOLVED_VERSION;
118
+ }
119
+ const COMMAND_VERSION = resolveCommandVersion();
120
+ /**
121
+ * `User-Agent` string sent on every CLI-originated HTTP request (SDK fetches,
122
+ * `/auth/refresh`, etc.). Without this Node defaults to `User-Agent: node`,
123
+ * which hides install / version / platform context from server-side trace
124
+ * backends — see issue #246. The format follows RFC 7231 §5.5.3 conventions
125
+ * (`product/version (comment)`).
126
+ */
127
+ const CLI_USER_AGENT = `first-tree-hub-cli/${COMMAND_VERSION} (${process.platform} ${process.arch})`;
128
+ //#endregion
129
+ //#region src/core/cli-fetch.ts
130
+ /**
131
+ * Drop-in `fetch` wrapper that stamps `User-Agent: first-tree-hub-cli/<version> (<platform> <arch>)`
132
+ * on every request.
133
+ *
134
+ * Issue #246: every CLI-originated HTTP request must carry a stable
135
+ * `User-Agent` so trace backends can group failures (401 / 429 / 5xx) by
136
+ * install. Plain `globalThis.fetch` defaults to `User-Agent: node`, which
137
+ * collapses every install into a single bucket. Centralising the header
138
+ * here means new direct-fetch sites pick the right UA up automatically —
139
+ * no need to remember at each call site.
140
+ *
141
+ * SDK-routed requests stamp UA via {@link FirstTreeHubSDK} (see
142
+ * `packages/client/src/sdk.ts`). This helper is for the
143
+ * direct-`fetch` paths the SDK doesn't cover (auth bootstrap,
144
+ * doctor probes, raw admin calls).
145
+ *
146
+ * Header precedence: caller-provided `User-Agent` in `init.headers` wins,
147
+ * so callers can override (e.g. tests injecting a deterministic UA).
148
+ */
149
+ function cliFetch(input, init) {
150
+ const merged = mergeHeaders(init?.headers);
151
+ return fetch(input, {
152
+ ...init,
153
+ headers: merged
154
+ });
155
+ }
156
+ function mergeHeaders(provided) {
157
+ const out = {};
158
+ if (provided) if (provided instanceof Headers) provided.forEach((v, k) => {
159
+ out[k] = v;
160
+ });
161
+ else if (Array.isArray(provided)) for (const [k, v] of provided) out[k] = v;
162
+ else Object.assign(out, provided);
163
+ if (!Object.keys(out).some((k) => k.toLowerCase() === "user-agent")) out["User-Agent"] = CLI_USER_AGENT;
164
+ return out;
165
+ }
166
+ //#endregion
167
+ export { print as a, blank as i, CLI_USER_AGENT as n, setJsonMode as o, COMMAND_VERSION as r, status as s, cliFetch as t };
@@ -0,0 +1,4 @@
1
+ import "./dist-BAqGZkco.mjs";
2
+ import "./errors-BmyRwN0Y-Dad3eV8F.mjs";
3
+ import { y as listMyPinnedAgents } from "./client-CLdRbuml-BRtalKpQ.mjs";
4
+ export { listMyPinnedAgents };
@@ -1,5 +1,5 @@
1
- import { C as clientCapabilitiesSchema } from "./dist-FuUBFTEB.mjs";
2
- import { a as ConflictError, i as ClientUserMismatchError, l as organizations, n as BadRequestError, s as NotFoundError, u as users } from "./errors-BmyRwN0Y-CIZZ_sDc.mjs";
1
+ import { C as clientCapabilitiesSchema } from "./dist-BAqGZkco.mjs";
2
+ import { a as ConflictError, i as ClientUserMismatchError, l as organizations, n as BadRequestError, s as NotFoundError, u as users } from "./errors-BmyRwN0Y-Dad3eV8F.mjs";
3
3
  import { and, eq, inArray, ne, sql } from "drizzle-orm";
4
4
  import { index, integer, jsonb, pgTable, text, timestamp, unique } from "drizzle-orm/pg-core";
5
5
  //#region ../server/dist/client-CLdRbuml.mjs
@@ -1,5 +1,5 @@
1
1
  import { i as __require, t as __commonJSMin } from "./chunk-BSw8zbkd.mjs";
2
- import { t as require_src } from "./src-DNBS5Yjj.mjs";
2
+ import { t as require_src } from "./src-aJMV60mR.mjs";
3
3
  //#region ../../node_modules/.pnpm/agent-base@7.1.4/node_modules/agent-base/dist/helpers.js
4
4
  var require_helpers = /* @__PURE__ */ __commonJSMin(((exports) => {
5
5
  var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
@@ -1,5 +1,6 @@
1
1
  import { r as __exportAll } from "./chunk-BSw8zbkd.mjs";
2
- import { r as AGENT_SELECTOR_HEADER } from "./dist-FuUBFTEB.mjs";
2
+ import { t as cliFetch } from "./cli-fetch--tiwKm5S.mjs";
3
+ import { r as AGENT_SELECTOR_HEADER } from "./dist-BAqGZkco.mjs";
3
4
  //#region src/core/feishu.ts
4
5
  var feishu_exports = /* @__PURE__ */ __exportAll({
5
6
  bindFeishuBot: () => bindFeishuBot,
@@ -13,7 +14,7 @@ var feishu_exports = /* @__PURE__ */ __exportAll({
13
14
  * middleware enforces Rule R-RUN.
14
15
  */
15
16
  async function bindFeishuBot(serverUrl, accessToken, agentId, appId, appSecret) {
16
- const res = await fetch(`${serverUrl}/api/v1/agent/me/feishu-bot`, {
17
+ const res = await cliFetch(`${serverUrl}/api/v1/agent/me/feishu-bot`, {
17
18
  method: "PUT",
18
19
  headers: {
19
20
  Authorization: `Bearer ${accessToken}`,
@@ -31,7 +32,7 @@ async function bindFeishuBot(serverUrl, accessToken, agentId, appId, appSecret)
31
32
  }
32
33
  }
33
34
  async function bindFeishuUser(serverUrl, accessToken, agentId, humanAgentId, feishuUserId, displayName) {
34
- const res = await fetch(`${serverUrl}/api/v1/agent/delegated/${encodeURIComponent(humanAgentId)}/feishu-user`, {
35
+ const res = await cliFetch(`${serverUrl}/api/v1/agent/delegated/${encodeURIComponent(humanAgentId)}/feishu-user`, {
35
36
  method: "POST",
36
37
  headers: {
37
38
  Authorization: `Bearer ${accessToken}`,
@@ -1,6 +1,6 @@
1
1
  import { a as __toCommonJS, i as __require, t as __commonJSMin } from "./chunk-BSw8zbkd.mjs";
2
- import { n as init_esm, t as esm_exports } from "./esm-iadMkGbV.mjs";
3
- import { t as require_execAsync } from "./execAsync-CCyouKZM.mjs";
2
+ import { n as init_esm, t as esm_exports } from "./esm-Ci8E1Gtj.mjs";
3
+ import { t as require_execAsync } from "./execAsync-YbEZSOYd.mjs";
4
4
  //#region ../../node_modules/.pnpm/@opentelemetry+resources@2.7.1_@opentelemetry+api@1.9.1/node_modules/@opentelemetry/resources/build/src/detectors/platform/node/machine-id/getMachineId-bsd.js
5
5
  var require_getMachineId_bsd = /* @__PURE__ */ __commonJSMin(((exports) => {
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,6 +1,6 @@
1
1
  import { a as __toCommonJS, i as __require, t as __commonJSMin } from "./chunk-BSw8zbkd.mjs";
2
- import { n as init_esm, t as esm_exports } from "./esm-iadMkGbV.mjs";
3
- import { t as require_execAsync } from "./execAsync-pImxPKN5.mjs";
2
+ import { n as init_esm, t as esm_exports } from "./esm-Ci8E1Gtj.mjs";
3
+ import { t as require_execAsync } from "./execAsync-DUfRkc4a.mjs";
4
4
  //#region ../../node_modules/.pnpm/@opentelemetry+resources@2.7.0_@opentelemetry+api@1.9.1/node_modules/@opentelemetry/resources/build/src/detectors/platform/node/machine-id/getMachineId-bsd.js
5
5
  var require_getMachineId_bsd = /* @__PURE__ */ __commonJSMin(((exports) => {
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,6 +1,6 @@
1
1
  import { a as __toCommonJS, t as __commonJSMin } from "./chunk-BSw8zbkd.mjs";
2
- import { n as init_esm, t as esm_exports } from "./esm-iadMkGbV.mjs";
3
- import { t as require_execAsync } from "./execAsync-pImxPKN5.mjs";
2
+ import { n as init_esm, t as esm_exports } from "./esm-Ci8E1Gtj.mjs";
3
+ import { t as require_execAsync } from "./execAsync-DUfRkc4a.mjs";
4
4
  //#region ../../node_modules/.pnpm/@opentelemetry+resources@2.7.0_@opentelemetry+api@1.9.1/node_modules/@opentelemetry/resources/build/src/detectors/platform/node/machine-id/getMachineId-darwin.js
5
5
  var require_getMachineId_darwin = /* @__PURE__ */ __commonJSMin(((exports) => {
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,6 +1,6 @@
1
1
  import { a as __toCommonJS, t as __commonJSMin } from "./chunk-BSw8zbkd.mjs";
2
- import { n as init_esm, t as esm_exports } from "./esm-iadMkGbV.mjs";
3
- import { t as require_execAsync } from "./execAsync-CCyouKZM.mjs";
2
+ import { n as init_esm, t as esm_exports } from "./esm-Ci8E1Gtj.mjs";
3
+ import { t as require_execAsync } from "./execAsync-YbEZSOYd.mjs";
4
4
  //#region ../../node_modules/.pnpm/@opentelemetry+resources@2.7.1_@opentelemetry+api@1.9.1/node_modules/@opentelemetry/resources/build/src/detectors/platform/node/machine-id/getMachineId-darwin.js
5
5
  var require_getMachineId_darwin = /* @__PURE__ */ __commonJSMin(((exports) => {
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,5 +1,5 @@
1
1
  import { a as __toCommonJS, i as __require, t as __commonJSMin } from "./chunk-BSw8zbkd.mjs";
2
- import { n as init_esm, t as esm_exports } from "./esm-iadMkGbV.mjs";
2
+ import { n as init_esm, t as esm_exports } from "./esm-Ci8E1Gtj.mjs";
3
3
  //#region ../../node_modules/.pnpm/@opentelemetry+resources@2.7.0_@opentelemetry+api@1.9.1/node_modules/@opentelemetry/resources/build/src/detectors/platform/node/machine-id/getMachineId-linux.js
4
4
  var require_getMachineId_linux = /* @__PURE__ */ __commonJSMin(((exports) => {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,5 +1,5 @@
1
1
  import { a as __toCommonJS, i as __require, t as __commonJSMin } from "./chunk-BSw8zbkd.mjs";
2
- import { n as init_esm, t as esm_exports } from "./esm-iadMkGbV.mjs";
2
+ import { n as init_esm, t as esm_exports } from "./esm-Ci8E1Gtj.mjs";
3
3
  //#region ../../node_modules/.pnpm/@opentelemetry+resources@2.7.1_@opentelemetry+api@1.9.1/node_modules/@opentelemetry/resources/build/src/detectors/platform/node/machine-id/getMachineId-linux.js
4
4
  var require_getMachineId_linux = /* @__PURE__ */ __commonJSMin(((exports) => {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,5 +1,5 @@
1
1
  import { a as __toCommonJS, t as __commonJSMin } from "./chunk-BSw8zbkd.mjs";
2
- import { n as init_esm, t as esm_exports } from "./esm-iadMkGbV.mjs";
2
+ import { n as init_esm, t as esm_exports } from "./esm-Ci8E1Gtj.mjs";
3
3
  //#region ../../node_modules/.pnpm/@opentelemetry+resources@2.7.1_@opentelemetry+api@1.9.1/node_modules/@opentelemetry/resources/build/src/detectors/platform/node/machine-id/getMachineId-unsupported.js
4
4
  var require_getMachineId_unsupported = /* @__PURE__ */ __commonJSMin(((exports) => {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,5 +1,5 @@
1
1
  import { a as __toCommonJS, t as __commonJSMin } from "./chunk-BSw8zbkd.mjs";
2
- import { n as init_esm, t as esm_exports } from "./esm-iadMkGbV.mjs";
2
+ import { n as init_esm, t as esm_exports } from "./esm-Ci8E1Gtj.mjs";
3
3
  //#region ../../node_modules/.pnpm/@opentelemetry+resources@2.7.0_@opentelemetry+api@1.9.1/node_modules/@opentelemetry/resources/build/src/detectors/platform/node/machine-id/getMachineId-unsupported.js
4
4
  var require_getMachineId_unsupported = /* @__PURE__ */ __commonJSMin(((exports) => {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,6 +1,6 @@
1
1
  import { a as __toCommonJS, i as __require, t as __commonJSMin } from "./chunk-BSw8zbkd.mjs";
2
- import { n as init_esm, t as esm_exports } from "./esm-iadMkGbV.mjs";
3
- import { t as require_execAsync } from "./execAsync-pImxPKN5.mjs";
2
+ import { n as init_esm, t as esm_exports } from "./esm-Ci8E1Gtj.mjs";
3
+ import { t as require_execAsync } from "./execAsync-DUfRkc4a.mjs";
4
4
  //#region ../../node_modules/.pnpm/@opentelemetry+resources@2.7.0_@opentelemetry+api@1.9.1/node_modules/@opentelemetry/resources/build/src/detectors/platform/node/machine-id/getMachineId-win.js
5
5
  var require_getMachineId_win = /* @__PURE__ */ __commonJSMin(((exports) => {
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,6 +1,6 @@
1
1
  import { a as __toCommonJS, i as __require, t as __commonJSMin } from "./chunk-BSw8zbkd.mjs";
2
- import { n as init_esm, t as esm_exports } from "./esm-iadMkGbV.mjs";
3
- import { t as require_execAsync } from "./execAsync-CCyouKZM.mjs";
2
+ import { n as init_esm, t as esm_exports } from "./esm-Ci8E1Gtj.mjs";
3
+ import { t as require_execAsync } from "./execAsync-YbEZSOYd.mjs";
4
4
  //#region ../../node_modules/.pnpm/@opentelemetry+resources@2.7.1_@opentelemetry+api@1.9.1/node_modules/@opentelemetry/resources/build/src/detectors/platform/node/machine-id/getMachineId-win.js
5
5
  var require_getMachineId_win = /* @__PURE__ */ __commonJSMin(((exports) => {
6
6
  Object.defineProperty(exports, "__esModule", { value: true });