@growthub/cli 0.9.0 → 0.9.2
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/worker-kits/growthub-agency-portal-starter-v1/apps/agency-portal/.env.example +36 -20
- package/assets/worker-kits/growthub-agency-portal-starter-v1/apps/agency-portal/README.md +2 -0
- package/assets/worker-kits/growthub-agency-portal-starter-v1/apps/agency-portal/app/api/workspace/route.js +15 -11
- package/assets/worker-kits/growthub-agency-portal-starter-v1/apps/agency-portal/app/globals.css +134 -2
- package/assets/worker-kits/growthub-agency-portal-starter-v1/apps/agency-portal/app/page.jsx +143 -149
- package/assets/worker-kits/growthub-agency-portal-starter-v1/apps/agency-portal/app/settings/integrations/page.jsx +67 -96
- package/assets/worker-kits/growthub-agency-portal-starter-v1/apps/agency-portal/jsconfig.json +8 -0
- package/assets/worker-kits/growthub-agency-portal-starter-v1/apps/agency-portal/lib/adapters/integrations/index.js +21 -1
- package/assets/worker-kits/growthub-agency-portal-starter-v1/apps/agency-portal/lib/domain/portal.js +146 -12
- package/assets/worker-kits/growthub-agency-portal-starter-v1/docs/adapter-contracts.md +7 -0
- package/assets/worker-kits/growthub-agency-portal-starter-v1/studio/src/App.jsx +34 -37
- package/assets/worker-kits/growthub-agency-portal-starter-v1/studio/src/app.css +2 -1
- package/dist/index.js +1649 -752
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8182,8 +8182,8 @@ var init_onboard = __esm({
|
|
|
8182
8182
|
|
|
8183
8183
|
// src/client/http.ts
|
|
8184
8184
|
import { URL as URL2 } from "node:url";
|
|
8185
|
-
function buildUrl(apiBase,
|
|
8186
|
-
const normalizedPath =
|
|
8185
|
+
function buildUrl(apiBase, path75) {
|
|
8186
|
+
const normalizedPath = path75.startsWith("/") ? path75 : `/${path75}`;
|
|
8187
8187
|
const [pathname, query] = normalizedPath.split("?");
|
|
8188
8188
|
const url = new URL2(apiBase);
|
|
8189
8189
|
url.pathname = `${url.pathname.replace(/\/+$/, "")}${pathname}`;
|
|
@@ -8245,26 +8245,26 @@ var init_http = __esm({
|
|
|
8245
8245
|
this.runId = opts.runId?.trim() || void 0;
|
|
8246
8246
|
this.userId = opts.userId?.trim() || void 0;
|
|
8247
8247
|
}
|
|
8248
|
-
get(
|
|
8249
|
-
return this.request(
|
|
8248
|
+
get(path75, opts) {
|
|
8249
|
+
return this.request(path75, { method: "GET" }, opts);
|
|
8250
8250
|
}
|
|
8251
|
-
post(
|
|
8252
|
-
return this.request(
|
|
8251
|
+
post(path75, body, opts) {
|
|
8252
|
+
return this.request(path75, {
|
|
8253
8253
|
method: "POST",
|
|
8254
8254
|
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
8255
8255
|
}, opts);
|
|
8256
8256
|
}
|
|
8257
|
-
patch(
|
|
8258
|
-
return this.request(
|
|
8257
|
+
patch(path75, body, opts) {
|
|
8258
|
+
return this.request(path75, {
|
|
8259
8259
|
method: "PATCH",
|
|
8260
8260
|
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
8261
8261
|
}, opts);
|
|
8262
8262
|
}
|
|
8263
|
-
delete(
|
|
8264
|
-
return this.request(
|
|
8263
|
+
delete(path75, opts) {
|
|
8264
|
+
return this.request(path75, { method: "DELETE" }, opts);
|
|
8265
8265
|
}
|
|
8266
|
-
async request(
|
|
8267
|
-
const url = buildUrl(this.apiBase,
|
|
8266
|
+
async request(path75, init, opts) {
|
|
8267
|
+
const url = buildUrl(this.apiBase, path75);
|
|
8268
8268
|
const headers = {
|
|
8269
8269
|
accept: "application/json",
|
|
8270
8270
|
...toStringRecord(init.headers)
|
|
@@ -9729,9 +9729,9 @@ async function fetchHostedIntegrations(session) {
|
|
|
9729
9729
|
}
|
|
9730
9730
|
async function fetchHostedIntegrationCredential(session, providerId) {
|
|
9731
9731
|
const client = toApiClient2(session);
|
|
9732
|
-
const
|
|
9732
|
+
const path75 = `${DEFAULT_INTEGRATION_CREDENTIAL_PATH}&provider=${encodeURIComponent(providerId)}`;
|
|
9733
9733
|
try {
|
|
9734
|
-
return await client.get(
|
|
9734
|
+
return await client.get(path75, { ignoreNotFound: true });
|
|
9735
9735
|
} catch (err) {
|
|
9736
9736
|
if (err instanceof ApiRequestError && (err.status === 404 || err.status === 501)) {
|
|
9737
9737
|
throw new HostedEndpointUnavailableError(err.status, err.message);
|
|
@@ -10134,7 +10134,7 @@ __export(github_exports, {
|
|
|
10134
10134
|
registerGithubCommands: () => registerGithubCommands
|
|
10135
10135
|
});
|
|
10136
10136
|
import * as p29 from "@clack/prompts";
|
|
10137
|
-
import
|
|
10137
|
+
import pc44 from "picocolors";
|
|
10138
10138
|
import open2 from "open";
|
|
10139
10139
|
async function sleep2(ms) {
|
|
10140
10140
|
await new Promise((r) => setTimeout(r, ms));
|
|
@@ -10157,14 +10157,14 @@ async function githubLogin(opts) {
|
|
|
10157
10157
|
if (opts.json) {
|
|
10158
10158
|
console.log(JSON.stringify({ status: "ok", mode: "pat", login: profile.login }, null, 2));
|
|
10159
10159
|
} else {
|
|
10160
|
-
p29.log.success(`Connected to GitHub as ${
|
|
10160
|
+
p29.log.success(`Connected to GitHub as ${pc44.cyan(profile.login)} (PAT).`);
|
|
10161
10161
|
}
|
|
10162
10162
|
return;
|
|
10163
10163
|
}
|
|
10164
|
-
p29.intro(
|
|
10164
|
+
p29.intro(pc44.cyan("GitHub device flow login"));
|
|
10165
10165
|
const start = await startDeviceFlow();
|
|
10166
10166
|
p29.log.step(
|
|
10167
|
-
`Open ${
|
|
10167
|
+
`Open ${pc44.cyan(start.verificationUri)} and enter code ${pc44.yellow(start.userCode)}`
|
|
10168
10168
|
);
|
|
10169
10169
|
if (!opts.noBrowser) {
|
|
10170
10170
|
try {
|
|
@@ -10193,7 +10193,7 @@ async function githubLogin(opts) {
|
|
|
10193
10193
|
if (opts.json) {
|
|
10194
10194
|
console.log(JSON.stringify({ status: "ok", mode: "device-flow", login: profile.login }));
|
|
10195
10195
|
} else {
|
|
10196
|
-
p29.outro(`Connected to GitHub as ${
|
|
10196
|
+
p29.outro(`Connected to GitHub as ${pc44.cyan(profile.login)}.`);
|
|
10197
10197
|
}
|
|
10198
10198
|
return;
|
|
10199
10199
|
}
|
|
@@ -10257,7 +10257,7 @@ async function githubWhoami(opts = {}) {
|
|
|
10257
10257
|
return;
|
|
10258
10258
|
}
|
|
10259
10259
|
if (token) {
|
|
10260
|
-
const status = directExpired ?
|
|
10260
|
+
const status = directExpired ? pc44.red("expired") : pc44.green("active");
|
|
10261
10261
|
p29.log.message(
|
|
10262
10262
|
`GitHub (direct): ${status} login=${profile?.login ?? token.login ?? "?"} mode=${token.authMode} scopes=[${token.scopes.join(", ")}]`
|
|
10263
10263
|
);
|
|
@@ -10265,7 +10265,7 @@ async function githubWhoami(opts = {}) {
|
|
|
10265
10265
|
if (bridge.growthubConnected) {
|
|
10266
10266
|
if (bridgeGithub) {
|
|
10267
10267
|
p29.log.message(
|
|
10268
|
-
`GitHub (via Growthub bridge): ${
|
|
10268
|
+
`GitHub (via Growthub bridge): ${pc44.green("connected")} handle=${bridgeGithub.handle ?? "?"} growthub=${bridge.growthubLogin ?? "?"} scopes=[${(bridgeGithub.scopes ?? []).join(", ")}]`
|
|
10269
10269
|
);
|
|
10270
10270
|
} else if (bridge.bridgeAvailable) {
|
|
10271
10271
|
p29.log.info(
|
|
@@ -10273,7 +10273,7 @@ async function githubWhoami(opts = {}) {
|
|
|
10273
10273
|
);
|
|
10274
10274
|
}
|
|
10275
10275
|
}
|
|
10276
|
-
p29.log.message(`Effective auth source: ${
|
|
10276
|
+
p29.log.message(`Effective auth source: ${pc44.cyan(effectiveSource)}`);
|
|
10277
10277
|
}
|
|
10278
10278
|
function githubLogout(opts = {}) {
|
|
10279
10279
|
clearGithubToken();
|
|
@@ -10306,24 +10306,24 @@ var init_github = __esm({
|
|
|
10306
10306
|
});
|
|
10307
10307
|
|
|
10308
10308
|
// src/starter/scaffold-session-memory.ts
|
|
10309
|
-
import
|
|
10310
|
-
import
|
|
10309
|
+
import fs50 from "node:fs";
|
|
10310
|
+
import path59 from "node:path";
|
|
10311
10311
|
function scaffoldSessionMemory(input) {
|
|
10312
|
-
const forkPath =
|
|
10313
|
-
const templatePath =
|
|
10314
|
-
const projectMdPath =
|
|
10315
|
-
if (!
|
|
10312
|
+
const forkPath = path59.resolve(input.forkPath);
|
|
10313
|
+
const templatePath = path59.join(forkPath, TEMPLATE_RELATIVE);
|
|
10314
|
+
const projectMdPath = path59.join(forkPath, PROJECT_MD_RELATIVE);
|
|
10315
|
+
if (!fs50.existsSync(templatePath)) {
|
|
10316
10316
|
return { written: false, projectMdPath, templatePath: null };
|
|
10317
10317
|
}
|
|
10318
|
-
if (
|
|
10318
|
+
if (fs50.existsSync(projectMdPath)) {
|
|
10319
10319
|
return { written: false, projectMdPath, templatePath };
|
|
10320
10320
|
}
|
|
10321
|
-
const template =
|
|
10321
|
+
const template = fs50.readFileSync(templatePath, "utf8");
|
|
10322
10322
|
const startedAt = input.startedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
10323
10323
|
const sourceRef = input.sourceRef ?? "";
|
|
10324
10324
|
const seeded = template.replaceAll("{{KIT_ID}}", input.kitId).replaceAll("{{FORK_ID}}", input.forkId).replaceAll("{{STARTED_AT}}", startedAt).replaceAll("{{SOURCE}}", input.source).replaceAll("{{SOURCE_REF}}", sourceRef);
|
|
10325
|
-
|
|
10326
|
-
|
|
10325
|
+
fs50.mkdirSync(path59.dirname(projectMdPath), { recursive: true });
|
|
10326
|
+
fs50.writeFileSync(projectMdPath, seeded, "utf8");
|
|
10327
10327
|
return { written: true, projectMdPath, templatePath };
|
|
10328
10328
|
}
|
|
10329
10329
|
var PROJECT_MD_RELATIVE, TEMPLATE_RELATIVE;
|
|
@@ -10336,12 +10336,12 @@ var init_scaffold_session_memory = __esm({
|
|
|
10336
10336
|
});
|
|
10337
10337
|
|
|
10338
10338
|
// src/starter/init.ts
|
|
10339
|
-
import
|
|
10340
|
-
import
|
|
10339
|
+
import fs51 from "node:fs";
|
|
10340
|
+
import path60 from "node:path";
|
|
10341
10341
|
async function initStarterWorkspace(opts) {
|
|
10342
10342
|
const kitId = opts.kitId ?? DEFAULT_STARTER_KIT_ID;
|
|
10343
|
-
const absOut =
|
|
10344
|
-
if (
|
|
10343
|
+
const absOut = path60.resolve(opts.out);
|
|
10344
|
+
if (fs51.existsSync(absOut) && fs51.readdirSync(absOut).length > 0) {
|
|
10345
10345
|
throw new Error(`Destination ${absOut} already exists and is not empty.`);
|
|
10346
10346
|
}
|
|
10347
10347
|
const info = getBundledKitSourceInfo(kitId);
|
|
@@ -10350,7 +10350,7 @@ async function initStarterWorkspace(opts) {
|
|
|
10350
10350
|
forkPath: absOut,
|
|
10351
10351
|
kitId: info.id,
|
|
10352
10352
|
baseVersion: info.version,
|
|
10353
|
-
label: opts.name?.trim() ||
|
|
10353
|
+
label: opts.name?.trim() || path60.basename(absOut)
|
|
10354
10354
|
});
|
|
10355
10355
|
const policy = {
|
|
10356
10356
|
...makeDefaultKitForkPolicy(),
|
|
@@ -10460,8 +10460,8 @@ var init_types2 = __esm({
|
|
|
10460
10460
|
});
|
|
10461
10461
|
|
|
10462
10462
|
// src/starter/source-import/github-source.ts
|
|
10463
|
-
import
|
|
10464
|
-
import
|
|
10463
|
+
import fs52 from "node:fs";
|
|
10464
|
+
import path61 from "node:path";
|
|
10465
10465
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
10466
10466
|
function baseHeaders() {
|
|
10467
10467
|
return {
|
|
@@ -10592,12 +10592,12 @@ function cloneGithubRepo(input) {
|
|
|
10592
10592
|
if (!gitAvailable()) {
|
|
10593
10593
|
throw new Error("`git` is not available on PATH \u2014 cannot clone.");
|
|
10594
10594
|
}
|
|
10595
|
-
if (
|
|
10595
|
+
if (fs52.existsSync(input.destination)) {
|
|
10596
10596
|
throw new Error(`Clone destination already exists: ${input.destination}`);
|
|
10597
10597
|
}
|
|
10598
10598
|
const cloneUrl = input.token ? buildTokenCloneUrl(input.probe.repo, input.token) : input.probe.cloneUrl;
|
|
10599
|
-
const parent =
|
|
10600
|
-
|
|
10599
|
+
const parent = path61.dirname(input.destination);
|
|
10600
|
+
fs52.mkdirSync(parent, { recursive: true });
|
|
10601
10601
|
const depth = input.depth ?? 1;
|
|
10602
10602
|
const branch = input.branch ?? input.probe.defaultBranch;
|
|
10603
10603
|
const args = ["clone"];
|
|
@@ -10624,17 +10624,17 @@ function cloneGithubRepo(input) {
|
|
|
10624
10624
|
function narrowToSubdirectory(rootDir, subdirectory) {
|
|
10625
10625
|
const normalizedSub = subdirectory.replace(/^\/+|\/+$/g, "");
|
|
10626
10626
|
if (!normalizedSub) return;
|
|
10627
|
-
const abs =
|
|
10628
|
-
if (!
|
|
10627
|
+
const abs = path61.resolve(rootDir, normalizedSub);
|
|
10628
|
+
if (!fs52.existsSync(abs) || !fs52.statSync(abs).isDirectory()) {
|
|
10629
10629
|
throw new Error(`Subdirectory not found in cloned repo: ${subdirectory}`);
|
|
10630
10630
|
}
|
|
10631
|
-
const tmp =
|
|
10632
|
-
|
|
10633
|
-
`.${
|
|
10631
|
+
const tmp = path61.resolve(
|
|
10632
|
+
path61.dirname(rootDir),
|
|
10633
|
+
`.${path61.basename(rootDir)}-narrow-${Date.now().toString(36)}`
|
|
10634
10634
|
);
|
|
10635
|
-
|
|
10636
|
-
|
|
10637
|
-
|
|
10635
|
+
fs52.renameSync(abs, tmp);
|
|
10636
|
+
fs52.rmSync(rootDir, { recursive: true, force: true });
|
|
10637
|
+
fs52.renameSync(tmp, rootDir);
|
|
10638
10638
|
}
|
|
10639
10639
|
var GITHUB_API_BASE2;
|
|
10640
10640
|
var init_github_source = __esm({
|
|
@@ -10648,9 +10648,9 @@ var init_github_source = __esm({
|
|
|
10648
10648
|
});
|
|
10649
10649
|
|
|
10650
10650
|
// src/starter/source-import/skills-source.ts
|
|
10651
|
-
import
|
|
10651
|
+
import fs53 from "node:fs";
|
|
10652
10652
|
import os11 from "node:os";
|
|
10653
|
-
import
|
|
10653
|
+
import path62 from "node:path";
|
|
10654
10654
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
10655
10655
|
function resolveBase() {
|
|
10656
10656
|
const raw = process.env.SKILLS_SH_BASE?.trim();
|
|
@@ -10916,9 +10916,9 @@ async function probeSkillsSource(input) {
|
|
|
10916
10916
|
};
|
|
10917
10917
|
}
|
|
10918
10918
|
function assertInsidePayloadRoot(root, candidate) {
|
|
10919
|
-
const abs =
|
|
10920
|
-
const rootAbs =
|
|
10921
|
-
if (!abs.startsWith(rootAbs +
|
|
10919
|
+
const abs = path62.resolve(candidate);
|
|
10920
|
+
const rootAbs = path62.resolve(root);
|
|
10921
|
+
if (!abs.startsWith(rootAbs + path62.sep) && abs !== rootAbs) {
|
|
10922
10922
|
throw new Error(`Refusing to write outside payload root: ${candidate}`);
|
|
10923
10923
|
}
|
|
10924
10924
|
}
|
|
@@ -10934,24 +10934,24 @@ function runGit3(args, cwd) {
|
|
|
10934
10934
|
};
|
|
10935
10935
|
}
|
|
10936
10936
|
function skillDirectoryMatches(dir, skillSlug) {
|
|
10937
|
-
const skillFile =
|
|
10938
|
-
if (!
|
|
10937
|
+
const skillFile = path62.resolve(dir, "SKILL.md");
|
|
10938
|
+
if (!fs53.existsSync(skillFile) || !fs53.statSync(skillFile).isFile()) {
|
|
10939
10939
|
return false;
|
|
10940
10940
|
}
|
|
10941
|
-
if (
|
|
10941
|
+
if (path62.basename(dir) === skillSlug) {
|
|
10942
10942
|
return true;
|
|
10943
10943
|
}
|
|
10944
|
-
const content =
|
|
10944
|
+
const content = fs53.readFileSync(skillFile, "utf8");
|
|
10945
10945
|
const nameMatch = content.match(/(?:^|\n)name:\s*["']?([A-Za-z0-9._:-]+)["']?\s*(?:\n|$)/i);
|
|
10946
10946
|
return nameMatch?.[1] === skillSlug;
|
|
10947
10947
|
}
|
|
10948
10948
|
function locateSkillDirectory(root, skillSlug) {
|
|
10949
10949
|
const preferred = [
|
|
10950
|
-
|
|
10951
|
-
|
|
10950
|
+
path62.resolve(root, "skills", skillSlug),
|
|
10951
|
+
path62.resolve(root, skillSlug)
|
|
10952
10952
|
];
|
|
10953
10953
|
for (const candidate of preferred) {
|
|
10954
|
-
if (
|
|
10954
|
+
if (fs53.existsSync(candidate) && fs53.statSync(candidate).isDirectory() && skillDirectoryMatches(candidate, skillSlug)) {
|
|
10955
10955
|
return candidate;
|
|
10956
10956
|
}
|
|
10957
10957
|
}
|
|
@@ -10961,12 +10961,12 @@ function locateSkillDirectory(root, skillSlug) {
|
|
|
10961
10961
|
if (skillDirectoryMatches(current, skillSlug)) {
|
|
10962
10962
|
return current;
|
|
10963
10963
|
}
|
|
10964
|
-
for (const entry of
|
|
10964
|
+
for (const entry of fs53.readdirSync(current, { withFileTypes: true })) {
|
|
10965
10965
|
if (!entry.isDirectory()) continue;
|
|
10966
10966
|
if ([".git", "node_modules", ".next", "dist", "build", "coverage"].includes(entry.name)) {
|
|
10967
10967
|
continue;
|
|
10968
10968
|
}
|
|
10969
|
-
queue.push(
|
|
10969
|
+
queue.push(path62.resolve(current, entry.name));
|
|
10970
10970
|
}
|
|
10971
10971
|
}
|
|
10972
10972
|
return null;
|
|
@@ -10976,18 +10976,18 @@ function copySkillTree(sourceDir, destination) {
|
|
|
10976
10976
|
const stack = [{ from: sourceDir, to: destination }];
|
|
10977
10977
|
while (stack.length > 0) {
|
|
10978
10978
|
const current = stack.pop();
|
|
10979
|
-
|
|
10980
|
-
for (const entry of
|
|
10981
|
-
const fromPath =
|
|
10982
|
-
const toPath =
|
|
10979
|
+
fs53.mkdirSync(current.to, { recursive: true });
|
|
10980
|
+
for (const entry of fs53.readdirSync(current.from, { withFileTypes: true })) {
|
|
10981
|
+
const fromPath = path62.resolve(current.from, entry.name);
|
|
10982
|
+
const toPath = path62.resolve(current.to, entry.name);
|
|
10983
10983
|
assertInsidePayloadRoot(destination, toPath);
|
|
10984
10984
|
if (entry.isDirectory()) {
|
|
10985
10985
|
stack.push({ from: fromPath, to: toPath });
|
|
10986
10986
|
continue;
|
|
10987
10987
|
}
|
|
10988
|
-
const data =
|
|
10989
|
-
|
|
10990
|
-
|
|
10988
|
+
const data = fs53.readFileSync(fromPath);
|
|
10989
|
+
fs53.mkdirSync(path62.dirname(toPath), { recursive: true });
|
|
10990
|
+
fs53.writeFileSync(toPath, data, { mode: 420 });
|
|
10991
10991
|
written += 1;
|
|
10992
10992
|
}
|
|
10993
10993
|
}
|
|
@@ -10998,7 +10998,7 @@ async function fetchSkillPayload(input) {
|
|
|
10998
10998
|
if (!gitAvailable()) {
|
|
10999
10999
|
throw new Error("`git` is not available on PATH \u2014 cannot materialize a skills.sh payload.");
|
|
11000
11000
|
}
|
|
11001
|
-
if (
|
|
11001
|
+
if (fs53.existsSync(destination)) {
|
|
11002
11002
|
throw new Error(`Skill payload destination already exists: ${destination}`);
|
|
11003
11003
|
}
|
|
11004
11004
|
const repoSource = probe.repoUrl ?? (probe.repository ? `https://github.com/${probe.repository}` : void 0);
|
|
@@ -11006,11 +11006,11 @@ async function fetchSkillPayload(input) {
|
|
|
11006
11006
|
if (!repoSource || !skillSlug) {
|
|
11007
11007
|
throw new Error(`Skill '${probe.skillId}' is missing repository metadata \u2014 cannot materialize payload.`);
|
|
11008
11008
|
}
|
|
11009
|
-
const cloneRoot =
|
|
11010
|
-
|
|
11009
|
+
const cloneRoot = fs53.mkdtempSync(
|
|
11010
|
+
path62.join(os11.tmpdir(), "growthub-skills-source-")
|
|
11011
11011
|
);
|
|
11012
11012
|
try {
|
|
11013
|
-
const cloneRes = runGit3(["clone", "--depth", "1", repoSource, cloneRoot],
|
|
11013
|
+
const cloneRes = runGit3(["clone", "--depth", "1", repoSource, cloneRoot], path62.dirname(cloneRoot));
|
|
11014
11014
|
if (!cloneRes.ok) {
|
|
11015
11015
|
throw new Error(`git clone failed: ${cloneRes.stderr || "unable to clone skill repository"}`);
|
|
11016
11016
|
}
|
|
@@ -11023,7 +11023,7 @@ async function fetchSkillPayload(input) {
|
|
|
11023
11023
|
const fileCount = copySkillTree(skillDir, destination);
|
|
11024
11024
|
return { destination, fileCount };
|
|
11025
11025
|
} finally {
|
|
11026
|
-
|
|
11026
|
+
fs53.rmSync(cloneRoot, { recursive: true, force: true });
|
|
11027
11027
|
}
|
|
11028
11028
|
}
|
|
11029
11029
|
var DEFAULT_BASE, COMMENT_PATTERN;
|
|
@@ -11037,13 +11037,13 @@ var init_skills_source = __esm({
|
|
|
11037
11037
|
});
|
|
11038
11038
|
|
|
11039
11039
|
// src/starter/source-import/detect.ts
|
|
11040
|
-
import
|
|
11041
|
-
import
|
|
11040
|
+
import fs54 from "node:fs";
|
|
11041
|
+
import path63 from "node:path";
|
|
11042
11042
|
function safeReadPackageJson(dir) {
|
|
11043
|
-
const p36 =
|
|
11044
|
-
if (!
|
|
11043
|
+
const p36 = path63.resolve(dir, "package.json");
|
|
11044
|
+
if (!fs54.existsSync(p36)) return null;
|
|
11045
11045
|
try {
|
|
11046
|
-
return JSON.parse(
|
|
11046
|
+
return JSON.parse(fs54.readFileSync(p36, "utf8"));
|
|
11047
11047
|
} catch {
|
|
11048
11048
|
return null;
|
|
11049
11049
|
}
|
|
@@ -11053,10 +11053,10 @@ function detectPackageManager(dir, pkg) {
|
|
|
11053
11053
|
if (pkg?.packageManager?.startsWith("yarn")) return "yarn";
|
|
11054
11054
|
if (pkg?.packageManager?.startsWith("npm")) return "npm";
|
|
11055
11055
|
if (pkg?.packageManager?.startsWith("bun")) return "bun";
|
|
11056
|
-
if (
|
|
11057
|
-
if (
|
|
11058
|
-
if (
|
|
11059
|
-
if (
|
|
11056
|
+
if (fs54.existsSync(path63.resolve(dir, "pnpm-lock.yaml"))) return "pnpm";
|
|
11057
|
+
if (fs54.existsSync(path63.resolve(dir, "yarn.lock"))) return "yarn";
|
|
11058
|
+
if (fs54.existsSync(path63.resolve(dir, "bun.lockb"))) return "bun";
|
|
11059
|
+
if (fs54.existsSync(path63.resolve(dir, "package-lock.json"))) return "npm";
|
|
11060
11060
|
return "unknown";
|
|
11061
11061
|
}
|
|
11062
11062
|
function collectDeps(pkg) {
|
|
@@ -11070,12 +11070,12 @@ function collectDeps(pkg) {
|
|
|
11070
11070
|
}
|
|
11071
11071
|
function looksLikeSkillPayload(rootDir) {
|
|
11072
11072
|
const markers = ["SKILL.md", "skill.md", "skill.json", "skill.yml", "skill.yaml", "prompt.md"];
|
|
11073
|
-
return markers.some((name) =>
|
|
11073
|
+
return markers.some((name) => fs54.existsSync(path63.resolve(rootDir, name)));
|
|
11074
11074
|
}
|
|
11075
11075
|
function detectFramework(rootDir, pkg) {
|
|
11076
11076
|
if (!pkg) {
|
|
11077
11077
|
if (looksLikeSkillPayload(rootDir)) return "skill";
|
|
11078
|
-
if (
|
|
11078
|
+
if (fs54.existsSync(path63.resolve(rootDir, "docs"))) return "docs";
|
|
11079
11079
|
return "unknown";
|
|
11080
11080
|
}
|
|
11081
11081
|
const deps = collectDeps(pkg);
|
|
@@ -11084,8 +11084,8 @@ function detectFramework(rootDir, pkg) {
|
|
|
11084
11084
|
"vite.config.ts",
|
|
11085
11085
|
"vite.config.mjs",
|
|
11086
11086
|
"vite.config.cjs"
|
|
11087
|
-
].some((name) =>
|
|
11088
|
-
if (deps.has("next") ||
|
|
11087
|
+
].some((name) => fs54.existsSync(path63.resolve(rootDir, name)));
|
|
11088
|
+
if (deps.has("next") || fs54.existsSync(path63.resolve(rootDir, "next.config.js")) || fs54.existsSync(path63.resolve(rootDir, "next.config.mjs"))) {
|
|
11089
11089
|
return "next";
|
|
11090
11090
|
}
|
|
11091
11091
|
if (deps.has("vite") || hasViteConfig) return "vite";
|
|
@@ -11111,15 +11111,15 @@ function pickScripts(pkg) {
|
|
|
11111
11111
|
return out;
|
|
11112
11112
|
}
|
|
11113
11113
|
function listEnvFiles(dir) {
|
|
11114
|
-
if (!
|
|
11115
|
-
return
|
|
11114
|
+
if (!fs54.existsSync(dir)) return [];
|
|
11115
|
+
return fs54.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name).filter((name) => name === ".env" || name.startsWith(".env.") || name === ".env.example");
|
|
11116
11116
|
}
|
|
11117
11117
|
function findAppRoot(rootDir, pkg) {
|
|
11118
11118
|
if (pkg) return ".";
|
|
11119
11119
|
const candidates = ["app", "src", "apps", "packages"];
|
|
11120
11120
|
for (const candidate of candidates) {
|
|
11121
|
-
const abs =
|
|
11122
|
-
if (
|
|
11121
|
+
const abs = path63.resolve(rootDir, candidate);
|
|
11122
|
+
if (fs54.existsSync(abs) && fs54.statSync(abs).isDirectory()) {
|
|
11123
11123
|
const child = safeReadPackageJson(abs);
|
|
11124
11124
|
if (child) return candidate;
|
|
11125
11125
|
}
|
|
@@ -11135,12 +11135,12 @@ function computeConfidence(framework, manager, pkg) {
|
|
|
11135
11135
|
return Math.min(1, Number(score.toFixed(2)));
|
|
11136
11136
|
}
|
|
11137
11137
|
function detectSourceShape(rootDir) {
|
|
11138
|
-
if (!
|
|
11138
|
+
if (!fs54.existsSync(rootDir) || !fs54.statSync(rootDir).isDirectory()) {
|
|
11139
11139
|
throw new Error(`Detection target is not a directory: ${rootDir}`);
|
|
11140
11140
|
}
|
|
11141
11141
|
const rootPkg = safeReadPackageJson(rootDir);
|
|
11142
11142
|
const appRootRel = findAppRoot(rootDir, rootPkg);
|
|
11143
|
-
const appRootAbs =
|
|
11143
|
+
const appRootAbs = path63.resolve(rootDir, appRootRel);
|
|
11144
11144
|
const appPkg = appRootRel === "." ? rootPkg : safeReadPackageJson(appRootAbs);
|
|
11145
11145
|
const framework = detectFramework(appRootAbs, appPkg ?? rootPkg);
|
|
11146
11146
|
const packageManager = detectPackageManager(rootDir, rootPkg ?? appPkg);
|
|
@@ -11180,18 +11180,18 @@ var init_detect = __esm({
|
|
|
11180
11180
|
});
|
|
11181
11181
|
|
|
11182
11182
|
// src/starter/source-import/security.ts
|
|
11183
|
-
import
|
|
11184
|
-
import
|
|
11183
|
+
import fs55 from "node:fs";
|
|
11184
|
+
import path64 from "node:path";
|
|
11185
11185
|
function isLikelyTextFile(filename) {
|
|
11186
|
-
const ext =
|
|
11186
|
+
const ext = path64.extname(filename).toLowerCase();
|
|
11187
11187
|
if (!ext) return true;
|
|
11188
11188
|
return TEXT_EXTENSIONS.has(ext);
|
|
11189
11189
|
}
|
|
11190
11190
|
function isSuspiciousBinary(filename) {
|
|
11191
|
-
return SUSPICIOUS_BINARY_EXTENSIONS.has(
|
|
11191
|
+
return SUSPICIOUS_BINARY_EXTENSIONS.has(path64.extname(filename).toLowerCase());
|
|
11192
11192
|
}
|
|
11193
11193
|
function isUnexpectedArchive(filename) {
|
|
11194
|
-
const ext =
|
|
11194
|
+
const ext = path64.extname(filename).toLowerCase();
|
|
11195
11195
|
return ARCHIVE_EXTENSIONS.has(ext) || filename.toLowerCase().endsWith(".tar.gz");
|
|
11196
11196
|
}
|
|
11197
11197
|
function shortExcerpt(line) {
|
|
@@ -11246,19 +11246,19 @@ function walkPayload(root, onFile, limits) {
|
|
|
11246
11246
|
if (!current) break;
|
|
11247
11247
|
let entries;
|
|
11248
11248
|
try {
|
|
11249
|
-
entries =
|
|
11249
|
+
entries = fs55.readdirSync(current, { withFileTypes: true });
|
|
11250
11250
|
} catch {
|
|
11251
11251
|
continue;
|
|
11252
11252
|
}
|
|
11253
11253
|
for (const entry of entries) {
|
|
11254
|
-
const abs =
|
|
11254
|
+
const abs = path64.resolve(current, entry.name);
|
|
11255
11255
|
if (entry.isDirectory()) {
|
|
11256
11256
|
if (entry.name === ".git" || entry.name === "node_modules") continue;
|
|
11257
11257
|
stack.push(abs);
|
|
11258
11258
|
continue;
|
|
11259
11259
|
}
|
|
11260
11260
|
if (!entry.isFile()) continue;
|
|
11261
|
-
const rel =
|
|
11261
|
+
const rel = path64.relative(root, abs);
|
|
11262
11262
|
onFile(abs, rel);
|
|
11263
11263
|
visited += 1;
|
|
11264
11264
|
if (visited >= limits.maxFiles) break;
|
|
@@ -11268,7 +11268,7 @@ function walkPayload(root, onFile, limits) {
|
|
|
11268
11268
|
}
|
|
11269
11269
|
function inspectSourcePayload(input) {
|
|
11270
11270
|
const { payloadRoot } = input;
|
|
11271
|
-
if (!
|
|
11271
|
+
if (!fs55.existsSync(payloadRoot) || !fs55.statSync(payloadRoot).isDirectory()) {
|
|
11272
11272
|
throw new Error(`Inspection target is not a directory: ${payloadRoot}`);
|
|
11273
11273
|
}
|
|
11274
11274
|
const findings = [];
|
|
@@ -11278,7 +11278,7 @@ function inspectSourcePayload(input) {
|
|
|
11278
11278
|
(abs, rel) => {
|
|
11279
11279
|
let size = 0;
|
|
11280
11280
|
try {
|
|
11281
|
-
size =
|
|
11281
|
+
size = fs55.statSync(abs).size;
|
|
11282
11282
|
} catch {
|
|
11283
11283
|
return;
|
|
11284
11284
|
}
|
|
@@ -11287,7 +11287,7 @@ function inspectSourcePayload(input) {
|
|
|
11287
11287
|
category: "suspicious-binary",
|
|
11288
11288
|
severity: "high-risk",
|
|
11289
11289
|
path: rel,
|
|
11290
|
-
message: `Payload ships a precompiled binary (${
|
|
11290
|
+
message: `Payload ships a precompiled binary (${path64.extname(rel)}). Review provenance before use.`
|
|
11291
11291
|
});
|
|
11292
11292
|
return;
|
|
11293
11293
|
}
|
|
@@ -11296,7 +11296,7 @@ function inspectSourcePayload(input) {
|
|
|
11296
11296
|
category: "unexpected-archive",
|
|
11297
11297
|
severity: "caution",
|
|
11298
11298
|
path: rel,
|
|
11299
|
-
message: `Payload ships an archive (${
|
|
11299
|
+
message: `Payload ships an archive (${path64.extname(rel)}) \u2014 expand and review contents before use.`
|
|
11300
11300
|
});
|
|
11301
11301
|
return;
|
|
11302
11302
|
}
|
|
@@ -11304,12 +11304,12 @@ function inspectSourcePayload(input) {
|
|
|
11304
11304
|
if (bytesInspected + Math.min(size, MAX_BYTES_PER_FILE) > MAX_TOTAL_BYTES) return;
|
|
11305
11305
|
let buf;
|
|
11306
11306
|
try {
|
|
11307
|
-
const handle =
|
|
11307
|
+
const handle = fs55.openSync(abs, "r");
|
|
11308
11308
|
try {
|
|
11309
11309
|
buf = Buffer.alloc(Math.min(size, MAX_BYTES_PER_FILE));
|
|
11310
|
-
|
|
11310
|
+
fs55.readSync(handle, buf, 0, buf.length, 0);
|
|
11311
11311
|
} finally {
|
|
11312
|
-
|
|
11312
|
+
fs55.closeSync(handle);
|
|
11313
11313
|
}
|
|
11314
11314
|
} catch {
|
|
11315
11315
|
return;
|
|
@@ -11518,18 +11518,18 @@ var init_security = __esm({
|
|
|
11518
11518
|
});
|
|
11519
11519
|
|
|
11520
11520
|
// src/starter/source-import/plan.ts
|
|
11521
|
-
import
|
|
11522
|
-
import
|
|
11521
|
+
import fs56 from "node:fs";
|
|
11522
|
+
import path65 from "node:path";
|
|
11523
11523
|
function generateImportId() {
|
|
11524
11524
|
return `si-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`;
|
|
11525
11525
|
}
|
|
11526
11526
|
function destinationState(absDest) {
|
|
11527
|
-
if (!
|
|
11528
|
-
const stats =
|
|
11527
|
+
if (!fs56.existsSync(absDest)) return { exists: false, nonEmpty: false };
|
|
11528
|
+
const stats = fs56.statSync(absDest);
|
|
11529
11529
|
if (!stats.isDirectory()) {
|
|
11530
11530
|
throw new Error(`Destination is not a directory: ${absDest}`);
|
|
11531
11531
|
}
|
|
11532
|
-
const entries =
|
|
11532
|
+
const entries = fs56.readdirSync(absDest);
|
|
11533
11533
|
return { exists: true, nonEmpty: entries.length > 0 };
|
|
11534
11534
|
}
|
|
11535
11535
|
function describeSource2(probe) {
|
|
@@ -11539,7 +11539,7 @@ function describeSource2(probe) {
|
|
|
11539
11539
|
return `skill ${probe.skillId}@${probe.version} (skills.sh)`;
|
|
11540
11540
|
}
|
|
11541
11541
|
function buildSourceImportPlan(input) {
|
|
11542
|
-
const absDest =
|
|
11542
|
+
const absDest = path65.resolve(input.destination);
|
|
11543
11543
|
const state = destinationState(absDest);
|
|
11544
11544
|
const payloadPath = "imported";
|
|
11545
11545
|
const warnings = [...input.probe.warnings];
|
|
@@ -11652,8 +11652,8 @@ var init_plan = __esm({
|
|
|
11652
11652
|
});
|
|
11653
11653
|
|
|
11654
11654
|
// src/starter/source-import/summarize.ts
|
|
11655
|
-
import
|
|
11656
|
-
import
|
|
11655
|
+
import fs57 from "node:fs";
|
|
11656
|
+
import path66 from "node:path";
|
|
11657
11657
|
function sourceHeading(manifest) {
|
|
11658
11658
|
const src = manifest.source;
|
|
11659
11659
|
if (src.kind === "github-repo") {
|
|
@@ -11708,7 +11708,7 @@ function nextStepsSection(manifest) {
|
|
|
11708
11708
|
}
|
|
11709
11709
|
function writeImportSummary(input) {
|
|
11710
11710
|
const { forkPath, summaryRelativePath, manifest } = input;
|
|
11711
|
-
const summaryPath =
|
|
11711
|
+
const summaryPath = path66.resolve(forkPath, summaryRelativePath);
|
|
11712
11712
|
const body = [
|
|
11713
11713
|
`# Source Import Summary`,
|
|
11714
11714
|
``,
|
|
@@ -11738,8 +11738,8 @@ function writeImportSummary(input) {
|
|
|
11738
11738
|
`Generated by the Growthub Source Import Agent. Canonical manifest lives at \`.growthub-fork/source-import.json\`.`,
|
|
11739
11739
|
``
|
|
11740
11740
|
].join("\n");
|
|
11741
|
-
|
|
11742
|
-
|
|
11741
|
+
fs57.mkdirSync(path66.dirname(summaryPath), { recursive: true });
|
|
11742
|
+
fs57.writeFileSync(summaryPath, body, "utf8");
|
|
11743
11743
|
return summaryPath;
|
|
11744
11744
|
}
|
|
11745
11745
|
var init_summarize = __esm({
|
|
@@ -11749,16 +11749,16 @@ var init_summarize = __esm({
|
|
|
11749
11749
|
});
|
|
11750
11750
|
|
|
11751
11751
|
// src/starter/source-import/materialize.ts
|
|
11752
|
-
import
|
|
11752
|
+
import fs58 from "node:fs";
|
|
11753
11753
|
import os12 from "node:os";
|
|
11754
|
-
import
|
|
11754
|
+
import path67 from "node:path";
|
|
11755
11755
|
function resolveSourceKind(probe) {
|
|
11756
11756
|
return probe.kind === "github-repo" ? "github-repo" : "skills-skill";
|
|
11757
11757
|
}
|
|
11758
11758
|
function stagingDirFor(forkPath) {
|
|
11759
|
-
return
|
|
11759
|
+
return path67.join(
|
|
11760
11760
|
os12.tmpdir(),
|
|
11761
|
-
`growthub-source-import-${
|
|
11761
|
+
`growthub-source-import-${path67.basename(forkPath)}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`
|
|
11762
11762
|
);
|
|
11763
11763
|
}
|
|
11764
11764
|
async function fetchPayload(probe, stagingDir, opts) {
|
|
@@ -11790,18 +11790,18 @@ async function fetchPayload(probe, stagingDir, opts) {
|
|
|
11790
11790
|
return { payloadRoot: stagingDir };
|
|
11791
11791
|
}
|
|
11792
11792
|
function movePayloadIntoFork(payloadRoot, forkPath, payloadRelativePath) {
|
|
11793
|
-
const target =
|
|
11794
|
-
if (
|
|
11795
|
-
|
|
11793
|
+
const target = path67.resolve(forkPath, payloadRelativePath);
|
|
11794
|
+
if (fs58.existsSync(target)) {
|
|
11795
|
+
fs58.rmSync(target, { recursive: true, force: true });
|
|
11796
11796
|
}
|
|
11797
|
-
|
|
11798
|
-
|
|
11797
|
+
fs58.mkdirSync(path67.dirname(target), { recursive: true });
|
|
11798
|
+
fs58.renameSync(payloadRoot, target);
|
|
11799
11799
|
return target;
|
|
11800
11800
|
}
|
|
11801
11801
|
function writeManifest(forkPath, manifest) {
|
|
11802
|
-
const p36 =
|
|
11803
|
-
|
|
11804
|
-
|
|
11802
|
+
const p36 = path67.resolve(forkPath, MANIFEST_RELATIVE_PATH);
|
|
11803
|
+
fs58.mkdirSync(path67.dirname(p36), { recursive: true });
|
|
11804
|
+
fs58.writeFileSync(p36, JSON.stringify(manifest, null, 2) + "\n", "utf8");
|
|
11805
11805
|
return p36;
|
|
11806
11806
|
}
|
|
11807
11807
|
function assertConfirmationsSatisfied(plan, confirmations) {
|
|
@@ -11841,7 +11841,7 @@ async function materializeImportPlan(input) {
|
|
|
11841
11841
|
requireSkillAcknowledgement: sourceKind === "skills-skill"
|
|
11842
11842
|
});
|
|
11843
11843
|
if (security.blocked) {
|
|
11844
|
-
|
|
11844
|
+
fs58.rmSync(fetchResult.payloadRoot, { recursive: true, force: true });
|
|
11845
11845
|
throw new Error(
|
|
11846
11846
|
`Security inspection blocked the fetched payload: ${security.summaryLines[0] ?? "blocking finding"}`
|
|
11847
11847
|
);
|
|
@@ -11989,26 +11989,26 @@ var init_materialize = __esm({
|
|
|
11989
11989
|
});
|
|
11990
11990
|
|
|
11991
11991
|
// src/starter/source-import/agent.ts
|
|
11992
|
-
import
|
|
11993
|
-
import
|
|
11992
|
+
import fs59 from "node:fs";
|
|
11993
|
+
import path68 from "node:path";
|
|
11994
11994
|
function resolveJobsDir() {
|
|
11995
|
-
return
|
|
11995
|
+
return path68.resolve(resolveKitForksHomeDir(), "source-import-jobs");
|
|
11996
11996
|
}
|
|
11997
11997
|
function resolveJobPath2(jobId) {
|
|
11998
|
-
return
|
|
11998
|
+
return path68.resolve(resolveJobsDir(), `${jobId}.json`);
|
|
11999
11999
|
}
|
|
12000
12000
|
function generateJobId2() {
|
|
12001
12001
|
return `sij-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`;
|
|
12002
12002
|
}
|
|
12003
12003
|
function writeJob2(job) {
|
|
12004
12004
|
const p36 = resolveJobPath2(job.jobId);
|
|
12005
|
-
|
|
12006
|
-
|
|
12005
|
+
fs59.mkdirSync(path68.dirname(p36), { recursive: true });
|
|
12006
|
+
fs59.writeFileSync(p36, JSON.stringify(job, null, 2) + "\n", "utf8");
|
|
12007
12007
|
}
|
|
12008
12008
|
function readJobFile(p36) {
|
|
12009
|
-
if (!
|
|
12009
|
+
if (!fs59.existsSync(p36)) return null;
|
|
12010
12010
|
try {
|
|
12011
|
-
return JSON.parse(
|
|
12011
|
+
return JSON.parse(fs59.readFileSync(p36, "utf8"));
|
|
12012
12012
|
} catch {
|
|
12013
12013
|
return null;
|
|
12014
12014
|
}
|
|
@@ -12018,7 +12018,7 @@ function patchJob2(jobId, status, patch = {}) {
|
|
|
12018
12018
|
const job = readJobFile(p36);
|
|
12019
12019
|
if (!job) return null;
|
|
12020
12020
|
const updated = { ...job, ...patch, status };
|
|
12021
|
-
|
|
12021
|
+
fs59.writeFileSync(p36, JSON.stringify(updated, null, 2) + "\n", "utf8");
|
|
12022
12022
|
return updated;
|
|
12023
12023
|
}
|
|
12024
12024
|
function getSourceImportJob(jobId) {
|
|
@@ -12037,7 +12037,7 @@ async function probeAndPlan(input, destination) {
|
|
|
12037
12037
|
}
|
|
12038
12038
|
async function runSourceImportJob(input) {
|
|
12039
12039
|
const jobId = generateJobId2();
|
|
12040
|
-
const destination =
|
|
12040
|
+
const destination = path68.resolve(input.out);
|
|
12041
12041
|
const sourceKind = input.source.kind;
|
|
12042
12042
|
const initial = {
|
|
12043
12043
|
jobId,
|
|
@@ -12347,11 +12347,11 @@ var catalog_exports = {};
|
|
|
12347
12347
|
__export(catalog_exports, {
|
|
12348
12348
|
readSkillCatalog: () => readSkillCatalog
|
|
12349
12349
|
});
|
|
12350
|
-
import
|
|
12351
|
-
import
|
|
12350
|
+
import fs60 from "node:fs";
|
|
12351
|
+
import path69 from "node:path";
|
|
12352
12352
|
function exists(p36) {
|
|
12353
12353
|
try {
|
|
12354
|
-
|
|
12354
|
+
fs60.accessSync(p36);
|
|
12355
12355
|
return true;
|
|
12356
12356
|
} catch {
|
|
12357
12357
|
return false;
|
|
@@ -12359,14 +12359,14 @@ function exists(p36) {
|
|
|
12359
12359
|
}
|
|
12360
12360
|
function isDir2(p36) {
|
|
12361
12361
|
try {
|
|
12362
|
-
return
|
|
12362
|
+
return fs60.statSync(p36).isDirectory();
|
|
12363
12363
|
} catch {
|
|
12364
12364
|
return false;
|
|
12365
12365
|
}
|
|
12366
12366
|
}
|
|
12367
12367
|
function safeRead(p36) {
|
|
12368
12368
|
try {
|
|
12369
|
-
return
|
|
12369
|
+
return fs60.readFileSync(p36, "utf8");
|
|
12370
12370
|
} catch {
|
|
12371
12371
|
return null;
|
|
12372
12372
|
}
|
|
@@ -12451,10 +12451,10 @@ function readOne(skillPath, source) {
|
|
|
12451
12451
|
}
|
|
12452
12452
|
function readSkillDirs(baseDir, source, out, warnings) {
|
|
12453
12453
|
if (!isDir2(baseDir)) return;
|
|
12454
|
-
for (const child of
|
|
12455
|
-
const dir =
|
|
12454
|
+
for (const child of fs60.readdirSync(baseDir).sort()) {
|
|
12455
|
+
const dir = path69.join(baseDir, child);
|
|
12456
12456
|
if (!isDir2(dir)) continue;
|
|
12457
|
-
const skill =
|
|
12457
|
+
const skill = path69.join(dir, "SKILL.md");
|
|
12458
12458
|
if (!exists(skill)) continue;
|
|
12459
12459
|
const res = readOne(skill, source);
|
|
12460
12460
|
if ("entry" in res) out.push(res.entry);
|
|
@@ -12462,16 +12462,16 @@ function readSkillDirs(baseDir, source, out, warnings) {
|
|
|
12462
12462
|
}
|
|
12463
12463
|
}
|
|
12464
12464
|
function readKitSubSkills(kitDir, out, warnings, depth = 0) {
|
|
12465
|
-
const base =
|
|
12465
|
+
const base = path69.join(kitDir, "skills");
|
|
12466
12466
|
if (!isDir2(base)) return;
|
|
12467
12467
|
const walk = (dir, d) => {
|
|
12468
12468
|
if (d > MAX_DEPTH) return;
|
|
12469
|
-
for (const entry of
|
|
12469
|
+
for (const entry of fs60.readdirSync(dir, { withFileTypes: true })) {
|
|
12470
12470
|
if (entry.isDirectory()) {
|
|
12471
12471
|
if (SKIP_DIRS.has(entry.name)) continue;
|
|
12472
|
-
walk(
|
|
12472
|
+
walk(path69.join(dir, entry.name), d + 1);
|
|
12473
12473
|
} else if (entry.isFile() && entry.name === "SKILL.md") {
|
|
12474
|
-
const res = readOne(
|
|
12474
|
+
const res = readOne(path69.join(dir, entry.name), "worker-kit-sub");
|
|
12475
12475
|
if ("entry" in res) out.push(res.entry);
|
|
12476
12476
|
else warnings.push(res.warning);
|
|
12477
12477
|
}
|
|
@@ -12480,11 +12480,11 @@ function readKitSubSkills(kitDir, out, warnings, depth = 0) {
|
|
|
12480
12480
|
walk(base, depth);
|
|
12481
12481
|
}
|
|
12482
12482
|
function readSkillCatalog(opts) {
|
|
12483
|
-
const root =
|
|
12483
|
+
const root = path69.resolve(opts.root);
|
|
12484
12484
|
const entries = [];
|
|
12485
12485
|
const warnings = [];
|
|
12486
12486
|
if (opts.includeProjectRoot !== false) {
|
|
12487
|
-
const rootSkill =
|
|
12487
|
+
const rootSkill = path69.join(root, "SKILL.md");
|
|
12488
12488
|
if (exists(rootSkill)) {
|
|
12489
12489
|
const res = readOne(rootSkill, "project-root");
|
|
12490
12490
|
if ("entry" in res) entries.push(res.entry);
|
|
@@ -12492,14 +12492,14 @@ function readSkillCatalog(opts) {
|
|
|
12492
12492
|
}
|
|
12493
12493
|
}
|
|
12494
12494
|
if (opts.includeClaudeSkills !== false) {
|
|
12495
|
-
readSkillDirs(
|
|
12495
|
+
readSkillDirs(path69.join(root, ".claude/skills"), "claude-skills", entries, warnings);
|
|
12496
12496
|
}
|
|
12497
12497
|
if (opts.includeWorkerKits !== false) {
|
|
12498
|
-
const kitsDir =
|
|
12498
|
+
const kitsDir = path69.join(root, "cli/assets/worker-kits");
|
|
12499
12499
|
readSkillDirs(kitsDir, "worker-kit", entries, warnings);
|
|
12500
12500
|
if (isDir2(kitsDir)) {
|
|
12501
|
-
for (const kit of
|
|
12502
|
-
const kitPath =
|
|
12501
|
+
for (const kit of fs60.readdirSync(kitsDir).sort()) {
|
|
12502
|
+
const kitPath = path69.join(kitsDir, kit);
|
|
12503
12503
|
if (!isDir2(kitPath)) continue;
|
|
12504
12504
|
readKitSubSkills(kitPath, entries, warnings);
|
|
12505
12505
|
}
|
|
@@ -12544,9 +12544,9 @@ __export(source_import_discovery_exports, {
|
|
|
12544
12544
|
startSourceImportFlow: () => startSourceImportFlow
|
|
12545
12545
|
});
|
|
12546
12546
|
import * as p34 from "@clack/prompts";
|
|
12547
|
-
import
|
|
12548
|
-
import
|
|
12549
|
-
import
|
|
12547
|
+
import pc50 from "picocolors";
|
|
12548
|
+
import fs65 from "node:fs";
|
|
12549
|
+
import path73 from "node:path";
|
|
12550
12550
|
import { pathToFileURL as pathToFileURL4 } from "node:url";
|
|
12551
12551
|
function slugifyWorkspaceName(input) {
|
|
12552
12552
|
const slug = input.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -12574,10 +12574,10 @@ async function promptForInteractiveWorkspacePath(input) {
|
|
|
12574
12574
|
});
|
|
12575
12575
|
if (p34.isCancel(raw) || !raw) return null;
|
|
12576
12576
|
const trimmed = String(raw).trim();
|
|
12577
|
-
const expanded = trimmed.startsWith("~/") ?
|
|
12578
|
-
const resolved =
|
|
12579
|
-
if (
|
|
12580
|
-
const finalPath =
|
|
12577
|
+
const expanded = trimmed.startsWith("~/") ? path73.join(process.env.HOME ?? "~", trimmed.slice(2)) : trimmed;
|
|
12578
|
+
const resolved = path73.resolve(expanded);
|
|
12579
|
+
if (fs65.existsSync(resolved) && fs65.statSync(resolved).isDirectory()) {
|
|
12580
|
+
const finalPath = path73.join(resolved, suggestedName);
|
|
12581
12581
|
p34.note(
|
|
12582
12582
|
[
|
|
12583
12583
|
`You selected an existing folder: ${resolved}`,
|
|
@@ -12753,14 +12753,14 @@ async function startSkillsSourceImportFlow() {
|
|
|
12753
12753
|
function renderSuccess(result, jobId) {
|
|
12754
12754
|
const sourceLine = result.source.kind === "github-repo" ? `${result.source.repo.owner}/${result.source.repo.repo}` : `${result.source.skillId}@${result.source.version}`;
|
|
12755
12755
|
p34.outro(
|
|
12756
|
-
`Imported ${sourceLine} into ${
|
|
12757
|
-
jobId: ${
|
|
12758
|
-
forkId: ${
|
|
12756
|
+
`Imported ${sourceLine} into ${pc50.cyan(result.forkPath)}
|
|
12757
|
+
jobId: ${pc50.cyan(jobId)}
|
|
12758
|
+
forkId: ${pc50.cyan(result.forkId)}
|
|
12759
12759
|
risk: ${result.security.riskClass} (${result.security.findings.length} findings)
|
|
12760
12760
|
detection: framework=${result.detection.framework} pm=${result.detection.packageManager}
|
|
12761
12761
|
open: ${folderOpenLabel3(result.forkPath)}
|
|
12762
|
-
summary: ${
|
|
12763
|
-
manifest: ${
|
|
12762
|
+
summary: ${pc50.dim(result.summaryPath)}
|
|
12763
|
+
manifest: ${pc50.dim(result.manifestPath)}`
|
|
12764
12764
|
);
|
|
12765
12765
|
}
|
|
12766
12766
|
async function confirmTwice(job) {
|
|
@@ -12847,9 +12847,9 @@ var init_source_import_discovery = __esm({
|
|
|
12847
12847
|
// src/index.ts
|
|
12848
12848
|
import { Command } from "commander";
|
|
12849
12849
|
import * as p35 from "@clack/prompts";
|
|
12850
|
-
import
|
|
12851
|
-
import
|
|
12852
|
-
import
|
|
12850
|
+
import pc51 from "picocolors";
|
|
12851
|
+
import fs66 from "node:fs";
|
|
12852
|
+
import path74 from "node:path";
|
|
12853
12853
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
12854
12854
|
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
12855
12855
|
|
|
@@ -13650,8 +13650,8 @@ function printItemCompleted(item) {
|
|
|
13650
13650
|
const changes = Array.isArray(item.changes) ? item.changes : [];
|
|
13651
13651
|
const entries = changes.map((changeRaw) => asRecord(changeRaw)).filter((change) => Boolean(change)).map((change) => {
|
|
13652
13652
|
const kind = asString(change.kind, "update");
|
|
13653
|
-
const
|
|
13654
|
-
return `${kind} ${
|
|
13653
|
+
const path75 = asString(change.path, "unknown");
|
|
13654
|
+
return `${kind} ${path75}`;
|
|
13655
13655
|
});
|
|
13656
13656
|
const preview = entries.length > 0 ? entries.slice(0, 6).join(", ") : "none";
|
|
13657
13657
|
const more = entries.length > 6 ? ` (+${entries.length - 6} more)` : "";
|
|
@@ -16485,8 +16485,8 @@ function registerIssueCommands(program2) {
|
|
|
16485
16485
|
if (opts.assigneeAgentId) params.set("assigneeAgentId", opts.assigneeAgentId);
|
|
16486
16486
|
if (opts.projectId) params.set("projectId", opts.projectId);
|
|
16487
16487
|
const query = params.toString();
|
|
16488
|
-
const
|
|
16489
|
-
const rows = await ctx.api.get(
|
|
16488
|
+
const path75 = `/api/companies/${ctx.companyId}/issues${query ? `?${query}` : ""}`;
|
|
16489
|
+
const rows = await ctx.api.get(path75) ?? [];
|
|
16490
16490
|
const filtered = filterIssueRows(rows, opts.match);
|
|
16491
16491
|
if (ctx.json) {
|
|
16492
16492
|
printOutput(filtered, { json: true });
|
|
@@ -17092,8 +17092,8 @@ function registerActivityCommands(program2) {
|
|
|
17092
17092
|
if (opts.entityType) params.set("entityType", opts.entityType);
|
|
17093
17093
|
if (opts.entityId) params.set("entityId", opts.entityId);
|
|
17094
17094
|
const query = params.toString();
|
|
17095
|
-
const
|
|
17096
|
-
const rows = await ctx.api.get(
|
|
17095
|
+
const path75 = `/api/companies/${ctx.companyId}/activity${query ? `?${query}` : ""}`;
|
|
17096
|
+
const rows = await ctx.api.get(path75) ?? [];
|
|
17097
17097
|
if (ctx.json) {
|
|
17098
17098
|
printOutput(rows, { json: true });
|
|
17099
17099
|
return;
|
|
@@ -23788,7 +23788,8 @@ function badge(a) {
|
|
|
23788
23788
|
return pc32.magenta("\u{1F9E9} Module");
|
|
23789
23789
|
}
|
|
23790
23790
|
function printCard(a) {
|
|
23791
|
-
const
|
|
23791
|
+
const compatibleFormats = "compatibleFormats" in a && Array.isArray(a.compatibleFormats) ? a.compatibleFormats : [];
|
|
23792
|
+
const compat = compatibleFormats.length ? pc32.dim("Works with: ") + compatibleFormats.map((f) => pc32.cyan(f)).join(", ") : pc32.dim("Works with: any format");
|
|
23792
23793
|
const rows = [
|
|
23793
23794
|
pc32.bold(a.name),
|
|
23794
23795
|
`${badge(a)} ${pc32.dim(a.id)}`,
|
|
@@ -23821,7 +23822,8 @@ ${pc32.bold(g.label)} ${pc32.dim("(" + g.count + ")")}`);
|
|
|
23821
23822
|
console.log(pc32.dim(" " + g.description));
|
|
23822
23823
|
console.log("");
|
|
23823
23824
|
for (const a of g.artifacts) {
|
|
23824
|
-
const
|
|
23825
|
+
const compatibleFormats = "compatibleFormats" in a && Array.isArray(a.compatibleFormats) ? a.compatibleFormats : [];
|
|
23826
|
+
const compat = compatibleFormats.length ? pc32.dim(" \xB7 " + compatibleFormats.join(", ")) : "";
|
|
23825
23827
|
console.log(` ${pc32.cyan(pc32.bold(a.name))}${compat}`);
|
|
23826
23828
|
console.log(` ${pc32.dim("growthub template get " + a.slug)}`);
|
|
23827
23829
|
console.log("");
|
|
@@ -24358,6 +24360,18 @@ function collectArtifacts(executionLog) {
|
|
|
24358
24360
|
const output = entry.output;
|
|
24359
24361
|
if (typeof output !== "object" || output === null) continue;
|
|
24360
24362
|
const record = output;
|
|
24363
|
+
const directStoragePath = stringValue(record.storagePath) ?? stringValue(record.storage_path);
|
|
24364
|
+
const directVideoUrl = stringValue(record.videoUrl);
|
|
24365
|
+
if (directStoragePath || directVideoUrl) {
|
|
24366
|
+
artifacts.push({
|
|
24367
|
+
artifactId: directStoragePath ?? `${entry.nodeId}-video-${artifacts.length + 1}`,
|
|
24368
|
+
artifactType: directVideoUrl || isVideoPath(directStoragePath) ? "video" : "file",
|
|
24369
|
+
nodeId: entry.nodeId,
|
|
24370
|
+
url: directVideoUrl,
|
|
24371
|
+
storagePath: directStoragePath,
|
|
24372
|
+
metadata: record
|
|
24373
|
+
});
|
|
24374
|
+
}
|
|
24361
24375
|
const images = Array.isArray(record.images) ? record.images : [];
|
|
24362
24376
|
for (const image of images) {
|
|
24363
24377
|
if (!image || typeof image !== "object") continue;
|
|
@@ -24386,8 +24400,40 @@ function collectArtifacts(executionLog) {
|
|
|
24386
24400
|
metadata: slideRecord
|
|
24387
24401
|
});
|
|
24388
24402
|
}
|
|
24403
|
+
const videos = Array.isArray(record.videos) ? record.videos : [];
|
|
24404
|
+
for (const video of videos) {
|
|
24405
|
+
if (!video || typeof video !== "object") continue;
|
|
24406
|
+
const videoRecord = video;
|
|
24407
|
+
const storagePath = stringValue(videoRecord.storagePath) ?? stringValue(videoRecord.storage_path);
|
|
24408
|
+
const videoUrl = stringValue(videoRecord.videoUrl) ?? stringValue(videoRecord.url);
|
|
24409
|
+
artifacts.push({
|
|
24410
|
+
artifactId: storagePath ?? `${entry.nodeId}-video-${artifacts.length + 1}`,
|
|
24411
|
+
artifactType: "video",
|
|
24412
|
+
nodeId: entry.nodeId,
|
|
24413
|
+
url: videoUrl,
|
|
24414
|
+
storagePath,
|
|
24415
|
+
metadata: videoRecord
|
|
24416
|
+
});
|
|
24417
|
+
}
|
|
24389
24418
|
}
|
|
24390
|
-
return artifacts;
|
|
24419
|
+
return dedupeArtifacts(artifacts);
|
|
24420
|
+
}
|
|
24421
|
+
function stringValue(value) {
|
|
24422
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
24423
|
+
}
|
|
24424
|
+
function isVideoPath(value) {
|
|
24425
|
+
return Boolean(value?.toLowerCase().match(/\.(mp4|mov|webm|mkv|m4v)(\?|$)/));
|
|
24426
|
+
}
|
|
24427
|
+
function dedupeArtifacts(artifacts) {
|
|
24428
|
+
const seen = /* @__PURE__ */ new Set();
|
|
24429
|
+
const deduped = [];
|
|
24430
|
+
for (const artifact of artifacts) {
|
|
24431
|
+
const key = artifact.storagePath ?? artifact.url ?? artifact.artifactId;
|
|
24432
|
+
if (seen.has(key)) continue;
|
|
24433
|
+
seen.add(key);
|
|
24434
|
+
deduped.push(artifact);
|
|
24435
|
+
}
|
|
24436
|
+
return deduped;
|
|
24391
24437
|
}
|
|
24392
24438
|
function resolveNodeSlug(request, nodeId) {
|
|
24393
24439
|
const match = request.nodes.find((node) => node.id === nodeId);
|
|
@@ -24416,6 +24462,7 @@ function summarizeExecution(executionLog) {
|
|
|
24416
24462
|
if (Array.isArray(record.images)) imageCount += record.images.length;
|
|
24417
24463
|
if (Array.isArray(record.slides)) slideCount += record.slides.length;
|
|
24418
24464
|
if (Array.isArray(record.videos)) videoCount += record.videos.length;
|
|
24465
|
+
if (typeof record.videoUrl === "string" && record.videoUrl.trim().length > 0) videoCount += 1;
|
|
24419
24466
|
}
|
|
24420
24467
|
return {
|
|
24421
24468
|
...outputText ? { outputText } : {},
|
|
@@ -24616,31 +24663,31 @@ function resolveManifestBaseUrl(opts = {}) {
|
|
|
24616
24663
|
function isRecord(value) {
|
|
24617
24664
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24618
24665
|
}
|
|
24619
|
-
function assertRecord(value,
|
|
24666
|
+
function assertRecord(value, path75) {
|
|
24620
24667
|
if (!isRecord(value)) {
|
|
24621
|
-
throw new ManifestMalformedError(`Expected object at \`${
|
|
24668
|
+
throw new ManifestMalformedError(`Expected object at \`${path75}\`.`);
|
|
24622
24669
|
}
|
|
24623
24670
|
return value;
|
|
24624
24671
|
}
|
|
24625
|
-
function assertArray(value,
|
|
24672
|
+
function assertArray(value, path75) {
|
|
24626
24673
|
if (!Array.isArray(value)) {
|
|
24627
|
-
throw new ManifestMalformedError(`Expected array at \`${
|
|
24674
|
+
throw new ManifestMalformedError(`Expected array at \`${path75}\`.`);
|
|
24628
24675
|
}
|
|
24629
24676
|
return value;
|
|
24630
24677
|
}
|
|
24631
|
-
function assertString(value,
|
|
24678
|
+
function assertString(value, path75) {
|
|
24632
24679
|
if (typeof value !== "string" || value.length === 0) {
|
|
24633
|
-
throw new ManifestMalformedError(`Expected non-empty string at \`${
|
|
24680
|
+
throw new ManifestMalformedError(`Expected non-empty string at \`${path75}\`.`);
|
|
24634
24681
|
}
|
|
24635
24682
|
return value;
|
|
24636
24683
|
}
|
|
24637
|
-
function normalizeProvenance(value,
|
|
24638
|
-
const record = assertRecord(value,
|
|
24639
|
-
const originType = assertString(record.originType, `${
|
|
24684
|
+
function normalizeProvenance(value, path75) {
|
|
24685
|
+
const record = assertRecord(value, path75);
|
|
24686
|
+
const originType = assertString(record.originType, `${path75}.originType`);
|
|
24640
24687
|
const allowed = ["hosted", "local-extension", "derived-from-workflow"];
|
|
24641
24688
|
if (!allowed.includes(originType)) {
|
|
24642
24689
|
throw new ManifestMalformedError(
|
|
24643
|
-
`Unknown provenance originType at \`${
|
|
24690
|
+
`Unknown provenance originType at \`${path75}.originType\`: ${originType}`
|
|
24644
24691
|
);
|
|
24645
24692
|
}
|
|
24646
24693
|
return {
|
|
@@ -24712,7 +24759,10 @@ function parseContractVersionHeader(raw) {
|
|
|
24712
24759
|
}
|
|
24713
24760
|
async function fetchCapabilityManifest(opts = {}) {
|
|
24714
24761
|
const resolvedBaseUrl = resolveManifestBaseUrl(opts);
|
|
24715
|
-
const url = new URL(MANIFEST_PATH, `${resolvedBaseUrl.baseUrl}/`)
|
|
24762
|
+
const url = new URL(MANIFEST_PATH, `${resolvedBaseUrl.baseUrl}/`);
|
|
24763
|
+
if (opts.includeExperimental) url.searchParams.set("include_experimental", "true");
|
|
24764
|
+
if (opts.includeDisabled) url.searchParams.set("include_disabled", "true");
|
|
24765
|
+
const manifestUrl = url.toString();
|
|
24716
24766
|
const headers = {
|
|
24717
24767
|
accept: "application/json"
|
|
24718
24768
|
};
|
|
@@ -24732,7 +24782,7 @@ async function fetchCapabilityManifest(opts = {}) {
|
|
|
24732
24782
|
const timer = controller ? setTimeout(() => controller.abort(), opts.timeoutMs) : null;
|
|
24733
24783
|
let response;
|
|
24734
24784
|
try {
|
|
24735
|
-
response = await fetch(
|
|
24785
|
+
response = await fetch(manifestUrl, {
|
|
24736
24786
|
method: "GET",
|
|
24737
24787
|
headers,
|
|
24738
24788
|
signal: controller?.signal
|
|
@@ -24740,7 +24790,7 @@ async function fetchCapabilityManifest(opts = {}) {
|
|
|
24740
24790
|
} catch (err) {
|
|
24741
24791
|
throw new ManifestEndpointUnavailableError(
|
|
24742
24792
|
void 0,
|
|
24743
|
-
`Hosted manifest endpoint unreachable (${
|
|
24793
|
+
`Hosted manifest endpoint unreachable (${manifestUrl}): ${err instanceof Error ? err.message : String(err)}`
|
|
24744
24794
|
);
|
|
24745
24795
|
} finally {
|
|
24746
24796
|
if (timer) clearTimeout(timer);
|
|
@@ -25030,6 +25080,7 @@ async function deriveCapabilitiesFromHostedWorkflows() {
|
|
|
25030
25080
|
}
|
|
25031
25081
|
function matchesQuery(node, query) {
|
|
25032
25082
|
if (query.enabledOnly !== false && !node.enabled) return false;
|
|
25083
|
+
if (query.includeExperimental !== true && node.experimental) return false;
|
|
25033
25084
|
if (query.family && node.family !== query.family) return false;
|
|
25034
25085
|
if (query.executionKind && node.executionKind !== query.executionKind) return false;
|
|
25035
25086
|
if (query.outputType && !node.outputTypes.includes(query.outputType)) return false;
|
|
@@ -25041,9 +25092,12 @@ function matchesQuery(node, query) {
|
|
|
25041
25092
|
}
|
|
25042
25093
|
return true;
|
|
25043
25094
|
}
|
|
25044
|
-
async function resolveFromManifest() {
|
|
25095
|
+
async function resolveFromManifest(query) {
|
|
25045
25096
|
try {
|
|
25046
|
-
const { envelope } = await fetchCapabilityManifest(
|
|
25097
|
+
const { envelope } = await fetchCapabilityManifest({
|
|
25098
|
+
includeExperimental: query?.includeExperimental === true,
|
|
25099
|
+
includeDisabled: query?.enabledOnly === false
|
|
25100
|
+
});
|
|
25047
25101
|
writeManifestCache(envelope);
|
|
25048
25102
|
const projected = projectManifestEnvelope(envelope);
|
|
25049
25103
|
return {
|
|
@@ -25104,7 +25158,7 @@ async function resolveFromLegacy() {
|
|
|
25104
25158
|
function createCmsCapabilityRegistryClient() {
|
|
25105
25159
|
return {
|
|
25106
25160
|
async listCapabilities(query) {
|
|
25107
|
-
const outcome = await resolveFromManifest();
|
|
25161
|
+
const outcome = await resolveFromManifest(query);
|
|
25108
25162
|
const nodes = outcome.nodes;
|
|
25109
25163
|
const enabledCount = nodes.filter((n) => n.enabled).length;
|
|
25110
25164
|
const filtered = query ? nodes.filter((n) => matchesQuery(n, query)) : nodes;
|
|
@@ -25476,7 +25530,8 @@ function printGroupedCapabilities(nodes) {
|
|
|
25476
25530
|
${header} ${pc33.dim("(" + groupNodes.length + ")")}`);
|
|
25477
25531
|
for (const node of groupNodes) {
|
|
25478
25532
|
const enabledTag = node.enabled ? pc33.green("enabled") : pc33.red("disabled");
|
|
25479
|
-
|
|
25533
|
+
const experimentalTag = node.experimental ? ` ${pc33.yellow("experimental")}` : "";
|
|
25534
|
+
console.log(` ${pc33.bold(node.slug)} ${pc33.dim(node.displayName)} ${enabledTag}${experimentalTag}`);
|
|
25480
25535
|
console.log(` ${pc33.dim("Execution:")} ${executionKindLabel(node.executionKind)} ${pc33.dim("Outputs:")} ${pc33.dim(node.outputTypes.join(", "))}`);
|
|
25481
25536
|
if (node.description) {
|
|
25482
25537
|
console.log(` ${pc33.dim(node.description)}`);
|
|
@@ -25493,6 +25548,7 @@ function printCapabilityCard(node) {
|
|
|
25493
25548
|
const lines = [
|
|
25494
25549
|
`${iconPrefix}${pc33.bold(node.displayName)} ${pc33.dim(node.slug)}`,
|
|
25495
25550
|
`${familyBadge(node.family)} ${node.enabled ? pc33.green("enabled") : pc33.red("disabled")}`,
|
|
25551
|
+
`${pc33.dim("Experimental:")} ${node.experimental ? pc33.yellow("true") : pc33.green("false")}`,
|
|
25496
25552
|
"",
|
|
25497
25553
|
`${pc33.dim("Category:")} ${node.category}`,
|
|
25498
25554
|
`${pc33.dim("Node Type:")} ${node.nodeType}`,
|
|
@@ -25620,6 +25676,7 @@ Examples:
|
|
|
25620
25676
|
$ growthub capability # interactive browser
|
|
25621
25677
|
$ growthub capability list # all capabilities grouped by family
|
|
25622
25678
|
$ growthub capability list --family video # filter by family
|
|
25679
|
+
$ growthub capability list --family video --include-experimental
|
|
25623
25680
|
$ growthub capability list --json # machine-readable output
|
|
25624
25681
|
$ growthub capability inspect video-gen # inspect a specific capability
|
|
25625
25682
|
$ growthub capability resolve # resolve machine bindings for all
|
|
@@ -25628,7 +25685,7 @@ Examples:
|
|
|
25628
25685
|
cap.action(async () => {
|
|
25629
25686
|
await runCapabilityPicker({});
|
|
25630
25687
|
});
|
|
25631
|
-
cap.command("list").description("List all CMS-backed runtime node capabilities").option("--family <family>", "Filter by family (video, image, slides, text, data, ops)").option("--json", "Output raw JSON for scripting").action(async (opts) => {
|
|
25688
|
+
cap.command("list").description("List all CMS-backed runtime node capabilities").option("--family <family>", "Filter by family (video, image, slides, text, data, ops)").option("--include-experimental", "Include experimental/admin-hidden capabilities").option("--json", "Output raw JSON for scripting").action(async (opts) => {
|
|
25632
25689
|
const access = getWorkflowAccess();
|
|
25633
25690
|
if (access.state !== "ready") {
|
|
25634
25691
|
console.error(pc33.red(`${access.reason}.`));
|
|
@@ -25636,7 +25693,7 @@ Examples:
|
|
|
25636
25693
|
return;
|
|
25637
25694
|
}
|
|
25638
25695
|
const registry = createCmsCapabilityRegistryClient();
|
|
25639
|
-
const query = opts.family ? { family: opts.family } :
|
|
25696
|
+
const query = opts.family ? { family: opts.family, includeExperimental: opts.includeExperimental === true } : { includeExperimental: opts.includeExperimental === true };
|
|
25640
25697
|
try {
|
|
25641
25698
|
const { nodes, meta } = await registry.listCapabilities(query);
|
|
25642
25699
|
if (opts.json) {
|
|
@@ -25657,7 +25714,7 @@ Examples:
|
|
|
25657
25714
|
process.exitCode = 1;
|
|
25658
25715
|
}
|
|
25659
25716
|
});
|
|
25660
|
-
cap.command("inspect").description("Inspect a specific CMS capability node").argument("<slug>", "Capability slug (e.g. 'video-gen', 'text-gen')").option("--json", "Output raw JSON").action(async (slug, opts) => {
|
|
25717
|
+
cap.command("inspect").description("Inspect a specific CMS capability node").argument("<slug>", "Capability slug (e.g. 'video-gen', 'text-gen')").option("--include-experimental", "Include experimental/admin-hidden capabilities").option("--json", "Output raw JSON").action(async (slug, opts) => {
|
|
25661
25718
|
const access = getWorkflowAccess();
|
|
25662
25719
|
if (access.state !== "ready") {
|
|
25663
25720
|
console.error(pc33.red(`${access.reason}.`));
|
|
@@ -25666,7 +25723,12 @@ Examples:
|
|
|
25666
25723
|
}
|
|
25667
25724
|
const registry = createCmsCapabilityRegistryClient();
|
|
25668
25725
|
try {
|
|
25669
|
-
const
|
|
25726
|
+
const { nodes } = await registry.listCapabilities({
|
|
25727
|
+
slug,
|
|
25728
|
+
enabledOnly: false,
|
|
25729
|
+
includeExperimental: opts.includeExperimental === true
|
|
25730
|
+
});
|
|
25731
|
+
const node = nodes.find((candidate) => candidate.slug === slug) ?? null;
|
|
25670
25732
|
if (!node) {
|
|
25671
25733
|
console.error(pc33.red(`Unknown capability: "${slug}".`) + pc33.dim(" Run `growthub capability list` to browse."));
|
|
25672
25734
|
process.exitCode = 1;
|
|
@@ -25920,8 +25982,8 @@ function printRefreshSummary(args) {
|
|
|
25920
25982
|
// src/commands/pipeline.ts
|
|
25921
25983
|
init_session_store();
|
|
25922
25984
|
init_hosted_client();
|
|
25923
|
-
import
|
|
25924
|
-
import
|
|
25985
|
+
import fs39 from "node:fs";
|
|
25986
|
+
import path47 from "node:path";
|
|
25925
25987
|
import * as p23 from "@clack/prompts";
|
|
25926
25988
|
import pc35 from "picocolors";
|
|
25927
25989
|
|
|
@@ -26584,10 +26646,188 @@ function createArtifactStore() {
|
|
|
26584
26646
|
};
|
|
26585
26647
|
}
|
|
26586
26648
|
|
|
26649
|
+
// src/runtime/execution-results/index.ts
|
|
26650
|
+
init_home();
|
|
26651
|
+
import fs36 from "node:fs";
|
|
26652
|
+
import path44 from "node:path";
|
|
26653
|
+
function resolveExecutionResultsDir() {
|
|
26654
|
+
return path44.resolve(resolvePaperclipHomeDir(), "execution-results");
|
|
26655
|
+
}
|
|
26656
|
+
function resolveExecutionResultPath(executionId) {
|
|
26657
|
+
return path44.resolve(resolveExecutionResultsDir(), `${sanitizeCacheKey(executionId)}.json`);
|
|
26658
|
+
}
|
|
26659
|
+
function sanitizeCacheKey(value) {
|
|
26660
|
+
return value.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
26661
|
+
}
|
|
26662
|
+
function writeJsonAtomic(filePath, value) {
|
|
26663
|
+
fs36.mkdirSync(path44.dirname(filePath), { recursive: true });
|
|
26664
|
+
const tempPath = `${filePath}.${process.pid}.tmp`;
|
|
26665
|
+
fs36.writeFileSync(tempPath, `${JSON.stringify(value, null, 2)}
|
|
26666
|
+
`, { mode: 384 });
|
|
26667
|
+
fs36.renameSync(tempPath, filePath);
|
|
26668
|
+
}
|
|
26669
|
+
function readJson(filePath) {
|
|
26670
|
+
if (!fs36.existsSync(filePath)) return null;
|
|
26671
|
+
try {
|
|
26672
|
+
return JSON.parse(fs36.readFileSync(filePath, "utf-8"));
|
|
26673
|
+
} catch {
|
|
26674
|
+
return null;
|
|
26675
|
+
}
|
|
26676
|
+
}
|
|
26677
|
+
function initialResult(input) {
|
|
26678
|
+
const cacheKey = input.workflowId?.trim() || input.threadId?.trim() || input.pipelineId;
|
|
26679
|
+
return {
|
|
26680
|
+
executionId: cacheKey,
|
|
26681
|
+
threadId: input.threadId,
|
|
26682
|
+
pipelineId: input.pipelineId,
|
|
26683
|
+
status: "running",
|
|
26684
|
+
nodeResults: Object.fromEntries(
|
|
26685
|
+
input.nodes.map((node) => [
|
|
26686
|
+
node.nodeId,
|
|
26687
|
+
{
|
|
26688
|
+
nodeId: node.nodeId,
|
|
26689
|
+
slug: node.slug,
|
|
26690
|
+
status: "pending"
|
|
26691
|
+
}
|
|
26692
|
+
])
|
|
26693
|
+
),
|
|
26694
|
+
artifacts: [],
|
|
26695
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26696
|
+
cacheKey,
|
|
26697
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
26698
|
+
};
|
|
26699
|
+
}
|
|
26700
|
+
function artifactRefsFromNodeResult(nodeResult) {
|
|
26701
|
+
const output = nodeResult.output;
|
|
26702
|
+
if (!output || typeof output !== "object") return [];
|
|
26703
|
+
const refs = [];
|
|
26704
|
+
const directStoragePath = stringValue2(output.storagePath) ?? stringValue2(output.storage_path);
|
|
26705
|
+
const directUrl = stringValue2(output.videoUrl) ?? stringValue2(output.url);
|
|
26706
|
+
if (directStoragePath || directUrl) {
|
|
26707
|
+
refs.push({
|
|
26708
|
+
artifactId: directStoragePath ?? `${nodeResult.nodeId}-artifact`,
|
|
26709
|
+
artifactType: inferArtifactType(output, directStoragePath, directUrl),
|
|
26710
|
+
nodeId: nodeResult.nodeId,
|
|
26711
|
+
url: directUrl,
|
|
26712
|
+
storagePath: directStoragePath,
|
|
26713
|
+
metadata: output
|
|
26714
|
+
});
|
|
26715
|
+
}
|
|
26716
|
+
const videos = Array.isArray(output.videos) ? output.videos : [];
|
|
26717
|
+
for (const video of videos) {
|
|
26718
|
+
if (!video || typeof video !== "object") continue;
|
|
26719
|
+
const record = video;
|
|
26720
|
+
const storagePath = stringValue2(record.storagePath) ?? stringValue2(record.storage_path);
|
|
26721
|
+
const url = stringValue2(record.videoUrl) ?? stringValue2(record.url);
|
|
26722
|
+
refs.push({
|
|
26723
|
+
artifactId: storagePath ?? `${nodeResult.nodeId}-video-${refs.length + 1}`,
|
|
26724
|
+
artifactType: "video",
|
|
26725
|
+
nodeId: nodeResult.nodeId,
|
|
26726
|
+
url,
|
|
26727
|
+
storagePath,
|
|
26728
|
+
metadata: record
|
|
26729
|
+
});
|
|
26730
|
+
}
|
|
26731
|
+
return dedupeArtifactRefs(refs);
|
|
26732
|
+
}
|
|
26733
|
+
function stringValue2(value) {
|
|
26734
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
26735
|
+
}
|
|
26736
|
+
function inferArtifactType(output, storagePath, url) {
|
|
26737
|
+
const hinted = stringValue2(output.type) ?? stringValue2(output.artifactType);
|
|
26738
|
+
if (hinted) return hinted;
|
|
26739
|
+
const candidate = `${storagePath ?? ""} ${url ?? ""}`.toLowerCase();
|
|
26740
|
+
if (candidate.match(/\.(mp4|mov|webm|mkv|m4v)(\?|$)/) || output.videoUrl) return "video";
|
|
26741
|
+
if (candidate.match(/\.(png|jpg|jpeg|gif|webp)(\?|$)/)) return "image";
|
|
26742
|
+
return "file";
|
|
26743
|
+
}
|
|
26744
|
+
function dedupeArtifactRefs(refs) {
|
|
26745
|
+
const seen = /* @__PURE__ */ new Set();
|
|
26746
|
+
const deduped = [];
|
|
26747
|
+
for (const ref of refs) {
|
|
26748
|
+
const key = ref.storagePath ?? ref.url ?? ref.artifactId;
|
|
26749
|
+
if (seen.has(key)) continue;
|
|
26750
|
+
seen.add(key);
|
|
26751
|
+
deduped.push(ref);
|
|
26752
|
+
}
|
|
26753
|
+
return deduped;
|
|
26754
|
+
}
|
|
26755
|
+
function mergeArtifactRefs(result) {
|
|
26756
|
+
return dedupeArtifactRefs([
|
|
26757
|
+
...result.artifacts,
|
|
26758
|
+
...Object.values(result.nodeResults).flatMap((nodeResult) => artifactRefsFromNodeResult(nodeResult))
|
|
26759
|
+
]);
|
|
26760
|
+
}
|
|
26761
|
+
function createExecutionResultCache(input) {
|
|
26762
|
+
let result = initialResult(input);
|
|
26763
|
+
let currentPath = resolveExecutionResultPath(result.executionId);
|
|
26764
|
+
writeJsonAtomic(currentPath, result);
|
|
26765
|
+
const persist = () => {
|
|
26766
|
+
result.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
26767
|
+
result.artifacts = mergeArtifactRefs(result);
|
|
26768
|
+
currentPath = resolveExecutionResultPath(result.executionId);
|
|
26769
|
+
writeJsonAtomic(currentPath, result);
|
|
26770
|
+
};
|
|
26771
|
+
return {
|
|
26772
|
+
handleEvent(event) {
|
|
26773
|
+
if (typeof event.executionId === "string" && event.executionId.trim()) {
|
|
26774
|
+
result.executionId = event.executionId.trim();
|
|
26775
|
+
}
|
|
26776
|
+
if (event.nodeId) {
|
|
26777
|
+
const current = result.nodeResults[event.nodeId] ?? {
|
|
26778
|
+
nodeId: event.nodeId,
|
|
26779
|
+
slug: event.nodeId,
|
|
26780
|
+
status: "pending"
|
|
26781
|
+
};
|
|
26782
|
+
if (event.type === "node_start") {
|
|
26783
|
+
current.status = "running";
|
|
26784
|
+
} else if (event.type === "node_complete") {
|
|
26785
|
+
current.status = "succeeded";
|
|
26786
|
+
current.output = event.output;
|
|
26787
|
+
} else if (event.type === "node_error") {
|
|
26788
|
+
current.status = "failed";
|
|
26789
|
+
current.error = event.error;
|
|
26790
|
+
}
|
|
26791
|
+
result.nodeResults[event.nodeId] = current;
|
|
26792
|
+
}
|
|
26793
|
+
if (event.type === "complete" && Array.isArray(event.executionLog)) {
|
|
26794
|
+
result.executionLog = event.executionLog;
|
|
26795
|
+
result.status = "succeeded";
|
|
26796
|
+
result.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
26797
|
+
}
|
|
26798
|
+
if (event.type === "error") {
|
|
26799
|
+
result.status = "failed";
|
|
26800
|
+
result.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
26801
|
+
}
|
|
26802
|
+
persist();
|
|
26803
|
+
},
|
|
26804
|
+
saveFinal(finalResult) {
|
|
26805
|
+
result = {
|
|
26806
|
+
...finalResult,
|
|
26807
|
+
pipelineId: input.pipelineId,
|
|
26808
|
+
artifacts: dedupeArtifactRefs([...finalResult.artifacts, ...mergeArtifactRefs(result)]),
|
|
26809
|
+
cacheKey: result.cacheKey,
|
|
26810
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
26811
|
+
};
|
|
26812
|
+
currentPath = resolveExecutionResultPath(result.executionId);
|
|
26813
|
+
writeJsonAtomic(currentPath, result);
|
|
26814
|
+
return result;
|
|
26815
|
+
},
|
|
26816
|
+
getPath() {
|
|
26817
|
+
return currentPath;
|
|
26818
|
+
}
|
|
26819
|
+
};
|
|
26820
|
+
}
|
|
26821
|
+
function readExecutionResult(executionId) {
|
|
26822
|
+
const raw = readJson(resolveExecutionResultPath(executionId));
|
|
26823
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
26824
|
+
return raw;
|
|
26825
|
+
}
|
|
26826
|
+
|
|
26587
26827
|
// src/runtime/native-intelligence/index.ts
|
|
26588
26828
|
init_home();
|
|
26589
|
-
import
|
|
26590
|
-
import
|
|
26829
|
+
import fs38 from "node:fs";
|
|
26830
|
+
import path46 from "node:path";
|
|
26591
26831
|
|
|
26592
26832
|
// src/runtime/native-intelligence/contract.ts
|
|
26593
26833
|
var DEFAULT_INTELLIGENCE_CONFIG = {
|
|
@@ -27858,8 +28098,8 @@ function parseJsonSafe5(text69) {
|
|
|
27858
28098
|
}
|
|
27859
28099
|
|
|
27860
28100
|
// src/runtime/native-intelligence/marketing-context-builder.ts
|
|
27861
|
-
import
|
|
27862
|
-
import
|
|
28101
|
+
import fs37 from "node:fs";
|
|
28102
|
+
import path45 from "node:path";
|
|
27863
28103
|
var CONTEXT_BUILDER_SYSTEM_PROMPT = `You are a marketing strategist drafting a product-marketing-context document.
|
|
27864
28104
|
|
|
27865
28105
|
You receive project artifacts (README content, package.json metadata, any landing page content) and produce a structured product-marketing-context.md with 12 sections.
|
|
@@ -27878,17 +28118,17 @@ var MAX_ARTIFACT_LENGTH = 8e3;
|
|
|
27878
28118
|
function scanProjectArtifacts(projectDir, existingContext) {
|
|
27879
28119
|
const artifacts = { otherFiles: [] };
|
|
27880
28120
|
for (const name of ["README.md", "readme.md", "Readme.md", "README"]) {
|
|
27881
|
-
const readmePath =
|
|
27882
|
-
if (
|
|
27883
|
-
const content =
|
|
28121
|
+
const readmePath = path45.join(projectDir, name);
|
|
28122
|
+
if (fs37.existsSync(readmePath)) {
|
|
28123
|
+
const content = fs37.readFileSync(readmePath, "utf-8");
|
|
27884
28124
|
artifacts.readme = content.slice(0, MAX_ARTIFACT_LENGTH);
|
|
27885
28125
|
break;
|
|
27886
28126
|
}
|
|
27887
28127
|
}
|
|
27888
|
-
const pkgPath =
|
|
27889
|
-
if (
|
|
28128
|
+
const pkgPath = path45.join(projectDir, "package.json");
|
|
28129
|
+
if (fs37.existsSync(pkgPath)) {
|
|
27890
28130
|
try {
|
|
27891
|
-
artifacts.packageJson = JSON.parse(
|
|
28131
|
+
artifacts.packageJson = JSON.parse(fs37.readFileSync(pkgPath, "utf-8"));
|
|
27892
28132
|
} catch {
|
|
27893
28133
|
}
|
|
27894
28134
|
}
|
|
@@ -27900,15 +28140,15 @@ function scanProjectArtifacts(projectDir, existingContext) {
|
|
|
27900
28140
|
".claude/product-marketing-context.md",
|
|
27901
28141
|
"brands/_template/product-marketing-context.md"
|
|
27902
28142
|
]) {
|
|
27903
|
-
const ctxPath =
|
|
27904
|
-
if (
|
|
27905
|
-
artifacts.existingContext =
|
|
28143
|
+
const ctxPath = path45.join(projectDir, candidate);
|
|
28144
|
+
if (fs37.existsSync(ctxPath)) {
|
|
28145
|
+
artifacts.existingContext = fs37.readFileSync(ctxPath, "utf-8");
|
|
27906
28146
|
break;
|
|
27907
28147
|
}
|
|
27908
28148
|
}
|
|
27909
28149
|
}
|
|
27910
28150
|
for (const name of ["CONTRIBUTING.md", "AGENTS.md", "landing-page.md", "about.md"]) {
|
|
27911
|
-
if (
|
|
28151
|
+
if (fs37.existsSync(path45.join(projectDir, name))) {
|
|
27912
28152
|
artifacts.otherFiles.push(name);
|
|
27913
28153
|
}
|
|
27914
28154
|
}
|
|
@@ -28149,15 +28389,15 @@ function cleanMarkdownResponse(text69) {
|
|
|
28149
28389
|
|
|
28150
28390
|
// src/runtime/native-intelligence/index.ts
|
|
28151
28391
|
function resolveConfigPath2() {
|
|
28152
|
-
return
|
|
28392
|
+
return path46.resolve(resolvePaperclipHomeDir(), "native-intelligence", "config.json");
|
|
28153
28393
|
}
|
|
28154
28394
|
function readIntelligenceConfig() {
|
|
28155
28395
|
const configPath = resolveConfigPath2();
|
|
28156
|
-
if (!
|
|
28396
|
+
if (!fs38.existsSync(configPath)) {
|
|
28157
28397
|
return { ...DEFAULT_INTELLIGENCE_CONFIG };
|
|
28158
28398
|
}
|
|
28159
28399
|
try {
|
|
28160
|
-
const raw = JSON.parse(
|
|
28400
|
+
const raw = JSON.parse(fs38.readFileSync(configPath, "utf-8"));
|
|
28161
28401
|
return {
|
|
28162
28402
|
modelId: validateModelId(raw.modelId),
|
|
28163
28403
|
backendType: raw.backendType === "hosted" ? "hosted" : "local",
|
|
@@ -28174,8 +28414,8 @@ function readIntelligenceConfig() {
|
|
|
28174
28414
|
}
|
|
28175
28415
|
function writeIntelligenceConfig(config) {
|
|
28176
28416
|
const configPath = resolveConfigPath2();
|
|
28177
|
-
|
|
28178
|
-
|
|
28417
|
+
fs38.mkdirSync(path46.dirname(configPath), { recursive: true });
|
|
28418
|
+
fs38.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
|
|
28179
28419
|
`, "utf-8");
|
|
28180
28420
|
}
|
|
28181
28421
|
function validateModelId(id) {
|
|
@@ -28494,7 +28734,7 @@ async function runPipelineAssembler(opts) {
|
|
|
28494
28734
|
const pipeline2 = builder.build();
|
|
28495
28735
|
const pkg = await builder.package();
|
|
28496
28736
|
p23.log.info(`Executing pipeline ${pc35.bold(pipeline2.pipelineId)} (${pkg.executionRoute})...`);
|
|
28497
|
-
const
|
|
28737
|
+
const executionInput = {
|
|
28498
28738
|
pipelineId: pipeline2.pipelineId,
|
|
28499
28739
|
threadId: pipeline2.threadId,
|
|
28500
28740
|
nodes: pipeline2.nodes.map((n) => ({
|
|
@@ -28505,11 +28745,19 @@ async function runPipelineAssembler(opts) {
|
|
|
28505
28745
|
})),
|
|
28506
28746
|
executionMode: pipeline2.executionMode,
|
|
28507
28747
|
metadata: pipeline2.metadata
|
|
28748
|
+
};
|
|
28749
|
+
const resultCache = createExecutionResultCache(executionInput);
|
|
28750
|
+
const result = await executionClient.executeWorkflow(executionInput, {
|
|
28751
|
+
onEvent: async (event) => {
|
|
28752
|
+
resultCache.handleEvent(event);
|
|
28753
|
+
}
|
|
28508
28754
|
});
|
|
28509
|
-
|
|
28755
|
+
const cachedResult = resultCache.saveFinal(result);
|
|
28756
|
+
p23.log.success(`Execution ${pc35.bold(cachedResult.executionId)}: ${cachedResult.status}`);
|
|
28757
|
+
p23.log.info(`Result cache: ${resultCache.getPath()}`);
|
|
28510
28758
|
const artifactStore = createArtifactStore();
|
|
28511
|
-
for (const artRef of
|
|
28512
|
-
const nodeResult =
|
|
28759
|
+
for (const artRef of cachedResult.artifacts) {
|
|
28760
|
+
const nodeResult = cachedResult.nodeResults[artRef.nodeId];
|
|
28513
28761
|
artifactStore.create({
|
|
28514
28762
|
artifactType: artRef.artifactType,
|
|
28515
28763
|
sourceNodeSlug: nodeResult?.slug ?? "unknown",
|
|
@@ -28517,11 +28765,15 @@ async function runPipelineAssembler(opts) {
|
|
|
28517
28765
|
pipelineId: pipeline2.pipelineId,
|
|
28518
28766
|
nodeId: artRef.nodeId,
|
|
28519
28767
|
threadId: pipeline2.threadId,
|
|
28520
|
-
metadata:
|
|
28768
|
+
metadata: {
|
|
28769
|
+
...artRef.metadata ?? {},
|
|
28770
|
+
...artRef.url ? { url: artRef.url } : {},
|
|
28771
|
+
...artRef.storagePath ? { storagePath: artRef.storagePath } : {}
|
|
28772
|
+
}
|
|
28521
28773
|
});
|
|
28522
28774
|
}
|
|
28523
|
-
if (
|
|
28524
|
-
p23.log.info(`${
|
|
28775
|
+
if (cachedResult.artifacts.length > 0) {
|
|
28776
|
+
p23.log.info(`${cachedResult.artifacts.length} artifact(s) recorded.`);
|
|
28525
28777
|
}
|
|
28526
28778
|
} catch (err) {
|
|
28527
28779
|
p23.log.error("Execution failed: " + err.message);
|
|
@@ -28531,9 +28783,9 @@ async function runPipelineAssembler(opts) {
|
|
|
28531
28783
|
}
|
|
28532
28784
|
}
|
|
28533
28785
|
function loadPipelineFromFileOrJson(input) {
|
|
28534
|
-
const resolvedPath =
|
|
28535
|
-
if (
|
|
28536
|
-
const content =
|
|
28786
|
+
const resolvedPath = path47.resolve(input);
|
|
28787
|
+
if (fs39.existsSync(resolvedPath)) {
|
|
28788
|
+
const content = fs39.readFileSync(resolvedPath, "utf-8");
|
|
28537
28789
|
return deserializePipeline(JSON.parse(content));
|
|
28538
28790
|
}
|
|
28539
28791
|
try {
|
|
@@ -28589,7 +28841,7 @@ async function executeHostedPipeline(pipeline, opts) {
|
|
|
28589
28841
|
startupSettled = true;
|
|
28590
28842
|
startupSpinner.stop(message ?? "Hosted workflow execution started.");
|
|
28591
28843
|
};
|
|
28592
|
-
const
|
|
28844
|
+
const executionInput = {
|
|
28593
28845
|
pipelineId: pipeline.pipelineId,
|
|
28594
28846
|
workflowId: hostedWorkflowId,
|
|
28595
28847
|
threadId: hostedWorkflowId ?? pipeline.threadId,
|
|
@@ -28601,8 +28853,12 @@ async function executeHostedPipeline(pipeline, opts) {
|
|
|
28601
28853
|
})),
|
|
28602
28854
|
executionMode: pipeline.executionMode,
|
|
28603
28855
|
metadata: pipeline.metadata
|
|
28604
|
-
}
|
|
28856
|
+
};
|
|
28857
|
+
const resultCache = createExecutionResultCache(executionInput);
|
|
28858
|
+
const result = await executionClient.executeWorkflow(executionInput, {
|
|
28605
28859
|
onEvent: async (event) => {
|
|
28860
|
+
resultCache.handleEvent(event);
|
|
28861
|
+
if (opts?.json) return;
|
|
28606
28862
|
if (event.type === "node_start" || event.type === "node_complete") {
|
|
28607
28863
|
settleStartup("Hosted workflow execution started.");
|
|
28608
28864
|
}
|
|
@@ -28618,31 +28874,33 @@ async function executeHostedPipeline(pipeline, opts) {
|
|
|
28618
28874
|
}
|
|
28619
28875
|
}
|
|
28620
28876
|
});
|
|
28877
|
+
const cachedResult = resultCache.saveFinal(result);
|
|
28621
28878
|
settleStartup("Hosted workflow execution started.");
|
|
28622
28879
|
if (opts?.json) {
|
|
28623
|
-
console.log(JSON.stringify(
|
|
28880
|
+
console.log(JSON.stringify(cachedResult, null, 2));
|
|
28624
28881
|
return;
|
|
28625
28882
|
}
|
|
28626
28883
|
console.log("");
|
|
28627
28884
|
console.log(pc35.bold("Pipeline Execution Result"));
|
|
28628
28885
|
console.log(hr6());
|
|
28629
|
-
console.log(` ${pc35.dim("Execution ID:")} ${
|
|
28886
|
+
console.log(` ${pc35.dim("Execution ID:")} ${cachedResult.executionId}`);
|
|
28887
|
+
console.log(` ${pc35.dim("Result Cache:")} ${resultCache.getPath()}`);
|
|
28630
28888
|
if (result.threadId) console.log(` ${pc35.dim("Thread ID:")} ${result.threadId}`);
|
|
28631
28889
|
console.log(` ${pc35.dim("Status:")} ${result.status === "succeeded" ? pc35.green(result.status) : pc35.red(result.status)}`);
|
|
28632
28890
|
if (result.startedAt) console.log(` ${pc35.dim("Started:")} ${result.startedAt}`);
|
|
28633
28891
|
if (result.completedAt) console.log(` ${pc35.dim("Completed:")} ${result.completedAt}`);
|
|
28634
28892
|
console.log(hr6());
|
|
28635
|
-
for (const [nodeId, nodeResult] of Object.entries(
|
|
28893
|
+
for (const [nodeId, nodeResult] of Object.entries(cachedResult.nodeResults)) {
|
|
28636
28894
|
const statusColor3 = nodeResult.status === "succeeded" ? pc35.green : pc35.red;
|
|
28637
28895
|
console.log(` ${statusColor3(nodeResult.status)} ${pc35.bold(nodeResult.slug)} (${pc35.dim(nodeId)})`);
|
|
28638
28896
|
if (nodeResult.error) {
|
|
28639
28897
|
console.log(` ${pc35.red(nodeResult.error)}`);
|
|
28640
28898
|
}
|
|
28641
28899
|
}
|
|
28642
|
-
if (
|
|
28900
|
+
if (cachedResult.artifacts.length > 0) {
|
|
28643
28901
|
console.log("");
|
|
28644
28902
|
console.log(pc35.bold(" Artifacts:"));
|
|
28645
|
-
for (const art of
|
|
28903
|
+
for (const art of cachedResult.artifacts) {
|
|
28646
28904
|
console.log(` ${pc35.dim("\xB7")} ${art.artifactType} (${art.artifactId})`);
|
|
28647
28905
|
}
|
|
28648
28906
|
}
|
|
@@ -28673,8 +28931,8 @@ async function executeHostedPipeline(pipeline, opts) {
|
|
|
28673
28931
|
}
|
|
28674
28932
|
}
|
|
28675
28933
|
const artifactStore = createArtifactStore();
|
|
28676
|
-
for (const artRef of
|
|
28677
|
-
const nodeResult =
|
|
28934
|
+
for (const artRef of cachedResult.artifacts) {
|
|
28935
|
+
const nodeResult = cachedResult.nodeResults[artRef.nodeId];
|
|
28678
28936
|
artifactStore.create({
|
|
28679
28937
|
artifactType: artRef.artifactType,
|
|
28680
28938
|
sourceNodeSlug: nodeResult?.slug ?? "unknown",
|
|
@@ -28682,7 +28940,11 @@ async function executeHostedPipeline(pipeline, opts) {
|
|
|
28682
28940
|
pipelineId: pipeline.pipelineId,
|
|
28683
28941
|
nodeId: artRef.nodeId,
|
|
28684
28942
|
threadId: result.threadId ?? pipeline.threadId,
|
|
28685
|
-
metadata:
|
|
28943
|
+
metadata: {
|
|
28944
|
+
...artRef.metadata ?? {},
|
|
28945
|
+
...artRef.url ? { url: artRef.url } : {},
|
|
28946
|
+
...artRef.storagePath ? { storagePath: artRef.storagePath } : {}
|
|
28947
|
+
}
|
|
28686
28948
|
});
|
|
28687
28949
|
}
|
|
28688
28950
|
console.log("");
|
|
@@ -28852,7 +29114,7 @@ Examples:
|
|
|
28852
29114
|
const totalNodes = Math.max(1, pipeline.nodes.length);
|
|
28853
29115
|
const completed = /* @__PURE__ */ new Set();
|
|
28854
29116
|
const trackableNodeIds = new Set(pipeline.nodes.map((node) => node.id));
|
|
28855
|
-
const
|
|
29117
|
+
const executionInput = {
|
|
28856
29118
|
pipelineId: pipeline.pipelineId,
|
|
28857
29119
|
workflowId: hostedWorkflowId,
|
|
28858
29120
|
threadId: hostedWorkflowId ?? pipeline.threadId,
|
|
@@ -28864,8 +29126,12 @@ Examples:
|
|
|
28864
29126
|
})),
|
|
28865
29127
|
executionMode: pipeline.executionMode,
|
|
28866
29128
|
metadata: pipeline.metadata
|
|
28867
|
-
}
|
|
29129
|
+
};
|
|
29130
|
+
const resultCache = createExecutionResultCache(executionInput);
|
|
29131
|
+
const result = await executionClient.executeWorkflow(executionInput, {
|
|
28868
29132
|
onEvent: async (event) => {
|
|
29133
|
+
resultCache.handleEvent(event);
|
|
29134
|
+
if (opts.json) return;
|
|
28869
29135
|
if (event.type === "node_complete" && event.nodeId && trackableNodeIds.has(event.nodeId) && !completed.has(event.nodeId)) {
|
|
28870
29136
|
completed.add(event.nodeId);
|
|
28871
29137
|
completedNodes = completed.size;
|
|
@@ -28877,42 +29143,44 @@ Examples:
|
|
|
28877
29143
|
}
|
|
28878
29144
|
}
|
|
28879
29145
|
});
|
|
29146
|
+
const cachedResult = resultCache.saveFinal(result);
|
|
28880
29147
|
if (opts.json) {
|
|
28881
|
-
console.log(JSON.stringify(
|
|
29148
|
+
console.log(JSON.stringify(cachedResult, null, 2));
|
|
28882
29149
|
return;
|
|
28883
29150
|
}
|
|
28884
29151
|
console.log("");
|
|
28885
29152
|
console.log(pc35.bold("Pipeline Execution Result"));
|
|
28886
29153
|
console.log(hr6());
|
|
28887
|
-
console.log(` ${pc35.dim("Execution ID:")} ${
|
|
28888
|
-
|
|
28889
|
-
console.log(` ${pc35.dim("
|
|
28890
|
-
|
|
28891
|
-
if (
|
|
29154
|
+
console.log(` ${pc35.dim("Execution ID:")} ${cachedResult.executionId}`);
|
|
29155
|
+
console.log(` ${pc35.dim("Result Cache:")} ${resultCache.getPath()}`);
|
|
29156
|
+
if (cachedResult.threadId) console.log(` ${pc35.dim("Thread ID:")} ${cachedResult.threadId}`);
|
|
29157
|
+
console.log(` ${pc35.dim("Status:")} ${cachedResult.status === "succeeded" ? pc35.green(cachedResult.status) : pc35.red(cachedResult.status)}`);
|
|
29158
|
+
if (cachedResult.startedAt) console.log(` ${pc35.dim("Started:")} ${cachedResult.startedAt}`);
|
|
29159
|
+
if (cachedResult.completedAt) console.log(` ${pc35.dim("Completed:")} ${cachedResult.completedAt}`);
|
|
28892
29160
|
console.log(hr6());
|
|
28893
|
-
for (const [nodeId, nodeResult] of Object.entries(
|
|
29161
|
+
for (const [nodeId, nodeResult] of Object.entries(cachedResult.nodeResults)) {
|
|
28894
29162
|
const statusColor3 = nodeResult.status === "succeeded" ? pc35.green : pc35.red;
|
|
28895
29163
|
console.log(` ${statusColor3(nodeResult.status)} ${pc35.bold(nodeResult.slug)} (${pc35.dim(nodeId)})`);
|
|
28896
29164
|
if (nodeResult.error) {
|
|
28897
29165
|
console.log(` ${pc35.red(nodeResult.error)}`);
|
|
28898
29166
|
}
|
|
28899
29167
|
}
|
|
28900
|
-
if (
|
|
29168
|
+
if (cachedResult.artifacts.length > 0) {
|
|
28901
29169
|
console.log("");
|
|
28902
29170
|
console.log(pc35.bold(" Artifacts:"));
|
|
28903
|
-
for (const art of
|
|
29171
|
+
for (const art of cachedResult.artifacts) {
|
|
28904
29172
|
console.log(` ${pc35.dim("\xB7")} ${art.artifactType} (${art.artifactId})`);
|
|
28905
29173
|
}
|
|
28906
29174
|
}
|
|
28907
|
-
if (
|
|
29175
|
+
if (cachedResult.summary) {
|
|
28908
29176
|
console.log("");
|
|
28909
29177
|
console.log(pc35.bold(" Summary:"));
|
|
28910
|
-
if (
|
|
28911
|
-
if (typeof
|
|
28912
|
-
if (typeof
|
|
28913
|
-
if (typeof
|
|
28914
|
-
if (
|
|
28915
|
-
if (
|
|
29178
|
+
if (cachedResult.summary.outputText) console.log(` ${pc35.dim("\xB7")} ${cachedResult.summary.outputText}`);
|
|
29179
|
+
if (typeof cachedResult.summary.imageCount === "number") console.log(` ${pc35.dim("\xB7")} images: ${cachedResult.summary.imageCount}`);
|
|
29180
|
+
if (typeof cachedResult.summary.slideCount === "number") console.log(` ${pc35.dim("\xB7")} slides: ${cachedResult.summary.slideCount}`);
|
|
29181
|
+
if (typeof cachedResult.summary.videoCount === "number") console.log(` ${pc35.dim("\xB7")} videos: ${cachedResult.summary.videoCount}`);
|
|
29182
|
+
if (cachedResult.summary.workflowRunId) console.log(` ${pc35.dim("\xB7")} workflow_run_id: ${cachedResult.summary.workflowRunId}`);
|
|
29183
|
+
if (cachedResult.summary.keyboardShortcutHint) console.log(` ${pc35.dim("\xB7")} ${cachedResult.summary.keyboardShortcutHint}`);
|
|
28916
29184
|
}
|
|
28917
29185
|
try {
|
|
28918
29186
|
const credits = await fetchHostedCredits(session);
|
|
@@ -28928,8 +29196,8 @@ Examples:
|
|
|
28928
29196
|
}
|
|
28929
29197
|
}
|
|
28930
29198
|
const artifactStore = createArtifactStore();
|
|
28931
|
-
for (const artRef of
|
|
28932
|
-
const nodeResult =
|
|
29199
|
+
for (const artRef of cachedResult.artifacts) {
|
|
29200
|
+
const nodeResult = cachedResult.nodeResults[artRef.nodeId];
|
|
28933
29201
|
artifactStore.create({
|
|
28934
29202
|
artifactType: artRef.artifactType,
|
|
28935
29203
|
sourceNodeSlug: nodeResult?.slug ?? "unknown",
|
|
@@ -28937,7 +29205,11 @@ Examples:
|
|
|
28937
29205
|
pipelineId: pipeline.pipelineId,
|
|
28938
29206
|
nodeId: artRef.nodeId,
|
|
28939
29207
|
threadId: pipeline.threadId,
|
|
28940
|
-
metadata:
|
|
29208
|
+
metadata: {
|
|
29209
|
+
...artRef.metadata ?? {},
|
|
29210
|
+
...artRef.url ? { url: artRef.url } : {},
|
|
29211
|
+
...artRef.storagePath ? { storagePath: artRef.storagePath } : {}
|
|
29212
|
+
}
|
|
28941
29213
|
});
|
|
28942
29214
|
}
|
|
28943
29215
|
console.log("");
|
|
@@ -28949,7 +29221,10 @@ Examples:
|
|
|
28949
29221
|
}
|
|
28950
29222
|
|
|
28951
29223
|
// src/commands/artifact.ts
|
|
29224
|
+
import fs40 from "node:fs";
|
|
29225
|
+
import path48 from "node:path";
|
|
28952
29226
|
import pc36 from "picocolors";
|
|
29227
|
+
init_session_store();
|
|
28953
29228
|
function hr7(width = 72) {
|
|
28954
29229
|
return pc36.dim("\u2500".repeat(width));
|
|
28955
29230
|
}
|
|
@@ -29027,6 +29302,64 @@ function printArtifactDetail(art) {
|
|
|
29027
29302
|
console.log(hr7());
|
|
29028
29303
|
console.log("");
|
|
29029
29304
|
}
|
|
29305
|
+
function stringMetadata(value) {
|
|
29306
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
29307
|
+
}
|
|
29308
|
+
function inferOutPath(storagePath, out) {
|
|
29309
|
+
if (out?.trim()) return path48.resolve(out);
|
|
29310
|
+
return path48.resolve(process.cwd(), path48.basename(storagePath));
|
|
29311
|
+
}
|
|
29312
|
+
async function downloadStoragePath(storagePath, outPath, bucket = "node_documents") {
|
|
29313
|
+
const session = readSession();
|
|
29314
|
+
if (!session || isSessionExpired(session)) {
|
|
29315
|
+
throw new Error("Hosted session expired. Run `growthub auth login` again.");
|
|
29316
|
+
}
|
|
29317
|
+
const url = new URL("/api/secure-image", `${session.hostedBaseUrl.replace(/\/+$/, "")}/`);
|
|
29318
|
+
url.searchParams.set("bucket", bucket);
|
|
29319
|
+
url.searchParams.set("path", storagePath);
|
|
29320
|
+
const response = await fetch(url, {
|
|
29321
|
+
headers: {
|
|
29322
|
+
authorization: `Bearer ${session.accessToken}`,
|
|
29323
|
+
...session.userId ? { "x-user-id": session.userId } : {}
|
|
29324
|
+
}
|
|
29325
|
+
});
|
|
29326
|
+
if (!response.ok) {
|
|
29327
|
+
throw new Error(`Authenticated artifact download failed (${response.status}).`);
|
|
29328
|
+
}
|
|
29329
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
29330
|
+
fs40.mkdirSync(path48.dirname(outPath), { recursive: true });
|
|
29331
|
+
fs40.writeFileSync(outPath, buffer);
|
|
29332
|
+
return buffer.length;
|
|
29333
|
+
}
|
|
29334
|
+
function resolveStorageRef(id, artifactSelector, direct) {
|
|
29335
|
+
if (direct?.storagePath?.trim()) {
|
|
29336
|
+
return {
|
|
29337
|
+
storagePath: direct.storagePath.trim(),
|
|
29338
|
+
bucket: direct.bucket?.trim() || "node_documents",
|
|
29339
|
+
artifactId: direct.storagePath.trim()
|
|
29340
|
+
};
|
|
29341
|
+
}
|
|
29342
|
+
if (!id) return null;
|
|
29343
|
+
const execution = readExecutionResult(id);
|
|
29344
|
+
if (execution) {
|
|
29345
|
+
const artifacts = execution.artifacts.filter((artifact2) => artifact2.storagePath);
|
|
29346
|
+
const selected = artifactSelector ? artifacts.find((artifact2) => artifact2.artifactId === artifactSelector || artifact2.nodeId === artifactSelector) : artifacts[0];
|
|
29347
|
+
if (!selected?.storagePath) return null;
|
|
29348
|
+
return {
|
|
29349
|
+
storagePath: selected.storagePath,
|
|
29350
|
+
bucket: stringMetadata(selected.metadata?.bucket) ?? "node_documents",
|
|
29351
|
+
artifactId: selected.artifactId
|
|
29352
|
+
};
|
|
29353
|
+
}
|
|
29354
|
+
const artifact = createArtifactStore().get(id);
|
|
29355
|
+
const storagePath = stringMetadata(artifact?.metadata?.storagePath) ?? stringMetadata(artifact?.metadata?.storage_path);
|
|
29356
|
+
if (!artifact || !storagePath) return null;
|
|
29357
|
+
return {
|
|
29358
|
+
storagePath,
|
|
29359
|
+
bucket: stringMetadata(artifact.metadata?.bucket) ?? "node_documents",
|
|
29360
|
+
artifactId: artifact.id
|
|
29361
|
+
};
|
|
29362
|
+
}
|
|
29030
29363
|
function registerArtifactCommands(program2) {
|
|
29031
29364
|
const art = program2.command("artifact").description("List and inspect pipeline execution artifacts").addHelpText("after", `
|
|
29032
29365
|
Examples:
|
|
@@ -29035,6 +29368,8 @@ Examples:
|
|
|
29035
29368
|
$ growthub artifact list --pipeline <id> # filter by pipeline
|
|
29036
29369
|
$ growthub artifact list --json # machine-readable output
|
|
29037
29370
|
$ growthub artifact inspect <id> # inspect a specific artifact
|
|
29371
|
+
$ growthub artifact download <executionId>
|
|
29372
|
+
$ growthub artifact download --storage-path workflow_videos/<user>/<thread>/<file>.mp4
|
|
29038
29373
|
`);
|
|
29039
29374
|
art.action(async (opts) => {
|
|
29040
29375
|
const store = createArtifactStore();
|
|
@@ -29075,27 +29410,543 @@ Examples:
|
|
|
29075
29410
|
}
|
|
29076
29411
|
printArtifactDetail(artifact);
|
|
29077
29412
|
});
|
|
29413
|
+
art.command("download").description("Download a hosted execution artifact through the authenticated Growthub proxy").argument("[id]", "Execution ID from pipeline execute, or local artifact ID").option("--artifact <id>", "Artifact ID or node ID when an execution has multiple artifacts").option("--storage-path <path>", "Download this Growthub storage path directly with the active auth session").option("--bucket <bucket>", "Storage bucket for --storage-path", "node_documents").option("--out <path>", "Output file path").option("--json", "Output raw JSON").action(async (id, opts) => {
|
|
29414
|
+
try {
|
|
29415
|
+
const ref = resolveStorageRef(id, opts.artifact, {
|
|
29416
|
+
storagePath: opts.storagePath,
|
|
29417
|
+
bucket: opts.bucket
|
|
29418
|
+
});
|
|
29419
|
+
if (!ref) {
|
|
29420
|
+
throw new Error(
|
|
29421
|
+
id ? `No downloadable storagePath found for "${id}".` : "Provide an execution ID, local artifact ID, or --storage-path."
|
|
29422
|
+
);
|
|
29423
|
+
}
|
|
29424
|
+
const outPath = inferOutPath(ref.storagePath, opts.out);
|
|
29425
|
+
const bytes = await downloadStoragePath(ref.storagePath, outPath, ref.bucket);
|
|
29426
|
+
const payload = {
|
|
29427
|
+
status: "ok",
|
|
29428
|
+
id,
|
|
29429
|
+
artifactId: ref.artifactId,
|
|
29430
|
+
bucket: ref.bucket,
|
|
29431
|
+
storagePath: ref.storagePath,
|
|
29432
|
+
outPath,
|
|
29433
|
+
bytes
|
|
29434
|
+
};
|
|
29435
|
+
if (opts.json) {
|
|
29436
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
29437
|
+
return;
|
|
29438
|
+
}
|
|
29439
|
+
console.log(pc36.green("Downloaded artifact"));
|
|
29440
|
+
console.log(` ${pc36.dim("Artifact:")} ${payload.artifactId}`);
|
|
29441
|
+
console.log(` ${pc36.dim("Storage:")} ${payload.bucket}/${payload.storagePath}`);
|
|
29442
|
+
console.log(` ${pc36.dim("Output:")} ${payload.outPath}`);
|
|
29443
|
+
console.log(` ${pc36.dim("Bytes:")} ${payload.bytes}`);
|
|
29444
|
+
} catch (err) {
|
|
29445
|
+
if (opts.json) {
|
|
29446
|
+
console.log(JSON.stringify({
|
|
29447
|
+
status: "error",
|
|
29448
|
+
message: err instanceof Error ? err.message : String(err)
|
|
29449
|
+
}, null, 2));
|
|
29450
|
+
} else {
|
|
29451
|
+
console.error(pc36.red("Download failed: " + (err instanceof Error ? err.message : String(err))));
|
|
29452
|
+
}
|
|
29453
|
+
process.exitCode = 1;
|
|
29454
|
+
}
|
|
29455
|
+
});
|
|
29456
|
+
}
|
|
29457
|
+
|
|
29458
|
+
// src/commands/bridge.ts
|
|
29459
|
+
import path50 from "node:path";
|
|
29460
|
+
import pc37 from "picocolors";
|
|
29461
|
+
|
|
29462
|
+
// src/runtime/growthub-bridge-client/index.ts
|
|
29463
|
+
init_session_store();
|
|
29464
|
+
import fs41 from "node:fs";
|
|
29465
|
+
import path49 from "node:path";
|
|
29466
|
+
function readActiveSession() {
|
|
29467
|
+
const session = readSession();
|
|
29468
|
+
if (!session || isSessionExpired(session)) {
|
|
29469
|
+
throw new Error("Hosted session expired. Run `growthub auth login` again.");
|
|
29470
|
+
}
|
|
29471
|
+
if (!session.userId) {
|
|
29472
|
+
throw new Error("Hosted session is missing userId. Run `growthub auth login` again.");
|
|
29473
|
+
}
|
|
29474
|
+
return session;
|
|
29475
|
+
}
|
|
29476
|
+
function bridgeUrl(session, pathname, params) {
|
|
29477
|
+
const baseUrl = process.env.GROWTHUB_BRIDGE_BASE_URL?.trim() || session.hostedBaseUrl;
|
|
29478
|
+
const url = new URL(pathname, `${baseUrl.replace(/\/+$/, "")}/`);
|
|
29479
|
+
for (const [key, value] of Object.entries(params ?? {})) {
|
|
29480
|
+
if (value === void 0 || value === "") continue;
|
|
29481
|
+
url.searchParams.set(key, String(value));
|
|
29482
|
+
}
|
|
29483
|
+
return url;
|
|
29484
|
+
}
|
|
29485
|
+
function buildSupabaseSessionCookie(session) {
|
|
29486
|
+
const payload = {
|
|
29487
|
+
access_token: session.accessToken,
|
|
29488
|
+
user: {
|
|
29489
|
+
id: session.userId,
|
|
29490
|
+
email: session.email
|
|
29491
|
+
}
|
|
29492
|
+
};
|
|
29493
|
+
return `sb-growthub-auth-token=${encodeURIComponent(JSON.stringify(payload))}`;
|
|
29494
|
+
}
|
|
29495
|
+
async function requestJson(session, url, init = {}) {
|
|
29496
|
+
const headers = {
|
|
29497
|
+
accept: "application/json",
|
|
29498
|
+
authorization: `Bearer ${session.accessToken}`,
|
|
29499
|
+
"x-user-id": session.userId ?? "",
|
|
29500
|
+
...Object.fromEntries(new Headers(init.headers).entries())
|
|
29501
|
+
};
|
|
29502
|
+
if (init.body !== void 0 && !headers["content-type"]) {
|
|
29503
|
+
headers["content-type"] = "application/json";
|
|
29504
|
+
}
|
|
29505
|
+
const response = await fetch(url, { ...init, headers });
|
|
29506
|
+
const text69 = await response.text();
|
|
29507
|
+
const parsed = text69.trim() ? safeJson(text69) : null;
|
|
29508
|
+
if (!response.ok) {
|
|
29509
|
+
const message = typeof parsed === "object" && parsed && "error" in parsed ? String(parsed.error) : `Growthub bridge request failed (${response.status})`;
|
|
29510
|
+
throw new Error(message);
|
|
29511
|
+
}
|
|
29512
|
+
return parsed;
|
|
29513
|
+
}
|
|
29514
|
+
async function requestJsonWithSessionCookie(session, url, init = {}) {
|
|
29515
|
+
const headers = {
|
|
29516
|
+
accept: "application/json",
|
|
29517
|
+
cookie: buildSupabaseSessionCookie(session),
|
|
29518
|
+
...Object.fromEntries(new Headers(init.headers).entries())
|
|
29519
|
+
};
|
|
29520
|
+
if (init.body !== void 0 && !headers["content-type"]) {
|
|
29521
|
+
headers["content-type"] = "application/json";
|
|
29522
|
+
}
|
|
29523
|
+
const response = await fetch(url, { ...init, headers });
|
|
29524
|
+
const text69 = await response.text();
|
|
29525
|
+
const parsed = text69.trim() ? safeJson(text69) : null;
|
|
29526
|
+
if (!response.ok) {
|
|
29527
|
+
const message = typeof parsed === "object" && parsed && "error" in parsed ? String(parsed.error) : `Growthub session-backed request failed (${response.status})`;
|
|
29528
|
+
throw new Error(message);
|
|
29529
|
+
}
|
|
29530
|
+
return parsed;
|
|
29531
|
+
}
|
|
29532
|
+
function safeJson(text69) {
|
|
29533
|
+
try {
|
|
29534
|
+
return JSON.parse(text69);
|
|
29535
|
+
} catch {
|
|
29536
|
+
return text69;
|
|
29537
|
+
}
|
|
29538
|
+
}
|
|
29539
|
+
var GrowthubBridgeClient = class {
|
|
29540
|
+
constructor(session = readActiveSession()) {
|
|
29541
|
+
this.session = session;
|
|
29542
|
+
}
|
|
29543
|
+
session;
|
|
29544
|
+
async listAssets(query = {}) {
|
|
29545
|
+
const url = bridgeUrl(this.session, "/api/cli/profile", {
|
|
29546
|
+
view: "gallery-assets",
|
|
29547
|
+
page: query.page ?? 1,
|
|
29548
|
+
limit: query.limit ?? 20,
|
|
29549
|
+
source: query.source,
|
|
29550
|
+
mediaType: query.mediaType,
|
|
29551
|
+
search: query.search
|
|
29552
|
+
});
|
|
29553
|
+
const bridgeResult = await requestJson(this.session, url);
|
|
29554
|
+
if (isAssetListResponse(bridgeResult)) return bridgeResult;
|
|
29555
|
+
const fallback = bridgeUrl(this.session, "/api/gallery/assets", {
|
|
29556
|
+
userId: this.session.userId,
|
|
29557
|
+
page: query.page ?? 1,
|
|
29558
|
+
limit: query.limit ?? 20,
|
|
29559
|
+
source: query.source,
|
|
29560
|
+
mediaType: query.mediaType,
|
|
29561
|
+
search: query.search
|
|
29562
|
+
});
|
|
29563
|
+
const fallbackResult = await requestJson(this.session, fallback);
|
|
29564
|
+
if (isAssetListResponse(fallbackResult)) return fallbackResult;
|
|
29565
|
+
throw new Error("Growthub bridge asset list did not return the asset contract.");
|
|
29566
|
+
}
|
|
29567
|
+
async listBrandKits(query = {}) {
|
|
29568
|
+
const url = bridgeUrl(this.session, "/api/brand-settings");
|
|
29569
|
+
const directResult = await requestJsonWithSessionCookie(this.session, url);
|
|
29570
|
+
if (!isRemoteBrandKitArray(directResult)) {
|
|
29571
|
+
throw new Error("Growthub brand settings endpoint did not return brand kits.");
|
|
29572
|
+
}
|
|
29573
|
+
const brandKits = directResult.brandKits;
|
|
29574
|
+
if (!query.includeAssets || brandKits.length === 0) {
|
|
29575
|
+
return {
|
|
29576
|
+
success: true,
|
|
29577
|
+
userId: this.session.userId,
|
|
29578
|
+
brandKits,
|
|
29579
|
+
count: brandKits.length
|
|
29580
|
+
};
|
|
29581
|
+
}
|
|
29582
|
+
const assetsResult = await this.listBrandAssets();
|
|
29583
|
+
const assetsByKitId = /* @__PURE__ */ new Map();
|
|
29584
|
+
for (const asset of assetsResult.assets) {
|
|
29585
|
+
const key = String(asset.brand_kit_id);
|
|
29586
|
+
assetsByKitId.set(key, [...assetsByKitId.get(key) ?? [], asset]);
|
|
29587
|
+
}
|
|
29588
|
+
return {
|
|
29589
|
+
success: true,
|
|
29590
|
+
userId: this.session.userId,
|
|
29591
|
+
brandKits: brandKits.map((kit) => ({
|
|
29592
|
+
...kit,
|
|
29593
|
+
assets: assetsByKitId.get(String(kit.id)) ?? []
|
|
29594
|
+
})),
|
|
29595
|
+
count: brandKits.length
|
|
29596
|
+
};
|
|
29597
|
+
}
|
|
29598
|
+
async listBrandAssets(query = {}) {
|
|
29599
|
+
const url = bridgeUrl(this.session, "/api/brand-settings/assets", {
|
|
29600
|
+
brandKitId: query.brandKitId
|
|
29601
|
+
});
|
|
29602
|
+
const result = await requestJsonWithSessionCookie(this.session, url);
|
|
29603
|
+
if (!isRemoteBrandAssetArray(result)) {
|
|
29604
|
+
throw new Error("Growthub brand assets endpoint did not return brand assets.");
|
|
29605
|
+
}
|
|
29606
|
+
const assets2 = query.assetType ? result.assets.filter((asset) => asset.asset_type === query.assetType) : result.assets;
|
|
29607
|
+
return {
|
|
29608
|
+
success: true,
|
|
29609
|
+
userId: this.session.userId,
|
|
29610
|
+
brandKitId: query.brandKitId,
|
|
29611
|
+
assets: assets2,
|
|
29612
|
+
count: assets2.length
|
|
29613
|
+
};
|
|
29614
|
+
}
|
|
29615
|
+
async listKnowledge(query = {}) {
|
|
29616
|
+
const url = bridgeUrl(this.session, "/api/cli/profile", {
|
|
29617
|
+
view: "knowledge",
|
|
29618
|
+
type: query.type,
|
|
29619
|
+
agentSlug: query.agentSlug,
|
|
29620
|
+
tableId: query.tableId
|
|
29621
|
+
});
|
|
29622
|
+
const result = await requestJson(this.session, url);
|
|
29623
|
+
if (isKnowledgeListResponse(result)) return result;
|
|
29624
|
+
const providerUrl = bridgeUrl(this.session, "/api/providers/growthub-local/knowledge/items", {
|
|
29625
|
+
type: query.type,
|
|
29626
|
+
agentSlug: query.agentSlug,
|
|
29627
|
+
tableId: query.tableId
|
|
29628
|
+
});
|
|
29629
|
+
const providerResult = await requestJson(this.session, providerUrl);
|
|
29630
|
+
if (isKnowledgeListResponse(providerResult)) return providerResult;
|
|
29631
|
+
throw new Error("Growthub bridge knowledge list did not return the knowledge contract.");
|
|
29632
|
+
}
|
|
29633
|
+
async listKnowledgeTables(query = {}) {
|
|
29634
|
+
const url = bridgeUrl(this.session, "/api/cli/profile", {
|
|
29635
|
+
view: "knowledge-tables",
|
|
29636
|
+
origin: query.origin,
|
|
29637
|
+
connectorType: query.connectorType
|
|
29638
|
+
});
|
|
29639
|
+
const result = await requestJson(this.session, url);
|
|
29640
|
+
if (isKnowledgeTableListResponse(result)) return result;
|
|
29641
|
+
const providerUrl = bridgeUrl(this.session, "/api/providers/growthub-local/knowledge/tables", {
|
|
29642
|
+
origin: query.origin,
|
|
29643
|
+
connectorType: query.connectorType
|
|
29644
|
+
});
|
|
29645
|
+
const providerResult = await requestJson(this.session, providerUrl);
|
|
29646
|
+
if (isKnowledgeTableListResponse(providerResult)) return providerResult;
|
|
29647
|
+
throw new Error("Growthub bridge knowledge table list did not return the knowledge table contract.");
|
|
29648
|
+
}
|
|
29649
|
+
async saveKnowledge(input) {
|
|
29650
|
+
const url = bridgeUrl(this.session, "/api/cli/profile", { action: "save-knowledge" });
|
|
29651
|
+
const result = await requestJson(this.session, url, {
|
|
29652
|
+
method: "POST",
|
|
29653
|
+
body: JSON.stringify(input)
|
|
29654
|
+
});
|
|
29655
|
+
if (result && typeof result === "object" && "success" in result) {
|
|
29656
|
+
return result;
|
|
29657
|
+
}
|
|
29658
|
+
const providerUrl = bridgeUrl(this.session, "/api/providers/growthub-local/knowledge/items");
|
|
29659
|
+
const providerResult = await requestJson(this.session, providerUrl, {
|
|
29660
|
+
method: "POST",
|
|
29661
|
+
body: JSON.stringify(input)
|
|
29662
|
+
});
|
|
29663
|
+
if (providerResult && typeof providerResult === "object" && "success" in providerResult) {
|
|
29664
|
+
return providerResult;
|
|
29665
|
+
}
|
|
29666
|
+
throw new Error("Growthub bridge knowledge save did not return the knowledge save contract.");
|
|
29667
|
+
}
|
|
29668
|
+
deleteKnowledge(id) {
|
|
29669
|
+
const url = bridgeUrl(this.session, "/api/cli/profile", { action: "delete-knowledge" });
|
|
29670
|
+
return requestJson(this.session, url, {
|
|
29671
|
+
method: "POST",
|
|
29672
|
+
body: JSON.stringify({ id })
|
|
29673
|
+
});
|
|
29674
|
+
}
|
|
29675
|
+
updateKnowledgeMetadata(input) {
|
|
29676
|
+
const url = bridgeUrl(this.session, "/api/providers/growthub-local/knowledge/items/metadata");
|
|
29677
|
+
return requestJson(this.session, url, {
|
|
29678
|
+
method: "PATCH",
|
|
29679
|
+
body: JSON.stringify(input)
|
|
29680
|
+
});
|
|
29681
|
+
}
|
|
29682
|
+
syncRunOutput(input) {
|
|
29683
|
+
const url = bridgeUrl(this.session, "/api/providers/growthub-local/runs/sync");
|
|
29684
|
+
return requestJson(this.session, url, {
|
|
29685
|
+
method: "POST",
|
|
29686
|
+
body: JSON.stringify(input)
|
|
29687
|
+
});
|
|
29688
|
+
}
|
|
29689
|
+
listMcpAccounts() {
|
|
29690
|
+
const url = bridgeUrl(this.session, "/api/mcp/accounts");
|
|
29691
|
+
return requestJson(this.session, url);
|
|
29692
|
+
}
|
|
29693
|
+
async downloadStoragePath(storagePath, outPath, bucket = "node_documents") {
|
|
29694
|
+
const url = bridgeUrl(this.session, "/api/secure-image", { bucket, path: storagePath });
|
|
29695
|
+
const response = await fetch(url, {
|
|
29696
|
+
headers: {
|
|
29697
|
+
authorization: `Bearer ${this.session.accessToken}`,
|
|
29698
|
+
"x-user-id": this.session.userId ?? ""
|
|
29699
|
+
}
|
|
29700
|
+
});
|
|
29701
|
+
if (!response.ok) {
|
|
29702
|
+
throw new Error(`Authenticated artifact download failed (${response.status}).`);
|
|
29703
|
+
}
|
|
29704
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
29705
|
+
fs41.mkdirSync(path49.dirname(path49.resolve(outPath)), { recursive: true });
|
|
29706
|
+
fs41.writeFileSync(path49.resolve(outPath), buffer);
|
|
29707
|
+
return buffer.length;
|
|
29708
|
+
}
|
|
29709
|
+
async downloadKnowledge(id, outPath) {
|
|
29710
|
+
const url = bridgeUrl(this.session, "/api/cli/profile", { view: "knowledge-download", id });
|
|
29711
|
+
const response = await fetch(url, {
|
|
29712
|
+
headers: {
|
|
29713
|
+
authorization: `Bearer ${this.session.accessToken}`,
|
|
29714
|
+
"x-user-id": this.session.userId ?? ""
|
|
29715
|
+
}
|
|
29716
|
+
});
|
|
29717
|
+
if (!response.ok) {
|
|
29718
|
+
throw new Error(`Knowledge download failed (${response.status}).`);
|
|
29719
|
+
}
|
|
29720
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
29721
|
+
fs41.mkdirSync(path49.dirname(path49.resolve(outPath)), { recursive: true });
|
|
29722
|
+
fs41.writeFileSync(path49.resolve(outPath), buffer);
|
|
29723
|
+
return buffer.length;
|
|
29724
|
+
}
|
|
29725
|
+
};
|
|
29726
|
+
function isAssetListResponse(value) {
|
|
29727
|
+
return Boolean(
|
|
29728
|
+
value && typeof value === "object" && Array.isArray(value.assets) && typeof value.pagination?.total === "number"
|
|
29729
|
+
);
|
|
29730
|
+
}
|
|
29731
|
+
function isRemoteBrandKitArray(value) {
|
|
29732
|
+
return Boolean(
|
|
29733
|
+
value && typeof value === "object" && Array.isArray(value.brandKits)
|
|
29734
|
+
);
|
|
29735
|
+
}
|
|
29736
|
+
function isRemoteBrandAssetArray(value) {
|
|
29737
|
+
return Boolean(
|
|
29738
|
+
value && typeof value === "object" && Array.isArray(value.assets)
|
|
29739
|
+
);
|
|
29740
|
+
}
|
|
29741
|
+
function isKnowledgeListResponse(value) {
|
|
29742
|
+
return Boolean(
|
|
29743
|
+
value && typeof value === "object" && Array.isArray(value.items) && typeof value.count === "number"
|
|
29744
|
+
);
|
|
29745
|
+
}
|
|
29746
|
+
function isKnowledgeTableListResponse(value) {
|
|
29747
|
+
return Boolean(
|
|
29748
|
+
value && typeof value === "object" && Array.isArray(value.tables) && typeof value.count === "number"
|
|
29749
|
+
);
|
|
29750
|
+
}
|
|
29751
|
+
function createGrowthubBridgeClient() {
|
|
29752
|
+
return new GrowthubBridgeClient();
|
|
29753
|
+
}
|
|
29754
|
+
|
|
29755
|
+
// src/commands/bridge.ts
|
|
29756
|
+
function printRows(rows, keys) {
|
|
29757
|
+
for (const row of rows) {
|
|
29758
|
+
console.log(keys.map((key) => `${pc37.dim(`${key}:`)} ${String(row[key] ?? "")}`).join(" "));
|
|
29759
|
+
}
|
|
29760
|
+
}
|
|
29761
|
+
function outPathFromStorage(storagePath, out) {
|
|
29762
|
+
return path50.resolve(out?.trim() || path50.basename(storagePath));
|
|
29763
|
+
}
|
|
29764
|
+
function registerBridgeCommands(program2) {
|
|
29765
|
+
const bridge = program2.command("bridge").description("Authenticated Growthub bridge resources: assets, knowledge, and MCP accounts.");
|
|
29766
|
+
const assets2 = bridge.command("assets").description("User asset gallery through the Growthub bridge.");
|
|
29767
|
+
assets2.command("list").description("List user-owned gallery assets.").option("--page <page>", "Page number", (value) => Number(value), 1).option("--limit <limit>", "Page size", (value) => Number(value), 20).option("--source <source>", "Source filter").option("--media-type <type>", "Media type filter").option("--search <query>", "Filename/source search").option("--json", "Output raw JSON").action(async (opts) => {
|
|
29768
|
+
const client = createGrowthubBridgeClient();
|
|
29769
|
+
const result = await client.listAssets({
|
|
29770
|
+
page: opts.page,
|
|
29771
|
+
limit: opts.limit,
|
|
29772
|
+
source: opts.source,
|
|
29773
|
+
mediaType: opts.mediaType,
|
|
29774
|
+
search: opts.search
|
|
29775
|
+
});
|
|
29776
|
+
if (opts.json) {
|
|
29777
|
+
console.log(JSON.stringify(result, null, 2));
|
|
29778
|
+
return;
|
|
29779
|
+
}
|
|
29780
|
+
console.log(pc37.bold(`Growthub assets (${result.assets.length}/${result.pagination.total})`));
|
|
29781
|
+
printRows(result.assets.map((asset) => ({
|
|
29782
|
+
id: asset.id,
|
|
29783
|
+
type: asset.type,
|
|
29784
|
+
source: asset.source,
|
|
29785
|
+
storage: asset.storage_path
|
|
29786
|
+
})), ["id", "type", "source", "storage"]);
|
|
29787
|
+
});
|
|
29788
|
+
assets2.command("download").description("Download a user-owned gallery asset by storage path.").requiredOption("--storage-path <path>", "Asset storage path from bridge assets list").option("--bucket <bucket>", "Storage bucket", "node_documents").option("--out <path>", "Output file path").option("--json", "Output raw JSON").action(async (opts) => {
|
|
29789
|
+
const client = createGrowthubBridgeClient();
|
|
29790
|
+
const outPath = outPathFromStorage(opts.storagePath, opts.out);
|
|
29791
|
+
const bytes = await client.downloadStoragePath(opts.storagePath, outPath, opts.bucket);
|
|
29792
|
+
const payload = { success: true, storagePath: opts.storagePath, bucket: opts.bucket, outPath, bytes };
|
|
29793
|
+
if (opts.json) console.log(JSON.stringify(payload, null, 2));
|
|
29794
|
+
else console.log(`${pc37.green("Downloaded")} ${outPath} (${bytes} bytes)`);
|
|
29795
|
+
});
|
|
29796
|
+
const brand = bridge.command("brand").description("User brand kits and brand assets through the Growthub bridge.");
|
|
29797
|
+
brand.command("kits").description("List accessible remote brand kits.").option("--include-assets", "Include each brand kit's assets").option("--json", "Output raw JSON").action(async (opts) => {
|
|
29798
|
+
const client = createGrowthubBridgeClient();
|
|
29799
|
+
const result = await client.listBrandKits({ includeAssets: opts.includeAssets === true });
|
|
29800
|
+
if (opts.json) {
|
|
29801
|
+
console.log(JSON.stringify(result, null, 2));
|
|
29802
|
+
return;
|
|
29803
|
+
}
|
|
29804
|
+
console.log(pc37.bold(`Growthub brand kits (${result.count})`));
|
|
29805
|
+
printRows(result.brandKits.map((kit) => ({
|
|
29806
|
+
id: kit.id,
|
|
29807
|
+
name: kit.brand_name,
|
|
29808
|
+
visibility: kit.visibility,
|
|
29809
|
+
assets: kit.assets?.length ?? ""
|
|
29810
|
+
})), ["id", "name", "visibility", "assets"]);
|
|
29811
|
+
});
|
|
29812
|
+
brand.command("assets").description("List remote brand assets from accessible brand kits.").option("--brand-kit-id <id>", "Filter by brand kit id").option("--asset-type <type>", "Filter by asset type").option("--json", "Output raw JSON").action(async (opts) => {
|
|
29813
|
+
const client = createGrowthubBridgeClient();
|
|
29814
|
+
const result = await client.listBrandAssets({ brandKitId: opts.brandKitId, assetType: opts.assetType });
|
|
29815
|
+
if (opts.json) {
|
|
29816
|
+
console.log(JSON.stringify(result, null, 2));
|
|
29817
|
+
return;
|
|
29818
|
+
}
|
|
29819
|
+
console.log(pc37.bold(`Growthub brand assets (${result.count})`));
|
|
29820
|
+
printRows(result.assets.map((asset) => ({
|
|
29821
|
+
id: asset.id,
|
|
29822
|
+
kit: asset.brand_kit_id,
|
|
29823
|
+
type: asset.asset_type,
|
|
29824
|
+
storage: asset.storage_path
|
|
29825
|
+
})), ["id", "kit", "type", "storage"]);
|
|
29826
|
+
});
|
|
29827
|
+
brand.command("download").description("Download a remote brand asset by storage path.").requiredOption("--storage-path <path>", "Brand asset storage path from bridge brand assets").option("--bucket <bucket>", "Storage bucket", "node_documents").option("--out <path>", "Output file path").option("--json", "Output raw JSON").action(async (opts) => {
|
|
29828
|
+
const client = createGrowthubBridgeClient();
|
|
29829
|
+
const outPath = outPathFromStorage(opts.storagePath, opts.out);
|
|
29830
|
+
const bytes = await client.downloadStoragePath(opts.storagePath, outPath, opts.bucket);
|
|
29831
|
+
const payload = { success: true, storagePath: opts.storagePath, bucket: opts.bucket, outPath, bytes };
|
|
29832
|
+
if (opts.json) console.log(JSON.stringify(payload, null, 2));
|
|
29833
|
+
else console.log(`${pc37.green("Downloaded")} ${outPath} (${bytes} bytes)`);
|
|
29834
|
+
});
|
|
29835
|
+
const knowledge = bridge.command("knowledge").description("User knowledge items through the Growthub bridge.");
|
|
29836
|
+
knowledge.command("tables").description("List user-owned knowledge tables, the custom groupings for knowledge items.").option("--origin <origin>", "Filter by metadata.origin").option("--connector-type <type>", "Filter by metadata.connector_type").option("--json", "Output raw JSON").action(async (opts) => {
|
|
29837
|
+
const client = createGrowthubBridgeClient();
|
|
29838
|
+
const result = await client.listKnowledgeTables({ origin: opts.origin, connectorType: opts.connectorType });
|
|
29839
|
+
if (opts.json) {
|
|
29840
|
+
console.log(JSON.stringify(result, null, 2));
|
|
29841
|
+
return;
|
|
29842
|
+
}
|
|
29843
|
+
console.log(pc37.bold(`Growthub knowledge tables (${result.count})`));
|
|
29844
|
+
printRows(result.tables.map((table) => ({
|
|
29845
|
+
id: table.id,
|
|
29846
|
+
file: table.file_name,
|
|
29847
|
+
origin: table.metadata?.origin,
|
|
29848
|
+
children: table.child_count ?? 0
|
|
29849
|
+
})), ["id", "file", "origin", "children"]);
|
|
29850
|
+
});
|
|
29851
|
+
knowledge.command("list").description("List user-owned knowledge items.").option("--type <type>", "Knowledge source type").option("--agent-slug <slug>", "Agent slug filter").option("--table-id <id>", "Filter by metadata.table_id knowledge table grouping").option("--json", "Output raw JSON").action(async (opts) => {
|
|
29852
|
+
const client = createGrowthubBridgeClient();
|
|
29853
|
+
const result = await client.listKnowledge({ type: opts.type, agentSlug: opts.agentSlug, tableId: opts.tableId });
|
|
29854
|
+
if (opts.json) {
|
|
29855
|
+
console.log(JSON.stringify(result, null, 2));
|
|
29856
|
+
return;
|
|
29857
|
+
}
|
|
29858
|
+
console.log(pc37.bold(`Growthub knowledge (${result.count})`));
|
|
29859
|
+
printRows(result.items.map((item) => ({
|
|
29860
|
+
id: item.id,
|
|
29861
|
+
file: item.file_name,
|
|
29862
|
+
type: item.source_type,
|
|
29863
|
+
storage: item.storage_path
|
|
29864
|
+
})), ["id", "file", "type", "storage"]);
|
|
29865
|
+
});
|
|
29866
|
+
knowledge.command("write").description("Create or update a markdown knowledge item.").option("--id <id>", "Existing knowledge item id to update").option("--title <title>", "Title for a new item").option("--file-name <name>", "File name for an update").option("--content <content>", "Markdown content for a new item").option("--table-id <id>", "Attach new item to metadata.table_id knowledge table grouping").option("--notes <notes>", "Notes metadata").option("--agent-slug <slug>", "Agent slug", "growthub-cli").option("--json", "Output raw JSON").action(async (opts) => {
|
|
29867
|
+
const client = createGrowthubBridgeClient();
|
|
29868
|
+
const result = await client.saveKnowledge({
|
|
29869
|
+
id: opts.id,
|
|
29870
|
+
title: opts.title,
|
|
29871
|
+
fileName: opts.fileName,
|
|
29872
|
+
content: opts.content,
|
|
29873
|
+
tableId: opts.tableId,
|
|
29874
|
+
notes: opts.notes,
|
|
29875
|
+
agentSlug: opts.agentSlug
|
|
29876
|
+
});
|
|
29877
|
+
if (opts.json) console.log(JSON.stringify(result, null, 2));
|
|
29878
|
+
else console.log(result.success ? pc37.green("Knowledge saved") : pc37.red(result.error ?? "Knowledge save failed"));
|
|
29879
|
+
});
|
|
29880
|
+
knowledge.command("download").description("Download a knowledge item by id.").argument("<id>", "Knowledge item id").requiredOption("--out <path>", "Output file path").option("--json", "Output raw JSON").action(async (id, opts) => {
|
|
29881
|
+
const client = createGrowthubBridgeClient();
|
|
29882
|
+
const outPath = path50.resolve(opts.out);
|
|
29883
|
+
const bytes = await client.downloadKnowledge(id, outPath);
|
|
29884
|
+
const payload = { success: true, id, outPath, bytes };
|
|
29885
|
+
if (opts.json) console.log(JSON.stringify(payload, null, 2));
|
|
29886
|
+
else console.log(`${pc37.green("Downloaded")} ${outPath} (${bytes} bytes)`);
|
|
29887
|
+
});
|
|
29888
|
+
knowledge.command("delete").description("Delete a knowledge item by id.").argument("<id>", "Knowledge item id").option("--json", "Output raw JSON").action(async (id, opts) => {
|
|
29889
|
+
const client = createGrowthubBridgeClient();
|
|
29890
|
+
const result = await client.deleteKnowledge(id);
|
|
29891
|
+
if (opts.json) console.log(JSON.stringify(result, null, 2));
|
|
29892
|
+
else console.log(result.success ? pc37.green("Knowledge deleted") : pc37.red(result.error ?? "Knowledge delete failed"));
|
|
29893
|
+
});
|
|
29894
|
+
knowledge.command("metadata").description("Patch metadata for an existing knowledge item.").argument("<id>", "Knowledge item id").option("--table-id <id>", "Set metadata.table_id").option("--notes <notes>", "Set metadata.notes").option("--json", "Output raw JSON").action(async (id, opts) => {
|
|
29895
|
+
const client = createGrowthubBridgeClient();
|
|
29896
|
+
const result = await client.updateKnowledgeMetadata({ id, tableId: opts.tableId, notes: opts.notes });
|
|
29897
|
+
if (opts.json) console.log(JSON.stringify(result, null, 2));
|
|
29898
|
+
else console.log(result.success ? pc37.green("Knowledge metadata updated") : pc37.red(result.error ?? "Knowledge metadata update failed"));
|
|
29899
|
+
});
|
|
29900
|
+
bridge.command("run-sync").description("Persist a local run output into the remote Growthub knowledge substrate.").option("--run-id <id>", "Run id").option("--title <title>", "Knowledge item title").option("--output <json>", "JSON output payload").option("--table-id <id>", "Attach to metadata.table_id").option("--agent-slug <slug>", "Agent slug", "growthub_local_bridge").option("--json", "Output raw JSON").action(async (opts) => {
|
|
29901
|
+
const client = createGrowthubBridgeClient();
|
|
29902
|
+
const parsedOutput = opts.output ? JSON.parse(opts.output) : {};
|
|
29903
|
+
const result = await client.syncRunOutput({
|
|
29904
|
+
runId: opts.runId,
|
|
29905
|
+
title: opts.title,
|
|
29906
|
+
output: parsedOutput,
|
|
29907
|
+
tableId: opts.tableId,
|
|
29908
|
+
agentSlug: opts.agentSlug
|
|
29909
|
+
});
|
|
29910
|
+
if (opts.json) console.log(JSON.stringify(result, null, 2));
|
|
29911
|
+
else console.log(result.success ? pc37.green("Run output synced") : pc37.red(result.error ?? "Run output sync failed"));
|
|
29912
|
+
});
|
|
29913
|
+
const mcp = bridge.command("mcp").description("MCP bridge accounts.");
|
|
29914
|
+
mcp.command("accounts").description("List connected MCP accounts.").option("--json", "Output raw JSON").action(async (opts) => {
|
|
29915
|
+
const client = createGrowthubBridgeClient();
|
|
29916
|
+
const result = await client.listMcpAccounts();
|
|
29917
|
+
if (opts.json) {
|
|
29918
|
+
console.log(JSON.stringify(result, null, 2));
|
|
29919
|
+
return;
|
|
29920
|
+
}
|
|
29921
|
+
console.log(pc37.bold(`Growthub MCP accounts (${result.accounts.length})`));
|
|
29922
|
+
printRows(result.accounts.map((account) => ({
|
|
29923
|
+
id: account.id,
|
|
29924
|
+
provider: account.provider,
|
|
29925
|
+
app: account.appSlug,
|
|
29926
|
+
active: account.isActive
|
|
29927
|
+
})), ["id", "provider", "app", "active"]);
|
|
29928
|
+
});
|
|
29078
29929
|
}
|
|
29079
29930
|
|
|
29080
29931
|
// src/commands/workflow.ts
|
|
29081
|
-
import
|
|
29082
|
-
import
|
|
29932
|
+
import fs43 from "node:fs";
|
|
29933
|
+
import path52 from "node:path";
|
|
29083
29934
|
import * as p24 from "@clack/prompts";
|
|
29084
|
-
import
|
|
29935
|
+
import pc39 from "picocolors";
|
|
29085
29936
|
init_session_store();
|
|
29086
29937
|
init_hosted_client();
|
|
29087
29938
|
|
|
29088
29939
|
// src/runtime/workflow-hygiene/labels.ts
|
|
29089
29940
|
init_home();
|
|
29090
|
-
import
|
|
29091
|
-
import
|
|
29941
|
+
import fs42 from "node:fs";
|
|
29942
|
+
import path51 from "node:path";
|
|
29092
29943
|
function resolveStorePath() {
|
|
29093
|
-
return
|
|
29944
|
+
return path51.resolve(resolvePaperclipHomeDir(), "workflow-hygiene", "labels.json");
|
|
29094
29945
|
}
|
|
29095
29946
|
function readStoreFile(filePath) {
|
|
29096
|
-
if (!
|
|
29947
|
+
if (!fs42.existsSync(filePath)) return { records: [] };
|
|
29097
29948
|
try {
|
|
29098
|
-
const raw = JSON.parse(
|
|
29949
|
+
const raw = JSON.parse(fs42.readFileSync(filePath, "utf-8"));
|
|
29099
29950
|
if (!Array.isArray(raw.records)) return { records: [] };
|
|
29100
29951
|
return raw;
|
|
29101
29952
|
} catch {
|
|
@@ -29103,8 +29954,8 @@ function readStoreFile(filePath) {
|
|
|
29103
29954
|
}
|
|
29104
29955
|
}
|
|
29105
29956
|
function writeStoreFile(filePath, data) {
|
|
29106
|
-
|
|
29107
|
-
|
|
29957
|
+
fs42.mkdirSync(path51.dirname(filePath), { recursive: true });
|
|
29958
|
+
fs42.writeFileSync(filePath, `${JSON.stringify(data, null, 2)}
|
|
29108
29959
|
`, "utf-8");
|
|
29109
29960
|
}
|
|
29110
29961
|
function inferDefaultLabel(name, createdAt, versionCount) {
|
|
@@ -29146,11 +29997,11 @@ function createWorkflowHygieneStore() {
|
|
|
29146
29997
|
}
|
|
29147
29998
|
|
|
29148
29999
|
// src/runtime/workflow-hygiene/summaries.ts
|
|
29149
|
-
import
|
|
30000
|
+
import pc38 from "picocolors";
|
|
29150
30001
|
function renderWorkflowLabel(label) {
|
|
29151
|
-
if (label === "canonical") return
|
|
29152
|
-
if (label === "archived") return
|
|
29153
|
-
return
|
|
30002
|
+
if (label === "canonical") return pc38.green("canonical");
|
|
30003
|
+
if (label === "archived") return pc38.dim("archived");
|
|
30004
|
+
return pc38.yellow("experimental");
|
|
29154
30005
|
}
|
|
29155
30006
|
function enrichWorkflowSummaries(entries, store) {
|
|
29156
30007
|
return entries.map((entry) => {
|
|
@@ -29164,14 +30015,14 @@ init_banner();
|
|
|
29164
30015
|
init_home();
|
|
29165
30016
|
var PAGE_SIZE = 10;
|
|
29166
30017
|
var FAMILY_CONFIG2 = {
|
|
29167
|
-
video: { color:
|
|
29168
|
-
image: { color:
|
|
29169
|
-
slides: { color:
|
|
29170
|
-
text: { color:
|
|
29171
|
-
data: { color:
|
|
29172
|
-
ops: { color:
|
|
29173
|
-
research: { color:
|
|
29174
|
-
vision: { color:
|
|
30018
|
+
video: { color: pc39.magenta, label: "Video" },
|
|
30019
|
+
image: { color: pc39.cyan, label: "Image" },
|
|
30020
|
+
slides: { color: pc39.yellow, label: "Slides" },
|
|
30021
|
+
text: { color: pc39.green, label: "Text" },
|
|
30022
|
+
data: { color: pc39.blue, label: "Data" },
|
|
30023
|
+
ops: { color: pc39.red, label: "Ops" },
|
|
30024
|
+
research: { color: pc39.blue, label: "Research" },
|
|
30025
|
+
vision: { color: pc39.cyan, label: "Vision" }
|
|
29175
30026
|
};
|
|
29176
30027
|
var FAMILY_EMOJI = {
|
|
29177
30028
|
video: "\u{1F3AC}",
|
|
@@ -29188,7 +30039,7 @@ function familyLabel(family) {
|
|
|
29188
30039
|
return cfg ? cfg.color(cfg.label) : family;
|
|
29189
30040
|
}
|
|
29190
30041
|
function hr8(width = 72) {
|
|
29191
|
-
return
|
|
30042
|
+
return pc39.dim("\u2500".repeat(width));
|
|
29192
30043
|
}
|
|
29193
30044
|
function stripAnsi6(str) {
|
|
29194
30045
|
return str.replace(/\x1B\[[0-9;]*m/g, "");
|
|
@@ -29196,25 +30047,25 @@ function stripAnsi6(str) {
|
|
|
29196
30047
|
function box5(lines) {
|
|
29197
30048
|
const padded = lines.map((l) => " " + l);
|
|
29198
30049
|
const width = Math.max(...padded.map((l) => stripAnsi6(l).length)) + 4;
|
|
29199
|
-
const top =
|
|
29200
|
-
const bottom =
|
|
30050
|
+
const top = pc39.dim("\u250C" + "\u2500".repeat(width) + "\u2510");
|
|
30051
|
+
const bottom = pc39.dim("\u2514" + "\u2500".repeat(width) + "\u2518");
|
|
29201
30052
|
const body = padded.map((l) => {
|
|
29202
30053
|
const pad2 = width - stripAnsi6(l).length;
|
|
29203
|
-
return
|
|
30054
|
+
return pc39.dim("\u2502") + l + " ".repeat(pad2) + pc39.dim("\u2502");
|
|
29204
30055
|
});
|
|
29205
30056
|
return [top, ...body, bottom].join("\n");
|
|
29206
30057
|
}
|
|
29207
30058
|
function resolveSavedWorkflowsDir() {
|
|
29208
|
-
return
|
|
30059
|
+
return path52.resolve(resolvePaperclipHomeDir(), "workflows");
|
|
29209
30060
|
}
|
|
29210
30061
|
function resolveDeletedWorkflowIdsPath() {
|
|
29211
|
-
return
|
|
30062
|
+
return path52.resolve(resolvePaperclipHomeDir(), "workflow-hygiene", "deleted-workflows.json");
|
|
29212
30063
|
}
|
|
29213
30064
|
function readDeletedWorkflowIds() {
|
|
29214
30065
|
const filePath = resolveDeletedWorkflowIdsPath();
|
|
29215
|
-
if (!
|
|
30066
|
+
if (!fs43.existsSync(filePath)) return /* @__PURE__ */ new Set();
|
|
29216
30067
|
try {
|
|
29217
|
-
const raw = JSON.parse(
|
|
30068
|
+
const raw = JSON.parse(fs43.readFileSync(filePath, "utf-8"));
|
|
29218
30069
|
if (!Array.isArray(raw?.workflowIds)) return /* @__PURE__ */ new Set();
|
|
29219
30070
|
return new Set(raw.workflowIds.filter((value) => typeof value === "string"));
|
|
29220
30071
|
} catch {
|
|
@@ -29223,8 +30074,8 @@ function readDeletedWorkflowIds() {
|
|
|
29223
30074
|
}
|
|
29224
30075
|
function writeDeletedWorkflowIds(ids) {
|
|
29225
30076
|
const filePath = resolveDeletedWorkflowIdsPath();
|
|
29226
|
-
|
|
29227
|
-
|
|
30077
|
+
fs43.mkdirSync(path52.dirname(filePath), { recursive: true });
|
|
30078
|
+
fs43.writeFileSync(filePath, `${JSON.stringify({ workflowIds: [...ids] }, null, 2)}
|
|
29228
30079
|
`, "utf-8");
|
|
29229
30080
|
}
|
|
29230
30081
|
function markWorkflowDeletedLocally(workflowId) {
|
|
@@ -29250,10 +30101,10 @@ function filterLocallyDeletedWorkflows(entries) {
|
|
|
29250
30101
|
}
|
|
29251
30102
|
function listLocalSavedWorkflows() {
|
|
29252
30103
|
const dir = resolveSavedWorkflowsDir();
|
|
29253
|
-
if (!
|
|
29254
|
-
const entries =
|
|
30104
|
+
if (!fs43.existsSync(dir)) return [];
|
|
30105
|
+
const entries = fs43.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".json")).map((e) => {
|
|
29255
30106
|
try {
|
|
29256
|
-
const raw = JSON.parse(
|
|
30107
|
+
const raw = JSON.parse(fs43.readFileSync(path52.resolve(dir, e.name), "utf-8"));
|
|
29257
30108
|
const pipeline = raw.pipeline ?? raw;
|
|
29258
30109
|
return {
|
|
29259
30110
|
filename: e.name,
|
|
@@ -29314,11 +30165,11 @@ async function archiveSavedWorkflow(entry) {
|
|
|
29314
30165
|
throw new Error("Local workflow entry is missing filename.");
|
|
29315
30166
|
}
|
|
29316
30167
|
const dir = resolveSavedWorkflowsDir();
|
|
29317
|
-
const archiveDir =
|
|
29318
|
-
|
|
29319
|
-
|
|
29320
|
-
|
|
29321
|
-
|
|
30168
|
+
const archiveDir = path52.resolve(dir, "archived");
|
|
30169
|
+
fs43.mkdirSync(archiveDir, { recursive: true });
|
|
30170
|
+
fs43.renameSync(
|
|
30171
|
+
path52.resolve(dir, entry.filename),
|
|
30172
|
+
path52.resolve(archiveDir, entry.filename)
|
|
29322
30173
|
);
|
|
29323
30174
|
}
|
|
29324
30175
|
async function deleteSavedWorkflow(entry) {
|
|
@@ -29342,7 +30193,7 @@ async function deleteSavedWorkflow(entry) {
|
|
|
29342
30193
|
if (!entry.filename) {
|
|
29343
30194
|
throw new Error("Local workflow entry is missing filename.");
|
|
29344
30195
|
}
|
|
29345
|
-
|
|
30196
|
+
fs43.rmSync(path52.resolve(resolveSavedWorkflowsDir(), entry.filename), { force: true });
|
|
29346
30197
|
markWorkflowDeletedLocally(entry.workflowId);
|
|
29347
30198
|
}
|
|
29348
30199
|
async function loadSavedWorkflowDetail(entry) {
|
|
@@ -29361,7 +30212,7 @@ async function loadSavedWorkflowDetail(entry) {
|
|
|
29361
30212
|
};
|
|
29362
30213
|
}
|
|
29363
30214
|
const dir = resolveSavedWorkflowsDir();
|
|
29364
|
-
const content =
|
|
30215
|
+
const content = fs43.readFileSync(path52.resolve(dir, entry.filename), "utf-8");
|
|
29365
30216
|
const raw = JSON.parse(content);
|
|
29366
30217
|
return {
|
|
29367
30218
|
pipeline: raw.pipeline ?? raw,
|
|
@@ -29431,7 +30282,7 @@ async function paginatedSelect(message, allOptions, opts) {
|
|
|
29431
30282
|
const hasPrev = offset > 0;
|
|
29432
30283
|
const totalPages = Math.ceil(filtered.length / PAGE_SIZE);
|
|
29433
30284
|
const currentPage = Math.floor(offset / PAGE_SIZE) + 1;
|
|
29434
|
-
const pageInfo = filtered.length > PAGE_SIZE ?
|
|
30285
|
+
const pageInfo = filtered.length > PAGE_SIZE ? pc39.dim(` (${currentPage}/${totalPages} \xB7 ${filtered.length} total)`) : "";
|
|
29435
30286
|
const options = [
|
|
29436
30287
|
...page.map((o) => ({
|
|
29437
30288
|
value: o.value,
|
|
@@ -29440,13 +30291,13 @@ async function paginatedSelect(message, allOptions, opts) {
|
|
|
29440
30291
|
}))
|
|
29441
30292
|
];
|
|
29442
30293
|
if (hasMore) {
|
|
29443
|
-
options.push({ value: "__next_page", label:
|
|
30294
|
+
options.push({ value: "__next_page", label: pc39.dim("\u2192 Next page") });
|
|
29444
30295
|
}
|
|
29445
30296
|
if (hasPrev) {
|
|
29446
|
-
options.push({ value: "__prev_page", label:
|
|
30297
|
+
options.push({ value: "__prev_page", label: pc39.dim("\u2190 Previous page") });
|
|
29447
30298
|
}
|
|
29448
30299
|
if (opts?.searchEnabled) {
|
|
29449
|
-
options.push({ value: "__search", label:
|
|
30300
|
+
options.push({ value: "__search", label: pc39.dim("\u{1F50E} Search") });
|
|
29450
30301
|
}
|
|
29451
30302
|
options.push({
|
|
29452
30303
|
value: opts?.backValue ?? "__back",
|
|
@@ -29494,8 +30345,8 @@ async function paginatedSelect(message, allOptions, opts) {
|
|
|
29494
30345
|
function printTemplateCard(node) {
|
|
29495
30346
|
const contract = introspectNodeContract(node);
|
|
29496
30347
|
const lines = renderContractCard(contract);
|
|
29497
|
-
lines.splice(1, 0, `${familyLabel(node.family)} ${node.enabled ?
|
|
29498
|
-
if (node.description) lines.push("",
|
|
30348
|
+
lines.splice(1, 0, `${familyLabel(node.family)} ${node.enabled ? pc39.green("enabled") : pc39.red("disabled")}`);
|
|
30349
|
+
if (node.description) lines.push("", pc39.dim(node.description));
|
|
29499
30350
|
console.log("");
|
|
29500
30351
|
console.log(box5(lines));
|
|
29501
30352
|
console.log("");
|
|
@@ -29509,9 +30360,9 @@ function renderTemplateTree(templates) {
|
|
|
29509
30360
|
byFamily.set(key, existing);
|
|
29510
30361
|
}
|
|
29511
30362
|
const families = [...byFamily.entries()].sort((a, b) => a[0].localeCompare(b[0]));
|
|
29512
|
-
const lines = [
|
|
30363
|
+
const lines = [pc39.bold("Public CMS Node Tree")];
|
|
29513
30364
|
for (const [family, nodes] of families) {
|
|
29514
|
-
lines.push(`${
|
|
30365
|
+
lines.push(`${pc39.cyan("\u2022")} ${pc39.bold(family)}`);
|
|
29515
30366
|
const sorted = [...nodes].sort((a, b) => a.slug.localeCompare(b.slug));
|
|
29516
30367
|
for (const [index51, node] of sorted.entries()) {
|
|
29517
30368
|
const branch = index51 === sorted.length - 1 ? "\u2514\u2500" : "\u251C\u2500";
|
|
@@ -29519,12 +30370,12 @@ function renderTemplateTree(templates) {
|
|
|
29519
30370
|
const requiredInputs = contract.inputs.filter((input) => input.required).length;
|
|
29520
30371
|
const optionalInputs = contract.inputs.length - requiredInputs;
|
|
29521
30372
|
lines.push(
|
|
29522
|
-
` ${branch} ${node.slug} ${
|
|
30373
|
+
` ${branch} ${node.slug} ${pc39.dim(`(req:${requiredInputs} opt:${optionalInputs} out:${contract.outputTypes.length})`)}`
|
|
29523
30374
|
);
|
|
29524
30375
|
}
|
|
29525
30376
|
}
|
|
29526
30377
|
lines.push("");
|
|
29527
|
-
lines.push(
|
|
30378
|
+
lines.push(pc39.dim("Shortcut: growthub workflow saved --json"));
|
|
29528
30379
|
return lines;
|
|
29529
30380
|
}
|
|
29530
30381
|
function renderWorkflowContractDiscoveryTree(nodes) {
|
|
@@ -29536,10 +30387,10 @@ function renderWorkflowContractDiscoveryTree(nodes) {
|
|
|
29536
30387
|
byFamily.set(key, group);
|
|
29537
30388
|
}
|
|
29538
30389
|
const families = [...byFamily.entries()].sort((a, b) => a[0].localeCompare(b[0]));
|
|
29539
|
-
const lines = [
|
|
30390
|
+
const lines = [pc39.bold("CMS Node Contract Discovery")];
|
|
29540
30391
|
for (const [family, familyNodes] of families) {
|
|
29541
30392
|
const emoji = FAMILY_EMOJI[family] ?? "\u2022";
|
|
29542
|
-
lines.push(`${emoji} ${
|
|
30393
|
+
lines.push(`${emoji} ${pc39.bold(familyLabel(family))} ${pc39.dim(`(${familyNodes.length})`)}`);
|
|
29543
30394
|
const sorted = [...familyNodes].sort((a, b) => a.slug.localeCompare(b.slug));
|
|
29544
30395
|
for (const [index51, node] of sorted.entries()) {
|
|
29545
30396
|
const branch = index51 === sorted.length - 1 ? "\u2514\u2500" : "\u251C\u2500";
|
|
@@ -29547,7 +30398,7 @@ function renderWorkflowContractDiscoveryTree(nodes) {
|
|
|
29547
30398
|
const requiredInputs = contract.inputs.filter((input) => input.required).length;
|
|
29548
30399
|
const optionalInputs = contract.inputs.length - requiredInputs;
|
|
29549
30400
|
lines.push(
|
|
29550
|
-
` ${branch} ${node.slug} ${
|
|
30401
|
+
` ${branch} ${node.slug} ${pc39.dim(`req:${requiredInputs} opt:${optionalInputs} bindings:${contract.requiredBindings.length} outputs:${contract.outputTypes.length}`)}`
|
|
29551
30402
|
);
|
|
29552
30403
|
}
|
|
29553
30404
|
}
|
|
@@ -29560,7 +30411,7 @@ function buildTemplateOption(template, viewMode) {
|
|
|
29560
30411
|
if (viewMode === "expanded") {
|
|
29561
30412
|
return {
|
|
29562
30413
|
value: template.slug,
|
|
29563
|
-
label: `${template.icon} ${template.displayName} ${
|
|
30414
|
+
label: `${template.icon} ${template.displayName} ${pc39.dim(template.slug)}`,
|
|
29564
30415
|
hint: `req:${requiredInputs} opt:${optionalInputs} outputs:${contract.outputTypes.join(", ") || "none"} exec:${contract.executionStrategy}`
|
|
29565
30416
|
};
|
|
29566
30417
|
}
|
|
@@ -29582,11 +30433,11 @@ async function runWorkflowPicker(opts) {
|
|
|
29582
30433
|
const hygieneStore = createWorkflowHygieneStore();
|
|
29583
30434
|
const access = getWorkflowAccess();
|
|
29584
30435
|
if (access.state === "unauthenticated") {
|
|
29585
|
-
p24.intro(
|
|
30436
|
+
p24.intro(pc39.bold("Workflows") + pc39.dim(" (not connected)"));
|
|
29586
30437
|
p24.note(
|
|
29587
30438
|
[
|
|
29588
30439
|
"Workflow assembly requires an authenticated Growthub session.",
|
|
29589
|
-
"Run " +
|
|
30440
|
+
"Run " + pc39.cyan("growthub auth login") + " to connect your account.",
|
|
29590
30441
|
"",
|
|
29591
30442
|
"Once connected you can:",
|
|
29592
30443
|
" - Browse CMS node contracts",
|
|
@@ -29598,7 +30449,7 @@ async function runWorkflowPicker(opts) {
|
|
|
29598
30449
|
if (opts.allowBackToHub) return "back";
|
|
29599
30450
|
return "done";
|
|
29600
30451
|
}
|
|
29601
|
-
p24.intro(
|
|
30452
|
+
p24.intro(pc39.bold("Workflows"));
|
|
29602
30453
|
while (true) {
|
|
29603
30454
|
const refreshedAccess = getWorkflowAccess();
|
|
29604
30455
|
const topChoice = await p24.select({
|
|
@@ -29606,12 +30457,12 @@ async function runWorkflowPicker(opts) {
|
|
|
29606
30457
|
options: [
|
|
29607
30458
|
{
|
|
29608
30459
|
value: "contracts",
|
|
29609
|
-
label: refreshedAccess.state === "ready" ? "0. CMS Node Contracts" :
|
|
30460
|
+
label: refreshedAccess.state === "ready" ? "0. CMS Node Contracts" : pc39.dim("0. CMS Node Contracts (locked)"),
|
|
29610
30461
|
hint: refreshedAccess.state === "ready" ? "Discovery tree for CMS node primitives" : refreshedAccess.reason
|
|
29611
30462
|
},
|
|
29612
30463
|
{
|
|
29613
30464
|
value: "pipelines",
|
|
29614
|
-
label: refreshedAccess.state === "ready" ? "1. Dynamic Pipelines" :
|
|
30465
|
+
label: refreshedAccess.state === "ready" ? "1. Dynamic Pipelines" : pc39.dim("1. Dynamic Pipelines (locked)"),
|
|
29615
30466
|
hint: refreshedAccess.state === "ready" ? "Create new pipelines and route into Saved Workflows" : refreshedAccess.reason
|
|
29616
30467
|
},
|
|
29617
30468
|
{
|
|
@@ -29678,7 +30529,7 @@ async function runWorkflowPicker(opts) {
|
|
|
29678
30529
|
const requiredInputs = contract.inputs.filter((input) => input.required).length;
|
|
29679
30530
|
return {
|
|
29680
30531
|
value: node.slug,
|
|
29681
|
-
label: `${node.icon} ${node.displayName} ${
|
|
30532
|
+
label: `${node.icon} ${node.displayName} ${pc39.dim(node.slug)}`,
|
|
29682
30533
|
hint: `${node.family} \xB7 required:${requiredInputs} \xB7 bindings:${contract.requiredBindings.length} \xB7 outputs:${contract.outputTypes.length}`
|
|
29683
30534
|
};
|
|
29684
30535
|
});
|
|
@@ -29719,7 +30570,7 @@ async function runWorkflowPicker(opts) {
|
|
|
29719
30570
|
}
|
|
29720
30571
|
}
|
|
29721
30572
|
} catch (err) {
|
|
29722
|
-
contractsSpinner.stop(
|
|
30573
|
+
contractsSpinner.stop(pc39.red("Failed to load CMS node contracts."));
|
|
29723
30574
|
p24.log.error("Failed to load CMS node contracts: " + err.message);
|
|
29724
30575
|
}
|
|
29725
30576
|
continue;
|
|
@@ -29754,14 +30605,14 @@ async function runWorkflowPicker(opts) {
|
|
|
29754
30605
|
saved = withEffectiveWorkflowLabels(enriched, hygieneStore);
|
|
29755
30606
|
savedSpinner.stop(`Loaded ${saved.length} saved workflow${saved.length === 1 ? "" : "s"}.`);
|
|
29756
30607
|
} catch (err) {
|
|
29757
|
-
savedSpinner.stop(
|
|
30608
|
+
savedSpinner.stop(pc39.red("Failed to load saved workflows."));
|
|
29758
30609
|
throw err;
|
|
29759
30610
|
}
|
|
29760
30611
|
if (saved.length === 0) {
|
|
29761
30612
|
p24.note(
|
|
29762
30613
|
[
|
|
29763
30614
|
"No saved workflows found.",
|
|
29764
|
-
"Use " +
|
|
30615
|
+
"Use " + pc39.cyan("growthub pipeline assemble") + " to create a new workflow pipeline."
|
|
29765
30616
|
].join("\n"),
|
|
29766
30617
|
"Nothing saved"
|
|
29767
30618
|
);
|
|
@@ -29769,7 +30620,7 @@ async function runWorkflowPicker(opts) {
|
|
|
29769
30620
|
}
|
|
29770
30621
|
const allOptions = saved.map((w) => ({
|
|
29771
30622
|
value: w.workflowId,
|
|
29772
|
-
label: `${w.name} ${
|
|
30623
|
+
label: `${w.name} ${pc39.dim(`[${renderWorkflowLabel(w.workflowLabel)}]`)} ${pc39.dim(`${w.nodeCount} node${w.nodeCount !== 1 ? "s" : ""}`)}`,
|
|
29773
30624
|
hint: `${w.executionMode} \xB7 ${w.updatedAt?.slice(0, 10) ?? w.createdAt.slice(0, 10)}`
|
|
29774
30625
|
}));
|
|
29775
30626
|
const choice = await paginatedSelect("Select a saved workflow", allOptions, {
|
|
@@ -29790,7 +30641,7 @@ async function runWorkflowPicker(opts) {
|
|
|
29790
30641
|
detail = await loadSavedWorkflowDetail(entry);
|
|
29791
30642
|
detailSpinner.stop(`Loaded ${entry.name}.`);
|
|
29792
30643
|
} catch (err) {
|
|
29793
|
-
detailSpinner.stop(
|
|
30644
|
+
detailSpinner.stop(pc39.red(`Failed to load ${entry.name}.`));
|
|
29794
30645
|
p24.log.error(err.message);
|
|
29795
30646
|
continue;
|
|
29796
30647
|
}
|
|
@@ -29798,14 +30649,14 @@ async function runWorkflowPicker(opts) {
|
|
|
29798
30649
|
const nodes = Array.isArray(pipeline.nodes) ? pipeline.nodes : [];
|
|
29799
30650
|
console.log("");
|
|
29800
30651
|
console.log(box5([
|
|
29801
|
-
`${
|
|
29802
|
-
`${
|
|
29803
|
-
`${
|
|
29804
|
-
`${
|
|
29805
|
-
`${
|
|
30652
|
+
`${pc39.bold("Workflow:")} ${entry.name}`,
|
|
30653
|
+
`${pc39.dim("ID:")} ${entry.workflowId}`,
|
|
30654
|
+
`${pc39.dim("Mode:")} hosted ${pc39.dim("Nodes:")} ${nodes.length}`,
|
|
30655
|
+
`${pc39.dim("Label:")} ${renderWorkflowLabel(entry.workflowLabel ?? "experimental")}`,
|
|
30656
|
+
`${pc39.dim("Created:")} ${detail.createdAt || "\u2014"}`,
|
|
29806
30657
|
"",
|
|
29807
30658
|
...nodes.map(
|
|
29808
|
-
(n, i) => `${
|
|
30659
|
+
(n, i) => `${pc39.dim(String(i + 1) + ".")} ${pc39.bold(n.data?.slug ?? n.slug ?? n.id)} ${pc39.dim(n.id)}`
|
|
29809
30660
|
)
|
|
29810
30661
|
]));
|
|
29811
30662
|
console.log("");
|
|
@@ -29816,7 +30667,7 @@ async function runWorkflowPicker(opts) {
|
|
|
29816
30667
|
{ value: "set_label", label: "Set workflow label" },
|
|
29817
30668
|
{ value: "archive", label: "Archive workflow" },
|
|
29818
30669
|
{ value: "unarchive", label: "Unarchive workflow" },
|
|
29819
|
-
{ value: "delete", label:
|
|
30670
|
+
{ value: "delete", label: pc39.red("Delete workflow") },
|
|
29820
30671
|
{ value: "back_to_saved", label: "\u2190 Back to saved workflows" }
|
|
29821
30672
|
]
|
|
29822
30673
|
});
|
|
@@ -29861,7 +30712,7 @@ async function runWorkflowPicker(opts) {
|
|
|
29861
30712
|
console.log("");
|
|
29862
30713
|
}
|
|
29863
30714
|
await executeHostedPipeline(executablePipeline);
|
|
29864
|
-
p24.log.success(`Saved workflow execution completed for ${
|
|
30715
|
+
p24.log.success(`Saved workflow execution completed for ${pc39.bold(entry.name)}.`);
|
|
29865
30716
|
} catch (err) {
|
|
29866
30717
|
p24.log.error("Saved workflow execution failed: " + err.message);
|
|
29867
30718
|
}
|
|
@@ -29880,7 +30731,7 @@ async function runWorkflowPicker(opts) {
|
|
|
29880
30731
|
continue;
|
|
29881
30732
|
}
|
|
29882
30733
|
hygieneStore.setLabel(entry.workflowId, labelChoice);
|
|
29883
|
-
p24.log.success(`Updated label for ${
|
|
30734
|
+
p24.log.success(`Updated label for ${pc39.bold(entry.name)} to ${renderWorkflowLabel(labelChoice)}.`);
|
|
29884
30735
|
continue;
|
|
29885
30736
|
}
|
|
29886
30737
|
if (nextAction === "archive") {
|
|
@@ -29894,10 +30745,10 @@ async function runWorkflowPicker(opts) {
|
|
|
29894
30745
|
try {
|
|
29895
30746
|
await archiveSavedWorkflow(entry);
|
|
29896
30747
|
hygieneStore.setLabel(entry.workflowId, "archived");
|
|
29897
|
-
p24.log.success(`Archived ${
|
|
30748
|
+
p24.log.success(`Archived ${pc39.bold(entry.name)}.`);
|
|
29898
30749
|
} catch {
|
|
29899
30750
|
hygieneStore.setLabel(entry.workflowId, "archived");
|
|
29900
|
-
p24.log.success(`Archived ${
|
|
30751
|
+
p24.log.success(`Archived ${pc39.bold(entry.name)} (local fallback).`);
|
|
29901
30752
|
}
|
|
29902
30753
|
continue;
|
|
29903
30754
|
}
|
|
@@ -29919,7 +30770,7 @@ async function runWorkflowPicker(opts) {
|
|
|
29919
30770
|
}
|
|
29920
30771
|
hygieneStore.setLabel(entry.workflowId, restoreChoice);
|
|
29921
30772
|
p24.log.success(
|
|
29922
|
-
`Unarchived ${
|
|
30773
|
+
`Unarchived ${pc39.bold(entry.name)} to ${renderWorkflowLabel(restoreChoice)}.`
|
|
29923
30774
|
);
|
|
29924
30775
|
continue;
|
|
29925
30776
|
}
|
|
@@ -29940,10 +30791,10 @@ async function runWorkflowPicker(opts) {
|
|
|
29940
30791
|
}
|
|
29941
30792
|
try {
|
|
29942
30793
|
await deleteSavedWorkflow(entry);
|
|
29943
|
-
p24.log.success(`Deleted ${
|
|
30794
|
+
p24.log.success(`Deleted ${pc39.bold(entry.name)}.`);
|
|
29944
30795
|
} catch {
|
|
29945
30796
|
markWorkflowDeletedLocally(entry.workflowId);
|
|
29946
|
-
p24.log.success(`Deleted ${
|
|
30797
|
+
p24.log.success(`Deleted ${pc39.bold(entry.name)} (local fallback).`);
|
|
29947
30798
|
}
|
|
29948
30799
|
continue;
|
|
29949
30800
|
}
|
|
@@ -30060,12 +30911,12 @@ async function runWorkflowPicker(opts) {
|
|
|
30060
30911
|
const resolver = createMachineCapabilityResolver();
|
|
30061
30912
|
const binding = await resolver.resolveCapability(selected.slug);
|
|
30062
30913
|
if (binding) {
|
|
30063
|
-
const statusColor3 = binding.allowed ?
|
|
30914
|
+
const statusColor3 = binding.allowed ? pc39.green : pc39.red;
|
|
30064
30915
|
console.log("");
|
|
30065
30916
|
console.log(box5([
|
|
30066
|
-
`${
|
|
30067
|
-
`${
|
|
30068
|
-
`${
|
|
30917
|
+
`${pc39.bold("Machine Binding:")} ${selected.slug}`,
|
|
30918
|
+
`${pc39.dim("Allowed:")} ${statusColor3(String(binding.allowed))}`,
|
|
30919
|
+
`${pc39.dim("Reason:")} ${binding.reason ?? "\u2014"}`
|
|
30069
30920
|
]));
|
|
30070
30921
|
console.log("");
|
|
30071
30922
|
}
|
|
@@ -30100,7 +30951,7 @@ async function runWorkflowPicker(opts) {
|
|
|
30100
30951
|
"Input normalization"
|
|
30101
30952
|
);
|
|
30102
30953
|
const nodeId = builder.addNode(selected.slug, normalized.bindings);
|
|
30103
|
-
p24.log.success(`Added ${
|
|
30954
|
+
p24.log.success(`Added ${pc39.bold(selected.displayName)} (${pc39.dim(nodeId)})`);
|
|
30104
30955
|
const next = await p24.select({
|
|
30105
30956
|
message: "Pipeline has 1 node. What next?",
|
|
30106
30957
|
options: [
|
|
@@ -30138,7 +30989,7 @@ async function runWorkflowPicker(opts) {
|
|
|
30138
30989
|
throw new Error("Hosted workflow save returned no payload.");
|
|
30139
30990
|
}
|
|
30140
30991
|
p24.log.success(
|
|
30141
|
-
`Hosted workflow saved as ${
|
|
30992
|
+
`Hosted workflow saved as ${pc39.bold(workflowName)} (${pc39.dim(saveResult.workflowId)} \xB7 v${saveResult.version})`
|
|
30142
30993
|
);
|
|
30143
30994
|
}
|
|
30144
30995
|
break;
|
|
@@ -30188,31 +31039,31 @@ async function renderWorkflowIntelligenceSummary(pipeline, capabilities, phase)
|
|
|
30188
31039
|
};
|
|
30189
31040
|
const result = await provider.summarizeExecution(input);
|
|
30190
31041
|
const lines = [
|
|
30191
|
-
`${
|
|
31042
|
+
`${pc39.bold("Intelligence Summary")} ${pc39.dim(result.title)}`,
|
|
30192
31043
|
result.explanation
|
|
30193
31044
|
];
|
|
30194
31045
|
if (result.runtimeModeNote) {
|
|
30195
|
-
lines.push(`${
|
|
31046
|
+
lines.push(`${pc39.dim("Runtime:")} ${result.runtimeModeNote}`);
|
|
30196
31047
|
}
|
|
30197
31048
|
if (result.outputExpectation) {
|
|
30198
|
-
lines.push(`${
|
|
31049
|
+
lines.push(`${pc39.dim("Expected:")} ${result.outputExpectation}`);
|
|
30199
31050
|
}
|
|
30200
31051
|
if (result.missingBindingGuidance.length > 0) {
|
|
30201
|
-
lines.push("",
|
|
31052
|
+
lines.push("", pc39.yellow("Missing Binding Guidance"));
|
|
30202
31053
|
for (const guidance of result.missingBindingGuidance) {
|
|
30203
|
-
lines.push(` ${
|
|
31054
|
+
lines.push(` ${pc39.dim("\xB7")} ${guidance}`);
|
|
30204
31055
|
}
|
|
30205
31056
|
}
|
|
30206
31057
|
if (result.costLatencyCautions.length > 0) {
|
|
30207
|
-
lines.push("",
|
|
31058
|
+
lines.push("", pc39.yellow("Cost/Latency Notes"));
|
|
30208
31059
|
for (const caution of result.costLatencyCautions) {
|
|
30209
|
-
lines.push(` ${
|
|
31060
|
+
lines.push(` ${pc39.dim("\xB7")} ${caution}`);
|
|
30210
31061
|
}
|
|
30211
31062
|
}
|
|
30212
31063
|
if (result.warnings.length > 0) {
|
|
30213
|
-
lines.push("",
|
|
31064
|
+
lines.push("", pc39.yellow("Warnings"));
|
|
30214
31065
|
for (const warning of result.warnings) {
|
|
30215
|
-
lines.push(` ${
|
|
31066
|
+
lines.push(` ${pc39.dim("\xB7")} ${warning}`);
|
|
30216
31067
|
}
|
|
30217
31068
|
}
|
|
30218
31069
|
return lines;
|
|
@@ -30235,7 +31086,7 @@ Examples:
|
|
|
30235
31086
|
wf.command("templates").description("List CMS workflow node starter templates").option("--family <family>", "Filter by family").option("--search <term>", "Search templates").option("--view <mode>", "List view mode: condensed | expanded | tree").option("--json", "Output raw JSON").action(async (opts) => {
|
|
30236
31087
|
const access = getWorkflowAccess();
|
|
30237
31088
|
if (access.state !== "ready") {
|
|
30238
|
-
console.error(
|
|
31089
|
+
console.error(pc39.red(`${access.reason}.`));
|
|
30239
31090
|
process.exitCode = 1;
|
|
30240
31091
|
return;
|
|
30241
31092
|
}
|
|
@@ -30252,24 +31103,24 @@ Examples:
|
|
|
30252
31103
|
return;
|
|
30253
31104
|
}
|
|
30254
31105
|
if (nodes.length === 0) {
|
|
30255
|
-
console.error(
|
|
31106
|
+
console.error(pc39.yellow("No templates found."));
|
|
30256
31107
|
process.exitCode = 1;
|
|
30257
31108
|
return;
|
|
30258
31109
|
}
|
|
30259
31110
|
const viewMode = opts.view ?? "condensed";
|
|
30260
31111
|
console.log("");
|
|
30261
31112
|
console.log(
|
|
30262
|
-
|
|
31113
|
+
pc39.bold("Workflow Node Templates") + pc39.dim(` ${nodes.length} template${nodes.length !== 1 ? "s" : ""}`)
|
|
30263
31114
|
);
|
|
30264
31115
|
console.log(hr8());
|
|
30265
|
-
console.log(
|
|
30266
|
-
console.log(
|
|
30267
|
-
console.log(
|
|
31116
|
+
console.log(pc39.bold("Step 1: CMS Node Contract Validation"));
|
|
31117
|
+
console.log(pc39.dim("Validate contract visibility before template selection."));
|
|
31118
|
+
console.log(pc39.dim(`View mode: ${viewMode}`));
|
|
30268
31119
|
console.log("");
|
|
30269
31120
|
if (viewMode === "tree") {
|
|
30270
31121
|
console.log(box5(renderTemplateTree(nodes)));
|
|
30271
31122
|
console.log(hr8());
|
|
30272
|
-
console.log(
|
|
31123
|
+
console.log(pc39.dim(` Source: ${meta.source} \xB7 growthub workflow`));
|
|
30273
31124
|
console.log("");
|
|
30274
31125
|
return;
|
|
30275
31126
|
}
|
|
@@ -30277,24 +31128,24 @@ Examples:
|
|
|
30277
31128
|
const contract = introspectNodeContract(node);
|
|
30278
31129
|
const requiredInputs = contract.inputs.filter((input) => input.required).length;
|
|
30279
31130
|
const optionalInputs = contract.inputs.length - requiredInputs;
|
|
30280
|
-
const enabledTag = node.enabled ?
|
|
30281
|
-
console.log(` ${node.icon} ${
|
|
31131
|
+
const enabledTag = node.enabled ? pc39.green("enabled") : pc39.red("disabled");
|
|
31132
|
+
console.log(` ${node.icon} ${pc39.bold(node.displayName)} ${pc39.dim(node.slug)} ${enabledTag}`);
|
|
30282
31133
|
console.log(
|
|
30283
|
-
` ${
|
|
31134
|
+
` ${pc39.dim("Contract:")} ${pc39.dim("required")}=${requiredInputs} ${pc39.dim("optional")}=${optionalInputs} ${pc39.dim("bindings")}=${contract.requiredBindings.length} ${pc39.dim("outputs")}=${contract.outputTypes.length}`
|
|
30284
31135
|
);
|
|
30285
31136
|
console.log(
|
|
30286
|
-
` ${
|
|
31137
|
+
` ${pc39.dim("Execution:")} ${contract.executionStrategy} \xB7 ${contract.executionKind}`
|
|
30287
31138
|
);
|
|
30288
31139
|
if (node.description) {
|
|
30289
|
-
console.log(` ${
|
|
31140
|
+
console.log(` ${pc39.dim(node.description)}`);
|
|
30290
31141
|
}
|
|
30291
31142
|
console.log("");
|
|
30292
31143
|
}
|
|
30293
31144
|
console.log(hr8());
|
|
30294
|
-
console.log(
|
|
31145
|
+
console.log(pc39.dim(` Source: ${meta.source} \xB7 growthub workflow`));
|
|
30295
31146
|
console.log("");
|
|
30296
31147
|
} catch (err) {
|
|
30297
|
-
console.error(
|
|
31148
|
+
console.error(pc39.red("Failed: " + err.message));
|
|
30298
31149
|
process.exitCode = 1;
|
|
30299
31150
|
}
|
|
30300
31151
|
});
|
|
@@ -30314,67 +31165,67 @@ Examples:
|
|
|
30314
31165
|
return;
|
|
30315
31166
|
}
|
|
30316
31167
|
if (visibleSaved.length === 0) {
|
|
30317
|
-
console.log(
|
|
31168
|
+
console.log(pc39.dim("No saved workflows. Run `growthub workflow` to assemble one."));
|
|
30318
31169
|
return;
|
|
30319
31170
|
}
|
|
30320
31171
|
console.log("");
|
|
30321
31172
|
console.log(
|
|
30322
|
-
|
|
31173
|
+
pc39.bold("Saved Workflows") + pc39.dim(` ${visibleSaved.length} workflow${visibleSaved.length !== 1 ? "s" : ""}`)
|
|
30323
31174
|
);
|
|
30324
31175
|
if (!opts.includeArchived) {
|
|
30325
31176
|
const hiddenArchivedCount = saved.length - visibleSaved.length;
|
|
30326
31177
|
if (hiddenArchivedCount > 0) {
|
|
30327
|
-
console.log(
|
|
31178
|
+
console.log(pc39.dim(` Archived hidden: ${hiddenArchivedCount} (use --include-archived to show)`));
|
|
30328
31179
|
}
|
|
30329
31180
|
}
|
|
30330
31181
|
console.log(hr8());
|
|
30331
31182
|
for (const w of visibleSaved) {
|
|
30332
31183
|
console.log(
|
|
30333
|
-
` ${
|
|
31184
|
+
` ${pc39.bold(w.name)} ` + pc39.dim(`[${renderWorkflowLabel(w.workflowLabel)}] `) + pc39.dim(`${w.nodeCount} node${w.nodeCount !== 1 ? "s" : ""} \xB7 ${w.executionMode} \xB7 ${w.updatedAt?.slice(0, 10) ?? w.createdAt.slice(0, 10)}`)
|
|
30334
31185
|
);
|
|
30335
31186
|
}
|
|
30336
31187
|
console.log("");
|
|
30337
|
-
console.log(
|
|
31188
|
+
console.log(pc39.dim(` Source: ${visibleSaved[0]?.source === "hosted" ? "hosted workflow registry" : resolveSavedWorkflowsDir()}`));
|
|
30338
31189
|
console.log("");
|
|
30339
31190
|
});
|
|
30340
31191
|
}
|
|
30341
31192
|
|
|
30342
31193
|
// src/commands/open-agents.ts
|
|
30343
31194
|
import * as p25 from "@clack/prompts";
|
|
30344
|
-
import
|
|
31195
|
+
import pc40 from "picocolors";
|
|
30345
31196
|
|
|
30346
31197
|
// src/runtime/agent-harness/auth-store.ts
|
|
30347
31198
|
init_home();
|
|
30348
|
-
import
|
|
30349
|
-
import
|
|
31199
|
+
import fs44 from "node:fs";
|
|
31200
|
+
import path53 from "node:path";
|
|
30350
31201
|
function resolveHarnessAuthDir() {
|
|
30351
|
-
return
|
|
31202
|
+
return path53.resolve(resolvePaperclipHomeDir(), "harness-auth");
|
|
30352
31203
|
}
|
|
30353
31204
|
function resolveHarnessAuthFile(harnessId) {
|
|
30354
|
-
return
|
|
31205
|
+
return path53.resolve(resolveHarnessAuthDir(), `${harnessId}.json`);
|
|
30355
31206
|
}
|
|
30356
31207
|
function normalizeSecret(value) {
|
|
30357
31208
|
const trimmed = value?.trim();
|
|
30358
31209
|
return trimmed && trimmed.length > 0 ? trimmed : void 0;
|
|
30359
31210
|
}
|
|
30360
31211
|
function ensureSecureDir(dirPath) {
|
|
30361
|
-
|
|
31212
|
+
fs44.mkdirSync(dirPath, { recursive: true });
|
|
30362
31213
|
try {
|
|
30363
|
-
|
|
31214
|
+
fs44.chmodSync(dirPath, 448);
|
|
30364
31215
|
} catch {
|
|
30365
31216
|
}
|
|
30366
31217
|
}
|
|
30367
31218
|
function ensureSecureFile(filePath) {
|
|
30368
31219
|
try {
|
|
30369
|
-
|
|
31220
|
+
fs44.chmodSync(filePath, 384);
|
|
30370
31221
|
} catch {
|
|
30371
31222
|
}
|
|
30372
31223
|
}
|
|
30373
31224
|
function readHarnessCredentials(harnessId) {
|
|
30374
31225
|
const filePath = resolveHarnessAuthFile(harnessId);
|
|
30375
|
-
if (!
|
|
31226
|
+
if (!fs44.existsSync(filePath)) return {};
|
|
30376
31227
|
try {
|
|
30377
|
-
const parsed = JSON.parse(
|
|
31228
|
+
const parsed = JSON.parse(fs44.readFileSync(filePath, "utf-8"));
|
|
30378
31229
|
const creds = {};
|
|
30379
31230
|
for (const [key, value] of Object.entries(parsed)) {
|
|
30380
31231
|
if (typeof value === "string" && value.trim().length > 0) {
|
|
@@ -30401,7 +31252,7 @@ function setHarnessCredential(harnessId, key, value) {
|
|
|
30401
31252
|
const dirPath = resolveHarnessAuthDir();
|
|
30402
31253
|
ensureSecureDir(dirPath);
|
|
30403
31254
|
const filePath = resolveHarnessAuthFile(harnessId);
|
|
30404
|
-
|
|
31255
|
+
fs44.writeFileSync(filePath, `${JSON.stringify(creds, null, 2)}
|
|
30405
31256
|
`, "utf-8");
|
|
30406
31257
|
ensureSecureFile(filePath);
|
|
30407
31258
|
}
|
|
@@ -30418,7 +31269,7 @@ function setHarnessCredentials(harnessId, updates) {
|
|
|
30418
31269
|
const dirPath = resolveHarnessAuthDir();
|
|
30419
31270
|
ensureSecureDir(dirPath);
|
|
30420
31271
|
const filePath = resolveHarnessAuthFile(harnessId);
|
|
30421
|
-
|
|
31272
|
+
fs44.writeFileSync(filePath, `${JSON.stringify(creds, null, 2)}
|
|
30422
31273
|
`, "utf-8");
|
|
30423
31274
|
ensureSecureFile(filePath);
|
|
30424
31275
|
}
|
|
@@ -30430,8 +31281,8 @@ function maskSecret(value) {
|
|
|
30430
31281
|
|
|
30431
31282
|
// src/runtime/open-agents/index.ts
|
|
30432
31283
|
init_home();
|
|
30433
|
-
import
|
|
30434
|
-
import
|
|
31284
|
+
import fs45 from "node:fs";
|
|
31285
|
+
import path54 from "node:path";
|
|
30435
31286
|
|
|
30436
31287
|
// src/runtime/open-agents/contract.ts
|
|
30437
31288
|
var DEFAULT_OPEN_AGENTS_CONFIG = {
|
|
@@ -30618,18 +31469,18 @@ var OpenAgentsBackendError = class extends Error {
|
|
|
30618
31469
|
|
|
30619
31470
|
// src/runtime/open-agents/index.ts
|
|
30620
31471
|
function resolveConfigPath3() {
|
|
30621
|
-
return
|
|
31472
|
+
return path54.resolve(resolvePaperclipHomeDir(), "open-agents", "config.json");
|
|
30622
31473
|
}
|
|
30623
31474
|
function readOpenAgentsConfig() {
|
|
30624
31475
|
const configPath = resolveConfigPath3();
|
|
30625
|
-
if (!
|
|
31476
|
+
if (!fs45.existsSync(configPath)) {
|
|
30626
31477
|
return {
|
|
30627
31478
|
...DEFAULT_OPEN_AGENTS_CONFIG,
|
|
30628
31479
|
apiKey: getHarnessCredential("open-agents", "apiKey")
|
|
30629
31480
|
};
|
|
30630
31481
|
}
|
|
30631
31482
|
try {
|
|
30632
|
-
const raw = JSON.parse(
|
|
31483
|
+
const raw = JSON.parse(fs45.readFileSync(configPath, "utf-8"));
|
|
30633
31484
|
const storedApiKey = getHarnessCredential("open-agents", "apiKey");
|
|
30634
31485
|
return {
|
|
30635
31486
|
backendType: validateBackendType(raw.backendType),
|
|
@@ -30650,13 +31501,13 @@ function readOpenAgentsConfig() {
|
|
|
30650
31501
|
}
|
|
30651
31502
|
function writeOpenAgentsConfig(config) {
|
|
30652
31503
|
const configPath = resolveConfigPath3();
|
|
30653
|
-
|
|
31504
|
+
fs45.mkdirSync(path54.dirname(configPath), { recursive: true });
|
|
30654
31505
|
const persisted = {
|
|
30655
31506
|
...config,
|
|
30656
31507
|
authMode: validateAuthMode(config.authMode),
|
|
30657
31508
|
apiKey: void 0
|
|
30658
31509
|
};
|
|
30659
|
-
|
|
31510
|
+
fs45.writeFileSync(configPath, `${JSON.stringify(persisted, null, 2)}
|
|
30660
31511
|
`, "utf-8");
|
|
30661
31512
|
setHarnessCredential("open-agents", "apiKey", config.apiKey);
|
|
30662
31513
|
}
|
|
@@ -30674,21 +31525,21 @@ function validateAuthMode(value) {
|
|
|
30674
31525
|
// src/commands/open-agents.ts
|
|
30675
31526
|
init_banner();
|
|
30676
31527
|
function statusColor2(status) {
|
|
30677
|
-
if (status === "running") return
|
|
30678
|
-
if (status === "completed") return
|
|
30679
|
-
if (status === "failed" || status === "cancelled") return
|
|
30680
|
-
if (status === "waiting" || status === "idle") return
|
|
30681
|
-
return
|
|
31528
|
+
if (status === "running") return pc40.green(status);
|
|
31529
|
+
if (status === "completed") return pc40.cyan(status);
|
|
31530
|
+
if (status === "failed" || status === "cancelled") return pc40.red(status);
|
|
31531
|
+
if (status === "waiting" || status === "idle") return pc40.yellow(status);
|
|
31532
|
+
return pc40.dim(status);
|
|
30682
31533
|
}
|
|
30683
31534
|
function sandboxBadge(state) {
|
|
30684
|
-
if (state === "running") return
|
|
30685
|
-
if (state === "hibernating") return
|
|
30686
|
-
if (state === "stopped") return
|
|
30687
|
-
if (state === "error") return
|
|
30688
|
-
return
|
|
31535
|
+
if (state === "running") return pc40.green("running");
|
|
31536
|
+
if (state === "hibernating") return pc40.yellow("hibernating");
|
|
31537
|
+
if (state === "stopped") return pc40.dim("stopped");
|
|
31538
|
+
if (state === "error") return pc40.red("error");
|
|
31539
|
+
return pc40.dim(state);
|
|
30689
31540
|
}
|
|
30690
31541
|
function hr9(width = 72) {
|
|
30691
|
-
return
|
|
31542
|
+
return pc40.dim("\u2500".repeat(width));
|
|
30692
31543
|
}
|
|
30693
31544
|
function stripAnsi7(str) {
|
|
30694
31545
|
return str.replace(/\x1B\[[0-9;]*m/g, "");
|
|
@@ -30696,27 +31547,27 @@ function stripAnsi7(str) {
|
|
|
30696
31547
|
function box6(lines) {
|
|
30697
31548
|
const padded = lines.map((l) => " " + l);
|
|
30698
31549
|
const width = Math.max(...padded.map((l) => stripAnsi7(l).length)) + 4;
|
|
30699
|
-
const top =
|
|
30700
|
-
const bottom =
|
|
31550
|
+
const top = pc40.dim("\u250C" + "\u2500".repeat(width) + "\u2510");
|
|
31551
|
+
const bottom = pc40.dim("\u2514" + "\u2500".repeat(width) + "\u2518");
|
|
30701
31552
|
const body = padded.map((l) => {
|
|
30702
31553
|
const pad2 = width - stripAnsi7(l).length;
|
|
30703
|
-
return
|
|
31554
|
+
return pc40.dim("\u2502") + l + " ".repeat(pad2) + pc40.dim("\u2502");
|
|
30704
31555
|
});
|
|
30705
31556
|
return [top, ...body, bottom].join("\n");
|
|
30706
31557
|
}
|
|
30707
31558
|
function printSessionCard(session) {
|
|
30708
31559
|
const lines = [
|
|
30709
|
-
`${
|
|
30710
|
-
`${
|
|
30711
|
-
`${
|
|
30712
|
-
`${
|
|
30713
|
-
`${
|
|
31560
|
+
`${pc40.bold("Session")} ${pc40.dim(session.sessionId)}`,
|
|
31561
|
+
`${pc40.dim("Status:")} ${statusColor2(session.status)}`,
|
|
31562
|
+
`${pc40.dim("Sandbox:")} ${sandboxBadge(session.sandboxState)}`,
|
|
31563
|
+
`${pc40.dim("Events:")} ${session.eventCount}`,
|
|
31564
|
+
`${pc40.dim("Created:")} ${session.createdAt}`
|
|
30714
31565
|
];
|
|
30715
|
-
if (session.repoUrl) lines.push(`${
|
|
30716
|
-
if (session.branch) lines.push(`${
|
|
31566
|
+
if (session.repoUrl) lines.push(`${pc40.dim("Repo:")} ${session.repoUrl}`);
|
|
31567
|
+
if (session.branch) lines.push(`${pc40.dim("Branch:")} ${session.branch}`);
|
|
30717
31568
|
if (session.prompt) {
|
|
30718
31569
|
const truncated = session.prompt.length > 80 ? session.prompt.slice(0, 77) + "..." : session.prompt;
|
|
30719
|
-
lines.push(`${
|
|
31570
|
+
lines.push(`${pc40.dim("Prompt:")} ${truncated}`);
|
|
30720
31571
|
}
|
|
30721
31572
|
console.log("");
|
|
30722
31573
|
console.log(box6(lines));
|
|
@@ -30743,12 +31594,12 @@ var EVENT_EMOJI = {
|
|
|
30743
31594
|
};
|
|
30744
31595
|
function printEvent(event) {
|
|
30745
31596
|
const emoji = EVENT_EMOJI[event.type] ?? "\xB7";
|
|
30746
|
-
const ts =
|
|
31597
|
+
const ts = pc40.dim(event.timestamp.split("T")[1]?.slice(0, 8) ?? "");
|
|
30747
31598
|
console.log(` ${emoji} ${ts} ${event.detail}`);
|
|
30748
31599
|
}
|
|
30749
31600
|
async function runOpenAgentsHub(opts) {
|
|
30750
31601
|
printPaperclipCliBanner();
|
|
30751
|
-
p25.intro(
|
|
31602
|
+
p25.intro(pc40.bold("Open Agents"));
|
|
30752
31603
|
while (true) {
|
|
30753
31604
|
const config = readOpenAgentsConfig();
|
|
30754
31605
|
const action = await p25.select({
|
|
@@ -30915,7 +31766,7 @@ async function runSessionListFlow(config) {
|
|
|
30915
31766
|
options: [
|
|
30916
31767
|
...sessions.map((s) => ({
|
|
30917
31768
|
value: s.sessionId,
|
|
30918
|
-
label: `${statusColor2(s.status)} ${
|
|
31769
|
+
label: `${statusColor2(s.status)} ${pc40.dim(s.sessionId.slice(0, 12))}`,
|
|
30919
31770
|
hint: s.prompt ? s.prompt.slice(0, 50) : void 0
|
|
30920
31771
|
})),
|
|
30921
31772
|
{ value: "__back", label: "\u2190 Back" }
|
|
@@ -30940,7 +31791,7 @@ async function runSessionListFlow(config) {
|
|
|
30940
31791
|
p25.note("No events recorded yet.", "Empty");
|
|
30941
31792
|
} else {
|
|
30942
31793
|
console.log("");
|
|
30943
|
-
console.log(
|
|
31794
|
+
console.log(pc40.bold("Recent Events") + pc40.dim(` (${events.length})`));
|
|
30944
31795
|
console.log(hr9());
|
|
30945
31796
|
for (const event of events.slice(-20)) {
|
|
30946
31797
|
printEvent(event);
|
|
@@ -31006,7 +31857,7 @@ async function runResumeSessionFlow(config) {
|
|
|
31006
31857
|
printSessionCard(session);
|
|
31007
31858
|
const events = await pollSessionEvents(config, session.sessionId);
|
|
31008
31859
|
if (events.length > 0) {
|
|
31009
|
-
console.log(
|
|
31860
|
+
console.log(pc40.bold("Latest Events") + pc40.dim(` (${events.length})`));
|
|
31010
31861
|
console.log(hr9());
|
|
31011
31862
|
for (const event of events.slice(-20)) {
|
|
31012
31863
|
printEvent(event);
|
|
@@ -31053,7 +31904,7 @@ Examples:
|
|
|
31053
31904
|
if (opts.json) {
|
|
31054
31905
|
console.log(JSON.stringify(updated, null, 2));
|
|
31055
31906
|
} else {
|
|
31056
|
-
console.log(
|
|
31907
|
+
console.log(pc40.green("Configuration updated."));
|
|
31057
31908
|
}
|
|
31058
31909
|
return;
|
|
31059
31910
|
}
|
|
@@ -31062,15 +31913,15 @@ Examples:
|
|
|
31062
31913
|
return;
|
|
31063
31914
|
}
|
|
31064
31915
|
console.log("");
|
|
31065
|
-
console.log(
|
|
31916
|
+
console.log(pc40.bold("Open Agents Configuration"));
|
|
31066
31917
|
console.log(hr9());
|
|
31067
|
-
console.log(` ${
|
|
31068
|
-
console.log(` ${
|
|
31069
|
-
console.log(` ${
|
|
31070
|
-
console.log(` ${
|
|
31071
|
-
console.log(` ${
|
|
31072
|
-
console.log(` ${
|
|
31073
|
-
console.log(` ${
|
|
31918
|
+
console.log(` ${pc40.dim("Backend:")} ${config.backendType}`);
|
|
31919
|
+
console.log(` ${pc40.dim("Auth Mode:")} ${config.authMode ?? "none"}`);
|
|
31920
|
+
console.log(` ${pc40.dim("Endpoint:")} ${config.endpoint}`);
|
|
31921
|
+
console.log(` ${pc40.dim("API Key:")} ${config.apiKey ? maskSecret(config.apiKey) : pc40.dim("(none)")}`);
|
|
31922
|
+
console.log(` ${pc40.dim("Repo:")} ${config.defaultRepo ?? pc40.dim("(none)")}`);
|
|
31923
|
+
console.log(` ${pc40.dim("Branch:")} ${config.defaultBranch ?? pc40.dim("(none)")}`);
|
|
31924
|
+
console.log(` ${pc40.dim("Timeout:")} ${config.timeoutMs ?? 3e4}ms`);
|
|
31074
31925
|
console.log(hr9());
|
|
31075
31926
|
console.log("");
|
|
31076
31927
|
});
|
|
@@ -31083,12 +31934,12 @@ Examples:
|
|
|
31083
31934
|
}
|
|
31084
31935
|
if (health.available) {
|
|
31085
31936
|
console.log(
|
|
31086
|
-
|
|
31937
|
+
pc40.green("\u2713") + ` Backend reachable at ${config.endpoint} (${health.latencyMs}ms)` + (health.version ? ` version: ${health.version}` : "")
|
|
31087
31938
|
);
|
|
31088
31939
|
} else {
|
|
31089
|
-
console.log(
|
|
31940
|
+
console.log(pc40.red("\u2717") + ` Backend unavailable at ${config.endpoint} (${health.latencyMs}ms)`);
|
|
31090
31941
|
if (health.error) {
|
|
31091
|
-
console.log(
|
|
31942
|
+
console.log(pc40.dim(` ${health.error}`));
|
|
31092
31943
|
}
|
|
31093
31944
|
process.exitCode = 1;
|
|
31094
31945
|
}
|
|
@@ -31102,22 +31953,22 @@ Examples:
|
|
|
31102
31953
|
return;
|
|
31103
31954
|
}
|
|
31104
31955
|
if (sessions.length === 0) {
|
|
31105
|
-
console.log(
|
|
31956
|
+
console.log(pc40.yellow("No sessions found.") + pc40.dim(" Run `growthub open-agents create` to start one."));
|
|
31106
31957
|
return;
|
|
31107
31958
|
}
|
|
31108
31959
|
console.log("");
|
|
31109
|
-
console.log(
|
|
31960
|
+
console.log(pc40.bold("Agent Sessions") + pc40.dim(` (${sessions.length})`));
|
|
31110
31961
|
console.log(hr9());
|
|
31111
31962
|
for (const session of sessions) {
|
|
31112
|
-
const truncatedPrompt = session.prompt ?
|
|
31963
|
+
const truncatedPrompt = session.prompt ? pc40.dim(session.prompt.slice(0, 50)) : "";
|
|
31113
31964
|
console.log(
|
|
31114
|
-
` ${statusColor2(session.status)} ${
|
|
31965
|
+
` ${statusColor2(session.status)} ${pc40.dim(session.sessionId.slice(0, 12))} ${sandboxBadge(session.sandboxState)} ${truncatedPrompt}`
|
|
31115
31966
|
);
|
|
31116
31967
|
}
|
|
31117
31968
|
console.log(hr9());
|
|
31118
31969
|
console.log("");
|
|
31119
31970
|
} catch (err) {
|
|
31120
|
-
console.error(
|
|
31971
|
+
console.error(pc40.red("Failed to list sessions: " + err.message));
|
|
31121
31972
|
process.exitCode = 1;
|
|
31122
31973
|
}
|
|
31123
31974
|
});
|
|
@@ -31139,7 +31990,7 @@ Examples:
|
|
|
31139
31990
|
}
|
|
31140
31991
|
printSessionCard(session);
|
|
31141
31992
|
} catch (err) {
|
|
31142
|
-
console.error(
|
|
31993
|
+
console.error(pc40.red("Failed to create session: " + err.message));
|
|
31143
31994
|
process.exitCode = 1;
|
|
31144
31995
|
}
|
|
31145
31996
|
});
|
|
@@ -31157,7 +32008,7 @@ Examples:
|
|
|
31157
32008
|
}
|
|
31158
32009
|
printSessionCard(session);
|
|
31159
32010
|
} catch (err) {
|
|
31160
|
-
console.error(
|
|
32011
|
+
console.error(pc40.red("Failed to create session: " + err.message));
|
|
31161
32012
|
process.exitCode = 1;
|
|
31162
32013
|
}
|
|
31163
32014
|
});
|
|
@@ -31172,7 +32023,7 @@ Examples:
|
|
|
31172
32023
|
printSessionCard(session);
|
|
31173
32024
|
const events = await pollSessionEvents(config, session.sessionId);
|
|
31174
32025
|
if (events.length > 0) {
|
|
31175
|
-
console.log(
|
|
32026
|
+
console.log(pc40.bold("Latest Events") + pc40.dim(` (${events.length})`));
|
|
31176
32027
|
console.log(hr9());
|
|
31177
32028
|
for (const event of events.slice(-20)) {
|
|
31178
32029
|
printEvent(event);
|
|
@@ -31181,7 +32032,7 @@ Examples:
|
|
|
31181
32032
|
console.log("");
|
|
31182
32033
|
}
|
|
31183
32034
|
} catch (err) {
|
|
31184
|
-
console.error(
|
|
32035
|
+
console.error(pc40.red("Failed to resume session: " + err.message));
|
|
31185
32036
|
process.exitCode = 1;
|
|
31186
32037
|
}
|
|
31187
32038
|
});
|
|
@@ -31196,7 +32047,7 @@ Examples:
|
|
|
31196
32047
|
printSessionCard(session);
|
|
31197
32048
|
const events = await pollSessionEvents(config, session.sessionId);
|
|
31198
32049
|
if (events.length > 0) {
|
|
31199
|
-
console.log(
|
|
32050
|
+
console.log(pc40.bold("Latest Events") + pc40.dim(` (${events.length})`));
|
|
31200
32051
|
console.log(hr9());
|
|
31201
32052
|
for (const event of events.slice(-20)) {
|
|
31202
32053
|
printEvent(event);
|
|
@@ -31205,7 +32056,7 @@ Examples:
|
|
|
31205
32056
|
console.log("");
|
|
31206
32057
|
}
|
|
31207
32058
|
} catch (err) {
|
|
31208
|
-
console.error(
|
|
32059
|
+
console.error(pc40.red("Failed to chat/resume session: " + err.message));
|
|
31209
32060
|
process.exitCode = 1;
|
|
31210
32061
|
}
|
|
31211
32062
|
});
|
|
@@ -31213,12 +32064,12 @@ Examples:
|
|
|
31213
32064
|
|
|
31214
32065
|
// src/commands/qwen-code.ts
|
|
31215
32066
|
import * as p26 from "@clack/prompts";
|
|
31216
|
-
import
|
|
32067
|
+
import pc41 from "picocolors";
|
|
31217
32068
|
|
|
31218
32069
|
// src/runtime/qwen-code/index.ts
|
|
31219
32070
|
init_home();
|
|
31220
|
-
import
|
|
31221
|
-
import
|
|
32071
|
+
import fs46 from "node:fs";
|
|
32072
|
+
import path55 from "node:path";
|
|
31222
32073
|
|
|
31223
32074
|
// src/runtime/qwen-code/contract.ts
|
|
31224
32075
|
var QWEN_CODE_APPROVAL_MODES = [
|
|
@@ -31436,19 +32287,19 @@ function buildSetupGuidance(env) {
|
|
|
31436
32287
|
|
|
31437
32288
|
// src/runtime/qwen-code/index.ts
|
|
31438
32289
|
function resolveConfigPath4() {
|
|
31439
|
-
return
|
|
32290
|
+
return path55.resolve(resolvePaperclipHomeDir(), "qwen-code", "config.json");
|
|
31440
32291
|
}
|
|
31441
32292
|
function readQwenCodeConfig() {
|
|
31442
32293
|
const configPath = resolveConfigPath4();
|
|
31443
32294
|
const storedCredentials = readHarnessCredentials("qwen-code");
|
|
31444
|
-
if (!
|
|
32295
|
+
if (!fs46.existsSync(configPath)) {
|
|
31445
32296
|
return {
|
|
31446
32297
|
...DEFAULT_QWEN_CODE_CONFIG,
|
|
31447
32298
|
env: mergeHarnessEnv(DEFAULT_QWEN_CODE_CONFIG.env, storedCredentials)
|
|
31448
32299
|
};
|
|
31449
32300
|
}
|
|
31450
32301
|
try {
|
|
31451
|
-
const raw = JSON.parse(
|
|
32302
|
+
const raw = JSON.parse(fs46.readFileSync(configPath, "utf-8"));
|
|
31452
32303
|
return {
|
|
31453
32304
|
binaryPath: typeof raw.binaryPath === "string" ? raw.binaryPath : DEFAULT_QWEN_CODE_CONFIG.binaryPath,
|
|
31454
32305
|
defaultModel: typeof raw.defaultModel === "string" ? raw.defaultModel : DEFAULT_QWEN_CODE_CONFIG.defaultModel,
|
|
@@ -31470,7 +32321,7 @@ function readQwenCodeConfig() {
|
|
|
31470
32321
|
}
|
|
31471
32322
|
function writeQwenCodeConfig(config) {
|
|
31472
32323
|
const configPath = resolveConfigPath4();
|
|
31473
|
-
|
|
32324
|
+
fs46.mkdirSync(path55.dirname(configPath), { recursive: true });
|
|
31474
32325
|
const rawEnv = typeof config.env === "object" && config.env !== null ? config.env : {};
|
|
31475
32326
|
const credentialUpdates = {};
|
|
31476
32327
|
const publicEnv = {};
|
|
@@ -31482,7 +32333,7 @@ function writeQwenCodeConfig(config) {
|
|
|
31482
32333
|
publicEnv[key] = value;
|
|
31483
32334
|
}
|
|
31484
32335
|
setHarnessCredentials("qwen-code", credentialUpdates);
|
|
31485
|
-
|
|
32336
|
+
fs46.writeFileSync(
|
|
31486
32337
|
configPath,
|
|
31487
32338
|
`${JSON.stringify({ ...config, env: publicEnv }, null, 2)}
|
|
31488
32339
|
`,
|
|
@@ -31508,7 +32359,7 @@ async function runQwenCodeHub(opts) {
|
|
|
31508
32359
|
while (true) {
|
|
31509
32360
|
const config = readQwenCodeConfig();
|
|
31510
32361
|
const health = checkHealth(config.binaryPath, config.env);
|
|
31511
|
-
const statusHint = health.status === "available" ?
|
|
32362
|
+
const statusHint = health.status === "available" ? pc41.green("ready") : health.status === "degraded" ? pc41.yellow("degraded") : pc41.red("unavailable");
|
|
31512
32363
|
const action = await p26.select({
|
|
31513
32364
|
message: `Qwen Code CLI (${statusHint})`,
|
|
31514
32365
|
options: [
|
|
@@ -31728,36 +32579,36 @@ function registerQwenCodeCommands(program2) {
|
|
|
31728
32579
|
|
|
31729
32580
|
// src/commands/t3code.ts
|
|
31730
32581
|
import * as p28 from "@clack/prompts";
|
|
31731
|
-
import
|
|
32582
|
+
import pc43 from "picocolors";
|
|
31732
32583
|
|
|
31733
32584
|
// src/runtime/t3code/index.ts
|
|
31734
32585
|
init_home();
|
|
31735
|
-
import
|
|
31736
|
-
import
|
|
32586
|
+
import fs48 from "node:fs";
|
|
32587
|
+
import path57 from "node:path";
|
|
31737
32588
|
|
|
31738
32589
|
// src/runtime/agent-harness/harness-profile.ts
|
|
31739
32590
|
init_home();
|
|
31740
|
-
import
|
|
31741
|
-
import
|
|
32591
|
+
import fs47 from "node:fs";
|
|
32592
|
+
import path56 from "node:path";
|
|
31742
32593
|
import * as p27 from "@clack/prompts";
|
|
31743
|
-
import
|
|
32594
|
+
import pc42 from "picocolors";
|
|
31744
32595
|
function resolveProfileDir(harnessId) {
|
|
31745
|
-
return
|
|
32596
|
+
return path56.resolve(resolvePaperclipHomeDir(), harnessId);
|
|
31746
32597
|
}
|
|
31747
32598
|
function resolveProfilePath(harnessId) {
|
|
31748
|
-
return
|
|
32599
|
+
return path56.resolve(resolveProfileDir(harnessId), "growthub-profile.json");
|
|
31749
32600
|
}
|
|
31750
32601
|
function ensureSecureFile2(filePath) {
|
|
31751
32602
|
try {
|
|
31752
|
-
|
|
32603
|
+
fs47.chmodSync(filePath, 384);
|
|
31753
32604
|
} catch {
|
|
31754
32605
|
}
|
|
31755
32606
|
}
|
|
31756
32607
|
function readHarnessProfile(harnessId) {
|
|
31757
32608
|
const filePath = resolveProfilePath(harnessId);
|
|
31758
|
-
if (!
|
|
32609
|
+
if (!fs47.existsSync(filePath)) return null;
|
|
31759
32610
|
try {
|
|
31760
|
-
const raw = JSON.parse(
|
|
32611
|
+
const raw = JSON.parse(fs47.readFileSync(filePath, "utf-8"));
|
|
31761
32612
|
if (typeof raw.workspaceId !== "string" || typeof raw.machineLabel !== "string") return null;
|
|
31762
32613
|
return {
|
|
31763
32614
|
profileVersion: 1,
|
|
@@ -31775,32 +32626,32 @@ function readHarnessProfile(harnessId) {
|
|
|
31775
32626
|
function writeHarnessProfile(harnessId, profile) {
|
|
31776
32627
|
const dirPath = resolveProfileDir(harnessId);
|
|
31777
32628
|
const filePath = resolveProfilePath(harnessId);
|
|
31778
|
-
|
|
31779
|
-
|
|
32629
|
+
fs47.mkdirSync(dirPath, { recursive: true });
|
|
32630
|
+
fs47.writeFileSync(filePath, `${JSON.stringify(profile, null, 2)}
|
|
31780
32631
|
`, "utf-8");
|
|
31781
32632
|
ensureSecureFile2(filePath);
|
|
31782
32633
|
}
|
|
31783
32634
|
function clearHarnessProfile(harnessId) {
|
|
31784
32635
|
const filePath = resolveProfilePath(harnessId);
|
|
31785
|
-
if (
|
|
32636
|
+
if (fs47.existsSync(filePath)) fs47.rmSync(filePath);
|
|
31786
32637
|
}
|
|
31787
32638
|
function buildProfileStatusLines(harnessId, harnessLabel, profile) {
|
|
31788
32639
|
if (!profile) {
|
|
31789
32640
|
return [
|
|
31790
|
-
`${harnessLabel} Growthub Profile: ${
|
|
32641
|
+
`${harnessLabel} Growthub Profile: ${pc42.yellow("not linked")}`,
|
|
31791
32642
|
"",
|
|
31792
32643
|
`Link this harness to a Growthub workspace:`,
|
|
31793
32644
|
` growthub ${harnessId} profile link`
|
|
31794
32645
|
];
|
|
31795
32646
|
}
|
|
31796
32647
|
return [
|
|
31797
|
-
`${harnessLabel} Growthub Profile: ${
|
|
32648
|
+
`${harnessLabel} Growthub Profile: ${pc42.green("linked")}`,
|
|
31798
32649
|
` Workspace ID : ${profile.workspaceId}`,
|
|
31799
32650
|
` Machine : ${profile.machineLabel}`,
|
|
31800
|
-
` Fork binary : ${profile.forkBinaryPath ??
|
|
31801
|
-
` Fork kit : ${profile.forkKitSlug ??
|
|
32651
|
+
` Fork binary : ${profile.forkBinaryPath ?? pc42.dim("(using default)")}`,
|
|
32652
|
+
` Fork kit : ${profile.forkKitSlug ?? pc42.dim("(none)")}`,
|
|
31802
32653
|
` Linked at : ${profile.linkedAt}`,
|
|
31803
|
-
` Last sync : ${profile.lastSyncAt ??
|
|
32654
|
+
` Last sync : ${profile.lastSyncAt ?? pc42.dim("(never synced)")}`
|
|
31804
32655
|
];
|
|
31805
32656
|
}
|
|
31806
32657
|
async function runProfileLinkFlow(harnessId, harnessLabel, existing) {
|
|
@@ -32063,19 +32914,19 @@ function writeT3GrowthubProfile(profile) {
|
|
|
32063
32914
|
writeHarnessProfile(T3_HARNESS_ID, profile);
|
|
32064
32915
|
}
|
|
32065
32916
|
function resolveConfigPath5() {
|
|
32066
|
-
return
|
|
32917
|
+
return path57.resolve(resolvePaperclipHomeDir(), "t3code", "config.json");
|
|
32067
32918
|
}
|
|
32068
32919
|
function readT3CodeConfig() {
|
|
32069
32920
|
const configPath = resolveConfigPath5();
|
|
32070
32921
|
const storedCredentials = readHarnessCredentials(T3_HARNESS_ID);
|
|
32071
|
-
if (!
|
|
32922
|
+
if (!fs48.existsSync(configPath)) {
|
|
32072
32923
|
return {
|
|
32073
32924
|
...DEFAULT_T3_CODE_CONFIG,
|
|
32074
32925
|
env: mergeHarnessEnv2(DEFAULT_T3_CODE_CONFIG.env, storedCredentials)
|
|
32075
32926
|
};
|
|
32076
32927
|
}
|
|
32077
32928
|
try {
|
|
32078
|
-
const raw = JSON.parse(
|
|
32929
|
+
const raw = JSON.parse(fs48.readFileSync(configPath, "utf-8"));
|
|
32079
32930
|
const profile = readHarnessProfile(T3_HARNESS_ID);
|
|
32080
32931
|
const resolvedBinaryPath = profile?.forkBinaryPath ?? (typeof raw.binaryPath === "string" ? raw.binaryPath : DEFAULT_T3_CODE_CONFIG.binaryPath);
|
|
32081
32932
|
return {
|
|
@@ -32098,7 +32949,7 @@ function readT3CodeConfig() {
|
|
|
32098
32949
|
}
|
|
32099
32950
|
function writeT3CodeConfig(config) {
|
|
32100
32951
|
const configPath = resolveConfigPath5();
|
|
32101
|
-
|
|
32952
|
+
fs48.mkdirSync(path57.dirname(configPath), { recursive: true });
|
|
32102
32953
|
const rawEnv = typeof config.env === "object" && config.env !== null ? config.env : {};
|
|
32103
32954
|
const credentialUpdates = {};
|
|
32104
32955
|
const publicEnv = {};
|
|
@@ -32110,7 +32961,7 @@ function writeT3CodeConfig(config) {
|
|
|
32110
32961
|
}
|
|
32111
32962
|
}
|
|
32112
32963
|
setHarnessCredentials(T3_HARNESS_ID, credentialUpdates);
|
|
32113
|
-
|
|
32964
|
+
fs48.writeFileSync(
|
|
32114
32965
|
configPath,
|
|
32115
32966
|
`${JSON.stringify({ ...config, env: publicEnv }, null, 2)}
|
|
32116
32967
|
`,
|
|
@@ -32137,8 +32988,8 @@ async function runT3CodeHub(opts) {
|
|
|
32137
32988
|
const config = readT3CodeConfig();
|
|
32138
32989
|
const health = checkHealth2(config.binaryPath, config.env);
|
|
32139
32990
|
const profile = readT3GrowthubProfile();
|
|
32140
|
-
const statusHint = health.status === "available" ?
|
|
32141
|
-
const profileHint = profile ?
|
|
32991
|
+
const statusHint = health.status === "available" ? pc43.green("ready") : health.status === "degraded" ? pc43.yellow("degraded") : pc43.red("unavailable");
|
|
32992
|
+
const profileHint = profile ? pc43.green(`linked \u2192 ${profile.workspaceId}`) : pc43.dim("not linked");
|
|
32142
32993
|
const action = await p28.select({
|
|
32143
32994
|
message: `T3 Code CLI (${statusHint})`,
|
|
32144
32995
|
options: [
|
|
@@ -32409,7 +33260,7 @@ init_github();
|
|
|
32409
33260
|
// src/commands/integrations.ts
|
|
32410
33261
|
init_bridge();
|
|
32411
33262
|
import * as p30 from "@clack/prompts";
|
|
32412
|
-
import
|
|
33263
|
+
import pc45 from "picocolors";
|
|
32413
33264
|
async function integrationsStatus(opts = {}) {
|
|
32414
33265
|
const status = await describeIntegrationBridge();
|
|
32415
33266
|
if (opts.json) {
|
|
@@ -32420,7 +33271,7 @@ async function integrationsStatus(opts = {}) {
|
|
|
32420
33271
|
p30.log.warn(status.notice ?? "Not logged into Growthub.");
|
|
32421
33272
|
return;
|
|
32422
33273
|
}
|
|
32423
|
-
p30.log.message(`Growthub: ${
|
|
33274
|
+
p30.log.message(`Growthub: ${pc45.green("connected")} as ${status.growthubLogin ?? "?"}`);
|
|
32424
33275
|
if (!status.bridgeAvailable) {
|
|
32425
33276
|
p30.log.info(status.notice ?? "Hosted integrations endpoint not available.");
|
|
32426
33277
|
return;
|
|
@@ -32430,9 +33281,9 @@ async function integrationsStatus(opts = {}) {
|
|
|
32430
33281
|
return;
|
|
32431
33282
|
}
|
|
32432
33283
|
for (const i of status.integrations) {
|
|
32433
|
-
const ready = i.ready ?
|
|
33284
|
+
const ready = i.ready ? pc45.green("ready") : pc45.yellow("reauth needed");
|
|
32434
33285
|
p30.log.message(
|
|
32435
|
-
` \u2022 ${
|
|
33286
|
+
` \u2022 ${pc45.cyan(i.provider)} ${ready} handle=${i.handle ?? "?"} scopes=[${(i.scopes ?? []).join(", ")}]`
|
|
32436
33287
|
);
|
|
32437
33288
|
}
|
|
32438
33289
|
}
|
|
@@ -32447,7 +33298,7 @@ async function integrationsList(opts = {}) {
|
|
|
32447
33298
|
return;
|
|
32448
33299
|
}
|
|
32449
33300
|
for (const i of integrations) {
|
|
32450
|
-
p30.log.message(`${
|
|
33301
|
+
p30.log.message(`${pc45.cyan(i.provider)} ${i.handle ?? ""} (ready=${i.ready})`);
|
|
32451
33302
|
}
|
|
32452
33303
|
}
|
|
32453
33304
|
async function integrationsProbe(opts) {
|
|
@@ -32470,7 +33321,7 @@ async function integrationsProbe(opts) {
|
|
|
32470
33321
|
return;
|
|
32471
33322
|
}
|
|
32472
33323
|
p30.log.success(
|
|
32473
|
-
`Resolved ${
|
|
33324
|
+
`Resolved ${pc45.cyan(opts.provider)} credential via ${cred.source} handle=${cred.handle ?? "?"} scopes=[${(cred.scopes ?? []).join(", ")}]`
|
|
32474
33325
|
);
|
|
32475
33326
|
}
|
|
32476
33327
|
function registerIntegrationsCommands(program2) {
|
|
@@ -32488,12 +33339,12 @@ function registerIntegrationsCommands(program2) {
|
|
|
32488
33339
|
|
|
32489
33340
|
// src/commands/status.ts
|
|
32490
33341
|
import * as p31 from "@clack/prompts";
|
|
32491
|
-
import
|
|
33342
|
+
import pc46 from "picocolors";
|
|
32492
33343
|
|
|
32493
33344
|
// src/status/probes.ts
|
|
32494
33345
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
32495
|
-
import
|
|
32496
|
-
import
|
|
33346
|
+
import fs49 from "node:fs";
|
|
33347
|
+
import path58 from "node:path";
|
|
32497
33348
|
var GITHUB_API = "https://api.github.com";
|
|
32498
33349
|
var NPM_REGISTRY = "https://registry.npmjs.org";
|
|
32499
33350
|
function isoNow() {
|
|
@@ -32658,7 +33509,7 @@ async function probeKitForksIndex(_timeoutMs) {
|
|
|
32658
33509
|
try {
|
|
32659
33510
|
const { resolveKitForksIndexPath: resolveKitForksIndexPath2 } = await Promise.resolve().then(() => (init_kit_forks_home(), kit_forks_home_exports));
|
|
32660
33511
|
const p36 = resolveKitForksIndexPath2();
|
|
32661
|
-
if (!
|
|
33512
|
+
if (!fs49.existsSync(p36)) {
|
|
32662
33513
|
return {
|
|
32663
33514
|
componentId: "kit-forks-index",
|
|
32664
33515
|
level: "operational",
|
|
@@ -32666,7 +33517,7 @@ async function probeKitForksIndex(_timeoutMs) {
|
|
|
32666
33517
|
lastCheckedAt: isoNow()
|
|
32667
33518
|
};
|
|
32668
33519
|
}
|
|
32669
|
-
const parsed = JSON.parse(
|
|
33520
|
+
const parsed = JSON.parse(fs49.readFileSync(p36, "utf8"));
|
|
32670
33521
|
const count = Array.isArray(parsed.entries) ? parsed.entries.length : 0;
|
|
32671
33522
|
return {
|
|
32672
33523
|
componentId: "kit-forks-index",
|
|
@@ -32735,10 +33586,10 @@ async function probeNode(_timeoutMs) {
|
|
|
32735
33586
|
};
|
|
32736
33587
|
}
|
|
32737
33588
|
async function probeReleaseBundleArtifacts(_timeoutMs) {
|
|
32738
|
-
const distPath =
|
|
32739
|
-
const installerPath =
|
|
32740
|
-
const distOk =
|
|
32741
|
-
const installerOk =
|
|
33589
|
+
const distPath = path58.resolve(process.cwd(), "cli/dist/index.js");
|
|
33590
|
+
const installerPath = path58.resolve(process.cwd(), "packages/create-growthub-local/bin/create-growthub-local.mjs");
|
|
33591
|
+
const distOk = fs49.existsSync(distPath);
|
|
33592
|
+
const installerOk = fs49.existsSync(installerPath);
|
|
32742
33593
|
const ok = distOk && installerOk;
|
|
32743
33594
|
return {
|
|
32744
33595
|
componentId: "release-bundle",
|
|
@@ -32921,25 +33772,25 @@ async function runStatuspageReport(opts = {}) {
|
|
|
32921
33772
|
function levelGlyph(level) {
|
|
32922
33773
|
switch (level) {
|
|
32923
33774
|
case "operational":
|
|
32924
|
-
return
|
|
33775
|
+
return pc46.green("\u25CF");
|
|
32925
33776
|
case "degraded":
|
|
32926
|
-
return
|
|
33777
|
+
return pc46.yellow("\u25CF");
|
|
32927
33778
|
case "outage":
|
|
32928
|
-
return
|
|
33779
|
+
return pc46.red("\u25CF");
|
|
32929
33780
|
default:
|
|
32930
|
-
return
|
|
33781
|
+
return pc46.dim("\u25CB");
|
|
32931
33782
|
}
|
|
32932
33783
|
}
|
|
32933
33784
|
function overallBanner(report) {
|
|
32934
33785
|
switch (report.overallLevel) {
|
|
32935
33786
|
case "operational":
|
|
32936
|
-
return
|
|
33787
|
+
return pc46.green("\u2713 All systems operational");
|
|
32937
33788
|
case "degraded":
|
|
32938
|
-
return
|
|
33789
|
+
return pc46.yellow("\u26A0 Degraded \u2014 non-critical issues detected");
|
|
32939
33790
|
case "outage":
|
|
32940
|
-
return
|
|
33791
|
+
return pc46.red("\u2717 Outage \u2014 at least one critical component is down");
|
|
32941
33792
|
default:
|
|
32942
|
-
return
|
|
33793
|
+
return pc46.dim("? Status indeterminate");
|
|
32943
33794
|
}
|
|
32944
33795
|
}
|
|
32945
33796
|
function renderHuman(report) {
|
|
@@ -32949,14 +33800,14 @@ function renderHuman(report) {
|
|
|
32949
33800
|
bucket.push(c);
|
|
32950
33801
|
byCategory.set(c.category, bucket);
|
|
32951
33802
|
}
|
|
32952
|
-
p31.log.message(`${overallBanner(report)} ${
|
|
33803
|
+
p31.log.message(`${overallBanner(report)} ${pc46.dim(`(${report.summary})`)}`);
|
|
32953
33804
|
for (const [category, list] of byCategory) {
|
|
32954
|
-
p31.log.message(
|
|
33805
|
+
p31.log.message(pc46.cyan(`
|
|
32955
33806
|
${category}`));
|
|
32956
33807
|
for (const c of list) {
|
|
32957
|
-
const crit = c.critical ?
|
|
32958
|
-
const lat = c.latencyMs !== void 0 ?
|
|
32959
|
-
const sa = c.superAdminOnly ?
|
|
33808
|
+
const crit = c.critical ? pc46.red("!") : pc46.dim("\xB7");
|
|
33809
|
+
const lat = c.latencyMs !== void 0 ? pc46.dim(` ${c.latencyMs}ms`) : "";
|
|
33810
|
+
const sa = c.superAdminOnly ? pc46.magenta(" [super-admin]") : "";
|
|
32960
33811
|
p31.log.message(` ${levelGlyph(c.level)} ${crit} ${c.label.padEnd(30)} ${c.summary}${lat}${sa}`);
|
|
32961
33812
|
}
|
|
32962
33813
|
}
|
|
@@ -32984,7 +33835,7 @@ function registerStatusCommands(program2) {
|
|
|
32984
33835
|
|
|
32985
33836
|
// src/commands/starter.ts
|
|
32986
33837
|
import * as p32 from "@clack/prompts";
|
|
32987
|
-
import
|
|
33838
|
+
import pc47 from "picocolors";
|
|
32988
33839
|
import { pathToFileURL as pathToFileURL3 } from "node:url";
|
|
32989
33840
|
init_init();
|
|
32990
33841
|
init_table_renderer();
|
|
@@ -32999,15 +33850,15 @@ async function runStarterInit(opts) {
|
|
|
32999
33850
|
return;
|
|
33000
33851
|
}
|
|
33001
33852
|
p32.outro(
|
|
33002
|
-
`Workspace scaffolded at ${
|
|
33853
|
+
`Workspace scaffolded at ${pc47.cyan(result.forkPath)}
|
|
33003
33854
|
kitId: ${result.kitId}
|
|
33004
|
-
forkId: ${
|
|
33855
|
+
forkId: ${pc47.cyan(result.forkId)}
|
|
33005
33856
|
baseVersion: ${result.baseVersion}
|
|
33006
33857
|
open: ${folderOpenLabel2(result.forkPath)}
|
|
33007
33858
|
policyMode: remoteSyncMode=${result.policyMode}` + (result.remote ? `
|
|
33008
|
-
remote: ${
|
|
33859
|
+
remote: ${pc47.cyan(result.remote.htmlUrl)}` : "") + `
|
|
33009
33860
|
|
|
33010
|
-
Next: ${
|
|
33861
|
+
Next: ${pc47.dim(`growthub kit fork status ${result.forkId}`)}`
|
|
33011
33862
|
);
|
|
33012
33863
|
} catch (err) {
|
|
33013
33864
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -33153,19 +34004,19 @@ function finalizeSuccess(result, jobId) {
|
|
|
33153
34004
|
printActivationNudge("import_completed");
|
|
33154
34005
|
const sourceLine = result.source.kind === "github-repo" ? `${result.source.repo.owner}/${result.source.repo.repo}` : `${result.source.skillId}@${result.source.version}`;
|
|
33155
34006
|
p32.outro(
|
|
33156
|
-
`Imported ${sourceLine} into ${
|
|
33157
|
-
jobId: ${
|
|
34007
|
+
`Imported ${sourceLine} into ${pc47.cyan(result.forkPath)}
|
|
34008
|
+
jobId: ${pc47.cyan(jobId)}
|
|
33158
34009
|
importId: ${result.importId}
|
|
33159
|
-
forkId: ${
|
|
34010
|
+
forkId: ${pc47.cyan(result.forkId)}
|
|
33160
34011
|
kitId: ${result.kitId}
|
|
33161
34012
|
sourceKind: ${result.sourceKind}
|
|
33162
34013
|
importMode: ${result.importMode}
|
|
33163
34014
|
detection: framework=${result.detection.framework} pm=${result.detection.packageManager} confidence=${result.detection.confidence}
|
|
33164
34015
|
security: ${formatSecuritySummary(result)}
|
|
33165
|
-
summary: ${
|
|
33166
|
-
manifest: ${
|
|
34016
|
+
summary: ${pc47.dim(result.summaryPath)}
|
|
34017
|
+
manifest: ${pc47.dim(result.manifestPath)}
|
|
33167
34018
|
|
|
33168
|
-
Next: ${
|
|
34019
|
+
Next: ${pc47.dim(`growthub kit fork status ${result.forkId}`)}`
|
|
33169
34020
|
);
|
|
33170
34021
|
}
|
|
33171
34022
|
function scopeLabel(scope) {
|
|
@@ -33213,7 +34064,7 @@ async function runBrowseSkills(opts) {
|
|
|
33213
34064
|
})
|
|
33214
34065
|
);
|
|
33215
34066
|
for (const entry of result.entries) {
|
|
33216
|
-
p32.log.message(`${
|
|
34067
|
+
p32.log.message(`${pc47.bold(entry.skillId)} ${pc47.dim(entry.htmlUrl)}`);
|
|
33217
34068
|
}
|
|
33218
34069
|
} catch (err) {
|
|
33219
34070
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -33292,22 +34143,22 @@ function registerStarterCommands(program2) {
|
|
|
33292
34143
|
|
|
33293
34144
|
// src/commands/skills.ts
|
|
33294
34145
|
init_catalog2();
|
|
33295
|
-
import
|
|
33296
|
-
import
|
|
33297
|
-
import
|
|
34146
|
+
import fs62 from "node:fs";
|
|
34147
|
+
import path71 from "node:path";
|
|
34148
|
+
import pc48 from "picocolors";
|
|
33298
34149
|
|
|
33299
34150
|
// src/skills/session-memory.ts
|
|
33300
34151
|
init_frontmatter();
|
|
33301
|
-
import
|
|
33302
|
-
import
|
|
34152
|
+
import fs61 from "node:fs";
|
|
34153
|
+
import path70 from "node:path";
|
|
33303
34154
|
var PROJECT_MD_RELATIVE2 = ".growthub-fork/project.md";
|
|
33304
34155
|
function resolveProjectMdPath(forkPath) {
|
|
33305
|
-
return
|
|
34156
|
+
return path70.resolve(forkPath, PROJECT_MD_RELATIVE2);
|
|
33306
34157
|
}
|
|
33307
34158
|
function readSessionMemory(forkPath) {
|
|
33308
34159
|
const projectMdPath = resolveProjectMdPath(forkPath);
|
|
33309
|
-
if (!
|
|
33310
|
-
const raw =
|
|
34160
|
+
if (!fs61.existsSync(projectMdPath)) return null;
|
|
34161
|
+
const raw = fs61.readFileSync(projectMdPath, "utf8");
|
|
33311
34162
|
const sizeBytes = Buffer.byteLength(raw, "utf8");
|
|
33312
34163
|
try {
|
|
33313
34164
|
const { frontmatter, body } = readFrontmatter(raw);
|
|
@@ -33325,9 +34176,9 @@ init_kit_forks_home();
|
|
|
33325
34176
|
init_table_renderer();
|
|
33326
34177
|
function readLocalForkHead(forkPath) {
|
|
33327
34178
|
const p36 = resolveInForkRegistrationPath(forkPath);
|
|
33328
|
-
if (!
|
|
34179
|
+
if (!fs62.existsSync(p36)) return null;
|
|
33329
34180
|
try {
|
|
33330
|
-
const parsed = JSON.parse(
|
|
34181
|
+
const parsed = JSON.parse(fs62.readFileSync(p36, "utf8"));
|
|
33331
34182
|
if (typeof parsed.forkId !== "string" || typeof parsed.kitId !== "string") return null;
|
|
33332
34183
|
return { forkId: parsed.forkId, kitId: parsed.kitId };
|
|
33333
34184
|
} catch {
|
|
@@ -33335,7 +34186,7 @@ function readLocalForkHead(forkPath) {
|
|
|
33335
34186
|
}
|
|
33336
34187
|
}
|
|
33337
34188
|
function resolveRoot(optRoot) {
|
|
33338
|
-
if (optRoot) return
|
|
34189
|
+
if (optRoot) return path71.resolve(optRoot);
|
|
33339
34190
|
return process.cwd();
|
|
33340
34191
|
}
|
|
33341
34192
|
function runList(opts) {
|
|
@@ -33357,13 +34208,13 @@ function runList(opts) {
|
|
|
33357
34208
|
return;
|
|
33358
34209
|
}
|
|
33359
34210
|
if (result.entries.length === 0) {
|
|
33360
|
-
console.log(
|
|
34211
|
+
console.log(pc48.dim(`No SKILL.md found under ${result.catalog.root}`));
|
|
33361
34212
|
return;
|
|
33362
34213
|
}
|
|
33363
34214
|
const rows = result.entries.map((e) => ({
|
|
33364
34215
|
name: e.manifest.name,
|
|
33365
34216
|
source: e.source,
|
|
33366
|
-
skillPath:
|
|
34217
|
+
skillPath: path71.relative(result.catalog.root ?? "", e.skillPath),
|
|
33367
34218
|
triggers: e.manifest.triggers?.length ?? 0,
|
|
33368
34219
|
helpers: e.manifest.helpers?.length ?? 0,
|
|
33369
34220
|
subSkills: e.manifest.subSkills?.length ?? 0,
|
|
@@ -33383,9 +34234,9 @@ function runList(opts) {
|
|
|
33383
34234
|
}));
|
|
33384
34235
|
if (result.warnings.length > 0) {
|
|
33385
34236
|
console.log("");
|
|
33386
|
-
console.log(
|
|
34237
|
+
console.log(pc48.yellow(`${result.warnings.length} warning(s):`));
|
|
33387
34238
|
for (const w of result.warnings) {
|
|
33388
|
-
console.log(
|
|
34239
|
+
console.log(pc48.yellow(` - ${path71.relative(result.catalog.root ?? "", w.skillPath)}: ${w.reason}`));
|
|
33389
34240
|
}
|
|
33390
34241
|
}
|
|
33391
34242
|
}
|
|
@@ -33412,10 +34263,10 @@ function runValidate(opts) {
|
|
|
33412
34263
|
severity: "error"
|
|
33413
34264
|
});
|
|
33414
34265
|
}
|
|
33415
|
-
const skillDir =
|
|
34266
|
+
const skillDir = path71.dirname(entry.skillPath);
|
|
33416
34267
|
for (const helper of m.helpers ?? []) {
|
|
33417
|
-
const helperPath =
|
|
33418
|
-
if (!
|
|
34268
|
+
const helperPath = path71.resolve(skillDir, helper.path);
|
|
34269
|
+
if (!fs62.existsSync(helperPath)) {
|
|
33419
34270
|
issues2.push({
|
|
33420
34271
|
skillPath: entry.skillPath,
|
|
33421
34272
|
reason: `helpers[].path missing on disk: ${helper.path}`,
|
|
@@ -33424,8 +34275,8 @@ function runValidate(opts) {
|
|
|
33424
34275
|
}
|
|
33425
34276
|
}
|
|
33426
34277
|
for (const sub of m.subSkills ?? []) {
|
|
33427
|
-
const subPath =
|
|
33428
|
-
if (!
|
|
34278
|
+
const subPath = path71.resolve(skillDir, sub.path);
|
|
34279
|
+
if (!fs62.existsSync(subPath)) {
|
|
33429
34280
|
issues2.push({
|
|
33430
34281
|
skillPath: entry.skillPath,
|
|
33431
34282
|
reason: `subSkills[].path missing on disk: ${sub.path}`,
|
|
@@ -33455,14 +34306,14 @@ function runValidate(opts) {
|
|
|
33455
34306
|
process.exitCode = issues2.filter((i) => i.severity === "error").length === 0 ? 0 : 1;
|
|
33456
34307
|
return;
|
|
33457
34308
|
}
|
|
33458
|
-
console.log(
|
|
34309
|
+
console.log(pc48.bold(`Validated ${result.entries.length} skill(s) under ${root}`));
|
|
33459
34310
|
if (issues2.length === 0) {
|
|
33460
|
-
console.log(
|
|
34311
|
+
console.log(pc48.green("OK \u2014 no issues."));
|
|
33461
34312
|
return;
|
|
33462
34313
|
}
|
|
33463
34314
|
for (const issue of issues2) {
|
|
33464
|
-
const rel =
|
|
33465
|
-
const tag = issue.severity === "error" ?
|
|
34315
|
+
const rel = path71.relative(root, issue.skillPath);
|
|
34316
|
+
const tag = issue.severity === "error" ? pc48.red("[error] ") : pc48.yellow("[warning]");
|
|
33466
34317
|
console.log(`${tag} ${rel}: ${issue.reason}`);
|
|
33467
34318
|
}
|
|
33468
34319
|
const errors = issues2.filter((i) => i.severity === "error").length;
|
|
@@ -33483,7 +34334,7 @@ function runSessionInit(opts) {
|
|
|
33483
34334
|
console.log(JSON.stringify({ status: "error", error: err }));
|
|
33484
34335
|
process.exitCode = 1;
|
|
33485
34336
|
} else {
|
|
33486
|
-
console.error(
|
|
34337
|
+
console.error(pc48.red(err));
|
|
33487
34338
|
process.exitCode = 1;
|
|
33488
34339
|
}
|
|
33489
34340
|
return;
|
|
@@ -33513,13 +34364,13 @@ function runSessionInit(opts) {
|
|
|
33513
34364
|
return;
|
|
33514
34365
|
}
|
|
33515
34366
|
if (result.written) {
|
|
33516
|
-
console.log(
|
|
34367
|
+
console.log(pc48.green(`Seeded ${path71.relative(forkPath, result.projectMdPath)}`));
|
|
33517
34368
|
} else if (!result.templatePath) {
|
|
33518
|
-
console.log(
|
|
34369
|
+
console.log(pc48.yellow(
|
|
33519
34370
|
`Kit tree does not ship templates/project.md \u2014 this kit has not been upgraded to the v1.2 primitives yet. No seed written; session memory can still be maintained manually.`
|
|
33520
34371
|
));
|
|
33521
34372
|
} else {
|
|
33522
|
-
console.log(
|
|
34373
|
+
console.log(pc48.dim(`${path71.relative(forkPath, result.projectMdPath)} already present; left untouched.`));
|
|
33523
34374
|
}
|
|
33524
34375
|
}
|
|
33525
34376
|
function runSessionShow(opts) {
|
|
@@ -33532,7 +34383,7 @@ function runSessionShow(opts) {
|
|
|
33532
34383
|
process.exitCode = 1;
|
|
33533
34384
|
return;
|
|
33534
34385
|
}
|
|
33535
|
-
console.error(
|
|
34386
|
+
console.error(pc48.yellow(err));
|
|
33536
34387
|
process.exitCode = 1;
|
|
33537
34388
|
return;
|
|
33538
34389
|
}
|
|
@@ -33549,8 +34400,8 @@ function runSessionShow(opts) {
|
|
|
33549
34400
|
));
|
|
33550
34401
|
return;
|
|
33551
34402
|
}
|
|
33552
|
-
console.log(
|
|
33553
|
-
console.log(
|
|
34403
|
+
console.log(pc48.bold(head.path));
|
|
34404
|
+
console.log(pc48.dim(`${head.sizeBytes} bytes`));
|
|
33554
34405
|
if (head.frontmatter) {
|
|
33555
34406
|
for (const [k, v] of Object.entries(head.frontmatter)) {
|
|
33556
34407
|
if (Array.isArray(v)) {
|
|
@@ -33567,7 +34418,7 @@ function runSessionShow(opts) {
|
|
|
33567
34418
|
console.log(head.body);
|
|
33568
34419
|
} else {
|
|
33569
34420
|
console.log("");
|
|
33570
|
-
console.log(
|
|
34421
|
+
console.log(pc48.dim("(pass --body to print the markdown body)"));
|
|
33571
34422
|
}
|
|
33572
34423
|
}
|
|
33573
34424
|
function registerSkillsCommands(program2) {
|
|
@@ -33582,11 +34433,11 @@ function registerSkillsCommands(program2) {
|
|
|
33582
34433
|
// src/commands/fleet.ts
|
|
33583
34434
|
init_fork_registry();
|
|
33584
34435
|
import * as p33 from "@clack/prompts";
|
|
33585
|
-
import
|
|
34436
|
+
import pc49 from "picocolors";
|
|
33586
34437
|
|
|
33587
34438
|
// src/fleet/summary.ts
|
|
33588
34439
|
init_fork_registry();
|
|
33589
|
-
import
|
|
34440
|
+
import fs63 from "node:fs";
|
|
33590
34441
|
init_fork_policy();
|
|
33591
34442
|
init_fork_trace();
|
|
33592
34443
|
function classifyHealth(drift, pendingConfirmationJobs, lastJobStatus) {
|
|
@@ -33606,7 +34457,7 @@ var REMOTE_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
|
33606
34457
|
"conflict_encountered"
|
|
33607
34458
|
]);
|
|
33608
34459
|
function buildForkSummary(reg) {
|
|
33609
|
-
if (!
|
|
34460
|
+
if (!fs63.existsSync(reg.forkPath)) {
|
|
33610
34461
|
return {
|
|
33611
34462
|
forkId: reg.forkId,
|
|
33612
34463
|
kitId: reg.kitId,
|
|
@@ -33915,17 +34766,17 @@ init_fork_policy();
|
|
|
33915
34766
|
function healthGlyph(level) {
|
|
33916
34767
|
switch (level) {
|
|
33917
34768
|
case "clean":
|
|
33918
|
-
return
|
|
34769
|
+
return pc49.green("\u25CF");
|
|
33919
34770
|
case "drift-minor":
|
|
33920
|
-
return
|
|
34771
|
+
return pc49.cyan("\u25CF");
|
|
33921
34772
|
case "drift-major":
|
|
33922
|
-
return
|
|
34773
|
+
return pc49.yellow("\u25CF");
|
|
33923
34774
|
case "awaiting-confirmation":
|
|
33924
|
-
return
|
|
34775
|
+
return pc49.magenta("\u25D0");
|
|
33925
34776
|
case "error":
|
|
33926
|
-
return
|
|
34777
|
+
return pc49.red("\u25CF");
|
|
33927
34778
|
default:
|
|
33928
|
-
return
|
|
34779
|
+
return pc49.dim("\u25CB");
|
|
33929
34780
|
}
|
|
33930
34781
|
}
|
|
33931
34782
|
function truncate4(s, n) {
|
|
@@ -33939,7 +34790,7 @@ async function fleetView(opts) {
|
|
|
33939
34790
|
return;
|
|
33940
34791
|
}
|
|
33941
34792
|
p33.log.message(
|
|
33942
|
-
`Fleet: ${
|
|
34793
|
+
`Fleet: ${pc49.cyan(String(fleet.totalForks))} fork(s) | remote=${fleet.forksWithRemote} awaiting=${fleet.forksAwaitingConfirmation} pending-approvals=${fleet.pendingApprovalCount}`
|
|
33943
34794
|
);
|
|
33944
34795
|
p33.log.message(
|
|
33945
34796
|
` Health \u2192 clean=${fleet.byHealth.clean} drift-minor=${fleet.byHealth["drift-minor"]} drift-major=${fleet.byHealth["drift-major"]} awaiting=${fleet.byHealth["awaiting-confirmation"]} error=${fleet.byHealth.error} unknown=${fleet.byHealth.unknown}`
|
|
@@ -33956,10 +34807,10 @@ function renderForkRow(f) {
|
|
|
33956
34807
|
const base = f.baseVersion.padEnd(8);
|
|
33957
34808
|
const upstream = (f.upstreamVersion ?? "?").padEnd(8);
|
|
33958
34809
|
const driftCounts = `files=${f.fileDriftCount} pkgs=${f.packageDriftCount}`;
|
|
33959
|
-
const pending = f.pendingConfirmationJobs > 0 ?
|
|
33960
|
-
const remote = f.remote ?
|
|
34810
|
+
const pending = f.pendingConfirmationJobs > 0 ? pc49.magenta(` awaits=${f.pendingConfirmationJobs}`) : "";
|
|
34811
|
+
const remote = f.remote ? pc49.dim(` ${f.remote.owner}/${f.remote.repo}`) : "";
|
|
33961
34812
|
p33.log.message(
|
|
33962
|
-
` ${healthGlyph(f.health)} ${label} ${
|
|
34813
|
+
` ${healthGlyph(f.health)} ${label} ${pc49.dim(kit)} ${base} \u2192 ${upstream} ${pc49.dim(driftCounts)}${pending}${remote}`
|
|
33963
34814
|
);
|
|
33964
34815
|
}
|
|
33965
34816
|
async function fleetDrift(opts) {
|
|
@@ -33974,7 +34825,7 @@ async function fleetDrift(opts) {
|
|
|
33974
34825
|
return;
|
|
33975
34826
|
}
|
|
33976
34827
|
p33.log.message(
|
|
33977
|
-
`Fleet drift: ${
|
|
34828
|
+
`Fleet drift: ${pc49.cyan(String(withDrift.length))} of ${fleet.totalForks} fork(s) have drift.`
|
|
33978
34829
|
);
|
|
33979
34830
|
p33.log.message(
|
|
33980
34831
|
` By severity \u2192 none=${fleet.bySeverity.none} info=${fleet.bySeverity.info} warning=${fleet.bySeverity.warning} critical=${fleet.bySeverity.critical}`
|
|
@@ -33994,7 +34845,7 @@ async function fleetDriftSummary(opts) {
|
|
|
33994
34845
|
console.log(JSON.stringify({ summary, narrative }, null, 2));
|
|
33995
34846
|
return;
|
|
33996
34847
|
}
|
|
33997
|
-
p33.log.message(
|
|
34848
|
+
p33.log.message(pc49.cyan(`Drift summary \u2014 ${reg.forkId} (${summary.fromVersion} \u2192 ${summary.toVersion})`));
|
|
33998
34849
|
for (const line of narrative) p33.log.message(` ${line}`);
|
|
33999
34850
|
const sections = [
|
|
34000
34851
|
["safe additions", summary.buckets.safeAdditions],
|
|
@@ -34007,13 +34858,13 @@ async function fleetDriftSummary(opts) {
|
|
|
34007
34858
|
];
|
|
34008
34859
|
for (const [label, items] of sections) {
|
|
34009
34860
|
if (items.length === 0) continue;
|
|
34010
|
-
p33.log.message(
|
|
34011
|
-
for (const item of items) p33.log.message(` \xB7 ${item.path} ${
|
|
34861
|
+
p33.log.message(pc49.dim(` \u2014 ${label} (${items.length}) \u2014`));
|
|
34862
|
+
for (const item of items) p33.log.message(` \xB7 ${item.path} ${pc49.dim(item.note)}`);
|
|
34012
34863
|
}
|
|
34013
34864
|
if (summary.buckets.packageAdditions.length || summary.buckets.packageUpgrades.length) {
|
|
34014
|
-
p33.log.message(
|
|
34865
|
+
p33.log.message(pc49.dim(` \u2014 dependency drift \u2014`));
|
|
34015
34866
|
for (const d of summary.buckets.packageAdditions) {
|
|
34016
|
-
p33.log.message(` + ${d.packageName}@${d.toVersion} ${
|
|
34867
|
+
p33.log.message(` + ${d.packageName}@${d.toVersion} ${pc49.dim("(added upstream)")}`);
|
|
34017
34868
|
}
|
|
34018
34869
|
for (const d of summary.buckets.packageUpgrades) {
|
|
34019
34870
|
p33.log.message(` \u2191 ${d.packageName} ${d.fromVersion ?? "?"} \u2192 ${d.toVersion}`);
|
|
@@ -34039,14 +34890,14 @@ async function fleetPolicy(opts) {
|
|
|
34039
34890
|
console.log(JSON.stringify({ count: rows.length, rows }, null, 2));
|
|
34040
34891
|
return;
|
|
34041
34892
|
}
|
|
34042
|
-
p33.log.message(
|
|
34893
|
+
p33.log.message(pc49.cyan(`Fleet policy matrix (${rows.length} fork(s))`));
|
|
34043
34894
|
for (const r of rows) {
|
|
34044
34895
|
const label = truncate4(r.label ?? r.forkId, 28).padEnd(28);
|
|
34045
34896
|
const aa = r.autoApprove.padEnd(9);
|
|
34046
34897
|
const ad = r.autoApproveDepUpdates.padEnd(9);
|
|
34047
34898
|
const rs = r.remoteSyncMode.padEnd(6);
|
|
34048
34899
|
const ut = String(r.untouchableCount).padStart(3);
|
|
34049
|
-
const remote = r.hasRemote ?
|
|
34900
|
+
const remote = r.hasRemote ? pc49.green("+") : pc49.dim("\xB7");
|
|
34050
34901
|
p33.log.message(
|
|
34051
34902
|
` ${label} autoApprove=${aa} deps=${ad} remote=${rs} untouchable=${ut} ${remote}`
|
|
34052
34903
|
);
|
|
@@ -34062,19 +34913,19 @@ async function fleetApprovals(opts) {
|
|
|
34062
34913
|
p33.log.success("Approval queue is empty.");
|
|
34063
34914
|
return;
|
|
34064
34915
|
}
|
|
34065
|
-
p33.log.message(
|
|
34916
|
+
p33.log.message(pc49.cyan(`Approval queue: ${queue.length} job(s) awaiting confirmation`));
|
|
34066
34917
|
for (const entry of queue) {
|
|
34067
34918
|
p33.log.message(
|
|
34068
|
-
` \xB7 ${
|
|
34919
|
+
` \xB7 ${pc49.cyan(entry.jobId)} fork=${entry.forkLabel ?? entry.forkId} created=${entry.createdAt.slice(0, 19)}`
|
|
34069
34920
|
);
|
|
34070
|
-
for (const
|
|
34071
|
-
p33.log.message(` ${
|
|
34921
|
+
for (const path75 of entry.pendingPaths.slice(0, 6)) {
|
|
34922
|
+
p33.log.message(` ${pc49.dim("awaits")} ${path75}`);
|
|
34072
34923
|
}
|
|
34073
34924
|
if (entry.pendingPaths.length > 6) {
|
|
34074
|
-
p33.log.message(` ${
|
|
34925
|
+
p33.log.message(` ${pc49.dim(`\u2026 +${entry.pendingPaths.length - 6} more`)}`);
|
|
34075
34926
|
}
|
|
34076
34927
|
p33.log.message(
|
|
34077
|
-
` ${
|
|
34928
|
+
` ${pc49.dim("resume:")} growthub kit fork confirm --job-id ${entry.jobId}`
|
|
34078
34929
|
);
|
|
34079
34930
|
}
|
|
34080
34931
|
}
|
|
@@ -34086,20 +34937,20 @@ async function fleetAgentPlan(opts) {
|
|
|
34086
34937
|
console.log(JSON.stringify(doc, null, 2));
|
|
34087
34938
|
return;
|
|
34088
34939
|
}
|
|
34089
|
-
p33.log.message(
|
|
34940
|
+
p33.log.message(pc49.cyan(`Agent heal plan \u2014 ${reg.forkId}`));
|
|
34090
34941
|
p33.log.message(` ${doc.summary}`);
|
|
34091
34942
|
for (const line of doc.narrative) p33.log.message(` ${line}`);
|
|
34092
34943
|
if (doc.awaitsConfirmation.length > 0) {
|
|
34093
|
-
p33.log.message(
|
|
34944
|
+
p33.log.message(pc49.magenta(` Awaiting confirmation on:`));
|
|
34094
34945
|
for (const p210 of doc.awaitsConfirmation) p33.log.message(` \xB7 ${p210}`);
|
|
34095
34946
|
p33.log.message(
|
|
34096
|
-
|
|
34947
|
+
pc49.dim(
|
|
34097
34948
|
` Next: growthub kit fork heal ${reg.forkId} (will park in awaiting_confirmation until resumed)`
|
|
34098
34949
|
)
|
|
34099
34950
|
);
|
|
34100
34951
|
} else if (doc.plan.actions.length > 0) {
|
|
34101
34952
|
p33.log.message(
|
|
34102
|
-
|
|
34953
|
+
pc49.dim(` Next: growthub kit fork heal ${reg.forkId} (${doc.plan.actions.length} safe action(s) ready)`)
|
|
34103
34954
|
);
|
|
34104
34955
|
}
|
|
34105
34956
|
}
|
|
@@ -34154,21 +35005,21 @@ var DEFAULT_MEMORY_PROVIDER_CONFIG = {
|
|
|
34154
35005
|
|
|
34155
35006
|
// src/runtime/memory/store.ts
|
|
34156
35007
|
init_home();
|
|
34157
|
-
import
|
|
34158
|
-
import
|
|
35008
|
+
import fs64 from "node:fs";
|
|
35009
|
+
import path72 from "node:path";
|
|
34159
35010
|
function toProjectSlug(project) {
|
|
34160
35011
|
return project.toLowerCase().replace(/[^a-z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "default";
|
|
34161
35012
|
}
|
|
34162
35013
|
function resolveProjectPath(project) {
|
|
34163
|
-
return
|
|
35014
|
+
return path72.resolve(resolveMemoryProjectsDir(), `${toProjectSlug(project)}.json`);
|
|
34164
35015
|
}
|
|
34165
35016
|
function loadMemoryDatabase(project) {
|
|
34166
35017
|
const filePath = resolveProjectPath(project);
|
|
34167
|
-
if (!
|
|
35018
|
+
if (!fs64.existsSync(filePath)) {
|
|
34168
35019
|
return { version: 1, project, observations: [], summaries: [], nextObservationId: 1, nextSummaryId: 1 };
|
|
34169
35020
|
}
|
|
34170
35021
|
try {
|
|
34171
|
-
const raw = JSON.parse(
|
|
35022
|
+
const raw = JSON.parse(fs64.readFileSync(filePath, "utf-8"));
|
|
34172
35023
|
return {
|
|
34173
35024
|
version: 1,
|
|
34174
35025
|
project,
|
|
@@ -34183,9 +35034,9 @@ function loadMemoryDatabase(project) {
|
|
|
34183
35034
|
}
|
|
34184
35035
|
function saveMemoryDatabase(db) {
|
|
34185
35036
|
const dir = resolveMemoryProjectsDir();
|
|
34186
|
-
|
|
35037
|
+
fs64.mkdirSync(dir, { recursive: true });
|
|
34187
35038
|
const filePath = resolveProjectPath(db.project);
|
|
34188
|
-
|
|
35039
|
+
fs64.writeFileSync(filePath, `${JSON.stringify(db, null, 2)}
|
|
34189
35040
|
`, "utf-8");
|
|
34190
35041
|
}
|
|
34191
35042
|
function addObservation(project, input) {
|
|
@@ -34279,8 +35130,8 @@ function incrementRelevanceCount(project, observationId) {
|
|
|
34279
35130
|
}
|
|
34280
35131
|
function listMemoryProjects() {
|
|
34281
35132
|
const dir = resolveMemoryProjectsDir();
|
|
34282
|
-
if (!
|
|
34283
|
-
return
|
|
35133
|
+
if (!fs64.existsSync(dir)) return [];
|
|
35134
|
+
return fs64.readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => f.replace(/\.json$/, "")).sort();
|
|
34284
35135
|
}
|
|
34285
35136
|
function getMemoryStats(project) {
|
|
34286
35137
|
const db = loadMemoryDatabase(project);
|
|
@@ -34292,15 +35143,15 @@ function getMemoryStats(project) {
|
|
|
34292
35143
|
};
|
|
34293
35144
|
}
|
|
34294
35145
|
function resolveProviderConfigPath() {
|
|
34295
|
-
return
|
|
35146
|
+
return path72.resolve(resolveMemoryDir(), "provider-config.json");
|
|
34296
35147
|
}
|
|
34297
35148
|
function readProviderConfig() {
|
|
34298
35149
|
const filePath = resolveProviderConfigPath();
|
|
34299
|
-
if (!
|
|
35150
|
+
if (!fs64.existsSync(filePath)) {
|
|
34300
35151
|
return { ...DEFAULT_MEMORY_PROVIDER_CONFIG };
|
|
34301
35152
|
}
|
|
34302
35153
|
try {
|
|
34303
|
-
const raw = JSON.parse(
|
|
35154
|
+
const raw = JSON.parse(fs64.readFileSync(filePath, "utf-8"));
|
|
34304
35155
|
return {
|
|
34305
35156
|
provider: validateProvider(raw.provider),
|
|
34306
35157
|
apiKey: typeof raw.apiKey === "string" ? raw.apiKey : void 0,
|
|
@@ -34313,9 +35164,9 @@ function readProviderConfig() {
|
|
|
34313
35164
|
}
|
|
34314
35165
|
function writeProviderConfig(config) {
|
|
34315
35166
|
const dir = resolveMemoryDir();
|
|
34316
|
-
|
|
35167
|
+
fs64.mkdirSync(dir, { recursive: true });
|
|
34317
35168
|
const filePath = resolveProviderConfigPath();
|
|
34318
|
-
|
|
35169
|
+
fs64.writeFileSync(filePath, `${JSON.stringify(config, null, 2)}
|
|
34319
35170
|
`, { mode: 384 });
|
|
34320
35171
|
}
|
|
34321
35172
|
function validateProvider(value) {
|
|
@@ -34694,14 +35545,14 @@ async function syncMemoriesToHosted(project, options) {
|
|
|
34694
35545
|
init_llm();
|
|
34695
35546
|
function resolveCliVersion() {
|
|
34696
35547
|
try {
|
|
34697
|
-
const moduleDir =
|
|
35548
|
+
const moduleDir = path74.dirname(fileURLToPath7(import.meta.url));
|
|
34698
35549
|
const candidates = [
|
|
34699
|
-
|
|
34700
|
-
|
|
35550
|
+
path74.resolve(moduleDir, "../package.json"),
|
|
35551
|
+
path74.resolve(moduleDir, "../../package.json")
|
|
34701
35552
|
];
|
|
34702
35553
|
for (const candidate of candidates) {
|
|
34703
|
-
if (!
|
|
34704
|
-
const parsed = JSON.parse(
|
|
35554
|
+
if (!fs66.existsSync(candidate)) continue;
|
|
35555
|
+
const parsed = JSON.parse(fs66.readFileSync(candidate, "utf8"));
|
|
34705
35556
|
if (parsed?.name === "@growthub/cli" && typeof parsed.version === "string") return parsed.version;
|
|
34706
35557
|
}
|
|
34707
35558
|
} catch {
|
|
@@ -34745,7 +35596,7 @@ function registerSharedCommands(target) {
|
|
|
34745
35596
|
});
|
|
34746
35597
|
target.command("allowed-hostname").description("Allow a hostname for authenticated/private mode access").argument("<host>", "Hostname to allow (for example dotta-macbook-pro)").option("-c, --config <path>", "Path to config file").option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP).action(addAllowedHostname);
|
|
34747
35598
|
target.command("run").description("Bootstrap local setup (onboard + doctor) and run Growthub").option("-c, --config <path>", "Path to config file").option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP).option("-i, --instance <id>", "Local instance id (default: default)").option("--repair", "Attempt automatic repairs during doctor", true).option("--no-repair", "Disable automatic repairs during doctor").action(runCommand);
|
|
34748
|
-
target.command("discover").description("Shared discovery entry for local app install, worker kits, and templates").option("-c, --config <path>", "Path to config file").option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP).option("--run", "Start Growthub immediately after saving config", false).action(async (opts) => {
|
|
35599
|
+
target.command("discover").description("Shared discovery entry for local app install, worker kits, and templates").option("-c, --config <path>", "Path to config file").option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP).option("--run", "Start Growthub immediately after saving config", false).option("--start <surface>", "Start in a discovery surface, e.g. create-workspace").action(async (opts) => {
|
|
34749
35600
|
await runDiscoveryHub(opts);
|
|
34750
35601
|
});
|
|
34751
35602
|
registerKitCommands(target);
|
|
@@ -34753,6 +35604,7 @@ function registerSharedCommands(target) {
|
|
|
34753
35604
|
registerCapabilityCommands(target);
|
|
34754
35605
|
registerPipelineCommands(target);
|
|
34755
35606
|
registerArtifactCommands(target);
|
|
35607
|
+
registerBridgeCommands(target);
|
|
34756
35608
|
registerWorkflowCommands(target);
|
|
34757
35609
|
registerOpenAgentsCommands(target);
|
|
34758
35610
|
registerQwenCodeCommands(target);
|
|
@@ -34928,7 +35780,7 @@ async function runMarketingContextBuilder(baseUrl, model) {
|
|
|
34928
35780
|
});
|
|
34929
35781
|
if (p35.isCancel(projectDir)) return;
|
|
34930
35782
|
const dir = String(projectDir).trim() || process.cwd();
|
|
34931
|
-
if (!
|
|
35783
|
+
if (!fs66.existsSync(dir)) {
|
|
34932
35784
|
p35.note(`Directory not found: ${dir}`, "Marketing Context Builder");
|
|
34933
35785
|
return;
|
|
34934
35786
|
}
|
|
@@ -34941,10 +35793,10 @@ async function runMarketingContextBuilder(baseUrl, model) {
|
|
|
34941
35793
|
endpoint: baseUrl.replace(/\/v1$/, "") + "/v1/chat/completions",
|
|
34942
35794
|
localModel: model
|
|
34943
35795
|
});
|
|
34944
|
-
const health = await checkBackendHealth(
|
|
35796
|
+
const health = await checkBackendHealth(config);
|
|
34945
35797
|
const input = { projectDir: dir };
|
|
34946
35798
|
let result;
|
|
34947
|
-
if (health.
|
|
35799
|
+
if (health.available) {
|
|
34948
35800
|
result = await buildMarketingContext(input, backend);
|
|
34949
35801
|
} else {
|
|
34950
35802
|
result = buildDeterministicContext(input);
|
|
@@ -34964,10 +35816,10 @@ async function runMarketingContextBuilder(baseUrl, model) {
|
|
|
34964
35816
|
p35.note("Draft was not saved. You can copy it from the output above.", "Marketing Context Builder");
|
|
34965
35817
|
return;
|
|
34966
35818
|
}
|
|
34967
|
-
const outDir =
|
|
34968
|
-
|
|
34969
|
-
const outPath =
|
|
34970
|
-
|
|
35819
|
+
const outDir = path74.resolve(dir, ".agents");
|
|
35820
|
+
fs66.mkdirSync(outDir, { recursive: true });
|
|
35821
|
+
const outPath = path74.resolve(outDir, "product-marketing-context.md");
|
|
35822
|
+
fs66.writeFileSync(outPath, result.contextMarkdown, "utf-8");
|
|
34971
35823
|
p35.note(`Saved to: ${outPath}
|
|
34972
35824
|
|
|
34973
35825
|
Review the file and replace [NEEDS INPUT] placeholders with real data.`, "Marketing Context Builder");
|
|
@@ -35176,39 +36028,39 @@ function captureSessionSummary(project, sessionId, messages) {
|
|
|
35176
36028
|
}
|
|
35177
36029
|
}
|
|
35178
36030
|
function resolveLocalThreadsDir() {
|
|
35179
|
-
return
|
|
36031
|
+
return path74.resolve(resolvePaperclipHomeDir(), "native-intelligence", "threads");
|
|
35180
36032
|
}
|
|
35181
36033
|
function loadOrCreateLocalThread() {
|
|
35182
36034
|
const dir = resolveLocalThreadsDir();
|
|
35183
|
-
|
|
35184
|
-
const activePath =
|
|
35185
|
-
if (
|
|
36035
|
+
fs66.mkdirSync(dir, { recursive: true });
|
|
36036
|
+
const activePath = path74.resolve(dir, "active-thread.json");
|
|
36037
|
+
if (fs66.existsSync(activePath)) {
|
|
35186
36038
|
try {
|
|
35187
|
-
const parsed = JSON.parse(
|
|
36039
|
+
const parsed = JSON.parse(fs66.readFileSync(activePath, "utf-8"));
|
|
35188
36040
|
const id2 = typeof parsed.id === "string" && parsed.id.length > 0 ? parsed.id : `thread-${Date.now()}`;
|
|
35189
|
-
const threadFile =
|
|
36041
|
+
const threadFile = path74.resolve(dir, `${id2}.json`);
|
|
35190
36042
|
const messages = Array.isArray(parsed.messages) ? parsed.messages : [];
|
|
35191
36043
|
return { id: id2, filePath: threadFile, messages };
|
|
35192
36044
|
} catch {
|
|
35193
36045
|
}
|
|
35194
36046
|
}
|
|
35195
36047
|
const id = `thread-${Date.now()}`;
|
|
35196
|
-
const filePath =
|
|
36048
|
+
const filePath = path74.resolve(dir, `${id}.json`);
|
|
35197
36049
|
const thread = { id, filePath, messages: [] };
|
|
35198
36050
|
saveLocalThread(thread);
|
|
35199
36051
|
return thread;
|
|
35200
36052
|
}
|
|
35201
36053
|
function saveLocalThread(thread) {
|
|
35202
36054
|
const dir = resolveLocalThreadsDir();
|
|
35203
|
-
|
|
35204
|
-
|
|
36055
|
+
fs66.mkdirSync(dir, { recursive: true });
|
|
36056
|
+
fs66.writeFileSync(
|
|
35205
36057
|
thread.filePath,
|
|
35206
36058
|
`${JSON.stringify({ id: thread.id, messages: thread.messages }, null, 2)}
|
|
35207
36059
|
`,
|
|
35208
36060
|
"utf-8"
|
|
35209
36061
|
);
|
|
35210
|
-
const activePath =
|
|
35211
|
-
|
|
36062
|
+
const activePath = path74.resolve(dir, "active-thread.json");
|
|
36063
|
+
fs66.writeFileSync(
|
|
35212
36064
|
activePath,
|
|
35213
36065
|
`${JSON.stringify({ id: thread.id, messages: thread.messages }, null, 2)}
|
|
35214
36066
|
`,
|
|
@@ -35354,7 +36206,7 @@ async function collectBindingsFromContract(contract, promptSeed) {
|
|
|
35354
36206
|
return bindings;
|
|
35355
36207
|
}
|
|
35356
36208
|
function resolveCurrentProject() {
|
|
35357
|
-
return
|
|
36209
|
+
return path74.basename(process.cwd());
|
|
35358
36210
|
}
|
|
35359
36211
|
async function runMemoryKnowledgeHub() {
|
|
35360
36212
|
const project = resolveCurrentProject();
|
|
@@ -35387,7 +36239,7 @@ async function runMemoryKnowledgeHub() {
|
|
|
35387
36239
|
},
|
|
35388
36240
|
{
|
|
35389
36241
|
value: "sync",
|
|
35390
|
-
label: syncStatus.available ? "Sync to Growthub" : "Sync to Growthub" +
|
|
36242
|
+
label: syncStatus.available ? "Sync to Growthub" : "Sync to Growthub" + pc51.dim(" (unavailable)"),
|
|
35391
36243
|
hint: syncStatus.available ? "push memories to hosted account" : syncStatus.reason
|
|
35392
36244
|
},
|
|
35393
36245
|
{ value: "__back_to_hub", label: "\u2190 Back to main menu" }
|
|
@@ -35505,34 +36357,126 @@ API key: ${result.apiKey ? "configured" : "not needed"}`,
|
|
|
35505
36357
|
}
|
|
35506
36358
|
}
|
|
35507
36359
|
}
|
|
36360
|
+
async function runCreateGovernedWorkspaceFlow(opts) {
|
|
36361
|
+
workspaceLoop: while (true) {
|
|
36362
|
+
const starterChoice = await p35.select({
|
|
36363
|
+
message: opts?.firstRun ? "What do you want to create?" : opts?.title ?? "Create Governed Workspace",
|
|
36364
|
+
options: [
|
|
36365
|
+
...opts?.importOnly ? [] : [
|
|
36366
|
+
{
|
|
36367
|
+
value: "new-greenfield",
|
|
36368
|
+
label: opts?.firstRun ? "\u{1F680} New governed workspace" : "\u{1F680} New greenfield workspace",
|
|
36369
|
+
hint: "Scaffold a fresh governed workspace"
|
|
36370
|
+
}
|
|
36371
|
+
],
|
|
36372
|
+
{
|
|
36373
|
+
value: "import-github",
|
|
36374
|
+
label: opts?.firstRun ? "\u{1F517} Import GitHub repository" : "\u{1F517} Import GitHub repository",
|
|
36375
|
+
hint: "Import a public or private repo via the Source Import Agent"
|
|
36376
|
+
},
|
|
36377
|
+
{
|
|
36378
|
+
value: "import-skill",
|
|
36379
|
+
label: opts?.firstRun ? "\u{1F9E0} Import skills.sh skill" : "\u{1F9E0} Import skills.sh skill",
|
|
36380
|
+
hint: "Discover live skills, inspect metadata, then import the selected skill"
|
|
36381
|
+
},
|
|
36382
|
+
...opts?.importOnly ? [] : [
|
|
36383
|
+
{
|
|
36384
|
+
value: "worker-kit",
|
|
36385
|
+
label: opts?.firstRun ? "\u{1F9F0} Start from worker kit" : "\u{1F9F0} Start from worker kit",
|
|
36386
|
+
hint: "Browse worker kits and materialize one locally"
|
|
36387
|
+
}
|
|
36388
|
+
],
|
|
36389
|
+
opts?.firstRun ? { value: "__full_menu", label: "\u{1F440} Open full discovery menu" } : { value: "__back", label: opts?.backLabel ?? "\u2190 Back" }
|
|
36390
|
+
],
|
|
36391
|
+
initialValue: opts?.firstRun ? "new-greenfield" : void 0
|
|
36392
|
+
});
|
|
36393
|
+
if (p35.isCancel(starterChoice)) {
|
|
36394
|
+
p35.cancel("Cancelled.");
|
|
36395
|
+
process.exit(0);
|
|
36396
|
+
}
|
|
36397
|
+
if (starterChoice === "__full_menu") return "full-menu";
|
|
36398
|
+
if (starterChoice === "__back") return "back";
|
|
36399
|
+
if (starterChoice === "new-greenfield") {
|
|
36400
|
+
const outRaw = await p35.text({
|
|
36401
|
+
message: "Destination path for the new workspace (will be created if missing):",
|
|
36402
|
+
placeholder: "./my-workspace"
|
|
36403
|
+
});
|
|
36404
|
+
if (p35.isCancel(outRaw) || !outRaw) continue workspaceLoop;
|
|
36405
|
+
const nameRaw = await p35.text({
|
|
36406
|
+
message: "Optional label (leave blank to use directory basename):",
|
|
36407
|
+
placeholder: ""
|
|
36408
|
+
});
|
|
36409
|
+
if (p35.isCancel(nameRaw)) continue workspaceLoop;
|
|
36410
|
+
await runStarterInit({ out: String(outRaw), name: nameRaw ? String(nameRaw) : void 0 });
|
|
36411
|
+
continue workspaceLoop;
|
|
36412
|
+
}
|
|
36413
|
+
if (starterChoice === "import-github") {
|
|
36414
|
+
const repoRaw = await p35.text({
|
|
36415
|
+
message: "GitHub repo (owner/repo or https URL):",
|
|
36416
|
+
placeholder: "octocat/Hello-World"
|
|
36417
|
+
});
|
|
36418
|
+
if (p35.isCancel(repoRaw) || !repoRaw) continue workspaceLoop;
|
|
36419
|
+
const {
|
|
36420
|
+
promptForInteractiveWorkspacePath: promptForInteractiveWorkspacePath2,
|
|
36421
|
+
startSourceImportFlow: startSourceImportFlow2
|
|
36422
|
+
} = await Promise.resolve().then(() => (init_source_import_discovery(), source_import_discovery_exports));
|
|
36423
|
+
const outPath = await promptForInteractiveWorkspacePath2({
|
|
36424
|
+
kind: "github-repo",
|
|
36425
|
+
repo: String(repoRaw),
|
|
36426
|
+
out: ""
|
|
36427
|
+
});
|
|
36428
|
+
if (!outPath) continue workspaceLoop;
|
|
36429
|
+
await startSourceImportFlow2({
|
|
36430
|
+
kind: "github-repo",
|
|
36431
|
+
repo: String(repoRaw),
|
|
36432
|
+
out: outPath
|
|
36433
|
+
});
|
|
36434
|
+
continue workspaceLoop;
|
|
36435
|
+
}
|
|
36436
|
+
if (starterChoice === "import-skill") {
|
|
36437
|
+
const { startSkillsSourceImportFlow: startSkillsSourceImportFlow2 } = await Promise.resolve().then(() => (init_source_import_discovery(), source_import_discovery_exports));
|
|
36438
|
+
await startSkillsSourceImportFlow2();
|
|
36439
|
+
continue workspaceLoop;
|
|
36440
|
+
}
|
|
36441
|
+
if (starterChoice === "worker-kit") {
|
|
36442
|
+
const result = await runInteractivePicker({ allowBackToHub: true });
|
|
36443
|
+
if (result === "back") continue workspaceLoop;
|
|
36444
|
+
return "done";
|
|
36445
|
+
}
|
|
36446
|
+
}
|
|
36447
|
+
}
|
|
35508
36448
|
async function runDiscoveryHub(opts) {
|
|
35509
36449
|
track("discover_opened");
|
|
35510
36450
|
printPaperclipCliBanner();
|
|
35511
36451
|
p35.intro("Growthub Local");
|
|
36452
|
+
if (opts?.start === "create-workspace") {
|
|
36453
|
+
const result = await runCreateGovernedWorkspaceFlow({ firstRun: true });
|
|
36454
|
+
if (result === "done") return;
|
|
36455
|
+
}
|
|
35512
36456
|
while (true) {
|
|
35513
36457
|
const workflowAccess = getWorkflowAccess();
|
|
35514
36458
|
const surfaceChoice = await p35.select({
|
|
35515
36459
|
message: "What do you want to do first?",
|
|
35516
36460
|
options: [
|
|
35517
36461
|
{
|
|
35518
|
-
value: "
|
|
35519
|
-
label: "\u{
|
|
35520
|
-
hint: "
|
|
36462
|
+
value: "create-workspace",
|
|
36463
|
+
label: "\u{1F680} Create Governed Workspace",
|
|
36464
|
+
hint: "Start from a repo, skills.sh skill, starter, or worker kit"
|
|
35521
36465
|
},
|
|
35522
36466
|
{
|
|
35523
|
-
value: "
|
|
35524
|
-
label: "\u{
|
|
35525
|
-
hint: "
|
|
36467
|
+
value: "kits",
|
|
36468
|
+
label: "\u{1F9F0} Browse Worker Kits",
|
|
36469
|
+
hint: "Self-contained workspace environments for agents"
|
|
35526
36470
|
},
|
|
35527
36471
|
{
|
|
35528
|
-
value: "
|
|
35529
|
-
label:
|
|
35530
|
-
hint:
|
|
36472
|
+
value: "import-source",
|
|
36473
|
+
label: "\u{1F501} Import Repo or Skill",
|
|
36474
|
+
hint: "Build a governed workspace from GitHub or skills.sh"
|
|
35531
36475
|
},
|
|
35532
36476
|
{
|
|
35533
|
-
value: "
|
|
35534
|
-
label: "\u{
|
|
35535
|
-
hint: "
|
|
36477
|
+
value: "memory-knowledge",
|
|
36478
|
+
label: "\u{1F4D6} Memory & Knowledge",
|
|
36479
|
+
hint: "persistent memory, search, multi-provider config, Growthub sync"
|
|
35536
36480
|
},
|
|
35537
36481
|
{
|
|
35538
36482
|
value: "agent-harness",
|
|
@@ -35542,12 +36486,7 @@ async function runDiscoveryHub(opts) {
|
|
|
35542
36486
|
{
|
|
35543
36487
|
value: "settings",
|
|
35544
36488
|
label: "\u2699\uFE0F Settings",
|
|
35545
|
-
hint: "GitHub, Fork Sync,
|
|
35546
|
-
},
|
|
35547
|
-
{
|
|
35548
|
-
value: "memory-knowledge",
|
|
35549
|
-
label: "\u{1F4D6} Memory & Knowledge",
|
|
35550
|
-
hint: "persistent memory, search, multi-provider config, Growthub sync"
|
|
36489
|
+
hint: "GitHub, Fork Sync, workflows, templates, local models, service status"
|
|
35551
36490
|
},
|
|
35552
36491
|
{
|
|
35553
36492
|
value: "help",
|
|
@@ -35564,10 +36503,10 @@ async function runDiscoveryHub(opts) {
|
|
|
35564
36503
|
p35.note(
|
|
35565
36504
|
[
|
|
35566
36505
|
"\u{1F916} Agent Harness: filter by type \u2014 Paperclip Local App (GTM/DX profiles), Open Agents (durable workflow orchestration), Qwen Code CLI, or T3 Code CLI (pingdotgg/t3code).",
|
|
35567
|
-
"\u{
|
|
35568
|
-
"\u{
|
|
35569
|
-
"\u{
|
|
35570
|
-
"\
|
|
36506
|
+
"\u{1F680} Create Governed Workspace: start from a repo, skills.sh skill, greenfield starter, or worker kit.",
|
|
36507
|
+
"\u{1F9F0} Browse Worker Kits: browse specialized agents and custom workspaces.",
|
|
36508
|
+
"\u{1F501} Import Repo or Skill: route directly into the Source Import Agent.",
|
|
36509
|
+
"\u2699\uFE0F Settings: GitHub, Fork Sync, workflows, templates, local models, service status, starter, fleet.",
|
|
35571
36510
|
"\u{1F4D6} Memory & Knowledge: persistent cross-session memory, search observations, multi-provider AI config, sync to Growthub.",
|
|
35572
36511
|
` Locked state: ${workflowAccess.reason}.`,
|
|
35573
36512
|
"\u{1F500} Fork Sync Agent: register, track, and heal your forked worker kits \u2014 preserves all customisations while syncing to the latest upstream version.",
|
|
@@ -35595,6 +36534,16 @@ async function runDiscoveryHub(opts) {
|
|
|
35595
36534
|
);
|
|
35596
36535
|
continue;
|
|
35597
36536
|
}
|
|
36537
|
+
if (surfaceChoice === "create-workspace") {
|
|
36538
|
+
const result = await runCreateGovernedWorkspaceFlow();
|
|
36539
|
+
if (result === "done") return;
|
|
36540
|
+
continue;
|
|
36541
|
+
}
|
|
36542
|
+
if (surfaceChoice === "import-source") {
|
|
36543
|
+
const result = await runCreateGovernedWorkspaceFlow({ importOnly: true, title: "Import Repo or Skill" });
|
|
36544
|
+
if (result === "done") return;
|
|
36545
|
+
continue;
|
|
36546
|
+
}
|
|
35598
36547
|
if (surfaceChoice === "agent-harness") {
|
|
35599
36548
|
while (true) {
|
|
35600
36549
|
const harnessType = await p35.select({
|
|
@@ -35777,8 +36726,23 @@ async function runDiscoveryHub(opts) {
|
|
|
35777
36726
|
},
|
|
35778
36727
|
{
|
|
35779
36728
|
value: "custom-workspace-starter",
|
|
35780
|
-
label: "\u{
|
|
35781
|
-
hint: "
|
|
36729
|
+
label: "\u{1F680} Create Governed Workspace",
|
|
36730
|
+
hint: "Start from a repo, skills.sh skill, starter, or worker kit"
|
|
36731
|
+
},
|
|
36732
|
+
{
|
|
36733
|
+
value: "workflows",
|
|
36734
|
+
label: workflowAccess.state === "ready" ? "\u{1F517} Workflows" : "\u{1F517} Workflows" + pc51.dim(" (locked)"),
|
|
36735
|
+
hint: workflowAccess.state === "ready" ? "CMS contracts, dynamic pipelines, and saved workflows" : workflowAccess.reason
|
|
36736
|
+
},
|
|
36737
|
+
{
|
|
36738
|
+
value: "templates",
|
|
36739
|
+
label: "\u{1F4DA} Templates",
|
|
36740
|
+
hint: "Artifact template library"
|
|
36741
|
+
},
|
|
36742
|
+
{
|
|
36743
|
+
value: "native-intelligence",
|
|
36744
|
+
label: "\u{1F9E0} Local Intelligence",
|
|
36745
|
+
hint: "Use local custom model adapters"
|
|
35782
36746
|
},
|
|
35783
36747
|
{
|
|
35784
36748
|
value: "fleet-ops",
|
|
@@ -35812,8 +36776,8 @@ async function runDiscoveryHub(opts) {
|
|
|
35812
36776
|
continue;
|
|
35813
36777
|
}
|
|
35814
36778
|
if (surfaceChoice2 === "fork-sync") {
|
|
35815
|
-
const
|
|
35816
|
-
if (
|
|
36779
|
+
const result = await runKitForkHub({ allowBackToHub: true });
|
|
36780
|
+
if (result === "back") continue;
|
|
35817
36781
|
break;
|
|
35818
36782
|
}
|
|
35819
36783
|
if (surfaceChoice2 === "service-status") {
|
|
@@ -35821,78 +36785,24 @@ async function runDiscoveryHub(opts) {
|
|
|
35821
36785
|
continue;
|
|
35822
36786
|
}
|
|
35823
36787
|
if (surfaceChoice2 === "custom-workspace-starter") {
|
|
35824
|
-
|
|
35825
|
-
const starterChoice = await p35.select({
|
|
35826
|
-
message: "Custom Workspace Starter",
|
|
35827
|
-
options: [
|
|
35828
|
-
{
|
|
35829
|
-
value: "new-greenfield",
|
|
35830
|
-
label: "\u{1F9EA} New greenfield workspace",
|
|
35831
|
-
hint: "Scaffold a fresh starter-derived fork (no source import)"
|
|
35832
|
-
},
|
|
35833
|
-
{
|
|
35834
|
-
value: "import-github",
|
|
35835
|
-
label: "\u{1F419} Build from GitHub repository",
|
|
35836
|
-
hint: "Import a public or private repo via the Source Import Agent"
|
|
35837
|
-
},
|
|
35838
|
-
{
|
|
35839
|
-
value: "import-skill",
|
|
35840
|
-
label: "\u{1F9E0} Build from skills.sh",
|
|
35841
|
-
hint: "Discover live skills, inspect metadata, then import the selected skill"
|
|
35842
|
-
},
|
|
35843
|
-
{ value: "__back", label: "\u2190 Back to Settings" }
|
|
35844
|
-
]
|
|
35845
|
-
});
|
|
35846
|
-
if (p35.isCancel(starterChoice)) {
|
|
35847
|
-
p35.cancel("Cancelled.");
|
|
35848
|
-
process.exit(0);
|
|
35849
|
-
}
|
|
35850
|
-
if (starterChoice === "__back") break starterLoop;
|
|
35851
|
-
if (starterChoice === "new-greenfield") {
|
|
35852
|
-
const outRaw = await p35.text({
|
|
35853
|
-
message: "Destination path for the new workspace (will be created if missing):",
|
|
35854
|
-
placeholder: "./my-workspace"
|
|
35855
|
-
});
|
|
35856
|
-
if (p35.isCancel(outRaw) || !outRaw) continue starterLoop;
|
|
35857
|
-
const nameRaw = await p35.text({
|
|
35858
|
-
message: "Optional label (leave blank to use directory basename):",
|
|
35859
|
-
placeholder: ""
|
|
35860
|
-
});
|
|
35861
|
-
if (p35.isCancel(nameRaw)) continue starterLoop;
|
|
35862
|
-
await runStarterInit({ out: String(outRaw), name: nameRaw ? String(nameRaw) : void 0 });
|
|
35863
|
-
continue starterLoop;
|
|
35864
|
-
}
|
|
35865
|
-
if (starterChoice === "import-github") {
|
|
35866
|
-
const repoRaw = await p35.text({
|
|
35867
|
-
message: "GitHub repo (owner/repo or https URL):",
|
|
35868
|
-
placeholder: "octocat/Hello-World"
|
|
35869
|
-
});
|
|
35870
|
-
if (p35.isCancel(repoRaw) || !repoRaw) continue starterLoop;
|
|
35871
|
-
const {
|
|
35872
|
-
promptForInteractiveWorkspacePath: promptForInteractiveWorkspacePath2,
|
|
35873
|
-
startSourceImportFlow: startSourceImportFlow2
|
|
35874
|
-
} = await Promise.resolve().then(() => (init_source_import_discovery(), source_import_discovery_exports));
|
|
35875
|
-
const outPath = await promptForInteractiveWorkspacePath2({
|
|
35876
|
-
kind: "github-repo",
|
|
35877
|
-
repo: String(repoRaw),
|
|
35878
|
-
out: ""
|
|
35879
|
-
});
|
|
35880
|
-
if (!outPath) continue starterLoop;
|
|
35881
|
-
await startSourceImportFlow2({
|
|
35882
|
-
kind: "github-repo",
|
|
35883
|
-
repo: String(repoRaw),
|
|
35884
|
-
out: outPath
|
|
35885
|
-
});
|
|
35886
|
-
continue starterLoop;
|
|
35887
|
-
}
|
|
35888
|
-
if (starterChoice === "import-skill") {
|
|
35889
|
-
const { startSkillsSourceImportFlow: startSkillsSourceImportFlow2 } = await Promise.resolve().then(() => (init_source_import_discovery(), source_import_discovery_exports));
|
|
35890
|
-
await startSkillsSourceImportFlow2();
|
|
35891
|
-
continue starterLoop;
|
|
35892
|
-
}
|
|
35893
|
-
}
|
|
36788
|
+
await runCreateGovernedWorkspaceFlow({ backLabel: "\u2190 Back to Settings" });
|
|
35894
36789
|
continue;
|
|
35895
36790
|
}
|
|
36791
|
+
if (surfaceChoice2 === "workflows") {
|
|
36792
|
+
const result = await runWorkflowPicker({ allowBackToHub: true });
|
|
36793
|
+
if (result === "back") continue;
|
|
36794
|
+
return;
|
|
36795
|
+
}
|
|
36796
|
+
if (surfaceChoice2 === "templates") {
|
|
36797
|
+
const result = await runTemplatePicker({ allowBackToHub: true });
|
|
36798
|
+
if (result === "back") continue;
|
|
36799
|
+
return;
|
|
36800
|
+
}
|
|
36801
|
+
if (surfaceChoice2 === "native-intelligence") {
|
|
36802
|
+
const result = await runNativeIntelligenceHub();
|
|
36803
|
+
if (result === "back") continue;
|
|
36804
|
+
return;
|
|
36805
|
+
}
|
|
35896
36806
|
if (surfaceChoice2 === "fleet-ops") {
|
|
35897
36807
|
await fleetView({});
|
|
35898
36808
|
continue;
|
|
@@ -35902,9 +36812,9 @@ async function runDiscoveryHub(opts) {
|
|
|
35902
36812
|
const catalog = readSkillCatalog2({ root: process.cwd() });
|
|
35903
36813
|
p35.note(
|
|
35904
36814
|
[
|
|
35905
|
-
`Root: ${
|
|
35906
|
-
`Skills discovered: ${
|
|
35907
|
-
catalog.warnings.length > 0 ? `Warnings: ${
|
|
36815
|
+
`Root: ${pc51.cyan(catalog.catalog.root ?? process.cwd())}`,
|
|
36816
|
+
`Skills discovered: ${pc51.bold(String(catalog.entries.length))}`,
|
|
36817
|
+
catalog.warnings.length > 0 ? `Warnings: ${pc51.yellow(String(catalog.warnings.length))}` : `Warnings: 0`,
|
|
35908
36818
|
"",
|
|
35909
36819
|
"Invoke directly:",
|
|
35910
36820
|
" growthub skills list --json",
|
|
@@ -35920,28 +36830,15 @@ async function runDiscoveryHub(opts) {
|
|
|
35920
36830
|
continue;
|
|
35921
36831
|
}
|
|
35922
36832
|
if (surfaceChoice === "kits") {
|
|
35923
|
-
const
|
|
35924
|
-
if (
|
|
35925
|
-
return;
|
|
35926
|
-
}
|
|
35927
|
-
if (surfaceChoice === "workflows") {
|
|
35928
|
-
const result2 = await runWorkflowPicker({ allowBackToHub: true });
|
|
35929
|
-
if (result2 === "back") continue;
|
|
35930
|
-
return;
|
|
35931
|
-
}
|
|
35932
|
-
if (surfaceChoice === "native-intelligence") {
|
|
35933
|
-
const result2 = await runNativeIntelligenceHub();
|
|
35934
|
-
if (result2 === "back") continue;
|
|
36833
|
+
const result = await runInteractivePicker({ allowBackToHub: true });
|
|
36834
|
+
if (result === "back") continue;
|
|
35935
36835
|
return;
|
|
35936
36836
|
}
|
|
35937
36837
|
if (surfaceChoice === "memory-knowledge") {
|
|
35938
|
-
const
|
|
35939
|
-
if (
|
|
36838
|
+
const result = await runMemoryKnowledgeHub();
|
|
36839
|
+
if (result === "back") continue;
|
|
35940
36840
|
return;
|
|
35941
36841
|
}
|
|
35942
|
-
const result = await runTemplatePicker({ allowBackToHub: true });
|
|
35943
|
-
if (result === "back") continue;
|
|
35944
|
-
return;
|
|
35945
36842
|
}
|
|
35946
36843
|
}
|
|
35947
36844
|
function isInstallerMode() {
|
|
@@ -35949,12 +36846,12 @@ function isInstallerMode() {
|
|
|
35949
36846
|
}
|
|
35950
36847
|
function listLocalSurfaces() {
|
|
35951
36848
|
const homeDir = resolvePaperclipHomeDir();
|
|
35952
|
-
const instancesDir =
|
|
35953
|
-
if (!
|
|
35954
|
-
return
|
|
36849
|
+
const instancesDir = path74.resolve(homeDir, "instances");
|
|
36850
|
+
if (!fs66.existsSync(instancesDir)) return [];
|
|
36851
|
+
return fs66.readdirSync(instancesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => {
|
|
35955
36852
|
const instanceId = entry.name;
|
|
35956
|
-
const configPath =
|
|
35957
|
-
if (!
|
|
36853
|
+
const configPath = path74.resolve(instancesDir, instanceId, "config.json");
|
|
36854
|
+
if (!fs66.existsSync(configPath)) return null;
|
|
35958
36855
|
try {
|
|
35959
36856
|
const config = readConfig(configPath);
|
|
35960
36857
|
if (!config) return null;
|