@agent-team-foundation/first-tree-hub 0.14.7-alpha.286.1 → 0.14.8

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,3 +1,3 @@
1
- import { a as loadCredentials, s as resolveServerUrl } from "./bootstrap-CmkHQsnS.mjs";
1
+ import { a as loadCredentials, s as resolveServerUrl } from "./bootstrap-D6RsdtJg.mjs";
2
2
  import "./cli-fetch-BGVItZxo.mjs";
3
3
  export { loadCredentials, resolveServerUrl };
@@ -664,6 +664,12 @@ defineConfig({
664
664
  captureClientIp: field(z.boolean().default(false), { env: "FIRST_TREE_HUB_OTEL_CAPTURE_CLIENT_IP" })
665
665
  })
666
666
  },
667
+ update: {
668
+ channel: field(z.enum(["latest", "alpha"]).default("latest"), { env: "FIRST_TREE_HUB_UPDATE_CHANNEL" }),
669
+ commandVersion: field(z.string().optional(), { env: "FIRST_TREE_HUB_COMMAND_VERSION" }),
670
+ pollIntervalMinutes: field(z.coerce.number().int().min(1).max(1440).default(60), { env: "FIRST_TREE_HUB_UPDATE_POLL_INTERVAL_MINUTES" }),
671
+ registryUrl: field(z.string().url().default("https://registry.npmjs.org"), { env: "FIRST_TREE_HUB_UPDATE_REGISTRY_URL" })
672
+ },
667
673
  runtime: {
668
674
  inboxTimeoutSeconds: field(z.coerce.number().int().positive().default(300), { env: "FIRST_TREE_HUB_INBOX_TIMEOUT_SECONDS" }),
669
675
  maxRetryCount: field(z.coerce.number().int().nonnegative().default(3), { env: "FIRST_TREE_HUB_MAX_RETRY_COUNT" }),
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { $ as SdkError, A as printResults, B as ClientRuntime, C as migrateLocalAgentDirs, D as checkNodeVersion, E as checkClientConfig, G as removeLocalAgent, I as restartClientService, J as fail, K as resolveReplyToFromEnv, L as startClientService, M as getClientServiceStatus, N as installClientService, O as checkServerReachable, P as isServiceSupported, Q as FirstTreeHubSDK, R as stopClientService, S as createApiNameResolver, T as checkBackgroundService, U as findStaleAliases, V as handleClientOrgMismatch, W as formatStaleReason, X as ClientOrgMismatchError, Y as success, Z as ClientUserMismatchError, _ as loadOnboardState, a as declineUpdate, b as saveOnboardState, c as detectInstallMode, d as reconcileLocalRuntimeProviders, et as SessionRegistry, f as uploadClientCapabilities, g as formatCheckReport, h as promptMissingFields, i as createExecuteUpdate, it as configureClientLoggerForService, j as reconcileAgentConfigs, k as checkWebSocket, l as fetchLatestVersion, m as promptAddAgent, nt as probeCapabilities, o as promptUpdate, p as isInteractive, q as resolveSenderName, r as registerSaaSConnectCommand, rt as applyClientLoggerConfig, s as PACKAGE_NAME, tt as cleanWorkspaces, u as installGlobalLatest, v as onboardCheck, w as checkAgentConfigs, x as runHomeMigration, y as onboardCreate } from "../saas-connect-gXv14p6J.mjs";
3
- import { C as resolveConfigReadonly, S as resetConfigMeta, _ as initConfig, a as loadCredentials, b as readConfigFile, c as saveAgentConfig, d as DEFAULT_DATA_DIR, f as DEFAULT_HOME_DIR, g as getConfigValue, i as ensureFreshAdminToken, m as clientConfigSchema, p as agentConfigSchema, r as ensureFreshAccessToken, s as resolveServerUrl, u as DEFAULT_CONFIG_DIR, v as loadAgents, w as setConfigValue, x as resetConfig } from "../bootstrap-CmkHQsnS.mjs";
2
+ import { $ as SdkError, A as printResults, B as ClientRuntime, C as migrateLocalAgentDirs, D as checkNodeVersion, E as checkClientConfig, G as removeLocalAgent, I as restartClientService, J as fail, K as resolveReplyToFromEnv, L as startClientService, M as getClientServiceStatus, N as installClientService, O as checkServerReachable, P as isServiceSupported, Q as FirstTreeHubSDK, R as stopClientService, S as createApiNameResolver, T as checkBackgroundService, U as findStaleAliases, V as handleClientOrgMismatch, W as formatStaleReason, X as ClientOrgMismatchError, Y as success, Z as ClientUserMismatchError, _ as loadOnboardState, a as declineUpdate, b as saveOnboardState, c as detectInstallMode, d as reconcileLocalRuntimeProviders, et as SessionRegistry, f as uploadClientCapabilities, g as formatCheckReport, h as promptMissingFields, i as createExecuteUpdate, it as configureClientLoggerForService, j as reconcileAgentConfigs, k as checkWebSocket, l as fetchLatestVersion, m as promptAddAgent, nt as probeCapabilities, o as promptUpdate, p as isInteractive, q as resolveSenderName, r as registerSaaSConnectCommand, rt as applyClientLoggerConfig, s as PACKAGE_NAME, tt as cleanWorkspaces, u as installGlobalLatest, v as onboardCheck, w as checkAgentConfigs, x as runHomeMigration, y as onboardCreate } from "../saas-connect-BOe1ynz2.mjs";
3
+ import { C as resolveConfigReadonly, S as resetConfigMeta, _ as initConfig, a as loadCredentials, b as readConfigFile, c as saveAgentConfig, d as DEFAULT_DATA_DIR, f as DEFAULT_HOME_DIR, g as getConfigValue, i as ensureFreshAdminToken, m as clientConfigSchema, p as agentConfigSchema, r as ensureFreshAccessToken, s as resolveServerUrl, u as DEFAULT_CONFIG_DIR, v as loadAgents, w as setConfigValue, x as resetConfig } from "../bootstrap-D6RsdtJg.mjs";
4
4
  import { a as print, n as CLI_USER_AGENT, o as setJsonMode, r as COMMAND_VERSION, t as cliFetch } from "../cli-fetch-BGVItZxo.mjs";
5
5
  import { n as bindFeishuUser, t as bindFeishuBot } from "../feishu-BBUEVTnb.mjs";
6
6
  import { join } from "node:path";
@@ -1463,13 +1463,13 @@ function decodeJwtExpSeconds(token) {
1463
1463
  //#region src/commands/onboard.ts
1464
1464
  async function promptMissing(args) {
1465
1465
  if (!args.server) try {
1466
- const { resolveServerUrl } = await import("../bootstrap-BmeaRhRp.mjs");
1466
+ const { resolveServerUrl } = await import("../bootstrap-BC1M0f6A.mjs");
1467
1467
  resolveServerUrl();
1468
1468
  } catch {
1469
1469
  args.server = await input({ message: "Hub server URL:" });
1470
1470
  saveOnboardState(args);
1471
1471
  }
1472
- const { loadCredentials } = await import("../bootstrap-BmeaRhRp.mjs");
1472
+ const { loadCredentials } = await import("../bootstrap-BC1M0f6A.mjs");
1473
1473
  if (!loadCredentials()) throw new Error("No saved credentials. Run `first-tree-hub connect <token>` before onboarding.");
1474
1474
  if (!args.id) {
1475
1475
  args.id = await input({ message: "Agent ID:" });
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { $ as SdkError, A as printResults, B as ClientRuntime, D as checkNodeVersion, E as checkClientConfig, F as resolveCliInvocation, H as rotateClientIdWithBackup, I as restartClientService, L as startClientService, M as getClientServiceStatus, N as installClientService, O as checkServerReachable, P as isServiceSupported, Q as FirstTreeHubSDK, R as stopClientService, V as handleClientOrgMismatch, g as formatCheckReport, h as promptMissingFields, k as checkWebSocket, m as promptAddAgent, n as deriveHubUrlFromToken, p as isInteractive, t as HubUrlDerivationError, v as onboardCheck, w as checkAgentConfigs, x as runHomeMigration, y as onboardCreate, z as uninstallClientService } from "./saas-connect-gXv14p6J.mjs";
2
- import { i as ensureFreshAdminToken, n as AuthRefreshRateLimitedError, o as resolveAccessToken, r as ensureFreshAccessToken, s as resolveServerUrl, t as AuthRefreshFailedError } from "./bootstrap-CmkHQsnS.mjs";
1
+ import { $ as SdkError, A as printResults, B as ClientRuntime, D as checkNodeVersion, E as checkClientConfig, F as resolveCliInvocation, H as rotateClientIdWithBackup, I as restartClientService, L as startClientService, M as getClientServiceStatus, N as installClientService, O as checkServerReachable, P as isServiceSupported, Q as FirstTreeHubSDK, R as stopClientService, V as handleClientOrgMismatch, g as formatCheckReport, h as promptMissingFields, k as checkWebSocket, m as promptAddAgent, n as deriveHubUrlFromToken, p as isInteractive, t as HubUrlDerivationError, v as onboardCheck, w as checkAgentConfigs, x as runHomeMigration, y as onboardCreate, z as uninstallClientService } from "./saas-connect-BOe1ynz2.mjs";
2
+ import { i as ensureFreshAdminToken, n as AuthRefreshRateLimitedError, o as resolveAccessToken, r as ensureFreshAccessToken, s as resolveServerUrl, t as AuthRefreshFailedError } from "./bootstrap-D6RsdtJg.mjs";
3
3
  import { i as blank, s as status } from "./cli-fetch-BGVItZxo.mjs";
4
4
  import { n as bindFeishuUser, t as bindFeishuBot } from "./feishu-BBUEVTnb.mjs";
5
5
  export { AuthRefreshFailedError, AuthRefreshRateLimitedError, ClientRuntime, FirstTreeHubSDK, HubUrlDerivationError, SdkError, bindFeishuBot, bindFeishuUser, blank, checkAgentConfigs, checkClientConfig, checkNodeVersion, checkServerReachable, checkWebSocket, deriveHubUrlFromToken, ensureFreshAccessToken, ensureFreshAdminToken, formatCheckReport, getClientServiceStatus, handleClientOrgMismatch, installClientService, isInteractive, isServiceSupported, onboardCheck, onboardCreate, printResults, promptAddAgent, promptMissingFields, resolveAccessToken, resolveCliInvocation, resolveServerUrl, restartClientService, rotateClientIdWithBackup, runHomeMigration, startClientService, status, stopClientService, uninstallClientService };
@@ -1,4 +1,4 @@
1
- import { C as resolveConfigReadonly, S as resetConfigMeta, _ as initConfig, a as loadCredentials, c as saveAgentConfig, d as DEFAULT_DATA_DIR$1, f as DEFAULT_HOME_DIR$1, h as collectMissingPrompts, l as saveCredentials, m as clientConfigSchema, p as agentConfigSchema, r as ensureFreshAccessToken, s as resolveServerUrl, u as DEFAULT_CONFIG_DIR, v as loadAgents, w as setConfigValue, x as resetConfig, y as migrateLegacyHome } from "./bootstrap-CmkHQsnS.mjs";
1
+ import { C as resolveConfigReadonly, S as resetConfigMeta, _ as initConfig, a as loadCredentials, c as saveAgentConfig, d as DEFAULT_DATA_DIR$1, f as DEFAULT_HOME_DIR$1, h as collectMissingPrompts, l as saveCredentials, m as clientConfigSchema, p as agentConfigSchema, r as ensureFreshAccessToken, s as resolveServerUrl, u as DEFAULT_CONFIG_DIR, v as loadAgents, w as setConfigValue, x as resetConfig, y as migrateLegacyHome } from "./bootstrap-D6RsdtJg.mjs";
2
2
  import { a as print, i as blank, n as CLI_USER_AGENT, r as COMMAND_VERSION, t as cliFetch } from "./cli-fetch-BGVItZxo.mjs";
3
3
  import { createRequire } from "node:module";
4
4
  import { z } from "zod";
@@ -5883,6 +5883,12 @@ defineConfig({
5883
5883
  captureClientIp: field(z.boolean().default(false), { env: "FIRST_TREE_HUB_OTEL_CAPTURE_CLIENT_IP" })
5884
5884
  })
5885
5885
  },
5886
+ update: {
5887
+ channel: field(z.enum(["latest", "alpha"]).default("latest"), { env: "FIRST_TREE_HUB_UPDATE_CHANNEL" }),
5888
+ commandVersion: field(z.string().optional(), { env: "FIRST_TREE_HUB_COMMAND_VERSION" }),
5889
+ pollIntervalMinutes: field(z.coerce.number().int().min(1).max(1440).default(60), { env: "FIRST_TREE_HUB_UPDATE_POLL_INTERVAL_MINUTES" }),
5890
+ registryUrl: field(z.string().url().default("https://registry.npmjs.org"), { env: "FIRST_TREE_HUB_UPDATE_REGISTRY_URL" })
5891
+ },
5886
5892
  runtime: {
5887
5893
  inboxTimeoutSeconds: field(z.coerce.number().int().positive().default(300), { env: "FIRST_TREE_HUB_INBOX_TIMEOUT_SECONDS" }),
5888
5894
  maxRetryCount: field(z.coerce.number().int().nonnegative().default(3), { env: "FIRST_TREE_HUB_MAX_RETRY_COUNT" }),
@@ -13610,17 +13616,46 @@ function detectInstallMode(argv1 = process.argv[1] ?? "") {
13610
13616
  return "npx";
13611
13617
  }
13612
13618
  /**
13613
- * Install `<pkg>@latest` globally. Returns after the child exits. Does not
13614
- * exit the parent process callers are expected to handle that (so the
13615
- * UpdateManager can attempt the restart itself while this function remains
13616
- * side-effect-scoped).
13617
- */
13618
- async function installGlobalLatest() {
13619
+ * Validate an npm install spec (the part after `@` in `<pkg>@<spec>`). We
13620
+ * accept either a known dist-tag string (`latest`, `alpha`, …) or an exact
13621
+ * SemVer version (`0.14.7`, `0.14.8-alpha.286.1`). The intent is purely
13622
+ * defensive: the spec is concatenated into the npm CLI args, and we never
13623
+ * want to forward an attacker-controlled shell metacharacter from a
13624
+ * (compromised) server welcome frame straight into `spawn`. spawn() already
13625
+ * argv-escapes, but a `--registry=...` style spec would still be
13626
+ * interpreted as an npm flag — refusing leading dashes and whitespace
13627
+ * collapses the surface unambiguously.
13628
+ */
13629
+ function isSafeInstallSpec(spec) {
13630
+ if (typeof spec !== "string" || spec.length === 0 || spec.length > 128) return false;
13631
+ if (spec.startsWith("-")) return false;
13632
+ return /^[A-Za-z0-9.+-]+$/.test(spec);
13633
+ }
13634
+ /**
13635
+ * Install `<pkg>@<spec>` globally. `spec` is either a dist-tag (e.g. `latest`)
13636
+ * or an exact version (e.g. `0.14.7-alpha.286.1`). Returns after the child
13637
+ * exits. Does not exit the parent process — callers are expected to handle
13638
+ * that (so the UpdateManager can attempt the restart itself while this
13639
+ * function remains side-effect-scoped).
13640
+ *
13641
+ * Why both shapes exist: the auto-update path receives `targetVersion` from
13642
+ * the server `welcome` frame and MUST install that exact version — using
13643
+ * `@latest` from auto-update would silently mis-resolve once the server
13644
+ * starts advertising alpha builds (alpha lives on a different dist-tag).
13645
+ * The manual `first-tree-hub update` CLI keeps the dist-tag form so users
13646
+ * who type the command without args still get "newest stable on npm".
13647
+ */
13648
+ async function installGlobalSpec(spec) {
13649
+ if (!isSafeInstallSpec(spec)) return {
13650
+ ok: false,
13651
+ mode: "global",
13652
+ reason: `Refusing to install: invalid npm spec ${JSON.stringify(spec)}`
13653
+ };
13619
13654
  return new Promise((resolvePromise) => {
13620
13655
  const child = spawn(resolveNpmCommand(), [
13621
13656
  "install",
13622
13657
  "-g",
13623
- `${PACKAGE_NAME}@latest`
13658
+ `${PACKAGE_NAME}@${spec}`
13624
13659
  ], { stdio: [
13625
13660
  "ignore",
13626
13661
  "pipe",
@@ -13658,6 +13693,15 @@ async function installGlobalLatest() {
13658
13693
  });
13659
13694
  }
13660
13695
  /**
13696
+ * Back-compat shim: install `<pkg>@latest`. The manual `first-tree-hub
13697
+ * update` CLI uses this so an operator-typed `update` keeps the
13698
+ * "newest stable on npm" behaviour. Auto-update prefers `installGlobalSpec`
13699
+ * with the welcome frame's `targetVersion`.
13700
+ */
13701
+ async function installGlobalLatest() {
13702
+ return installGlobalSpec("latest");
13703
+ }
13704
+ /**
13661
13705
  * Best-effort extraction of the version npm reported as installed. npm's
13662
13706
  * stdout lines look like `+ @agent-team-foundation/first-tree-hub@0.9.2`.
13663
13707
  * Returns null if nothing matches — callers treat null as "install succeeded
@@ -13710,7 +13754,7 @@ function fetchLatestVersion(timeoutMs = 1e4) {
13710
13754
  }
13711
13755
  /** Interactive update prompt. Defaults to N on timeout. */
13712
13756
  const promptUpdate = async ({ currentVersion, targetVersion, timeoutSeconds }) => {
13713
- const message = `A newer First Tree Hub client is available.\n You: ${currentVersion}\n Server bundled with: ${targetVersion}\n Will install: latest on npm (>= ${targetVersion})\n Updating will restart the client and briefly interrupt any active sessions.\n Update now?`;
13757
+ const message = `A newer First Tree Hub client is available.\n You: ${currentVersion}\n Server recommends: ${targetVersion}\n Will install: ${targetVersion}\n Updating will restart the client and briefly interrupt any active sessions.\n Update now?`;
13714
13758
  try {
13715
13759
  const controller = new AbortController();
13716
13760
  const timer = setTimeout(() => controller.abort(), timeoutSeconds * 1e3);
@@ -13748,7 +13792,7 @@ const declineUpdate = async () => false;
13748
13792
  * operator restarts manually.
13749
13793
  */
13750
13794
  function createExecuteUpdate({ managed }) {
13751
- return async () => {
13795
+ return async ({ targetVersion }) => {
13752
13796
  const mode = detectInstallMode();
13753
13797
  if (mode === "source") {
13754
13798
  print.line(" [update] Running from source checkout — self-update skipped. Use `git pull` instead.\n");
@@ -13758,13 +13802,13 @@ function createExecuteUpdate({ managed }) {
13758
13802
  print.line(" [update] Cannot self-update — not launched from a global npm install.\n Run `npm i -g @agent-team-foundation/first-tree-hub` manually.\n");
13759
13803
  return { installed: false };
13760
13804
  }
13761
- print.line(" [update] Running `npm install -g @agent-team-foundation/first-tree-hub@latest`...\n");
13762
- const result = await installGlobalLatest();
13805
+ print.line(` [update] Running \`npm install -g @agent-team-foundation/first-tree-hub@${targetVersion}\`...\n`);
13806
+ const result = await installGlobalSpec(targetVersion);
13763
13807
  if (!result.ok) {
13764
13808
  print.line(` [update] Install failed: ${result.reason}\n`);
13765
13809
  return { installed: false };
13766
13810
  }
13767
- const installed = result.installedVersion ?? "latest";
13811
+ const installed = result.installedVersion ?? targetVersion;
13768
13812
  if (managed) {
13769
13813
  print.line(` [update] Installed ${installed}. Restarting (exit 75).\n`);
13770
13814
  process.exit(75);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-team-foundation/first-tree-hub",
3
- "version": "0.14.7-alpha.286.1",
3
+ "version": "0.14.8",
4
4
  "type": "module",
5
5
  "description": "First Tree Hub — unified CLI for client and agent management",
6
6
  "exports": {