@agent-team-foundation/first-tree-hub 0.4.0 → 0.6.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.
- package/dist/{bootstrap-uyPaaI05.mjs → bootstrap-BnlTKa0H.mjs} +94 -18
- package/dist/cli/index.mjs +412 -28
- package/dist/{core-CMeOAZmx.mjs → core-B9bH7EjM.mjs} +4141 -704
- package/dist/drizzle/0008_uuid_identity.sql +12 -0
- 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/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/_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-C_FKYVro.js +310 -0
- package/dist/web/index.html +2 -2
- package/package.json +3 -2
- package/dist/web/assets/index-C4J9gHaF.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,17 @@ 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
|
+
loadCredentials: () => loadCredentials,
|
|
491
|
+
maskToken: () => maskToken,
|
|
493
492
|
resolveAgentToken: () => resolveAgentToken,
|
|
494
|
-
resolveServerUrl: () => resolveServerUrl
|
|
493
|
+
resolveServerUrl: () => resolveServerUrl,
|
|
494
|
+
saveAgentConfig: () => saveAgentConfig,
|
|
495
|
+
saveCredentials: () => saveCredentials
|
|
495
496
|
});
|
|
497
|
+
const CREDENTIALS_PATH = join(DEFAULT_CONFIG_DIR, "credentials.json");
|
|
496
498
|
/**
|
|
497
499
|
* Get the current GitHub username from `gh auth status`.
|
|
498
500
|
*/
|
|
@@ -532,7 +534,7 @@ function resolveServerUrl(flagValue) {
|
|
|
532
534
|
/**
|
|
533
535
|
* Bootstrap a token for an agent using GitHub identity.
|
|
534
536
|
*/
|
|
535
|
-
async function bootstrapToken(serverUrl,
|
|
537
|
+
async function bootstrapToken(serverUrl, agentName, options = {}) {
|
|
536
538
|
const githubToken = getGitHubToken();
|
|
537
539
|
const body = { name: "bootstrap" };
|
|
538
540
|
if (options.type) body.type = options.type;
|
|
@@ -540,7 +542,7 @@ async function bootstrapToken(serverUrl, agentId, options = {}) {
|
|
|
540
542
|
if (options.delegateMention) body.delegateMention = options.delegateMention;
|
|
541
543
|
if (options.profile) body.profile = options.profile;
|
|
542
544
|
if (options.metadata) body.metadata = options.metadata;
|
|
543
|
-
const res = await fetch(`${serverUrl}/api/v1/bootstrap/${encodeURIComponent(
|
|
545
|
+
const res = await fetch(`${serverUrl}/api/v1/bootstrap/${encodeURIComponent(agentName)}/token`, {
|
|
544
546
|
method: "POST",
|
|
545
547
|
headers: {
|
|
546
548
|
"X-GitHub-Token": githubToken,
|
|
@@ -550,19 +552,20 @@ async function bootstrapToken(serverUrl, agentId, options = {}) {
|
|
|
550
552
|
});
|
|
551
553
|
if (!res.ok) {
|
|
552
554
|
const msg = (await res.json().catch(() => ({}))).error ?? `HTTP ${res.status}`;
|
|
553
|
-
throw new Error(`Bootstrap failed for "${
|
|
555
|
+
throw new Error(`Bootstrap failed for "${agentName}": ${msg}`);
|
|
554
556
|
}
|
|
555
557
|
const data = await res.json();
|
|
556
|
-
|
|
557
|
-
|
|
558
|
+
const isHuman = options.type === "human";
|
|
559
|
+
if ((options.saveTo === "agent" || !options.saveTo) && !isHuman) {
|
|
560
|
+
const configDir = join(DEFAULT_CONFIG_DIR, "agents", agentName);
|
|
558
561
|
const configPath = `${configDir}/agent.yaml`;
|
|
559
562
|
mkdirSync(configDir, {
|
|
560
563
|
recursive: true,
|
|
561
564
|
mode: 448
|
|
562
565
|
});
|
|
563
|
-
writeFileSync(configPath, `token: "${data.token}"\
|
|
566
|
+
writeFileSync(configPath, `token: "${data.token}"\nruntime: claude-code\n`, { mode: 384 });
|
|
564
567
|
chmodSync(configDir, 448);
|
|
565
|
-
} else if (options.saveTo) {
|
|
568
|
+
} else if (options.saveTo && options.saveTo !== "agent") {
|
|
566
569
|
mkdirSync(dirname(options.saveTo), { recursive: true });
|
|
567
570
|
writeFileSync(options.saveTo, data.token, { mode: 384 });
|
|
568
571
|
}
|
|
@@ -578,16 +581,89 @@ function resolveAgentToken() {
|
|
|
578
581
|
return token;
|
|
579
582
|
}
|
|
580
583
|
/**
|
|
584
|
+
* Ensure the persisted access token is fresh. Call before any admin API request
|
|
585
|
+
* when using persisted credentials. Returns the (possibly refreshed) access token.
|
|
586
|
+
*/
|
|
587
|
+
async function ensureFreshAdminToken() {
|
|
588
|
+
const envToken = process.env.FIRST_TREE_HUB_ADMIN_TOKEN;
|
|
589
|
+
if (envToken) return envToken;
|
|
590
|
+
const creds = loadCredentials();
|
|
591
|
+
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.");
|
|
592
|
+
if (!isTokenExpired(creds.accessToken)) return creds.accessToken;
|
|
593
|
+
const res = await fetch(`${creds.serverUrl}/api/v1/auth/refresh`, {
|
|
594
|
+
method: "POST",
|
|
595
|
+
headers: { "Content-Type": "application/json" },
|
|
596
|
+
body: JSON.stringify({ refreshToken: creds.refreshToken }),
|
|
597
|
+
signal: AbortSignal.timeout(1e4)
|
|
598
|
+
});
|
|
599
|
+
if (!res.ok) throw new Error("Access token expired and refresh failed.\n Run: first-tree-hub connect <server-url>");
|
|
600
|
+
const data = await res.json();
|
|
601
|
+
saveCredentials({
|
|
602
|
+
...creds,
|
|
603
|
+
accessToken: data.accessToken
|
|
604
|
+
});
|
|
605
|
+
return data.accessToken;
|
|
606
|
+
}
|
|
607
|
+
/** Check if a JWT access token is expired (with 30s margin). */
|
|
608
|
+
function isTokenExpired(token) {
|
|
609
|
+
try {
|
|
610
|
+
const parts = token.split(".");
|
|
611
|
+
if (parts.length !== 3 || !parts[1]) return true;
|
|
612
|
+
const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString());
|
|
613
|
+
if (!payload.exp) return false;
|
|
614
|
+
return payload.exp * 1e3 < Date.now() - 3e4;
|
|
615
|
+
} catch {
|
|
616
|
+
return true;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
/** Persist credentials to disk. */
|
|
620
|
+
function saveCredentials(creds) {
|
|
621
|
+
mkdirSync(dirname(CREDENTIALS_PATH), {
|
|
622
|
+
recursive: true,
|
|
623
|
+
mode: 448
|
|
624
|
+
});
|
|
625
|
+
writeFileSync(CREDENTIALS_PATH, JSON.stringify(creds, null, 2), { mode: 384 });
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Load persisted credentials saved by `connect` command.
|
|
629
|
+
*/
|
|
630
|
+
function loadCredentials() {
|
|
631
|
+
try {
|
|
632
|
+
const data = JSON.parse(readFileSync(CREDENTIALS_PATH, "utf-8"));
|
|
633
|
+
if (data.accessToken && data.refreshToken && data.serverUrl) return data;
|
|
634
|
+
return null;
|
|
635
|
+
} catch {
|
|
636
|
+
return null;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
581
640
|
* Check if an agent exists and is synced.
|
|
582
641
|
*/
|
|
583
|
-
async function checkBootstrapStatus(serverUrl,
|
|
642
|
+
async function checkBootstrapStatus(serverUrl, agentName) {
|
|
584
643
|
const githubToken = getGitHubToken();
|
|
585
|
-
const res = await fetch(`${serverUrl}/api/v1/bootstrap/${encodeURIComponent(
|
|
644
|
+
const res = await fetch(`${serverUrl}/api/v1/bootstrap/${encodeURIComponent(agentName)}/status`, { headers: { "X-GitHub-Token": githubToken } });
|
|
586
645
|
if (!res.ok) {
|
|
587
646
|
const body = await res.json().catch(() => ({}));
|
|
588
647
|
throw new Error(body.error ?? `HTTP ${res.status}`);
|
|
589
648
|
}
|
|
590
649
|
return await res.json();
|
|
591
650
|
}
|
|
651
|
+
/**
|
|
652
|
+
* Write agent config (token + runtime) to disk.
|
|
653
|
+
* Used by `agent create`, `agent add`, bootstrap, and server-pushed provisioning.
|
|
654
|
+
*/
|
|
655
|
+
function saveAgentConfig(agentName, token, runtime) {
|
|
656
|
+
const agentDir = join(DEFAULT_CONFIG_DIR, "agents", agentName);
|
|
657
|
+
mkdirSync(agentDir, {
|
|
658
|
+
recursive: true,
|
|
659
|
+
mode: 448
|
|
660
|
+
});
|
|
661
|
+
writeFileSync(join(agentDir, "agent.yaml"), `token: "${token}"\nruntime: ${runtime}\n`, { mode: 384 });
|
|
662
|
+
return agentDir;
|
|
663
|
+
}
|
|
664
|
+
/** Mask a token for display: show first 6 + last 2 chars. */
|
|
665
|
+
function maskToken(token) {
|
|
666
|
+
return token.length > 8 ? `${token.slice(0, 6)}***${token.slice(-2)}` : "***";
|
|
667
|
+
}
|
|
592
668
|
//#endregion
|
|
593
|
-
export { setConfigValue as S,
|
|
669
|
+
export { resetConfigMeta as C, setConfigValue as E, resetConfig as S, serverConfigSchema as T, collectMissingPrompts as _, getGitHubToken as a, loadAgents as b, resolveAgentToken as c, saveCredentials as d, DEFAULT_CONFIG_DIR as f, clientConfigSchema as g, agentConfigSchema as h, ensureFreshAdminToken as i, resolveServerUrl as l, DEFAULT_HOME_DIR as m, bootstrap_exports as n, getGitHubUsername as o, DEFAULT_DATA_DIR as p, checkBootstrapStatus as r, maskToken as s, bootstrapToken as t, saveAgentConfig as u, getConfigValue as v, resolveConfigReadonly as w, readConfigFile as x, initConfig as y };
|