@agent-team-foundation/first-tree-hub 0.5.0 → 0.6.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-BU_7B03u.mjs → bootstrap-Dq_k_6ZD.mjs} +119 -19
- package/dist/cli/index.mjs +416 -28
- package/dist/{core-jjk1xFW_.mjs → core-Dt3yNBTm.mjs} +4264 -568
- package/dist/drizzle/0009_agent_runtime_m1.sql +31 -0
- package/dist/drizzle/0010_cloud_multi_tenancy.sql +34 -0
- package/dist/drizzle/0011_org_uuid_pk.sql +22 -0
- package/dist/drizzle/0012_session_level_state.sql +19 -0
- package/dist/drizzle/0013_hub_tasks.sql +38 -0
- package/dist/drizzle/0014_drop_task_fks.sql +9 -0
- package/dist/drizzle/0015_member_system.sql +34 -0
- package/dist/drizzle/0016_strange_havok.sql +25 -0
- package/dist/drizzle/0017_session_outputs_unique.sql +1 -0
- package/dist/drizzle/0018_agent_visibility.sql +13 -0
- package/dist/drizzle/meta/0012_snapshot.json +1451 -0
- package/dist/drizzle/meta/0013_snapshot.json +1771 -0
- package/dist/drizzle/meta/0014_snapshot.json +1717 -0
- package/dist/drizzle/meta/0016_snapshot.json +1917 -0
- package/dist/drizzle/meta/0018_snapshot.json +1938 -0
- package/dist/drizzle/meta/_journal.json +70 -0
- package/dist/index.mjs +3 -3
- package/dist/web/assets/index--kyp_ZHv.css +1 -0
- package/dist/web/assets/index-D7-5shxZ.js +310 -0
- package/dist/web/index.html +2 -2
- package/package.json +3 -2
- package/dist/web/assets/index-LFh6j4ki.js +0 -280
- package/dist/web/assets/index-vo2Sa6IQ.css +0 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-twds-ZHy.mjs";
|
|
2
2
|
import { chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
|
-
import { randomBytes } from "node:crypto";
|
|
5
|
-
import { execSync } from "node:child_process";
|
|
6
4
|
import { z } from "zod";
|
|
7
5
|
import { parse, stringify } from "yaml";
|
|
6
|
+
import { randomBytes } from "node:crypto";
|
|
8
7
|
import { homedir } from "node:os";
|
|
8
|
+
import { execSync } from "node:child_process";
|
|
9
9
|
//#region ../shared/dist/config/index.mjs
|
|
10
10
|
/** Declare a config field with a Zod schema and optional metadata. */
|
|
11
11
|
function field(schema, options) {
|
|
@@ -29,7 +29,7 @@ function defineConfig(shape) {
|
|
|
29
29
|
}
|
|
30
30
|
const agentConfigSchema = defineConfig({
|
|
31
31
|
token: field(z.string(), { secret: true }),
|
|
32
|
-
|
|
32
|
+
runtime: field(z.string().default("claude-code")),
|
|
33
33
|
concurrency: field(z.number().int().positive().default(5)),
|
|
34
34
|
session: {
|
|
35
35
|
idle_timeout: field(z.number().int().positive().default(300)),
|
|
@@ -458,10 +458,6 @@ const serverConfigSchema = defineConfig({
|
|
|
458
458
|
branch: field(z.string().default("main"))
|
|
459
459
|
}),
|
|
460
460
|
github: {
|
|
461
|
-
token: field(z.string().optional(), {
|
|
462
|
-
env: "FIRST_TREE_HUB_GITHUB_TOKEN",
|
|
463
|
-
secret: true
|
|
464
|
-
}),
|
|
465
461
|
webhookSecret: field(z.string().optional(), {
|
|
466
462
|
env: "FIRST_TREE_HUB_GITHUB_WEBHOOK_SECRET",
|
|
467
463
|
secret: true
|
|
@@ -488,11 +484,18 @@ const serverConfigSchema = defineConfig({
|
|
|
488
484
|
var bootstrap_exports = /* @__PURE__ */ __exportAll({
|
|
489
485
|
bootstrapToken: () => bootstrapToken,
|
|
490
486
|
checkBootstrapStatus: () => checkBootstrapStatus,
|
|
487
|
+
ensureFreshAdminToken: () => ensureFreshAdminToken,
|
|
491
488
|
getGitHubToken: () => getGitHubToken,
|
|
492
489
|
getGitHubUsername: () => getGitHubUsername,
|
|
490
|
+
loadAgentTokenByName: () => loadAgentTokenByName,
|
|
491
|
+
loadCredentials: () => loadCredentials,
|
|
492
|
+
maskToken: () => maskToken,
|
|
493
493
|
resolveAgentToken: () => resolveAgentToken,
|
|
494
|
-
resolveServerUrl: () => resolveServerUrl
|
|
494
|
+
resolveServerUrl: () => resolveServerUrl,
|
|
495
|
+
saveAgentConfig: () => saveAgentConfig,
|
|
496
|
+
saveCredentials: () => saveCredentials
|
|
495
497
|
});
|
|
498
|
+
const CREDENTIALS_PATH = join(DEFAULT_CONFIG_DIR, "credentials.json");
|
|
496
499
|
/**
|
|
497
500
|
* Get the current GitHub username from `gh auth status`.
|
|
498
501
|
*/
|
|
@@ -522,12 +525,12 @@ function getGitHubToken() {
|
|
|
522
525
|
*/
|
|
523
526
|
function resolveServerUrl(flagValue) {
|
|
524
527
|
if (flagValue) return flagValue;
|
|
525
|
-
if (process.env.
|
|
528
|
+
if (process.env.FIRST_TREE_HUB_SERVER_URL) return process.env.FIRST_TREE_HUB_SERVER_URL;
|
|
526
529
|
try {
|
|
527
530
|
const config = getClientConfig();
|
|
528
531
|
if (config.server?.url) return config.server.url;
|
|
529
532
|
} catch {}
|
|
530
|
-
throw new Error("Server URL not configured.\n Provide via: --server <url>,
|
|
533
|
+
throw new Error("Server URL not configured.\n Provide via: --server <url>, FIRST_TREE_HUB_SERVER_URL env var, or\n first-tree-hub config set -c server.url <url>");
|
|
531
534
|
}
|
|
532
535
|
/**
|
|
533
536
|
* Bootstrap a token for an agent using GitHub identity.
|
|
@@ -553,29 +556,109 @@ async function bootstrapToken(serverUrl, agentName, options = {}) {
|
|
|
553
556
|
throw new Error(`Bootstrap failed for "${agentName}": ${msg}`);
|
|
554
557
|
}
|
|
555
558
|
const data = await res.json();
|
|
556
|
-
|
|
559
|
+
const isHuman = options.type === "human";
|
|
560
|
+
if ((options.saveTo === "agent" || !options.saveTo) && !isHuman) {
|
|
557
561
|
const configDir = join(DEFAULT_CONFIG_DIR, "agents", agentName);
|
|
558
562
|
const configPath = `${configDir}/agent.yaml`;
|
|
559
563
|
mkdirSync(configDir, {
|
|
560
564
|
recursive: true,
|
|
561
565
|
mode: 448
|
|
562
566
|
});
|
|
563
|
-
writeFileSync(configPath, `token: "${data.token}"\
|
|
567
|
+
writeFileSync(configPath, `token: "${data.token}"\nruntime: claude-code\n`, { mode: 384 });
|
|
564
568
|
chmodSync(configDir, 448);
|
|
565
|
-
} else if (options.saveTo) {
|
|
569
|
+
} else if (options.saveTo && options.saveTo !== "agent") {
|
|
566
570
|
mkdirSync(dirname(options.saveTo), { recursive: true });
|
|
567
571
|
writeFileSync(options.saveTo, data.token, { mode: 384 });
|
|
568
572
|
}
|
|
569
573
|
return data;
|
|
570
574
|
}
|
|
571
575
|
/**
|
|
572
|
-
*
|
|
573
|
-
*
|
|
576
|
+
* Load an agent's token from `~/.first-tree-hub/agents/<agentName>/agent.yaml`.
|
|
577
|
+
* Returns null if the file is missing or has no token.
|
|
578
|
+
*/
|
|
579
|
+
function loadAgentTokenByName(agentName) {
|
|
580
|
+
const configPath = join(DEFAULT_CONFIG_DIR, "agents", agentName, "agent.yaml");
|
|
581
|
+
if (!existsSync(configPath)) return null;
|
|
582
|
+
try {
|
|
583
|
+
const raw = parse(readFileSync(configPath, "utf-8"));
|
|
584
|
+
if (typeof raw.token === "string" && raw.token.length > 0) return raw.token;
|
|
585
|
+
return null;
|
|
586
|
+
} catch {
|
|
587
|
+
return null;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Resolve agent token with the following precedence:
|
|
592
|
+
* 1. FIRST_TREE_HUB_AGENT_TOKEN env var (explicit token; runtime or manual export)
|
|
593
|
+
* 2. FIRST_TREE_HUB_AGENT env var → lookup in ~/.first-tree-hub/agents/<name>/agent.yaml
|
|
594
|
+
* Throws if neither is configured or the named agent has no stored token.
|
|
574
595
|
*/
|
|
575
596
|
function resolveAgentToken() {
|
|
576
|
-
const token = process.env.
|
|
577
|
-
if (
|
|
578
|
-
|
|
597
|
+
const token = process.env.FIRST_TREE_HUB_AGENT_TOKEN;
|
|
598
|
+
if (token) return token;
|
|
599
|
+
const agentName = process.env.FIRST_TREE_HUB_AGENT;
|
|
600
|
+
if (agentName) {
|
|
601
|
+
const loaded = loadAgentTokenByName(agentName);
|
|
602
|
+
if (loaded) return loaded;
|
|
603
|
+
throw new Error(`Agent "${agentName}" has no token in ${join(DEFAULT_CONFIG_DIR, "agents", agentName)}/agent.yaml.\n Verify the agent exists locally or set FIRST_TREE_HUB_AGENT_TOKEN explicitly.`);
|
|
604
|
+
}
|
|
605
|
+
throw new Error("No agent token configured.\n Set FIRST_TREE_HUB_AGENT_TOKEN directly, or\n set FIRST_TREE_HUB_AGENT=<agentName> to use a stored agent config.");
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Ensure the persisted access token is fresh. Call before any admin API request
|
|
609
|
+
* when using persisted credentials. Returns the (possibly refreshed) access token.
|
|
610
|
+
*/
|
|
611
|
+
async function ensureFreshAdminToken() {
|
|
612
|
+
const envToken = process.env.FIRST_TREE_HUB_ADMIN_TOKEN;
|
|
613
|
+
if (envToken) return envToken;
|
|
614
|
+
const creds = loadCredentials();
|
|
615
|
+
if (!creds) throw new Error("No credentials found.\n Run: first-tree-hub connect <server-url>\n Or set FIRST_TREE_HUB_ADMIN_TOKEN environment variable.");
|
|
616
|
+
if (!isTokenExpired(creds.accessToken)) return creds.accessToken;
|
|
617
|
+
const res = await fetch(`${creds.serverUrl}/api/v1/auth/refresh`, {
|
|
618
|
+
method: "POST",
|
|
619
|
+
headers: { "Content-Type": "application/json" },
|
|
620
|
+
body: JSON.stringify({ refreshToken: creds.refreshToken }),
|
|
621
|
+
signal: AbortSignal.timeout(1e4)
|
|
622
|
+
});
|
|
623
|
+
if (!res.ok) throw new Error("Access token expired and refresh failed.\n Run: first-tree-hub connect <server-url>");
|
|
624
|
+
const data = await res.json();
|
|
625
|
+
saveCredentials({
|
|
626
|
+
...creds,
|
|
627
|
+
accessToken: data.accessToken
|
|
628
|
+
});
|
|
629
|
+
return data.accessToken;
|
|
630
|
+
}
|
|
631
|
+
/** Check if a JWT access token is expired (with 30s margin). */
|
|
632
|
+
function isTokenExpired(token) {
|
|
633
|
+
try {
|
|
634
|
+
const parts = token.split(".");
|
|
635
|
+
if (parts.length !== 3 || !parts[1]) return true;
|
|
636
|
+
const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString());
|
|
637
|
+
if (!payload.exp) return false;
|
|
638
|
+
return payload.exp * 1e3 < Date.now() - 3e4;
|
|
639
|
+
} catch {
|
|
640
|
+
return true;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
/** Persist credentials to disk. */
|
|
644
|
+
function saveCredentials(creds) {
|
|
645
|
+
mkdirSync(dirname(CREDENTIALS_PATH), {
|
|
646
|
+
recursive: true,
|
|
647
|
+
mode: 448
|
|
648
|
+
});
|
|
649
|
+
writeFileSync(CREDENTIALS_PATH, JSON.stringify(creds, null, 2), { mode: 384 });
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Load persisted credentials saved by `connect` command.
|
|
653
|
+
*/
|
|
654
|
+
function loadCredentials() {
|
|
655
|
+
try {
|
|
656
|
+
const data = JSON.parse(readFileSync(CREDENTIALS_PATH, "utf-8"));
|
|
657
|
+
if (data.accessToken && data.refreshToken && data.serverUrl) return data;
|
|
658
|
+
return null;
|
|
659
|
+
} catch {
|
|
660
|
+
return null;
|
|
661
|
+
}
|
|
579
662
|
}
|
|
580
663
|
/**
|
|
581
664
|
* Check if an agent exists and is synced.
|
|
@@ -589,5 +672,22 @@ async function checkBootstrapStatus(serverUrl, agentName) {
|
|
|
589
672
|
}
|
|
590
673
|
return await res.json();
|
|
591
674
|
}
|
|
675
|
+
/**
|
|
676
|
+
* Write agent config (token + runtime) to disk.
|
|
677
|
+
* Used by `agent create`, `agent add`, bootstrap, and server-pushed provisioning.
|
|
678
|
+
*/
|
|
679
|
+
function saveAgentConfig(agentName, token, runtime) {
|
|
680
|
+
const agentDir = join(DEFAULT_CONFIG_DIR, "agents", agentName);
|
|
681
|
+
mkdirSync(agentDir, {
|
|
682
|
+
recursive: true,
|
|
683
|
+
mode: 448
|
|
684
|
+
});
|
|
685
|
+
writeFileSync(join(agentDir, "agent.yaml"), `token: "${token}"\nruntime: ${runtime}\n`, { mode: 384 });
|
|
686
|
+
return agentDir;
|
|
687
|
+
}
|
|
688
|
+
/** Mask a token for display: show first 6 + last 2 chars. */
|
|
689
|
+
function maskToken(token) {
|
|
690
|
+
return token.length > 8 ? `${token.slice(0, 6)}***${token.slice(-2)}` : "***";
|
|
691
|
+
}
|
|
592
692
|
//#endregion
|
|
593
|
-
export { setConfigValue as
|
|
693
|
+
export { resetConfig as C, setConfigValue as D, serverConfigSchema as E, readConfigFile as S, resolveConfigReadonly as T, clientConfigSchema as _, getGitHubToken as a, initConfig as b, maskToken as c, saveAgentConfig as d, saveCredentials as f, agentConfigSchema as g, DEFAULT_HOME_DIR as h, ensureFreshAdminToken as i, resolveAgentToken as l, DEFAULT_DATA_DIR as m, bootstrap_exports as n, getGitHubUsername as o, DEFAULT_CONFIG_DIR as p, checkBootstrapStatus as r, loadAgentTokenByName as s, bootstrapToken as t, resolveServerUrl as u, collectMissingPrompts as v, resetConfigMeta as w, loadAgents as x, getConfigValue as y };
|