@growthub/cli 0.6.8 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/shared-templates/marketing-frameworks/competitor-positioning.md +46 -0
- package/assets/shared-templates/marketing-frameworks/cro-7-dimensions.md +48 -0
- package/assets/shared-templates/marketing-frameworks/email-sequence-architecture.md +50 -0
- package/assets/shared-templates/marketing-frameworks/launch-playbook.md +62 -0
- package/assets/shared-templates/marketing-frameworks/product-marketing-context.md +62 -0
- package/assets/shared-templates/marketing-frameworks/seo-audit-eeat.md +44 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/.env.example +7 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/QUICKSTART.md +21 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/brands/NEW-CLIENT.md +3 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/brands/_template/brand-kit.md +7 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/brands/growthub/brand-kit.md +6 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/bundles/growthub-hyperframes-studio-v1.json +47 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/docs/hyperframes-discovery-path.md +12 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/docs/hyperframes-fork-integration.md +11 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/docs/provider-adapter-layer.md +9 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/examples/video-brief-sample.md +15 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/growthub-meta/README.md +3 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/growthub-meta/kit-standard.md +6 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/kit.json +97 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/output/README.md +5 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/output-standards.md +12 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/runtime-assumptions.md +6 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/setup/check-deps.sh +14 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/setup/clone-fork.sh +12 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/setup/verify-env.mjs +16 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/skills.md +12 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/templates/composition-spec.md +6 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/templates/qa-checklist.md +6 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/templates/render-plan.md +13 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/templates/video-brief.md +7 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/validation-checklist.md +6 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/workers/hyperframes-studio-operator/CLAUDE.md +18 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/.env.example +7 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/QUICKSTART.md +111 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/brands/_template/product-marketing-context.md +147 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/brands/growthub/product-marketing-context.md +145 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/bundles/growthub-marketing-skills-v1.json +43 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/docs/evaluation-frameworks.md +53 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/docs/fork-integration.md +43 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/docs/skill-dispatch-methodology.md +50 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/examples/email-sequence-sample.md +75 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/examples/landing-page-cro-sample.md +128 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/examples/seo-audit-sample.md +102 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/growthub-meta/README.md +5 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/growthub-meta/kit-standard.md +25 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/kit.json +87 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/output/README.md +32 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/output-standards.md +51 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/runtime-assumptions.md +46 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/setup/verify-env.mjs +81 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/skills.md +157 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/templates/competitor-analysis.md +81 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/templates/content-strategy-plan.md +75 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/templates/cro-audit-brief.md +138 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/templates/email-sequence-plan.md +80 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/templates/launch-checklist.md +83 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/templates/seo-audit-report.md +119 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/workers/marketing-operator/CLAUDE.md +198 -0
- package/dist/index.js +694 -268
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8015,8 +8015,8 @@ var init_onboard = __esm({
|
|
|
8015
8015
|
|
|
8016
8016
|
// src/client/http.ts
|
|
8017
8017
|
import { URL as URL2 } from "node:url";
|
|
8018
|
-
function buildUrl(apiBase,
|
|
8019
|
-
const normalizedPath =
|
|
8018
|
+
function buildUrl(apiBase, path57) {
|
|
8019
|
+
const normalizedPath = path57.startsWith("/") ? path57 : `/${path57}`;
|
|
8020
8020
|
const [pathname, query] = normalizedPath.split("?");
|
|
8021
8021
|
const url = new URL2(apiBase);
|
|
8022
8022
|
url.pathname = `${url.pathname.replace(/\/+$/, "")}${pathname}`;
|
|
@@ -8078,26 +8078,26 @@ var init_http = __esm({
|
|
|
8078
8078
|
this.runId = opts.runId?.trim() || void 0;
|
|
8079
8079
|
this.userId = opts.userId?.trim() || void 0;
|
|
8080
8080
|
}
|
|
8081
|
-
get(
|
|
8082
|
-
return this.request(
|
|
8081
|
+
get(path57, opts) {
|
|
8082
|
+
return this.request(path57, { method: "GET" }, opts);
|
|
8083
8083
|
}
|
|
8084
|
-
post(
|
|
8085
|
-
return this.request(
|
|
8084
|
+
post(path57, body, opts) {
|
|
8085
|
+
return this.request(path57, {
|
|
8086
8086
|
method: "POST",
|
|
8087
8087
|
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
8088
8088
|
}, opts);
|
|
8089
8089
|
}
|
|
8090
|
-
patch(
|
|
8091
|
-
return this.request(
|
|
8090
|
+
patch(path57, body, opts) {
|
|
8091
|
+
return this.request(path57, {
|
|
8092
8092
|
method: "PATCH",
|
|
8093
8093
|
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
8094
8094
|
}, opts);
|
|
8095
8095
|
}
|
|
8096
|
-
delete(
|
|
8097
|
-
return this.request(
|
|
8096
|
+
delete(path57, opts) {
|
|
8097
|
+
return this.request(path57, { method: "DELETE" }, opts);
|
|
8098
8098
|
}
|
|
8099
|
-
async request(
|
|
8100
|
-
const url = buildUrl(this.apiBase,
|
|
8099
|
+
async request(path57, init, opts) {
|
|
8100
|
+
const url = buildUrl(this.apiBase, path57);
|
|
8101
8101
|
const headers = {
|
|
8102
8102
|
accept: "application/json",
|
|
8103
8103
|
...toStringRecord(init.headers)
|
|
@@ -9622,9 +9622,9 @@ async function fetchHostedIntegrations(session) {
|
|
|
9622
9622
|
}
|
|
9623
9623
|
async function fetchHostedIntegrationCredential(session, providerId) {
|
|
9624
9624
|
const client = toApiClient2(session);
|
|
9625
|
-
const
|
|
9625
|
+
const path57 = `${DEFAULT_INTEGRATION_CREDENTIAL_PATH}&provider=${encodeURIComponent(providerId)}`;
|
|
9626
9626
|
try {
|
|
9627
|
-
return await client.get(
|
|
9627
|
+
return await client.get(path57, { ignoreNotFound: true });
|
|
9628
9628
|
} catch (err) {
|
|
9629
9629
|
if (err instanceof ApiRequestError && (err.status === 404 || err.status === 501)) {
|
|
9630
9630
|
throw new HostedEndpointUnavailableError(err.status, err.message);
|
|
@@ -10199,12 +10199,12 @@ var init_github = __esm({
|
|
|
10199
10199
|
});
|
|
10200
10200
|
|
|
10201
10201
|
// src/starter/init.ts
|
|
10202
|
-
import
|
|
10203
|
-
import
|
|
10202
|
+
import fs38 from "node:fs";
|
|
10203
|
+
import path46 from "node:path";
|
|
10204
10204
|
async function initStarterWorkspace(opts) {
|
|
10205
10205
|
const kitId = opts.kitId ?? DEFAULT_STARTER_KIT_ID;
|
|
10206
|
-
const absOut =
|
|
10207
|
-
if (
|
|
10206
|
+
const absOut = path46.resolve(opts.out);
|
|
10207
|
+
if (fs38.existsSync(absOut) && fs38.readdirSync(absOut).length > 0) {
|
|
10208
10208
|
throw new Error(`Destination ${absOut} already exists and is not empty.`);
|
|
10209
10209
|
}
|
|
10210
10210
|
const info = getBundledKitSourceInfo(kitId);
|
|
@@ -10213,7 +10213,7 @@ async function initStarterWorkspace(opts) {
|
|
|
10213
10213
|
forkPath: absOut,
|
|
10214
10214
|
kitId: info.id,
|
|
10215
10215
|
baseVersion: info.version,
|
|
10216
|
-
label: opts.name?.trim() ||
|
|
10216
|
+
label: opts.name?.trim() || path46.basename(absOut)
|
|
10217
10217
|
});
|
|
10218
10218
|
const policy = {
|
|
10219
10219
|
...makeDefaultKitForkPolicy(),
|
|
@@ -10306,8 +10306,8 @@ var init_types2 = __esm({
|
|
|
10306
10306
|
});
|
|
10307
10307
|
|
|
10308
10308
|
// src/starter/source-import/github-source.ts
|
|
10309
|
-
import
|
|
10310
|
-
import
|
|
10309
|
+
import fs39 from "node:fs";
|
|
10310
|
+
import path47 from "node:path";
|
|
10311
10311
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
10312
10312
|
function baseHeaders() {
|
|
10313
10313
|
return {
|
|
@@ -10438,12 +10438,12 @@ function cloneGithubRepo(input) {
|
|
|
10438
10438
|
if (!gitAvailable()) {
|
|
10439
10439
|
throw new Error("`git` is not available on PATH \u2014 cannot clone.");
|
|
10440
10440
|
}
|
|
10441
|
-
if (
|
|
10441
|
+
if (fs39.existsSync(input.destination)) {
|
|
10442
10442
|
throw new Error(`Clone destination already exists: ${input.destination}`);
|
|
10443
10443
|
}
|
|
10444
10444
|
const cloneUrl = input.token ? buildTokenCloneUrl(input.probe.repo, input.token) : input.probe.cloneUrl;
|
|
10445
|
-
const parent =
|
|
10446
|
-
|
|
10445
|
+
const parent = path47.dirname(input.destination);
|
|
10446
|
+
fs39.mkdirSync(parent, { recursive: true });
|
|
10447
10447
|
const depth = input.depth ?? 1;
|
|
10448
10448
|
const branch = input.branch ?? input.probe.defaultBranch;
|
|
10449
10449
|
const args = ["clone"];
|
|
@@ -10470,17 +10470,17 @@ function cloneGithubRepo(input) {
|
|
|
10470
10470
|
function narrowToSubdirectory(rootDir, subdirectory) {
|
|
10471
10471
|
const normalizedSub = subdirectory.replace(/^\/+|\/+$/g, "");
|
|
10472
10472
|
if (!normalizedSub) return;
|
|
10473
|
-
const abs =
|
|
10474
|
-
if (!
|
|
10473
|
+
const abs = path47.resolve(rootDir, normalizedSub);
|
|
10474
|
+
if (!fs39.existsSync(abs) || !fs39.statSync(abs).isDirectory()) {
|
|
10475
10475
|
throw new Error(`Subdirectory not found in cloned repo: ${subdirectory}`);
|
|
10476
10476
|
}
|
|
10477
|
-
const tmp =
|
|
10478
|
-
|
|
10479
|
-
`.${
|
|
10477
|
+
const tmp = path47.resolve(
|
|
10478
|
+
path47.dirname(rootDir),
|
|
10479
|
+
`.${path47.basename(rootDir)}-narrow-${Date.now().toString(36)}`
|
|
10480
10480
|
);
|
|
10481
|
-
|
|
10482
|
-
|
|
10483
|
-
|
|
10481
|
+
fs39.renameSync(abs, tmp);
|
|
10482
|
+
fs39.rmSync(rootDir, { recursive: true, force: true });
|
|
10483
|
+
fs39.renameSync(tmp, rootDir);
|
|
10484
10484
|
}
|
|
10485
10485
|
var GITHUB_API_BASE2;
|
|
10486
10486
|
var init_github_source = __esm({
|
|
@@ -10494,9 +10494,9 @@ var init_github_source = __esm({
|
|
|
10494
10494
|
});
|
|
10495
10495
|
|
|
10496
10496
|
// src/starter/source-import/skills-source.ts
|
|
10497
|
-
import
|
|
10498
|
-
import
|
|
10499
|
-
import
|
|
10497
|
+
import fs40 from "node:fs";
|
|
10498
|
+
import os10 from "node:os";
|
|
10499
|
+
import path48 from "node:path";
|
|
10500
10500
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
10501
10501
|
function resolveBase() {
|
|
10502
10502
|
const raw = process.env.SKILLS_SH_BASE?.trim();
|
|
@@ -10762,9 +10762,9 @@ async function probeSkillsSource(input) {
|
|
|
10762
10762
|
};
|
|
10763
10763
|
}
|
|
10764
10764
|
function assertInsidePayloadRoot(root, candidate) {
|
|
10765
|
-
const abs =
|
|
10766
|
-
const rootAbs =
|
|
10767
|
-
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) {
|
|
10768
10768
|
throw new Error(`Refusing to write outside payload root: ${candidate}`);
|
|
10769
10769
|
}
|
|
10770
10770
|
}
|
|
@@ -10780,24 +10780,24 @@ function runGit3(args, cwd) {
|
|
|
10780
10780
|
};
|
|
10781
10781
|
}
|
|
10782
10782
|
function skillDirectoryMatches(dir, skillSlug) {
|
|
10783
|
-
const skillFile =
|
|
10784
|
-
if (!
|
|
10783
|
+
const skillFile = path48.resolve(dir, "SKILL.md");
|
|
10784
|
+
if (!fs40.existsSync(skillFile) || !fs40.statSync(skillFile).isFile()) {
|
|
10785
10785
|
return false;
|
|
10786
10786
|
}
|
|
10787
|
-
if (
|
|
10787
|
+
if (path48.basename(dir) === skillSlug) {
|
|
10788
10788
|
return true;
|
|
10789
10789
|
}
|
|
10790
|
-
const content =
|
|
10790
|
+
const content = fs40.readFileSync(skillFile, "utf8");
|
|
10791
10791
|
const nameMatch = content.match(/(?:^|\n)name:\s*["']?([A-Za-z0-9._:-]+)["']?\s*(?:\n|$)/i);
|
|
10792
10792
|
return nameMatch?.[1] === skillSlug;
|
|
10793
10793
|
}
|
|
10794
10794
|
function locateSkillDirectory(root, skillSlug) {
|
|
10795
10795
|
const preferred = [
|
|
10796
|
-
|
|
10797
|
-
|
|
10796
|
+
path48.resolve(root, "skills", skillSlug),
|
|
10797
|
+
path48.resolve(root, skillSlug)
|
|
10798
10798
|
];
|
|
10799
10799
|
for (const candidate of preferred) {
|
|
10800
|
-
if (
|
|
10800
|
+
if (fs40.existsSync(candidate) && fs40.statSync(candidate).isDirectory() && skillDirectoryMatches(candidate, skillSlug)) {
|
|
10801
10801
|
return candidate;
|
|
10802
10802
|
}
|
|
10803
10803
|
}
|
|
@@ -10807,12 +10807,12 @@ function locateSkillDirectory(root, skillSlug) {
|
|
|
10807
10807
|
if (skillDirectoryMatches(current, skillSlug)) {
|
|
10808
10808
|
return current;
|
|
10809
10809
|
}
|
|
10810
|
-
for (const entry of
|
|
10810
|
+
for (const entry of fs40.readdirSync(current, { withFileTypes: true })) {
|
|
10811
10811
|
if (!entry.isDirectory()) continue;
|
|
10812
10812
|
if ([".git", "node_modules", ".next", "dist", "build", "coverage"].includes(entry.name)) {
|
|
10813
10813
|
continue;
|
|
10814
10814
|
}
|
|
10815
|
-
queue.push(
|
|
10815
|
+
queue.push(path48.resolve(current, entry.name));
|
|
10816
10816
|
}
|
|
10817
10817
|
}
|
|
10818
10818
|
return null;
|
|
@@ -10822,18 +10822,18 @@ function copySkillTree(sourceDir, destination) {
|
|
|
10822
10822
|
const stack = [{ from: sourceDir, to: destination }];
|
|
10823
10823
|
while (stack.length > 0) {
|
|
10824
10824
|
const current = stack.pop();
|
|
10825
|
-
|
|
10826
|
-
for (const entry of
|
|
10827
|
-
const fromPath =
|
|
10828
|
-
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);
|
|
10829
10829
|
assertInsidePayloadRoot(destination, toPath);
|
|
10830
10830
|
if (entry.isDirectory()) {
|
|
10831
10831
|
stack.push({ from: fromPath, to: toPath });
|
|
10832
10832
|
continue;
|
|
10833
10833
|
}
|
|
10834
|
-
const data =
|
|
10835
|
-
|
|
10836
|
-
|
|
10834
|
+
const data = fs40.readFileSync(fromPath);
|
|
10835
|
+
fs40.mkdirSync(path48.dirname(toPath), { recursive: true });
|
|
10836
|
+
fs40.writeFileSync(toPath, data, { mode: 420 });
|
|
10837
10837
|
written += 1;
|
|
10838
10838
|
}
|
|
10839
10839
|
}
|
|
@@ -10844,7 +10844,7 @@ async function fetchSkillPayload(input) {
|
|
|
10844
10844
|
if (!gitAvailable()) {
|
|
10845
10845
|
throw new Error("`git` is not available on PATH \u2014 cannot materialize a skills.sh payload.");
|
|
10846
10846
|
}
|
|
10847
|
-
if (
|
|
10847
|
+
if (fs40.existsSync(destination)) {
|
|
10848
10848
|
throw new Error(`Skill payload destination already exists: ${destination}`);
|
|
10849
10849
|
}
|
|
10850
10850
|
const repoSource = probe.repoUrl ?? (probe.repository ? `https://github.com/${probe.repository}` : void 0);
|
|
@@ -10852,11 +10852,11 @@ async function fetchSkillPayload(input) {
|
|
|
10852
10852
|
if (!repoSource || !skillSlug) {
|
|
10853
10853
|
throw new Error(`Skill '${probe.skillId}' is missing repository metadata \u2014 cannot materialize payload.`);
|
|
10854
10854
|
}
|
|
10855
|
-
const cloneRoot =
|
|
10856
|
-
|
|
10855
|
+
const cloneRoot = fs40.mkdtempSync(
|
|
10856
|
+
path48.join(os10.tmpdir(), "growthub-skills-source-")
|
|
10857
10857
|
);
|
|
10858
10858
|
try {
|
|
10859
|
-
const cloneRes = runGit3(["clone", "--depth", "1", repoSource, cloneRoot],
|
|
10859
|
+
const cloneRes = runGit3(["clone", "--depth", "1", repoSource, cloneRoot], path48.dirname(cloneRoot));
|
|
10860
10860
|
if (!cloneRes.ok) {
|
|
10861
10861
|
throw new Error(`git clone failed: ${cloneRes.stderr || "unable to clone skill repository"}`);
|
|
10862
10862
|
}
|
|
@@ -10869,7 +10869,7 @@ async function fetchSkillPayload(input) {
|
|
|
10869
10869
|
const fileCount = copySkillTree(skillDir, destination);
|
|
10870
10870
|
return { destination, fileCount };
|
|
10871
10871
|
} finally {
|
|
10872
|
-
|
|
10872
|
+
fs40.rmSync(cloneRoot, { recursive: true, force: true });
|
|
10873
10873
|
}
|
|
10874
10874
|
}
|
|
10875
10875
|
var DEFAULT_BASE, COMMENT_PATTERN;
|
|
@@ -10883,13 +10883,13 @@ var init_skills_source = __esm({
|
|
|
10883
10883
|
});
|
|
10884
10884
|
|
|
10885
10885
|
// src/starter/source-import/detect.ts
|
|
10886
|
-
import
|
|
10887
|
-
import
|
|
10886
|
+
import fs41 from "node:fs";
|
|
10887
|
+
import path49 from "node:path";
|
|
10888
10888
|
function safeReadPackageJson(dir) {
|
|
10889
|
-
const p33 =
|
|
10890
|
-
if (!
|
|
10889
|
+
const p33 = path49.resolve(dir, "package.json");
|
|
10890
|
+
if (!fs41.existsSync(p33)) return null;
|
|
10891
10891
|
try {
|
|
10892
|
-
return JSON.parse(
|
|
10892
|
+
return JSON.parse(fs41.readFileSync(p33, "utf8"));
|
|
10893
10893
|
} catch {
|
|
10894
10894
|
return null;
|
|
10895
10895
|
}
|
|
@@ -10899,10 +10899,10 @@ function detectPackageManager(dir, pkg) {
|
|
|
10899
10899
|
if (pkg?.packageManager?.startsWith("yarn")) return "yarn";
|
|
10900
10900
|
if (pkg?.packageManager?.startsWith("npm")) return "npm";
|
|
10901
10901
|
if (pkg?.packageManager?.startsWith("bun")) return "bun";
|
|
10902
|
-
if (
|
|
10903
|
-
if (
|
|
10904
|
-
if (
|
|
10905
|
-
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";
|
|
10906
10906
|
return "unknown";
|
|
10907
10907
|
}
|
|
10908
10908
|
function collectDeps(pkg) {
|
|
@@ -10916,12 +10916,12 @@ function collectDeps(pkg) {
|
|
|
10916
10916
|
}
|
|
10917
10917
|
function looksLikeSkillPayload(rootDir) {
|
|
10918
10918
|
const markers = ["SKILL.md", "skill.md", "skill.json", "skill.yml", "skill.yaml", "prompt.md"];
|
|
10919
|
-
return markers.some((name) =>
|
|
10919
|
+
return markers.some((name) => fs41.existsSync(path49.resolve(rootDir, name)));
|
|
10920
10920
|
}
|
|
10921
10921
|
function detectFramework(rootDir, pkg) {
|
|
10922
10922
|
if (!pkg) {
|
|
10923
10923
|
if (looksLikeSkillPayload(rootDir)) return "skill";
|
|
10924
|
-
if (
|
|
10924
|
+
if (fs41.existsSync(path49.resolve(rootDir, "docs"))) return "docs";
|
|
10925
10925
|
return "unknown";
|
|
10926
10926
|
}
|
|
10927
10927
|
const deps = collectDeps(pkg);
|
|
@@ -10930,8 +10930,8 @@ function detectFramework(rootDir, pkg) {
|
|
|
10930
10930
|
"vite.config.ts",
|
|
10931
10931
|
"vite.config.mjs",
|
|
10932
10932
|
"vite.config.cjs"
|
|
10933
|
-
].some((name) =>
|
|
10934
|
-
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"))) {
|
|
10935
10935
|
return "next";
|
|
10936
10936
|
}
|
|
10937
10937
|
if (deps.has("vite") || hasViteConfig) return "vite";
|
|
@@ -10957,15 +10957,15 @@ function pickScripts(pkg) {
|
|
|
10957
10957
|
return out;
|
|
10958
10958
|
}
|
|
10959
10959
|
function listEnvFiles(dir) {
|
|
10960
|
-
if (!
|
|
10961
|
-
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");
|
|
10962
10962
|
}
|
|
10963
10963
|
function findAppRoot(rootDir, pkg) {
|
|
10964
10964
|
if (pkg) return ".";
|
|
10965
10965
|
const candidates = ["app", "src", "apps", "packages"];
|
|
10966
10966
|
for (const candidate of candidates) {
|
|
10967
|
-
const abs =
|
|
10968
|
-
if (
|
|
10967
|
+
const abs = path49.resolve(rootDir, candidate);
|
|
10968
|
+
if (fs41.existsSync(abs) && fs41.statSync(abs).isDirectory()) {
|
|
10969
10969
|
const child = safeReadPackageJson(abs);
|
|
10970
10970
|
if (child) return candidate;
|
|
10971
10971
|
}
|
|
@@ -10981,12 +10981,12 @@ function computeConfidence(framework, manager, pkg) {
|
|
|
10981
10981
|
return Math.min(1, Number(score.toFixed(2)));
|
|
10982
10982
|
}
|
|
10983
10983
|
function detectSourceShape(rootDir) {
|
|
10984
|
-
if (!
|
|
10984
|
+
if (!fs41.existsSync(rootDir) || !fs41.statSync(rootDir).isDirectory()) {
|
|
10985
10985
|
throw new Error(`Detection target is not a directory: ${rootDir}`);
|
|
10986
10986
|
}
|
|
10987
10987
|
const rootPkg = safeReadPackageJson(rootDir);
|
|
10988
10988
|
const appRootRel = findAppRoot(rootDir, rootPkg);
|
|
10989
|
-
const appRootAbs =
|
|
10989
|
+
const appRootAbs = path49.resolve(rootDir, appRootRel);
|
|
10990
10990
|
const appPkg = appRootRel === "." ? rootPkg : safeReadPackageJson(appRootAbs);
|
|
10991
10991
|
const framework = detectFramework(appRootAbs, appPkg ?? rootPkg);
|
|
10992
10992
|
const packageManager = detectPackageManager(rootDir, rootPkg ?? appPkg);
|
|
@@ -11026,18 +11026,18 @@ var init_detect = __esm({
|
|
|
11026
11026
|
});
|
|
11027
11027
|
|
|
11028
11028
|
// src/starter/source-import/security.ts
|
|
11029
|
-
import
|
|
11030
|
-
import
|
|
11029
|
+
import fs42 from "node:fs";
|
|
11030
|
+
import path50 from "node:path";
|
|
11031
11031
|
function isLikelyTextFile(filename) {
|
|
11032
|
-
const ext =
|
|
11032
|
+
const ext = path50.extname(filename).toLowerCase();
|
|
11033
11033
|
if (!ext) return true;
|
|
11034
11034
|
return TEXT_EXTENSIONS.has(ext);
|
|
11035
11035
|
}
|
|
11036
11036
|
function isSuspiciousBinary(filename) {
|
|
11037
|
-
return SUSPICIOUS_BINARY_EXTENSIONS.has(
|
|
11037
|
+
return SUSPICIOUS_BINARY_EXTENSIONS.has(path50.extname(filename).toLowerCase());
|
|
11038
11038
|
}
|
|
11039
11039
|
function isUnexpectedArchive(filename) {
|
|
11040
|
-
const ext =
|
|
11040
|
+
const ext = path50.extname(filename).toLowerCase();
|
|
11041
11041
|
return ARCHIVE_EXTENSIONS.has(ext) || filename.toLowerCase().endsWith(".tar.gz");
|
|
11042
11042
|
}
|
|
11043
11043
|
function shortExcerpt(line) {
|
|
@@ -11092,19 +11092,19 @@ function walkPayload(root, onFile, limits) {
|
|
|
11092
11092
|
if (!current) break;
|
|
11093
11093
|
let entries;
|
|
11094
11094
|
try {
|
|
11095
|
-
entries =
|
|
11095
|
+
entries = fs42.readdirSync(current, { withFileTypes: true });
|
|
11096
11096
|
} catch {
|
|
11097
11097
|
continue;
|
|
11098
11098
|
}
|
|
11099
11099
|
for (const entry of entries) {
|
|
11100
|
-
const abs =
|
|
11100
|
+
const abs = path50.resolve(current, entry.name);
|
|
11101
11101
|
if (entry.isDirectory()) {
|
|
11102
11102
|
if (entry.name === ".git" || entry.name === "node_modules") continue;
|
|
11103
11103
|
stack.push(abs);
|
|
11104
11104
|
continue;
|
|
11105
11105
|
}
|
|
11106
11106
|
if (!entry.isFile()) continue;
|
|
11107
|
-
const rel =
|
|
11107
|
+
const rel = path50.relative(root, abs);
|
|
11108
11108
|
onFile(abs, rel);
|
|
11109
11109
|
visited += 1;
|
|
11110
11110
|
if (visited >= limits.maxFiles) break;
|
|
@@ -11114,7 +11114,7 @@ function walkPayload(root, onFile, limits) {
|
|
|
11114
11114
|
}
|
|
11115
11115
|
function inspectSourcePayload(input) {
|
|
11116
11116
|
const { payloadRoot } = input;
|
|
11117
|
-
if (!
|
|
11117
|
+
if (!fs42.existsSync(payloadRoot) || !fs42.statSync(payloadRoot).isDirectory()) {
|
|
11118
11118
|
throw new Error(`Inspection target is not a directory: ${payloadRoot}`);
|
|
11119
11119
|
}
|
|
11120
11120
|
const findings = [];
|
|
@@ -11124,7 +11124,7 @@ function inspectSourcePayload(input) {
|
|
|
11124
11124
|
(abs, rel) => {
|
|
11125
11125
|
let size = 0;
|
|
11126
11126
|
try {
|
|
11127
|
-
size =
|
|
11127
|
+
size = fs42.statSync(abs).size;
|
|
11128
11128
|
} catch {
|
|
11129
11129
|
return;
|
|
11130
11130
|
}
|
|
@@ -11133,7 +11133,7 @@ function inspectSourcePayload(input) {
|
|
|
11133
11133
|
category: "suspicious-binary",
|
|
11134
11134
|
severity: "high-risk",
|
|
11135
11135
|
path: rel,
|
|
11136
|
-
message: `Payload ships a precompiled binary (${
|
|
11136
|
+
message: `Payload ships a precompiled binary (${path50.extname(rel)}). Review provenance before use.`
|
|
11137
11137
|
});
|
|
11138
11138
|
return;
|
|
11139
11139
|
}
|
|
@@ -11142,7 +11142,7 @@ function inspectSourcePayload(input) {
|
|
|
11142
11142
|
category: "unexpected-archive",
|
|
11143
11143
|
severity: "caution",
|
|
11144
11144
|
path: rel,
|
|
11145
|
-
message: `Payload ships an archive (${
|
|
11145
|
+
message: `Payload ships an archive (${path50.extname(rel)}) \u2014 expand and review contents before use.`
|
|
11146
11146
|
});
|
|
11147
11147
|
return;
|
|
11148
11148
|
}
|
|
@@ -11150,12 +11150,12 @@ function inspectSourcePayload(input) {
|
|
|
11150
11150
|
if (bytesInspected + Math.min(size, MAX_BYTES_PER_FILE) > MAX_TOTAL_BYTES) return;
|
|
11151
11151
|
let buf;
|
|
11152
11152
|
try {
|
|
11153
|
-
const handle =
|
|
11153
|
+
const handle = fs42.openSync(abs, "r");
|
|
11154
11154
|
try {
|
|
11155
11155
|
buf = Buffer.alloc(Math.min(size, MAX_BYTES_PER_FILE));
|
|
11156
|
-
|
|
11156
|
+
fs42.readSync(handle, buf, 0, buf.length, 0);
|
|
11157
11157
|
} finally {
|
|
11158
|
-
|
|
11158
|
+
fs42.closeSync(handle);
|
|
11159
11159
|
}
|
|
11160
11160
|
} catch {
|
|
11161
11161
|
return;
|
|
@@ -11364,18 +11364,18 @@ var init_security = __esm({
|
|
|
11364
11364
|
});
|
|
11365
11365
|
|
|
11366
11366
|
// src/starter/source-import/plan.ts
|
|
11367
|
-
import
|
|
11368
|
-
import
|
|
11367
|
+
import fs43 from "node:fs";
|
|
11368
|
+
import path51 from "node:path";
|
|
11369
11369
|
function generateImportId() {
|
|
11370
11370
|
return `si-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`;
|
|
11371
11371
|
}
|
|
11372
11372
|
function destinationState(absDest) {
|
|
11373
|
-
if (!
|
|
11374
|
-
const stats =
|
|
11373
|
+
if (!fs43.existsSync(absDest)) return { exists: false, nonEmpty: false };
|
|
11374
|
+
const stats = fs43.statSync(absDest);
|
|
11375
11375
|
if (!stats.isDirectory()) {
|
|
11376
11376
|
throw new Error(`Destination is not a directory: ${absDest}`);
|
|
11377
11377
|
}
|
|
11378
|
-
const entries =
|
|
11378
|
+
const entries = fs43.readdirSync(absDest);
|
|
11379
11379
|
return { exists: true, nonEmpty: entries.length > 0 };
|
|
11380
11380
|
}
|
|
11381
11381
|
function describeSource(probe) {
|
|
@@ -11385,7 +11385,7 @@ function describeSource(probe) {
|
|
|
11385
11385
|
return `skill ${probe.skillId}@${probe.version} (skills.sh)`;
|
|
11386
11386
|
}
|
|
11387
11387
|
function buildSourceImportPlan(input) {
|
|
11388
|
-
const absDest =
|
|
11388
|
+
const absDest = path51.resolve(input.destination);
|
|
11389
11389
|
const state = destinationState(absDest);
|
|
11390
11390
|
const payloadPath = "imported";
|
|
11391
11391
|
const warnings = [...input.probe.warnings];
|
|
@@ -11498,8 +11498,8 @@ var init_plan = __esm({
|
|
|
11498
11498
|
});
|
|
11499
11499
|
|
|
11500
11500
|
// src/starter/source-import/summarize.ts
|
|
11501
|
-
import
|
|
11502
|
-
import
|
|
11501
|
+
import fs44 from "node:fs";
|
|
11502
|
+
import path52 from "node:path";
|
|
11503
11503
|
function sourceHeading(manifest) {
|
|
11504
11504
|
const src = manifest.source;
|
|
11505
11505
|
if (src.kind === "github-repo") {
|
|
@@ -11554,7 +11554,7 @@ function nextStepsSection(manifest) {
|
|
|
11554
11554
|
}
|
|
11555
11555
|
function writeImportSummary(input) {
|
|
11556
11556
|
const { forkPath, summaryRelativePath, manifest } = input;
|
|
11557
|
-
const summaryPath =
|
|
11557
|
+
const summaryPath = path52.resolve(forkPath, summaryRelativePath);
|
|
11558
11558
|
const body = [
|
|
11559
11559
|
`# Source Import Summary`,
|
|
11560
11560
|
``,
|
|
@@ -11584,8 +11584,8 @@ function writeImportSummary(input) {
|
|
|
11584
11584
|
`Generated by the Growthub Source Import Agent. Canonical manifest lives at \`.growthub-fork/source-import.json\`.`,
|
|
11585
11585
|
``
|
|
11586
11586
|
].join("\n");
|
|
11587
|
-
|
|
11588
|
-
|
|
11587
|
+
fs44.mkdirSync(path52.dirname(summaryPath), { recursive: true });
|
|
11588
|
+
fs44.writeFileSync(summaryPath, body, "utf8");
|
|
11589
11589
|
return summaryPath;
|
|
11590
11590
|
}
|
|
11591
11591
|
var init_summarize = __esm({
|
|
@@ -11595,16 +11595,16 @@ var init_summarize = __esm({
|
|
|
11595
11595
|
});
|
|
11596
11596
|
|
|
11597
11597
|
// src/starter/source-import/materialize.ts
|
|
11598
|
-
import
|
|
11599
|
-
import
|
|
11600
|
-
import
|
|
11598
|
+
import fs45 from "node:fs";
|
|
11599
|
+
import os11 from "node:os";
|
|
11600
|
+
import path53 from "node:path";
|
|
11601
11601
|
function resolveSourceKind(probe) {
|
|
11602
11602
|
return probe.kind === "github-repo" ? "github-repo" : "skills-skill";
|
|
11603
11603
|
}
|
|
11604
11604
|
function stagingDirFor(forkPath) {
|
|
11605
|
-
return
|
|
11606
|
-
|
|
11607
|
-
`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)}`
|
|
11608
11608
|
);
|
|
11609
11609
|
}
|
|
11610
11610
|
async function fetchPayload(probe, stagingDir, opts) {
|
|
@@ -11636,18 +11636,18 @@ async function fetchPayload(probe, stagingDir, opts) {
|
|
|
11636
11636
|
return { payloadRoot: stagingDir };
|
|
11637
11637
|
}
|
|
11638
11638
|
function movePayloadIntoFork(payloadRoot, forkPath, payloadRelativePath) {
|
|
11639
|
-
const target =
|
|
11640
|
-
if (
|
|
11641
|
-
|
|
11639
|
+
const target = path53.resolve(forkPath, payloadRelativePath);
|
|
11640
|
+
if (fs45.existsSync(target)) {
|
|
11641
|
+
fs45.rmSync(target, { recursive: true, force: true });
|
|
11642
11642
|
}
|
|
11643
|
-
|
|
11644
|
-
|
|
11643
|
+
fs45.mkdirSync(path53.dirname(target), { recursive: true });
|
|
11644
|
+
fs45.renameSync(payloadRoot, target);
|
|
11645
11645
|
return target;
|
|
11646
11646
|
}
|
|
11647
11647
|
function writeManifest(forkPath, manifest) {
|
|
11648
|
-
const p33 =
|
|
11649
|
-
|
|
11650
|
-
|
|
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");
|
|
11651
11651
|
return p33;
|
|
11652
11652
|
}
|
|
11653
11653
|
function assertConfirmationsSatisfied(plan, confirmations) {
|
|
@@ -11687,7 +11687,7 @@ async function materializeImportPlan(input) {
|
|
|
11687
11687
|
requireSkillAcknowledgement: sourceKind === "skills-skill"
|
|
11688
11688
|
});
|
|
11689
11689
|
if (security.blocked) {
|
|
11690
|
-
|
|
11690
|
+
fs45.rmSync(fetchResult.payloadRoot, { recursive: true, force: true });
|
|
11691
11691
|
throw new Error(
|
|
11692
11692
|
`Security inspection blocked the fetched payload: ${security.summaryLines[0] ?? "blocking finding"}`
|
|
11693
11693
|
);
|
|
@@ -11818,26 +11818,26 @@ var init_materialize = __esm({
|
|
|
11818
11818
|
});
|
|
11819
11819
|
|
|
11820
11820
|
// src/starter/source-import/agent.ts
|
|
11821
|
-
import
|
|
11822
|
-
import
|
|
11821
|
+
import fs46 from "node:fs";
|
|
11822
|
+
import path54 from "node:path";
|
|
11823
11823
|
function resolveJobsDir() {
|
|
11824
|
-
return
|
|
11824
|
+
return path54.resolve(resolveKitForksHomeDir(), "source-import-jobs");
|
|
11825
11825
|
}
|
|
11826
11826
|
function resolveJobPath2(jobId) {
|
|
11827
|
-
return
|
|
11827
|
+
return path54.resolve(resolveJobsDir(), `${jobId}.json`);
|
|
11828
11828
|
}
|
|
11829
11829
|
function generateJobId2() {
|
|
11830
11830
|
return `sij-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`;
|
|
11831
11831
|
}
|
|
11832
11832
|
function writeJob2(job) {
|
|
11833
11833
|
const p33 = resolveJobPath2(job.jobId);
|
|
11834
|
-
|
|
11835
|
-
|
|
11834
|
+
fs46.mkdirSync(path54.dirname(p33), { recursive: true });
|
|
11835
|
+
fs46.writeFileSync(p33, JSON.stringify(job, null, 2) + "\n", "utf8");
|
|
11836
11836
|
}
|
|
11837
11837
|
function readJobFile(p33) {
|
|
11838
|
-
if (!
|
|
11838
|
+
if (!fs46.existsSync(p33)) return null;
|
|
11839
11839
|
try {
|
|
11840
|
-
return JSON.parse(
|
|
11840
|
+
return JSON.parse(fs46.readFileSync(p33, "utf8"));
|
|
11841
11841
|
} catch {
|
|
11842
11842
|
return null;
|
|
11843
11843
|
}
|
|
@@ -11847,7 +11847,7 @@ function patchJob2(jobId, status, patch = {}) {
|
|
|
11847
11847
|
const job = readJobFile(p33);
|
|
11848
11848
|
if (!job) return null;
|
|
11849
11849
|
const updated = { ...job, ...patch, status };
|
|
11850
|
-
|
|
11850
|
+
fs46.writeFileSync(p33, JSON.stringify(updated, null, 2) + "\n", "utf8");
|
|
11851
11851
|
return updated;
|
|
11852
11852
|
}
|
|
11853
11853
|
function getSourceImportJob(jobId) {
|
|
@@ -11866,7 +11866,7 @@ async function probeAndPlan(input, destination) {
|
|
|
11866
11866
|
}
|
|
11867
11867
|
async function runSourceImportJob(input) {
|
|
11868
11868
|
const jobId = generateJobId2();
|
|
11869
|
-
const destination =
|
|
11869
|
+
const destination = path54.resolve(input.out);
|
|
11870
11870
|
const sourceKind = input.source.kind;
|
|
11871
11871
|
const initial = {
|
|
11872
11872
|
jobId,
|
|
@@ -12023,8 +12023,8 @@ __export(source_import_discovery_exports, {
|
|
|
12023
12023
|
});
|
|
12024
12024
|
import * as p31 from "@clack/prompts";
|
|
12025
12025
|
import pc45 from "picocolors";
|
|
12026
|
-
import
|
|
12027
|
-
import
|
|
12026
|
+
import fs48 from "node:fs";
|
|
12027
|
+
import path55 from "node:path";
|
|
12028
12028
|
import { pathToFileURL as pathToFileURL4 } from "node:url";
|
|
12029
12029
|
function slugifyWorkspaceName(input) {
|
|
12030
12030
|
const slug = input.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -12052,10 +12052,10 @@ async function promptForInteractiveWorkspacePath(input) {
|
|
|
12052
12052
|
});
|
|
12053
12053
|
if (p31.isCancel(raw) || !raw) return null;
|
|
12054
12054
|
const trimmed = String(raw).trim();
|
|
12055
|
-
const expanded = trimmed.startsWith("~/") ?
|
|
12056
|
-
const resolved =
|
|
12057
|
-
if (
|
|
12058
|
-
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);
|
|
12059
12059
|
p31.note(
|
|
12060
12060
|
[
|
|
12061
12061
|
`You selected an existing folder: ${resolved}`,
|
|
@@ -12328,8 +12328,8 @@ init_doctor();
|
|
|
12328
12328
|
import { Command } from "commander";
|
|
12329
12329
|
import * as p32 from "@clack/prompts";
|
|
12330
12330
|
import pc46 from "picocolors";
|
|
12331
|
-
import
|
|
12332
|
-
import
|
|
12331
|
+
import fs49 from "node:fs";
|
|
12332
|
+
import path56 from "node:path";
|
|
12333
12333
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
12334
12334
|
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
12335
12335
|
|
|
@@ -12990,8 +12990,8 @@ function printItemCompleted(item) {
|
|
|
12990
12990
|
const changes = Array.isArray(item.changes) ? item.changes : [];
|
|
12991
12991
|
const entries = changes.map((changeRaw) => asRecord(changeRaw)).filter((change) => Boolean(change)).map((change) => {
|
|
12992
12992
|
const kind = asString(change.kind, "update");
|
|
12993
|
-
const
|
|
12994
|
-
return `${kind} ${
|
|
12993
|
+
const path57 = asString(change.path, "unknown");
|
|
12994
|
+
return `${kind} ${path57}`;
|
|
12995
12995
|
});
|
|
12996
12996
|
const preview = entries.length > 0 ? entries.slice(0, 6).join(", ") : "none";
|
|
12997
12997
|
const more = entries.length > 6 ? ` (+${entries.length - 6} more)` : "";
|
|
@@ -15823,8 +15823,8 @@ function registerIssueCommands(program2) {
|
|
|
15823
15823
|
if (opts.assigneeAgentId) params.set("assigneeAgentId", opts.assigneeAgentId);
|
|
15824
15824
|
if (opts.projectId) params.set("projectId", opts.projectId);
|
|
15825
15825
|
const query = params.toString();
|
|
15826
|
-
const
|
|
15827
|
-
const rows = await ctx.api.get(
|
|
15826
|
+
const path57 = `/api/companies/${ctx.companyId}/issues${query ? `?${query}` : ""}`;
|
|
15827
|
+
const rows = await ctx.api.get(path57) ?? [];
|
|
15828
15828
|
const filtered = filterIssueRows(rows, opts.match);
|
|
15829
15829
|
if (ctx.json) {
|
|
15830
15830
|
printOutput(filtered, { json: true });
|
|
@@ -16430,8 +16430,8 @@ function registerActivityCommands(program2) {
|
|
|
16430
16430
|
if (opts.entityType) params.set("entityType", opts.entityType);
|
|
16431
16431
|
if (opts.entityId) params.set("entityId", opts.entityId);
|
|
16432
16432
|
const query = params.toString();
|
|
16433
|
-
const
|
|
16434
|
-
const rows = await ctx.api.get(
|
|
16433
|
+
const path57 = `/api/companies/${ctx.companyId}/activity${query ? `?${query}` : ""}`;
|
|
16434
|
+
const rows = await ctx.api.get(path57) ?? [];
|
|
16435
16435
|
if (ctx.json) {
|
|
16436
16436
|
printOutput(rows, { json: true });
|
|
16437
16437
|
return;
|
|
@@ -17865,12 +17865,13 @@ ${result.lastError}`);
|
|
|
17865
17865
|
// src/commands/kit.ts
|
|
17866
17866
|
init_service();
|
|
17867
17867
|
init_banner();
|
|
17868
|
-
import
|
|
17868
|
+
import path34 from "node:path";
|
|
17869
17869
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
17870
17870
|
import * as p19 from "@clack/prompts";
|
|
17871
17871
|
import pc30 from "picocolors";
|
|
17872
17872
|
|
|
17873
17873
|
// src/commands/kit-fork.ts
|
|
17874
|
+
import fs27 from "node:fs";
|
|
17874
17875
|
import * as p18 from "@clack/prompts";
|
|
17875
17876
|
|
|
17876
17877
|
// src/commands/kit-fork-remote.ts
|
|
@@ -19219,6 +19220,243 @@ init_fork_registry();
|
|
|
19219
19220
|
init_service();
|
|
19220
19221
|
init_fork_policy();
|
|
19221
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
|
|
19222
19460
|
function hr(width = 72) {
|
|
19223
19461
|
return pc29.dim("\u2500".repeat(width));
|
|
19224
19462
|
}
|
|
@@ -20367,6 +20605,7 @@ function addForkSubcommands(parentCmd) {
|
|
|
20367
20605
|
console.log(hr());
|
|
20368
20606
|
console.log("");
|
|
20369
20607
|
});
|
|
20608
|
+
registerAuthoritySubcommands(parentCmd);
|
|
20370
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) => {
|
|
20371
20610
|
const allForks = listKitForkRegistrations();
|
|
20372
20611
|
const reg = allForks.find((f) => f.forkId === forkId);
|
|
@@ -20392,6 +20631,193 @@ function addForkSubcommands(parentCmd) {
|
|
|
20392
20631
|
}
|
|
20393
20632
|
});
|
|
20394
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
|
+
}
|
|
20395
20821
|
|
|
20396
20822
|
// src/commands/kit.ts
|
|
20397
20823
|
var TYPE_CONFIG = {
|
|
@@ -20836,7 +21262,7 @@ Examples:
|
|
|
20836
21262
|
$ growthub kit validate ./my-kit
|
|
20837
21263
|
$ growthub kit validate ~/kits/growthub-open-higgsfield-studio-v1
|
|
20838
21264
|
`).action((kitPath) => {
|
|
20839
|
-
const resolvedPath =
|
|
21265
|
+
const resolvedPath = path34.resolve(kitPath);
|
|
20840
21266
|
const result = validateKitDirectory(resolvedPath);
|
|
20841
21267
|
console.log("");
|
|
20842
21268
|
console.log(pc30.bold("Kit: " + result.kitId) + pc30.dim(" schema v" + result.schemaVersion));
|
|
@@ -20881,13 +21307,13 @@ Examples:
|
|
|
20881
21307
|
}
|
|
20882
21308
|
|
|
20883
21309
|
// src/commands/template.ts
|
|
20884
|
-
import
|
|
21310
|
+
import path36 from "node:path";
|
|
20885
21311
|
import * as p20 from "@clack/prompts";
|
|
20886
21312
|
import pc31 from "picocolors";
|
|
20887
21313
|
|
|
20888
21314
|
// src/templates/service.ts
|
|
20889
|
-
import
|
|
20890
|
-
import
|
|
21315
|
+
import fs28 from "node:fs";
|
|
21316
|
+
import path35 from "node:path";
|
|
20891
21317
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
20892
21318
|
|
|
20893
21319
|
// src/templates/catalog.ts
|
|
@@ -21134,12 +21560,12 @@ var TEMPLATE_CATALOG = [
|
|
|
21134
21560
|
|
|
21135
21561
|
// src/templates/service.ts
|
|
21136
21562
|
function resolveSharedTemplatesRoot() {
|
|
21137
|
-
const moduleDir =
|
|
21563
|
+
const moduleDir = path35.dirname(fileURLToPath6(import.meta.url));
|
|
21138
21564
|
for (const candidate of [
|
|
21139
|
-
|
|
21140
|
-
|
|
21565
|
+
path35.resolve(moduleDir, "../../assets/shared-templates"),
|
|
21566
|
+
path35.resolve(moduleDir, "../assets/shared-templates")
|
|
21141
21567
|
]) {
|
|
21142
|
-
if (
|
|
21568
|
+
if (fs28.existsSync(candidate)) return candidate;
|
|
21143
21569
|
}
|
|
21144
21570
|
throw new Error("Shared template assets not found at cli/assets/shared-templates/");
|
|
21145
21571
|
}
|
|
@@ -21174,15 +21600,15 @@ function getArtifact(slugOrId) {
|
|
|
21174
21600
|
const artifact = resolveSlug(slugOrId);
|
|
21175
21601
|
if (!artifact) throw new Error(`Unknown template '${slugOrId}'. Run 'growthub template list' to browse.`);
|
|
21176
21602
|
const root = resolveSharedTemplatesRoot();
|
|
21177
|
-
const absolutePath =
|
|
21178
|
-
if (!
|
|
21179
|
-
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 };
|
|
21180
21606
|
}
|
|
21181
21607
|
function copyArtifact(slugOrId, destDir) {
|
|
21182
21608
|
const resolved = getArtifact(slugOrId);
|
|
21183
|
-
|
|
21184
|
-
const destPath =
|
|
21185
|
-
|
|
21609
|
+
fs28.mkdirSync(destDir, { recursive: true });
|
|
21610
|
+
const destPath = path35.resolve(destDir, path35.basename(resolved.absolutePath));
|
|
21611
|
+
fs28.copyFileSync(resolved.absolutePath, destPath);
|
|
21186
21612
|
return destPath;
|
|
21187
21613
|
}
|
|
21188
21614
|
var GROUP_ORDER = ["ad-formats", "scene-modules/hooks", "scene-modules/body", "scene-modules/cta"];
|
|
@@ -21422,7 +21848,7 @@ async function runTemplatePicker(opts) {
|
|
|
21422
21848
|
p20.cancel("Cancelled.");
|
|
21423
21849
|
process.exit(0);
|
|
21424
21850
|
}
|
|
21425
|
-
const destDir =
|
|
21851
|
+
const destDir = path36.resolve(destInput.replace(/^~/, process.env["HOME"] ?? ""));
|
|
21426
21852
|
const destPath = copyArtifact(selected.id, destDir);
|
|
21427
21853
|
p20.outro(pc31.green("Copied \u2192 ") + destPath);
|
|
21428
21854
|
return "done";
|
|
@@ -21496,7 +21922,7 @@ Any agent or kit resolves them by slug.
|
|
|
21496
21922
|
return;
|
|
21497
21923
|
}
|
|
21498
21924
|
if (opts.out) {
|
|
21499
|
-
const destDir =
|
|
21925
|
+
const destDir = path36.resolve(opts.out.replace(/^~/, process.env["HOME"] ?? ""));
|
|
21500
21926
|
try {
|
|
21501
21927
|
const dest = copyArtifact(artifact.id, destDir);
|
|
21502
21928
|
console.log(pc31.green("Copied \u2192 ") + dest);
|
|
@@ -22158,10 +22584,10 @@ function createCmsCapabilityRegistryClient() {
|
|
|
22158
22584
|
}
|
|
22159
22585
|
|
|
22160
22586
|
// src/runtime/machine-capability-resolver/index.ts
|
|
22161
|
-
import
|
|
22587
|
+
import os9 from "node:os";
|
|
22162
22588
|
function buildMachineContext(profile) {
|
|
22163
22589
|
return {
|
|
22164
|
-
hostname:
|
|
22590
|
+
hostname: os9.hostname(),
|
|
22165
22591
|
machineLabel: profile.local.machineLabel ?? void 0,
|
|
22166
22592
|
workspaceLabel: profile.local.workspaceLabel ?? void 0,
|
|
22167
22593
|
instanceId: profile.local.instanceId,
|
|
@@ -22580,8 +23006,8 @@ Examples:
|
|
|
22580
23006
|
// src/commands/pipeline.ts
|
|
22581
23007
|
init_session_store();
|
|
22582
23008
|
init_hosted_client();
|
|
22583
|
-
import
|
|
22584
|
-
import
|
|
23009
|
+
import fs31 from "node:fs";
|
|
23010
|
+
import path39 from "node:path";
|
|
22585
23011
|
import * as p22 from "@clack/prompts";
|
|
22586
23012
|
import pc34 from "picocolors";
|
|
22587
23013
|
|
|
@@ -23091,40 +23517,40 @@ function renderPreSaveReview(input) {
|
|
|
23091
23517
|
|
|
23092
23518
|
// src/runtime/artifact-contracts/index.ts
|
|
23093
23519
|
init_home();
|
|
23094
|
-
import
|
|
23095
|
-
import
|
|
23520
|
+
import fs29 from "node:fs";
|
|
23521
|
+
import path37 from "node:path";
|
|
23096
23522
|
import { randomBytes as randomBytes7 } from "node:crypto";
|
|
23097
23523
|
function generateArtifactId() {
|
|
23098
23524
|
return `art_${randomBytes7(8).toString("hex")}`;
|
|
23099
23525
|
}
|
|
23100
23526
|
function resolveArtifactsDir() {
|
|
23101
|
-
return
|
|
23527
|
+
return path37.resolve(resolvePaperclipHomeDir(), "artifacts");
|
|
23102
23528
|
}
|
|
23103
23529
|
function resolveArtifactManifestPath(artifactId) {
|
|
23104
|
-
return
|
|
23530
|
+
return path37.resolve(resolveArtifactsDir(), `${artifactId}.json`);
|
|
23105
23531
|
}
|
|
23106
23532
|
function readLocalManifest(artifactId) {
|
|
23107
23533
|
const filePath = resolveArtifactManifestPath(artifactId);
|
|
23108
|
-
if (!
|
|
23534
|
+
if (!fs29.existsSync(filePath)) return null;
|
|
23109
23535
|
try {
|
|
23110
|
-
return JSON.parse(
|
|
23536
|
+
return JSON.parse(fs29.readFileSync(filePath, "utf-8"));
|
|
23111
23537
|
} catch {
|
|
23112
23538
|
return null;
|
|
23113
23539
|
}
|
|
23114
23540
|
}
|
|
23115
23541
|
function writeLocalManifest(manifest) {
|
|
23116
23542
|
const dir = resolveArtifactsDir();
|
|
23117
|
-
|
|
23543
|
+
fs29.mkdirSync(dir, { recursive: true });
|
|
23118
23544
|
const filePath = resolveArtifactManifestPath(manifest.id);
|
|
23119
|
-
|
|
23545
|
+
fs29.writeFileSync(filePath, `${JSON.stringify(manifest, null, 2)}
|
|
23120
23546
|
`, { mode: 384 });
|
|
23121
23547
|
}
|
|
23122
23548
|
function listLocalManifests() {
|
|
23123
23549
|
const dir = resolveArtifactsDir();
|
|
23124
|
-
if (!
|
|
23125
|
-
return
|
|
23550
|
+
if (!fs29.existsSync(dir)) return [];
|
|
23551
|
+
return fs29.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => {
|
|
23126
23552
|
try {
|
|
23127
|
-
const content =
|
|
23553
|
+
const content = fs29.readFileSync(path37.resolve(dir, entry.name), "utf-8");
|
|
23128
23554
|
return JSON.parse(content);
|
|
23129
23555
|
} catch {
|
|
23130
23556
|
return null;
|
|
@@ -23202,8 +23628,8 @@ function createArtifactStore() {
|
|
|
23202
23628
|
|
|
23203
23629
|
// src/runtime/native-intelligence/index.ts
|
|
23204
23630
|
init_home();
|
|
23205
|
-
import
|
|
23206
|
-
import
|
|
23631
|
+
import fs30 from "node:fs";
|
|
23632
|
+
import path38 from "node:path";
|
|
23207
23633
|
|
|
23208
23634
|
// src/runtime/native-intelligence/contract.ts
|
|
23209
23635
|
var DEFAULT_INTELLIGENCE_CONFIG = {
|
|
@@ -24313,15 +24739,15 @@ function parseJsonSafe4(text66) {
|
|
|
24313
24739
|
|
|
24314
24740
|
// src/runtime/native-intelligence/index.ts
|
|
24315
24741
|
function resolveConfigPath2() {
|
|
24316
|
-
return
|
|
24742
|
+
return path38.resolve(resolvePaperclipHomeDir(), "native-intelligence", "config.json");
|
|
24317
24743
|
}
|
|
24318
24744
|
function readIntelligenceConfig() {
|
|
24319
24745
|
const configPath = resolveConfigPath2();
|
|
24320
|
-
if (!
|
|
24746
|
+
if (!fs30.existsSync(configPath)) {
|
|
24321
24747
|
return { ...DEFAULT_INTELLIGENCE_CONFIG };
|
|
24322
24748
|
}
|
|
24323
24749
|
try {
|
|
24324
|
-
const raw = JSON.parse(
|
|
24750
|
+
const raw = JSON.parse(fs30.readFileSync(configPath, "utf-8"));
|
|
24325
24751
|
return {
|
|
24326
24752
|
modelId: validateModelId(raw.modelId),
|
|
24327
24753
|
backendType: raw.backendType === "hosted" ? "hosted" : "local",
|
|
@@ -24338,8 +24764,8 @@ function readIntelligenceConfig() {
|
|
|
24338
24764
|
}
|
|
24339
24765
|
function writeIntelligenceConfig(config) {
|
|
24340
24766
|
const configPath = resolveConfigPath2();
|
|
24341
|
-
|
|
24342
|
-
|
|
24767
|
+
fs30.mkdirSync(path38.dirname(configPath), { recursive: true });
|
|
24768
|
+
fs30.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
|
|
24343
24769
|
`, "utf-8");
|
|
24344
24770
|
}
|
|
24345
24771
|
function validateModelId(id) {
|
|
@@ -24695,9 +25121,9 @@ async function runPipelineAssembler(opts) {
|
|
|
24695
25121
|
}
|
|
24696
25122
|
}
|
|
24697
25123
|
function loadPipelineFromFileOrJson(input) {
|
|
24698
|
-
const resolvedPath =
|
|
24699
|
-
if (
|
|
24700
|
-
const content =
|
|
25124
|
+
const resolvedPath = path39.resolve(input);
|
|
25125
|
+
if (fs31.existsSync(resolvedPath)) {
|
|
25126
|
+
const content = fs31.readFileSync(resolvedPath, "utf-8");
|
|
24701
25127
|
return deserializePipeline(JSON.parse(content));
|
|
24702
25128
|
}
|
|
24703
25129
|
try {
|
|
@@ -25242,8 +25668,8 @@ Examples:
|
|
|
25242
25668
|
}
|
|
25243
25669
|
|
|
25244
25670
|
// src/commands/workflow.ts
|
|
25245
|
-
import
|
|
25246
|
-
import
|
|
25671
|
+
import fs33 from "node:fs";
|
|
25672
|
+
import path41 from "node:path";
|
|
25247
25673
|
import * as p23 from "@clack/prompts";
|
|
25248
25674
|
import pc37 from "picocolors";
|
|
25249
25675
|
init_session_store();
|
|
@@ -25251,15 +25677,15 @@ init_hosted_client();
|
|
|
25251
25677
|
|
|
25252
25678
|
// src/runtime/workflow-hygiene/labels.ts
|
|
25253
25679
|
init_home();
|
|
25254
|
-
import
|
|
25255
|
-
import
|
|
25680
|
+
import fs32 from "node:fs";
|
|
25681
|
+
import path40 from "node:path";
|
|
25256
25682
|
function resolveStorePath() {
|
|
25257
|
-
return
|
|
25683
|
+
return path40.resolve(resolvePaperclipHomeDir(), "workflow-hygiene", "labels.json");
|
|
25258
25684
|
}
|
|
25259
25685
|
function readStoreFile(filePath) {
|
|
25260
|
-
if (!
|
|
25686
|
+
if (!fs32.existsSync(filePath)) return { records: [] };
|
|
25261
25687
|
try {
|
|
25262
|
-
const raw = JSON.parse(
|
|
25688
|
+
const raw = JSON.parse(fs32.readFileSync(filePath, "utf-8"));
|
|
25263
25689
|
if (!Array.isArray(raw.records)) return { records: [] };
|
|
25264
25690
|
return raw;
|
|
25265
25691
|
} catch {
|
|
@@ -25267,8 +25693,8 @@ function readStoreFile(filePath) {
|
|
|
25267
25693
|
}
|
|
25268
25694
|
}
|
|
25269
25695
|
function writeStoreFile(filePath, data) {
|
|
25270
|
-
|
|
25271
|
-
|
|
25696
|
+
fs32.mkdirSync(path40.dirname(filePath), { recursive: true });
|
|
25697
|
+
fs32.writeFileSync(filePath, `${JSON.stringify(data, null, 2)}
|
|
25272
25698
|
`, "utf-8");
|
|
25273
25699
|
}
|
|
25274
25700
|
function inferDefaultLabel(name, createdAt, versionCount) {
|
|
@@ -25369,16 +25795,16 @@ function box5(lines) {
|
|
|
25369
25795
|
return [top, ...body, bottom].join("\n");
|
|
25370
25796
|
}
|
|
25371
25797
|
function resolveSavedWorkflowsDir() {
|
|
25372
|
-
return
|
|
25798
|
+
return path41.resolve(resolvePaperclipHomeDir(), "workflows");
|
|
25373
25799
|
}
|
|
25374
25800
|
function resolveDeletedWorkflowIdsPath() {
|
|
25375
|
-
return
|
|
25801
|
+
return path41.resolve(resolvePaperclipHomeDir(), "workflow-hygiene", "deleted-workflows.json");
|
|
25376
25802
|
}
|
|
25377
25803
|
function readDeletedWorkflowIds() {
|
|
25378
25804
|
const filePath = resolveDeletedWorkflowIdsPath();
|
|
25379
|
-
if (!
|
|
25805
|
+
if (!fs33.existsSync(filePath)) return /* @__PURE__ */ new Set();
|
|
25380
25806
|
try {
|
|
25381
|
-
const raw = JSON.parse(
|
|
25807
|
+
const raw = JSON.parse(fs33.readFileSync(filePath, "utf-8"));
|
|
25382
25808
|
if (!Array.isArray(raw?.workflowIds)) return /* @__PURE__ */ new Set();
|
|
25383
25809
|
return new Set(raw.workflowIds.filter((value) => typeof value === "string"));
|
|
25384
25810
|
} catch {
|
|
@@ -25387,8 +25813,8 @@ function readDeletedWorkflowIds() {
|
|
|
25387
25813
|
}
|
|
25388
25814
|
function writeDeletedWorkflowIds(ids) {
|
|
25389
25815
|
const filePath = resolveDeletedWorkflowIdsPath();
|
|
25390
|
-
|
|
25391
|
-
|
|
25816
|
+
fs33.mkdirSync(path41.dirname(filePath), { recursive: true });
|
|
25817
|
+
fs33.writeFileSync(filePath, `${JSON.stringify({ workflowIds: [...ids] }, null, 2)}
|
|
25392
25818
|
`, "utf-8");
|
|
25393
25819
|
}
|
|
25394
25820
|
function markWorkflowDeletedLocally(workflowId) {
|
|
@@ -25414,10 +25840,10 @@ function filterLocallyDeletedWorkflows(entries) {
|
|
|
25414
25840
|
}
|
|
25415
25841
|
function listLocalSavedWorkflows() {
|
|
25416
25842
|
const dir = resolveSavedWorkflowsDir();
|
|
25417
|
-
if (!
|
|
25418
|
-
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) => {
|
|
25419
25845
|
try {
|
|
25420
|
-
const raw = JSON.parse(
|
|
25846
|
+
const raw = JSON.parse(fs33.readFileSync(path41.resolve(dir, e.name), "utf-8"));
|
|
25421
25847
|
const pipeline = raw.pipeline ?? raw;
|
|
25422
25848
|
return {
|
|
25423
25849
|
filename: e.name,
|
|
@@ -25478,11 +25904,11 @@ async function archiveSavedWorkflow(entry) {
|
|
|
25478
25904
|
throw new Error("Local workflow entry is missing filename.");
|
|
25479
25905
|
}
|
|
25480
25906
|
const dir = resolveSavedWorkflowsDir();
|
|
25481
|
-
const archiveDir =
|
|
25482
|
-
|
|
25483
|
-
|
|
25484
|
-
|
|
25485
|
-
|
|
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)
|
|
25486
25912
|
);
|
|
25487
25913
|
}
|
|
25488
25914
|
async function deleteSavedWorkflow(entry) {
|
|
@@ -25506,7 +25932,7 @@ async function deleteSavedWorkflow(entry) {
|
|
|
25506
25932
|
if (!entry.filename) {
|
|
25507
25933
|
throw new Error("Local workflow entry is missing filename.");
|
|
25508
25934
|
}
|
|
25509
|
-
|
|
25935
|
+
fs33.rmSync(path41.resolve(resolveSavedWorkflowsDir(), entry.filename), { force: true });
|
|
25510
25936
|
markWorkflowDeletedLocally(entry.workflowId);
|
|
25511
25937
|
}
|
|
25512
25938
|
async function loadSavedWorkflowDetail(entry) {
|
|
@@ -25525,7 +25951,7 @@ async function loadSavedWorkflowDetail(entry) {
|
|
|
25525
25951
|
};
|
|
25526
25952
|
}
|
|
25527
25953
|
const dir = resolveSavedWorkflowsDir();
|
|
25528
|
-
const content =
|
|
25954
|
+
const content = fs33.readFileSync(path41.resolve(dir, entry.filename), "utf-8");
|
|
25529
25955
|
const raw = JSON.parse(content);
|
|
25530
25956
|
return {
|
|
25531
25957
|
pipeline: raw.pipeline ?? raw,
|
|
@@ -26509,36 +26935,36 @@ import pc38 from "picocolors";
|
|
|
26509
26935
|
|
|
26510
26936
|
// src/runtime/agent-harness/auth-store.ts
|
|
26511
26937
|
init_home();
|
|
26512
|
-
import
|
|
26513
|
-
import
|
|
26938
|
+
import fs34 from "node:fs";
|
|
26939
|
+
import path42 from "node:path";
|
|
26514
26940
|
function resolveHarnessAuthDir() {
|
|
26515
|
-
return
|
|
26941
|
+
return path42.resolve(resolvePaperclipHomeDir(), "harness-auth");
|
|
26516
26942
|
}
|
|
26517
26943
|
function resolveHarnessAuthFile(harnessId) {
|
|
26518
|
-
return
|
|
26944
|
+
return path42.resolve(resolveHarnessAuthDir(), `${harnessId}.json`);
|
|
26519
26945
|
}
|
|
26520
26946
|
function normalizeSecret(value) {
|
|
26521
26947
|
const trimmed = value?.trim();
|
|
26522
26948
|
return trimmed && trimmed.length > 0 ? trimmed : void 0;
|
|
26523
26949
|
}
|
|
26524
26950
|
function ensureSecureDir(dirPath) {
|
|
26525
|
-
|
|
26951
|
+
fs34.mkdirSync(dirPath, { recursive: true });
|
|
26526
26952
|
try {
|
|
26527
|
-
|
|
26953
|
+
fs34.chmodSync(dirPath, 448);
|
|
26528
26954
|
} catch {
|
|
26529
26955
|
}
|
|
26530
26956
|
}
|
|
26531
26957
|
function ensureSecureFile(filePath) {
|
|
26532
26958
|
try {
|
|
26533
|
-
|
|
26959
|
+
fs34.chmodSync(filePath, 384);
|
|
26534
26960
|
} catch {
|
|
26535
26961
|
}
|
|
26536
26962
|
}
|
|
26537
26963
|
function readHarnessCredentials(harnessId) {
|
|
26538
26964
|
const filePath = resolveHarnessAuthFile(harnessId);
|
|
26539
|
-
if (!
|
|
26965
|
+
if (!fs34.existsSync(filePath)) return {};
|
|
26540
26966
|
try {
|
|
26541
|
-
const parsed = JSON.parse(
|
|
26967
|
+
const parsed = JSON.parse(fs34.readFileSync(filePath, "utf-8"));
|
|
26542
26968
|
const creds = {};
|
|
26543
26969
|
for (const [key, value] of Object.entries(parsed)) {
|
|
26544
26970
|
if (typeof value === "string" && value.trim().length > 0) {
|
|
@@ -26565,7 +26991,7 @@ function setHarnessCredential(harnessId, key, value) {
|
|
|
26565
26991
|
const dirPath = resolveHarnessAuthDir();
|
|
26566
26992
|
ensureSecureDir(dirPath);
|
|
26567
26993
|
const filePath = resolveHarnessAuthFile(harnessId);
|
|
26568
|
-
|
|
26994
|
+
fs34.writeFileSync(filePath, `${JSON.stringify(creds, null, 2)}
|
|
26569
26995
|
`, "utf-8");
|
|
26570
26996
|
ensureSecureFile(filePath);
|
|
26571
26997
|
}
|
|
@@ -26582,7 +27008,7 @@ function setHarnessCredentials(harnessId, updates) {
|
|
|
26582
27008
|
const dirPath = resolveHarnessAuthDir();
|
|
26583
27009
|
ensureSecureDir(dirPath);
|
|
26584
27010
|
const filePath = resolveHarnessAuthFile(harnessId);
|
|
26585
|
-
|
|
27011
|
+
fs34.writeFileSync(filePath, `${JSON.stringify(creds, null, 2)}
|
|
26586
27012
|
`, "utf-8");
|
|
26587
27013
|
ensureSecureFile(filePath);
|
|
26588
27014
|
}
|
|
@@ -26594,8 +27020,8 @@ function maskSecret(value) {
|
|
|
26594
27020
|
|
|
26595
27021
|
// src/runtime/open-agents/index.ts
|
|
26596
27022
|
init_home();
|
|
26597
|
-
import
|
|
26598
|
-
import
|
|
27023
|
+
import fs35 from "node:fs";
|
|
27024
|
+
import path43 from "node:path";
|
|
26599
27025
|
|
|
26600
27026
|
// src/runtime/open-agents/contract.ts
|
|
26601
27027
|
var DEFAULT_OPEN_AGENTS_CONFIG = {
|
|
@@ -26782,18 +27208,18 @@ var OpenAgentsBackendError = class extends Error {
|
|
|
26782
27208
|
|
|
26783
27209
|
// src/runtime/open-agents/index.ts
|
|
26784
27210
|
function resolveConfigPath3() {
|
|
26785
|
-
return
|
|
27211
|
+
return path43.resolve(resolvePaperclipHomeDir(), "open-agents", "config.json");
|
|
26786
27212
|
}
|
|
26787
27213
|
function readOpenAgentsConfig() {
|
|
26788
27214
|
const configPath = resolveConfigPath3();
|
|
26789
|
-
if (!
|
|
27215
|
+
if (!fs35.existsSync(configPath)) {
|
|
26790
27216
|
return {
|
|
26791
27217
|
...DEFAULT_OPEN_AGENTS_CONFIG,
|
|
26792
27218
|
apiKey: getHarnessCredential("open-agents", "apiKey")
|
|
26793
27219
|
};
|
|
26794
27220
|
}
|
|
26795
27221
|
try {
|
|
26796
|
-
const raw = JSON.parse(
|
|
27222
|
+
const raw = JSON.parse(fs35.readFileSync(configPath, "utf-8"));
|
|
26797
27223
|
const storedApiKey = getHarnessCredential("open-agents", "apiKey");
|
|
26798
27224
|
return {
|
|
26799
27225
|
backendType: validateBackendType(raw.backendType),
|
|
@@ -26814,13 +27240,13 @@ function readOpenAgentsConfig() {
|
|
|
26814
27240
|
}
|
|
26815
27241
|
function writeOpenAgentsConfig(config) {
|
|
26816
27242
|
const configPath = resolveConfigPath3();
|
|
26817
|
-
|
|
27243
|
+
fs35.mkdirSync(path43.dirname(configPath), { recursive: true });
|
|
26818
27244
|
const persisted = {
|
|
26819
27245
|
...config,
|
|
26820
27246
|
authMode: validateAuthMode(config.authMode),
|
|
26821
27247
|
apiKey: void 0
|
|
26822
27248
|
};
|
|
26823
|
-
|
|
27249
|
+
fs35.writeFileSync(configPath, `${JSON.stringify(persisted, null, 2)}
|
|
26824
27250
|
`, "utf-8");
|
|
26825
27251
|
setHarnessCredential("open-agents", "apiKey", config.apiKey);
|
|
26826
27252
|
}
|
|
@@ -27381,8 +27807,8 @@ import pc39 from "picocolors";
|
|
|
27381
27807
|
|
|
27382
27808
|
// src/runtime/qwen-code/index.ts
|
|
27383
27809
|
init_home();
|
|
27384
|
-
import
|
|
27385
|
-
import
|
|
27810
|
+
import fs36 from "node:fs";
|
|
27811
|
+
import path44 from "node:path";
|
|
27386
27812
|
|
|
27387
27813
|
// src/runtime/qwen-code/contract.ts
|
|
27388
27814
|
var QWEN_CODE_APPROVAL_MODES = [
|
|
@@ -27600,19 +28026,19 @@ function buildSetupGuidance(env) {
|
|
|
27600
28026
|
|
|
27601
28027
|
// src/runtime/qwen-code/index.ts
|
|
27602
28028
|
function resolveConfigPath4() {
|
|
27603
|
-
return
|
|
28029
|
+
return path44.resolve(resolvePaperclipHomeDir(), "qwen-code", "config.json");
|
|
27604
28030
|
}
|
|
27605
28031
|
function readQwenCodeConfig() {
|
|
27606
28032
|
const configPath = resolveConfigPath4();
|
|
27607
28033
|
const storedCredentials = readHarnessCredentials("qwen-code");
|
|
27608
|
-
if (!
|
|
28034
|
+
if (!fs36.existsSync(configPath)) {
|
|
27609
28035
|
return {
|
|
27610
28036
|
...DEFAULT_QWEN_CODE_CONFIG,
|
|
27611
28037
|
env: mergeHarnessEnv(DEFAULT_QWEN_CODE_CONFIG.env, storedCredentials)
|
|
27612
28038
|
};
|
|
27613
28039
|
}
|
|
27614
28040
|
try {
|
|
27615
|
-
const raw = JSON.parse(
|
|
28041
|
+
const raw = JSON.parse(fs36.readFileSync(configPath, "utf-8"));
|
|
27616
28042
|
return {
|
|
27617
28043
|
binaryPath: typeof raw.binaryPath === "string" ? raw.binaryPath : DEFAULT_QWEN_CODE_CONFIG.binaryPath,
|
|
27618
28044
|
defaultModel: typeof raw.defaultModel === "string" ? raw.defaultModel : DEFAULT_QWEN_CODE_CONFIG.defaultModel,
|
|
@@ -27634,7 +28060,7 @@ function readQwenCodeConfig() {
|
|
|
27634
28060
|
}
|
|
27635
28061
|
function writeQwenCodeConfig(config) {
|
|
27636
28062
|
const configPath = resolveConfigPath4();
|
|
27637
|
-
|
|
28063
|
+
fs36.mkdirSync(path44.dirname(configPath), { recursive: true });
|
|
27638
28064
|
const rawEnv = typeof config.env === "object" && config.env !== null ? config.env : {};
|
|
27639
28065
|
const credentialUpdates = {};
|
|
27640
28066
|
const publicEnv = {};
|
|
@@ -27646,7 +28072,7 @@ function writeQwenCodeConfig(config) {
|
|
|
27646
28072
|
publicEnv[key] = value;
|
|
27647
28073
|
}
|
|
27648
28074
|
setHarnessCredentials("qwen-code", credentialUpdates);
|
|
27649
|
-
|
|
28075
|
+
fs36.writeFileSync(
|
|
27650
28076
|
configPath,
|
|
27651
28077
|
`${JSON.stringify({ ...config, env: publicEnv }, null, 2)}
|
|
27652
28078
|
`,
|
|
@@ -27979,8 +28405,8 @@ import pc42 from "picocolors";
|
|
|
27979
28405
|
|
|
27980
28406
|
// src/status/probes.ts
|
|
27981
28407
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
27982
|
-
import
|
|
27983
|
-
import
|
|
28408
|
+
import fs37 from "node:fs";
|
|
28409
|
+
import path45 from "node:path";
|
|
27984
28410
|
var GITHUB_API = "https://api.github.com";
|
|
27985
28411
|
var NPM_REGISTRY = "https://registry.npmjs.org";
|
|
27986
28412
|
function isoNow() {
|
|
@@ -28145,7 +28571,7 @@ async function probeKitForksIndex(_timeoutMs) {
|
|
|
28145
28571
|
try {
|
|
28146
28572
|
const { resolveKitForksIndexPath: resolveKitForksIndexPath2 } = await Promise.resolve().then(() => (init_kit_forks_home(), kit_forks_home_exports));
|
|
28147
28573
|
const p33 = resolveKitForksIndexPath2();
|
|
28148
|
-
if (!
|
|
28574
|
+
if (!fs37.existsSync(p33)) {
|
|
28149
28575
|
return {
|
|
28150
28576
|
componentId: "kit-forks-index",
|
|
28151
28577
|
level: "operational",
|
|
@@ -28153,7 +28579,7 @@ async function probeKitForksIndex(_timeoutMs) {
|
|
|
28153
28579
|
lastCheckedAt: isoNow()
|
|
28154
28580
|
};
|
|
28155
28581
|
}
|
|
28156
|
-
const parsed = JSON.parse(
|
|
28582
|
+
const parsed = JSON.parse(fs37.readFileSync(p33, "utf8"));
|
|
28157
28583
|
const count = Array.isArray(parsed.entries) ? parsed.entries.length : 0;
|
|
28158
28584
|
return {
|
|
28159
28585
|
componentId: "kit-forks-index",
|
|
@@ -28222,10 +28648,10 @@ async function probeNode(_timeoutMs) {
|
|
|
28222
28648
|
};
|
|
28223
28649
|
}
|
|
28224
28650
|
async function probeReleaseBundleArtifacts(_timeoutMs) {
|
|
28225
|
-
const distPath =
|
|
28226
|
-
const installerPath =
|
|
28227
|
-
const distOk =
|
|
28228
|
-
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);
|
|
28229
28655
|
const ok = distOk && installerOk;
|
|
28230
28656
|
return {
|
|
28231
28657
|
componentId: "release-bundle",
|
|
@@ -28772,7 +29198,7 @@ import pc44 from "picocolors";
|
|
|
28772
29198
|
|
|
28773
29199
|
// src/fleet/summary.ts
|
|
28774
29200
|
init_fork_registry();
|
|
28775
|
-
import
|
|
29201
|
+
import fs47 from "node:fs";
|
|
28776
29202
|
init_fork_policy();
|
|
28777
29203
|
init_fork_trace();
|
|
28778
29204
|
function classifyHealth(drift, pendingConfirmationJobs, lastJobStatus) {
|
|
@@ -28792,7 +29218,7 @@ var REMOTE_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
|
28792
29218
|
"conflict_encountered"
|
|
28793
29219
|
]);
|
|
28794
29220
|
function buildForkSummary(reg) {
|
|
28795
|
-
if (!
|
|
29221
|
+
if (!fs47.existsSync(reg.forkPath)) {
|
|
28796
29222
|
return {
|
|
28797
29223
|
forkId: reg.forkId,
|
|
28798
29224
|
kitId: reg.kitId,
|
|
@@ -29253,8 +29679,8 @@ async function fleetApprovals(opts) {
|
|
|
29253
29679
|
p30.log.message(
|
|
29254
29680
|
` \xB7 ${pc44.cyan(entry.jobId)} fork=${entry.forkLabel ?? entry.forkId} created=${entry.createdAt.slice(0, 19)}`
|
|
29255
29681
|
);
|
|
29256
|
-
for (const
|
|
29257
|
-
p30.log.message(` ${pc44.dim("awaits")} ${
|
|
29682
|
+
for (const path57 of entry.pendingPaths.slice(0, 6)) {
|
|
29683
|
+
p30.log.message(` ${pc44.dim("awaits")} ${path57}`);
|
|
29258
29684
|
}
|
|
29259
29685
|
if (entry.pendingPaths.length > 6) {
|
|
29260
29686
|
p30.log.message(` ${pc44.dim(`\u2026 +${entry.pendingPaths.length - 6} more`)}`);
|
|
@@ -29321,14 +29747,14 @@ init_banner();
|
|
|
29321
29747
|
init_home();
|
|
29322
29748
|
function resolveCliVersion() {
|
|
29323
29749
|
try {
|
|
29324
|
-
const moduleDir =
|
|
29750
|
+
const moduleDir = path56.dirname(fileURLToPath7(import.meta.url));
|
|
29325
29751
|
const candidates = [
|
|
29326
|
-
|
|
29327
|
-
|
|
29752
|
+
path56.resolve(moduleDir, "../package.json"),
|
|
29753
|
+
path56.resolve(moduleDir, "../../package.json")
|
|
29328
29754
|
];
|
|
29329
29755
|
for (const candidate of candidates) {
|
|
29330
|
-
if (!
|
|
29331
|
-
const parsed = JSON.parse(
|
|
29756
|
+
if (!fs49.existsSync(candidate)) continue;
|
|
29757
|
+
const parsed = JSON.parse(fs49.readFileSync(candidate, "utf8"));
|
|
29332
29758
|
if (parsed?.name === "@growthub/cli" && typeof parsed.version === "string") return parsed.version;
|
|
29333
29759
|
}
|
|
29334
29760
|
} catch {
|
|
@@ -29630,39 +30056,39 @@ User: ${prompt}` : prompt,
|
|
|
29630
30056
|
}
|
|
29631
30057
|
}
|
|
29632
30058
|
function resolveLocalThreadsDir() {
|
|
29633
|
-
return
|
|
30059
|
+
return path56.resolve(resolvePaperclipHomeDir(), "native-intelligence", "threads");
|
|
29634
30060
|
}
|
|
29635
30061
|
function loadOrCreateLocalThread() {
|
|
29636
30062
|
const dir = resolveLocalThreadsDir();
|
|
29637
|
-
|
|
29638
|
-
const activePath =
|
|
29639
|
-
if (
|
|
30063
|
+
fs49.mkdirSync(dir, { recursive: true });
|
|
30064
|
+
const activePath = path56.resolve(dir, "active-thread.json");
|
|
30065
|
+
if (fs49.existsSync(activePath)) {
|
|
29640
30066
|
try {
|
|
29641
|
-
const parsed = JSON.parse(
|
|
30067
|
+
const parsed = JSON.parse(fs49.readFileSync(activePath, "utf-8"));
|
|
29642
30068
|
const id2 = typeof parsed.id === "string" && parsed.id.length > 0 ? parsed.id : `thread-${Date.now()}`;
|
|
29643
|
-
const threadFile =
|
|
30069
|
+
const threadFile = path56.resolve(dir, `${id2}.json`);
|
|
29644
30070
|
const messages = Array.isArray(parsed.messages) ? parsed.messages : [];
|
|
29645
30071
|
return { id: id2, filePath: threadFile, messages };
|
|
29646
30072
|
} catch {
|
|
29647
30073
|
}
|
|
29648
30074
|
}
|
|
29649
30075
|
const id = `thread-${Date.now()}`;
|
|
29650
|
-
const filePath =
|
|
30076
|
+
const filePath = path56.resolve(dir, `${id}.json`);
|
|
29651
30077
|
const thread = { id, filePath, messages: [] };
|
|
29652
30078
|
saveLocalThread(thread);
|
|
29653
30079
|
return thread;
|
|
29654
30080
|
}
|
|
29655
30081
|
function saveLocalThread(thread) {
|
|
29656
30082
|
const dir = resolveLocalThreadsDir();
|
|
29657
|
-
|
|
29658
|
-
|
|
30083
|
+
fs49.mkdirSync(dir, { recursive: true });
|
|
30084
|
+
fs49.writeFileSync(
|
|
29659
30085
|
thread.filePath,
|
|
29660
30086
|
`${JSON.stringify({ id: thread.id, messages: thread.messages }, null, 2)}
|
|
29661
30087
|
`,
|
|
29662
30088
|
"utf-8"
|
|
29663
30089
|
);
|
|
29664
|
-
const activePath =
|
|
29665
|
-
|
|
30090
|
+
const activePath = path56.resolve(dir, "active-thread.json");
|
|
30091
|
+
fs49.writeFileSync(
|
|
29666
30092
|
activePath,
|
|
29667
30093
|
`${JSON.stringify({ id: thread.id, messages: thread.messages }, null, 2)}
|
|
29668
30094
|
`,
|
|
@@ -30205,12 +30631,12 @@ function isInstallerMode() {
|
|
|
30205
30631
|
}
|
|
30206
30632
|
function listLocalSurfaces() {
|
|
30207
30633
|
const homeDir = resolvePaperclipHomeDir();
|
|
30208
|
-
const instancesDir =
|
|
30209
|
-
if (!
|
|
30210
|
-
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) => {
|
|
30211
30637
|
const instanceId = entry.name;
|
|
30212
|
-
const configPath =
|
|
30213
|
-
if (!
|
|
30638
|
+
const configPath = path56.resolve(instancesDir, instanceId, "config.json");
|
|
30639
|
+
if (!fs49.existsSync(configPath)) return null;
|
|
30214
30640
|
try {
|
|
30215
30641
|
const config = readConfig(configPath);
|
|
30216
30642
|
if (!config) return null;
|