@agent-team-foundation/first-tree-hub 0.6.3 → 0.7.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-DNL1cEwv.mjs → bootstrap-CRDR6NwE.mjs} +1 -1
- package/dist/cli/index.mjs +90 -13
- package/dist/{core-B10jgThe.mjs → core-C05B8FzH.mjs} +948 -167
- package/dist/drizzle/0022_session_events.sql +32 -0
- package/dist/drizzle/meta/_journal.json +7 -0
- package/dist/{feishu-BoMJHlOv.mjs → feishu-CJ08ntOD.mjs} +54 -6
- package/dist/index.mjs +4 -4
- package/dist/web/assets/index-30C-bada.js +333 -0
- package/dist/web/assets/index-COlVuDVR.css +1 -0
- package/dist/web/index.html +9 -2
- package/package.json +1 -1
- package/dist/web/assets/index-CTl4pHIL.css +0 -1
- package/dist/web/assets/index-CnLpaSBg.js +0 -308
|
@@ -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()
|
|
553
|
+
return payload.exp * 1e3 < Date.now() + 3e4;
|
|
554
554
|
} catch {
|
|
555
555
|
return true;
|
|
556
556
|
}
|
package/dist/cli/index.mjs
CHANGED
|
@@ -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-
|
|
3
|
-
import { A as SdkError,
|
|
4
|
-
import { n as bindFeishuUser, t as bindFeishuBot } from "../feishu-
|
|
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 stopPostgres, C as checkServerReachable, F as SdkError, I as SessionRegistry, L as cleanWorkspaces, M as createOwner, P as FirstTreeHubSDK, S as checkServerHealth, T as printResults, _ as checkClientConfig, a as uninstallClientService, b as checkNodeVersion, c as promptAddAgent, d as loadOnboardState, f as onboardCheck, g as checkAgentConfigs, h as runMigrations, j as ClientRuntime, l as promptMissingFields, m as saveOnboardState, n as installClientService, o as startServer, p as onboardCreate, r as isServiceSupported, s as isInteractive, t as getClientServiceStatus, u as formatCheckReport, v as checkDatabase, w as checkWebSocket, x as checkServerConfig, y as checkDocker } from "../core-C05B8FzH.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
|
|
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".`);
|
|
@@ -774,7 +798,7 @@ async function authenticateInteractive(url) {
|
|
|
774
798
|
return await loginRes.json();
|
|
775
799
|
}
|
|
776
800
|
function registerConnectCommand(parent) {
|
|
777
|
-
parent.command("connect <server-url>").description("Connect to a Hub server — configure, authenticate, and
|
|
801
|
+
parent.command("connect <server-url>").description("Connect to a Hub server — configure, authenticate, and install the background service").option("--token <token>", "Connect token (from Hub web console) — skips interactive login").option("--no-service", "Skip background service install (runs inline until Ctrl+C)").action(async (serverUrl, options) => {
|
|
778
802
|
try {
|
|
779
803
|
const url = serverUrl.replace(/\/+$/, "");
|
|
780
804
|
setConfigValue(join(DEFAULT_CONFIG_DIR, "client.yaml"), "server.url", url);
|
|
@@ -790,12 +814,23 @@ function registerConnectCommand(parent) {
|
|
|
790
814
|
schema: clientConfigSchema,
|
|
791
815
|
role: "client"
|
|
792
816
|
});
|
|
817
|
+
process.stderr.write(` \u2713 Connected as this computer (id: ${config.client.id})\n`);
|
|
818
|
+
if (options.service !== false && isServiceSupported()) {
|
|
819
|
+
const info = installClientService();
|
|
820
|
+
process.stderr.write(` \u2713 Installed as a background service (${info.platform}) — you can close this terminal\n\n`);
|
|
821
|
+
process.stderr.write(` Unit: ${info.unitPath}\n`);
|
|
822
|
+
process.stderr.write(` Logs: ${info.logDir}\n`);
|
|
823
|
+
if (info.state === "active" && info.detail) process.stderr.write(` State: running (${info.detail})\n`);
|
|
824
|
+
process.stderr.write("\n");
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
if (options.service === false) process.stderr.write(" (--no-service) running inline — Ctrl+C to stop\n");
|
|
828
|
+
else process.stderr.write(` Background service not supported on ${process.platform}; running inline.\n`);
|
|
793
829
|
const agentsDir = join(DEFAULT_CONFIG_DIR, "agents");
|
|
794
830
|
const agents = loadAgents({
|
|
795
831
|
schema: agentConfigSchema,
|
|
796
832
|
agentsDir
|
|
797
833
|
});
|
|
798
|
-
process.stderr.write(`\n Starting client (id: ${config.client.id})...\n`);
|
|
799
834
|
const runtime = new ClientRuntime(config.server.url, config.client.id);
|
|
800
835
|
for (const [name, agentConfig] of agents) runtime.addAgent(name, agentConfig);
|
|
801
836
|
await runtime.start();
|
|
@@ -899,21 +934,63 @@ function registerClientCommands(program) {
|
|
|
899
934
|
process.stderr.write(" No agents directory found.\n");
|
|
900
935
|
}
|
|
901
936
|
});
|
|
902
|
-
client.command("
|
|
937
|
+
const service = client.command("service").description("Install/uninstall the background service that keeps this computer online");
|
|
938
|
+
service.command("install").description("Install as a background service — auto-starts on login/boot").action(() => {
|
|
939
|
+
if (!isServiceSupported()) {
|
|
940
|
+
process.stderr.write(` Background service is not supported on ${process.platform}.\n Run \`first-tree-hub client start\` manually to keep the computer online.
|
|
941
|
+
`);
|
|
942
|
+
process.exit(1);
|
|
943
|
+
}
|
|
944
|
+
try {
|
|
945
|
+
const info = installClientService();
|
|
946
|
+
process.stderr.write(`\n \u2713 Installed as a background service (${info.platform}).\n`);
|
|
947
|
+
process.stderr.write(` Unit: ${info.unitPath}\n`);
|
|
948
|
+
process.stderr.write(` Logs: ${info.logDir}\n`);
|
|
949
|
+
if (info.state === "active") process.stderr.write(` State: running${info.detail ? ` (${info.detail})` : ""}\n`);
|
|
950
|
+
else process.stderr.write(` State: ${info.state}${info.detail ? ` (${info.detail})` : ""}\n`);
|
|
951
|
+
process.stderr.write("\n You can close this terminal — the computer stays online.\n");
|
|
952
|
+
} catch (error) {
|
|
953
|
+
fail("SERVICE_INSTALL_ERROR", error instanceof Error ? error.message : String(error));
|
|
954
|
+
}
|
|
955
|
+
});
|
|
956
|
+
service.command("status").description("Show background service state").action(() => {
|
|
957
|
+
const info = getClientServiceStatus();
|
|
958
|
+
if (info.platform === "unsupported") {
|
|
959
|
+
process.stderr.write(` Not supported on ${process.platform}.\n`);
|
|
960
|
+
return;
|
|
961
|
+
}
|
|
962
|
+
process.stderr.write(`\n ${info.platform}: ${info.label}\n`);
|
|
963
|
+
process.stderr.write(` Unit: ${info.unitPath}\n`);
|
|
964
|
+
process.stderr.write(` Logs: ${info.logDir}\n`);
|
|
965
|
+
process.stderr.write(` State: ${info.state}${info.detail ? ` (${info.detail})` : ""}\n\n`);
|
|
966
|
+
});
|
|
967
|
+
service.command("uninstall").description("Stop and remove the background service").action(() => {
|
|
968
|
+
if (!isServiceSupported()) {
|
|
969
|
+
process.stderr.write(` Not supported on ${process.platform}.\n`);
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
try {
|
|
973
|
+
const info = uninstallClientService();
|
|
974
|
+
process.stderr.write(`\n \u2713 Uninstalled background service (${info.platform}).\n\n`);
|
|
975
|
+
} catch (error) {
|
|
976
|
+
fail("SERVICE_UNINSTALL_ERROR", error instanceof Error ? error.message : String(error));
|
|
977
|
+
}
|
|
978
|
+
});
|
|
979
|
+
client.command("hub-list").description("List clients on the Hub server").option("--server <url>", "Hub server URL").action(async (options) => {
|
|
903
980
|
try {
|
|
904
981
|
const serverUrl = resolveServerUrl(options.server);
|
|
905
982
|
const token = await ensureFreshAccessToken();
|
|
906
|
-
const response = await fetch(`${serverUrl}/api/v1/
|
|
983
|
+
const response = await fetch(`${serverUrl}/api/v1/clients`, {
|
|
907
984
|
headers: { Authorization: `Bearer ${token}` },
|
|
908
985
|
signal: AbortSignal.timeout(1e4)
|
|
909
986
|
});
|
|
910
987
|
if (!response.ok) fail("FETCH_ERROR", `Server returned ${response.status}`, 1);
|
|
911
988
|
const clients = await response.json();
|
|
912
989
|
if (clients.length === 0) {
|
|
913
|
-
process.stderr.write(" No
|
|
990
|
+
process.stderr.write(" No clients.\n");
|
|
914
991
|
return;
|
|
915
992
|
}
|
|
916
|
-
process.stderr.write(`\n
|
|
993
|
+
process.stderr.write(`\n Clients: ${clients.length}\n\n`);
|
|
917
994
|
const header = ` ${"CLIENT".padEnd(20)} ${"HOST".padEnd(25)} ${"AGENTS".padEnd(8)} CONNECTED`;
|
|
918
995
|
process.stderr.write(`${header}\n`);
|
|
919
996
|
process.stderr.write(` ${"─".repeat(header.length - 2)}\n`);
|
|
@@ -930,7 +1007,7 @@ function registerClientCommands(program) {
|
|
|
930
1007
|
try {
|
|
931
1008
|
const serverUrl = resolveServerUrl(options.server);
|
|
932
1009
|
const token = await ensureFreshAccessToken();
|
|
933
|
-
const response = await fetch(`${serverUrl}/api/v1/
|
|
1010
|
+
const response = await fetch(`${serverUrl}/api/v1/clients/${clientId}/disconnect`, {
|
|
934
1011
|
method: "POST",
|
|
935
1012
|
headers: { Authorization: `Bearer ${token}` },
|
|
936
1013
|
signal: AbortSignal.timeout(1e4)
|
|
@@ -1049,13 +1126,13 @@ function isSecretField(schema, dotPath) {
|
|
|
1049
1126
|
//#region src/commands/onboard.ts
|
|
1050
1127
|
async function promptMissing(args) {
|
|
1051
1128
|
if (!args.server) try {
|
|
1052
|
-
const { resolveServerUrl } = await import("../bootstrap-
|
|
1129
|
+
const { resolveServerUrl } = await import("../bootstrap-CRDR6NwE.mjs").then((n) => n.t);
|
|
1053
1130
|
resolveServerUrl();
|
|
1054
1131
|
} catch {
|
|
1055
1132
|
args.server = await input({ message: "Hub server URL:" });
|
|
1056
1133
|
saveOnboardState(args);
|
|
1057
1134
|
}
|
|
1058
|
-
const { loadCredentials } = await import("../bootstrap-
|
|
1135
|
+
const { loadCredentials } = await import("../bootstrap-CRDR6NwE.mjs").then((n) => n.t);
|
|
1059
1136
|
if (!loadCredentials()) throw new Error("No saved credentials. Run `first-tree-hub client connect <server-url>` before onboarding.");
|
|
1060
1137
|
if (!args.id) {
|
|
1061
1138
|
args.id = await input({ message: "Agent ID:" });
|