@growthub/cli 0.6.7 → 0.7.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/index.js +694 -269
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
#!/usr/bin/env node
|
|
3
2
|
var __defProp = Object.defineProperty;
|
|
4
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
4
|
var __esm = (fn, res) => function __init() {
|
|
@@ -8016,8 +8015,8 @@ var init_onboard = __esm({
|
|
|
8016
8015
|
|
|
8017
8016
|
// src/client/http.ts
|
|
8018
8017
|
import { URL as URL2 } from "node:url";
|
|
8019
|
-
function buildUrl(apiBase,
|
|
8020
|
-
const normalizedPath =
|
|
8018
|
+
function buildUrl(apiBase, path57) {
|
|
8019
|
+
const normalizedPath = path57.startsWith("/") ? path57 : `/${path57}`;
|
|
8021
8020
|
const [pathname, query] = normalizedPath.split("?");
|
|
8022
8021
|
const url = new URL2(apiBase);
|
|
8023
8022
|
url.pathname = `${url.pathname.replace(/\/+$/, "")}${pathname}`;
|
|
@@ -8079,26 +8078,26 @@ var init_http = __esm({
|
|
|
8079
8078
|
this.runId = opts.runId?.trim() || void 0;
|
|
8080
8079
|
this.userId = opts.userId?.trim() || void 0;
|
|
8081
8080
|
}
|
|
8082
|
-
get(
|
|
8083
|
-
return this.request(
|
|
8081
|
+
get(path57, opts) {
|
|
8082
|
+
return this.request(path57, { method: "GET" }, opts);
|
|
8084
8083
|
}
|
|
8085
|
-
post(
|
|
8086
|
-
return this.request(
|
|
8084
|
+
post(path57, body, opts) {
|
|
8085
|
+
return this.request(path57, {
|
|
8087
8086
|
method: "POST",
|
|
8088
8087
|
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
8089
8088
|
}, opts);
|
|
8090
8089
|
}
|
|
8091
|
-
patch(
|
|
8092
|
-
return this.request(
|
|
8090
|
+
patch(path57, body, opts) {
|
|
8091
|
+
return this.request(path57, {
|
|
8093
8092
|
method: "PATCH",
|
|
8094
8093
|
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
8095
8094
|
}, opts);
|
|
8096
8095
|
}
|
|
8097
|
-
delete(
|
|
8098
|
-
return this.request(
|
|
8096
|
+
delete(path57, opts) {
|
|
8097
|
+
return this.request(path57, { method: "DELETE" }, opts);
|
|
8099
8098
|
}
|
|
8100
|
-
async request(
|
|
8101
|
-
const url = buildUrl(this.apiBase,
|
|
8099
|
+
async request(path57, init, opts) {
|
|
8100
|
+
const url = buildUrl(this.apiBase, path57);
|
|
8102
8101
|
const headers = {
|
|
8103
8102
|
accept: "application/json",
|
|
8104
8103
|
...toStringRecord(init.headers)
|
|
@@ -9623,9 +9622,9 @@ async function fetchHostedIntegrations(session) {
|
|
|
9623
9622
|
}
|
|
9624
9623
|
async function fetchHostedIntegrationCredential(session, providerId) {
|
|
9625
9624
|
const client = toApiClient2(session);
|
|
9626
|
-
const
|
|
9625
|
+
const path57 = `${DEFAULT_INTEGRATION_CREDENTIAL_PATH}&provider=${encodeURIComponent(providerId)}`;
|
|
9627
9626
|
try {
|
|
9628
|
-
return await client.get(
|
|
9627
|
+
return await client.get(path57, { ignoreNotFound: true });
|
|
9629
9628
|
} catch (err) {
|
|
9630
9629
|
if (err instanceof ApiRequestError && (err.status === 404 || err.status === 501)) {
|
|
9631
9630
|
throw new HostedEndpointUnavailableError(err.status, err.message);
|
|
@@ -10200,12 +10199,12 @@ var init_github = __esm({
|
|
|
10200
10199
|
});
|
|
10201
10200
|
|
|
10202
10201
|
// src/starter/init.ts
|
|
10203
|
-
import
|
|
10204
|
-
import
|
|
10202
|
+
import fs38 from "node:fs";
|
|
10203
|
+
import path46 from "node:path";
|
|
10205
10204
|
async function initStarterWorkspace(opts) {
|
|
10206
10205
|
const kitId = opts.kitId ?? DEFAULT_STARTER_KIT_ID;
|
|
10207
|
-
const absOut =
|
|
10208
|
-
if (
|
|
10206
|
+
const absOut = path46.resolve(opts.out);
|
|
10207
|
+
if (fs38.existsSync(absOut) && fs38.readdirSync(absOut).length > 0) {
|
|
10209
10208
|
throw new Error(`Destination ${absOut} already exists and is not empty.`);
|
|
10210
10209
|
}
|
|
10211
10210
|
const info = getBundledKitSourceInfo(kitId);
|
|
@@ -10214,7 +10213,7 @@ async function initStarterWorkspace(opts) {
|
|
|
10214
10213
|
forkPath: absOut,
|
|
10215
10214
|
kitId: info.id,
|
|
10216
10215
|
baseVersion: info.version,
|
|
10217
|
-
label: opts.name?.trim() ||
|
|
10216
|
+
label: opts.name?.trim() || path46.basename(absOut)
|
|
10218
10217
|
});
|
|
10219
10218
|
const policy = {
|
|
10220
10219
|
...makeDefaultKitForkPolicy(),
|
|
@@ -10307,8 +10306,8 @@ var init_types2 = __esm({
|
|
|
10307
10306
|
});
|
|
10308
10307
|
|
|
10309
10308
|
// src/starter/source-import/github-source.ts
|
|
10310
|
-
import
|
|
10311
|
-
import
|
|
10309
|
+
import fs39 from "node:fs";
|
|
10310
|
+
import path47 from "node:path";
|
|
10312
10311
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
10313
10312
|
function baseHeaders() {
|
|
10314
10313
|
return {
|
|
@@ -10439,12 +10438,12 @@ function cloneGithubRepo(input) {
|
|
|
10439
10438
|
if (!gitAvailable()) {
|
|
10440
10439
|
throw new Error("`git` is not available on PATH \u2014 cannot clone.");
|
|
10441
10440
|
}
|
|
10442
|
-
if (
|
|
10441
|
+
if (fs39.existsSync(input.destination)) {
|
|
10443
10442
|
throw new Error(`Clone destination already exists: ${input.destination}`);
|
|
10444
10443
|
}
|
|
10445
10444
|
const cloneUrl = input.token ? buildTokenCloneUrl(input.probe.repo, input.token) : input.probe.cloneUrl;
|
|
10446
|
-
const parent =
|
|
10447
|
-
|
|
10445
|
+
const parent = path47.dirname(input.destination);
|
|
10446
|
+
fs39.mkdirSync(parent, { recursive: true });
|
|
10448
10447
|
const depth = input.depth ?? 1;
|
|
10449
10448
|
const branch = input.branch ?? input.probe.defaultBranch;
|
|
10450
10449
|
const args = ["clone"];
|
|
@@ -10471,17 +10470,17 @@ function cloneGithubRepo(input) {
|
|
|
10471
10470
|
function narrowToSubdirectory(rootDir, subdirectory) {
|
|
10472
10471
|
const normalizedSub = subdirectory.replace(/^\/+|\/+$/g, "");
|
|
10473
10472
|
if (!normalizedSub) return;
|
|
10474
|
-
const abs =
|
|
10475
|
-
if (!
|
|
10473
|
+
const abs = path47.resolve(rootDir, normalizedSub);
|
|
10474
|
+
if (!fs39.existsSync(abs) || !fs39.statSync(abs).isDirectory()) {
|
|
10476
10475
|
throw new Error(`Subdirectory not found in cloned repo: ${subdirectory}`);
|
|
10477
10476
|
}
|
|
10478
|
-
const tmp =
|
|
10479
|
-
|
|
10480
|
-
`.${
|
|
10477
|
+
const tmp = path47.resolve(
|
|
10478
|
+
path47.dirname(rootDir),
|
|
10479
|
+
`.${path47.basename(rootDir)}-narrow-${Date.now().toString(36)}`
|
|
10481
10480
|
);
|
|
10482
|
-
|
|
10483
|
-
|
|
10484
|
-
|
|
10481
|
+
fs39.renameSync(abs, tmp);
|
|
10482
|
+
fs39.rmSync(rootDir, { recursive: true, force: true });
|
|
10483
|
+
fs39.renameSync(tmp, rootDir);
|
|
10485
10484
|
}
|
|
10486
10485
|
var GITHUB_API_BASE2;
|
|
10487
10486
|
var init_github_source = __esm({
|
|
@@ -10495,9 +10494,9 @@ var init_github_source = __esm({
|
|
|
10495
10494
|
});
|
|
10496
10495
|
|
|
10497
10496
|
// src/starter/source-import/skills-source.ts
|
|
10498
|
-
import
|
|
10499
|
-
import
|
|
10500
|
-
import
|
|
10497
|
+
import fs40 from "node:fs";
|
|
10498
|
+
import os10 from "node:os";
|
|
10499
|
+
import path48 from "node:path";
|
|
10501
10500
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
10502
10501
|
function resolveBase() {
|
|
10503
10502
|
const raw = process.env.SKILLS_SH_BASE?.trim();
|
|
@@ -10763,9 +10762,9 @@ async function probeSkillsSource(input) {
|
|
|
10763
10762
|
};
|
|
10764
10763
|
}
|
|
10765
10764
|
function assertInsidePayloadRoot(root, candidate) {
|
|
10766
|
-
const abs =
|
|
10767
|
-
const rootAbs =
|
|
10768
|
-
if (!abs.startsWith(rootAbs +
|
|
10765
|
+
const abs = path48.resolve(candidate);
|
|
10766
|
+
const rootAbs = path48.resolve(root);
|
|
10767
|
+
if (!abs.startsWith(rootAbs + path48.sep) && abs !== rootAbs) {
|
|
10769
10768
|
throw new Error(`Refusing to write outside payload root: ${candidate}`);
|
|
10770
10769
|
}
|
|
10771
10770
|
}
|
|
@@ -10781,24 +10780,24 @@ function runGit3(args, cwd) {
|
|
|
10781
10780
|
};
|
|
10782
10781
|
}
|
|
10783
10782
|
function skillDirectoryMatches(dir, skillSlug) {
|
|
10784
|
-
const skillFile =
|
|
10785
|
-
if (!
|
|
10783
|
+
const skillFile = path48.resolve(dir, "SKILL.md");
|
|
10784
|
+
if (!fs40.existsSync(skillFile) || !fs40.statSync(skillFile).isFile()) {
|
|
10786
10785
|
return false;
|
|
10787
10786
|
}
|
|
10788
|
-
if (
|
|
10787
|
+
if (path48.basename(dir) === skillSlug) {
|
|
10789
10788
|
return true;
|
|
10790
10789
|
}
|
|
10791
|
-
const content =
|
|
10790
|
+
const content = fs40.readFileSync(skillFile, "utf8");
|
|
10792
10791
|
const nameMatch = content.match(/(?:^|\n)name:\s*["']?([A-Za-z0-9._:-]+)["']?\s*(?:\n|$)/i);
|
|
10793
10792
|
return nameMatch?.[1] === skillSlug;
|
|
10794
10793
|
}
|
|
10795
10794
|
function locateSkillDirectory(root, skillSlug) {
|
|
10796
10795
|
const preferred = [
|
|
10797
|
-
|
|
10798
|
-
|
|
10796
|
+
path48.resolve(root, "skills", skillSlug),
|
|
10797
|
+
path48.resolve(root, skillSlug)
|
|
10799
10798
|
];
|
|
10800
10799
|
for (const candidate of preferred) {
|
|
10801
|
-
if (
|
|
10800
|
+
if (fs40.existsSync(candidate) && fs40.statSync(candidate).isDirectory() && skillDirectoryMatches(candidate, skillSlug)) {
|
|
10802
10801
|
return candidate;
|
|
10803
10802
|
}
|
|
10804
10803
|
}
|
|
@@ -10808,12 +10807,12 @@ function locateSkillDirectory(root, skillSlug) {
|
|
|
10808
10807
|
if (skillDirectoryMatches(current, skillSlug)) {
|
|
10809
10808
|
return current;
|
|
10810
10809
|
}
|
|
10811
|
-
for (const entry of
|
|
10810
|
+
for (const entry of fs40.readdirSync(current, { withFileTypes: true })) {
|
|
10812
10811
|
if (!entry.isDirectory()) continue;
|
|
10813
10812
|
if ([".git", "node_modules", ".next", "dist", "build", "coverage"].includes(entry.name)) {
|
|
10814
10813
|
continue;
|
|
10815
10814
|
}
|
|
10816
|
-
queue.push(
|
|
10815
|
+
queue.push(path48.resolve(current, entry.name));
|
|
10817
10816
|
}
|
|
10818
10817
|
}
|
|
10819
10818
|
return null;
|
|
@@ -10823,18 +10822,18 @@ function copySkillTree(sourceDir, destination) {
|
|
|
10823
10822
|
const stack = [{ from: sourceDir, to: destination }];
|
|
10824
10823
|
while (stack.length > 0) {
|
|
10825
10824
|
const current = stack.pop();
|
|
10826
|
-
|
|
10827
|
-
for (const entry of
|
|
10828
|
-
const fromPath =
|
|
10829
|
-
const toPath =
|
|
10825
|
+
fs40.mkdirSync(current.to, { recursive: true });
|
|
10826
|
+
for (const entry of fs40.readdirSync(current.from, { withFileTypes: true })) {
|
|
10827
|
+
const fromPath = path48.resolve(current.from, entry.name);
|
|
10828
|
+
const toPath = path48.resolve(current.to, entry.name);
|
|
10830
10829
|
assertInsidePayloadRoot(destination, toPath);
|
|
10831
10830
|
if (entry.isDirectory()) {
|
|
10832
10831
|
stack.push({ from: fromPath, to: toPath });
|
|
10833
10832
|
continue;
|
|
10834
10833
|
}
|
|
10835
|
-
const data =
|
|
10836
|
-
|
|
10837
|
-
|
|
10834
|
+
const data = fs40.readFileSync(fromPath);
|
|
10835
|
+
fs40.mkdirSync(path48.dirname(toPath), { recursive: true });
|
|
10836
|
+
fs40.writeFileSync(toPath, data, { mode: 420 });
|
|
10838
10837
|
written += 1;
|
|
10839
10838
|
}
|
|
10840
10839
|
}
|
|
@@ -10845,7 +10844,7 @@ async function fetchSkillPayload(input) {
|
|
|
10845
10844
|
if (!gitAvailable()) {
|
|
10846
10845
|
throw new Error("`git` is not available on PATH \u2014 cannot materialize a skills.sh payload.");
|
|
10847
10846
|
}
|
|
10848
|
-
if (
|
|
10847
|
+
if (fs40.existsSync(destination)) {
|
|
10849
10848
|
throw new Error(`Skill payload destination already exists: ${destination}`);
|
|
10850
10849
|
}
|
|
10851
10850
|
const repoSource = probe.repoUrl ?? (probe.repository ? `https://github.com/${probe.repository}` : void 0);
|
|
@@ -10853,11 +10852,11 @@ async function fetchSkillPayload(input) {
|
|
|
10853
10852
|
if (!repoSource || !skillSlug) {
|
|
10854
10853
|
throw new Error(`Skill '${probe.skillId}' is missing repository metadata \u2014 cannot materialize payload.`);
|
|
10855
10854
|
}
|
|
10856
|
-
const cloneRoot =
|
|
10857
|
-
|
|
10855
|
+
const cloneRoot = fs40.mkdtempSync(
|
|
10856
|
+
path48.join(os10.tmpdir(), "growthub-skills-source-")
|
|
10858
10857
|
);
|
|
10859
10858
|
try {
|
|
10860
|
-
const cloneRes = runGit3(["clone", "--depth", "1", repoSource, cloneRoot],
|
|
10859
|
+
const cloneRes = runGit3(["clone", "--depth", "1", repoSource, cloneRoot], path48.dirname(cloneRoot));
|
|
10861
10860
|
if (!cloneRes.ok) {
|
|
10862
10861
|
throw new Error(`git clone failed: ${cloneRes.stderr || "unable to clone skill repository"}`);
|
|
10863
10862
|
}
|
|
@@ -10870,7 +10869,7 @@ async function fetchSkillPayload(input) {
|
|
|
10870
10869
|
const fileCount = copySkillTree(skillDir, destination);
|
|
10871
10870
|
return { destination, fileCount };
|
|
10872
10871
|
} finally {
|
|
10873
|
-
|
|
10872
|
+
fs40.rmSync(cloneRoot, { recursive: true, force: true });
|
|
10874
10873
|
}
|
|
10875
10874
|
}
|
|
10876
10875
|
var DEFAULT_BASE, COMMENT_PATTERN;
|
|
@@ -10884,13 +10883,13 @@ var init_skills_source = __esm({
|
|
|
10884
10883
|
});
|
|
10885
10884
|
|
|
10886
10885
|
// src/starter/source-import/detect.ts
|
|
10887
|
-
import
|
|
10888
|
-
import
|
|
10886
|
+
import fs41 from "node:fs";
|
|
10887
|
+
import path49 from "node:path";
|
|
10889
10888
|
function safeReadPackageJson(dir) {
|
|
10890
|
-
const p33 =
|
|
10891
|
-
if (!
|
|
10889
|
+
const p33 = path49.resolve(dir, "package.json");
|
|
10890
|
+
if (!fs41.existsSync(p33)) return null;
|
|
10892
10891
|
try {
|
|
10893
|
-
return JSON.parse(
|
|
10892
|
+
return JSON.parse(fs41.readFileSync(p33, "utf8"));
|
|
10894
10893
|
} catch {
|
|
10895
10894
|
return null;
|
|
10896
10895
|
}
|
|
@@ -10900,10 +10899,10 @@ function detectPackageManager(dir, pkg) {
|
|
|
10900
10899
|
if (pkg?.packageManager?.startsWith("yarn")) return "yarn";
|
|
10901
10900
|
if (pkg?.packageManager?.startsWith("npm")) return "npm";
|
|
10902
10901
|
if (pkg?.packageManager?.startsWith("bun")) return "bun";
|
|
10903
|
-
if (
|
|
10904
|
-
if (
|
|
10905
|
-
if (
|
|
10906
|
-
if (
|
|
10902
|
+
if (fs41.existsSync(path49.resolve(dir, "pnpm-lock.yaml"))) return "pnpm";
|
|
10903
|
+
if (fs41.existsSync(path49.resolve(dir, "yarn.lock"))) return "yarn";
|
|
10904
|
+
if (fs41.existsSync(path49.resolve(dir, "bun.lockb"))) return "bun";
|
|
10905
|
+
if (fs41.existsSync(path49.resolve(dir, "package-lock.json"))) return "npm";
|
|
10907
10906
|
return "unknown";
|
|
10908
10907
|
}
|
|
10909
10908
|
function collectDeps(pkg) {
|
|
@@ -10917,12 +10916,12 @@ function collectDeps(pkg) {
|
|
|
10917
10916
|
}
|
|
10918
10917
|
function looksLikeSkillPayload(rootDir) {
|
|
10919
10918
|
const markers = ["SKILL.md", "skill.md", "skill.json", "skill.yml", "skill.yaml", "prompt.md"];
|
|
10920
|
-
return markers.some((name) =>
|
|
10919
|
+
return markers.some((name) => fs41.existsSync(path49.resolve(rootDir, name)));
|
|
10921
10920
|
}
|
|
10922
10921
|
function detectFramework(rootDir, pkg) {
|
|
10923
10922
|
if (!pkg) {
|
|
10924
10923
|
if (looksLikeSkillPayload(rootDir)) return "skill";
|
|
10925
|
-
if (
|
|
10924
|
+
if (fs41.existsSync(path49.resolve(rootDir, "docs"))) return "docs";
|
|
10926
10925
|
return "unknown";
|
|
10927
10926
|
}
|
|
10928
10927
|
const deps = collectDeps(pkg);
|
|
@@ -10931,8 +10930,8 @@ function detectFramework(rootDir, pkg) {
|
|
|
10931
10930
|
"vite.config.ts",
|
|
10932
10931
|
"vite.config.mjs",
|
|
10933
10932
|
"vite.config.cjs"
|
|
10934
|
-
].some((name) =>
|
|
10935
|
-
if (deps.has("next") ||
|
|
10933
|
+
].some((name) => fs41.existsSync(path49.resolve(rootDir, name)));
|
|
10934
|
+
if (deps.has("next") || fs41.existsSync(path49.resolve(rootDir, "next.config.js")) || fs41.existsSync(path49.resolve(rootDir, "next.config.mjs"))) {
|
|
10936
10935
|
return "next";
|
|
10937
10936
|
}
|
|
10938
10937
|
if (deps.has("vite") || hasViteConfig) return "vite";
|
|
@@ -10958,15 +10957,15 @@ function pickScripts(pkg) {
|
|
|
10958
10957
|
return out;
|
|
10959
10958
|
}
|
|
10960
10959
|
function listEnvFiles(dir) {
|
|
10961
|
-
if (!
|
|
10962
|
-
return
|
|
10960
|
+
if (!fs41.existsSync(dir)) return [];
|
|
10961
|
+
return fs41.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name).filter((name) => name === ".env" || name.startsWith(".env.") || name === ".env.example");
|
|
10963
10962
|
}
|
|
10964
10963
|
function findAppRoot(rootDir, pkg) {
|
|
10965
10964
|
if (pkg) return ".";
|
|
10966
10965
|
const candidates = ["app", "src", "apps", "packages"];
|
|
10967
10966
|
for (const candidate of candidates) {
|
|
10968
|
-
const abs =
|
|
10969
|
-
if (
|
|
10967
|
+
const abs = path49.resolve(rootDir, candidate);
|
|
10968
|
+
if (fs41.existsSync(abs) && fs41.statSync(abs).isDirectory()) {
|
|
10970
10969
|
const child = safeReadPackageJson(abs);
|
|
10971
10970
|
if (child) return candidate;
|
|
10972
10971
|
}
|
|
@@ -10982,12 +10981,12 @@ function computeConfidence(framework, manager, pkg) {
|
|
|
10982
10981
|
return Math.min(1, Number(score.toFixed(2)));
|
|
10983
10982
|
}
|
|
10984
10983
|
function detectSourceShape(rootDir) {
|
|
10985
|
-
if (!
|
|
10984
|
+
if (!fs41.existsSync(rootDir) || !fs41.statSync(rootDir).isDirectory()) {
|
|
10986
10985
|
throw new Error(`Detection target is not a directory: ${rootDir}`);
|
|
10987
10986
|
}
|
|
10988
10987
|
const rootPkg = safeReadPackageJson(rootDir);
|
|
10989
10988
|
const appRootRel = findAppRoot(rootDir, rootPkg);
|
|
10990
|
-
const appRootAbs =
|
|
10989
|
+
const appRootAbs = path49.resolve(rootDir, appRootRel);
|
|
10991
10990
|
const appPkg = appRootRel === "." ? rootPkg : safeReadPackageJson(appRootAbs);
|
|
10992
10991
|
const framework = detectFramework(appRootAbs, appPkg ?? rootPkg);
|
|
10993
10992
|
const packageManager = detectPackageManager(rootDir, rootPkg ?? appPkg);
|
|
@@ -11027,18 +11026,18 @@ var init_detect = __esm({
|
|
|
11027
11026
|
});
|
|
11028
11027
|
|
|
11029
11028
|
// src/starter/source-import/security.ts
|
|
11030
|
-
import
|
|
11031
|
-
import
|
|
11029
|
+
import fs42 from "node:fs";
|
|
11030
|
+
import path50 from "node:path";
|
|
11032
11031
|
function isLikelyTextFile(filename) {
|
|
11033
|
-
const ext =
|
|
11032
|
+
const ext = path50.extname(filename).toLowerCase();
|
|
11034
11033
|
if (!ext) return true;
|
|
11035
11034
|
return TEXT_EXTENSIONS.has(ext);
|
|
11036
11035
|
}
|
|
11037
11036
|
function isSuspiciousBinary(filename) {
|
|
11038
|
-
return SUSPICIOUS_BINARY_EXTENSIONS.has(
|
|
11037
|
+
return SUSPICIOUS_BINARY_EXTENSIONS.has(path50.extname(filename).toLowerCase());
|
|
11039
11038
|
}
|
|
11040
11039
|
function isUnexpectedArchive(filename) {
|
|
11041
|
-
const ext =
|
|
11040
|
+
const ext = path50.extname(filename).toLowerCase();
|
|
11042
11041
|
return ARCHIVE_EXTENSIONS.has(ext) || filename.toLowerCase().endsWith(".tar.gz");
|
|
11043
11042
|
}
|
|
11044
11043
|
function shortExcerpt(line) {
|
|
@@ -11093,19 +11092,19 @@ function walkPayload(root, onFile, limits) {
|
|
|
11093
11092
|
if (!current) break;
|
|
11094
11093
|
let entries;
|
|
11095
11094
|
try {
|
|
11096
|
-
entries =
|
|
11095
|
+
entries = fs42.readdirSync(current, { withFileTypes: true });
|
|
11097
11096
|
} catch {
|
|
11098
11097
|
continue;
|
|
11099
11098
|
}
|
|
11100
11099
|
for (const entry of entries) {
|
|
11101
|
-
const abs =
|
|
11100
|
+
const abs = path50.resolve(current, entry.name);
|
|
11102
11101
|
if (entry.isDirectory()) {
|
|
11103
11102
|
if (entry.name === ".git" || entry.name === "node_modules") continue;
|
|
11104
11103
|
stack.push(abs);
|
|
11105
11104
|
continue;
|
|
11106
11105
|
}
|
|
11107
11106
|
if (!entry.isFile()) continue;
|
|
11108
|
-
const rel =
|
|
11107
|
+
const rel = path50.relative(root, abs);
|
|
11109
11108
|
onFile(abs, rel);
|
|
11110
11109
|
visited += 1;
|
|
11111
11110
|
if (visited >= limits.maxFiles) break;
|
|
@@ -11115,7 +11114,7 @@ function walkPayload(root, onFile, limits) {
|
|
|
11115
11114
|
}
|
|
11116
11115
|
function inspectSourcePayload(input) {
|
|
11117
11116
|
const { payloadRoot } = input;
|
|
11118
|
-
if (!
|
|
11117
|
+
if (!fs42.existsSync(payloadRoot) || !fs42.statSync(payloadRoot).isDirectory()) {
|
|
11119
11118
|
throw new Error(`Inspection target is not a directory: ${payloadRoot}`);
|
|
11120
11119
|
}
|
|
11121
11120
|
const findings = [];
|
|
@@ -11125,7 +11124,7 @@ function inspectSourcePayload(input) {
|
|
|
11125
11124
|
(abs, rel) => {
|
|
11126
11125
|
let size = 0;
|
|
11127
11126
|
try {
|
|
11128
|
-
size =
|
|
11127
|
+
size = fs42.statSync(abs).size;
|
|
11129
11128
|
} catch {
|
|
11130
11129
|
return;
|
|
11131
11130
|
}
|
|
@@ -11134,7 +11133,7 @@ function inspectSourcePayload(input) {
|
|
|
11134
11133
|
category: "suspicious-binary",
|
|
11135
11134
|
severity: "high-risk",
|
|
11136
11135
|
path: rel,
|
|
11137
|
-
message: `Payload ships a precompiled binary (${
|
|
11136
|
+
message: `Payload ships a precompiled binary (${path50.extname(rel)}). Review provenance before use.`
|
|
11138
11137
|
});
|
|
11139
11138
|
return;
|
|
11140
11139
|
}
|
|
@@ -11143,7 +11142,7 @@ function inspectSourcePayload(input) {
|
|
|
11143
11142
|
category: "unexpected-archive",
|
|
11144
11143
|
severity: "caution",
|
|
11145
11144
|
path: rel,
|
|
11146
|
-
message: `Payload ships an archive (${
|
|
11145
|
+
message: `Payload ships an archive (${path50.extname(rel)}) \u2014 expand and review contents before use.`
|
|
11147
11146
|
});
|
|
11148
11147
|
return;
|
|
11149
11148
|
}
|
|
@@ -11151,12 +11150,12 @@ function inspectSourcePayload(input) {
|
|
|
11151
11150
|
if (bytesInspected + Math.min(size, MAX_BYTES_PER_FILE) > MAX_TOTAL_BYTES) return;
|
|
11152
11151
|
let buf;
|
|
11153
11152
|
try {
|
|
11154
|
-
const handle =
|
|
11153
|
+
const handle = fs42.openSync(abs, "r");
|
|
11155
11154
|
try {
|
|
11156
11155
|
buf = Buffer.alloc(Math.min(size, MAX_BYTES_PER_FILE));
|
|
11157
|
-
|
|
11156
|
+
fs42.readSync(handle, buf, 0, buf.length, 0);
|
|
11158
11157
|
} finally {
|
|
11159
|
-
|
|
11158
|
+
fs42.closeSync(handle);
|
|
11160
11159
|
}
|
|
11161
11160
|
} catch {
|
|
11162
11161
|
return;
|
|
@@ -11365,18 +11364,18 @@ var init_security = __esm({
|
|
|
11365
11364
|
});
|
|
11366
11365
|
|
|
11367
11366
|
// src/starter/source-import/plan.ts
|
|
11368
|
-
import
|
|
11369
|
-
import
|
|
11367
|
+
import fs43 from "node:fs";
|
|
11368
|
+
import path51 from "node:path";
|
|
11370
11369
|
function generateImportId() {
|
|
11371
11370
|
return `si-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`;
|
|
11372
11371
|
}
|
|
11373
11372
|
function destinationState(absDest) {
|
|
11374
|
-
if (!
|
|
11375
|
-
const stats =
|
|
11373
|
+
if (!fs43.existsSync(absDest)) return { exists: false, nonEmpty: false };
|
|
11374
|
+
const stats = fs43.statSync(absDest);
|
|
11376
11375
|
if (!stats.isDirectory()) {
|
|
11377
11376
|
throw new Error(`Destination is not a directory: ${absDest}`);
|
|
11378
11377
|
}
|
|
11379
|
-
const entries =
|
|
11378
|
+
const entries = fs43.readdirSync(absDest);
|
|
11380
11379
|
return { exists: true, nonEmpty: entries.length > 0 };
|
|
11381
11380
|
}
|
|
11382
11381
|
function describeSource(probe) {
|
|
@@ -11386,7 +11385,7 @@ function describeSource(probe) {
|
|
|
11386
11385
|
return `skill ${probe.skillId}@${probe.version} (skills.sh)`;
|
|
11387
11386
|
}
|
|
11388
11387
|
function buildSourceImportPlan(input) {
|
|
11389
|
-
const absDest =
|
|
11388
|
+
const absDest = path51.resolve(input.destination);
|
|
11390
11389
|
const state = destinationState(absDest);
|
|
11391
11390
|
const payloadPath = "imported";
|
|
11392
11391
|
const warnings = [...input.probe.warnings];
|
|
@@ -11499,8 +11498,8 @@ var init_plan = __esm({
|
|
|
11499
11498
|
});
|
|
11500
11499
|
|
|
11501
11500
|
// src/starter/source-import/summarize.ts
|
|
11502
|
-
import
|
|
11503
|
-
import
|
|
11501
|
+
import fs44 from "node:fs";
|
|
11502
|
+
import path52 from "node:path";
|
|
11504
11503
|
function sourceHeading(manifest) {
|
|
11505
11504
|
const src = manifest.source;
|
|
11506
11505
|
if (src.kind === "github-repo") {
|
|
@@ -11555,7 +11554,7 @@ function nextStepsSection(manifest) {
|
|
|
11555
11554
|
}
|
|
11556
11555
|
function writeImportSummary(input) {
|
|
11557
11556
|
const { forkPath, summaryRelativePath, manifest } = input;
|
|
11558
|
-
const summaryPath =
|
|
11557
|
+
const summaryPath = path52.resolve(forkPath, summaryRelativePath);
|
|
11559
11558
|
const body = [
|
|
11560
11559
|
`# Source Import Summary`,
|
|
11561
11560
|
``,
|
|
@@ -11585,8 +11584,8 @@ function writeImportSummary(input) {
|
|
|
11585
11584
|
`Generated by the Growthub Source Import Agent. Canonical manifest lives at \`.growthub-fork/source-import.json\`.`,
|
|
11586
11585
|
``
|
|
11587
11586
|
].join("\n");
|
|
11588
|
-
|
|
11589
|
-
|
|
11587
|
+
fs44.mkdirSync(path52.dirname(summaryPath), { recursive: true });
|
|
11588
|
+
fs44.writeFileSync(summaryPath, body, "utf8");
|
|
11590
11589
|
return summaryPath;
|
|
11591
11590
|
}
|
|
11592
11591
|
var init_summarize = __esm({
|
|
@@ -11596,16 +11595,16 @@ var init_summarize = __esm({
|
|
|
11596
11595
|
});
|
|
11597
11596
|
|
|
11598
11597
|
// src/starter/source-import/materialize.ts
|
|
11599
|
-
import
|
|
11600
|
-
import
|
|
11601
|
-
import
|
|
11598
|
+
import fs45 from "node:fs";
|
|
11599
|
+
import os11 from "node:os";
|
|
11600
|
+
import path53 from "node:path";
|
|
11602
11601
|
function resolveSourceKind(probe) {
|
|
11603
11602
|
return probe.kind === "github-repo" ? "github-repo" : "skills-skill";
|
|
11604
11603
|
}
|
|
11605
11604
|
function stagingDirFor(forkPath) {
|
|
11606
|
-
return
|
|
11607
|
-
|
|
11608
|
-
`growthub-source-import-${
|
|
11605
|
+
return path53.join(
|
|
11606
|
+
os11.tmpdir(),
|
|
11607
|
+
`growthub-source-import-${path53.basename(forkPath)}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`
|
|
11609
11608
|
);
|
|
11610
11609
|
}
|
|
11611
11610
|
async function fetchPayload(probe, stagingDir, opts) {
|
|
@@ -11637,18 +11636,18 @@ async function fetchPayload(probe, stagingDir, opts) {
|
|
|
11637
11636
|
return { payloadRoot: stagingDir };
|
|
11638
11637
|
}
|
|
11639
11638
|
function movePayloadIntoFork(payloadRoot, forkPath, payloadRelativePath) {
|
|
11640
|
-
const target =
|
|
11641
|
-
if (
|
|
11642
|
-
|
|
11639
|
+
const target = path53.resolve(forkPath, payloadRelativePath);
|
|
11640
|
+
if (fs45.existsSync(target)) {
|
|
11641
|
+
fs45.rmSync(target, { recursive: true, force: true });
|
|
11643
11642
|
}
|
|
11644
|
-
|
|
11645
|
-
|
|
11643
|
+
fs45.mkdirSync(path53.dirname(target), { recursive: true });
|
|
11644
|
+
fs45.renameSync(payloadRoot, target);
|
|
11646
11645
|
return target;
|
|
11647
11646
|
}
|
|
11648
11647
|
function writeManifest(forkPath, manifest) {
|
|
11649
|
-
const p33 =
|
|
11650
|
-
|
|
11651
|
-
|
|
11648
|
+
const p33 = path53.resolve(forkPath, MANIFEST_RELATIVE_PATH);
|
|
11649
|
+
fs45.mkdirSync(path53.dirname(p33), { recursive: true });
|
|
11650
|
+
fs45.writeFileSync(p33, JSON.stringify(manifest, null, 2) + "\n", "utf8");
|
|
11652
11651
|
return p33;
|
|
11653
11652
|
}
|
|
11654
11653
|
function assertConfirmationsSatisfied(plan, confirmations) {
|
|
@@ -11688,7 +11687,7 @@ async function materializeImportPlan(input) {
|
|
|
11688
11687
|
requireSkillAcknowledgement: sourceKind === "skills-skill"
|
|
11689
11688
|
});
|
|
11690
11689
|
if (security.blocked) {
|
|
11691
|
-
|
|
11690
|
+
fs45.rmSync(fetchResult.payloadRoot, { recursive: true, force: true });
|
|
11692
11691
|
throw new Error(
|
|
11693
11692
|
`Security inspection blocked the fetched payload: ${security.summaryLines[0] ?? "blocking finding"}`
|
|
11694
11693
|
);
|
|
@@ -11819,26 +11818,26 @@ var init_materialize = __esm({
|
|
|
11819
11818
|
});
|
|
11820
11819
|
|
|
11821
11820
|
// src/starter/source-import/agent.ts
|
|
11822
|
-
import
|
|
11823
|
-
import
|
|
11821
|
+
import fs46 from "node:fs";
|
|
11822
|
+
import path54 from "node:path";
|
|
11824
11823
|
function resolveJobsDir() {
|
|
11825
|
-
return
|
|
11824
|
+
return path54.resolve(resolveKitForksHomeDir(), "source-import-jobs");
|
|
11826
11825
|
}
|
|
11827
11826
|
function resolveJobPath2(jobId) {
|
|
11828
|
-
return
|
|
11827
|
+
return path54.resolve(resolveJobsDir(), `${jobId}.json`);
|
|
11829
11828
|
}
|
|
11830
11829
|
function generateJobId2() {
|
|
11831
11830
|
return `sij-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`;
|
|
11832
11831
|
}
|
|
11833
11832
|
function writeJob2(job) {
|
|
11834
11833
|
const p33 = resolveJobPath2(job.jobId);
|
|
11835
|
-
|
|
11836
|
-
|
|
11834
|
+
fs46.mkdirSync(path54.dirname(p33), { recursive: true });
|
|
11835
|
+
fs46.writeFileSync(p33, JSON.stringify(job, null, 2) + "\n", "utf8");
|
|
11837
11836
|
}
|
|
11838
11837
|
function readJobFile(p33) {
|
|
11839
|
-
if (!
|
|
11838
|
+
if (!fs46.existsSync(p33)) return null;
|
|
11840
11839
|
try {
|
|
11841
|
-
return JSON.parse(
|
|
11840
|
+
return JSON.parse(fs46.readFileSync(p33, "utf8"));
|
|
11842
11841
|
} catch {
|
|
11843
11842
|
return null;
|
|
11844
11843
|
}
|
|
@@ -11848,7 +11847,7 @@ function patchJob2(jobId, status, patch = {}) {
|
|
|
11848
11847
|
const job = readJobFile(p33);
|
|
11849
11848
|
if (!job) return null;
|
|
11850
11849
|
const updated = { ...job, ...patch, status };
|
|
11851
|
-
|
|
11850
|
+
fs46.writeFileSync(p33, JSON.stringify(updated, null, 2) + "\n", "utf8");
|
|
11852
11851
|
return updated;
|
|
11853
11852
|
}
|
|
11854
11853
|
function getSourceImportJob(jobId) {
|
|
@@ -11867,7 +11866,7 @@ async function probeAndPlan(input, destination) {
|
|
|
11867
11866
|
}
|
|
11868
11867
|
async function runSourceImportJob(input) {
|
|
11869
11868
|
const jobId = generateJobId2();
|
|
11870
|
-
const destination =
|
|
11869
|
+
const destination = path54.resolve(input.out);
|
|
11871
11870
|
const sourceKind = input.source.kind;
|
|
11872
11871
|
const initial = {
|
|
11873
11872
|
jobId,
|
|
@@ -12024,8 +12023,8 @@ __export(source_import_discovery_exports, {
|
|
|
12024
12023
|
});
|
|
12025
12024
|
import * as p31 from "@clack/prompts";
|
|
12026
12025
|
import pc45 from "picocolors";
|
|
12027
|
-
import
|
|
12028
|
-
import
|
|
12026
|
+
import fs48 from "node:fs";
|
|
12027
|
+
import path55 from "node:path";
|
|
12029
12028
|
import { pathToFileURL as pathToFileURL4 } from "node:url";
|
|
12030
12029
|
function slugifyWorkspaceName(input) {
|
|
12031
12030
|
const slug = input.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -12053,10 +12052,10 @@ async function promptForInteractiveWorkspacePath(input) {
|
|
|
12053
12052
|
});
|
|
12054
12053
|
if (p31.isCancel(raw) || !raw) return null;
|
|
12055
12054
|
const trimmed = String(raw).trim();
|
|
12056
|
-
const expanded = trimmed.startsWith("~/") ?
|
|
12057
|
-
const resolved =
|
|
12058
|
-
if (
|
|
12059
|
-
const finalPath =
|
|
12055
|
+
const expanded = trimmed.startsWith("~/") ? path55.join(process.env.HOME ?? "~", trimmed.slice(2)) : trimmed;
|
|
12056
|
+
const resolved = path55.resolve(expanded);
|
|
12057
|
+
if (fs48.existsSync(resolved) && fs48.statSync(resolved).isDirectory()) {
|
|
12058
|
+
const finalPath = path55.join(resolved, suggestedName);
|
|
12060
12059
|
p31.note(
|
|
12061
12060
|
[
|
|
12062
12061
|
`You selected an existing folder: ${resolved}`,
|
|
@@ -12329,8 +12328,8 @@ init_doctor();
|
|
|
12329
12328
|
import { Command } from "commander";
|
|
12330
12329
|
import * as p32 from "@clack/prompts";
|
|
12331
12330
|
import pc46 from "picocolors";
|
|
12332
|
-
import
|
|
12333
|
-
import
|
|
12331
|
+
import fs49 from "node:fs";
|
|
12332
|
+
import path56 from "node:path";
|
|
12334
12333
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
12335
12334
|
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
12336
12335
|
|
|
@@ -12991,8 +12990,8 @@ function printItemCompleted(item) {
|
|
|
12991
12990
|
const changes = Array.isArray(item.changes) ? item.changes : [];
|
|
12992
12991
|
const entries = changes.map((changeRaw) => asRecord(changeRaw)).filter((change) => Boolean(change)).map((change) => {
|
|
12993
12992
|
const kind = asString(change.kind, "update");
|
|
12994
|
-
const
|
|
12995
|
-
return `${kind} ${
|
|
12993
|
+
const path57 = asString(change.path, "unknown");
|
|
12994
|
+
return `${kind} ${path57}`;
|
|
12996
12995
|
});
|
|
12997
12996
|
const preview = entries.length > 0 ? entries.slice(0, 6).join(", ") : "none";
|
|
12998
12997
|
const more = entries.length > 6 ? ` (+${entries.length - 6} more)` : "";
|
|
@@ -15824,8 +15823,8 @@ function registerIssueCommands(program2) {
|
|
|
15824
15823
|
if (opts.assigneeAgentId) params.set("assigneeAgentId", opts.assigneeAgentId);
|
|
15825
15824
|
if (opts.projectId) params.set("projectId", opts.projectId);
|
|
15826
15825
|
const query = params.toString();
|
|
15827
|
-
const
|
|
15828
|
-
const rows = await ctx.api.get(
|
|
15826
|
+
const path57 = `/api/companies/${ctx.companyId}/issues${query ? `?${query}` : ""}`;
|
|
15827
|
+
const rows = await ctx.api.get(path57) ?? [];
|
|
15829
15828
|
const filtered = filterIssueRows(rows, opts.match);
|
|
15830
15829
|
if (ctx.json) {
|
|
15831
15830
|
printOutput(filtered, { json: true });
|
|
@@ -16431,8 +16430,8 @@ function registerActivityCommands(program2) {
|
|
|
16431
16430
|
if (opts.entityType) params.set("entityType", opts.entityType);
|
|
16432
16431
|
if (opts.entityId) params.set("entityId", opts.entityId);
|
|
16433
16432
|
const query = params.toString();
|
|
16434
|
-
const
|
|
16435
|
-
const rows = await ctx.api.get(
|
|
16433
|
+
const path57 = `/api/companies/${ctx.companyId}/activity${query ? `?${query}` : ""}`;
|
|
16434
|
+
const rows = await ctx.api.get(path57) ?? [];
|
|
16436
16435
|
if (ctx.json) {
|
|
16437
16436
|
printOutput(rows, { json: true });
|
|
16438
16437
|
return;
|
|
@@ -17866,12 +17865,13 @@ ${result.lastError}`);
|
|
|
17866
17865
|
// src/commands/kit.ts
|
|
17867
17866
|
init_service();
|
|
17868
17867
|
init_banner();
|
|
17869
|
-
import
|
|
17868
|
+
import path34 from "node:path";
|
|
17870
17869
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
17871
17870
|
import * as p19 from "@clack/prompts";
|
|
17872
17871
|
import pc30 from "picocolors";
|
|
17873
17872
|
|
|
17874
17873
|
// src/commands/kit-fork.ts
|
|
17874
|
+
import fs27 from "node:fs";
|
|
17875
17875
|
import * as p18 from "@clack/prompts";
|
|
17876
17876
|
|
|
17877
17877
|
// src/commands/kit-fork-remote.ts
|
|
@@ -19220,6 +19220,243 @@ init_fork_registry();
|
|
|
19220
19220
|
init_service();
|
|
19221
19221
|
init_fork_policy();
|
|
19222
19222
|
init_fork_trace();
|
|
19223
|
+
|
|
19224
|
+
// src/kits/fork-authority.ts
|
|
19225
|
+
init_home();
|
|
19226
|
+
init_kit_forks_home();
|
|
19227
|
+
import crypto from "node:crypto";
|
|
19228
|
+
import fs26 from "node:fs";
|
|
19229
|
+
import os8 from "node:os";
|
|
19230
|
+
import path33 from "node:path";
|
|
19231
|
+
function resolveAuthorityHomeDir() {
|
|
19232
|
+
const env = process.env.GROWTHUB_AUTHORITY_HOME?.trim();
|
|
19233
|
+
if (env) return path33.resolve(expandHomePrefix(env));
|
|
19234
|
+
return path33.resolve(os8.homedir(), ".growthub", "authority");
|
|
19235
|
+
}
|
|
19236
|
+
function resolveAuthorityIssuersPath() {
|
|
19237
|
+
return path33.resolve(resolveAuthorityHomeDir(), "issuers.json");
|
|
19238
|
+
}
|
|
19239
|
+
function resolveInForkAuthorityPath(forkPath) {
|
|
19240
|
+
return path33.resolve(resolveInForkStateDir(forkPath), "authority.json");
|
|
19241
|
+
}
|
|
19242
|
+
function canonicalize(value) {
|
|
19243
|
+
if (value === null) return "null";
|
|
19244
|
+
if (typeof value === "number") {
|
|
19245
|
+
if (!Number.isFinite(value)) {
|
|
19246
|
+
throw new Error("Cannot canonicalize non-finite number");
|
|
19247
|
+
}
|
|
19248
|
+
return JSON.stringify(value);
|
|
19249
|
+
}
|
|
19250
|
+
if (typeof value === "string" || typeof value === "boolean") {
|
|
19251
|
+
return JSON.stringify(value);
|
|
19252
|
+
}
|
|
19253
|
+
if (Array.isArray(value)) {
|
|
19254
|
+
return "[" + value.map(canonicalize).join(",") + "]";
|
|
19255
|
+
}
|
|
19256
|
+
if (typeof value === "object") {
|
|
19257
|
+
const entries = Object.entries(value).filter(([, v]) => v !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
|
|
19258
|
+
return "{" + entries.map(([k, v]) => JSON.stringify(k) + ":" + canonicalize(v)).join(",") + "}";
|
|
19259
|
+
}
|
|
19260
|
+
throw new Error(`Unsupported value type for canonicalize: ${typeof value}`);
|
|
19261
|
+
}
|
|
19262
|
+
function buildSigningPayload(envelope) {
|
|
19263
|
+
return canonicalize({
|
|
19264
|
+
version: envelope.version,
|
|
19265
|
+
envelopeId: envelope.envelopeId,
|
|
19266
|
+
issuerId: envelope.issuerId,
|
|
19267
|
+
algorithm: envelope.algorithm,
|
|
19268
|
+
subject: envelope.subject,
|
|
19269
|
+
grants: envelope.grants,
|
|
19270
|
+
issuedAt: envelope.issuedAt,
|
|
19271
|
+
expiresAt: envelope.expiresAt,
|
|
19272
|
+
nonce: envelope.nonce
|
|
19273
|
+
});
|
|
19274
|
+
}
|
|
19275
|
+
function computePolicyHash(policy) {
|
|
19276
|
+
const shape = {
|
|
19277
|
+
version: policy.version,
|
|
19278
|
+
untouchablePaths: [...policy.untouchablePaths].sort(),
|
|
19279
|
+
confirmBeforeChange: [...policy.confirmBeforeChange].sort(),
|
|
19280
|
+
autoApprove: policy.autoApprove,
|
|
19281
|
+
autoApproveDepUpdates: policy.autoApproveDepUpdates,
|
|
19282
|
+
remoteSyncMode: policy.remoteSyncMode,
|
|
19283
|
+
interactiveConflicts: policy.interactiveConflicts,
|
|
19284
|
+
allowedScripts: [...policy.allowedScripts].sort()
|
|
19285
|
+
};
|
|
19286
|
+
return crypto.createHash("sha256").update(canonicalize(shape), "utf8").digest("hex");
|
|
19287
|
+
}
|
|
19288
|
+
function readIssuerRegistry() {
|
|
19289
|
+
const p33 = resolveAuthorityIssuersPath();
|
|
19290
|
+
if (!fs26.existsSync(p33)) return { version: 1, issuers: [] };
|
|
19291
|
+
try {
|
|
19292
|
+
const parsed = JSON.parse(fs26.readFileSync(p33, "utf8"));
|
|
19293
|
+
if (!parsed || !Array.isArray(parsed.issuers)) return { version: 1, issuers: [] };
|
|
19294
|
+
return { version: 1, issuers: parsed.issuers.filter(isValidIssuer) };
|
|
19295
|
+
} catch {
|
|
19296
|
+
return { version: 1, issuers: [] };
|
|
19297
|
+
}
|
|
19298
|
+
}
|
|
19299
|
+
function isValidIssuer(x) {
|
|
19300
|
+
if (!x || typeof x !== "object") return false;
|
|
19301
|
+
const o = x;
|
|
19302
|
+
return typeof o.id === "string" && typeof o.publicKeyPem === "string" && (o.kind === "growthub-hosted" || o.kind === "self-signed" || o.kind === "enterprise");
|
|
19303
|
+
}
|
|
19304
|
+
function writeIssuerRegistry(registry) {
|
|
19305
|
+
const p33 = resolveAuthorityIssuersPath();
|
|
19306
|
+
fs26.mkdirSync(path33.dirname(p33), { recursive: true });
|
|
19307
|
+
fs26.writeFileSync(p33, JSON.stringify({ version: 1, issuers: registry.issuers }, null, 2) + "\n", "utf8");
|
|
19308
|
+
}
|
|
19309
|
+
function upsertIssuer(issuer) {
|
|
19310
|
+
if (!isValidIssuer(issuer)) {
|
|
19311
|
+
throw new Error("Invalid issuer: id, publicKeyPem, and kind are required.");
|
|
19312
|
+
}
|
|
19313
|
+
const reg = readIssuerRegistry();
|
|
19314
|
+
const idx = reg.issuers.findIndex((i) => i.id === issuer.id);
|
|
19315
|
+
const stamped = { ...issuer, addedAt: issuer.addedAt ?? (/* @__PURE__ */ new Date()).toISOString() };
|
|
19316
|
+
if (idx >= 0) reg.issuers[idx] = stamped;
|
|
19317
|
+
else reg.issuers.push(stamped);
|
|
19318
|
+
writeIssuerRegistry(reg);
|
|
19319
|
+
return reg;
|
|
19320
|
+
}
|
|
19321
|
+
function removeIssuer(issuerId) {
|
|
19322
|
+
const reg = readIssuerRegistry();
|
|
19323
|
+
const next = reg.issuers.filter((i) => i.id !== issuerId);
|
|
19324
|
+
if (next.length === reg.issuers.length) return false;
|
|
19325
|
+
writeIssuerRegistry({ version: 1, issuers: next });
|
|
19326
|
+
return true;
|
|
19327
|
+
}
|
|
19328
|
+
function findIssuer(issuerId) {
|
|
19329
|
+
return readIssuerRegistry().issuers.find((i) => i.id === issuerId) ?? null;
|
|
19330
|
+
}
|
|
19331
|
+
function verifyAuthorityEnvelope(envelope, options = {}) {
|
|
19332
|
+
if (!isWellFormedEnvelope(envelope)) {
|
|
19333
|
+
return { ok: false, reason: "malformed", detail: "envelope shape invalid" };
|
|
19334
|
+
}
|
|
19335
|
+
if (options.expectedKitId && envelope.subject.kitId !== options.expectedKitId) {
|
|
19336
|
+
return { ok: false, reason: "subject-mismatch", detail: `kitId expected ${options.expectedKitId}` };
|
|
19337
|
+
}
|
|
19338
|
+
if (options.expectedForkId && envelope.subject.forkId !== options.expectedForkId) {
|
|
19339
|
+
return { ok: false, reason: "subject-mismatch", detail: `forkId expected ${options.expectedForkId}` };
|
|
19340
|
+
}
|
|
19341
|
+
if (options.expectedForkPath && envelope.subject.forkPath && envelope.subject.forkPath !== options.expectedForkPath) {
|
|
19342
|
+
return { ok: false, reason: "subject-mismatch", detail: `forkPath expected ${options.expectedForkPath}` };
|
|
19343
|
+
}
|
|
19344
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
19345
|
+
if (envelope.expiresAt) {
|
|
19346
|
+
const exp = Date.parse(envelope.expiresAt);
|
|
19347
|
+
if (!Number.isFinite(exp) || exp <= now.getTime()) {
|
|
19348
|
+
return { ok: false, reason: "expired", detail: envelope.expiresAt };
|
|
19349
|
+
}
|
|
19350
|
+
}
|
|
19351
|
+
const issuer = options.issuerOverride ?? findIssuer(envelope.issuerId);
|
|
19352
|
+
if (!issuer) {
|
|
19353
|
+
return { ok: false, reason: "unknown-issuer", detail: envelope.issuerId };
|
|
19354
|
+
}
|
|
19355
|
+
let signatureBytes;
|
|
19356
|
+
try {
|
|
19357
|
+
signatureBytes = Buffer.from(envelope.signature, "base64");
|
|
19358
|
+
} catch {
|
|
19359
|
+
return { ok: false, reason: "bad-signature", detail: "signature is not base64" };
|
|
19360
|
+
}
|
|
19361
|
+
let ok = false;
|
|
19362
|
+
try {
|
|
19363
|
+
const { signature: _omit, ...unsigned } = envelope;
|
|
19364
|
+
const payload = buildSigningPayload(unsigned);
|
|
19365
|
+
const pubKey = crypto.createPublicKey({ key: issuer.publicKeyPem, format: "pem" });
|
|
19366
|
+
ok = crypto.verify(null, Buffer.from(payload, "utf8"), pubKey, signatureBytes);
|
|
19367
|
+
} catch (err) {
|
|
19368
|
+
return { ok: false, reason: "bad-signature", detail: err.message };
|
|
19369
|
+
}
|
|
19370
|
+
if (!ok) return { ok: false, reason: "bad-signature" };
|
|
19371
|
+
return { ok: true, issuer };
|
|
19372
|
+
}
|
|
19373
|
+
function isWellFormedEnvelope(x) {
|
|
19374
|
+
if (!x || typeof x !== "object") return false;
|
|
19375
|
+
const e = x;
|
|
19376
|
+
return e.version === 1 && typeof e.envelopeId === "string" && typeof e.issuerId === "string" && e.algorithm === "ed25519" && typeof e.signature === "string" && typeof e.issuedAt === "string" && typeof e.nonce === "string" && !!e.subject && typeof e.subject.kitId === "string" && typeof e.subject.forkId === "string" && !!e.grants && Array.isArray(e.grants.capabilities) && typeof e.grants.policyAttested === "boolean";
|
|
19377
|
+
}
|
|
19378
|
+
function readForkAuthorityState(forkPath) {
|
|
19379
|
+
const p33 = resolveInForkAuthorityPath(forkPath);
|
|
19380
|
+
if (!fs26.existsSync(p33)) {
|
|
19381
|
+
return { state: "none", version: 1, updatedAt: (/* @__PURE__ */ new Date(0)).toISOString() };
|
|
19382
|
+
}
|
|
19383
|
+
try {
|
|
19384
|
+
const parsed = JSON.parse(fs26.readFileSync(p33, "utf8"));
|
|
19385
|
+
if (!parsed || parsed.version !== 1) {
|
|
19386
|
+
return { state: "none", version: 1, updatedAt: (/* @__PURE__ */ new Date(0)).toISOString() };
|
|
19387
|
+
}
|
|
19388
|
+
return parsed;
|
|
19389
|
+
} catch {
|
|
19390
|
+
return { state: "none", version: 1, updatedAt: (/* @__PURE__ */ new Date(0)).toISOString() };
|
|
19391
|
+
}
|
|
19392
|
+
}
|
|
19393
|
+
function writeForkAuthorityState(forkPath, state) {
|
|
19394
|
+
const p33 = resolveInForkAuthorityPath(forkPath);
|
|
19395
|
+
fs26.mkdirSync(path33.dirname(p33), { recursive: true });
|
|
19396
|
+
fs26.writeFileSync(p33, JSON.stringify(state, null, 2) + "\n", "utf8");
|
|
19397
|
+
}
|
|
19398
|
+
function attachAuthorityEnvelope(forkPath, envelope, options = {}) {
|
|
19399
|
+
const verification = verifyAuthorityEnvelope(envelope, options);
|
|
19400
|
+
if (!verification.ok) {
|
|
19401
|
+
throw new Error(
|
|
19402
|
+
`Authority envelope rejected (${verification.reason}${verification.detail ? `: ${verification.detail}` : ""}).`
|
|
19403
|
+
);
|
|
19404
|
+
}
|
|
19405
|
+
const state = {
|
|
19406
|
+
state: "attested",
|
|
19407
|
+
version: 1,
|
|
19408
|
+
envelope,
|
|
19409
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
19410
|
+
};
|
|
19411
|
+
writeForkAuthorityState(forkPath, state);
|
|
19412
|
+
return { state, verification };
|
|
19413
|
+
}
|
|
19414
|
+
function revokeForkAuthority(forkPath, reason) {
|
|
19415
|
+
const current = readForkAuthorityState(forkPath);
|
|
19416
|
+
if (current.state !== "attested") {
|
|
19417
|
+
throw new Error("No active authority envelope to revoke.");
|
|
19418
|
+
}
|
|
19419
|
+
const next = {
|
|
19420
|
+
state: "revoked",
|
|
19421
|
+
version: 1,
|
|
19422
|
+
envelope: current.envelope,
|
|
19423
|
+
revocation: { reason, revokedAt: (/* @__PURE__ */ new Date()).toISOString() },
|
|
19424
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
19425
|
+
};
|
|
19426
|
+
writeForkAuthorityState(forkPath, next);
|
|
19427
|
+
return next;
|
|
19428
|
+
}
|
|
19429
|
+
function describePolicyAttestation(forkPath, policy, options = {}) {
|
|
19430
|
+
const stateRecord = readForkAuthorityState(forkPath);
|
|
19431
|
+
if (stateRecord.state === "none") {
|
|
19432
|
+
return { origin: "operator-local" };
|
|
19433
|
+
}
|
|
19434
|
+
if (stateRecord.state === "revoked") {
|
|
19435
|
+
return {
|
|
19436
|
+
origin: "authority-revoked",
|
|
19437
|
+
envelope: stateRecord.envelope,
|
|
19438
|
+
revokedReason: stateRecord.revocation.reason
|
|
19439
|
+
};
|
|
19440
|
+
}
|
|
19441
|
+
const envelope = stateRecord.envelope;
|
|
19442
|
+
const verification = verifyAuthorityEnvelope(envelope, {
|
|
19443
|
+
expectedForkId: options.expectedForkId,
|
|
19444
|
+
expectedKitId: options.expectedKitId,
|
|
19445
|
+
now: options.now
|
|
19446
|
+
});
|
|
19447
|
+
if (!verification.ok) {
|
|
19448
|
+
return { origin: "operator-local", envelope, verification };
|
|
19449
|
+
}
|
|
19450
|
+
const policyHashMatches = envelope.subject.policyHash ? envelope.subject.policyHash === computePolicyHash(policy) : void 0;
|
|
19451
|
+
return {
|
|
19452
|
+
origin: "authority-attested",
|
|
19453
|
+
envelope,
|
|
19454
|
+
verification,
|
|
19455
|
+
policyHashMatches
|
|
19456
|
+
};
|
|
19457
|
+
}
|
|
19458
|
+
|
|
19459
|
+
// src/commands/kit-fork.ts
|
|
19223
19460
|
function hr(width = 72) {
|
|
19224
19461
|
return pc29.dim("\u2500".repeat(width));
|
|
19225
19462
|
}
|
|
@@ -20368,6 +20605,7 @@ function addForkSubcommands(parentCmd) {
|
|
|
20368
20605
|
console.log(hr());
|
|
20369
20606
|
console.log("");
|
|
20370
20607
|
});
|
|
20608
|
+
registerAuthoritySubcommands(parentCmd);
|
|
20371
20609
|
parentCmd.command("deregister").description("Remove a fork registration (does not delete your fork directory)").argument("<fork-id>", "Fork ID to deregister").action(async (forkId) => {
|
|
20372
20610
|
const allForks = listKitForkRegistrations();
|
|
20373
20611
|
const reg = allForks.find((f) => f.forkId === forkId);
|
|
@@ -20393,6 +20631,193 @@ function addForkSubcommands(parentCmd) {
|
|
|
20393
20631
|
}
|
|
20394
20632
|
});
|
|
20395
20633
|
}
|
|
20634
|
+
function registerAuthoritySubcommands(parentCmd) {
|
|
20635
|
+
const authorityCmd = parentCmd.command("authority").description("Inspect and manage hosted-authority attestations attached to a fork");
|
|
20636
|
+
authorityCmd.command("status").description("Show current authority attestation state for a fork").argument("<fork-id>", "Fork ID from list").option("--json", "Emit machine-readable JSON").action((forkId, opts) => {
|
|
20637
|
+
const reg = listKitForkRegistrations().find((f) => f.forkId === forkId);
|
|
20638
|
+
if (!reg) {
|
|
20639
|
+
console.error(pc29.red(`Fork not found: ${forkId}`));
|
|
20640
|
+
process.exitCode = 1;
|
|
20641
|
+
return;
|
|
20642
|
+
}
|
|
20643
|
+
const policy = readKitForkPolicy(reg.forkPath);
|
|
20644
|
+
const summary = describePolicyAttestation(reg.forkPath, policy, {
|
|
20645
|
+
expectedForkId: reg.forkId,
|
|
20646
|
+
expectedKitId: reg.kitId
|
|
20647
|
+
});
|
|
20648
|
+
const state = readForkAuthorityState(reg.forkPath);
|
|
20649
|
+
if (opts.json) {
|
|
20650
|
+
console.log(JSON.stringify({ forkId: reg.forkId, kitId: reg.kitId, state, summary }, null, 2));
|
|
20651
|
+
return;
|
|
20652
|
+
}
|
|
20653
|
+
console.log("");
|
|
20654
|
+
console.log(pc29.bold(`Authority: ${reg.forkId}`));
|
|
20655
|
+
console.log(hr());
|
|
20656
|
+
console.log(` ${pc29.dim("Origin:")} ${originLabel(summary.origin)}`);
|
|
20657
|
+
if (summary.envelope) {
|
|
20658
|
+
const env = summary.envelope;
|
|
20659
|
+
console.log(` ${pc29.dim("Issuer:")} ${env.issuerId}`);
|
|
20660
|
+
console.log(` ${pc29.dim("Envelope ID:")} ${env.envelopeId}`);
|
|
20661
|
+
console.log(` ${pc29.dim("Issued at:")} ${env.issuedAt}`);
|
|
20662
|
+
if (env.expiresAt) console.log(` ${pc29.dim("Expires at:")} ${env.expiresAt}`);
|
|
20663
|
+
console.log(` ${pc29.dim("Capabilities:")} ${env.grants.capabilities.join(", ") || pc29.dim("(none)")}`);
|
|
20664
|
+
console.log(` ${pc29.dim("Policy attested:")} ${env.grants.policyAttested ? "yes" : "no"}`);
|
|
20665
|
+
if (summary.policyHashMatches === true) {
|
|
20666
|
+
console.log(` ${pc29.dim("Policy hash:")} ${pc29.green("matches on-disk policy")}`);
|
|
20667
|
+
} else if (summary.policyHashMatches === false) {
|
|
20668
|
+
console.log(` ${pc29.dim("Policy hash:")} ${pc29.yellow("MISMATCH \u2014 policy changed since attestation")}`);
|
|
20669
|
+
}
|
|
20670
|
+
if (summary.verification && !summary.verification.ok) {
|
|
20671
|
+
console.log(` ${pc29.dim("Verification:")} ${pc29.red(summary.verification.reason)}${summary.verification.detail ? pc29.dim(` (${summary.verification.detail})`) : ""}`);
|
|
20672
|
+
}
|
|
20673
|
+
if (summary.origin === "authority-revoked") {
|
|
20674
|
+
console.log(` ${pc29.dim("Revoked reason:")} ${summary.revokedReason ?? pc29.dim("(none)")}`);
|
|
20675
|
+
}
|
|
20676
|
+
} else {
|
|
20677
|
+
console.log(pc29.dim(" No authority envelope attached. Operator-local policy in effect."));
|
|
20678
|
+
}
|
|
20679
|
+
console.log("");
|
|
20680
|
+
});
|
|
20681
|
+
authorityCmd.command("attest").description("Attach a signed authority envelope to a fork").argument("<fork-id>", "Fork ID from list").requiredOption("--file <path>", "Path to a JSON file containing the envelope").action((forkId, opts) => {
|
|
20682
|
+
const reg = listKitForkRegistrations().find((f) => f.forkId === forkId);
|
|
20683
|
+
if (!reg) {
|
|
20684
|
+
console.error(pc29.red(`Fork not found: ${forkId}`));
|
|
20685
|
+
process.exitCode = 1;
|
|
20686
|
+
return;
|
|
20687
|
+
}
|
|
20688
|
+
let raw;
|
|
20689
|
+
try {
|
|
20690
|
+
raw = fs27.readFileSync(opts.file, "utf8");
|
|
20691
|
+
} catch (err) {
|
|
20692
|
+
console.error(pc29.red(`Cannot read envelope file: ${err.message}`));
|
|
20693
|
+
process.exitCode = 1;
|
|
20694
|
+
return;
|
|
20695
|
+
}
|
|
20696
|
+
let envelope;
|
|
20697
|
+
try {
|
|
20698
|
+
envelope = JSON.parse(raw);
|
|
20699
|
+
} catch (err) {
|
|
20700
|
+
console.error(pc29.red(`Invalid JSON in envelope file: ${err.message}`));
|
|
20701
|
+
process.exitCode = 1;
|
|
20702
|
+
return;
|
|
20703
|
+
}
|
|
20704
|
+
try {
|
|
20705
|
+
const result = attachAuthorityEnvelope(reg.forkPath, envelope, {
|
|
20706
|
+
expectedForkId: reg.forkId,
|
|
20707
|
+
expectedKitId: reg.kitId
|
|
20708
|
+
});
|
|
20709
|
+
appendKitForkTraceEvent(reg.forkPath, {
|
|
20710
|
+
forkId: reg.forkId,
|
|
20711
|
+
kitId: reg.kitId,
|
|
20712
|
+
type: "authority_attested",
|
|
20713
|
+
summary: `Authority envelope ${envelope.envelopeId} attached (issuer ${envelope.issuerId})`,
|
|
20714
|
+
detail: {
|
|
20715
|
+
envelopeId: envelope.envelopeId,
|
|
20716
|
+
issuerId: envelope.issuerId,
|
|
20717
|
+
capabilities: envelope.grants.capabilities,
|
|
20718
|
+
expiresAt: envelope.expiresAt ?? null
|
|
20719
|
+
}
|
|
20720
|
+
});
|
|
20721
|
+
console.log(pc29.green("Authority envelope attached:"), result.state.state);
|
|
20722
|
+
if (result.verification.ok) {
|
|
20723
|
+
console.log(pc29.dim(" Issuer: "), result.verification.issuer.id);
|
|
20724
|
+
console.log(pc29.dim(" Capabilities:"), envelope.grants.capabilities.join(", ") || pc29.dim("(none)"));
|
|
20725
|
+
}
|
|
20726
|
+
} catch (err) {
|
|
20727
|
+
console.error(pc29.red(err.message));
|
|
20728
|
+
process.exitCode = 1;
|
|
20729
|
+
}
|
|
20730
|
+
});
|
|
20731
|
+
authorityCmd.command("revoke").description("Revoke the local authority attestation for a fork").argument("<fork-id>", "Fork ID from list").option("--reason <text>", "Why the attestation is being revoked").action((forkId, opts) => {
|
|
20732
|
+
const reg = listKitForkRegistrations().find((f) => f.forkId === forkId);
|
|
20733
|
+
if (!reg) {
|
|
20734
|
+
console.error(pc29.red(`Fork not found: ${forkId}`));
|
|
20735
|
+
process.exitCode = 1;
|
|
20736
|
+
return;
|
|
20737
|
+
}
|
|
20738
|
+
try {
|
|
20739
|
+
const next = revokeForkAuthority(reg.forkPath, opts.reason);
|
|
20740
|
+
appendKitForkTraceEvent(reg.forkPath, {
|
|
20741
|
+
forkId: reg.forkId,
|
|
20742
|
+
kitId: reg.kitId,
|
|
20743
|
+
type: "authority_revoked",
|
|
20744
|
+
summary: `Authority envelope ${next.state === "revoked" ? next.envelope.envelopeId : ""} revoked`,
|
|
20745
|
+
detail: { reason: opts.reason ?? null }
|
|
20746
|
+
});
|
|
20747
|
+
console.log(pc29.yellow("Authority attestation revoked."));
|
|
20748
|
+
if (opts.reason) console.log(pc29.dim(" Reason:"), opts.reason);
|
|
20749
|
+
} catch (err) {
|
|
20750
|
+
console.error(pc29.red(err.message));
|
|
20751
|
+
process.exitCode = 1;
|
|
20752
|
+
}
|
|
20753
|
+
});
|
|
20754
|
+
const issuerCmd = authorityCmd.command("issuer").description("Manage trusted authority issuers (local trust root)");
|
|
20755
|
+
issuerCmd.command("list").description("List trusted issuers from the local registry").option("--json", "Emit machine-readable JSON").action((opts) => {
|
|
20756
|
+
const reg = readIssuerRegistry();
|
|
20757
|
+
if (opts.json) {
|
|
20758
|
+
console.log(JSON.stringify(reg, null, 2));
|
|
20759
|
+
return;
|
|
20760
|
+
}
|
|
20761
|
+
console.log("");
|
|
20762
|
+
console.log(pc29.bold("Trusted Authority Issuers"));
|
|
20763
|
+
console.log(hr());
|
|
20764
|
+
if (reg.issuers.length === 0) {
|
|
20765
|
+
console.log(pc29.dim(" No issuers trusted. Add one with `growthub kit fork authority issuer add`."));
|
|
20766
|
+
} else {
|
|
20767
|
+
for (const i of reg.issuers) {
|
|
20768
|
+
console.log(` ${pc29.cyan(i.id)} ${pc29.dim(`[${i.kind}]`)}${i.label ? ` ${i.label}` : ""}`);
|
|
20769
|
+
if (i.addedAt) console.log(` ${pc29.dim("added:")} ${i.addedAt}`);
|
|
20770
|
+
}
|
|
20771
|
+
}
|
|
20772
|
+
console.log("");
|
|
20773
|
+
});
|
|
20774
|
+
issuerCmd.command("add").description("Add or replace a trusted issuer in the local registry").requiredOption("--id <id>", "Issuer ID (must match envelope.issuerId)").requiredOption("--kind <kind>", "Issuer kind: growthub-hosted | self-signed | enterprise").requiredOption("--key <path>", "Path to a PEM-encoded ed25519 public key file").option("--label <label>", "Human-readable label").action((opts) => {
|
|
20775
|
+
const kinds = ["growthub-hosted", "self-signed", "enterprise"];
|
|
20776
|
+
if (!kinds.includes(opts.kind)) {
|
|
20777
|
+
console.error(pc29.red(`Invalid --kind: ${opts.kind}. Expected one of ${kinds.join(", ")}.`));
|
|
20778
|
+
process.exitCode = 1;
|
|
20779
|
+
return;
|
|
20780
|
+
}
|
|
20781
|
+
let pem;
|
|
20782
|
+
try {
|
|
20783
|
+
pem = fs27.readFileSync(opts.key, "utf8");
|
|
20784
|
+
} catch (err) {
|
|
20785
|
+
console.error(pc29.red(`Cannot read key file: ${err.message}`));
|
|
20786
|
+
process.exitCode = 1;
|
|
20787
|
+
return;
|
|
20788
|
+
}
|
|
20789
|
+
try {
|
|
20790
|
+
upsertIssuer({
|
|
20791
|
+
id: opts.id,
|
|
20792
|
+
kind: opts.kind,
|
|
20793
|
+
publicKeyPem: pem,
|
|
20794
|
+
label: opts.label
|
|
20795
|
+
});
|
|
20796
|
+
console.log(pc29.green("Issuer added:"), opts.id);
|
|
20797
|
+
} catch (err) {
|
|
20798
|
+
console.error(pc29.red(err.message));
|
|
20799
|
+
process.exitCode = 1;
|
|
20800
|
+
}
|
|
20801
|
+
});
|
|
20802
|
+
issuerCmd.command("remove").description("Remove a trusted issuer from the local registry").requiredOption("--id <id>", "Issuer ID to remove").action((opts) => {
|
|
20803
|
+
const ok = removeIssuer(opts.id);
|
|
20804
|
+
if (ok) console.log(pc29.green("Issuer removed:"), opts.id);
|
|
20805
|
+
else {
|
|
20806
|
+
console.error(pc29.red(`No issuer with id: ${opts.id}`));
|
|
20807
|
+
process.exitCode = 1;
|
|
20808
|
+
}
|
|
20809
|
+
});
|
|
20810
|
+
}
|
|
20811
|
+
function originLabel(origin) {
|
|
20812
|
+
switch (origin) {
|
|
20813
|
+
case "authority-attested":
|
|
20814
|
+
return pc29.green("authority-attested");
|
|
20815
|
+
case "authority-revoked":
|
|
20816
|
+
return pc29.yellow("authority-revoked");
|
|
20817
|
+
default:
|
|
20818
|
+
return pc29.dim("operator-local");
|
|
20819
|
+
}
|
|
20820
|
+
}
|
|
20396
20821
|
|
|
20397
20822
|
// src/commands/kit.ts
|
|
20398
20823
|
var TYPE_CONFIG = {
|
|
@@ -20837,7 +21262,7 @@ Examples:
|
|
|
20837
21262
|
$ growthub kit validate ./my-kit
|
|
20838
21263
|
$ growthub kit validate ~/kits/growthub-open-higgsfield-studio-v1
|
|
20839
21264
|
`).action((kitPath) => {
|
|
20840
|
-
const resolvedPath =
|
|
21265
|
+
const resolvedPath = path34.resolve(kitPath);
|
|
20841
21266
|
const result = validateKitDirectory(resolvedPath);
|
|
20842
21267
|
console.log("");
|
|
20843
21268
|
console.log(pc30.bold("Kit: " + result.kitId) + pc30.dim(" schema v" + result.schemaVersion));
|
|
@@ -20882,13 +21307,13 @@ Examples:
|
|
|
20882
21307
|
}
|
|
20883
21308
|
|
|
20884
21309
|
// src/commands/template.ts
|
|
20885
|
-
import
|
|
21310
|
+
import path36 from "node:path";
|
|
20886
21311
|
import * as p20 from "@clack/prompts";
|
|
20887
21312
|
import pc31 from "picocolors";
|
|
20888
21313
|
|
|
20889
21314
|
// src/templates/service.ts
|
|
20890
|
-
import
|
|
20891
|
-
import
|
|
21315
|
+
import fs28 from "node:fs";
|
|
21316
|
+
import path35 from "node:path";
|
|
20892
21317
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
20893
21318
|
|
|
20894
21319
|
// src/templates/catalog.ts
|
|
@@ -21135,12 +21560,12 @@ var TEMPLATE_CATALOG = [
|
|
|
21135
21560
|
|
|
21136
21561
|
// src/templates/service.ts
|
|
21137
21562
|
function resolveSharedTemplatesRoot() {
|
|
21138
|
-
const moduleDir =
|
|
21563
|
+
const moduleDir = path35.dirname(fileURLToPath6(import.meta.url));
|
|
21139
21564
|
for (const candidate of [
|
|
21140
|
-
|
|
21141
|
-
|
|
21565
|
+
path35.resolve(moduleDir, "../../assets/shared-templates"),
|
|
21566
|
+
path35.resolve(moduleDir, "../assets/shared-templates")
|
|
21142
21567
|
]) {
|
|
21143
|
-
if (
|
|
21568
|
+
if (fs28.existsSync(candidate)) return candidate;
|
|
21144
21569
|
}
|
|
21145
21570
|
throw new Error("Shared template assets not found at cli/assets/shared-templates/");
|
|
21146
21571
|
}
|
|
@@ -21175,15 +21600,15 @@ function getArtifact(slugOrId) {
|
|
|
21175
21600
|
const artifact = resolveSlug(slugOrId);
|
|
21176
21601
|
if (!artifact) throw new Error(`Unknown template '${slugOrId}'. Run 'growthub template list' to browse.`);
|
|
21177
21602
|
const root = resolveSharedTemplatesRoot();
|
|
21178
|
-
const absolutePath =
|
|
21179
|
-
if (!
|
|
21180
|
-
return { artifact, content:
|
|
21603
|
+
const absolutePath = path35.resolve(root, artifact.path);
|
|
21604
|
+
if (!fs28.existsSync(absolutePath)) throw new Error(`Template file missing: ${absolutePath}`);
|
|
21605
|
+
return { artifact, content: fs28.readFileSync(absolutePath, "utf8"), absolutePath };
|
|
21181
21606
|
}
|
|
21182
21607
|
function copyArtifact(slugOrId, destDir) {
|
|
21183
21608
|
const resolved = getArtifact(slugOrId);
|
|
21184
|
-
|
|
21185
|
-
const destPath =
|
|
21186
|
-
|
|
21609
|
+
fs28.mkdirSync(destDir, { recursive: true });
|
|
21610
|
+
const destPath = path35.resolve(destDir, path35.basename(resolved.absolutePath));
|
|
21611
|
+
fs28.copyFileSync(resolved.absolutePath, destPath);
|
|
21187
21612
|
return destPath;
|
|
21188
21613
|
}
|
|
21189
21614
|
var GROUP_ORDER = ["ad-formats", "scene-modules/hooks", "scene-modules/body", "scene-modules/cta"];
|
|
@@ -21423,7 +21848,7 @@ async function runTemplatePicker(opts) {
|
|
|
21423
21848
|
p20.cancel("Cancelled.");
|
|
21424
21849
|
process.exit(0);
|
|
21425
21850
|
}
|
|
21426
|
-
const destDir =
|
|
21851
|
+
const destDir = path36.resolve(destInput.replace(/^~/, process.env["HOME"] ?? ""));
|
|
21427
21852
|
const destPath = copyArtifact(selected.id, destDir);
|
|
21428
21853
|
p20.outro(pc31.green("Copied \u2192 ") + destPath);
|
|
21429
21854
|
return "done";
|
|
@@ -21497,7 +21922,7 @@ Any agent or kit resolves them by slug.
|
|
|
21497
21922
|
return;
|
|
21498
21923
|
}
|
|
21499
21924
|
if (opts.out) {
|
|
21500
|
-
const destDir =
|
|
21925
|
+
const destDir = path36.resolve(opts.out.replace(/^~/, process.env["HOME"] ?? ""));
|
|
21501
21926
|
try {
|
|
21502
21927
|
const dest = copyArtifact(artifact.id, destDir);
|
|
21503
21928
|
console.log(pc31.green("Copied \u2192 ") + dest);
|
|
@@ -22159,10 +22584,10 @@ function createCmsCapabilityRegistryClient() {
|
|
|
22159
22584
|
}
|
|
22160
22585
|
|
|
22161
22586
|
// src/runtime/machine-capability-resolver/index.ts
|
|
22162
|
-
import
|
|
22587
|
+
import os9 from "node:os";
|
|
22163
22588
|
function buildMachineContext(profile) {
|
|
22164
22589
|
return {
|
|
22165
|
-
hostname:
|
|
22590
|
+
hostname: os9.hostname(),
|
|
22166
22591
|
machineLabel: profile.local.machineLabel ?? void 0,
|
|
22167
22592
|
workspaceLabel: profile.local.workspaceLabel ?? void 0,
|
|
22168
22593
|
instanceId: profile.local.instanceId,
|
|
@@ -22581,8 +23006,8 @@ Examples:
|
|
|
22581
23006
|
// src/commands/pipeline.ts
|
|
22582
23007
|
init_session_store();
|
|
22583
23008
|
init_hosted_client();
|
|
22584
|
-
import
|
|
22585
|
-
import
|
|
23009
|
+
import fs31 from "node:fs";
|
|
23010
|
+
import path39 from "node:path";
|
|
22586
23011
|
import * as p22 from "@clack/prompts";
|
|
22587
23012
|
import pc34 from "picocolors";
|
|
22588
23013
|
|
|
@@ -23092,40 +23517,40 @@ function renderPreSaveReview(input) {
|
|
|
23092
23517
|
|
|
23093
23518
|
// src/runtime/artifact-contracts/index.ts
|
|
23094
23519
|
init_home();
|
|
23095
|
-
import
|
|
23096
|
-
import
|
|
23520
|
+
import fs29 from "node:fs";
|
|
23521
|
+
import path37 from "node:path";
|
|
23097
23522
|
import { randomBytes as randomBytes7 } from "node:crypto";
|
|
23098
23523
|
function generateArtifactId() {
|
|
23099
23524
|
return `art_${randomBytes7(8).toString("hex")}`;
|
|
23100
23525
|
}
|
|
23101
23526
|
function resolveArtifactsDir() {
|
|
23102
|
-
return
|
|
23527
|
+
return path37.resolve(resolvePaperclipHomeDir(), "artifacts");
|
|
23103
23528
|
}
|
|
23104
23529
|
function resolveArtifactManifestPath(artifactId) {
|
|
23105
|
-
return
|
|
23530
|
+
return path37.resolve(resolveArtifactsDir(), `${artifactId}.json`);
|
|
23106
23531
|
}
|
|
23107
23532
|
function readLocalManifest(artifactId) {
|
|
23108
23533
|
const filePath = resolveArtifactManifestPath(artifactId);
|
|
23109
|
-
if (!
|
|
23534
|
+
if (!fs29.existsSync(filePath)) return null;
|
|
23110
23535
|
try {
|
|
23111
|
-
return JSON.parse(
|
|
23536
|
+
return JSON.parse(fs29.readFileSync(filePath, "utf-8"));
|
|
23112
23537
|
} catch {
|
|
23113
23538
|
return null;
|
|
23114
23539
|
}
|
|
23115
23540
|
}
|
|
23116
23541
|
function writeLocalManifest(manifest) {
|
|
23117
23542
|
const dir = resolveArtifactsDir();
|
|
23118
|
-
|
|
23543
|
+
fs29.mkdirSync(dir, { recursive: true });
|
|
23119
23544
|
const filePath = resolveArtifactManifestPath(manifest.id);
|
|
23120
|
-
|
|
23545
|
+
fs29.writeFileSync(filePath, `${JSON.stringify(manifest, null, 2)}
|
|
23121
23546
|
`, { mode: 384 });
|
|
23122
23547
|
}
|
|
23123
23548
|
function listLocalManifests() {
|
|
23124
23549
|
const dir = resolveArtifactsDir();
|
|
23125
|
-
if (!
|
|
23126
|
-
return
|
|
23550
|
+
if (!fs29.existsSync(dir)) return [];
|
|
23551
|
+
return fs29.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => {
|
|
23127
23552
|
try {
|
|
23128
|
-
const content =
|
|
23553
|
+
const content = fs29.readFileSync(path37.resolve(dir, entry.name), "utf-8");
|
|
23129
23554
|
return JSON.parse(content);
|
|
23130
23555
|
} catch {
|
|
23131
23556
|
return null;
|
|
@@ -23203,8 +23628,8 @@ function createArtifactStore() {
|
|
|
23203
23628
|
|
|
23204
23629
|
// src/runtime/native-intelligence/index.ts
|
|
23205
23630
|
init_home();
|
|
23206
|
-
import
|
|
23207
|
-
import
|
|
23631
|
+
import fs30 from "node:fs";
|
|
23632
|
+
import path38 from "node:path";
|
|
23208
23633
|
|
|
23209
23634
|
// src/runtime/native-intelligence/contract.ts
|
|
23210
23635
|
var DEFAULT_INTELLIGENCE_CONFIG = {
|
|
@@ -24314,15 +24739,15 @@ function parseJsonSafe4(text66) {
|
|
|
24314
24739
|
|
|
24315
24740
|
// src/runtime/native-intelligence/index.ts
|
|
24316
24741
|
function resolveConfigPath2() {
|
|
24317
|
-
return
|
|
24742
|
+
return path38.resolve(resolvePaperclipHomeDir(), "native-intelligence", "config.json");
|
|
24318
24743
|
}
|
|
24319
24744
|
function readIntelligenceConfig() {
|
|
24320
24745
|
const configPath = resolveConfigPath2();
|
|
24321
|
-
if (!
|
|
24746
|
+
if (!fs30.existsSync(configPath)) {
|
|
24322
24747
|
return { ...DEFAULT_INTELLIGENCE_CONFIG };
|
|
24323
24748
|
}
|
|
24324
24749
|
try {
|
|
24325
|
-
const raw = JSON.parse(
|
|
24750
|
+
const raw = JSON.parse(fs30.readFileSync(configPath, "utf-8"));
|
|
24326
24751
|
return {
|
|
24327
24752
|
modelId: validateModelId(raw.modelId),
|
|
24328
24753
|
backendType: raw.backendType === "hosted" ? "hosted" : "local",
|
|
@@ -24339,8 +24764,8 @@ function readIntelligenceConfig() {
|
|
|
24339
24764
|
}
|
|
24340
24765
|
function writeIntelligenceConfig(config) {
|
|
24341
24766
|
const configPath = resolveConfigPath2();
|
|
24342
|
-
|
|
24343
|
-
|
|
24767
|
+
fs30.mkdirSync(path38.dirname(configPath), { recursive: true });
|
|
24768
|
+
fs30.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
|
|
24344
24769
|
`, "utf-8");
|
|
24345
24770
|
}
|
|
24346
24771
|
function validateModelId(id) {
|
|
@@ -24696,9 +25121,9 @@ async function runPipelineAssembler(opts) {
|
|
|
24696
25121
|
}
|
|
24697
25122
|
}
|
|
24698
25123
|
function loadPipelineFromFileOrJson(input) {
|
|
24699
|
-
const resolvedPath =
|
|
24700
|
-
if (
|
|
24701
|
-
const content =
|
|
25124
|
+
const resolvedPath = path39.resolve(input);
|
|
25125
|
+
if (fs31.existsSync(resolvedPath)) {
|
|
25126
|
+
const content = fs31.readFileSync(resolvedPath, "utf-8");
|
|
24702
25127
|
return deserializePipeline(JSON.parse(content));
|
|
24703
25128
|
}
|
|
24704
25129
|
try {
|
|
@@ -25243,8 +25668,8 @@ Examples:
|
|
|
25243
25668
|
}
|
|
25244
25669
|
|
|
25245
25670
|
// src/commands/workflow.ts
|
|
25246
|
-
import
|
|
25247
|
-
import
|
|
25671
|
+
import fs33 from "node:fs";
|
|
25672
|
+
import path41 from "node:path";
|
|
25248
25673
|
import * as p23 from "@clack/prompts";
|
|
25249
25674
|
import pc37 from "picocolors";
|
|
25250
25675
|
init_session_store();
|
|
@@ -25252,15 +25677,15 @@ init_hosted_client();
|
|
|
25252
25677
|
|
|
25253
25678
|
// src/runtime/workflow-hygiene/labels.ts
|
|
25254
25679
|
init_home();
|
|
25255
|
-
import
|
|
25256
|
-
import
|
|
25680
|
+
import fs32 from "node:fs";
|
|
25681
|
+
import path40 from "node:path";
|
|
25257
25682
|
function resolveStorePath() {
|
|
25258
|
-
return
|
|
25683
|
+
return path40.resolve(resolvePaperclipHomeDir(), "workflow-hygiene", "labels.json");
|
|
25259
25684
|
}
|
|
25260
25685
|
function readStoreFile(filePath) {
|
|
25261
|
-
if (!
|
|
25686
|
+
if (!fs32.existsSync(filePath)) return { records: [] };
|
|
25262
25687
|
try {
|
|
25263
|
-
const raw = JSON.parse(
|
|
25688
|
+
const raw = JSON.parse(fs32.readFileSync(filePath, "utf-8"));
|
|
25264
25689
|
if (!Array.isArray(raw.records)) return { records: [] };
|
|
25265
25690
|
return raw;
|
|
25266
25691
|
} catch {
|
|
@@ -25268,8 +25693,8 @@ function readStoreFile(filePath) {
|
|
|
25268
25693
|
}
|
|
25269
25694
|
}
|
|
25270
25695
|
function writeStoreFile(filePath, data) {
|
|
25271
|
-
|
|
25272
|
-
|
|
25696
|
+
fs32.mkdirSync(path40.dirname(filePath), { recursive: true });
|
|
25697
|
+
fs32.writeFileSync(filePath, `${JSON.stringify(data, null, 2)}
|
|
25273
25698
|
`, "utf-8");
|
|
25274
25699
|
}
|
|
25275
25700
|
function inferDefaultLabel(name, createdAt, versionCount) {
|
|
@@ -25370,16 +25795,16 @@ function box5(lines) {
|
|
|
25370
25795
|
return [top, ...body, bottom].join("\n");
|
|
25371
25796
|
}
|
|
25372
25797
|
function resolveSavedWorkflowsDir() {
|
|
25373
|
-
return
|
|
25798
|
+
return path41.resolve(resolvePaperclipHomeDir(), "workflows");
|
|
25374
25799
|
}
|
|
25375
25800
|
function resolveDeletedWorkflowIdsPath() {
|
|
25376
|
-
return
|
|
25801
|
+
return path41.resolve(resolvePaperclipHomeDir(), "workflow-hygiene", "deleted-workflows.json");
|
|
25377
25802
|
}
|
|
25378
25803
|
function readDeletedWorkflowIds() {
|
|
25379
25804
|
const filePath = resolveDeletedWorkflowIdsPath();
|
|
25380
|
-
if (!
|
|
25805
|
+
if (!fs33.existsSync(filePath)) return /* @__PURE__ */ new Set();
|
|
25381
25806
|
try {
|
|
25382
|
-
const raw = JSON.parse(
|
|
25807
|
+
const raw = JSON.parse(fs33.readFileSync(filePath, "utf-8"));
|
|
25383
25808
|
if (!Array.isArray(raw?.workflowIds)) return /* @__PURE__ */ new Set();
|
|
25384
25809
|
return new Set(raw.workflowIds.filter((value) => typeof value === "string"));
|
|
25385
25810
|
} catch {
|
|
@@ -25388,8 +25813,8 @@ function readDeletedWorkflowIds() {
|
|
|
25388
25813
|
}
|
|
25389
25814
|
function writeDeletedWorkflowIds(ids) {
|
|
25390
25815
|
const filePath = resolveDeletedWorkflowIdsPath();
|
|
25391
|
-
|
|
25392
|
-
|
|
25816
|
+
fs33.mkdirSync(path41.dirname(filePath), { recursive: true });
|
|
25817
|
+
fs33.writeFileSync(filePath, `${JSON.stringify({ workflowIds: [...ids] }, null, 2)}
|
|
25393
25818
|
`, "utf-8");
|
|
25394
25819
|
}
|
|
25395
25820
|
function markWorkflowDeletedLocally(workflowId) {
|
|
@@ -25415,10 +25840,10 @@ function filterLocallyDeletedWorkflows(entries) {
|
|
|
25415
25840
|
}
|
|
25416
25841
|
function listLocalSavedWorkflows() {
|
|
25417
25842
|
const dir = resolveSavedWorkflowsDir();
|
|
25418
|
-
if (!
|
|
25419
|
-
const entries =
|
|
25843
|
+
if (!fs33.existsSync(dir)) return [];
|
|
25844
|
+
const entries = fs33.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".json")).map((e) => {
|
|
25420
25845
|
try {
|
|
25421
|
-
const raw = JSON.parse(
|
|
25846
|
+
const raw = JSON.parse(fs33.readFileSync(path41.resolve(dir, e.name), "utf-8"));
|
|
25422
25847
|
const pipeline = raw.pipeline ?? raw;
|
|
25423
25848
|
return {
|
|
25424
25849
|
filename: e.name,
|
|
@@ -25479,11 +25904,11 @@ async function archiveSavedWorkflow(entry) {
|
|
|
25479
25904
|
throw new Error("Local workflow entry is missing filename.");
|
|
25480
25905
|
}
|
|
25481
25906
|
const dir = resolveSavedWorkflowsDir();
|
|
25482
|
-
const archiveDir =
|
|
25483
|
-
|
|
25484
|
-
|
|
25485
|
-
|
|
25486
|
-
|
|
25907
|
+
const archiveDir = path41.resolve(dir, "archived");
|
|
25908
|
+
fs33.mkdirSync(archiveDir, { recursive: true });
|
|
25909
|
+
fs33.renameSync(
|
|
25910
|
+
path41.resolve(dir, entry.filename),
|
|
25911
|
+
path41.resolve(archiveDir, entry.filename)
|
|
25487
25912
|
);
|
|
25488
25913
|
}
|
|
25489
25914
|
async function deleteSavedWorkflow(entry) {
|
|
@@ -25507,7 +25932,7 @@ async function deleteSavedWorkflow(entry) {
|
|
|
25507
25932
|
if (!entry.filename) {
|
|
25508
25933
|
throw new Error("Local workflow entry is missing filename.");
|
|
25509
25934
|
}
|
|
25510
|
-
|
|
25935
|
+
fs33.rmSync(path41.resolve(resolveSavedWorkflowsDir(), entry.filename), { force: true });
|
|
25511
25936
|
markWorkflowDeletedLocally(entry.workflowId);
|
|
25512
25937
|
}
|
|
25513
25938
|
async function loadSavedWorkflowDetail(entry) {
|
|
@@ -25526,7 +25951,7 @@ async function loadSavedWorkflowDetail(entry) {
|
|
|
25526
25951
|
};
|
|
25527
25952
|
}
|
|
25528
25953
|
const dir = resolveSavedWorkflowsDir();
|
|
25529
|
-
const content =
|
|
25954
|
+
const content = fs33.readFileSync(path41.resolve(dir, entry.filename), "utf-8");
|
|
25530
25955
|
const raw = JSON.parse(content);
|
|
25531
25956
|
return {
|
|
25532
25957
|
pipeline: raw.pipeline ?? raw,
|
|
@@ -26510,36 +26935,36 @@ import pc38 from "picocolors";
|
|
|
26510
26935
|
|
|
26511
26936
|
// src/runtime/agent-harness/auth-store.ts
|
|
26512
26937
|
init_home();
|
|
26513
|
-
import
|
|
26514
|
-
import
|
|
26938
|
+
import fs34 from "node:fs";
|
|
26939
|
+
import path42 from "node:path";
|
|
26515
26940
|
function resolveHarnessAuthDir() {
|
|
26516
|
-
return
|
|
26941
|
+
return path42.resolve(resolvePaperclipHomeDir(), "harness-auth");
|
|
26517
26942
|
}
|
|
26518
26943
|
function resolveHarnessAuthFile(harnessId) {
|
|
26519
|
-
return
|
|
26944
|
+
return path42.resolve(resolveHarnessAuthDir(), `${harnessId}.json`);
|
|
26520
26945
|
}
|
|
26521
26946
|
function normalizeSecret(value) {
|
|
26522
26947
|
const trimmed = value?.trim();
|
|
26523
26948
|
return trimmed && trimmed.length > 0 ? trimmed : void 0;
|
|
26524
26949
|
}
|
|
26525
26950
|
function ensureSecureDir(dirPath) {
|
|
26526
|
-
|
|
26951
|
+
fs34.mkdirSync(dirPath, { recursive: true });
|
|
26527
26952
|
try {
|
|
26528
|
-
|
|
26953
|
+
fs34.chmodSync(dirPath, 448);
|
|
26529
26954
|
} catch {
|
|
26530
26955
|
}
|
|
26531
26956
|
}
|
|
26532
26957
|
function ensureSecureFile(filePath) {
|
|
26533
26958
|
try {
|
|
26534
|
-
|
|
26959
|
+
fs34.chmodSync(filePath, 384);
|
|
26535
26960
|
} catch {
|
|
26536
26961
|
}
|
|
26537
26962
|
}
|
|
26538
26963
|
function readHarnessCredentials(harnessId) {
|
|
26539
26964
|
const filePath = resolveHarnessAuthFile(harnessId);
|
|
26540
|
-
if (!
|
|
26965
|
+
if (!fs34.existsSync(filePath)) return {};
|
|
26541
26966
|
try {
|
|
26542
|
-
const parsed = JSON.parse(
|
|
26967
|
+
const parsed = JSON.parse(fs34.readFileSync(filePath, "utf-8"));
|
|
26543
26968
|
const creds = {};
|
|
26544
26969
|
for (const [key, value] of Object.entries(parsed)) {
|
|
26545
26970
|
if (typeof value === "string" && value.trim().length > 0) {
|
|
@@ -26566,7 +26991,7 @@ function setHarnessCredential(harnessId, key, value) {
|
|
|
26566
26991
|
const dirPath = resolveHarnessAuthDir();
|
|
26567
26992
|
ensureSecureDir(dirPath);
|
|
26568
26993
|
const filePath = resolveHarnessAuthFile(harnessId);
|
|
26569
|
-
|
|
26994
|
+
fs34.writeFileSync(filePath, `${JSON.stringify(creds, null, 2)}
|
|
26570
26995
|
`, "utf-8");
|
|
26571
26996
|
ensureSecureFile(filePath);
|
|
26572
26997
|
}
|
|
@@ -26583,7 +27008,7 @@ function setHarnessCredentials(harnessId, updates) {
|
|
|
26583
27008
|
const dirPath = resolveHarnessAuthDir();
|
|
26584
27009
|
ensureSecureDir(dirPath);
|
|
26585
27010
|
const filePath = resolveHarnessAuthFile(harnessId);
|
|
26586
|
-
|
|
27011
|
+
fs34.writeFileSync(filePath, `${JSON.stringify(creds, null, 2)}
|
|
26587
27012
|
`, "utf-8");
|
|
26588
27013
|
ensureSecureFile(filePath);
|
|
26589
27014
|
}
|
|
@@ -26595,8 +27020,8 @@ function maskSecret(value) {
|
|
|
26595
27020
|
|
|
26596
27021
|
// src/runtime/open-agents/index.ts
|
|
26597
27022
|
init_home();
|
|
26598
|
-
import
|
|
26599
|
-
import
|
|
27023
|
+
import fs35 from "node:fs";
|
|
27024
|
+
import path43 from "node:path";
|
|
26600
27025
|
|
|
26601
27026
|
// src/runtime/open-agents/contract.ts
|
|
26602
27027
|
var DEFAULT_OPEN_AGENTS_CONFIG = {
|
|
@@ -26783,18 +27208,18 @@ var OpenAgentsBackendError = class extends Error {
|
|
|
26783
27208
|
|
|
26784
27209
|
// src/runtime/open-agents/index.ts
|
|
26785
27210
|
function resolveConfigPath3() {
|
|
26786
|
-
return
|
|
27211
|
+
return path43.resolve(resolvePaperclipHomeDir(), "open-agents", "config.json");
|
|
26787
27212
|
}
|
|
26788
27213
|
function readOpenAgentsConfig() {
|
|
26789
27214
|
const configPath = resolveConfigPath3();
|
|
26790
|
-
if (!
|
|
27215
|
+
if (!fs35.existsSync(configPath)) {
|
|
26791
27216
|
return {
|
|
26792
27217
|
...DEFAULT_OPEN_AGENTS_CONFIG,
|
|
26793
27218
|
apiKey: getHarnessCredential("open-agents", "apiKey")
|
|
26794
27219
|
};
|
|
26795
27220
|
}
|
|
26796
27221
|
try {
|
|
26797
|
-
const raw = JSON.parse(
|
|
27222
|
+
const raw = JSON.parse(fs35.readFileSync(configPath, "utf-8"));
|
|
26798
27223
|
const storedApiKey = getHarnessCredential("open-agents", "apiKey");
|
|
26799
27224
|
return {
|
|
26800
27225
|
backendType: validateBackendType(raw.backendType),
|
|
@@ -26815,13 +27240,13 @@ function readOpenAgentsConfig() {
|
|
|
26815
27240
|
}
|
|
26816
27241
|
function writeOpenAgentsConfig(config) {
|
|
26817
27242
|
const configPath = resolveConfigPath3();
|
|
26818
|
-
|
|
27243
|
+
fs35.mkdirSync(path43.dirname(configPath), { recursive: true });
|
|
26819
27244
|
const persisted = {
|
|
26820
27245
|
...config,
|
|
26821
27246
|
authMode: validateAuthMode(config.authMode),
|
|
26822
27247
|
apiKey: void 0
|
|
26823
27248
|
};
|
|
26824
|
-
|
|
27249
|
+
fs35.writeFileSync(configPath, `${JSON.stringify(persisted, null, 2)}
|
|
26825
27250
|
`, "utf-8");
|
|
26826
27251
|
setHarnessCredential("open-agents", "apiKey", config.apiKey);
|
|
26827
27252
|
}
|
|
@@ -27382,8 +27807,8 @@ import pc39 from "picocolors";
|
|
|
27382
27807
|
|
|
27383
27808
|
// src/runtime/qwen-code/index.ts
|
|
27384
27809
|
init_home();
|
|
27385
|
-
import
|
|
27386
|
-
import
|
|
27810
|
+
import fs36 from "node:fs";
|
|
27811
|
+
import path44 from "node:path";
|
|
27387
27812
|
|
|
27388
27813
|
// src/runtime/qwen-code/contract.ts
|
|
27389
27814
|
var QWEN_CODE_APPROVAL_MODES = [
|
|
@@ -27601,19 +28026,19 @@ function buildSetupGuidance(env) {
|
|
|
27601
28026
|
|
|
27602
28027
|
// src/runtime/qwen-code/index.ts
|
|
27603
28028
|
function resolveConfigPath4() {
|
|
27604
|
-
return
|
|
28029
|
+
return path44.resolve(resolvePaperclipHomeDir(), "qwen-code", "config.json");
|
|
27605
28030
|
}
|
|
27606
28031
|
function readQwenCodeConfig() {
|
|
27607
28032
|
const configPath = resolveConfigPath4();
|
|
27608
28033
|
const storedCredentials = readHarnessCredentials("qwen-code");
|
|
27609
|
-
if (!
|
|
28034
|
+
if (!fs36.existsSync(configPath)) {
|
|
27610
28035
|
return {
|
|
27611
28036
|
...DEFAULT_QWEN_CODE_CONFIG,
|
|
27612
28037
|
env: mergeHarnessEnv(DEFAULT_QWEN_CODE_CONFIG.env, storedCredentials)
|
|
27613
28038
|
};
|
|
27614
28039
|
}
|
|
27615
28040
|
try {
|
|
27616
|
-
const raw = JSON.parse(
|
|
28041
|
+
const raw = JSON.parse(fs36.readFileSync(configPath, "utf-8"));
|
|
27617
28042
|
return {
|
|
27618
28043
|
binaryPath: typeof raw.binaryPath === "string" ? raw.binaryPath : DEFAULT_QWEN_CODE_CONFIG.binaryPath,
|
|
27619
28044
|
defaultModel: typeof raw.defaultModel === "string" ? raw.defaultModel : DEFAULT_QWEN_CODE_CONFIG.defaultModel,
|
|
@@ -27635,7 +28060,7 @@ function readQwenCodeConfig() {
|
|
|
27635
28060
|
}
|
|
27636
28061
|
function writeQwenCodeConfig(config) {
|
|
27637
28062
|
const configPath = resolveConfigPath4();
|
|
27638
|
-
|
|
28063
|
+
fs36.mkdirSync(path44.dirname(configPath), { recursive: true });
|
|
27639
28064
|
const rawEnv = typeof config.env === "object" && config.env !== null ? config.env : {};
|
|
27640
28065
|
const credentialUpdates = {};
|
|
27641
28066
|
const publicEnv = {};
|
|
@@ -27647,7 +28072,7 @@ function writeQwenCodeConfig(config) {
|
|
|
27647
28072
|
publicEnv[key] = value;
|
|
27648
28073
|
}
|
|
27649
28074
|
setHarnessCredentials("qwen-code", credentialUpdates);
|
|
27650
|
-
|
|
28075
|
+
fs36.writeFileSync(
|
|
27651
28076
|
configPath,
|
|
27652
28077
|
`${JSON.stringify({ ...config, env: publicEnv }, null, 2)}
|
|
27653
28078
|
`,
|
|
@@ -27980,8 +28405,8 @@ import pc42 from "picocolors";
|
|
|
27980
28405
|
|
|
27981
28406
|
// src/status/probes.ts
|
|
27982
28407
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
27983
|
-
import
|
|
27984
|
-
import
|
|
28408
|
+
import fs37 from "node:fs";
|
|
28409
|
+
import path45 from "node:path";
|
|
27985
28410
|
var GITHUB_API = "https://api.github.com";
|
|
27986
28411
|
var NPM_REGISTRY = "https://registry.npmjs.org";
|
|
27987
28412
|
function isoNow() {
|
|
@@ -28146,7 +28571,7 @@ async function probeKitForksIndex(_timeoutMs) {
|
|
|
28146
28571
|
try {
|
|
28147
28572
|
const { resolveKitForksIndexPath: resolveKitForksIndexPath2 } = await Promise.resolve().then(() => (init_kit_forks_home(), kit_forks_home_exports));
|
|
28148
28573
|
const p33 = resolveKitForksIndexPath2();
|
|
28149
|
-
if (!
|
|
28574
|
+
if (!fs37.existsSync(p33)) {
|
|
28150
28575
|
return {
|
|
28151
28576
|
componentId: "kit-forks-index",
|
|
28152
28577
|
level: "operational",
|
|
@@ -28154,7 +28579,7 @@ async function probeKitForksIndex(_timeoutMs) {
|
|
|
28154
28579
|
lastCheckedAt: isoNow()
|
|
28155
28580
|
};
|
|
28156
28581
|
}
|
|
28157
|
-
const parsed = JSON.parse(
|
|
28582
|
+
const parsed = JSON.parse(fs37.readFileSync(p33, "utf8"));
|
|
28158
28583
|
const count = Array.isArray(parsed.entries) ? parsed.entries.length : 0;
|
|
28159
28584
|
return {
|
|
28160
28585
|
componentId: "kit-forks-index",
|
|
@@ -28223,10 +28648,10 @@ async function probeNode(_timeoutMs) {
|
|
|
28223
28648
|
};
|
|
28224
28649
|
}
|
|
28225
28650
|
async function probeReleaseBundleArtifacts(_timeoutMs) {
|
|
28226
|
-
const distPath =
|
|
28227
|
-
const installerPath =
|
|
28228
|
-
const distOk =
|
|
28229
|
-
const installerOk =
|
|
28651
|
+
const distPath = path45.resolve(process.cwd(), "cli/dist/index.js");
|
|
28652
|
+
const installerPath = path45.resolve(process.cwd(), "packages/create-growthub-local/bin/create-growthub-local.mjs");
|
|
28653
|
+
const distOk = fs37.existsSync(distPath);
|
|
28654
|
+
const installerOk = fs37.existsSync(installerPath);
|
|
28230
28655
|
const ok = distOk && installerOk;
|
|
28231
28656
|
return {
|
|
28232
28657
|
componentId: "release-bundle",
|
|
@@ -28773,7 +29198,7 @@ import pc44 from "picocolors";
|
|
|
28773
29198
|
|
|
28774
29199
|
// src/fleet/summary.ts
|
|
28775
29200
|
init_fork_registry();
|
|
28776
|
-
import
|
|
29201
|
+
import fs47 from "node:fs";
|
|
28777
29202
|
init_fork_policy();
|
|
28778
29203
|
init_fork_trace();
|
|
28779
29204
|
function classifyHealth(drift, pendingConfirmationJobs, lastJobStatus) {
|
|
@@ -28793,7 +29218,7 @@ var REMOTE_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
|
28793
29218
|
"conflict_encountered"
|
|
28794
29219
|
]);
|
|
28795
29220
|
function buildForkSummary(reg) {
|
|
28796
|
-
if (!
|
|
29221
|
+
if (!fs47.existsSync(reg.forkPath)) {
|
|
28797
29222
|
return {
|
|
28798
29223
|
forkId: reg.forkId,
|
|
28799
29224
|
kitId: reg.kitId,
|
|
@@ -29254,8 +29679,8 @@ async function fleetApprovals(opts) {
|
|
|
29254
29679
|
p30.log.message(
|
|
29255
29680
|
` \xB7 ${pc44.cyan(entry.jobId)} fork=${entry.forkLabel ?? entry.forkId} created=${entry.createdAt.slice(0, 19)}`
|
|
29256
29681
|
);
|
|
29257
|
-
for (const
|
|
29258
|
-
p30.log.message(` ${pc44.dim("awaits")} ${
|
|
29682
|
+
for (const path57 of entry.pendingPaths.slice(0, 6)) {
|
|
29683
|
+
p30.log.message(` ${pc44.dim("awaits")} ${path57}`);
|
|
29259
29684
|
}
|
|
29260
29685
|
if (entry.pendingPaths.length > 6) {
|
|
29261
29686
|
p30.log.message(` ${pc44.dim(`\u2026 +${entry.pendingPaths.length - 6} more`)}`);
|
|
@@ -29322,14 +29747,14 @@ init_banner();
|
|
|
29322
29747
|
init_home();
|
|
29323
29748
|
function resolveCliVersion() {
|
|
29324
29749
|
try {
|
|
29325
|
-
const moduleDir =
|
|
29750
|
+
const moduleDir = path56.dirname(fileURLToPath7(import.meta.url));
|
|
29326
29751
|
const candidates = [
|
|
29327
|
-
|
|
29328
|
-
|
|
29752
|
+
path56.resolve(moduleDir, "../package.json"),
|
|
29753
|
+
path56.resolve(moduleDir, "../../package.json")
|
|
29329
29754
|
];
|
|
29330
29755
|
for (const candidate of candidates) {
|
|
29331
|
-
if (!
|
|
29332
|
-
const parsed = JSON.parse(
|
|
29756
|
+
if (!fs49.existsSync(candidate)) continue;
|
|
29757
|
+
const parsed = JSON.parse(fs49.readFileSync(candidate, "utf8"));
|
|
29333
29758
|
if (parsed?.name === "@growthub/cli" && typeof parsed.version === "string") return parsed.version;
|
|
29334
29759
|
}
|
|
29335
29760
|
} catch {
|
|
@@ -29631,39 +30056,39 @@ User: ${prompt}` : prompt,
|
|
|
29631
30056
|
}
|
|
29632
30057
|
}
|
|
29633
30058
|
function resolveLocalThreadsDir() {
|
|
29634
|
-
return
|
|
30059
|
+
return path56.resolve(resolvePaperclipHomeDir(), "native-intelligence", "threads");
|
|
29635
30060
|
}
|
|
29636
30061
|
function loadOrCreateLocalThread() {
|
|
29637
30062
|
const dir = resolveLocalThreadsDir();
|
|
29638
|
-
|
|
29639
|
-
const activePath =
|
|
29640
|
-
if (
|
|
30063
|
+
fs49.mkdirSync(dir, { recursive: true });
|
|
30064
|
+
const activePath = path56.resolve(dir, "active-thread.json");
|
|
30065
|
+
if (fs49.existsSync(activePath)) {
|
|
29641
30066
|
try {
|
|
29642
|
-
const parsed = JSON.parse(
|
|
30067
|
+
const parsed = JSON.parse(fs49.readFileSync(activePath, "utf-8"));
|
|
29643
30068
|
const id2 = typeof parsed.id === "string" && parsed.id.length > 0 ? parsed.id : `thread-${Date.now()}`;
|
|
29644
|
-
const threadFile =
|
|
30069
|
+
const threadFile = path56.resolve(dir, `${id2}.json`);
|
|
29645
30070
|
const messages = Array.isArray(parsed.messages) ? parsed.messages : [];
|
|
29646
30071
|
return { id: id2, filePath: threadFile, messages };
|
|
29647
30072
|
} catch {
|
|
29648
30073
|
}
|
|
29649
30074
|
}
|
|
29650
30075
|
const id = `thread-${Date.now()}`;
|
|
29651
|
-
const filePath =
|
|
30076
|
+
const filePath = path56.resolve(dir, `${id}.json`);
|
|
29652
30077
|
const thread = { id, filePath, messages: [] };
|
|
29653
30078
|
saveLocalThread(thread);
|
|
29654
30079
|
return thread;
|
|
29655
30080
|
}
|
|
29656
30081
|
function saveLocalThread(thread) {
|
|
29657
30082
|
const dir = resolveLocalThreadsDir();
|
|
29658
|
-
|
|
29659
|
-
|
|
30083
|
+
fs49.mkdirSync(dir, { recursive: true });
|
|
30084
|
+
fs49.writeFileSync(
|
|
29660
30085
|
thread.filePath,
|
|
29661
30086
|
`${JSON.stringify({ id: thread.id, messages: thread.messages }, null, 2)}
|
|
29662
30087
|
`,
|
|
29663
30088
|
"utf-8"
|
|
29664
30089
|
);
|
|
29665
|
-
const activePath =
|
|
29666
|
-
|
|
30090
|
+
const activePath = path56.resolve(dir, "active-thread.json");
|
|
30091
|
+
fs49.writeFileSync(
|
|
29667
30092
|
activePath,
|
|
29668
30093
|
`${JSON.stringify({ id: thread.id, messages: thread.messages }, null, 2)}
|
|
29669
30094
|
`,
|
|
@@ -30206,12 +30631,12 @@ function isInstallerMode() {
|
|
|
30206
30631
|
}
|
|
30207
30632
|
function listLocalSurfaces() {
|
|
30208
30633
|
const homeDir = resolvePaperclipHomeDir();
|
|
30209
|
-
const instancesDir =
|
|
30210
|
-
if (!
|
|
30211
|
-
return
|
|
30634
|
+
const instancesDir = path56.resolve(homeDir, "instances");
|
|
30635
|
+
if (!fs49.existsSync(instancesDir)) return [];
|
|
30636
|
+
return fs49.readdirSync(instancesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => {
|
|
30212
30637
|
const instanceId = entry.name;
|
|
30213
|
-
const configPath =
|
|
30214
|
-
if (!
|
|
30638
|
+
const configPath = path56.resolve(instancesDir, instanceId, "config.json");
|
|
30639
|
+
if (!fs49.existsSync(configPath)) return null;
|
|
30215
30640
|
try {
|
|
30216
30641
|
const config = readConfig(configPath);
|
|
30217
30642
|
if (!config) return null;
|