@growthub/cli 0.7.4 → 0.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1099 -933
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9,6 +9,188 @@ var __export = (target, all) => {
|
|
|
9
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
+
// src/config/home.ts
|
|
13
|
+
import os from "node:os";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
function resolvePaperclipHomeDir() {
|
|
16
|
+
const envHome = process.env.PAPERCLIP_HOME?.trim();
|
|
17
|
+
if (envHome) return path.resolve(expandHomePrefix(envHome));
|
|
18
|
+
return path.resolve(os.homedir(), ".paperclip");
|
|
19
|
+
}
|
|
20
|
+
function resolvePaperclipInstanceId(override) {
|
|
21
|
+
const raw = override?.trim() || process.env.PAPERCLIP_INSTANCE_ID?.trim() || DEFAULT_INSTANCE_ID;
|
|
22
|
+
if (!INSTANCE_ID_RE.test(raw)) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
`Invalid instance id '${raw}'. Allowed characters: letters, numbers, '_' and '-'.`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
return raw;
|
|
28
|
+
}
|
|
29
|
+
function resolvePaperclipInstanceRoot(instanceId) {
|
|
30
|
+
const id = resolvePaperclipInstanceId(instanceId);
|
|
31
|
+
return path.resolve(resolvePaperclipHomeDir(), "instances", id);
|
|
32
|
+
}
|
|
33
|
+
function resolveDefaultConfigPath(instanceId) {
|
|
34
|
+
return path.resolve(resolvePaperclipInstanceRoot(instanceId), "config.json");
|
|
35
|
+
}
|
|
36
|
+
function resolveDefaultContextPath() {
|
|
37
|
+
return path.resolve(resolvePaperclipHomeDir(), "context.json");
|
|
38
|
+
}
|
|
39
|
+
function resolveDefaultEmbeddedPostgresDir(instanceId) {
|
|
40
|
+
return path.resolve(resolvePaperclipInstanceRoot(instanceId), "db");
|
|
41
|
+
}
|
|
42
|
+
function resolveDefaultLogsDir(instanceId) {
|
|
43
|
+
return path.resolve(resolvePaperclipInstanceRoot(instanceId), "logs");
|
|
44
|
+
}
|
|
45
|
+
function resolveDefaultSecretsKeyFilePath(instanceId) {
|
|
46
|
+
return path.resolve(resolvePaperclipInstanceRoot(instanceId), "secrets", "master.key");
|
|
47
|
+
}
|
|
48
|
+
function resolveDefaultStorageDir(instanceId) {
|
|
49
|
+
return path.resolve(resolvePaperclipInstanceRoot(instanceId), "data", "storage");
|
|
50
|
+
}
|
|
51
|
+
function resolveDefaultBackupDir(instanceId) {
|
|
52
|
+
return path.resolve(resolvePaperclipInstanceRoot(instanceId), "data", "backups");
|
|
53
|
+
}
|
|
54
|
+
function resolveMemoryDir() {
|
|
55
|
+
return path.resolve(resolvePaperclipHomeDir(), "memory");
|
|
56
|
+
}
|
|
57
|
+
function resolveMemoryProjectsDir() {
|
|
58
|
+
return path.resolve(resolveMemoryDir(), "projects");
|
|
59
|
+
}
|
|
60
|
+
function expandHomePrefix(value) {
|
|
61
|
+
if (value === "~") return os.homedir();
|
|
62
|
+
if (value.startsWith("~/")) return path.resolve(os.homedir(), value.slice(2));
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
function describeLocalInstancePaths(instanceId) {
|
|
66
|
+
const resolvedInstanceId = resolvePaperclipInstanceId(instanceId);
|
|
67
|
+
const instanceRoot = resolvePaperclipInstanceRoot(resolvedInstanceId);
|
|
68
|
+
return {
|
|
69
|
+
homeDir: resolvePaperclipHomeDir(),
|
|
70
|
+
instanceId: resolvedInstanceId,
|
|
71
|
+
instanceRoot,
|
|
72
|
+
configPath: resolveDefaultConfigPath(resolvedInstanceId),
|
|
73
|
+
embeddedPostgresDataDir: resolveDefaultEmbeddedPostgresDir(resolvedInstanceId),
|
|
74
|
+
backupDir: resolveDefaultBackupDir(resolvedInstanceId),
|
|
75
|
+
logDir: resolveDefaultLogsDir(resolvedInstanceId),
|
|
76
|
+
secretsKeyFilePath: resolveDefaultSecretsKeyFilePath(resolvedInstanceId),
|
|
77
|
+
storageDir: resolveDefaultStorageDir(resolvedInstanceId)
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
var DEFAULT_INSTANCE_ID, INSTANCE_ID_RE;
|
|
81
|
+
var init_home = __esm({
|
|
82
|
+
"src/config/home.ts"() {
|
|
83
|
+
"use strict";
|
|
84
|
+
DEFAULT_INSTANCE_ID = "default";
|
|
85
|
+
INSTANCE_ID_RE = /^[a-zA-Z0-9_-]+$/;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// src/auth/paths.ts
|
|
90
|
+
import path2 from "node:path";
|
|
91
|
+
function resolveAuthDir() {
|
|
92
|
+
return path2.resolve(resolvePaperclipHomeDir(), "auth");
|
|
93
|
+
}
|
|
94
|
+
function resolveProfilesDir() {
|
|
95
|
+
return path2.resolve(resolvePaperclipHomeDir(), "profiles");
|
|
96
|
+
}
|
|
97
|
+
function resolveSessionPath() {
|
|
98
|
+
return path2.resolve(resolveAuthDir(), "session.json");
|
|
99
|
+
}
|
|
100
|
+
function resolveHostedOverlayPath() {
|
|
101
|
+
return path2.resolve(resolveProfilesDir(), "hosted-overlay.json");
|
|
102
|
+
}
|
|
103
|
+
function resolveEffectiveProfilePath() {
|
|
104
|
+
return path2.resolve(resolveProfilesDir(), "effective-profile.json");
|
|
105
|
+
}
|
|
106
|
+
var init_paths = __esm({
|
|
107
|
+
"src/auth/paths.ts"() {
|
|
108
|
+
"use strict";
|
|
109
|
+
init_home();
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// src/auth/session-store.ts
|
|
114
|
+
var session_store_exports = {};
|
|
115
|
+
__export(session_store_exports, {
|
|
116
|
+
clearSession: () => clearSession,
|
|
117
|
+
describeSessionPath: () => describeSessionPath,
|
|
118
|
+
isSessionExpired: () => isSessionExpired,
|
|
119
|
+
readSession: () => readSession,
|
|
120
|
+
writeSession: () => writeSession
|
|
121
|
+
});
|
|
122
|
+
import fs from "node:fs";
|
|
123
|
+
import path3 from "node:path";
|
|
124
|
+
function parseJson(filePath) {
|
|
125
|
+
try {
|
|
126
|
+
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
127
|
+
} catch (err) {
|
|
128
|
+
throw new Error(
|
|
129
|
+
`Failed to parse auth session at ${filePath}: ${err instanceof Error ? err.message : String(err)}`
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function toStringOrUndefined(value) {
|
|
134
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
135
|
+
}
|
|
136
|
+
function normalizeSession(raw) {
|
|
137
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null;
|
|
138
|
+
const record = raw;
|
|
139
|
+
const accessToken = toStringOrUndefined(record.accessToken);
|
|
140
|
+
const hostedBaseUrl = toStringOrUndefined(record.hostedBaseUrl);
|
|
141
|
+
if (!accessToken || !hostedBaseUrl) return null;
|
|
142
|
+
const issuedAt = toStringOrUndefined(record.issuedAt) ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
143
|
+
return {
|
|
144
|
+
version: 1,
|
|
145
|
+
hostedBaseUrl,
|
|
146
|
+
accessToken,
|
|
147
|
+
expiresAt: toStringOrUndefined(record.expiresAt),
|
|
148
|
+
userId: toStringOrUndefined(record.userId),
|
|
149
|
+
email: toStringOrUndefined(record.email),
|
|
150
|
+
orgId: toStringOrUndefined(record.orgId),
|
|
151
|
+
orgName: toStringOrUndefined(record.orgName),
|
|
152
|
+
machineLabel: toStringOrUndefined(record.machineLabel),
|
|
153
|
+
issuedAt
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function readSession() {
|
|
157
|
+
const filePath = resolveSessionPath();
|
|
158
|
+
if (!fs.existsSync(filePath)) return null;
|
|
159
|
+
const raw = parseJson(filePath);
|
|
160
|
+
return normalizeSession(raw);
|
|
161
|
+
}
|
|
162
|
+
function writeSession(session) {
|
|
163
|
+
const filePath = resolveSessionPath();
|
|
164
|
+
fs.mkdirSync(resolveAuthDir(), { recursive: true });
|
|
165
|
+
fs.writeFileSync(filePath, `${JSON.stringify(session, null, 2)}
|
|
166
|
+
`, { mode: 384 });
|
|
167
|
+
try {
|
|
168
|
+
fs.chmodSync(filePath, 384);
|
|
169
|
+
} catch {
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function clearSession() {
|
|
173
|
+
const filePath = resolveSessionPath();
|
|
174
|
+
if (!fs.existsSync(filePath)) return false;
|
|
175
|
+
fs.rmSync(filePath, { force: true });
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
function isSessionExpired(session, now = /* @__PURE__ */ new Date()) {
|
|
179
|
+
if (!session.expiresAt) return false;
|
|
180
|
+
const expires = Date.parse(session.expiresAt);
|
|
181
|
+
if (Number.isNaN(expires)) return false;
|
|
182
|
+
return expires <= now.getTime();
|
|
183
|
+
}
|
|
184
|
+
function describeSessionPath() {
|
|
185
|
+
return path3.resolve(resolveSessionPath());
|
|
186
|
+
}
|
|
187
|
+
var init_session_store = __esm({
|
|
188
|
+
"src/auth/session-store.ts"() {
|
|
189
|
+
"use strict";
|
|
190
|
+
init_paths();
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
12
194
|
// ../packages/shared/src/constants.ts
|
|
13
195
|
var COMPANY_STATUSES, DEPLOYMENT_MODES, DEPLOYMENT_EXPOSURES, AUTH_BASE_URL_MODES, SURFACE_PROFILES, AGENT_STATUSES, AGENT_ADAPTER_TYPES, AGENT_ROLES, AGENT_ICON_NAMES, TICKET_STAGE_KINDS, TICKET_STAGE_HANDOFF_MODES, TICKET_STATUSES, ISSUE_STATUSES, ISSUE_PRIORITIES, GOAL_LEVELS, GOAL_STATUSES, PROJECT_STATUSES, APPROVAL_TYPES, SECRET_PROVIDERS, STORAGE_PROVIDERS, BILLING_TYPES, FINANCE_EVENT_KINDS, FINANCE_DIRECTIONS, FINANCE_UNITS, BUDGET_SCOPE_TYPES, BUDGET_METRICS, BUDGET_WINDOW_KINDS, BUDGET_INCIDENT_RESOLUTION_ACTIONS, INVITE_JOIN_TYPES, JOIN_REQUEST_TYPES, JOIN_REQUEST_STATUSES, PERMISSION_KEYS, PLUGIN_STATUSES, PLUGIN_CATEGORIES, PLUGIN_CAPABILITIES, PLUGIN_UI_SLOT_TYPES, PLUGIN_RESERVED_COMPANY_ROUTE_SEGMENTS, PLUGIN_LAUNCHER_PLACEMENT_ZONES, PLUGIN_LAUNCHER_ACTIONS, PLUGIN_LAUNCHER_BOUNDS, PLUGIN_LAUNCHER_RENDER_ENVIRONMENTS, PLUGIN_UI_SLOT_ENTITY_TYPES, PLUGIN_STATE_SCOPE_KINDS;
|
|
14
196
|
var init_constants = __esm({
|
|
@@ -2084,108 +2266,31 @@ var init_schema = __esm({
|
|
|
2084
2266
|
}
|
|
2085
2267
|
});
|
|
2086
2268
|
|
|
2087
|
-
// src/config/home.ts
|
|
2088
|
-
import os from "node:os";
|
|
2089
|
-
import path from "node:path";
|
|
2090
|
-
function resolvePaperclipHomeDir() {
|
|
2091
|
-
const envHome = process.env.PAPERCLIP_HOME?.trim();
|
|
2092
|
-
if (envHome) return path.resolve(expandHomePrefix(envHome));
|
|
2093
|
-
return path.resolve(os.homedir(), ".paperclip");
|
|
2094
|
-
}
|
|
2095
|
-
function resolvePaperclipInstanceId(override) {
|
|
2096
|
-
const raw = override?.trim() || process.env.PAPERCLIP_INSTANCE_ID?.trim() || DEFAULT_INSTANCE_ID;
|
|
2097
|
-
if (!INSTANCE_ID_RE.test(raw)) {
|
|
2098
|
-
throw new Error(
|
|
2099
|
-
`Invalid instance id '${raw}'. Allowed characters: letters, numbers, '_' and '-'.`
|
|
2100
|
-
);
|
|
2101
|
-
}
|
|
2102
|
-
return raw;
|
|
2103
|
-
}
|
|
2104
|
-
function resolvePaperclipInstanceRoot(instanceId) {
|
|
2105
|
-
const id = resolvePaperclipInstanceId(instanceId);
|
|
2106
|
-
return path.resolve(resolvePaperclipHomeDir(), "instances", id);
|
|
2107
|
-
}
|
|
2108
|
-
function resolveDefaultConfigPath(instanceId) {
|
|
2109
|
-
return path.resolve(resolvePaperclipInstanceRoot(instanceId), "config.json");
|
|
2110
|
-
}
|
|
2111
|
-
function resolveDefaultContextPath() {
|
|
2112
|
-
return path.resolve(resolvePaperclipHomeDir(), "context.json");
|
|
2113
|
-
}
|
|
2114
|
-
function resolveDefaultEmbeddedPostgresDir(instanceId) {
|
|
2115
|
-
return path.resolve(resolvePaperclipInstanceRoot(instanceId), "db");
|
|
2116
|
-
}
|
|
2117
|
-
function resolveDefaultLogsDir(instanceId) {
|
|
2118
|
-
return path.resolve(resolvePaperclipInstanceRoot(instanceId), "logs");
|
|
2119
|
-
}
|
|
2120
|
-
function resolveDefaultSecretsKeyFilePath(instanceId) {
|
|
2121
|
-
return path.resolve(resolvePaperclipInstanceRoot(instanceId), "secrets", "master.key");
|
|
2122
|
-
}
|
|
2123
|
-
function resolveDefaultStorageDir(instanceId) {
|
|
2124
|
-
return path.resolve(resolvePaperclipInstanceRoot(instanceId), "data", "storage");
|
|
2125
|
-
}
|
|
2126
|
-
function resolveDefaultBackupDir(instanceId) {
|
|
2127
|
-
return path.resolve(resolvePaperclipInstanceRoot(instanceId), "data", "backups");
|
|
2128
|
-
}
|
|
2129
|
-
function resolveMemoryDir() {
|
|
2130
|
-
return path.resolve(resolvePaperclipHomeDir(), "memory");
|
|
2131
|
-
}
|
|
2132
|
-
function resolveMemoryProjectsDir() {
|
|
2133
|
-
return path.resolve(resolveMemoryDir(), "projects");
|
|
2134
|
-
}
|
|
2135
|
-
function expandHomePrefix(value) {
|
|
2136
|
-
if (value === "~") return os.homedir();
|
|
2137
|
-
if (value.startsWith("~/")) return path.resolve(os.homedir(), value.slice(2));
|
|
2138
|
-
return value;
|
|
2139
|
-
}
|
|
2140
|
-
function describeLocalInstancePaths(instanceId) {
|
|
2141
|
-
const resolvedInstanceId = resolvePaperclipInstanceId(instanceId);
|
|
2142
|
-
const instanceRoot = resolvePaperclipInstanceRoot(resolvedInstanceId);
|
|
2143
|
-
return {
|
|
2144
|
-
homeDir: resolvePaperclipHomeDir(),
|
|
2145
|
-
instanceId: resolvedInstanceId,
|
|
2146
|
-
instanceRoot,
|
|
2147
|
-
configPath: resolveDefaultConfigPath(resolvedInstanceId),
|
|
2148
|
-
embeddedPostgresDataDir: resolveDefaultEmbeddedPostgresDir(resolvedInstanceId),
|
|
2149
|
-
backupDir: resolveDefaultBackupDir(resolvedInstanceId),
|
|
2150
|
-
logDir: resolveDefaultLogsDir(resolvedInstanceId),
|
|
2151
|
-
secretsKeyFilePath: resolveDefaultSecretsKeyFilePath(resolvedInstanceId),
|
|
2152
|
-
storageDir: resolveDefaultStorageDir(resolvedInstanceId)
|
|
2153
|
-
};
|
|
2154
|
-
}
|
|
2155
|
-
var DEFAULT_INSTANCE_ID, INSTANCE_ID_RE;
|
|
2156
|
-
var init_home = __esm({
|
|
2157
|
-
"src/config/home.ts"() {
|
|
2158
|
-
"use strict";
|
|
2159
|
-
DEFAULT_INSTANCE_ID = "default";
|
|
2160
|
-
INSTANCE_ID_RE = /^[a-zA-Z0-9_-]+$/;
|
|
2161
|
-
}
|
|
2162
|
-
});
|
|
2163
|
-
|
|
2164
2269
|
// src/config/store.ts
|
|
2165
|
-
import
|
|
2166
|
-
import
|
|
2270
|
+
import fs3 from "node:fs";
|
|
2271
|
+
import path5 from "node:path";
|
|
2167
2272
|
function findConfigFileFromAncestors(startDir) {
|
|
2168
|
-
const absoluteStartDir =
|
|
2273
|
+
const absoluteStartDir = path5.resolve(startDir);
|
|
2169
2274
|
let currentDir = absoluteStartDir;
|
|
2170
2275
|
while (true) {
|
|
2171
|
-
const candidate =
|
|
2172
|
-
if (
|
|
2276
|
+
const candidate = path5.resolve(currentDir, ".paperclip", DEFAULT_CONFIG_BASENAME);
|
|
2277
|
+
if (fs3.existsSync(candidate)) {
|
|
2173
2278
|
return candidate;
|
|
2174
2279
|
}
|
|
2175
|
-
const nextDir =
|
|
2280
|
+
const nextDir = path5.resolve(currentDir, "..");
|
|
2176
2281
|
if (nextDir === currentDir) break;
|
|
2177
2282
|
currentDir = nextDir;
|
|
2178
2283
|
}
|
|
2179
2284
|
return null;
|
|
2180
2285
|
}
|
|
2181
2286
|
function resolveConfigPath(overridePath) {
|
|
2182
|
-
if (overridePath) return
|
|
2183
|
-
if (process.env.PAPERCLIP_CONFIG) return
|
|
2287
|
+
if (overridePath) return path5.resolve(overridePath);
|
|
2288
|
+
if (process.env.PAPERCLIP_CONFIG) return path5.resolve(process.env.PAPERCLIP_CONFIG);
|
|
2184
2289
|
return findConfigFileFromAncestors(process.cwd()) ?? resolveDefaultConfigPath(resolvePaperclipInstanceId());
|
|
2185
2290
|
}
|
|
2186
|
-
function
|
|
2291
|
+
function parseJson2(filePath) {
|
|
2187
2292
|
try {
|
|
2188
|
-
return JSON.parse(
|
|
2293
|
+
return JSON.parse(fs3.readFileSync(filePath, "utf-8"));
|
|
2189
2294
|
} catch (err) {
|
|
2190
2295
|
throw new Error(`Failed to parse JSON at ${filePath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
2191
2296
|
}
|
|
@@ -2224,8 +2329,8 @@ function formatValidationError(err) {
|
|
|
2224
2329
|
}
|
|
2225
2330
|
function readConfig(configPath) {
|
|
2226
2331
|
const filePath = resolveConfigPath(configPath);
|
|
2227
|
-
if (!
|
|
2228
|
-
const raw =
|
|
2332
|
+
if (!fs3.existsSync(filePath)) return null;
|
|
2333
|
+
const raw = parseJson2(filePath);
|
|
2229
2334
|
const migrated = migrateLegacyConfig(raw);
|
|
2230
2335
|
const parsed = paperclipConfigSchema.safeParse(migrated);
|
|
2231
2336
|
if (!parsed.success) {
|
|
@@ -2235,19 +2340,19 @@ function readConfig(configPath) {
|
|
|
2235
2340
|
}
|
|
2236
2341
|
function writeConfig(config, configPath) {
|
|
2237
2342
|
const filePath = resolveConfigPath(configPath);
|
|
2238
|
-
const dir =
|
|
2239
|
-
|
|
2240
|
-
if (
|
|
2343
|
+
const dir = path5.dirname(filePath);
|
|
2344
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
2345
|
+
if (fs3.existsSync(filePath)) {
|
|
2241
2346
|
const backupPath = filePath + ".backup";
|
|
2242
|
-
|
|
2243
|
-
|
|
2347
|
+
fs3.copyFileSync(filePath, backupPath);
|
|
2348
|
+
fs3.chmodSync(backupPath, 384);
|
|
2244
2349
|
}
|
|
2245
|
-
|
|
2350
|
+
fs3.writeFileSync(filePath, JSON.stringify(config, null, 2) + "\n", {
|
|
2246
2351
|
mode: 384
|
|
2247
2352
|
});
|
|
2248
2353
|
}
|
|
2249
2354
|
function configExists(configPath) {
|
|
2250
|
-
return
|
|
2355
|
+
return fs3.existsSync(resolveConfigPath(configPath));
|
|
2251
2356
|
}
|
|
2252
2357
|
var DEFAULT_CONFIG_BASENAME;
|
|
2253
2358
|
var init_store = __esm({
|
|
@@ -2260,12 +2365,12 @@ var init_store = __esm({
|
|
|
2260
2365
|
});
|
|
2261
2366
|
|
|
2262
2367
|
// src/config/env.ts
|
|
2263
|
-
import
|
|
2264
|
-
import
|
|
2368
|
+
import fs4 from "node:fs";
|
|
2369
|
+
import path6 from "node:path";
|
|
2265
2370
|
import { randomBytes } from "node:crypto";
|
|
2266
2371
|
import { config as loadDotenv, parse as parseEnvFileContents } from "dotenv";
|
|
2267
2372
|
function resolveEnvFilePath(configPath) {
|
|
2268
|
-
return
|
|
2373
|
+
return path6.resolve(path6.dirname(resolveConfigPath(configPath)), ".env");
|
|
2269
2374
|
}
|
|
2270
2375
|
function isNonEmpty(value) {
|
|
2271
2376
|
return typeof value === "string" && value.trim().length > 0;
|
|
@@ -2303,7 +2408,7 @@ function loadPaperclipEnvFile(configPath) {
|
|
|
2303
2408
|
}
|
|
2304
2409
|
function loadAgentJwtEnvFile(filePath = resolveEnvFilePath()) {
|
|
2305
2410
|
if (loadedEnvFiles.has(filePath)) return;
|
|
2306
|
-
if (!
|
|
2411
|
+
if (!fs4.existsSync(filePath)) return;
|
|
2307
2412
|
loadedEnvFiles.add(filePath);
|
|
2308
2413
|
loadDotenv({ path: filePath, override: false, quiet: true });
|
|
2309
2414
|
}
|
|
@@ -2313,8 +2418,8 @@ function readAgentJwtSecretFromEnv(configPath) {
|
|
|
2313
2418
|
return isNonEmpty(raw) ? raw.trim() : null;
|
|
2314
2419
|
}
|
|
2315
2420
|
function readAgentJwtSecretFromEnvFile(filePath = resolveEnvFilePath()) {
|
|
2316
|
-
if (!
|
|
2317
|
-
const raw =
|
|
2421
|
+
if (!fs4.existsSync(filePath)) return null;
|
|
2422
|
+
const raw = fs4.readFileSync(filePath, "utf-8");
|
|
2318
2423
|
const values = parseEnvFile(raw);
|
|
2319
2424
|
const value = values[JWT_SECRET_ENV_KEY];
|
|
2320
2425
|
return isNonEmpty(value) ? value.trim() : null;
|
|
@@ -2337,13 +2442,13 @@ function writeAgentJwtEnv(secret, filePath = resolveEnvFilePath()) {
|
|
|
2337
2442
|
mergePaperclipEnvEntries({ [JWT_SECRET_ENV_KEY]: secret }, filePath);
|
|
2338
2443
|
}
|
|
2339
2444
|
function readPaperclipEnvEntries(filePath = resolveEnvFilePath()) {
|
|
2340
|
-
if (!
|
|
2341
|
-
return parseEnvFile(
|
|
2445
|
+
if (!fs4.existsSync(filePath)) return {};
|
|
2446
|
+
return parseEnvFile(fs4.readFileSync(filePath, "utf-8"));
|
|
2342
2447
|
}
|
|
2343
2448
|
function writePaperclipEnvEntries(entries, filePath = resolveEnvFilePath()) {
|
|
2344
|
-
const dir =
|
|
2345
|
-
|
|
2346
|
-
|
|
2449
|
+
const dir = path6.dirname(filePath);
|
|
2450
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
2451
|
+
fs4.writeFileSync(filePath, renderEnvFile(entries), {
|
|
2347
2452
|
mode: 384
|
|
2348
2453
|
});
|
|
2349
2454
|
}
|
|
@@ -2369,24 +2474,24 @@ var init_env = __esm({
|
|
|
2369
2474
|
});
|
|
2370
2475
|
|
|
2371
2476
|
// src/utils/path-resolver.ts
|
|
2372
|
-
import
|
|
2373
|
-
import
|
|
2477
|
+
import fs5 from "node:fs";
|
|
2478
|
+
import path7 from "node:path";
|
|
2374
2479
|
function unique(items) {
|
|
2375
2480
|
return Array.from(new Set(items));
|
|
2376
2481
|
}
|
|
2377
2482
|
function resolveRuntimeLikePath(value, configPath) {
|
|
2378
2483
|
const expanded = expandHomePrefix(value);
|
|
2379
|
-
if (
|
|
2484
|
+
if (path7.isAbsolute(expanded)) return path7.resolve(expanded);
|
|
2380
2485
|
const cwd = process.cwd();
|
|
2381
|
-
const configDir = configPath ?
|
|
2382
|
-
const workspaceRoot = configDir ?
|
|
2486
|
+
const configDir = configPath ? path7.dirname(configPath) : null;
|
|
2487
|
+
const workspaceRoot = configDir ? path7.resolve(configDir, "..") : cwd;
|
|
2383
2488
|
const candidates = unique([
|
|
2384
|
-
...configDir ? [
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2489
|
+
...configDir ? [path7.resolve(configDir, expanded)] : [],
|
|
2490
|
+
path7.resolve(workspaceRoot, "server", expanded),
|
|
2491
|
+
path7.resolve(workspaceRoot, expanded),
|
|
2492
|
+
path7.resolve(cwd, expanded)
|
|
2388
2493
|
]);
|
|
2389
|
-
return candidates.find((candidate) =>
|
|
2494
|
+
return candidates.find((candidate) => fs5.existsSync(candidate)) ?? candidates[0];
|
|
2390
2495
|
}
|
|
2391
2496
|
var init_path_resolver = __esm({
|
|
2392
2497
|
"src/utils/path-resolver.ts"() {
|
|
@@ -2397,8 +2502,8 @@ var init_path_resolver = __esm({
|
|
|
2397
2502
|
|
|
2398
2503
|
// src/config/secrets-key.ts
|
|
2399
2504
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
2400
|
-
import
|
|
2401
|
-
import
|
|
2505
|
+
import fs6 from "node:fs";
|
|
2506
|
+
import path8 from "node:path";
|
|
2402
2507
|
function ensureLocalSecretsKeyFile(config, configPath) {
|
|
2403
2508
|
if (config.secrets.provider !== "local_encrypted") {
|
|
2404
2509
|
return { status: "skipped_provider", path: null };
|
|
@@ -2410,16 +2515,16 @@ function ensureLocalSecretsKeyFile(config, configPath) {
|
|
|
2410
2515
|
const keyFileOverride = process.env.PAPERCLIP_SECRETS_MASTER_KEY_FILE;
|
|
2411
2516
|
const configuredPath = keyFileOverride && keyFileOverride.trim().length > 0 ? keyFileOverride.trim() : config.secrets.localEncrypted.keyFilePath;
|
|
2412
2517
|
const keyFilePath = resolveRuntimeLikePath(configuredPath, configPath);
|
|
2413
|
-
if (
|
|
2518
|
+
if (fs6.existsSync(keyFilePath)) {
|
|
2414
2519
|
return { status: "existing", path: keyFilePath };
|
|
2415
2520
|
}
|
|
2416
|
-
|
|
2417
|
-
|
|
2521
|
+
fs6.mkdirSync(path8.dirname(keyFilePath), { recursive: true });
|
|
2522
|
+
fs6.writeFileSync(keyFilePath, randomBytes2(32).toString("base64"), {
|
|
2418
2523
|
encoding: "utf8",
|
|
2419
2524
|
mode: 384
|
|
2420
2525
|
});
|
|
2421
2526
|
try {
|
|
2422
|
-
|
|
2527
|
+
fs6.chmodSync(keyFilePath, 384);
|
|
2423
2528
|
} catch {
|
|
2424
2529
|
}
|
|
2425
2530
|
return { status: "created", path: keyFilePath };
|
|
@@ -2596,17 +2701,17 @@ async function promptLlm() {
|
|
|
2596
2701
|
p2.cancel("Setup cancelled.");
|
|
2597
2702
|
process.exit(0);
|
|
2598
2703
|
}
|
|
2599
|
-
const
|
|
2704
|
+
const apiKey2 = await p2.password({
|
|
2600
2705
|
message: `${provider === "claude" ? "Anthropic" : "OpenAI"} API key`,
|
|
2601
2706
|
validate: (val) => {
|
|
2602
2707
|
if (!val) return "API key is required";
|
|
2603
2708
|
}
|
|
2604
2709
|
});
|
|
2605
|
-
if (p2.isCancel(
|
|
2710
|
+
if (p2.isCancel(apiKey2)) {
|
|
2606
2711
|
p2.cancel("Setup cancelled.");
|
|
2607
2712
|
process.exit(0);
|
|
2608
2713
|
}
|
|
2609
|
-
return { provider, apiKey };
|
|
2714
|
+
return { provider, apiKey: apiKey2 };
|
|
2610
2715
|
}
|
|
2611
2716
|
async function promptExtendedProvider(context) {
|
|
2612
2717
|
const provider = await p2.select({
|
|
@@ -2630,13 +2735,13 @@ async function promptExtendedProvider(context) {
|
|
|
2630
2735
|
modelId: String(modelId2).trim() || DEFAULT_MODELS.local
|
|
2631
2736
|
};
|
|
2632
2737
|
}
|
|
2633
|
-
const
|
|
2738
|
+
const apiKey2 = await p2.password({
|
|
2634
2739
|
message: `${PROVIDER_LABELS[selectedProvider]} API key`,
|
|
2635
2740
|
validate: (val) => {
|
|
2636
2741
|
if (!val) return "API key is required for this provider";
|
|
2637
2742
|
}
|
|
2638
2743
|
});
|
|
2639
|
-
if (p2.isCancel(
|
|
2744
|
+
if (p2.isCancel(apiKey2)) return null;
|
|
2640
2745
|
const modelId = await p2.text({
|
|
2641
2746
|
message: "Model id",
|
|
2642
2747
|
placeholder: DEFAULT_MODELS[selectedProvider],
|
|
@@ -2645,7 +2750,7 @@ async function promptExtendedProvider(context) {
|
|
|
2645
2750
|
if (p2.isCancel(modelId)) return null;
|
|
2646
2751
|
return {
|
|
2647
2752
|
provider: selectedProvider,
|
|
2648
|
-
apiKey: String(
|
|
2753
|
+
apiKey: String(apiKey2),
|
|
2649
2754
|
modelId: String(modelId).trim() || DEFAULT_MODELS[selectedProvider]
|
|
2650
2755
|
};
|
|
2651
2756
|
}
|
|
@@ -6912,7 +7017,7 @@ var init_path_resolver2 = __esm({
|
|
|
6912
7017
|
});
|
|
6913
7018
|
|
|
6914
7019
|
// src/checks/database-check.ts
|
|
6915
|
-
import
|
|
7020
|
+
import fs7 from "node:fs";
|
|
6916
7021
|
async function databaseCheck(config, configPath) {
|
|
6917
7022
|
if (config.database.mode === "postgres") {
|
|
6918
7023
|
if (!config.database.connectionString) {
|
|
@@ -6946,8 +7051,8 @@ async function databaseCheck(config, configPath) {
|
|
|
6946
7051
|
if (config.database.mode === "embedded-postgres") {
|
|
6947
7052
|
const dataDir = resolveRuntimeLikePath(config.database.embeddedPostgresDataDir, configPath);
|
|
6948
7053
|
const reportedPath = dataDir;
|
|
6949
|
-
if (!
|
|
6950
|
-
|
|
7054
|
+
if (!fs7.existsSync(dataDir)) {
|
|
7055
|
+
fs7.mkdirSync(reportedPath, { recursive: true });
|
|
6951
7056
|
}
|
|
6952
7057
|
return {
|
|
6953
7058
|
name: "Database",
|
|
@@ -7055,15 +7160,15 @@ var init_llm_check = __esm({
|
|
|
7055
7160
|
});
|
|
7056
7161
|
|
|
7057
7162
|
// src/checks/log-check.ts
|
|
7058
|
-
import
|
|
7163
|
+
import fs8 from "node:fs";
|
|
7059
7164
|
function logCheck(config, configPath) {
|
|
7060
7165
|
const logDir = resolveRuntimeLikePath(config.logging.logDir, configPath);
|
|
7061
7166
|
const reportedDir = logDir;
|
|
7062
|
-
if (!
|
|
7063
|
-
|
|
7167
|
+
if (!fs8.existsSync(logDir)) {
|
|
7168
|
+
fs8.mkdirSync(reportedDir, { recursive: true });
|
|
7064
7169
|
}
|
|
7065
7170
|
try {
|
|
7066
|
-
|
|
7171
|
+
fs8.accessSync(reportedDir, fs8.constants.W_OK);
|
|
7067
7172
|
return {
|
|
7068
7173
|
name: "Log directory",
|
|
7069
7174
|
status: "pass",
|
|
@@ -7138,8 +7243,8 @@ var init_port_check = __esm({
|
|
|
7138
7243
|
|
|
7139
7244
|
// src/checks/secrets-check.ts
|
|
7140
7245
|
import { randomBytes as randomBytes4 } from "node:crypto";
|
|
7141
|
-
import
|
|
7142
|
-
import
|
|
7246
|
+
import fs9 from "node:fs";
|
|
7247
|
+
import path9 from "node:path";
|
|
7143
7248
|
function decodeMasterKey(raw) {
|
|
7144
7249
|
const trimmed = raw.trim();
|
|
7145
7250
|
if (!trimmed) return null;
|
|
@@ -7201,7 +7306,7 @@ function secretsCheck(config, configPath) {
|
|
|
7201
7306
|
const keyFileOverride = process.env.PAPERCLIP_SECRETS_MASTER_KEY_FILE;
|
|
7202
7307
|
const configuredPath = keyFileOverride && keyFileOverride.trim().length > 0 ? keyFileOverride.trim() : config.secrets.localEncrypted.keyFilePath;
|
|
7203
7308
|
const keyFilePath = resolveRuntimeLikePath(configuredPath, configPath);
|
|
7204
|
-
if (!
|
|
7309
|
+
if (!fs9.existsSync(keyFilePath)) {
|
|
7205
7310
|
return withStrictModeNote(
|
|
7206
7311
|
{
|
|
7207
7312
|
name: "Secrets adapter",
|
|
@@ -7209,13 +7314,13 @@ function secretsCheck(config, configPath) {
|
|
|
7209
7314
|
message: `Secrets key file does not exist yet: ${keyFilePath}`,
|
|
7210
7315
|
canRepair: true,
|
|
7211
7316
|
repair: () => {
|
|
7212
|
-
|
|
7213
|
-
|
|
7317
|
+
fs9.mkdirSync(path9.dirname(keyFilePath), { recursive: true });
|
|
7318
|
+
fs9.writeFileSync(keyFilePath, randomBytes4(32).toString("base64"), {
|
|
7214
7319
|
encoding: "utf8",
|
|
7215
7320
|
mode: 384
|
|
7216
7321
|
});
|
|
7217
7322
|
try {
|
|
7218
|
-
|
|
7323
|
+
fs9.chmodSync(keyFilePath, 384);
|
|
7219
7324
|
} catch {
|
|
7220
7325
|
}
|
|
7221
7326
|
},
|
|
@@ -7226,7 +7331,7 @@ function secretsCheck(config, configPath) {
|
|
|
7226
7331
|
}
|
|
7227
7332
|
let raw;
|
|
7228
7333
|
try {
|
|
7229
|
-
raw =
|
|
7334
|
+
raw = fs9.readFileSync(keyFilePath, "utf8");
|
|
7230
7335
|
} catch (err) {
|
|
7231
7336
|
return {
|
|
7232
7337
|
name: "Secrets adapter",
|
|
@@ -7262,15 +7367,15 @@ var init_secrets_check = __esm({
|
|
|
7262
7367
|
});
|
|
7263
7368
|
|
|
7264
7369
|
// src/checks/storage-check.ts
|
|
7265
|
-
import
|
|
7370
|
+
import fs10 from "node:fs";
|
|
7266
7371
|
function storageCheck(config, configPath) {
|
|
7267
7372
|
if (config.storage.provider === "local_disk") {
|
|
7268
7373
|
const baseDir = resolveRuntimeLikePath(config.storage.localDisk.baseDir, configPath);
|
|
7269
|
-
if (!
|
|
7270
|
-
|
|
7374
|
+
if (!fs10.existsSync(baseDir)) {
|
|
7375
|
+
fs10.mkdirSync(baseDir, { recursive: true });
|
|
7271
7376
|
}
|
|
7272
7377
|
try {
|
|
7273
|
-
|
|
7378
|
+
fs10.accessSync(baseDir, fs10.constants.W_OK);
|
|
7274
7379
|
return {
|
|
7275
7380
|
name: "Storage",
|
|
7276
7381
|
status: "pass",
|
|
@@ -7483,8 +7588,8 @@ var run_exports = {};
|
|
|
7483
7588
|
__export(run_exports, {
|
|
7484
7589
|
runCommand: () => runCommand
|
|
7485
7590
|
});
|
|
7486
|
-
import
|
|
7487
|
-
import
|
|
7591
|
+
import fs11 from "node:fs";
|
|
7592
|
+
import path10 from "node:path";
|
|
7488
7593
|
import { fileURLToPath as fileURLToPath2, pathToFileURL } from "node:url";
|
|
7489
7594
|
import * as p9 from "@clack/prompts";
|
|
7490
7595
|
import pc4 from "picocolors";
|
|
@@ -7492,9 +7597,9 @@ async function runCommand(opts) {
|
|
|
7492
7597
|
const instanceId = resolvePaperclipInstanceId(opts.instance);
|
|
7493
7598
|
process.env.PAPERCLIP_INSTANCE_ID = instanceId;
|
|
7494
7599
|
const homeDir = resolvePaperclipHomeDir();
|
|
7495
|
-
|
|
7600
|
+
fs11.mkdirSync(homeDir, { recursive: true });
|
|
7496
7601
|
const paths = describeLocalInstancePaths(instanceId);
|
|
7497
|
-
|
|
7602
|
+
fs11.mkdirSync(paths.instanceRoot, { recursive: true });
|
|
7498
7603
|
const configPath = resolveConfigPath(opts.config);
|
|
7499
7604
|
process.env.PAPERCLIP_CONFIG = configPath;
|
|
7500
7605
|
loadPaperclipEnvFile(configPath);
|
|
@@ -7578,18 +7683,18 @@ function maybeEnableUiDevMiddleware(entrypoint) {
|
|
|
7578
7683
|
}
|
|
7579
7684
|
}
|
|
7580
7685
|
async function importServerEntry() {
|
|
7581
|
-
const projectRoot =
|
|
7582
|
-
const devEntry =
|
|
7583
|
-
if (
|
|
7686
|
+
const projectRoot = path10.resolve(path10.dirname(fileURLToPath2(import.meta.url)), "../../..");
|
|
7687
|
+
const devEntry = path10.resolve(projectRoot, "server/src/index.ts");
|
|
7688
|
+
if (fs11.existsSync(devEntry)) {
|
|
7584
7689
|
maybeEnableUiDevMiddleware(devEntry);
|
|
7585
7690
|
const mod = await import(pathToFileURL(devEntry).href);
|
|
7586
7691
|
return await startServerFromModule(mod, devEntry);
|
|
7587
7692
|
}
|
|
7588
|
-
const bundledEntry =
|
|
7589
|
-
|
|
7693
|
+
const bundledEntry = path10.resolve(
|
|
7694
|
+
path10.dirname(fileURLToPath2(import.meta.url)),
|
|
7590
7695
|
"./runtime/server/dist/index.js"
|
|
7591
7696
|
);
|
|
7592
|
-
if (
|
|
7697
|
+
if (fs11.existsSync(bundledEntry)) {
|
|
7593
7698
|
const mod = await import(pathToFileURL(bundledEntry).href);
|
|
7594
7699
|
return await startServerFromModule(mod, bundledEntry);
|
|
7595
7700
|
}
|
|
@@ -7637,7 +7742,7 @@ var init_run = __esm({
|
|
|
7637
7742
|
|
|
7638
7743
|
// src/commands/onboard.ts
|
|
7639
7744
|
import * as p10 from "@clack/prompts";
|
|
7640
|
-
import
|
|
7745
|
+
import path11 from "node:path";
|
|
7641
7746
|
import pc5 from "picocolors";
|
|
7642
7747
|
function parseBooleanFromEnv(rawValue) {
|
|
7643
7748
|
if (rawValue === void 0) return null;
|
|
@@ -7658,7 +7763,7 @@ function parseEnumFromEnv(rawValue, allowedValues) {
|
|
|
7658
7763
|
}
|
|
7659
7764
|
function resolvePathFromEnv(rawValue) {
|
|
7660
7765
|
if (!rawValue || rawValue.trim().length === 0) return null;
|
|
7661
|
-
return
|
|
7766
|
+
return path11.resolve(expandHomePrefix(rawValue.trim()));
|
|
7662
7767
|
}
|
|
7663
7768
|
function quickstartDefaultsFromEnv() {
|
|
7664
7769
|
const instanceId = resolvePaperclipInstanceId();
|
|
@@ -8077,8 +8182,8 @@ var init_onboard = __esm({
|
|
|
8077
8182
|
|
|
8078
8183
|
// src/client/http.ts
|
|
8079
8184
|
import { URL as URL2 } from "node:url";
|
|
8080
|
-
function buildUrl(apiBase,
|
|
8081
|
-
const normalizedPath =
|
|
8185
|
+
function buildUrl(apiBase, path62) {
|
|
8186
|
+
const normalizedPath = path62.startsWith("/") ? path62 : `/${path62}`;
|
|
8082
8187
|
const [pathname, query] = normalizedPath.split("?");
|
|
8083
8188
|
const url = new URL2(apiBase);
|
|
8084
8189
|
url.pathname = `${url.pathname.replace(/\/+$/, "")}${pathname}`;
|
|
@@ -8140,26 +8245,26 @@ var init_http = __esm({
|
|
|
8140
8245
|
this.runId = opts.runId?.trim() || void 0;
|
|
8141
8246
|
this.userId = opts.userId?.trim() || void 0;
|
|
8142
8247
|
}
|
|
8143
|
-
get(
|
|
8144
|
-
return this.request(
|
|
8248
|
+
get(path62, opts) {
|
|
8249
|
+
return this.request(path62, { method: "GET" }, opts);
|
|
8145
8250
|
}
|
|
8146
|
-
post(
|
|
8147
|
-
return this.request(
|
|
8251
|
+
post(path62, body, opts) {
|
|
8252
|
+
return this.request(path62, {
|
|
8148
8253
|
method: "POST",
|
|
8149
8254
|
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
8150
8255
|
}, opts);
|
|
8151
8256
|
}
|
|
8152
|
-
patch(
|
|
8153
|
-
return this.request(
|
|
8257
|
+
patch(path62, body, opts) {
|
|
8258
|
+
return this.request(path62, {
|
|
8154
8259
|
method: "PATCH",
|
|
8155
8260
|
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
8156
8261
|
}, opts);
|
|
8157
8262
|
}
|
|
8158
|
-
delete(
|
|
8159
|
-
return this.request(
|
|
8263
|
+
delete(path62, opts) {
|
|
8264
|
+
return this.request(path62, { method: "DELETE" }, opts);
|
|
8160
8265
|
}
|
|
8161
|
-
async request(
|
|
8162
|
-
const url = buildUrl(this.apiBase,
|
|
8266
|
+
async request(path62, init, opts) {
|
|
8267
|
+
const url = buildUrl(this.apiBase, path62);
|
|
8163
8268
|
const headers = {
|
|
8164
8269
|
accept: "application/json",
|
|
8165
8270
|
...toStringRecord(init.headers)
|
|
@@ -8199,111 +8304,6 @@ var init_http = __esm({
|
|
|
8199
8304
|
}
|
|
8200
8305
|
});
|
|
8201
8306
|
|
|
8202
|
-
// src/auth/paths.ts
|
|
8203
|
-
import path10 from "node:path";
|
|
8204
|
-
function resolveAuthDir() {
|
|
8205
|
-
return path10.resolve(resolvePaperclipHomeDir(), "auth");
|
|
8206
|
-
}
|
|
8207
|
-
function resolveProfilesDir() {
|
|
8208
|
-
return path10.resolve(resolvePaperclipHomeDir(), "profiles");
|
|
8209
|
-
}
|
|
8210
|
-
function resolveSessionPath() {
|
|
8211
|
-
return path10.resolve(resolveAuthDir(), "session.json");
|
|
8212
|
-
}
|
|
8213
|
-
function resolveHostedOverlayPath() {
|
|
8214
|
-
return path10.resolve(resolveProfilesDir(), "hosted-overlay.json");
|
|
8215
|
-
}
|
|
8216
|
-
function resolveEffectiveProfilePath() {
|
|
8217
|
-
return path10.resolve(resolveProfilesDir(), "effective-profile.json");
|
|
8218
|
-
}
|
|
8219
|
-
var init_paths = __esm({
|
|
8220
|
-
"src/auth/paths.ts"() {
|
|
8221
|
-
"use strict";
|
|
8222
|
-
init_home();
|
|
8223
|
-
}
|
|
8224
|
-
});
|
|
8225
|
-
|
|
8226
|
-
// src/auth/session-store.ts
|
|
8227
|
-
var session_store_exports = {};
|
|
8228
|
-
__export(session_store_exports, {
|
|
8229
|
-
clearSession: () => clearSession,
|
|
8230
|
-
describeSessionPath: () => describeSessionPath,
|
|
8231
|
-
isSessionExpired: () => isSessionExpired,
|
|
8232
|
-
readSession: () => readSession,
|
|
8233
|
-
writeSession: () => writeSession
|
|
8234
|
-
});
|
|
8235
|
-
import fs11 from "node:fs";
|
|
8236
|
-
import path11 from "node:path";
|
|
8237
|
-
function parseJson3(filePath) {
|
|
8238
|
-
try {
|
|
8239
|
-
return JSON.parse(fs11.readFileSync(filePath, "utf-8"));
|
|
8240
|
-
} catch (err) {
|
|
8241
|
-
throw new Error(
|
|
8242
|
-
`Failed to parse auth session at ${filePath}: ${err instanceof Error ? err.message : String(err)}`
|
|
8243
|
-
);
|
|
8244
|
-
}
|
|
8245
|
-
}
|
|
8246
|
-
function toStringOrUndefined2(value) {
|
|
8247
|
-
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
8248
|
-
}
|
|
8249
|
-
function normalizeSession(raw) {
|
|
8250
|
-
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null;
|
|
8251
|
-
const record = raw;
|
|
8252
|
-
const accessToken = toStringOrUndefined2(record.accessToken);
|
|
8253
|
-
const hostedBaseUrl = toStringOrUndefined2(record.hostedBaseUrl);
|
|
8254
|
-
if (!accessToken || !hostedBaseUrl) return null;
|
|
8255
|
-
const issuedAt = toStringOrUndefined2(record.issuedAt) ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
8256
|
-
return {
|
|
8257
|
-
version: 1,
|
|
8258
|
-
hostedBaseUrl,
|
|
8259
|
-
accessToken,
|
|
8260
|
-
expiresAt: toStringOrUndefined2(record.expiresAt),
|
|
8261
|
-
userId: toStringOrUndefined2(record.userId),
|
|
8262
|
-
email: toStringOrUndefined2(record.email),
|
|
8263
|
-
orgId: toStringOrUndefined2(record.orgId),
|
|
8264
|
-
orgName: toStringOrUndefined2(record.orgName),
|
|
8265
|
-
machineLabel: toStringOrUndefined2(record.machineLabel),
|
|
8266
|
-
issuedAt
|
|
8267
|
-
};
|
|
8268
|
-
}
|
|
8269
|
-
function readSession() {
|
|
8270
|
-
const filePath = resolveSessionPath();
|
|
8271
|
-
if (!fs11.existsSync(filePath)) return null;
|
|
8272
|
-
const raw = parseJson3(filePath);
|
|
8273
|
-
return normalizeSession(raw);
|
|
8274
|
-
}
|
|
8275
|
-
function writeSession(session) {
|
|
8276
|
-
const filePath = resolveSessionPath();
|
|
8277
|
-
fs11.mkdirSync(resolveAuthDir(), { recursive: true });
|
|
8278
|
-
fs11.writeFileSync(filePath, `${JSON.stringify(session, null, 2)}
|
|
8279
|
-
`, { mode: 384 });
|
|
8280
|
-
try {
|
|
8281
|
-
fs11.chmodSync(filePath, 384);
|
|
8282
|
-
} catch {
|
|
8283
|
-
}
|
|
8284
|
-
}
|
|
8285
|
-
function clearSession() {
|
|
8286
|
-
const filePath = resolveSessionPath();
|
|
8287
|
-
if (!fs11.existsSync(filePath)) return false;
|
|
8288
|
-
fs11.rmSync(filePath, { force: true });
|
|
8289
|
-
return true;
|
|
8290
|
-
}
|
|
8291
|
-
function isSessionExpired(session, now = /* @__PURE__ */ new Date()) {
|
|
8292
|
-
if (!session.expiresAt) return false;
|
|
8293
|
-
const expires = Date.parse(session.expiresAt);
|
|
8294
|
-
if (Number.isNaN(expires)) return false;
|
|
8295
|
-
return expires <= now.getTime();
|
|
8296
|
-
}
|
|
8297
|
-
function describeSessionPath() {
|
|
8298
|
-
return path11.resolve(resolveSessionPath());
|
|
8299
|
-
}
|
|
8300
|
-
var init_session_store = __esm({
|
|
8301
|
-
"src/auth/session-store.ts"() {
|
|
8302
|
-
"use strict";
|
|
8303
|
-
init_paths();
|
|
8304
|
-
}
|
|
8305
|
-
});
|
|
8306
|
-
|
|
8307
8307
|
// src/auth/hosted-client.ts
|
|
8308
8308
|
var hosted_client_exports = {};
|
|
8309
8309
|
__export(hosted_client_exports, {
|
|
@@ -8643,52 +8643,52 @@ __export(service_exports, {
|
|
|
8643
8643
|
validateBundledKitAssetRoot: () => validateBundledKitAssetRoot,
|
|
8644
8644
|
validateKitDirectory: () => validateKitDirectory
|
|
8645
8645
|
});
|
|
8646
|
-
import
|
|
8647
|
-
import
|
|
8646
|
+
import fs18 from "node:fs";
|
|
8647
|
+
import path24 from "node:path";
|
|
8648
8648
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
8649
8649
|
function resolveBundledKitAssetsRoot() {
|
|
8650
|
-
const moduleDir =
|
|
8650
|
+
const moduleDir = path24.dirname(fileURLToPath4(import.meta.url));
|
|
8651
8651
|
const candidates = [
|
|
8652
|
-
|
|
8653
|
-
|
|
8652
|
+
path24.resolve(moduleDir, "../../assets/worker-kits"),
|
|
8653
|
+
path24.resolve(moduleDir, "../assets/worker-kits")
|
|
8654
8654
|
];
|
|
8655
8655
|
for (const candidate of candidates) {
|
|
8656
|
-
if (
|
|
8656
|
+
if (fs18.existsSync(candidate)) return candidate;
|
|
8657
8657
|
}
|
|
8658
8658
|
throw new Error("Could not locate bundled worker kit assets.");
|
|
8659
8659
|
}
|
|
8660
8660
|
function resolveRequestedOutputRoot(outDir) {
|
|
8661
8661
|
if (outDir?.trim()) {
|
|
8662
|
-
return
|
|
8662
|
+
return path24.resolve(expandHomePrefix(outDir.trim()));
|
|
8663
8663
|
}
|
|
8664
|
-
return
|
|
8664
|
+
return path24.resolve(resolvePaperclipHomeDir(), "kits", "exports");
|
|
8665
8665
|
}
|
|
8666
8666
|
function readJsonFile(filePath) {
|
|
8667
|
-
return JSON.parse(
|
|
8667
|
+
return JSON.parse(fs18.readFileSync(filePath, "utf8"));
|
|
8668
8668
|
}
|
|
8669
8669
|
function assertRelativePathExists(assetRoot, relativePath, label) {
|
|
8670
|
-
const fullPath =
|
|
8671
|
-
if (!
|
|
8670
|
+
const fullPath = path24.resolve(assetRoot, relativePath);
|
|
8671
|
+
if (!fs18.existsSync(fullPath)) {
|
|
8672
8672
|
throw new Error(`${label} is missing required path: ${relativePath}`);
|
|
8673
8673
|
}
|
|
8674
8674
|
}
|
|
8675
8675
|
function listRelativeFiles(rootDir) {
|
|
8676
8676
|
const files = [];
|
|
8677
8677
|
const walk = (currentDir) => {
|
|
8678
|
-
for (const entry of
|
|
8679
|
-
const fullPath =
|
|
8678
|
+
for (const entry of fs18.readdirSync(currentDir, { withFileTypes: true })) {
|
|
8679
|
+
const fullPath = path24.join(currentDir, entry.name);
|
|
8680
8680
|
if (entry.isDirectory()) {
|
|
8681
8681
|
walk(fullPath);
|
|
8682
8682
|
continue;
|
|
8683
8683
|
}
|
|
8684
|
-
files.push(
|
|
8684
|
+
files.push(path24.relative(rootDir, fullPath).split(path24.sep).join("/"));
|
|
8685
8685
|
}
|
|
8686
8686
|
};
|
|
8687
8687
|
walk(rootDir);
|
|
8688
8688
|
return files.sort();
|
|
8689
8689
|
}
|
|
8690
8690
|
function parseManifest(assetRoot) {
|
|
8691
|
-
const raw = readJsonFile(
|
|
8691
|
+
const raw = readJsonFile(path24.resolve(assetRoot, "kit.json"));
|
|
8692
8692
|
if (!SUPPORTED_SCHEMA_VERSIONS.includes(raw.schemaVersion)) {
|
|
8693
8693
|
throw new Error(`Unsupported kit schema version for ${assetRoot}: ${raw.schemaVersion}`);
|
|
8694
8694
|
}
|
|
@@ -8699,7 +8699,7 @@ function parseBundleManifest(assetRoot, manifest, bundleId) {
|
|
|
8699
8699
|
if (!bundleRef) {
|
|
8700
8700
|
throw new Error(`Kit ${manifest.kit.id} does not declare bundle ${bundleId}.`);
|
|
8701
8701
|
}
|
|
8702
|
-
const raw = readJsonFile(
|
|
8702
|
+
const raw = readJsonFile(path24.resolve(assetRoot, bundleRef.path));
|
|
8703
8703
|
if (!SUPPORTED_SCHEMA_VERSIONS.includes(raw.schemaVersion)) {
|
|
8704
8704
|
throw new Error(
|
|
8705
8705
|
`Unsupported bundle schema version for ${bundleRef.path}: ${raw.schemaVersion}`
|
|
@@ -8764,14 +8764,14 @@ function validateKitDirectory(kitPath) {
|
|
|
8764
8764
|
const warnings = [];
|
|
8765
8765
|
let schemaVersion = 0;
|
|
8766
8766
|
let kitId = "<unknown>";
|
|
8767
|
-
const kitJsonPath =
|
|
8768
|
-
if (!
|
|
8767
|
+
const kitJsonPath = path24.resolve(kitPath, "kit.json");
|
|
8768
|
+
if (!fs18.existsSync(kitJsonPath)) {
|
|
8769
8769
|
errors.push({ field: "kit.json", message: "kit.json not found in kit directory" });
|
|
8770
8770
|
return { valid: false, schemaVersion, kitId, errors, warnings };
|
|
8771
8771
|
}
|
|
8772
8772
|
let raw;
|
|
8773
8773
|
try {
|
|
8774
|
-
raw = JSON.parse(
|
|
8774
|
+
raw = JSON.parse(fs18.readFileSync(kitJsonPath, "utf8"));
|
|
8775
8775
|
} catch {
|
|
8776
8776
|
errors.push({ field: "kit.json", message: "kit.json is not valid JSON" });
|
|
8777
8777
|
return { valid: false, schemaVersion, kitId, errors, warnings };
|
|
@@ -8838,8 +8838,8 @@ function validateKitDirectory(kitPath) {
|
|
|
8838
8838
|
if (typeof entrypoint.path !== "string") {
|
|
8839
8839
|
errors.push({ field: "entrypoint.path", message: "Missing required field 'entrypoint.path'" });
|
|
8840
8840
|
} else {
|
|
8841
|
-
const fullPath =
|
|
8842
|
-
if (!
|
|
8841
|
+
const fullPath = path24.resolve(kitPath, entrypoint.path);
|
|
8842
|
+
if (!fs18.existsSync(fullPath)) {
|
|
8843
8843
|
errors.push({ field: "entrypoint.path", message: `Entrypoint file not found: ${entrypoint.path}` });
|
|
8844
8844
|
}
|
|
8845
8845
|
}
|
|
@@ -8847,16 +8847,16 @@ function validateKitDirectory(kitPath) {
|
|
|
8847
8847
|
if (typeof raw.agentContractPath !== "string") {
|
|
8848
8848
|
errors.push({ field: "agentContractPath", message: "Missing required field 'agentContractPath'" });
|
|
8849
8849
|
} else {
|
|
8850
|
-
const fullPath =
|
|
8851
|
-
if (!
|
|
8850
|
+
const fullPath = path24.resolve(kitPath, raw.agentContractPath);
|
|
8851
|
+
if (!fs18.existsSync(fullPath)) {
|
|
8852
8852
|
errors.push({ field: "agentContractPath", message: `Agent contract not found: ${raw.agentContractPath}` });
|
|
8853
8853
|
}
|
|
8854
8854
|
}
|
|
8855
8855
|
if (typeof raw.brandTemplatePath !== "string") {
|
|
8856
8856
|
errors.push({ field: "brandTemplatePath", message: "Missing required field 'brandTemplatePath'" });
|
|
8857
8857
|
} else {
|
|
8858
|
-
const fullPath =
|
|
8859
|
-
if (!
|
|
8858
|
+
const fullPath = path24.resolve(kitPath, raw.brandTemplatePath);
|
|
8859
|
+
if (!fs18.existsSync(fullPath)) {
|
|
8860
8860
|
errors.push({ field: "brandTemplatePath", message: `Brand template not found: ${raw.brandTemplatePath}` });
|
|
8861
8861
|
}
|
|
8862
8862
|
}
|
|
@@ -8866,8 +8866,8 @@ function validateKitDirectory(kitPath) {
|
|
|
8866
8866
|
} else {
|
|
8867
8867
|
for (const assetPath of frozenAssets) {
|
|
8868
8868
|
if (typeof assetPath !== "string") continue;
|
|
8869
|
-
const fullPath =
|
|
8870
|
-
if (!
|
|
8869
|
+
const fullPath = path24.resolve(kitPath, assetPath);
|
|
8870
|
+
if (!fs18.existsSync(fullPath)) {
|
|
8871
8871
|
errors.push({ field: "frozenAssetPaths", message: `Frozen asset not found: ${assetPath}` });
|
|
8872
8872
|
}
|
|
8873
8873
|
}
|
|
@@ -8885,8 +8885,8 @@ function validateKitDirectory(kitPath) {
|
|
|
8885
8885
|
} else {
|
|
8886
8886
|
for (const reqPath of requiredPaths) {
|
|
8887
8887
|
if (typeof reqPath !== "string") continue;
|
|
8888
|
-
const fullPath =
|
|
8889
|
-
if (!
|
|
8888
|
+
const fullPath = path24.resolve(kitPath, reqPath);
|
|
8889
|
+
if (!fs18.existsSync(fullPath)) {
|
|
8890
8890
|
errors.push({ field: "outputStandard.requiredPaths", message: `Required output path not found: ${reqPath}` });
|
|
8891
8891
|
}
|
|
8892
8892
|
}
|
|
@@ -8902,13 +8902,13 @@ function validateKitDirectory(kitPath) {
|
|
|
8902
8902
|
errors.push({ field: "bundles[].path", message: "Bundle ref missing 'path' field" });
|
|
8903
8903
|
continue;
|
|
8904
8904
|
}
|
|
8905
|
-
const bundlePath =
|
|
8906
|
-
if (!
|
|
8905
|
+
const bundlePath = path24.resolve(kitPath, ref.path);
|
|
8906
|
+
if (!fs18.existsSync(bundlePath)) {
|
|
8907
8907
|
errors.push({ field: "bundles[].path", message: `Bundle manifest not found: ${ref.path}` });
|
|
8908
8908
|
continue;
|
|
8909
8909
|
}
|
|
8910
8910
|
try {
|
|
8911
|
-
const bundleRaw = JSON.parse(
|
|
8911
|
+
const bundleRaw = JSON.parse(fs18.readFileSync(bundlePath, "utf8"));
|
|
8912
8912
|
const bundleBlock = bundleRaw.bundle;
|
|
8913
8913
|
if (!bundleBlock || typeof bundleBlock !== "object") {
|
|
8914
8914
|
errors.push({ field: `bundle(${ref.id})`, message: "Bundle manifest missing 'bundle' block" });
|
|
@@ -8946,7 +8946,7 @@ function loadResolvedBundledKit(assetRoot, catalogEntry) {
|
|
|
8946
8946
|
function validateBundledKitAssetRoot(assetRoot, input) {
|
|
8947
8947
|
const catalogEntry = {
|
|
8948
8948
|
id: input.kitId,
|
|
8949
|
-
packageDirName:
|
|
8949
|
+
packageDirName: path24.basename(assetRoot),
|
|
8950
8950
|
defaultBundleId: input.bundleId ?? input.kitId,
|
|
8951
8951
|
type: "worker",
|
|
8952
8952
|
executionMode: "export",
|
|
@@ -8980,7 +8980,7 @@ Available: ${available}`
|
|
|
8980
8980
|
);
|
|
8981
8981
|
}
|
|
8982
8982
|
const catalogEntry = BUNDLED_KIT_CATALOG.find((e) => e.id === resolvedId);
|
|
8983
|
-
const assetRoot =
|
|
8983
|
+
const assetRoot = path24.resolve(resolveBundledKitAssetsRoot(), catalogEntry.packageDirName);
|
|
8984
8984
|
return loadResolvedBundledKit(assetRoot, catalogEntry);
|
|
8985
8985
|
}
|
|
8986
8986
|
function toListItem(resolved) {
|
|
@@ -9000,8 +9000,8 @@ function toListItem(resolved) {
|
|
|
9000
9000
|
}
|
|
9001
9001
|
function resolveOutputPaths(resolved, outDir) {
|
|
9002
9002
|
const outputRoot = resolveRequestedOutputRoot(outDir);
|
|
9003
|
-
const folderPath =
|
|
9004
|
-
const zipPath =
|
|
9003
|
+
const folderPath = path24.resolve(outputRoot, resolved.bundleManifest.export.folderName);
|
|
9004
|
+
const zipPath = path24.resolve(outputRoot, resolved.bundleManifest.export.zipFileName);
|
|
9005
9005
|
return { outputRoot, folderPath, zipPath };
|
|
9006
9006
|
}
|
|
9007
9007
|
function listBundledKits() {
|
|
@@ -9043,9 +9043,9 @@ function getBundledKitSourceInfo(kitId) {
|
|
|
9043
9043
|
}
|
|
9044
9044
|
function copyBundledKitSource(kitId, destinationPath) {
|
|
9045
9045
|
const info = getBundledKitSourceInfo(kitId);
|
|
9046
|
-
|
|
9047
|
-
|
|
9048
|
-
|
|
9046
|
+
fs18.mkdirSync(path24.dirname(destinationPath), { recursive: true });
|
|
9047
|
+
fs18.rmSync(destinationPath, { recursive: true, force: true });
|
|
9048
|
+
fs18.cpSync(info.assetRoot, destinationPath, { recursive: true });
|
|
9049
9049
|
return info;
|
|
9050
9050
|
}
|
|
9051
9051
|
function crc32(buffer) {
|
|
@@ -9133,7 +9133,7 @@ function reportProgress(onProgress, progress) {
|
|
|
9133
9133
|
function copyDirectoryWithProgress(sourceRoot, targetRoot, onProgress) {
|
|
9134
9134
|
const files = listRelativeFiles(sourceRoot);
|
|
9135
9135
|
const total = Math.max(files.length, 1);
|
|
9136
|
-
|
|
9136
|
+
fs18.mkdirSync(targetRoot, { recursive: true });
|
|
9137
9137
|
reportProgress(onProgress, {
|
|
9138
9138
|
phase: "copying",
|
|
9139
9139
|
completed: 0,
|
|
@@ -9142,10 +9142,10 @@ function copyDirectoryWithProgress(sourceRoot, targetRoot, onProgress) {
|
|
|
9142
9142
|
detail: "Preparing files"
|
|
9143
9143
|
});
|
|
9144
9144
|
files.forEach((relativePath, index51) => {
|
|
9145
|
-
const sourcePath =
|
|
9146
|
-
const targetPath =
|
|
9147
|
-
|
|
9148
|
-
|
|
9145
|
+
const sourcePath = path24.resolve(sourceRoot, relativePath);
|
|
9146
|
+
const targetPath = path24.resolve(targetRoot, relativePath);
|
|
9147
|
+
fs18.mkdirSync(path24.dirname(targetPath), { recursive: true });
|
|
9148
|
+
fs18.copyFileSync(sourcePath, targetPath);
|
|
9149
9149
|
const completed = index51 + 1;
|
|
9150
9150
|
const percent = 10 + Math.round(completed / total * 55);
|
|
9151
9151
|
reportProgress(onProgress, {
|
|
@@ -9171,8 +9171,8 @@ function buildZipEntriesWithProgress(sourceRoot, exportFolderName, onProgress) {
|
|
|
9171
9171
|
detail: relativePath
|
|
9172
9172
|
});
|
|
9173
9173
|
return {
|
|
9174
|
-
name:
|
|
9175
|
-
data:
|
|
9174
|
+
name: path24.posix.join(exportFolderName, relativePath),
|
|
9175
|
+
data: fs18.readFileSync(path24.resolve(sourceRoot, relativePath))
|
|
9176
9176
|
};
|
|
9177
9177
|
});
|
|
9178
9178
|
}
|
|
@@ -9187,8 +9187,8 @@ function downloadBundledKit(kitId, outDir, options = {}) {
|
|
|
9187
9187
|
percent: 0,
|
|
9188
9188
|
detail: "Resolving export target"
|
|
9189
9189
|
});
|
|
9190
|
-
|
|
9191
|
-
|
|
9190
|
+
fs18.mkdirSync(outputPaths.outputRoot, { recursive: true });
|
|
9191
|
+
fs18.rmSync(outputPaths.folderPath, { recursive: true, force: true });
|
|
9192
9192
|
copyDirectoryWithProgress(resolved.assetRoot, outputPaths.folderPath, onProgress);
|
|
9193
9193
|
const zipBuffer = buildStoredZip(
|
|
9194
9194
|
buildZipEntriesWithProgress(outputPaths.folderPath, resolved.bundleManifest.export.folderName, onProgress)
|
|
@@ -9198,9 +9198,9 @@ function downloadBundledKit(kitId, outDir, options = {}) {
|
|
|
9198
9198
|
completed: 1,
|
|
9199
9199
|
total: 1,
|
|
9200
9200
|
percent: 98,
|
|
9201
|
-
detail:
|
|
9201
|
+
detail: path24.basename(outputPaths.zipPath)
|
|
9202
9202
|
});
|
|
9203
|
-
|
|
9203
|
+
fs18.writeFileSync(outputPaths.zipPath, zipBuffer);
|
|
9204
9204
|
reportProgress(onProgress, {
|
|
9205
9205
|
phase: "done",
|
|
9206
9206
|
completed: 1,
|
|
@@ -9236,26 +9236,26 @@ __export(kit_forks_home_exports, {
|
|
|
9236
9236
|
resolveKitForksOrphanJobsDir: () => resolveKitForksOrphanJobsDir
|
|
9237
9237
|
});
|
|
9238
9238
|
import os6 from "node:os";
|
|
9239
|
-
import
|
|
9239
|
+
import path25 from "node:path";
|
|
9240
9240
|
function resolveKitForksHomeDir() {
|
|
9241
9241
|
const envHome = process.env.GROWTHUB_KIT_FORKS_HOME?.trim();
|
|
9242
|
-
if (envHome) return
|
|
9243
|
-
return
|
|
9242
|
+
if (envHome) return path25.resolve(expandHomePrefix(envHome));
|
|
9243
|
+
return path25.resolve(os6.homedir(), ".growthub", "kit-forks");
|
|
9244
9244
|
}
|
|
9245
9245
|
function resolveKitForksIndexPath() {
|
|
9246
|
-
return
|
|
9246
|
+
return path25.resolve(resolveKitForksHomeDir(), "index.json");
|
|
9247
9247
|
}
|
|
9248
9248
|
function resolveKitForksJobsDir() {
|
|
9249
|
-
return
|
|
9249
|
+
return path25.resolve(resolveKitForksHomeDir(), "jobs");
|
|
9250
9250
|
}
|
|
9251
9251
|
function resolveKitForksOrphanJobsDir() {
|
|
9252
|
-
return
|
|
9252
|
+
return path25.resolve(resolveKitForksHomeDir(), "orphan-jobs");
|
|
9253
9253
|
}
|
|
9254
9254
|
function resolveInForkStateDir(forkPath) {
|
|
9255
|
-
return
|
|
9255
|
+
return path25.resolve(forkPath, IN_FORK_STATE_DIRNAME);
|
|
9256
9256
|
}
|
|
9257
9257
|
function resolveInForkRegistrationPath(forkPath) {
|
|
9258
|
-
return
|
|
9258
|
+
return path25.resolve(resolveInForkStateDir(forkPath), "fork.json");
|
|
9259
9259
|
}
|
|
9260
9260
|
var IN_FORK_STATE_DIRNAME;
|
|
9261
9261
|
var init_kit_forks_home = __esm({
|
|
@@ -9267,13 +9267,13 @@ var init_kit_forks_home = __esm({
|
|
|
9267
9267
|
});
|
|
9268
9268
|
|
|
9269
9269
|
// src/kits/fork-registry.ts
|
|
9270
|
-
import
|
|
9271
|
-
import
|
|
9270
|
+
import fs19 from "node:fs";
|
|
9271
|
+
import path26 from "node:path";
|
|
9272
9272
|
function readIndex() {
|
|
9273
9273
|
const p35 = resolveKitForksIndexPath();
|
|
9274
|
-
if (!
|
|
9274
|
+
if (!fs19.existsSync(p35)) return { version: 1, entries: [] };
|
|
9275
9275
|
try {
|
|
9276
|
-
const parsed = JSON.parse(
|
|
9276
|
+
const parsed = JSON.parse(fs19.readFileSync(p35, "utf8"));
|
|
9277
9277
|
if (!parsed || !Array.isArray(parsed.entries)) return { version: 1, entries: [] };
|
|
9278
9278
|
return parsed;
|
|
9279
9279
|
} catch {
|
|
@@ -9282,8 +9282,8 @@ function readIndex() {
|
|
|
9282
9282
|
}
|
|
9283
9283
|
function writeIndex(index51) {
|
|
9284
9284
|
const p35 = resolveKitForksIndexPath();
|
|
9285
|
-
|
|
9286
|
-
|
|
9285
|
+
fs19.mkdirSync(path26.dirname(p35), { recursive: true });
|
|
9286
|
+
fs19.writeFileSync(p35, JSON.stringify(index51, null, 2) + "\n", "utf8");
|
|
9287
9287
|
}
|
|
9288
9288
|
function upsertIndexEntry(entry) {
|
|
9289
9289
|
const index51 = readIndex();
|
|
@@ -9303,32 +9303,32 @@ function sanitizeForkId(raw) {
|
|
|
9303
9303
|
return raw.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").slice(0, 56);
|
|
9304
9304
|
}
|
|
9305
9305
|
function generateForkId(forkPath, kitId) {
|
|
9306
|
-
const dirName =
|
|
9306
|
+
const dirName = path26.basename(forkPath);
|
|
9307
9307
|
const base = sanitizeForkId(`${kitId}-${dirName}`);
|
|
9308
9308
|
const suffix = Date.now().toString(36).slice(-4);
|
|
9309
9309
|
return `${base}-${suffix}`;
|
|
9310
9310
|
}
|
|
9311
9311
|
function readForkJson(forkPath) {
|
|
9312
9312
|
const p35 = resolveInForkRegistrationPath(forkPath);
|
|
9313
|
-
if (!
|
|
9313
|
+
if (!fs19.existsSync(p35)) return null;
|
|
9314
9314
|
try {
|
|
9315
|
-
return JSON.parse(
|
|
9315
|
+
return JSON.parse(fs19.readFileSync(p35, "utf8"));
|
|
9316
9316
|
} catch {
|
|
9317
9317
|
return null;
|
|
9318
9318
|
}
|
|
9319
9319
|
}
|
|
9320
9320
|
function writeForkJson(reg) {
|
|
9321
9321
|
const stateDir = resolveInForkStateDir(reg.forkPath);
|
|
9322
|
-
|
|
9323
|
-
|
|
9322
|
+
fs19.mkdirSync(stateDir, { recursive: true });
|
|
9323
|
+
fs19.writeFileSync(
|
|
9324
9324
|
resolveInForkRegistrationPath(reg.forkPath),
|
|
9325
9325
|
JSON.stringify(reg, null, 2) + "\n",
|
|
9326
9326
|
"utf8"
|
|
9327
9327
|
);
|
|
9328
9328
|
}
|
|
9329
9329
|
function registerKitFork(opts) {
|
|
9330
|
-
const resolvedPath =
|
|
9331
|
-
if (!
|
|
9330
|
+
const resolvedPath = path26.resolve(opts.forkPath);
|
|
9331
|
+
if (!fs19.existsSync(resolvedPath)) {
|
|
9332
9332
|
throw new Error(`Fork path does not exist: ${resolvedPath}`);
|
|
9333
9333
|
}
|
|
9334
9334
|
const forkId = generateForkId(resolvedPath, opts.kitId);
|
|
@@ -9362,7 +9362,7 @@ function updateKitForkRegistration(reg) {
|
|
|
9362
9362
|
function loadKitForkRegistration(kitId, forkId) {
|
|
9363
9363
|
const entry = readIndex().entries.find((e) => e.kitId === kitId && e.forkId === forkId);
|
|
9364
9364
|
if (!entry) return null;
|
|
9365
|
-
if (!
|
|
9365
|
+
if (!fs19.existsSync(entry.forkPath)) return null;
|
|
9366
9366
|
return readForkJson(entry.forkPath);
|
|
9367
9367
|
}
|
|
9368
9368
|
function listKitForkRegistrations(filterKitId) {
|
|
@@ -9370,7 +9370,7 @@ function listKitForkRegistrations(filterKitId) {
|
|
|
9370
9370
|
const results = [];
|
|
9371
9371
|
for (const entry of index51.entries) {
|
|
9372
9372
|
if (filterKitId && entry.kitId !== filterKitId) continue;
|
|
9373
|
-
if (!
|
|
9373
|
+
if (!fs19.existsSync(entry.forkPath)) continue;
|
|
9374
9374
|
const reg = readForkJson(entry.forkPath);
|
|
9375
9375
|
if (reg) results.push(reg);
|
|
9376
9376
|
}
|
|
@@ -9379,9 +9379,9 @@ function listKitForkRegistrations(filterKitId) {
|
|
|
9379
9379
|
function deregisterKitFork(kitId, forkId) {
|
|
9380
9380
|
const entry = readIndex().entries.find((e) => e.kitId === kitId && e.forkId === forkId);
|
|
9381
9381
|
if (!entry) return false;
|
|
9382
|
-
if (
|
|
9382
|
+
if (fs19.existsSync(entry.forkPath)) {
|
|
9383
9383
|
const stateDir = resolveInForkStateDir(entry.forkPath);
|
|
9384
|
-
|
|
9384
|
+
fs19.rmSync(stateDir, { recursive: true, force: true });
|
|
9385
9385
|
}
|
|
9386
9386
|
removeIndexEntry(kitId, forkId);
|
|
9387
9387
|
return true;
|
|
@@ -9389,7 +9389,7 @@ function deregisterKitFork(kitId, forkId) {
|
|
|
9389
9389
|
function lookupKitForkPath(kitId, forkId) {
|
|
9390
9390
|
const entry = readIndex().entries.find((e) => e.kitId === kitId && e.forkId === forkId);
|
|
9391
9391
|
if (!entry) return null;
|
|
9392
|
-
if (!
|
|
9392
|
+
if (!fs19.existsSync(entry.forkPath)) return null;
|
|
9393
9393
|
return entry.forkPath;
|
|
9394
9394
|
}
|
|
9395
9395
|
var init_fork_registry = __esm({
|
|
@@ -9400,8 +9400,8 @@ var init_fork_registry = __esm({
|
|
|
9400
9400
|
});
|
|
9401
9401
|
|
|
9402
9402
|
// src/kits/fork-policy.ts
|
|
9403
|
-
import
|
|
9404
|
-
import
|
|
9403
|
+
import fs20 from "node:fs";
|
|
9404
|
+
import path27 from "node:path";
|
|
9405
9405
|
function makeDefaultKitForkPolicy() {
|
|
9406
9406
|
return {
|
|
9407
9407
|
version: 1,
|
|
@@ -9416,13 +9416,13 @@ function makeDefaultKitForkPolicy() {
|
|
|
9416
9416
|
};
|
|
9417
9417
|
}
|
|
9418
9418
|
function resolvePolicyPath(forkPath) {
|
|
9419
|
-
return
|
|
9419
|
+
return path27.resolve(resolveInForkStateDir(forkPath), "policy.json");
|
|
9420
9420
|
}
|
|
9421
9421
|
function readKitForkPolicy(forkPath) {
|
|
9422
9422
|
const p35 = resolvePolicyPath(forkPath);
|
|
9423
|
-
if (!
|
|
9423
|
+
if (!fs20.existsSync(p35)) return makeDefaultKitForkPolicy();
|
|
9424
9424
|
try {
|
|
9425
|
-
const parsed = JSON.parse(
|
|
9425
|
+
const parsed = JSON.parse(fs20.readFileSync(p35, "utf8"));
|
|
9426
9426
|
return { ...makeDefaultKitForkPolicy(), ...parsed, version: 1 };
|
|
9427
9427
|
} catch {
|
|
9428
9428
|
return makeDefaultKitForkPolicy();
|
|
@@ -9430,9 +9430,9 @@ function readKitForkPolicy(forkPath) {
|
|
|
9430
9430
|
}
|
|
9431
9431
|
function writeKitForkPolicy(forkPath, policy) {
|
|
9432
9432
|
const p35 = resolvePolicyPath(forkPath);
|
|
9433
|
-
|
|
9433
|
+
fs20.mkdirSync(path27.dirname(p35), { recursive: true });
|
|
9434
9434
|
const body = { ...policy, version: 1, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
9435
|
-
|
|
9435
|
+
fs20.writeFileSync(p35, JSON.stringify(body, null, 2) + "\n", "utf8");
|
|
9436
9436
|
}
|
|
9437
9437
|
function matchesAnyPrefix(targetPath, patterns) {
|
|
9438
9438
|
const normalized = targetPath.replace(/^\/+|\/+$/g, "");
|
|
@@ -9454,10 +9454,10 @@ var init_fork_policy = __esm({
|
|
|
9454
9454
|
});
|
|
9455
9455
|
|
|
9456
9456
|
// src/kits/fork-trace.ts
|
|
9457
|
-
import
|
|
9458
|
-
import
|
|
9457
|
+
import fs21 from "node:fs";
|
|
9458
|
+
import path28 from "node:path";
|
|
9459
9459
|
function resolveTracePath(forkPath) {
|
|
9460
|
-
return
|
|
9460
|
+
return path28.resolve(resolveInForkStateDir(forkPath), "trace.jsonl");
|
|
9461
9461
|
}
|
|
9462
9462
|
function appendKitForkTraceEvent(forkPath, event) {
|
|
9463
9463
|
const full = {
|
|
@@ -9465,14 +9465,14 @@ function appendKitForkTraceEvent(forkPath, event) {
|
|
|
9465
9465
|
timestamp: event.timestamp ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
9466
9466
|
};
|
|
9467
9467
|
const p35 = resolveTracePath(forkPath);
|
|
9468
|
-
|
|
9469
|
-
|
|
9468
|
+
fs21.mkdirSync(path28.dirname(p35), { recursive: true });
|
|
9469
|
+
fs21.appendFileSync(p35, JSON.stringify(full) + "\n", "utf8");
|
|
9470
9470
|
return full;
|
|
9471
9471
|
}
|
|
9472
9472
|
function readKitForkTrace(forkPath) {
|
|
9473
9473
|
const p35 = resolveTracePath(forkPath);
|
|
9474
|
-
if (!
|
|
9475
|
-
const raw =
|
|
9474
|
+
if (!fs21.existsSync(p35)) return [];
|
|
9475
|
+
const raw = fs21.readFileSync(p35, "utf8").trim();
|
|
9476
9476
|
if (!raw) return [];
|
|
9477
9477
|
const events = [];
|
|
9478
9478
|
for (const line of raw.split("\n")) {
|
|
@@ -9498,8 +9498,8 @@ var init_fork_trace = __esm({
|
|
|
9498
9498
|
|
|
9499
9499
|
// src/kits/fork-remote.ts
|
|
9500
9500
|
import { execFileSync as execFileSync2, spawnSync } from "node:child_process";
|
|
9501
|
-
import
|
|
9502
|
-
import
|
|
9501
|
+
import fs22 from "node:fs";
|
|
9502
|
+
import path29 from "node:path";
|
|
9503
9503
|
function runGit(cwd, args, opts) {
|
|
9504
9504
|
const res = spawnSync("git", args, {
|
|
9505
9505
|
cwd,
|
|
@@ -9515,7 +9515,7 @@ function runGit(cwd, args, opts) {
|
|
|
9515
9515
|
};
|
|
9516
9516
|
}
|
|
9517
9517
|
function isGitRepo(forkPath) {
|
|
9518
|
-
if (!
|
|
9518
|
+
if (!fs22.existsSync(path29.resolve(forkPath, ".git"))) return false;
|
|
9519
9519
|
const res = runGit(forkPath, ["rev-parse", "--is-inside-work-tree"]);
|
|
9520
9520
|
return res.ok && res.stdout.trim() === "true";
|
|
9521
9521
|
}
|
|
@@ -9582,17 +9582,17 @@ var init_fork_remote = __esm({
|
|
|
9582
9582
|
|
|
9583
9583
|
// src/config/github-home.ts
|
|
9584
9584
|
import os7 from "node:os";
|
|
9585
|
-
import
|
|
9585
|
+
import path31 from "node:path";
|
|
9586
9586
|
function resolveGithubHomeDir() {
|
|
9587
9587
|
const envHome = process.env.GROWTHUB_GITHUB_HOME?.trim();
|
|
9588
|
-
if (envHome) return
|
|
9589
|
-
return
|
|
9588
|
+
if (envHome) return path31.resolve(expandHomePrefix(envHome));
|
|
9589
|
+
return path31.resolve(os7.homedir(), ".growthub", "github");
|
|
9590
9590
|
}
|
|
9591
9591
|
function resolveGithubTokenPath() {
|
|
9592
|
-
return
|
|
9592
|
+
return path31.resolve(resolveGithubHomeDir(), "token.json");
|
|
9593
9593
|
}
|
|
9594
9594
|
function resolveGithubProfilePath() {
|
|
9595
|
-
return
|
|
9595
|
+
return path31.resolve(resolveGithubHomeDir(), "profile.json");
|
|
9596
9596
|
}
|
|
9597
9597
|
var init_github_home = __esm({
|
|
9598
9598
|
"src/config/github-home.ts"() {
|
|
@@ -9613,24 +9613,24 @@ __export(token_store_exports, {
|
|
|
9613
9613
|
writeGithubProfile: () => writeGithubProfile,
|
|
9614
9614
|
writeGithubToken: () => writeGithubToken
|
|
9615
9615
|
});
|
|
9616
|
-
import
|
|
9616
|
+
import fs24 from "node:fs";
|
|
9617
9617
|
function ensureDir(dir) {
|
|
9618
|
-
|
|
9618
|
+
fs24.mkdirSync(dir, { recursive: true });
|
|
9619
9619
|
}
|
|
9620
9620
|
function atomicWrite(filePath, body) {
|
|
9621
9621
|
const tmp = `${filePath}.tmp`;
|
|
9622
|
-
|
|
9623
|
-
|
|
9622
|
+
fs24.writeFileSync(tmp, body, { encoding: "utf8", mode: 384 });
|
|
9623
|
+
fs24.renameSync(tmp, filePath);
|
|
9624
9624
|
try {
|
|
9625
|
-
|
|
9625
|
+
fs24.chmodSync(filePath, 384);
|
|
9626
9626
|
} catch {
|
|
9627
9627
|
}
|
|
9628
9628
|
}
|
|
9629
9629
|
function readGithubToken() {
|
|
9630
9630
|
const p35 = resolveGithubTokenPath();
|
|
9631
|
-
if (!
|
|
9631
|
+
if (!fs24.existsSync(p35)) return null;
|
|
9632
9632
|
try {
|
|
9633
|
-
const parsed = JSON.parse(
|
|
9633
|
+
const parsed = JSON.parse(fs24.readFileSync(p35, "utf8"));
|
|
9634
9634
|
if (!parsed?.accessToken) return null;
|
|
9635
9635
|
return parsed;
|
|
9636
9636
|
} catch {
|
|
@@ -9643,7 +9643,7 @@ function writeGithubToken(token) {
|
|
|
9643
9643
|
}
|
|
9644
9644
|
function clearGithubToken() {
|
|
9645
9645
|
const p35 = resolveGithubTokenPath();
|
|
9646
|
-
if (
|
|
9646
|
+
if (fs24.existsSync(p35)) fs24.rmSync(p35, { force: true });
|
|
9647
9647
|
}
|
|
9648
9648
|
function isGithubTokenExpired(token) {
|
|
9649
9649
|
if (!token) return true;
|
|
@@ -9654,9 +9654,9 @@ function isGithubTokenExpired(token) {
|
|
|
9654
9654
|
}
|
|
9655
9655
|
function readGithubProfile() {
|
|
9656
9656
|
const p35 = resolveGithubProfilePath();
|
|
9657
|
-
if (!
|
|
9657
|
+
if (!fs24.existsSync(p35)) return null;
|
|
9658
9658
|
try {
|
|
9659
|
-
return JSON.parse(
|
|
9659
|
+
return JSON.parse(fs24.readFileSync(p35, "utf8"));
|
|
9660
9660
|
} catch {
|
|
9661
9661
|
return null;
|
|
9662
9662
|
}
|
|
@@ -9667,7 +9667,7 @@ function writeGithubProfile(profile) {
|
|
|
9667
9667
|
}
|
|
9668
9668
|
function clearGithubProfile() {
|
|
9669
9669
|
const p35 = resolveGithubProfilePath();
|
|
9670
|
-
if (
|
|
9670
|
+
if (fs24.existsSync(p35)) fs24.rmSync(p35, { force: true });
|
|
9671
9671
|
}
|
|
9672
9672
|
function describeGithubTokenPath() {
|
|
9673
9673
|
return resolveGithubTokenPath();
|
|
@@ -9702,9 +9702,9 @@ async function fetchHostedIntegrations(session) {
|
|
|
9702
9702
|
}
|
|
9703
9703
|
async function fetchHostedIntegrationCredential(session, providerId) {
|
|
9704
9704
|
const client = toApiClient2(session);
|
|
9705
|
-
const
|
|
9705
|
+
const path62 = `${DEFAULT_INTEGRATION_CREDENTIAL_PATH}&provider=${encodeURIComponent(providerId)}`;
|
|
9706
9706
|
try {
|
|
9707
|
-
return await client.get(
|
|
9707
|
+
return await client.get(path62, { ignoreNotFound: true });
|
|
9708
9708
|
} catch (err) {
|
|
9709
9709
|
if (err instanceof ApiRequestError && (err.status === 404 || err.status === 501)) {
|
|
9710
9710
|
throw new HostedEndpointUnavailableError(err.status, err.message);
|
|
@@ -10278,13 +10278,13 @@ var init_github = __esm({
|
|
|
10278
10278
|
}
|
|
10279
10279
|
});
|
|
10280
10280
|
|
|
10281
|
-
// src/starter/init.ts
|
|
10282
|
-
import
|
|
10283
|
-
import
|
|
10281
|
+
// src/starter/init.ts
|
|
10282
|
+
import fs42 from "node:fs";
|
|
10283
|
+
import path50 from "node:path";
|
|
10284
10284
|
async function initStarterWorkspace(opts) {
|
|
10285
10285
|
const kitId = opts.kitId ?? DEFAULT_STARTER_KIT_ID;
|
|
10286
|
-
const absOut =
|
|
10287
|
-
if (
|
|
10286
|
+
const absOut = path50.resolve(opts.out);
|
|
10287
|
+
if (fs42.existsSync(absOut) && fs42.readdirSync(absOut).length > 0) {
|
|
10288
10288
|
throw new Error(`Destination ${absOut} already exists and is not empty.`);
|
|
10289
10289
|
}
|
|
10290
10290
|
const info = getBundledKitSourceInfo(kitId);
|
|
@@ -10293,7 +10293,7 @@ async function initStarterWorkspace(opts) {
|
|
|
10293
10293
|
forkPath: absOut,
|
|
10294
10294
|
kitId: info.id,
|
|
10295
10295
|
baseVersion: info.version,
|
|
10296
|
-
label: opts.name?.trim() ||
|
|
10296
|
+
label: opts.name?.trim() || path50.basename(absOut)
|
|
10297
10297
|
});
|
|
10298
10298
|
const policy = {
|
|
10299
10299
|
...makeDefaultKitForkPolicy(),
|
|
@@ -10386,8 +10386,8 @@ var init_types2 = __esm({
|
|
|
10386
10386
|
});
|
|
10387
10387
|
|
|
10388
10388
|
// src/starter/source-import/github-source.ts
|
|
10389
|
-
import
|
|
10390
|
-
import
|
|
10389
|
+
import fs43 from "node:fs";
|
|
10390
|
+
import path51 from "node:path";
|
|
10391
10391
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
10392
10392
|
function baseHeaders() {
|
|
10393
10393
|
return {
|
|
@@ -10518,12 +10518,12 @@ function cloneGithubRepo(input) {
|
|
|
10518
10518
|
if (!gitAvailable()) {
|
|
10519
10519
|
throw new Error("`git` is not available on PATH \u2014 cannot clone.");
|
|
10520
10520
|
}
|
|
10521
|
-
if (
|
|
10521
|
+
if (fs43.existsSync(input.destination)) {
|
|
10522
10522
|
throw new Error(`Clone destination already exists: ${input.destination}`);
|
|
10523
10523
|
}
|
|
10524
10524
|
const cloneUrl = input.token ? buildTokenCloneUrl(input.probe.repo, input.token) : input.probe.cloneUrl;
|
|
10525
|
-
const parent =
|
|
10526
|
-
|
|
10525
|
+
const parent = path51.dirname(input.destination);
|
|
10526
|
+
fs43.mkdirSync(parent, { recursive: true });
|
|
10527
10527
|
const depth = input.depth ?? 1;
|
|
10528
10528
|
const branch = input.branch ?? input.probe.defaultBranch;
|
|
10529
10529
|
const args = ["clone"];
|
|
@@ -10550,17 +10550,17 @@ function cloneGithubRepo(input) {
|
|
|
10550
10550
|
function narrowToSubdirectory(rootDir, subdirectory) {
|
|
10551
10551
|
const normalizedSub = subdirectory.replace(/^\/+|\/+$/g, "");
|
|
10552
10552
|
if (!normalizedSub) return;
|
|
10553
|
-
const abs =
|
|
10554
|
-
if (!
|
|
10553
|
+
const abs = path51.resolve(rootDir, normalizedSub);
|
|
10554
|
+
if (!fs43.existsSync(abs) || !fs43.statSync(abs).isDirectory()) {
|
|
10555
10555
|
throw new Error(`Subdirectory not found in cloned repo: ${subdirectory}`);
|
|
10556
10556
|
}
|
|
10557
|
-
const tmp =
|
|
10558
|
-
|
|
10559
|
-
`.${
|
|
10557
|
+
const tmp = path51.resolve(
|
|
10558
|
+
path51.dirname(rootDir),
|
|
10559
|
+
`.${path51.basename(rootDir)}-narrow-${Date.now().toString(36)}`
|
|
10560
10560
|
);
|
|
10561
|
-
|
|
10562
|
-
|
|
10563
|
-
|
|
10561
|
+
fs43.renameSync(abs, tmp);
|
|
10562
|
+
fs43.rmSync(rootDir, { recursive: true, force: true });
|
|
10563
|
+
fs43.renameSync(tmp, rootDir);
|
|
10564
10564
|
}
|
|
10565
10565
|
var GITHUB_API_BASE2;
|
|
10566
10566
|
var init_github_source = __esm({
|
|
@@ -10574,9 +10574,9 @@ var init_github_source = __esm({
|
|
|
10574
10574
|
});
|
|
10575
10575
|
|
|
10576
10576
|
// src/starter/source-import/skills-source.ts
|
|
10577
|
-
import
|
|
10577
|
+
import fs44 from "node:fs";
|
|
10578
10578
|
import os10 from "node:os";
|
|
10579
|
-
import
|
|
10579
|
+
import path52 from "node:path";
|
|
10580
10580
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
10581
10581
|
function resolveBase() {
|
|
10582
10582
|
const raw = process.env.SKILLS_SH_BASE?.trim();
|
|
@@ -10842,9 +10842,9 @@ async function probeSkillsSource(input) {
|
|
|
10842
10842
|
};
|
|
10843
10843
|
}
|
|
10844
10844
|
function assertInsidePayloadRoot(root, candidate) {
|
|
10845
|
-
const abs =
|
|
10846
|
-
const rootAbs =
|
|
10847
|
-
if (!abs.startsWith(rootAbs +
|
|
10845
|
+
const abs = path52.resolve(candidate);
|
|
10846
|
+
const rootAbs = path52.resolve(root);
|
|
10847
|
+
if (!abs.startsWith(rootAbs + path52.sep) && abs !== rootAbs) {
|
|
10848
10848
|
throw new Error(`Refusing to write outside payload root: ${candidate}`);
|
|
10849
10849
|
}
|
|
10850
10850
|
}
|
|
@@ -10860,24 +10860,24 @@ function runGit3(args, cwd) {
|
|
|
10860
10860
|
};
|
|
10861
10861
|
}
|
|
10862
10862
|
function skillDirectoryMatches(dir, skillSlug) {
|
|
10863
|
-
const skillFile =
|
|
10864
|
-
if (!
|
|
10863
|
+
const skillFile = path52.resolve(dir, "SKILL.md");
|
|
10864
|
+
if (!fs44.existsSync(skillFile) || !fs44.statSync(skillFile).isFile()) {
|
|
10865
10865
|
return false;
|
|
10866
10866
|
}
|
|
10867
|
-
if (
|
|
10867
|
+
if (path52.basename(dir) === skillSlug) {
|
|
10868
10868
|
return true;
|
|
10869
10869
|
}
|
|
10870
|
-
const content =
|
|
10870
|
+
const content = fs44.readFileSync(skillFile, "utf8");
|
|
10871
10871
|
const nameMatch = content.match(/(?:^|\n)name:\s*["']?([A-Za-z0-9._:-]+)["']?\s*(?:\n|$)/i);
|
|
10872
10872
|
return nameMatch?.[1] === skillSlug;
|
|
10873
10873
|
}
|
|
10874
10874
|
function locateSkillDirectory(root, skillSlug) {
|
|
10875
10875
|
const preferred = [
|
|
10876
|
-
|
|
10877
|
-
|
|
10876
|
+
path52.resolve(root, "skills", skillSlug),
|
|
10877
|
+
path52.resolve(root, skillSlug)
|
|
10878
10878
|
];
|
|
10879
10879
|
for (const candidate of preferred) {
|
|
10880
|
-
if (
|
|
10880
|
+
if (fs44.existsSync(candidate) && fs44.statSync(candidate).isDirectory() && skillDirectoryMatches(candidate, skillSlug)) {
|
|
10881
10881
|
return candidate;
|
|
10882
10882
|
}
|
|
10883
10883
|
}
|
|
@@ -10887,12 +10887,12 @@ function locateSkillDirectory(root, skillSlug) {
|
|
|
10887
10887
|
if (skillDirectoryMatches(current, skillSlug)) {
|
|
10888
10888
|
return current;
|
|
10889
10889
|
}
|
|
10890
|
-
for (const entry of
|
|
10890
|
+
for (const entry of fs44.readdirSync(current, { withFileTypes: true })) {
|
|
10891
10891
|
if (!entry.isDirectory()) continue;
|
|
10892
10892
|
if ([".git", "node_modules", ".next", "dist", "build", "coverage"].includes(entry.name)) {
|
|
10893
10893
|
continue;
|
|
10894
10894
|
}
|
|
10895
|
-
queue.push(
|
|
10895
|
+
queue.push(path52.resolve(current, entry.name));
|
|
10896
10896
|
}
|
|
10897
10897
|
}
|
|
10898
10898
|
return null;
|
|
@@ -10902,18 +10902,18 @@ function copySkillTree(sourceDir, destination) {
|
|
|
10902
10902
|
const stack = [{ from: sourceDir, to: destination }];
|
|
10903
10903
|
while (stack.length > 0) {
|
|
10904
10904
|
const current = stack.pop();
|
|
10905
|
-
|
|
10906
|
-
for (const entry of
|
|
10907
|
-
const fromPath =
|
|
10908
|
-
const toPath =
|
|
10905
|
+
fs44.mkdirSync(current.to, { recursive: true });
|
|
10906
|
+
for (const entry of fs44.readdirSync(current.from, { withFileTypes: true })) {
|
|
10907
|
+
const fromPath = path52.resolve(current.from, entry.name);
|
|
10908
|
+
const toPath = path52.resolve(current.to, entry.name);
|
|
10909
10909
|
assertInsidePayloadRoot(destination, toPath);
|
|
10910
10910
|
if (entry.isDirectory()) {
|
|
10911
10911
|
stack.push({ from: fromPath, to: toPath });
|
|
10912
10912
|
continue;
|
|
10913
10913
|
}
|
|
10914
|
-
const data =
|
|
10915
|
-
|
|
10916
|
-
|
|
10914
|
+
const data = fs44.readFileSync(fromPath);
|
|
10915
|
+
fs44.mkdirSync(path52.dirname(toPath), { recursive: true });
|
|
10916
|
+
fs44.writeFileSync(toPath, data, { mode: 420 });
|
|
10917
10917
|
written += 1;
|
|
10918
10918
|
}
|
|
10919
10919
|
}
|
|
@@ -10924,7 +10924,7 @@ async function fetchSkillPayload(input) {
|
|
|
10924
10924
|
if (!gitAvailable()) {
|
|
10925
10925
|
throw new Error("`git` is not available on PATH \u2014 cannot materialize a skills.sh payload.");
|
|
10926
10926
|
}
|
|
10927
|
-
if (
|
|
10927
|
+
if (fs44.existsSync(destination)) {
|
|
10928
10928
|
throw new Error(`Skill payload destination already exists: ${destination}`);
|
|
10929
10929
|
}
|
|
10930
10930
|
const repoSource = probe.repoUrl ?? (probe.repository ? `https://github.com/${probe.repository}` : void 0);
|
|
@@ -10932,11 +10932,11 @@ async function fetchSkillPayload(input) {
|
|
|
10932
10932
|
if (!repoSource || !skillSlug) {
|
|
10933
10933
|
throw new Error(`Skill '${probe.skillId}' is missing repository metadata \u2014 cannot materialize payload.`);
|
|
10934
10934
|
}
|
|
10935
|
-
const cloneRoot =
|
|
10936
|
-
|
|
10935
|
+
const cloneRoot = fs44.mkdtempSync(
|
|
10936
|
+
path52.join(os10.tmpdir(), "growthub-skills-source-")
|
|
10937
10937
|
);
|
|
10938
10938
|
try {
|
|
10939
|
-
const cloneRes = runGit3(["clone", "--depth", "1", repoSource, cloneRoot],
|
|
10939
|
+
const cloneRes = runGit3(["clone", "--depth", "1", repoSource, cloneRoot], path52.dirname(cloneRoot));
|
|
10940
10940
|
if (!cloneRes.ok) {
|
|
10941
10941
|
throw new Error(`git clone failed: ${cloneRes.stderr || "unable to clone skill repository"}`);
|
|
10942
10942
|
}
|
|
@@ -10949,7 +10949,7 @@ async function fetchSkillPayload(input) {
|
|
|
10949
10949
|
const fileCount = copySkillTree(skillDir, destination);
|
|
10950
10950
|
return { destination, fileCount };
|
|
10951
10951
|
} finally {
|
|
10952
|
-
|
|
10952
|
+
fs44.rmSync(cloneRoot, { recursive: true, force: true });
|
|
10953
10953
|
}
|
|
10954
10954
|
}
|
|
10955
10955
|
var DEFAULT_BASE, COMMENT_PATTERN;
|
|
@@ -10963,13 +10963,13 @@ var init_skills_source = __esm({
|
|
|
10963
10963
|
});
|
|
10964
10964
|
|
|
10965
10965
|
// src/starter/source-import/detect.ts
|
|
10966
|
-
import
|
|
10967
|
-
import
|
|
10966
|
+
import fs45 from "node:fs";
|
|
10967
|
+
import path53 from "node:path";
|
|
10968
10968
|
function safeReadPackageJson(dir) {
|
|
10969
|
-
const p35 =
|
|
10970
|
-
if (!
|
|
10969
|
+
const p35 = path53.resolve(dir, "package.json");
|
|
10970
|
+
if (!fs45.existsSync(p35)) return null;
|
|
10971
10971
|
try {
|
|
10972
|
-
return JSON.parse(
|
|
10972
|
+
return JSON.parse(fs45.readFileSync(p35, "utf8"));
|
|
10973
10973
|
} catch {
|
|
10974
10974
|
return null;
|
|
10975
10975
|
}
|
|
@@ -10979,10 +10979,10 @@ function detectPackageManager(dir, pkg) {
|
|
|
10979
10979
|
if (pkg?.packageManager?.startsWith("yarn")) return "yarn";
|
|
10980
10980
|
if (pkg?.packageManager?.startsWith("npm")) return "npm";
|
|
10981
10981
|
if (pkg?.packageManager?.startsWith("bun")) return "bun";
|
|
10982
|
-
if (
|
|
10983
|
-
if (
|
|
10984
|
-
if (
|
|
10985
|
-
if (
|
|
10982
|
+
if (fs45.existsSync(path53.resolve(dir, "pnpm-lock.yaml"))) return "pnpm";
|
|
10983
|
+
if (fs45.existsSync(path53.resolve(dir, "yarn.lock"))) return "yarn";
|
|
10984
|
+
if (fs45.existsSync(path53.resolve(dir, "bun.lockb"))) return "bun";
|
|
10985
|
+
if (fs45.existsSync(path53.resolve(dir, "package-lock.json"))) return "npm";
|
|
10986
10986
|
return "unknown";
|
|
10987
10987
|
}
|
|
10988
10988
|
function collectDeps(pkg) {
|
|
@@ -10996,12 +10996,12 @@ function collectDeps(pkg) {
|
|
|
10996
10996
|
}
|
|
10997
10997
|
function looksLikeSkillPayload(rootDir) {
|
|
10998
10998
|
const markers = ["SKILL.md", "skill.md", "skill.json", "skill.yml", "skill.yaml", "prompt.md"];
|
|
10999
|
-
return markers.some((name) =>
|
|
10999
|
+
return markers.some((name) => fs45.existsSync(path53.resolve(rootDir, name)));
|
|
11000
11000
|
}
|
|
11001
11001
|
function detectFramework(rootDir, pkg) {
|
|
11002
11002
|
if (!pkg) {
|
|
11003
11003
|
if (looksLikeSkillPayload(rootDir)) return "skill";
|
|
11004
|
-
if (
|
|
11004
|
+
if (fs45.existsSync(path53.resolve(rootDir, "docs"))) return "docs";
|
|
11005
11005
|
return "unknown";
|
|
11006
11006
|
}
|
|
11007
11007
|
const deps = collectDeps(pkg);
|
|
@@ -11010,8 +11010,8 @@ function detectFramework(rootDir, pkg) {
|
|
|
11010
11010
|
"vite.config.ts",
|
|
11011
11011
|
"vite.config.mjs",
|
|
11012
11012
|
"vite.config.cjs"
|
|
11013
|
-
].some((name) =>
|
|
11014
|
-
if (deps.has("next") ||
|
|
11013
|
+
].some((name) => fs45.existsSync(path53.resolve(rootDir, name)));
|
|
11014
|
+
if (deps.has("next") || fs45.existsSync(path53.resolve(rootDir, "next.config.js")) || fs45.existsSync(path53.resolve(rootDir, "next.config.mjs"))) {
|
|
11015
11015
|
return "next";
|
|
11016
11016
|
}
|
|
11017
11017
|
if (deps.has("vite") || hasViteConfig) return "vite";
|
|
@@ -11037,15 +11037,15 @@ function pickScripts(pkg) {
|
|
|
11037
11037
|
return out;
|
|
11038
11038
|
}
|
|
11039
11039
|
function listEnvFiles(dir) {
|
|
11040
|
-
if (!
|
|
11041
|
-
return
|
|
11040
|
+
if (!fs45.existsSync(dir)) return [];
|
|
11041
|
+
return fs45.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name).filter((name) => name === ".env" || name.startsWith(".env.") || name === ".env.example");
|
|
11042
11042
|
}
|
|
11043
11043
|
function findAppRoot(rootDir, pkg) {
|
|
11044
11044
|
if (pkg) return ".";
|
|
11045
11045
|
const candidates = ["app", "src", "apps", "packages"];
|
|
11046
11046
|
for (const candidate of candidates) {
|
|
11047
|
-
const abs =
|
|
11048
|
-
if (
|
|
11047
|
+
const abs = path53.resolve(rootDir, candidate);
|
|
11048
|
+
if (fs45.existsSync(abs) && fs45.statSync(abs).isDirectory()) {
|
|
11049
11049
|
const child = safeReadPackageJson(abs);
|
|
11050
11050
|
if (child) return candidate;
|
|
11051
11051
|
}
|
|
@@ -11061,12 +11061,12 @@ function computeConfidence(framework, manager, pkg) {
|
|
|
11061
11061
|
return Math.min(1, Number(score.toFixed(2)));
|
|
11062
11062
|
}
|
|
11063
11063
|
function detectSourceShape(rootDir) {
|
|
11064
|
-
if (!
|
|
11064
|
+
if (!fs45.existsSync(rootDir) || !fs45.statSync(rootDir).isDirectory()) {
|
|
11065
11065
|
throw new Error(`Detection target is not a directory: ${rootDir}`);
|
|
11066
11066
|
}
|
|
11067
11067
|
const rootPkg = safeReadPackageJson(rootDir);
|
|
11068
11068
|
const appRootRel = findAppRoot(rootDir, rootPkg);
|
|
11069
|
-
const appRootAbs =
|
|
11069
|
+
const appRootAbs = path53.resolve(rootDir, appRootRel);
|
|
11070
11070
|
const appPkg = appRootRel === "." ? rootPkg : safeReadPackageJson(appRootAbs);
|
|
11071
11071
|
const framework = detectFramework(appRootAbs, appPkg ?? rootPkg);
|
|
11072
11072
|
const packageManager = detectPackageManager(rootDir, rootPkg ?? appPkg);
|
|
@@ -11106,18 +11106,18 @@ var init_detect = __esm({
|
|
|
11106
11106
|
});
|
|
11107
11107
|
|
|
11108
11108
|
// src/starter/source-import/security.ts
|
|
11109
|
-
import
|
|
11110
|
-
import
|
|
11109
|
+
import fs46 from "node:fs";
|
|
11110
|
+
import path54 from "node:path";
|
|
11111
11111
|
function isLikelyTextFile(filename) {
|
|
11112
|
-
const ext =
|
|
11112
|
+
const ext = path54.extname(filename).toLowerCase();
|
|
11113
11113
|
if (!ext) return true;
|
|
11114
11114
|
return TEXT_EXTENSIONS.has(ext);
|
|
11115
11115
|
}
|
|
11116
11116
|
function isSuspiciousBinary(filename) {
|
|
11117
|
-
return SUSPICIOUS_BINARY_EXTENSIONS.has(
|
|
11117
|
+
return SUSPICIOUS_BINARY_EXTENSIONS.has(path54.extname(filename).toLowerCase());
|
|
11118
11118
|
}
|
|
11119
11119
|
function isUnexpectedArchive(filename) {
|
|
11120
|
-
const ext =
|
|
11120
|
+
const ext = path54.extname(filename).toLowerCase();
|
|
11121
11121
|
return ARCHIVE_EXTENSIONS.has(ext) || filename.toLowerCase().endsWith(".tar.gz");
|
|
11122
11122
|
}
|
|
11123
11123
|
function shortExcerpt(line) {
|
|
@@ -11172,19 +11172,19 @@ function walkPayload(root, onFile, limits) {
|
|
|
11172
11172
|
if (!current) break;
|
|
11173
11173
|
let entries;
|
|
11174
11174
|
try {
|
|
11175
|
-
entries =
|
|
11175
|
+
entries = fs46.readdirSync(current, { withFileTypes: true });
|
|
11176
11176
|
} catch {
|
|
11177
11177
|
continue;
|
|
11178
11178
|
}
|
|
11179
11179
|
for (const entry of entries) {
|
|
11180
|
-
const abs =
|
|
11180
|
+
const abs = path54.resolve(current, entry.name);
|
|
11181
11181
|
if (entry.isDirectory()) {
|
|
11182
11182
|
if (entry.name === ".git" || entry.name === "node_modules") continue;
|
|
11183
11183
|
stack.push(abs);
|
|
11184
11184
|
continue;
|
|
11185
11185
|
}
|
|
11186
11186
|
if (!entry.isFile()) continue;
|
|
11187
|
-
const rel =
|
|
11187
|
+
const rel = path54.relative(root, abs);
|
|
11188
11188
|
onFile(abs, rel);
|
|
11189
11189
|
visited += 1;
|
|
11190
11190
|
if (visited >= limits.maxFiles) break;
|
|
@@ -11194,7 +11194,7 @@ function walkPayload(root, onFile, limits) {
|
|
|
11194
11194
|
}
|
|
11195
11195
|
function inspectSourcePayload(input) {
|
|
11196
11196
|
const { payloadRoot } = input;
|
|
11197
|
-
if (!
|
|
11197
|
+
if (!fs46.existsSync(payloadRoot) || !fs46.statSync(payloadRoot).isDirectory()) {
|
|
11198
11198
|
throw new Error(`Inspection target is not a directory: ${payloadRoot}`);
|
|
11199
11199
|
}
|
|
11200
11200
|
const findings = [];
|
|
@@ -11204,7 +11204,7 @@ function inspectSourcePayload(input) {
|
|
|
11204
11204
|
(abs, rel) => {
|
|
11205
11205
|
let size = 0;
|
|
11206
11206
|
try {
|
|
11207
|
-
size =
|
|
11207
|
+
size = fs46.statSync(abs).size;
|
|
11208
11208
|
} catch {
|
|
11209
11209
|
return;
|
|
11210
11210
|
}
|
|
@@ -11213,7 +11213,7 @@ function inspectSourcePayload(input) {
|
|
|
11213
11213
|
category: "suspicious-binary",
|
|
11214
11214
|
severity: "high-risk",
|
|
11215
11215
|
path: rel,
|
|
11216
|
-
message: `Payload ships a precompiled binary (${
|
|
11216
|
+
message: `Payload ships a precompiled binary (${path54.extname(rel)}). Review provenance before use.`
|
|
11217
11217
|
});
|
|
11218
11218
|
return;
|
|
11219
11219
|
}
|
|
@@ -11222,7 +11222,7 @@ function inspectSourcePayload(input) {
|
|
|
11222
11222
|
category: "unexpected-archive",
|
|
11223
11223
|
severity: "caution",
|
|
11224
11224
|
path: rel,
|
|
11225
|
-
message: `Payload ships an archive (${
|
|
11225
|
+
message: `Payload ships an archive (${path54.extname(rel)}) \u2014 expand and review contents before use.`
|
|
11226
11226
|
});
|
|
11227
11227
|
return;
|
|
11228
11228
|
}
|
|
@@ -11230,12 +11230,12 @@ function inspectSourcePayload(input) {
|
|
|
11230
11230
|
if (bytesInspected + Math.min(size, MAX_BYTES_PER_FILE) > MAX_TOTAL_BYTES) return;
|
|
11231
11231
|
let buf;
|
|
11232
11232
|
try {
|
|
11233
|
-
const handle =
|
|
11233
|
+
const handle = fs46.openSync(abs, "r");
|
|
11234
11234
|
try {
|
|
11235
11235
|
buf = Buffer.alloc(Math.min(size, MAX_BYTES_PER_FILE));
|
|
11236
|
-
|
|
11236
|
+
fs46.readSync(handle, buf, 0, buf.length, 0);
|
|
11237
11237
|
} finally {
|
|
11238
|
-
|
|
11238
|
+
fs46.closeSync(handle);
|
|
11239
11239
|
}
|
|
11240
11240
|
} catch {
|
|
11241
11241
|
return;
|
|
@@ -11444,18 +11444,18 @@ var init_security = __esm({
|
|
|
11444
11444
|
});
|
|
11445
11445
|
|
|
11446
11446
|
// src/starter/source-import/plan.ts
|
|
11447
|
-
import
|
|
11448
|
-
import
|
|
11447
|
+
import fs47 from "node:fs";
|
|
11448
|
+
import path55 from "node:path";
|
|
11449
11449
|
function generateImportId() {
|
|
11450
11450
|
return `si-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`;
|
|
11451
11451
|
}
|
|
11452
11452
|
function destinationState(absDest) {
|
|
11453
|
-
if (!
|
|
11454
|
-
const stats =
|
|
11453
|
+
if (!fs47.existsSync(absDest)) return { exists: false, nonEmpty: false };
|
|
11454
|
+
const stats = fs47.statSync(absDest);
|
|
11455
11455
|
if (!stats.isDirectory()) {
|
|
11456
11456
|
throw new Error(`Destination is not a directory: ${absDest}`);
|
|
11457
11457
|
}
|
|
11458
|
-
const entries =
|
|
11458
|
+
const entries = fs47.readdirSync(absDest);
|
|
11459
11459
|
return { exists: true, nonEmpty: entries.length > 0 };
|
|
11460
11460
|
}
|
|
11461
11461
|
function describeSource(probe) {
|
|
@@ -11465,7 +11465,7 @@ function describeSource(probe) {
|
|
|
11465
11465
|
return `skill ${probe.skillId}@${probe.version} (skills.sh)`;
|
|
11466
11466
|
}
|
|
11467
11467
|
function buildSourceImportPlan(input) {
|
|
11468
|
-
const absDest =
|
|
11468
|
+
const absDest = path55.resolve(input.destination);
|
|
11469
11469
|
const state = destinationState(absDest);
|
|
11470
11470
|
const payloadPath = "imported";
|
|
11471
11471
|
const warnings = [...input.probe.warnings];
|
|
@@ -11578,8 +11578,8 @@ var init_plan = __esm({
|
|
|
11578
11578
|
});
|
|
11579
11579
|
|
|
11580
11580
|
// src/starter/source-import/summarize.ts
|
|
11581
|
-
import
|
|
11582
|
-
import
|
|
11581
|
+
import fs48 from "node:fs";
|
|
11582
|
+
import path56 from "node:path";
|
|
11583
11583
|
function sourceHeading(manifest) {
|
|
11584
11584
|
const src = manifest.source;
|
|
11585
11585
|
if (src.kind === "github-repo") {
|
|
@@ -11634,7 +11634,7 @@ function nextStepsSection(manifest) {
|
|
|
11634
11634
|
}
|
|
11635
11635
|
function writeImportSummary(input) {
|
|
11636
11636
|
const { forkPath, summaryRelativePath, manifest } = input;
|
|
11637
|
-
const summaryPath =
|
|
11637
|
+
const summaryPath = path56.resolve(forkPath, summaryRelativePath);
|
|
11638
11638
|
const body = [
|
|
11639
11639
|
`# Source Import Summary`,
|
|
11640
11640
|
``,
|
|
@@ -11664,8 +11664,8 @@ function writeImportSummary(input) {
|
|
|
11664
11664
|
`Generated by the Growthub Source Import Agent. Canonical manifest lives at \`.growthub-fork/source-import.json\`.`,
|
|
11665
11665
|
``
|
|
11666
11666
|
].join("\n");
|
|
11667
|
-
|
|
11668
|
-
|
|
11667
|
+
fs48.mkdirSync(path56.dirname(summaryPath), { recursive: true });
|
|
11668
|
+
fs48.writeFileSync(summaryPath, body, "utf8");
|
|
11669
11669
|
return summaryPath;
|
|
11670
11670
|
}
|
|
11671
11671
|
var init_summarize = __esm({
|
|
@@ -11675,16 +11675,16 @@ var init_summarize = __esm({
|
|
|
11675
11675
|
});
|
|
11676
11676
|
|
|
11677
11677
|
// src/starter/source-import/materialize.ts
|
|
11678
|
-
import
|
|
11678
|
+
import fs49 from "node:fs";
|
|
11679
11679
|
import os11 from "node:os";
|
|
11680
|
-
import
|
|
11680
|
+
import path57 from "node:path";
|
|
11681
11681
|
function resolveSourceKind(probe) {
|
|
11682
11682
|
return probe.kind === "github-repo" ? "github-repo" : "skills-skill";
|
|
11683
11683
|
}
|
|
11684
11684
|
function stagingDirFor(forkPath) {
|
|
11685
|
-
return
|
|
11685
|
+
return path57.join(
|
|
11686
11686
|
os11.tmpdir(),
|
|
11687
|
-
`growthub-source-import-${
|
|
11687
|
+
`growthub-source-import-${path57.basename(forkPath)}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`
|
|
11688
11688
|
);
|
|
11689
11689
|
}
|
|
11690
11690
|
async function fetchPayload(probe, stagingDir, opts) {
|
|
@@ -11716,18 +11716,18 @@ async function fetchPayload(probe, stagingDir, opts) {
|
|
|
11716
11716
|
return { payloadRoot: stagingDir };
|
|
11717
11717
|
}
|
|
11718
11718
|
function movePayloadIntoFork(payloadRoot, forkPath, payloadRelativePath) {
|
|
11719
|
-
const target =
|
|
11720
|
-
if (
|
|
11721
|
-
|
|
11719
|
+
const target = path57.resolve(forkPath, payloadRelativePath);
|
|
11720
|
+
if (fs49.existsSync(target)) {
|
|
11721
|
+
fs49.rmSync(target, { recursive: true, force: true });
|
|
11722
11722
|
}
|
|
11723
|
-
|
|
11724
|
-
|
|
11723
|
+
fs49.mkdirSync(path57.dirname(target), { recursive: true });
|
|
11724
|
+
fs49.renameSync(payloadRoot, target);
|
|
11725
11725
|
return target;
|
|
11726
11726
|
}
|
|
11727
11727
|
function writeManifest(forkPath, manifest) {
|
|
11728
|
-
const p35 =
|
|
11729
|
-
|
|
11730
|
-
|
|
11728
|
+
const p35 = path57.resolve(forkPath, MANIFEST_RELATIVE_PATH);
|
|
11729
|
+
fs49.mkdirSync(path57.dirname(p35), { recursive: true });
|
|
11730
|
+
fs49.writeFileSync(p35, JSON.stringify(manifest, null, 2) + "\n", "utf8");
|
|
11731
11731
|
return p35;
|
|
11732
11732
|
}
|
|
11733
11733
|
function assertConfirmationsSatisfied(plan, confirmations) {
|
|
@@ -11767,7 +11767,7 @@ async function materializeImportPlan(input) {
|
|
|
11767
11767
|
requireSkillAcknowledgement: sourceKind === "skills-skill"
|
|
11768
11768
|
});
|
|
11769
11769
|
if (security.blocked) {
|
|
11770
|
-
|
|
11770
|
+
fs49.rmSync(fetchResult.payloadRoot, { recursive: true, force: true });
|
|
11771
11771
|
throw new Error(
|
|
11772
11772
|
`Security inspection blocked the fetched payload: ${security.summaryLines[0] ?? "blocking finding"}`
|
|
11773
11773
|
);
|
|
@@ -11898,26 +11898,26 @@ var init_materialize = __esm({
|
|
|
11898
11898
|
});
|
|
11899
11899
|
|
|
11900
11900
|
// src/starter/source-import/agent.ts
|
|
11901
|
-
import
|
|
11902
|
-
import
|
|
11901
|
+
import fs50 from "node:fs";
|
|
11902
|
+
import path58 from "node:path";
|
|
11903
11903
|
function resolveJobsDir() {
|
|
11904
|
-
return
|
|
11904
|
+
return path58.resolve(resolveKitForksHomeDir(), "source-import-jobs");
|
|
11905
11905
|
}
|
|
11906
11906
|
function resolveJobPath2(jobId) {
|
|
11907
|
-
return
|
|
11907
|
+
return path58.resolve(resolveJobsDir(), `${jobId}.json`);
|
|
11908
11908
|
}
|
|
11909
11909
|
function generateJobId2() {
|
|
11910
11910
|
return `sij-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`;
|
|
11911
11911
|
}
|
|
11912
11912
|
function writeJob2(job) {
|
|
11913
11913
|
const p35 = resolveJobPath2(job.jobId);
|
|
11914
|
-
|
|
11915
|
-
|
|
11914
|
+
fs50.mkdirSync(path58.dirname(p35), { recursive: true });
|
|
11915
|
+
fs50.writeFileSync(p35, JSON.stringify(job, null, 2) + "\n", "utf8");
|
|
11916
11916
|
}
|
|
11917
11917
|
function readJobFile(p35) {
|
|
11918
|
-
if (!
|
|
11918
|
+
if (!fs50.existsSync(p35)) return null;
|
|
11919
11919
|
try {
|
|
11920
|
-
return JSON.parse(
|
|
11920
|
+
return JSON.parse(fs50.readFileSync(p35, "utf8"));
|
|
11921
11921
|
} catch {
|
|
11922
11922
|
return null;
|
|
11923
11923
|
}
|
|
@@ -11927,7 +11927,7 @@ function patchJob2(jobId, status, patch = {}) {
|
|
|
11927
11927
|
const job = readJobFile(p35);
|
|
11928
11928
|
if (!job) return null;
|
|
11929
11929
|
const updated = { ...job, ...patch, status };
|
|
11930
|
-
|
|
11930
|
+
fs50.writeFileSync(p35, JSON.stringify(updated, null, 2) + "\n", "utf8");
|
|
11931
11931
|
return updated;
|
|
11932
11932
|
}
|
|
11933
11933
|
function getSourceImportJob(jobId) {
|
|
@@ -11946,7 +11946,7 @@ async function probeAndPlan(input, destination) {
|
|
|
11946
11946
|
}
|
|
11947
11947
|
async function runSourceImportJob(input) {
|
|
11948
11948
|
const jobId = generateJobId2();
|
|
11949
|
-
const destination =
|
|
11949
|
+
const destination = path58.resolve(input.out);
|
|
11950
11950
|
const sourceKind = input.source.kind;
|
|
11951
11951
|
const initial = {
|
|
11952
11952
|
jobId,
|
|
@@ -12103,20 +12103,20 @@ __export(source_import_discovery_exports, {
|
|
|
12103
12103
|
});
|
|
12104
12104
|
import * as p33 from "@clack/prompts";
|
|
12105
12105
|
import pc47 from "picocolors";
|
|
12106
|
-
import
|
|
12107
|
-
import
|
|
12106
|
+
import fs53 from "node:fs";
|
|
12107
|
+
import path60 from "node:path";
|
|
12108
12108
|
import { pathToFileURL as pathToFileURL4 } from "node:url";
|
|
12109
12109
|
function slugifyWorkspaceName(input) {
|
|
12110
12110
|
const slug = input.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
12111
12111
|
return slug || "custom-workspace";
|
|
12112
12112
|
}
|
|
12113
|
-
function
|
|
12113
|
+
function terminalLink4(label, href) {
|
|
12114
12114
|
return `\x1B]8;;${href}\x07${label}\x1B]8;;\x07`;
|
|
12115
12115
|
}
|
|
12116
12116
|
function folderOpenLabel3(folderPath) {
|
|
12117
12117
|
const href = pathToFileURL4(folderPath).href;
|
|
12118
12118
|
const label = process.platform === "darwin" ? "Open in Finder" : process.platform === "win32" ? "Open in Explorer" : "Open folder";
|
|
12119
|
-
return
|
|
12119
|
+
return terminalLink4(label, href);
|
|
12120
12120
|
}
|
|
12121
12121
|
function defaultWorkspaceFolderName(input) {
|
|
12122
12122
|
if (input.kind === "github-repo") {
|
|
@@ -12132,10 +12132,10 @@ async function promptForInteractiveWorkspacePath(input) {
|
|
|
12132
12132
|
});
|
|
12133
12133
|
if (p33.isCancel(raw) || !raw) return null;
|
|
12134
12134
|
const trimmed = String(raw).trim();
|
|
12135
|
-
const expanded = trimmed.startsWith("~/") ?
|
|
12136
|
-
const resolved =
|
|
12137
|
-
if (
|
|
12138
|
-
const finalPath =
|
|
12135
|
+
const expanded = trimmed.startsWith("~/") ? path60.join(process.env.HOME ?? "~", trimmed.slice(2)) : trimmed;
|
|
12136
|
+
const resolved = path60.resolve(expanded);
|
|
12137
|
+
if (fs53.existsSync(resolved) && fs53.statSync(resolved).isDirectory()) {
|
|
12138
|
+
const finalPath = path60.join(resolved, suggestedName);
|
|
12139
12139
|
p33.note(
|
|
12140
12140
|
[
|
|
12141
12141
|
`You selected an existing folder: ${resolved}`,
|
|
@@ -12403,16 +12403,154 @@ var init_source_import_discovery = __esm({
|
|
|
12403
12403
|
});
|
|
12404
12404
|
|
|
12405
12405
|
// src/index.ts
|
|
12406
|
-
init_onboard();
|
|
12407
|
-
init_doctor();
|
|
12408
12406
|
import { Command } from "commander";
|
|
12409
12407
|
import * as p34 from "@clack/prompts";
|
|
12410
12408
|
import pc48 from "picocolors";
|
|
12411
|
-
import
|
|
12412
|
-
import
|
|
12409
|
+
import fs54 from "node:fs";
|
|
12410
|
+
import path61 from "node:path";
|
|
12413
12411
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
12414
12412
|
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
12415
12413
|
|
|
12414
|
+
// src/analytics/posthog.ts
|
|
12415
|
+
init_home();
|
|
12416
|
+
init_session_store();
|
|
12417
|
+
import crypto from "node:crypto";
|
|
12418
|
+
import fs2 from "node:fs";
|
|
12419
|
+
import path4 from "node:path";
|
|
12420
|
+
function resolveHost() {
|
|
12421
|
+
return (process.env.GROWTHUB_POSTHOG_HOST ?? "").trim() || (process.env.NEXT_PUBLIC_POSTHOG_HOST ?? "").trim() || "https://us.posthog.com";
|
|
12422
|
+
}
|
|
12423
|
+
function apiKey() {
|
|
12424
|
+
return (process.env.GROWTHUB_POSTHOG_API_KEY ?? "").trim() || (process.env.NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN ?? "").trim();
|
|
12425
|
+
}
|
|
12426
|
+
function debugEnabled() {
|
|
12427
|
+
return process.env.GROWTHUB_POSTHOG_DEBUG === "true";
|
|
12428
|
+
}
|
|
12429
|
+
function resolveHostedIdentity() {
|
|
12430
|
+
if (_hostedUserId !== null || _hostedEmail !== null) {
|
|
12431
|
+
return { userId: _hostedUserId, email: _hostedEmail };
|
|
12432
|
+
}
|
|
12433
|
+
const session = readSession();
|
|
12434
|
+
if (session && !isSessionExpired(session)) {
|
|
12435
|
+
if (typeof session.userId === "string" && session.userId.length > 0) {
|
|
12436
|
+
_hostedUserId = session.userId;
|
|
12437
|
+
}
|
|
12438
|
+
if (typeof session.email === "string" && session.email.length > 0) {
|
|
12439
|
+
_hostedEmail = session.email;
|
|
12440
|
+
}
|
|
12441
|
+
}
|
|
12442
|
+
return { userId: _hostedUserId, email: _hostedEmail };
|
|
12443
|
+
}
|
|
12444
|
+
function isDisabled() {
|
|
12445
|
+
return !apiKey() || process.env.GROWTHUB_TELEMETRY_DISABLED === "true" || process.env.DO_NOT_TRACK === "1" || process.env.CI === "true";
|
|
12446
|
+
}
|
|
12447
|
+
var _machineId = null;
|
|
12448
|
+
var _isFirstRun = false;
|
|
12449
|
+
var _hostedUserId = null;
|
|
12450
|
+
var _hostedEmail = null;
|
|
12451
|
+
function analyticsIdPath() {
|
|
12452
|
+
return path4.resolve(resolvePaperclipHomeDir(), "analytics-machine-id");
|
|
12453
|
+
}
|
|
12454
|
+
function ensureMachineId() {
|
|
12455
|
+
if (_machineId !== null) return _machineId;
|
|
12456
|
+
const idPath = analyticsIdPath();
|
|
12457
|
+
let resolved = "anon";
|
|
12458
|
+
try {
|
|
12459
|
+
if (fs2.existsSync(idPath)) {
|
|
12460
|
+
const stored = fs2.readFileSync(idPath, "utf-8").trim();
|
|
12461
|
+
if (stored.length > 0) {
|
|
12462
|
+
_machineId = stored;
|
|
12463
|
+
return stored;
|
|
12464
|
+
}
|
|
12465
|
+
}
|
|
12466
|
+
_isFirstRun = true;
|
|
12467
|
+
const fresh = crypto.randomUUID();
|
|
12468
|
+
fs2.mkdirSync(path4.dirname(idPath), { recursive: true });
|
|
12469
|
+
fs2.writeFileSync(idPath, fresh, "utf-8");
|
|
12470
|
+
resolved = fresh;
|
|
12471
|
+
} catch {
|
|
12472
|
+
resolved = "anon";
|
|
12473
|
+
}
|
|
12474
|
+
_machineId = resolved;
|
|
12475
|
+
return resolved;
|
|
12476
|
+
}
|
|
12477
|
+
function setHostedUserId(userId) {
|
|
12478
|
+
_hostedUserId = userId;
|
|
12479
|
+
}
|
|
12480
|
+
function track(event, properties) {
|
|
12481
|
+
if (isDisabled()) return;
|
|
12482
|
+
const distinctId = ensureMachineId();
|
|
12483
|
+
const key = apiKey();
|
|
12484
|
+
const host = resolveHost();
|
|
12485
|
+
const identity = resolveHostedIdentity();
|
|
12486
|
+
const body = JSON.stringify({
|
|
12487
|
+
api_key: key,
|
|
12488
|
+
event,
|
|
12489
|
+
distinct_id: distinctId,
|
|
12490
|
+
properties: {
|
|
12491
|
+
...properties,
|
|
12492
|
+
$lib: "growthub-cli",
|
|
12493
|
+
platform: process.platform,
|
|
12494
|
+
...identity.userId !== null ? { hosted_user_id: identity.userId } : {},
|
|
12495
|
+
...identity.email !== null ? { hosted_email: identity.email } : {}
|
|
12496
|
+
},
|
|
12497
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
12498
|
+
});
|
|
12499
|
+
fetch(`${host}/capture/`, {
|
|
12500
|
+
method: "POST",
|
|
12501
|
+
headers: { "Content-Type": "application/json" },
|
|
12502
|
+
body,
|
|
12503
|
+
signal: AbortSignal.timeout(5e3)
|
|
12504
|
+
}).then(async (response) => {
|
|
12505
|
+
if (response.ok) {
|
|
12506
|
+
if (debugEnabled()) {
|
|
12507
|
+
console.error(
|
|
12508
|
+
`[posthog] captured ${event} (${response.status}) user=${identity.userId ?? "none"} email=${identity.email ?? "none"}`
|
|
12509
|
+
);
|
|
12510
|
+
}
|
|
12511
|
+
return;
|
|
12512
|
+
}
|
|
12513
|
+
if (debugEnabled()) {
|
|
12514
|
+
const text69 = await response.text().catch(() => "");
|
|
12515
|
+
console.error(`[posthog] failed ${event} (${response.status}) ${text69.slice(0, 240)}`);
|
|
12516
|
+
}
|
|
12517
|
+
}).catch((err) => {
|
|
12518
|
+
if (debugEnabled()) {
|
|
12519
|
+
console.error(`[posthog] error ${event}: ${err instanceof Error ? err.message : String(err)}`);
|
|
12520
|
+
}
|
|
12521
|
+
});
|
|
12522
|
+
}
|
|
12523
|
+
function trackCliStart() {
|
|
12524
|
+
ensureMachineId();
|
|
12525
|
+
if (_isFirstRun) {
|
|
12526
|
+
track("cli_first_run");
|
|
12527
|
+
}
|
|
12528
|
+
}
|
|
12529
|
+
var ACTIVATION_URL = "https://www.growthub.ai/";
|
|
12530
|
+
function isAlreadyConnected() {
|
|
12531
|
+
const session = readSession();
|
|
12532
|
+
if (!session) return false;
|
|
12533
|
+
return !isSessionExpired(session);
|
|
12534
|
+
}
|
|
12535
|
+
function terminalLink(label, href) {
|
|
12536
|
+
return `\x1B]8;;${href}\x07${label}\x1B]8;;\x07`;
|
|
12537
|
+
}
|
|
12538
|
+
function printActivationNudge(trigger) {
|
|
12539
|
+
if (isAlreadyConnected()) return;
|
|
12540
|
+
track("email_capture_shown", { trigger });
|
|
12541
|
+
const link = terminalLink("growthub.ai \u2014 first month $1", ACTIVATION_URL);
|
|
12542
|
+
console.log("");
|
|
12543
|
+
console.log(` \x1B[2m\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\x1B[0m`);
|
|
12544
|
+
console.log(` \x1B[36m\u2726\x1B[0m Fork anything. Stay current. Ship faster.`);
|
|
12545
|
+
console.log(` \x1B[2mActivate Growthub \u2192\x1B[0m \x1B[36m${link}\x1B[0m`);
|
|
12546
|
+
console.log(` \x1B[2m\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\x1B[0m`);
|
|
12547
|
+
console.log("");
|
|
12548
|
+
}
|
|
12549
|
+
|
|
12550
|
+
// src/index.ts
|
|
12551
|
+
init_onboard();
|
|
12552
|
+
init_doctor();
|
|
12553
|
+
|
|
12416
12554
|
// src/commands/env.ts
|
|
12417
12555
|
init_store();
|
|
12418
12556
|
init_env();
|
|
@@ -13070,8 +13208,8 @@ function printItemCompleted(item) {
|
|
|
13070
13208
|
const changes = Array.isArray(item.changes) ? item.changes : [];
|
|
13071
13209
|
const entries = changes.map((changeRaw) => asRecord(changeRaw)).filter((change) => Boolean(change)).map((change) => {
|
|
13072
13210
|
const kind = asString(change.kind, "update");
|
|
13073
|
-
const
|
|
13074
|
-
return `${kind} ${
|
|
13211
|
+
const path62 = asString(change.path, "unknown");
|
|
13212
|
+
return `${kind} ${path62}`;
|
|
13075
13213
|
});
|
|
13076
13214
|
const preview = entries.length > 0 ? entries.slice(0, 6).join(", ") : "none";
|
|
13077
13215
|
const more = entries.length > 6 ? ` (+${entries.length - 6} more)` : "";
|
|
@@ -13958,27 +14096,27 @@ import pc17 from "picocolors";
|
|
|
13958
14096
|
|
|
13959
14097
|
// src/client/context.ts
|
|
13960
14098
|
init_home();
|
|
13961
|
-
import
|
|
13962
|
-
import
|
|
14099
|
+
import fs12 from "node:fs";
|
|
14100
|
+
import path12 from "node:path";
|
|
13963
14101
|
var DEFAULT_CONTEXT_BASENAME = "context.json";
|
|
13964
14102
|
var DEFAULT_PROFILE = "default";
|
|
13965
14103
|
function findContextFileFromAncestors(startDir) {
|
|
13966
|
-
const absoluteStartDir =
|
|
14104
|
+
const absoluteStartDir = path12.resolve(startDir);
|
|
13967
14105
|
let currentDir = absoluteStartDir;
|
|
13968
14106
|
while (true) {
|
|
13969
|
-
const candidate =
|
|
13970
|
-
if (
|
|
14107
|
+
const candidate = path12.resolve(currentDir, ".paperclip", DEFAULT_CONTEXT_BASENAME);
|
|
14108
|
+
if (fs12.existsSync(candidate)) {
|
|
13971
14109
|
return candidate;
|
|
13972
14110
|
}
|
|
13973
|
-
const nextDir =
|
|
14111
|
+
const nextDir = path12.resolve(currentDir, "..");
|
|
13974
14112
|
if (nextDir === currentDir) break;
|
|
13975
14113
|
currentDir = nextDir;
|
|
13976
14114
|
}
|
|
13977
14115
|
return null;
|
|
13978
14116
|
}
|
|
13979
14117
|
function resolveContextPath(overridePath) {
|
|
13980
|
-
if (overridePath) return
|
|
13981
|
-
if (process.env.PAPERCLIP_CONTEXT) return
|
|
14118
|
+
if (overridePath) return path12.resolve(overridePath);
|
|
14119
|
+
if (process.env.PAPERCLIP_CONTEXT) return path12.resolve(process.env.PAPERCLIP_CONTEXT);
|
|
13982
14120
|
return findContextFileFromAncestors(process.cwd()) ?? resolveDefaultContextPath();
|
|
13983
14121
|
}
|
|
13984
14122
|
function defaultClientContext() {
|
|
@@ -13990,23 +14128,23 @@ function defaultClientContext() {
|
|
|
13990
14128
|
}
|
|
13991
14129
|
};
|
|
13992
14130
|
}
|
|
13993
|
-
function
|
|
14131
|
+
function parseJson3(filePath) {
|
|
13994
14132
|
try {
|
|
13995
|
-
return JSON.parse(
|
|
14133
|
+
return JSON.parse(fs12.readFileSync(filePath, "utf-8"));
|
|
13996
14134
|
} catch (err) {
|
|
13997
14135
|
throw new Error(`Failed to parse JSON at ${filePath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
13998
14136
|
}
|
|
13999
14137
|
}
|
|
14000
|
-
function
|
|
14138
|
+
function toStringOrUndefined2(value) {
|
|
14001
14139
|
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
14002
14140
|
}
|
|
14003
14141
|
function normalizeProfile(value) {
|
|
14004
14142
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return {};
|
|
14005
14143
|
const profile = value;
|
|
14006
14144
|
return {
|
|
14007
|
-
apiBase:
|
|
14008
|
-
companyId:
|
|
14009
|
-
apiKeyEnvVarName:
|
|
14145
|
+
apiBase: toStringOrUndefined2(profile.apiBase),
|
|
14146
|
+
companyId: toStringOrUndefined2(profile.companyId),
|
|
14147
|
+
apiKeyEnvVarName: toStringOrUndefined2(profile.apiKeyEnvVarName)
|
|
14010
14148
|
};
|
|
14011
14149
|
}
|
|
14012
14150
|
function normalizeContext(raw) {
|
|
@@ -14015,7 +14153,7 @@ function normalizeContext(raw) {
|
|
|
14015
14153
|
}
|
|
14016
14154
|
const record = raw;
|
|
14017
14155
|
const version = record.version === 1 ? 1 : 1;
|
|
14018
|
-
const currentProfile =
|
|
14156
|
+
const currentProfile = toStringOrUndefined2(record.currentProfile) ?? DEFAULT_PROFILE;
|
|
14019
14157
|
const rawProfiles = record.profiles;
|
|
14020
14158
|
const profiles = {};
|
|
14021
14159
|
if (typeof rawProfiles === "object" && rawProfiles !== null && !Array.isArray(rawProfiles)) {
|
|
@@ -14038,18 +14176,18 @@ function normalizeContext(raw) {
|
|
|
14038
14176
|
}
|
|
14039
14177
|
function readContext(contextPath) {
|
|
14040
14178
|
const filePath = resolveContextPath(contextPath);
|
|
14041
|
-
if (!
|
|
14179
|
+
if (!fs12.existsSync(filePath)) {
|
|
14042
14180
|
return defaultClientContext();
|
|
14043
14181
|
}
|
|
14044
|
-
const raw =
|
|
14182
|
+
const raw = parseJson3(filePath);
|
|
14045
14183
|
return normalizeContext(raw);
|
|
14046
14184
|
}
|
|
14047
14185
|
function writeContext(context, contextPath) {
|
|
14048
14186
|
const filePath = resolveContextPath(contextPath);
|
|
14049
|
-
const dir =
|
|
14050
|
-
|
|
14187
|
+
const dir = path12.dirname(filePath);
|
|
14188
|
+
fs12.mkdirSync(dir, { recursive: true });
|
|
14051
14189
|
const normalized = normalizeContext(context);
|
|
14052
|
-
|
|
14190
|
+
fs12.writeFileSync(filePath, `${JSON.stringify(normalized, null, 2)}
|
|
14053
14191
|
`, { mode: 384 });
|
|
14054
14192
|
}
|
|
14055
14193
|
function upsertProfile(profileName, patch, contextPath) {
|
|
@@ -14101,14 +14239,14 @@ function resolveCommandContext(options, opts) {
|
|
|
14101
14239
|
const context = readContext(options.context);
|
|
14102
14240
|
const { name: profileName, profile } = resolveProfile(context, options.profile);
|
|
14103
14241
|
const apiBase = options.apiBase?.trim() || process.env.PAPERCLIP_API_URL?.trim() || profile.apiBase || inferApiBaseFromConfig(options.config);
|
|
14104
|
-
const
|
|
14242
|
+
const apiKey2 = options.apiKey?.trim() || process.env.PAPERCLIP_API_KEY?.trim() || readKeyFromProfileEnv(profile);
|
|
14105
14243
|
const companyId = options.companyId?.trim() || process.env.PAPERCLIP_COMPANY_ID?.trim() || profile.companyId;
|
|
14106
14244
|
if (opts?.requireCompany && !companyId) {
|
|
14107
14245
|
throw new Error(
|
|
14108
14246
|
"Company ID is required. Pass --company-id, set PAPERCLIP_COMPANY_ID, or set context profile companyId via `growthub context set`."
|
|
14109
14247
|
);
|
|
14110
14248
|
}
|
|
14111
|
-
const api = new PaperclipApiClient({ apiBase, apiKey });
|
|
14249
|
+
const api = new PaperclipApiClient({ apiBase, apiKey: apiKey2 });
|
|
14112
14250
|
return {
|
|
14113
14251
|
api,
|
|
14114
14252
|
companyId,
|
|
@@ -14468,12 +14606,12 @@ init_run();
|
|
|
14468
14606
|
init_auth_bootstrap_ceo();
|
|
14469
14607
|
|
|
14470
14608
|
// src/commands/auth-login.ts
|
|
14471
|
-
init_store();
|
|
14472
|
-
init_env();
|
|
14473
14609
|
import os3 from "node:os";
|
|
14474
14610
|
import * as p14 from "@clack/prompts";
|
|
14475
14611
|
import pc19 from "picocolors";
|
|
14476
14612
|
import open from "open";
|
|
14613
|
+
init_store();
|
|
14614
|
+
init_env();
|
|
14477
14615
|
|
|
14478
14616
|
// src/auth/login-flow.ts
|
|
14479
14617
|
import { createServer } from "node:http";
|
|
@@ -14667,11 +14805,11 @@ init_session_store();
|
|
|
14667
14805
|
|
|
14668
14806
|
// src/auth/overlay-store.ts
|
|
14669
14807
|
init_paths();
|
|
14670
|
-
import
|
|
14671
|
-
import
|
|
14808
|
+
import fs13 from "node:fs";
|
|
14809
|
+
import path13 from "node:path";
|
|
14672
14810
|
function parseJson4(filePath) {
|
|
14673
14811
|
try {
|
|
14674
|
-
return JSON.parse(
|
|
14812
|
+
return JSON.parse(fs13.readFileSync(filePath, "utf-8"));
|
|
14675
14813
|
} catch (err) {
|
|
14676
14814
|
throw new Error(
|
|
14677
14815
|
`Failed to parse hosted overlay at ${filePath}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -14735,27 +14873,27 @@ function normalizeOverlay(raw) {
|
|
|
14735
14873
|
}
|
|
14736
14874
|
function readHostedOverlay() {
|
|
14737
14875
|
const filePath = resolveHostedOverlayPath();
|
|
14738
|
-
if (!
|
|
14876
|
+
if (!fs13.existsSync(filePath)) return null;
|
|
14739
14877
|
return normalizeOverlay(parseJson4(filePath));
|
|
14740
14878
|
}
|
|
14741
14879
|
function writeHostedOverlay(overlay) {
|
|
14742
14880
|
const filePath = resolveHostedOverlayPath();
|
|
14743
|
-
|
|
14744
|
-
|
|
14881
|
+
fs13.mkdirSync(resolveProfilesDir(), { recursive: true });
|
|
14882
|
+
fs13.writeFileSync(filePath, `${JSON.stringify(overlay, null, 2)}
|
|
14745
14883
|
`, { mode: 384 });
|
|
14746
14884
|
try {
|
|
14747
|
-
|
|
14885
|
+
fs13.chmodSync(filePath, 384);
|
|
14748
14886
|
} catch {
|
|
14749
14887
|
}
|
|
14750
14888
|
}
|
|
14751
14889
|
function clearHostedOverlay() {
|
|
14752
14890
|
const filePath = resolveHostedOverlayPath();
|
|
14753
|
-
if (!
|
|
14754
|
-
|
|
14891
|
+
if (!fs13.existsSync(filePath)) return false;
|
|
14892
|
+
fs13.rmSync(filePath, { force: true });
|
|
14755
14893
|
return true;
|
|
14756
14894
|
}
|
|
14757
14895
|
function describeHostedOverlayPath() {
|
|
14758
|
-
return
|
|
14896
|
+
return path13.resolve(resolveHostedOverlayPath());
|
|
14759
14897
|
}
|
|
14760
14898
|
function seedHostedOverlayFromSession(input) {
|
|
14761
14899
|
return {
|
|
@@ -14779,8 +14917,8 @@ function seedHostedOverlayFromSession(input) {
|
|
|
14779
14917
|
// src/auth/effective-profile.ts
|
|
14780
14918
|
init_store();
|
|
14781
14919
|
init_home();
|
|
14782
|
-
import
|
|
14783
|
-
import
|
|
14920
|
+
import fs14 from "node:fs";
|
|
14921
|
+
import path14 from "node:path";
|
|
14784
14922
|
init_session_store();
|
|
14785
14923
|
init_paths();
|
|
14786
14924
|
function toLocalWorkspaceView(configPath, config) {
|
|
@@ -14890,10 +15028,10 @@ function computeEffectiveProfile(opts = {}) {
|
|
|
14890
15028
|
}
|
|
14891
15029
|
function writeEffectiveProfileSnapshot(profile) {
|
|
14892
15030
|
const filePath = resolveEffectiveProfilePath();
|
|
14893
|
-
|
|
14894
|
-
|
|
15031
|
+
fs14.mkdirSync(resolveProfilesDir(), { recursive: true });
|
|
15032
|
+
fs14.writeFileSync(filePath, `${JSON.stringify(profile, null, 2)}
|
|
14895
15033
|
`, { mode: 384 });
|
|
14896
|
-
return
|
|
15034
|
+
return path14.resolve(filePath);
|
|
14897
15035
|
}
|
|
14898
15036
|
|
|
14899
15037
|
// src/commands/auth-login.ts
|
|
@@ -15052,6 +15190,8 @@ async function authLogin(opts) {
|
|
|
15052
15190
|
writeHostedOverlay(overlay);
|
|
15053
15191
|
const effective = computeEffectiveProfile({ configPath });
|
|
15054
15192
|
writeEffectiveProfileSnapshot(effective);
|
|
15193
|
+
if (result.userId) setHostedUserId(result.userId);
|
|
15194
|
+
track("growthub_auth_connected", { has_org: Boolean(result.orgId) });
|
|
15055
15195
|
if (opts.json) {
|
|
15056
15196
|
console.log(
|
|
15057
15197
|
JSON.stringify(
|
|
@@ -15414,7 +15554,7 @@ init_src2();
|
|
|
15414
15554
|
init_home();
|
|
15415
15555
|
init_store();
|
|
15416
15556
|
init_banner();
|
|
15417
|
-
import
|
|
15557
|
+
import path15 from "node:path";
|
|
15418
15558
|
import * as p15 from "@clack/prompts";
|
|
15419
15559
|
import pc21 from "picocolors";
|
|
15420
15560
|
function resolveConnectionString(configPath) {
|
|
@@ -15438,7 +15578,7 @@ function normalizeRetentionDays(value, fallback) {
|
|
|
15438
15578
|
return candidate;
|
|
15439
15579
|
}
|
|
15440
15580
|
function resolveBackupDir(raw) {
|
|
15441
|
-
return
|
|
15581
|
+
return path15.resolve(expandHomePrefix(raw.trim()));
|
|
15442
15582
|
}
|
|
15443
15583
|
async function dbBackupCommand(opts) {
|
|
15444
15584
|
printPaperclipCliBanner();
|
|
@@ -15559,7 +15699,7 @@ function registerContextCommands(program2) {
|
|
|
15559
15699
|
// src/commands/client/company.ts
|
|
15560
15700
|
init_http();
|
|
15561
15701
|
import { mkdir, readFile as readFile3, stat, writeFile as writeFile2 } from "node:fs/promises";
|
|
15562
|
-
import
|
|
15702
|
+
import path16 from "node:path";
|
|
15563
15703
|
function isUuidLike2(value) {
|
|
15564
15704
|
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
|
|
15565
15705
|
}
|
|
@@ -15593,32 +15733,32 @@ function isGithubUrl(input) {
|
|
|
15593
15733
|
return /^https?:\/\/github\.com\//i.test(input.trim());
|
|
15594
15734
|
}
|
|
15595
15735
|
async function resolveInlineSourceFromPath(inputPath) {
|
|
15596
|
-
const resolved =
|
|
15736
|
+
const resolved = path16.resolve(inputPath);
|
|
15597
15737
|
const resolvedStat = await stat(resolved);
|
|
15598
|
-
const manifestPath = resolvedStat.isDirectory() ?
|
|
15599
|
-
const manifestBaseDir =
|
|
15738
|
+
const manifestPath = resolvedStat.isDirectory() ? path16.join(resolved, "paperclip.manifest.json") : resolved;
|
|
15739
|
+
const manifestBaseDir = path16.dirname(manifestPath);
|
|
15600
15740
|
const manifestRaw = await readFile3(manifestPath, "utf8");
|
|
15601
15741
|
const manifest = JSON.parse(manifestRaw);
|
|
15602
15742
|
const files = {};
|
|
15603
15743
|
if (manifest.company?.path) {
|
|
15604
15744
|
const companyPath = manifest.company.path.replace(/\\/g, "/");
|
|
15605
|
-
files[companyPath] = await readFile3(
|
|
15745
|
+
files[companyPath] = await readFile3(path16.join(manifestBaseDir, companyPath), "utf8");
|
|
15606
15746
|
}
|
|
15607
15747
|
for (const agent of manifest.agents ?? []) {
|
|
15608
15748
|
const agentPath = agent.path.replace(/\\/g, "/");
|
|
15609
|
-
files[agentPath] = await readFile3(
|
|
15749
|
+
files[agentPath] = await readFile3(path16.join(manifestBaseDir, agentPath), "utf8");
|
|
15610
15750
|
}
|
|
15611
15751
|
return { manifest, files };
|
|
15612
15752
|
}
|
|
15613
15753
|
async function writeExportToFolder(outDir, exported) {
|
|
15614
|
-
const root =
|
|
15754
|
+
const root = path16.resolve(outDir);
|
|
15615
15755
|
await mkdir(root, { recursive: true });
|
|
15616
|
-
const manifestPath =
|
|
15756
|
+
const manifestPath = path16.join(root, "paperclip.manifest.json");
|
|
15617
15757
|
await writeFile2(manifestPath, JSON.stringify(exported.manifest, null, 2), "utf8");
|
|
15618
15758
|
for (const [relativePath, content] of Object.entries(exported.files)) {
|
|
15619
15759
|
const normalized = relativePath.replace(/\\/g, "/");
|
|
15620
|
-
const filePath =
|
|
15621
|
-
await mkdir(
|
|
15760
|
+
const filePath = path16.join(root, normalized);
|
|
15761
|
+
await mkdir(path16.dirname(filePath), { recursive: true });
|
|
15622
15762
|
await writeFile2(filePath, content, "utf8");
|
|
15623
15763
|
}
|
|
15624
15764
|
}
|
|
@@ -15741,7 +15881,7 @@ function registerCompanyCommands(program2) {
|
|
|
15741
15881
|
printOutput(
|
|
15742
15882
|
{
|
|
15743
15883
|
ok: true,
|
|
15744
|
-
out:
|
|
15884
|
+
out: path16.resolve(opts.out),
|
|
15745
15885
|
filesWritten: Object.keys(exported.files).length + 1,
|
|
15746
15886
|
warningCount: exported.warnings.length
|
|
15747
15887
|
},
|
|
@@ -15903,8 +16043,8 @@ function registerIssueCommands(program2) {
|
|
|
15903
16043
|
if (opts.assigneeAgentId) params.set("assigneeAgentId", opts.assigneeAgentId);
|
|
15904
16044
|
if (opts.projectId) params.set("projectId", opts.projectId);
|
|
15905
16045
|
const query = params.toString();
|
|
15906
|
-
const
|
|
15907
|
-
const rows = await ctx.api.get(
|
|
16046
|
+
const path62 = `/api/companies/${ctx.companyId}/issues${query ? `?${query}` : ""}`;
|
|
16047
|
+
const rows = await ctx.api.get(path62) ?? [];
|
|
15908
16048
|
const filtered = filterIssueRows(rows, opts.match);
|
|
15909
16049
|
if (ctx.json) {
|
|
15910
16050
|
printOutput(filtered, { json: true });
|
|
@@ -16066,8 +16206,8 @@ function filterIssueRows(rows, match) {
|
|
|
16066
16206
|
}
|
|
16067
16207
|
|
|
16068
16208
|
// ../packages/adapter-utils/src/server-utils.ts
|
|
16069
|
-
import { constants as fsConstants, promises as
|
|
16070
|
-
import
|
|
16209
|
+
import { constants as fsConstants, promises as fs15 } from "node:fs";
|
|
16210
|
+
import path17 from "node:path";
|
|
16071
16211
|
var MAX_CAPTURE_BYTES = 4 * 1024 * 1024;
|
|
16072
16212
|
var MAX_EXCERPT_BYTES = 32 * 1024;
|
|
16073
16213
|
var PAPERCLIP_SKILL_ROOT_RELATIVE_CANDIDATES = [
|
|
@@ -16082,14 +16222,14 @@ function isMaintainerOnlySkillTarget(candidate) {
|
|
|
16082
16222
|
}
|
|
16083
16223
|
async function resolvePaperclipSkillsDir(moduleDir, additionalCandidates = []) {
|
|
16084
16224
|
const candidates = [
|
|
16085
|
-
...PAPERCLIP_SKILL_ROOT_RELATIVE_CANDIDATES.map((relativePath) =>
|
|
16086
|
-
...additionalCandidates.map((candidate) =>
|
|
16225
|
+
...PAPERCLIP_SKILL_ROOT_RELATIVE_CANDIDATES.map((relativePath) => path17.resolve(moduleDir, relativePath)),
|
|
16226
|
+
...additionalCandidates.map((candidate) => path17.resolve(candidate))
|
|
16087
16227
|
];
|
|
16088
16228
|
const seenRoots = /* @__PURE__ */ new Set();
|
|
16089
16229
|
for (const root of candidates) {
|
|
16090
16230
|
if (seenRoots.has(root)) continue;
|
|
16091
16231
|
seenRoots.add(root);
|
|
16092
|
-
const isDirectory = await
|
|
16232
|
+
const isDirectory = await fs15.stat(root).then((stats) => stats.isDirectory()).catch(() => false);
|
|
16093
16233
|
if (isDirectory) return root;
|
|
16094
16234
|
}
|
|
16095
16235
|
return null;
|
|
@@ -16097,20 +16237,20 @@ async function resolvePaperclipSkillsDir(moduleDir, additionalCandidates = []) {
|
|
|
16097
16237
|
async function removeMaintainerOnlySkillSymlinks(skillsHome, allowedSkillNames) {
|
|
16098
16238
|
const allowed = new Set(Array.from(allowedSkillNames));
|
|
16099
16239
|
try {
|
|
16100
|
-
const entries = await
|
|
16240
|
+
const entries = await fs15.readdir(skillsHome, { withFileTypes: true });
|
|
16101
16241
|
const removed = [];
|
|
16102
16242
|
for (const entry of entries) {
|
|
16103
16243
|
if (allowed.has(entry.name)) continue;
|
|
16104
|
-
const target =
|
|
16105
|
-
const existing = await
|
|
16244
|
+
const target = path17.join(skillsHome, entry.name);
|
|
16245
|
+
const existing = await fs15.lstat(target).catch(() => null);
|
|
16106
16246
|
if (!existing?.isSymbolicLink()) continue;
|
|
16107
|
-
const linkedPath = await
|
|
16247
|
+
const linkedPath = await fs15.readlink(target).catch(() => null);
|
|
16108
16248
|
if (!linkedPath) continue;
|
|
16109
|
-
const resolvedLinkedPath =
|
|
16249
|
+
const resolvedLinkedPath = path17.isAbsolute(linkedPath) ? linkedPath : path17.resolve(path17.dirname(target), linkedPath);
|
|
16110
16250
|
if (!isMaintainerOnlySkillTarget(linkedPath) && !isMaintainerOnlySkillTarget(resolvedLinkedPath)) {
|
|
16111
16251
|
continue;
|
|
16112
16252
|
}
|
|
16113
|
-
await
|
|
16253
|
+
await fs15.unlink(target);
|
|
16114
16254
|
removed.push(entry.name);
|
|
16115
16255
|
}
|
|
16116
16256
|
return removed;
|
|
@@ -16120,20 +16260,20 @@ async function removeMaintainerOnlySkillSymlinks(skillsHome, allowedSkillNames)
|
|
|
16120
16260
|
}
|
|
16121
16261
|
|
|
16122
16262
|
// src/commands/client/agent.ts
|
|
16123
|
-
import
|
|
16263
|
+
import fs16 from "node:fs/promises";
|
|
16124
16264
|
import os4 from "node:os";
|
|
16125
|
-
import
|
|
16265
|
+
import path18 from "node:path";
|
|
16126
16266
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
16127
|
-
var __moduleDir =
|
|
16267
|
+
var __moduleDir = path18.dirname(fileURLToPath3(import.meta.url));
|
|
16128
16268
|
function codexSkillsHome() {
|
|
16129
16269
|
const fromEnv = process.env.CODEX_HOME?.trim();
|
|
16130
|
-
const base = fromEnv && fromEnv.length > 0 ? fromEnv :
|
|
16131
|
-
return
|
|
16270
|
+
const base = fromEnv && fromEnv.length > 0 ? fromEnv : path18.join(os4.homedir(), ".codex");
|
|
16271
|
+
return path18.join(base, "skills");
|
|
16132
16272
|
}
|
|
16133
16273
|
function claudeSkillsHome() {
|
|
16134
16274
|
const fromEnv = process.env.CLAUDE_HOME?.trim();
|
|
16135
|
-
const base = fromEnv && fromEnv.length > 0 ? fromEnv :
|
|
16136
|
-
return
|
|
16275
|
+
const base = fromEnv && fromEnv.length > 0 ? fromEnv : path18.join(os4.homedir(), ".claude");
|
|
16276
|
+
return path18.join(base, "skills");
|
|
16137
16277
|
}
|
|
16138
16278
|
async function installSkillsForTarget(sourceSkillsDir, targetSkillsDir, tool) {
|
|
16139
16279
|
const summary = {
|
|
@@ -16144,26 +16284,26 @@ async function installSkillsForTarget(sourceSkillsDir, targetSkillsDir, tool) {
|
|
|
16144
16284
|
skipped: [],
|
|
16145
16285
|
failed: []
|
|
16146
16286
|
};
|
|
16147
|
-
await
|
|
16148
|
-
const entries = await
|
|
16287
|
+
await fs16.mkdir(targetSkillsDir, { recursive: true });
|
|
16288
|
+
const entries = await fs16.readdir(sourceSkillsDir, { withFileTypes: true });
|
|
16149
16289
|
summary.removed = await removeMaintainerOnlySkillSymlinks(
|
|
16150
16290
|
targetSkillsDir,
|
|
16151
16291
|
entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name)
|
|
16152
16292
|
);
|
|
16153
16293
|
for (const entry of entries) {
|
|
16154
16294
|
if (!entry.isDirectory()) continue;
|
|
16155
|
-
const source =
|
|
16156
|
-
const target =
|
|
16157
|
-
const existing = await
|
|
16295
|
+
const source = path18.join(sourceSkillsDir, entry.name);
|
|
16296
|
+
const target = path18.join(targetSkillsDir, entry.name);
|
|
16297
|
+
const existing = await fs16.lstat(target).catch(() => null);
|
|
16158
16298
|
if (existing) {
|
|
16159
16299
|
if (existing.isSymbolicLink()) {
|
|
16160
16300
|
let linkedPath = null;
|
|
16161
16301
|
try {
|
|
16162
|
-
linkedPath = await
|
|
16302
|
+
linkedPath = await fs16.readlink(target);
|
|
16163
16303
|
} catch (err) {
|
|
16164
|
-
await
|
|
16304
|
+
await fs16.unlink(target);
|
|
16165
16305
|
try {
|
|
16166
|
-
await
|
|
16306
|
+
await fs16.symlink(source, target);
|
|
16167
16307
|
summary.linked.push(entry.name);
|
|
16168
16308
|
continue;
|
|
16169
16309
|
} catch (linkErr) {
|
|
@@ -16174,10 +16314,10 @@ async function installSkillsForTarget(sourceSkillsDir, targetSkillsDir, tool) {
|
|
|
16174
16314
|
continue;
|
|
16175
16315
|
}
|
|
16176
16316
|
}
|
|
16177
|
-
const resolvedLinkedPath =
|
|
16178
|
-
const linkedTargetExists = await
|
|
16317
|
+
const resolvedLinkedPath = path18.isAbsolute(linkedPath) ? linkedPath : path18.resolve(path18.dirname(target), linkedPath);
|
|
16318
|
+
const linkedTargetExists = await fs16.stat(resolvedLinkedPath).then(() => true).catch(() => false);
|
|
16179
16319
|
if (!linkedTargetExists) {
|
|
16180
|
-
await
|
|
16320
|
+
await fs16.unlink(target);
|
|
16181
16321
|
} else {
|
|
16182
16322
|
summary.skipped.push(entry.name);
|
|
16183
16323
|
continue;
|
|
@@ -16188,7 +16328,7 @@ async function installSkillsForTarget(sourceSkillsDir, targetSkillsDir, tool) {
|
|
|
16188
16328
|
}
|
|
16189
16329
|
}
|
|
16190
16330
|
try {
|
|
16191
|
-
await
|
|
16331
|
+
await fs16.symlink(source, target);
|
|
16192
16332
|
summary.linked.push(entry.name);
|
|
16193
16333
|
} catch (err) {
|
|
16194
16334
|
summary.failed.push({
|
|
@@ -16277,7 +16417,7 @@ function registerAgentCommands(program2) {
|
|
|
16277
16417
|
}
|
|
16278
16418
|
const installSummaries = [];
|
|
16279
16419
|
if (opts.installSkills !== false) {
|
|
16280
|
-
const skillsDir = await resolvePaperclipSkillsDir(__moduleDir, [
|
|
16420
|
+
const skillsDir = await resolvePaperclipSkillsDir(__moduleDir, [path18.resolve(process.cwd(), "skills")]);
|
|
16281
16421
|
if (!skillsDir) {
|
|
16282
16422
|
throw new Error(
|
|
16283
16423
|
"Could not locate local Paperclip skills directory. Expected ./skills in the repo checkout."
|
|
@@ -16510,8 +16650,8 @@ function registerActivityCommands(program2) {
|
|
|
16510
16650
|
if (opts.entityType) params.set("entityType", opts.entityType);
|
|
16511
16651
|
if (opts.entityId) params.set("entityId", opts.entityId);
|
|
16512
16652
|
const query = params.toString();
|
|
16513
|
-
const
|
|
16514
|
-
const rows = await ctx.api.get(
|
|
16653
|
+
const path62 = `/api/companies/${ctx.companyId}/activity${query ? `?${query}` : ""}`;
|
|
16654
|
+
const rows = await ctx.api.get(path62) ?? [];
|
|
16515
16655
|
if (ctx.json) {
|
|
16516
16656
|
printOutput(rows, { json: true });
|
|
16517
16657
|
return;
|
|
@@ -16560,11 +16700,11 @@ function registerDashboardCommands(program2) {
|
|
|
16560
16700
|
|
|
16561
16701
|
// src/config/data-dir.ts
|
|
16562
16702
|
init_home();
|
|
16563
|
-
import
|
|
16703
|
+
import path19 from "node:path";
|
|
16564
16704
|
function applyDataDirOverride(options, support = {}) {
|
|
16565
16705
|
const rawDataDir = options.dataDir?.trim();
|
|
16566
16706
|
if (!rawDataDir) return null;
|
|
16567
|
-
const resolvedDataDir =
|
|
16707
|
+
const resolvedDataDir = path19.resolve(expandHomePrefix(rawDataDir));
|
|
16568
16708
|
process.env.PAPERCLIP_HOME = resolvedDataDir;
|
|
16569
16709
|
if (support.hasConfigOption) {
|
|
16570
16710
|
const hasConfigOverride = Boolean(options.config?.trim()) || Boolean(process.env.PAPERCLIP_CONFIG?.trim());
|
|
@@ -16591,35 +16731,35 @@ init_store();
|
|
|
16591
16731
|
// src/commands/gtm.ts
|
|
16592
16732
|
init_src();
|
|
16593
16733
|
init_home();
|
|
16594
|
-
import
|
|
16595
|
-
import
|
|
16734
|
+
import fs17 from "node:fs";
|
|
16735
|
+
import path20 from "node:path";
|
|
16596
16736
|
import { spawn } from "node:child_process";
|
|
16597
16737
|
import pc23 from "picocolors";
|
|
16598
16738
|
function resolveGtmStatePath() {
|
|
16599
|
-
return
|
|
16739
|
+
return path20.resolve(resolvePaperclipHomeDir(), "gtm", "state.json");
|
|
16600
16740
|
}
|
|
16601
16741
|
function readState() {
|
|
16602
16742
|
const filePath = resolveGtmStatePath();
|
|
16603
|
-
if (!
|
|
16604
|
-
return coerceGtmState(JSON.parse(
|
|
16743
|
+
if (!fs17.existsSync(filePath)) return createDefaultGtmState();
|
|
16744
|
+
return coerceGtmState(JSON.parse(fs17.readFileSync(filePath, "utf-8")));
|
|
16605
16745
|
}
|
|
16606
16746
|
function writeState(state) {
|
|
16607
16747
|
const filePath = resolveGtmStatePath();
|
|
16608
|
-
|
|
16609
|
-
|
|
16748
|
+
fs17.mkdirSync(path20.dirname(filePath), { recursive: true });
|
|
16749
|
+
fs17.writeFileSync(filePath, JSON.stringify(state, null, 2) + "\n", "utf-8");
|
|
16610
16750
|
}
|
|
16611
16751
|
function launchWorkflow(state) {
|
|
16612
16752
|
const runnerPath = state.workflow.runnerPath?.trim();
|
|
16613
16753
|
if (!runnerPath) {
|
|
16614
16754
|
throw new Error("No local SDR runner configured.");
|
|
16615
16755
|
}
|
|
16616
|
-
if (!
|
|
16756
|
+
if (!fs17.existsSync(runnerPath)) {
|
|
16617
16757
|
throw new Error(`Runner not found at ${runnerPath}`);
|
|
16618
16758
|
}
|
|
16619
16759
|
const args = runnerPath.endsWith(".mjs") || runnerPath.endsWith(".js") ? [runnerPath] : [];
|
|
16620
16760
|
const command = args.length > 0 ? process.execPath : runnerPath;
|
|
16621
16761
|
const child = spawn(command, args, {
|
|
16622
|
-
cwd:
|
|
16762
|
+
cwd: path20.dirname(runnerPath),
|
|
16623
16763
|
detached: true,
|
|
16624
16764
|
stdio: "ignore"
|
|
16625
16765
|
});
|
|
@@ -16657,7 +16797,7 @@ function registerGtmCommands(program2) {
|
|
|
16657
16797
|
if (opts.internalSocialsPath) state.workflow.referenceInterfaces.internalSocialsPath = opts.internalSocialsPath.trim();
|
|
16658
16798
|
if (opts.localSdrPath) {
|
|
16659
16799
|
state.workflow.referenceInterfaces.localSdrPath = opts.localSdrPath.trim();
|
|
16660
|
-
state.workflow.runnerPath =
|
|
16800
|
+
state.workflow.runnerPath = path20.resolve(opts.localSdrPath.trim(), "sdr-bot.mjs");
|
|
16661
16801
|
}
|
|
16662
16802
|
writeState(state);
|
|
16663
16803
|
const view = toGtmViewModel(state);
|
|
@@ -16710,7 +16850,7 @@ import {
|
|
|
16710
16850
|
writeFileSync
|
|
16711
16851
|
} from "node:fs";
|
|
16712
16852
|
import os5 from "node:os";
|
|
16713
|
-
import
|
|
16853
|
+
import path22 from "node:path";
|
|
16714
16854
|
import { execFileSync } from "node:child_process";
|
|
16715
16855
|
import { createServer as createServer2 } from "node:net";
|
|
16716
16856
|
import * as p16 from "@clack/prompts";
|
|
@@ -16720,7 +16860,7 @@ import { eq as eq2 } from "drizzle-orm";
|
|
|
16720
16860
|
// src/commands/worktree-lib.ts
|
|
16721
16861
|
init_home();
|
|
16722
16862
|
import { randomInt } from "node:crypto";
|
|
16723
|
-
import
|
|
16863
|
+
import path21 from "node:path";
|
|
16724
16864
|
var DEFAULT_WORKTREE_HOME = "~/.paperclip-worktrees";
|
|
16725
16865
|
var WORKTREE_SEED_MODES = ["minimal", "full"];
|
|
16726
16866
|
var MINIMAL_WORKTREE_EXCLUDED_TABLES = [
|
|
@@ -16768,7 +16908,7 @@ function sanitizeWorktreeInstanceId(rawValue) {
|
|
|
16768
16908
|
return normalized || "worktree";
|
|
16769
16909
|
}
|
|
16770
16910
|
function resolveSuggestedWorktreeName(cwd, explicitName) {
|
|
16771
|
-
return nonEmpty(explicitName) ??
|
|
16911
|
+
return nonEmpty(explicitName) ?? path21.basename(path21.resolve(cwd));
|
|
16772
16912
|
}
|
|
16773
16913
|
function hslComponentToHex(n) {
|
|
16774
16914
|
return Math.round(Math.max(0, Math.min(255, n))).toString(16).padStart(2, "0");
|
|
@@ -16808,24 +16948,24 @@ function generateWorktreeColor() {
|
|
|
16808
16948
|
return hslToHex(randomInt(0, 360), 68, 56);
|
|
16809
16949
|
}
|
|
16810
16950
|
function resolveWorktreeLocalPaths(opts) {
|
|
16811
|
-
const cwd =
|
|
16812
|
-
const homeDir =
|
|
16813
|
-
const instanceRoot =
|
|
16814
|
-
const repoConfigDir =
|
|
16951
|
+
const cwd = path21.resolve(opts.cwd);
|
|
16952
|
+
const homeDir = path21.resolve(expandHomePrefix(opts.homeDir ?? DEFAULT_WORKTREE_HOME));
|
|
16953
|
+
const instanceRoot = path21.resolve(homeDir, "instances", opts.instanceId);
|
|
16954
|
+
const repoConfigDir = path21.resolve(cwd, ".paperclip");
|
|
16815
16955
|
return {
|
|
16816
16956
|
cwd,
|
|
16817
16957
|
repoConfigDir,
|
|
16818
|
-
configPath:
|
|
16819
|
-
envPath:
|
|
16958
|
+
configPath: path21.resolve(repoConfigDir, "config.json"),
|
|
16959
|
+
envPath: path21.resolve(repoConfigDir, ".env"),
|
|
16820
16960
|
homeDir,
|
|
16821
16961
|
instanceId: opts.instanceId,
|
|
16822
16962
|
instanceRoot,
|
|
16823
|
-
contextPath:
|
|
16824
|
-
embeddedPostgresDataDir:
|
|
16825
|
-
backupDir:
|
|
16826
|
-
logDir:
|
|
16827
|
-
secretsKeyFilePath:
|
|
16828
|
-
storageDir:
|
|
16963
|
+
contextPath: path21.resolve(homeDir, "context.json"),
|
|
16964
|
+
embeddedPostgresDataDir: path21.resolve(instanceRoot, "db"),
|
|
16965
|
+
backupDir: path21.resolve(instanceRoot, "data", "backups"),
|
|
16966
|
+
logDir: path21.resolve(instanceRoot, "logs"),
|
|
16967
|
+
secretsKeyFilePath: path21.resolve(instanceRoot, "secrets", "master.key"),
|
|
16968
|
+
storageDir: path21.resolve(instanceRoot, "data", "storage")
|
|
16829
16969
|
};
|
|
16830
16970
|
}
|
|
16831
16971
|
function rewriteLocalUrlPort(rawUrl, port) {
|
|
@@ -16931,7 +17071,7 @@ function isCurrentSourceConfigPath(sourceConfigPath) {
|
|
|
16931
17071
|
if (!currentConfigPath || currentConfigPath.trim().length === 0) {
|
|
16932
17072
|
return false;
|
|
16933
17073
|
}
|
|
16934
|
-
return
|
|
17074
|
+
return path22.resolve(currentConfigPath) === path22.resolve(sourceConfigPath);
|
|
16935
17075
|
}
|
|
16936
17076
|
var WORKTREE_NAME_PREFIX = "paperclip-";
|
|
16937
17077
|
function resolveWorktreeMakeName(name) {
|
|
@@ -16953,7 +17093,7 @@ function resolveWorktreeStartPoint(explicit) {
|
|
|
16953
17093
|
return explicit ?? nonEmpty2(process.env.PAPERCLIP_WORKTREE_START_POINT) ?? void 0;
|
|
16954
17094
|
}
|
|
16955
17095
|
function resolveWorktreeMakeTargetPath(name) {
|
|
16956
|
-
return
|
|
17096
|
+
return path22.resolve(os5.homedir(), resolveWorktreeMakeName(name));
|
|
16957
17097
|
}
|
|
16958
17098
|
function extractExecSyncErrorMessage(error) {
|
|
16959
17099
|
if (!error || typeof error !== "object") {
|
|
@@ -17059,10 +17199,10 @@ function detectGitWorkspaceInfo(cwd) {
|
|
|
17059
17199
|
stdio: ["ignore", "pipe", "ignore"]
|
|
17060
17200
|
}).trim();
|
|
17061
17201
|
return {
|
|
17062
|
-
root:
|
|
17063
|
-
commonDir:
|
|
17064
|
-
gitDir:
|
|
17065
|
-
hooksPath:
|
|
17202
|
+
root: path22.resolve(root),
|
|
17203
|
+
commonDir: path22.resolve(root, commonDirRaw),
|
|
17204
|
+
gitDir: path22.resolve(root, gitDirRaw),
|
|
17205
|
+
hooksPath: path22.resolve(root, hooksPathRaw)
|
|
17066
17206
|
};
|
|
17067
17207
|
} catch {
|
|
17068
17208
|
return null;
|
|
@@ -17075,8 +17215,8 @@ function copyDirectoryContents(sourceDir, targetDir) {
|
|
|
17075
17215
|
mkdirSync2(targetDir, { recursive: true });
|
|
17076
17216
|
let copied = false;
|
|
17077
17217
|
for (const entry of entries) {
|
|
17078
|
-
const sourcePath =
|
|
17079
|
-
const targetPath =
|
|
17218
|
+
const sourcePath = path22.resolve(sourceDir, entry.name);
|
|
17219
|
+
const targetPath = path22.resolve(targetDir, entry.name);
|
|
17080
17220
|
if (entry.isDirectory()) {
|
|
17081
17221
|
mkdirSync2(targetPath, { recursive: true });
|
|
17082
17222
|
copyDirectoryContents(sourcePath, targetPath);
|
|
@@ -17102,7 +17242,7 @@ function copyGitHooksToWorktreeGitDir(cwd) {
|
|
|
17102
17242
|
const workspace = detectGitWorkspaceInfo(cwd);
|
|
17103
17243
|
if (!workspace) return null;
|
|
17104
17244
|
const sourceHooksPath = workspace.hooksPath;
|
|
17105
|
-
const targetHooksPath =
|
|
17245
|
+
const targetHooksPath = path22.resolve(workspace.gitDir, "hooks");
|
|
17106
17246
|
if (sourceHooksPath === targetHooksPath) {
|
|
17107
17247
|
return {
|
|
17108
17248
|
sourceHooksPath,
|
|
@@ -17117,17 +17257,17 @@ function copyGitHooksToWorktreeGitDir(cwd) {
|
|
|
17117
17257
|
};
|
|
17118
17258
|
}
|
|
17119
17259
|
function rebindWorkspaceCwd(input) {
|
|
17120
|
-
const sourceRepoRoot =
|
|
17121
|
-
const targetRepoRoot =
|
|
17122
|
-
const workspaceCwd =
|
|
17123
|
-
const relative =
|
|
17260
|
+
const sourceRepoRoot = path22.resolve(input.sourceRepoRoot);
|
|
17261
|
+
const targetRepoRoot = path22.resolve(input.targetRepoRoot);
|
|
17262
|
+
const workspaceCwd = path22.resolve(input.workspaceCwd);
|
|
17263
|
+
const relative = path22.relative(sourceRepoRoot, workspaceCwd);
|
|
17124
17264
|
if (!relative || relative === "") {
|
|
17125
17265
|
return targetRepoRoot;
|
|
17126
17266
|
}
|
|
17127
|
-
if (relative.startsWith("..") ||
|
|
17267
|
+
if (relative.startsWith("..") || path22.isAbsolute(relative)) {
|
|
17128
17268
|
return null;
|
|
17129
17269
|
}
|
|
17130
|
-
return
|
|
17270
|
+
return path22.resolve(targetRepoRoot, relative);
|
|
17131
17271
|
}
|
|
17132
17272
|
async function rebindSeededProjectWorkspaces(input) {
|
|
17133
17273
|
const targetRepo = detectGitWorkspaceInfo(input.currentCwd);
|
|
@@ -17153,7 +17293,7 @@ async function rebindSeededProjectWorkspaces(input) {
|
|
|
17153
17293
|
workspaceCwd
|
|
17154
17294
|
});
|
|
17155
17295
|
if (!reboundCwd) continue;
|
|
17156
|
-
const normalizedCurrent =
|
|
17296
|
+
const normalizedCurrent = path22.resolve(workspaceCwd);
|
|
17157
17297
|
if (reboundCwd === normalizedCurrent) continue;
|
|
17158
17298
|
if (!existsSync2(reboundCwd)) continue;
|
|
17159
17299
|
await db.update(projectWorkspaces).set({
|
|
@@ -17172,14 +17312,14 @@ async function rebindSeededProjectWorkspaces(input) {
|
|
|
17172
17312
|
}
|
|
17173
17313
|
}
|
|
17174
17314
|
function resolveSourceConfigPath(opts) {
|
|
17175
|
-
if (opts.sourceConfigPathOverride) return
|
|
17176
|
-
if (opts.fromConfig) return
|
|
17315
|
+
if (opts.sourceConfigPathOverride) return path22.resolve(opts.sourceConfigPathOverride);
|
|
17316
|
+
if (opts.fromConfig) return path22.resolve(opts.fromConfig);
|
|
17177
17317
|
if (!opts.fromDataDir && !opts.fromInstance) {
|
|
17178
17318
|
return resolveConfigPath();
|
|
17179
17319
|
}
|
|
17180
|
-
const sourceHome =
|
|
17320
|
+
const sourceHome = path22.resolve(expandHomePrefix(opts.fromDataDir ?? "~/.paperclip"));
|
|
17181
17321
|
const sourceInstanceId = sanitizeWorktreeInstanceId(opts.fromInstance ?? "default");
|
|
17182
|
-
return
|
|
17322
|
+
return path22.resolve(sourceHome, "instances", sourceInstanceId, "config.json");
|
|
17183
17323
|
}
|
|
17184
17324
|
function resolveSourceConnectionString(config, envEntries, portOverride) {
|
|
17185
17325
|
if (config.database.mode === "postgres") {
|
|
@@ -17198,7 +17338,7 @@ function copySeededSecretsKey(input) {
|
|
|
17198
17338
|
if (input.sourceConfig.secrets.provider !== "local_encrypted") {
|
|
17199
17339
|
return;
|
|
17200
17340
|
}
|
|
17201
|
-
mkdirSync2(
|
|
17341
|
+
mkdirSync2(path22.dirname(input.targetKeyFilePath), { recursive: true });
|
|
17202
17342
|
const allowProcessEnvFallback = isCurrentSourceConfigPath(input.sourceConfigPath);
|
|
17203
17343
|
const sourceInlineMasterKey = nonEmpty2(input.sourceEnvEntries.PAPERCLIP_SECRETS_MASTER_KEY) ?? (allowProcessEnvFallback ? nonEmpty2(process.env.PAPERCLIP_SECRETS_MASTER_KEY) : null);
|
|
17204
17344
|
if (sourceInlineMasterKey) {
|
|
@@ -17237,7 +17377,7 @@ async function ensureEmbeddedPostgres(dataDir, preferredPort) {
|
|
|
17237
17377
|
"Embedded PostgreSQL support requires dependency `embedded-postgres`. Reinstall dependencies and try again."
|
|
17238
17378
|
);
|
|
17239
17379
|
}
|
|
17240
|
-
const postmasterPidFile =
|
|
17380
|
+
const postmasterPidFile = path22.resolve(dataDir, "postmaster.pid");
|
|
17241
17381
|
const runningPid = readRunningPostmasterPid(postmasterPidFile);
|
|
17242
17382
|
if (runningPid) {
|
|
17243
17383
|
return {
|
|
@@ -17260,7 +17400,7 @@ async function ensureEmbeddedPostgres(dataDir, preferredPort) {
|
|
|
17260
17400
|
onError: () => {
|
|
17261
17401
|
}
|
|
17262
17402
|
});
|
|
17263
|
-
if (!existsSync2(
|
|
17403
|
+
if (!existsSync2(path22.resolve(dataDir, "PG_VERSION"))) {
|
|
17264
17404
|
await instance.initialise();
|
|
17265
17405
|
}
|
|
17266
17406
|
if (existsSync2(postmasterPidFile)) {
|
|
@@ -17301,7 +17441,7 @@ async function seedWorktreeDatabase(input) {
|
|
|
17301
17441
|
);
|
|
17302
17442
|
const backup = await runDatabaseBackup({
|
|
17303
17443
|
connectionString: sourceConnectionString,
|
|
17304
|
-
backupDir:
|
|
17444
|
+
backupDir: path22.resolve(input.targetPaths.backupDir, "seed"),
|
|
17305
17445
|
retentionDays: 7,
|
|
17306
17446
|
filenamePrefix: `${input.instanceId}-seed`,
|
|
17307
17447
|
includeMigrationJournal: true,
|
|
@@ -17460,7 +17600,7 @@ async function worktreeMakeCommand(nameArg, opts) {
|
|
|
17460
17600
|
if (existsSync2(targetPath)) {
|
|
17461
17601
|
throw new Error(`Target path already exists: ${targetPath}`);
|
|
17462
17602
|
}
|
|
17463
|
-
mkdirSync2(
|
|
17603
|
+
mkdirSync2(path22.dirname(targetPath), { recursive: true });
|
|
17464
17604
|
if (startPoint) {
|
|
17465
17605
|
const [remote] = startPoint.split("/", 1);
|
|
17466
17606
|
try {
|
|
@@ -17517,7 +17657,7 @@ async function worktreeMakeCommand(nameArg, opts) {
|
|
|
17517
17657
|
} finally {
|
|
17518
17658
|
process.chdir(originalCwd);
|
|
17519
17659
|
}
|
|
17520
|
-
const bootstrapScript =
|
|
17660
|
+
const bootstrapScript = path22.resolve(sourceCwd, "scripts/worktree-bootstrap.mjs");
|
|
17521
17661
|
if (existsSync2(bootstrapScript)) {
|
|
17522
17662
|
p16.log.message(pc24.dim(`Running worktree bootstrap in ${targetPath}...`));
|
|
17523
17663
|
try {
|
|
@@ -17607,14 +17747,14 @@ async function worktreeCleanupCommand(nameArg, opts) {
|
|
|
17607
17747
|
const sourceCwd = process.cwd();
|
|
17608
17748
|
const targetPath = resolveWorktreeMakeTargetPath(name);
|
|
17609
17749
|
const instanceId = sanitizeWorktreeInstanceId(opts.instance ?? name);
|
|
17610
|
-
const homeDir =
|
|
17611
|
-
const instanceRoot =
|
|
17750
|
+
const homeDir = path22.resolve(expandHomePrefix(resolveWorktreeHome(opts.home)));
|
|
17751
|
+
const instanceRoot = path22.resolve(homeDir, "instances", instanceId);
|
|
17612
17752
|
const hasBranch = localBranchExists(sourceCwd, name);
|
|
17613
17753
|
const hasTargetDir = existsSync2(targetPath);
|
|
17614
17754
|
const hasInstanceData = existsSync2(instanceRoot);
|
|
17615
17755
|
const worktrees = parseGitWorktreeList(sourceCwd);
|
|
17616
17756
|
const linkedWorktree = worktrees.find(
|
|
17617
|
-
(wt) => wt.branch === `refs/heads/${name}` ||
|
|
17757
|
+
(wt) => wt.branch === `refs/heads/${name}` || path22.resolve(wt.worktree) === path22.resolve(targetPath)
|
|
17618
17758
|
);
|
|
17619
17759
|
if (!hasBranch && !hasTargetDir && !hasInstanceData && !linkedWorktree) {
|
|
17620
17760
|
p16.log.info("Nothing to clean up \u2014 no branch, worktree directory, or instance data found.");
|
|
@@ -17736,16 +17876,16 @@ function registerWorktreeCommands(program2) {
|
|
|
17736
17876
|
}
|
|
17737
17877
|
|
|
17738
17878
|
// src/commands/client/plugin.ts
|
|
17739
|
-
import
|
|
17879
|
+
import path23 from "node:path";
|
|
17740
17880
|
import pc25 from "picocolors";
|
|
17741
17881
|
function resolvePackageArg(packageArg, isLocal) {
|
|
17742
17882
|
if (!isLocal) return packageArg;
|
|
17743
|
-
if (
|
|
17883
|
+
if (path23.isAbsolute(packageArg)) return packageArg;
|
|
17744
17884
|
if (packageArg.startsWith("~")) {
|
|
17745
17885
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
17746
|
-
return
|
|
17886
|
+
return path23.resolve(home, packageArg.slice(1).replace(/^[\\/]/, ""));
|
|
17747
17887
|
}
|
|
17748
|
-
return
|
|
17888
|
+
return path23.resolve(process.cwd(), packageArg);
|
|
17749
17889
|
}
|
|
17750
17890
|
function formatPlugin(p35) {
|
|
17751
17891
|
const statusColor3 = p35.status === "ready" ? pc25.green(p35.status) : p35.status === "error" ? pc25.red(p35.status) : p35.status === "disabled" ? pc25.dim(p35.status) : pc25.yellow(p35.status);
|
|
@@ -17943,15 +18083,15 @@ ${result.lastError}`);
|
|
|
17943
18083
|
}
|
|
17944
18084
|
|
|
17945
18085
|
// src/commands/kit.ts
|
|
17946
|
-
|
|
17947
|
-
init_banner();
|
|
17948
|
-
import path34 from "node:path";
|
|
18086
|
+
import path35 from "node:path";
|
|
17949
18087
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
17950
18088
|
import * as p19 from "@clack/prompts";
|
|
17951
18089
|
import pc30 from "picocolors";
|
|
18090
|
+
init_service();
|
|
18091
|
+
init_banner();
|
|
17952
18092
|
|
|
17953
18093
|
// src/commands/kit-fork.ts
|
|
17954
|
-
import
|
|
18094
|
+
import fs28 from "node:fs";
|
|
17955
18095
|
import * as p18 from "@clack/prompts";
|
|
17956
18096
|
|
|
17957
18097
|
// src/commands/kit-fork-remote.ts
|
|
@@ -17961,50 +18101,50 @@ init_fork_trace();
|
|
|
17961
18101
|
init_fork_remote();
|
|
17962
18102
|
import * as p17 from "@clack/prompts";
|
|
17963
18103
|
import pc26 from "picocolors";
|
|
17964
|
-
import
|
|
17965
|
-
import
|
|
18104
|
+
import fs26 from "node:fs";
|
|
18105
|
+
import path33 from "node:path";
|
|
17966
18106
|
|
|
17967
18107
|
// src/kits/fork-sync-agent.ts
|
|
17968
18108
|
init_kit_forks_home();
|
|
17969
18109
|
init_fork_registry();
|
|
17970
|
-
import
|
|
17971
|
-
import
|
|
18110
|
+
import fs25 from "node:fs";
|
|
18111
|
+
import path32 from "node:path";
|
|
17972
18112
|
|
|
17973
18113
|
// src/kits/fork-sync.ts
|
|
17974
18114
|
init_service();
|
|
17975
|
-
import
|
|
17976
|
-
import
|
|
18115
|
+
import fs23 from "node:fs";
|
|
18116
|
+
import path30 from "node:path";
|
|
17977
18117
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
17978
18118
|
function resolveUpstreamAssetRoot(kitId) {
|
|
17979
|
-
const moduleDir =
|
|
18119
|
+
const moduleDir = path30.dirname(fileURLToPath5(import.meta.url));
|
|
17980
18120
|
const candidates = [
|
|
17981
|
-
|
|
17982
|
-
|
|
18121
|
+
path30.resolve(moduleDir, "../../assets/worker-kits", kitId),
|
|
18122
|
+
path30.resolve(moduleDir, "../assets/worker-kits", kitId)
|
|
17983
18123
|
];
|
|
17984
18124
|
for (const c of candidates) {
|
|
17985
|
-
if (
|
|
18125
|
+
if (fs23.existsSync(c)) return c;
|
|
17986
18126
|
}
|
|
17987
18127
|
throw new Error(`Cannot locate bundled asset root for kit: ${kitId}`);
|
|
17988
18128
|
}
|
|
17989
18129
|
function readFileIfExists(p35) {
|
|
17990
|
-
if (!
|
|
18130
|
+
if (!fs23.existsSync(p35)) return null;
|
|
17991
18131
|
try {
|
|
17992
|
-
return
|
|
18132
|
+
return fs23.readFileSync(p35, "utf8");
|
|
17993
18133
|
} catch {
|
|
17994
18134
|
return null;
|
|
17995
18135
|
}
|
|
17996
18136
|
}
|
|
17997
18137
|
function listRelativeFiles2(rootDir) {
|
|
17998
18138
|
const files = /* @__PURE__ */ new Set();
|
|
17999
|
-
if (!
|
|
18139
|
+
if (!fs23.existsSync(rootDir)) return files;
|
|
18000
18140
|
const walk = (cur) => {
|
|
18001
|
-
for (const entry of
|
|
18002
|
-
const full =
|
|
18141
|
+
for (const entry of fs23.readdirSync(cur, { withFileTypes: true })) {
|
|
18142
|
+
const full = path30.join(cur, entry.name);
|
|
18003
18143
|
if (entry.isDirectory()) {
|
|
18004
18144
|
walk(full);
|
|
18005
18145
|
continue;
|
|
18006
18146
|
}
|
|
18007
|
-
files.add(
|
|
18147
|
+
files.add(path30.relative(rootDir, full).split(path30.sep).join("/"));
|
|
18008
18148
|
}
|
|
18009
18149
|
};
|
|
18010
18150
|
walk(rootDir);
|
|
@@ -18068,7 +18208,7 @@ function getUpstreamFiles(kitId) {
|
|
|
18068
18208
|
return files;
|
|
18069
18209
|
}
|
|
18070
18210
|
function detectKitForkDrift(reg) {
|
|
18071
|
-
if (!
|
|
18211
|
+
if (!fs23.existsSync(reg.forkPath)) {
|
|
18072
18212
|
throw new Error(`Fork path does not exist: ${reg.forkPath}`);
|
|
18073
18213
|
}
|
|
18074
18214
|
const allKits = listBundledKits();
|
|
@@ -18107,8 +18247,8 @@ function detectKitForkDrift(reg) {
|
|
|
18107
18247
|
const AUDIT_PATHS = ["kit.json", "package.json", ".env.example", "QUICKSTART.md"];
|
|
18108
18248
|
for (const rel of AUDIT_PATHS) {
|
|
18109
18249
|
if (upstreamFiles.has(rel) && forkFiles.has(rel)) {
|
|
18110
|
-
const upContent = readFileIfExists(
|
|
18111
|
-
const fkContent = readFileIfExists(
|
|
18250
|
+
const upContent = readFileIfExists(path30.resolve(upstreamRoot, rel));
|
|
18251
|
+
const fkContent = readFileIfExists(path30.resolve(reg.forkPath, rel));
|
|
18112
18252
|
if (upContent !== null && fkContent !== null && upContent !== fkContent) {
|
|
18113
18253
|
fileDrifts.push({
|
|
18114
18254
|
relativePath: rel,
|
|
@@ -18120,12 +18260,12 @@ function detectKitForkDrift(reg) {
|
|
|
18120
18260
|
}
|
|
18121
18261
|
}
|
|
18122
18262
|
let packageDrifts = [];
|
|
18123
|
-
const upPkgPath =
|
|
18124
|
-
const fkPkgPath =
|
|
18125
|
-
if (
|
|
18263
|
+
const upPkgPath = path30.resolve(upstreamRoot, "package.json");
|
|
18264
|
+
const fkPkgPath = path30.resolve(reg.forkPath, "package.json");
|
|
18265
|
+
if (fs23.existsSync(upPkgPath) && fs23.existsSync(fkPkgPath)) {
|
|
18126
18266
|
try {
|
|
18127
|
-
const upPkg = JSON.parse(
|
|
18128
|
-
const fkPkg = JSON.parse(
|
|
18267
|
+
const upPkg = JSON.parse(fs23.readFileSync(upPkgPath, "utf8"));
|
|
18268
|
+
const fkPkg = JSON.parse(fs23.readFileSync(fkPkgPath, "utf8"));
|
|
18129
18269
|
packageDrifts = detectPackageDrift(upPkg, fkPkg);
|
|
18130
18270
|
} catch {
|
|
18131
18271
|
}
|
|
@@ -18384,38 +18524,38 @@ function executeHealAction(action, forkPath, kitId, _toVersion) {
|
|
|
18384
18524
|
}
|
|
18385
18525
|
}
|
|
18386
18526
|
function execAddFile(action, forkPath, kitId) {
|
|
18387
|
-
const targetFull =
|
|
18388
|
-
if (
|
|
18527
|
+
const targetFull = path30.resolve(forkPath, action.targetPath);
|
|
18528
|
+
if (fs23.existsSync(targetFull)) {
|
|
18389
18529
|
return { action, status: "skipped", detail: "File already exists in fork" };
|
|
18390
18530
|
}
|
|
18391
18531
|
const upstreamRoot = resolveUpstreamAssetRoot(kitId);
|
|
18392
|
-
const upstreamFull =
|
|
18393
|
-
if (!
|
|
18532
|
+
const upstreamFull = path30.resolve(upstreamRoot, action.targetPath);
|
|
18533
|
+
if (!fs23.existsSync(upstreamFull)) {
|
|
18394
18534
|
return { action, status: "skipped", detail: "Upstream file not found \u2014 skipped" };
|
|
18395
18535
|
}
|
|
18396
18536
|
const content = readFileIfExists(upstreamFull);
|
|
18397
18537
|
if (content === null) {
|
|
18398
18538
|
return { action, status: "skipped", detail: "Could not read upstream file (binary?) \u2014 skipped" };
|
|
18399
18539
|
}
|
|
18400
|
-
|
|
18401
|
-
|
|
18540
|
+
fs23.mkdirSync(path30.dirname(targetFull), { recursive: true });
|
|
18541
|
+
fs23.writeFileSync(targetFull, content, "utf8");
|
|
18402
18542
|
return { action, status: "applied", detail: `Added ${action.targetPath}` };
|
|
18403
18543
|
}
|
|
18404
18544
|
function execUpdatePackageDeps(action, forkPath, kitId) {
|
|
18405
|
-
const forkPkgPath =
|
|
18406
|
-
if (!
|
|
18545
|
+
const forkPkgPath = path30.resolve(forkPath, "package.json");
|
|
18546
|
+
if (!fs23.existsSync(forkPkgPath)) {
|
|
18407
18547
|
return { action, status: "skipped", detail: "No package.json in fork" };
|
|
18408
18548
|
}
|
|
18409
18549
|
const upstreamRoot = resolveUpstreamAssetRoot(kitId);
|
|
18410
|
-
const upstreamPkgPath =
|
|
18411
|
-
if (!
|
|
18550
|
+
const upstreamPkgPath = path30.resolve(upstreamRoot, "package.json");
|
|
18551
|
+
if (!fs23.existsSync(upstreamPkgPath)) {
|
|
18412
18552
|
return { action, status: "skipped", detail: "No package.json in upstream kit" };
|
|
18413
18553
|
}
|
|
18414
18554
|
let forkPkg;
|
|
18415
18555
|
let upstreamPkg;
|
|
18416
18556
|
try {
|
|
18417
|
-
forkPkg = JSON.parse(
|
|
18418
|
-
upstreamPkg = JSON.parse(
|
|
18557
|
+
forkPkg = JSON.parse(fs23.readFileSync(forkPkgPath, "utf8"));
|
|
18558
|
+
upstreamPkg = JSON.parse(fs23.readFileSync(upstreamPkgPath, "utf8"));
|
|
18419
18559
|
} catch {
|
|
18420
18560
|
return { action, status: "error", detail: "Failed to parse package.json" };
|
|
18421
18561
|
}
|
|
@@ -18433,7 +18573,7 @@ function execUpdatePackageDeps(action, forkPath, kitId) {
|
|
|
18433
18573
|
const updated = { ...forkPkg };
|
|
18434
18574
|
if (mergedDeps) updated.dependencies = mergedDeps;
|
|
18435
18575
|
if (mergedDevDeps) updated.devDependencies = mergedDevDeps;
|
|
18436
|
-
|
|
18576
|
+
fs23.writeFileSync(forkPkgPath, JSON.stringify(updated, null, 2) + "\n", "utf8");
|
|
18437
18577
|
return { action, status: "applied", detail: "Merged upstream dependency additions" };
|
|
18438
18578
|
}
|
|
18439
18579
|
function mergeAddOnlyDeps(fork, upstream) {
|
|
@@ -18449,20 +18589,20 @@ function mergeAddOnlyDeps(fork, upstream) {
|
|
|
18449
18589
|
return changed ? merged : null;
|
|
18450
18590
|
}
|
|
18451
18591
|
function execPatchManifest(action, forkPath, kitId) {
|
|
18452
|
-
const forkManifestPath =
|
|
18453
|
-
if (!
|
|
18592
|
+
const forkManifestPath = path30.resolve(forkPath, "kit.json");
|
|
18593
|
+
if (!fs23.existsSync(forkManifestPath)) {
|
|
18454
18594
|
return { action, status: "skipped", detail: "No kit.json in fork" };
|
|
18455
18595
|
}
|
|
18456
18596
|
const upstreamRoot = resolveUpstreamAssetRoot(kitId);
|
|
18457
|
-
const upstreamManifestPath =
|
|
18458
|
-
if (!
|
|
18597
|
+
const upstreamManifestPath = path30.resolve(upstreamRoot, "kit.json");
|
|
18598
|
+
if (!fs23.existsSync(upstreamManifestPath)) {
|
|
18459
18599
|
return { action, status: "skipped", detail: "No kit.json in upstream kit" };
|
|
18460
18600
|
}
|
|
18461
18601
|
let forkManifest;
|
|
18462
18602
|
let upstreamManifest;
|
|
18463
18603
|
try {
|
|
18464
|
-
forkManifest = JSON.parse(
|
|
18465
|
-
upstreamManifest = JSON.parse(
|
|
18604
|
+
forkManifest = JSON.parse(fs23.readFileSync(forkManifestPath, "utf8"));
|
|
18605
|
+
upstreamManifest = JSON.parse(fs23.readFileSync(upstreamManifestPath, "utf8"));
|
|
18466
18606
|
} catch {
|
|
18467
18607
|
return { action, status: "error", detail: "Failed to parse kit.json" };
|
|
18468
18608
|
}
|
|
@@ -18478,7 +18618,7 @@ function execPatchManifest(action, forkPath, kitId) {
|
|
|
18478
18618
|
if (patched.length === 0) {
|
|
18479
18619
|
return { action, status: "skipped", detail: "kit.json alignment fields already match" };
|
|
18480
18620
|
}
|
|
18481
|
-
|
|
18621
|
+
fs23.writeFileSync(forkManifestPath, JSON.stringify(updated, null, 2) + "\n", "utf8");
|
|
18482
18622
|
return { action, status: "applied", detail: `Patched kit.json fields: ${patched.join(", ")}` };
|
|
18483
18623
|
}
|
|
18484
18624
|
|
|
@@ -18489,35 +18629,35 @@ init_fork_remote();
|
|
|
18489
18629
|
init_github_resolver();
|
|
18490
18630
|
init_client2();
|
|
18491
18631
|
function resolveInForkJobsDir(forkPath) {
|
|
18492
|
-
return
|
|
18632
|
+
return path32.resolve(resolveInForkStateDir(forkPath), "jobs");
|
|
18493
18633
|
}
|
|
18494
18634
|
function resolveJobPath(jobId, kitId, forkId) {
|
|
18495
18635
|
const forkPath = lookupKitForkPath(kitId, forkId);
|
|
18496
18636
|
if (forkPath) {
|
|
18497
|
-
return
|
|
18637
|
+
return path32.resolve(resolveInForkJobsDir(forkPath), `${jobId}.json`);
|
|
18498
18638
|
}
|
|
18499
|
-
return
|
|
18639
|
+
return path32.resolve(resolveKitForksOrphanJobsDir(), `${jobId}.json`);
|
|
18500
18640
|
}
|
|
18501
18641
|
function resolveOrphanJobPath(jobId) {
|
|
18502
|
-
return
|
|
18642
|
+
return path32.resolve(resolveKitForksOrphanJobsDir(), `${jobId}.json`);
|
|
18503
18643
|
}
|
|
18504
18644
|
function generateJobId() {
|
|
18505
18645
|
return `kfj-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`;
|
|
18506
18646
|
}
|
|
18507
18647
|
function parseJobFile(p35) {
|
|
18508
|
-
if (!
|
|
18648
|
+
if (!fs25.existsSync(p35)) return null;
|
|
18509
18649
|
try {
|
|
18510
|
-
return JSON.parse(
|
|
18650
|
+
return JSON.parse(fs25.readFileSync(p35, "utf8"));
|
|
18511
18651
|
} catch {
|
|
18512
18652
|
return null;
|
|
18513
18653
|
}
|
|
18514
18654
|
}
|
|
18515
18655
|
function findJobPath(jobId) {
|
|
18516
18656
|
const orphanPath = resolveOrphanJobPath(jobId);
|
|
18517
|
-
if (
|
|
18657
|
+
if (fs25.existsSync(orphanPath)) return orphanPath;
|
|
18518
18658
|
for (const reg of listKitForkRegistrations()) {
|
|
18519
|
-
const p35 =
|
|
18520
|
-
if (
|
|
18659
|
+
const p35 = path32.resolve(resolveInForkJobsDir(reg.forkPath), `${jobId}.json`);
|
|
18660
|
+
if (fs25.existsSync(p35)) return p35;
|
|
18521
18661
|
}
|
|
18522
18662
|
return null;
|
|
18523
18663
|
}
|
|
@@ -18527,8 +18667,8 @@ function readJob(jobId) {
|
|
|
18527
18667
|
}
|
|
18528
18668
|
function writeJob(job) {
|
|
18529
18669
|
const p35 = resolveJobPath(job.jobId, job.kitId, job.forkId);
|
|
18530
|
-
|
|
18531
|
-
|
|
18670
|
+
fs25.mkdirSync(path32.dirname(p35), { recursive: true });
|
|
18671
|
+
fs25.writeFileSync(p35, JSON.stringify(job, null, 2) + "\n", "utf8");
|
|
18532
18672
|
}
|
|
18533
18673
|
function patchJob(jobId, status, patch) {
|
|
18534
18674
|
const existingPath = findJobPath(jobId);
|
|
@@ -18537,30 +18677,30 @@ function patchJob(jobId, status, patch) {
|
|
|
18537
18677
|
if (!job) return null;
|
|
18538
18678
|
const updated = { ...job, ...patch, status };
|
|
18539
18679
|
const targetPath = resolveJobPath(updated.jobId, updated.kitId, updated.forkId);
|
|
18540
|
-
if (
|
|
18541
|
-
|
|
18542
|
-
|
|
18680
|
+
if (path32.resolve(existingPath) !== path32.resolve(targetPath)) {
|
|
18681
|
+
fs25.mkdirSync(path32.dirname(targetPath), { recursive: true });
|
|
18682
|
+
fs25.rmSync(existingPath, { force: true });
|
|
18543
18683
|
}
|
|
18544
|
-
|
|
18545
|
-
|
|
18684
|
+
fs25.mkdirSync(path32.dirname(targetPath), { recursive: true });
|
|
18685
|
+
fs25.writeFileSync(targetPath, JSON.stringify(updated, null, 2) + "\n", "utf8");
|
|
18546
18686
|
return updated;
|
|
18547
18687
|
}
|
|
18548
18688
|
function collectAllJobFiles() {
|
|
18549
18689
|
const files = [];
|
|
18550
18690
|
for (const reg of listKitForkRegistrations()) {
|
|
18551
18691
|
const dir = resolveInForkJobsDir(reg.forkPath);
|
|
18552
|
-
if (!
|
|
18553
|
-
for (const entry of
|
|
18692
|
+
if (!fs25.existsSync(dir)) continue;
|
|
18693
|
+
for (const entry of fs25.readdirSync(dir, { withFileTypes: true })) {
|
|
18554
18694
|
if (entry.isFile() && entry.name.endsWith(".json")) {
|
|
18555
|
-
files.push(
|
|
18695
|
+
files.push(path32.resolve(dir, entry.name));
|
|
18556
18696
|
}
|
|
18557
18697
|
}
|
|
18558
18698
|
}
|
|
18559
18699
|
const orphanDir = resolveKitForksOrphanJobsDir();
|
|
18560
|
-
if (
|
|
18561
|
-
for (const entry of
|
|
18700
|
+
if (fs25.existsSync(orphanDir)) {
|
|
18701
|
+
for (const entry of fs25.readdirSync(orphanDir, { withFileTypes: true })) {
|
|
18562
18702
|
if (entry.isFile() && entry.name.endsWith(".json")) {
|
|
18563
|
-
files.push(
|
|
18703
|
+
files.push(path32.resolve(orphanDir, entry.name));
|
|
18564
18704
|
}
|
|
18565
18705
|
}
|
|
18566
18706
|
}
|
|
@@ -18598,7 +18738,7 @@ function pruneKitForkSyncJobs(retentionMs = 7 * 24 * 60 * 60 * 1e3) {
|
|
|
18598
18738
|
if (!terminal.includes(job.status)) continue;
|
|
18599
18739
|
const ts = new Date(job.completedAt ?? job.createdAt).getTime();
|
|
18600
18740
|
if (ts < cutoff) {
|
|
18601
|
-
|
|
18741
|
+
fs25.rmSync(filePath, { force: true });
|
|
18602
18742
|
pruned++;
|
|
18603
18743
|
}
|
|
18604
18744
|
}
|
|
@@ -18858,8 +18998,8 @@ async function requireGithubToken() {
|
|
|
18858
18998
|
async function kitForkCreate(opts) {
|
|
18859
18999
|
const accessToken = await requireGithubToken();
|
|
18860
19000
|
const upstream = parseRepoRef(opts.upstream);
|
|
18861
|
-
const absOut =
|
|
18862
|
-
if (
|
|
19001
|
+
const absOut = path33.resolve(opts.out);
|
|
19002
|
+
if (fs26.existsSync(absOut) && fs26.readdirSync(absOut).length > 0) {
|
|
18863
19003
|
throw new Error(`Destination ${absOut} already exists and is not empty.`);
|
|
18864
19004
|
}
|
|
18865
19005
|
p17.log.step(`Forking ${upstream.owner}/${upstream.repo} on GitHub...`);
|
|
@@ -19260,9 +19400,9 @@ Examples:
|
|
|
19260
19400
|
}
|
|
19261
19401
|
|
|
19262
19402
|
// src/commands/kit-fork.ts
|
|
19403
|
+
import pc29 from "picocolors";
|
|
19263
19404
|
init_banner();
|
|
19264
19405
|
init_table_renderer();
|
|
19265
|
-
import pc29 from "picocolors";
|
|
19266
19406
|
|
|
19267
19407
|
// src/utils/progress.ts
|
|
19268
19408
|
import pc28 from "picocolors";
|
|
@@ -19304,20 +19444,20 @@ init_fork_trace();
|
|
|
19304
19444
|
// src/kits/fork-authority.ts
|
|
19305
19445
|
init_home();
|
|
19306
19446
|
init_kit_forks_home();
|
|
19307
|
-
import
|
|
19308
|
-
import
|
|
19447
|
+
import crypto2 from "node:crypto";
|
|
19448
|
+
import fs27 from "node:fs";
|
|
19309
19449
|
import os8 from "node:os";
|
|
19310
|
-
import
|
|
19450
|
+
import path34 from "node:path";
|
|
19311
19451
|
function resolveAuthorityHomeDir() {
|
|
19312
19452
|
const env = process.env.GROWTHUB_AUTHORITY_HOME?.trim();
|
|
19313
|
-
if (env) return
|
|
19314
|
-
return
|
|
19453
|
+
if (env) return path34.resolve(expandHomePrefix(env));
|
|
19454
|
+
return path34.resolve(os8.homedir(), ".growthub", "authority");
|
|
19315
19455
|
}
|
|
19316
19456
|
function resolveAuthorityIssuersPath() {
|
|
19317
|
-
return
|
|
19457
|
+
return path34.resolve(resolveAuthorityHomeDir(), "issuers.json");
|
|
19318
19458
|
}
|
|
19319
19459
|
function resolveInForkAuthorityPath(forkPath) {
|
|
19320
|
-
return
|
|
19460
|
+
return path34.resolve(resolveInForkStateDir(forkPath), "authority.json");
|
|
19321
19461
|
}
|
|
19322
19462
|
function canonicalize(value) {
|
|
19323
19463
|
if (value === null) return "null";
|
|
@@ -19363,13 +19503,13 @@ function computePolicyHash(policy) {
|
|
|
19363
19503
|
interactiveConflicts: policy.interactiveConflicts,
|
|
19364
19504
|
allowedScripts: [...policy.allowedScripts].sort()
|
|
19365
19505
|
};
|
|
19366
|
-
return
|
|
19506
|
+
return crypto2.createHash("sha256").update(canonicalize(shape), "utf8").digest("hex");
|
|
19367
19507
|
}
|
|
19368
19508
|
function readIssuerRegistry() {
|
|
19369
19509
|
const p35 = resolveAuthorityIssuersPath();
|
|
19370
|
-
if (!
|
|
19510
|
+
if (!fs27.existsSync(p35)) return { version: 1, issuers: [] };
|
|
19371
19511
|
try {
|
|
19372
|
-
const parsed = JSON.parse(
|
|
19512
|
+
const parsed = JSON.parse(fs27.readFileSync(p35, "utf8"));
|
|
19373
19513
|
if (!parsed || !Array.isArray(parsed.issuers)) return { version: 1, issuers: [] };
|
|
19374
19514
|
return { version: 1, issuers: parsed.issuers.filter(isValidIssuer) };
|
|
19375
19515
|
} catch {
|
|
@@ -19383,8 +19523,8 @@ function isValidIssuer(x) {
|
|
|
19383
19523
|
}
|
|
19384
19524
|
function writeIssuerRegistry(registry) {
|
|
19385
19525
|
const p35 = resolveAuthorityIssuersPath();
|
|
19386
|
-
|
|
19387
|
-
|
|
19526
|
+
fs27.mkdirSync(path34.dirname(p35), { recursive: true });
|
|
19527
|
+
fs27.writeFileSync(p35, JSON.stringify({ version: 1, issuers: registry.issuers }, null, 2) + "\n", "utf8");
|
|
19388
19528
|
}
|
|
19389
19529
|
function upsertIssuer(issuer) {
|
|
19390
19530
|
if (!isValidIssuer(issuer)) {
|
|
@@ -19442,8 +19582,8 @@ function verifyAuthorityEnvelope(envelope, options = {}) {
|
|
|
19442
19582
|
try {
|
|
19443
19583
|
const { signature: _omit, ...unsigned } = envelope;
|
|
19444
19584
|
const payload = buildSigningPayload(unsigned);
|
|
19445
|
-
const pubKey =
|
|
19446
|
-
ok =
|
|
19585
|
+
const pubKey = crypto2.createPublicKey({ key: issuer.publicKeyPem, format: "pem" });
|
|
19586
|
+
ok = crypto2.verify(null, Buffer.from(payload, "utf8"), pubKey, signatureBytes);
|
|
19447
19587
|
} catch (err) {
|
|
19448
19588
|
return { ok: false, reason: "bad-signature", detail: err.message };
|
|
19449
19589
|
}
|
|
@@ -19457,11 +19597,11 @@ function isWellFormedEnvelope(x) {
|
|
|
19457
19597
|
}
|
|
19458
19598
|
function readForkAuthorityState(forkPath) {
|
|
19459
19599
|
const p35 = resolveInForkAuthorityPath(forkPath);
|
|
19460
|
-
if (!
|
|
19600
|
+
if (!fs27.existsSync(p35)) {
|
|
19461
19601
|
return { state: "none", version: 1, updatedAt: (/* @__PURE__ */ new Date(0)).toISOString() };
|
|
19462
19602
|
}
|
|
19463
19603
|
try {
|
|
19464
|
-
const parsed = JSON.parse(
|
|
19604
|
+
const parsed = JSON.parse(fs27.readFileSync(p35, "utf8"));
|
|
19465
19605
|
if (!parsed || parsed.version !== 1) {
|
|
19466
19606
|
return { state: "none", version: 1, updatedAt: (/* @__PURE__ */ new Date(0)).toISOString() };
|
|
19467
19607
|
}
|
|
@@ -19472,8 +19612,8 @@ function readForkAuthorityState(forkPath) {
|
|
|
19472
19612
|
}
|
|
19473
19613
|
function writeForkAuthorityState(forkPath, state) {
|
|
19474
19614
|
const p35 = resolveInForkAuthorityPath(forkPath);
|
|
19475
|
-
|
|
19476
|
-
|
|
19615
|
+
fs27.mkdirSync(path34.dirname(p35), { recursive: true });
|
|
19616
|
+
fs27.writeFileSync(p35, JSON.stringify(state, null, 2) + "\n", "utf8");
|
|
19477
19617
|
}
|
|
19478
19618
|
function attachAuthorityEnvelope(forkPath, envelope, options = {}) {
|
|
19479
19619
|
const verification = verifyAuthorityEnvelope(envelope, options);
|
|
@@ -20090,6 +20230,7 @@ async function runRegisterFlow() {
|
|
|
20090
20230
|
label: labelInput.trim() || void 0
|
|
20091
20231
|
});
|
|
20092
20232
|
spinner13.stop(pc29.green("Fork registered."));
|
|
20233
|
+
track("fork_registered", { kit_id: reg.kitId });
|
|
20093
20234
|
p18.note(
|
|
20094
20235
|
[
|
|
20095
20236
|
`Fork ID: ${pc29.cyan(reg.forkId)}`,
|
|
@@ -20136,6 +20277,7 @@ async function runStatusFlow() {
|
|
|
20136
20277
|
try {
|
|
20137
20278
|
const report = detectKitForkDrift(reg);
|
|
20138
20279
|
spinner13.stop(pc29.green("Analysis complete."));
|
|
20280
|
+
track("fork_sync_preview_started", { kit_id: reg.kitId, drift_severity: report.overallSeverity });
|
|
20139
20281
|
printDriftReport(report);
|
|
20140
20282
|
} catch (err) {
|
|
20141
20283
|
spinner13.stop(pc29.red("Drift detection failed."));
|
|
@@ -20221,6 +20363,13 @@ async function runHealFlow() {
|
|
|
20221
20363
|
healSpinner.stop(
|
|
20222
20364
|
job.status === "completed" ? pc29.green(isDryRun ? "Dry run complete." : "Heal complete.") : pc29.red("Heal encountered errors.")
|
|
20223
20365
|
);
|
|
20366
|
+
if (job.status === "completed" && !isDryRun) {
|
|
20367
|
+
track("fork_sync_heal_applied", {
|
|
20368
|
+
kit_id: reg.kitId,
|
|
20369
|
+
action_count: plan.actions.length,
|
|
20370
|
+
applied_count: job.healResult?.appliedCount ?? 0
|
|
20371
|
+
});
|
|
20372
|
+
}
|
|
20224
20373
|
if (job.healResult) {
|
|
20225
20374
|
const r = job.healResult;
|
|
20226
20375
|
console.log("");
|
|
@@ -20767,7 +20916,7 @@ function registerAuthoritySubcommands(parentCmd) {
|
|
|
20767
20916
|
}
|
|
20768
20917
|
let raw;
|
|
20769
20918
|
try {
|
|
20770
|
-
raw =
|
|
20919
|
+
raw = fs28.readFileSync(opts.file, "utf8");
|
|
20771
20920
|
} catch (err) {
|
|
20772
20921
|
console.error(pc29.red(`Cannot read envelope file: ${err.message}`));
|
|
20773
20922
|
process.exitCode = 1;
|
|
@@ -20860,7 +21009,7 @@ function registerAuthoritySubcommands(parentCmd) {
|
|
|
20860
21009
|
}
|
|
20861
21010
|
let pem;
|
|
20862
21011
|
try {
|
|
20863
|
-
pem =
|
|
21012
|
+
pem = fs28.readFileSync(opts.key, "utf8");
|
|
20864
21013
|
} catch (err) {
|
|
20865
21014
|
console.error(pc29.red(`Cannot read key file: ${err.message}`));
|
|
20866
21015
|
process.exitCode = 1;
|
|
@@ -20944,13 +21093,13 @@ function box(lines) {
|
|
|
20944
21093
|
function stripAnsi2(str) {
|
|
20945
21094
|
return str.replace(/\x1B\[[0-9;]*m/g, "");
|
|
20946
21095
|
}
|
|
20947
|
-
function
|
|
21096
|
+
function terminalLink2(label, href) {
|
|
20948
21097
|
return `\x1B]8;;${href}\x07${label}\x1B]8;;\x07`;
|
|
20949
21098
|
}
|
|
20950
21099
|
function folderOpenLabel(folderPath) {
|
|
20951
21100
|
const href = pathToFileURL2(folderPath).href;
|
|
20952
21101
|
const label = process.platform === "darwin" ? "Open in Finder" : process.platform === "win32" ? "Open in Explorer" : "Open folder";
|
|
20953
|
-
return
|
|
21102
|
+
return terminalLink2(label, href);
|
|
20954
21103
|
}
|
|
20955
21104
|
function renderProgressBar2(progress) {
|
|
20956
21105
|
if (!process.stdout.isTTY) return;
|
|
@@ -21180,6 +21329,8 @@ async function runDownload(kitId, opts) {
|
|
|
21180
21329
|
const result = downloadBundledKit(resolvedId, opts.out, {
|
|
21181
21330
|
onProgress: renderProgressBar2
|
|
21182
21331
|
});
|
|
21332
|
+
track("kit_download_completed", { kit_id: resolvedId });
|
|
21333
|
+
printActivationNudge("kit_download");
|
|
21183
21334
|
console.log("");
|
|
21184
21335
|
console.log(pc30.green(pc30.bold("Kit exported successfully.")));
|
|
21185
21336
|
console.log("");
|
|
@@ -21318,6 +21469,7 @@ Examples:
|
|
|
21318
21469
|
const result = downloadBundledKit(resolvedId, opts.out, {
|
|
21319
21470
|
onProgress: renderProgressBar2
|
|
21320
21471
|
});
|
|
21472
|
+
track("kit_download_completed", { kit_id: resolvedId });
|
|
21321
21473
|
console.log("");
|
|
21322
21474
|
console.log(pc30.bold("Exported folder:"), pc30.cyan(result.folderPath));
|
|
21323
21475
|
console.log(pc30.bold("Open folder: "), folderOpenLabel(result.folderPath));
|
|
@@ -21347,7 +21499,7 @@ Examples:
|
|
|
21347
21499
|
$ growthub kit validate ./my-kit
|
|
21348
21500
|
$ growthub kit validate ~/kits/growthub-open-higgsfield-studio-v1
|
|
21349
21501
|
`).action((kitPath) => {
|
|
21350
|
-
const resolvedPath =
|
|
21502
|
+
const resolvedPath = path35.resolve(kitPath);
|
|
21351
21503
|
const result = validateKitDirectory(resolvedPath);
|
|
21352
21504
|
console.log("");
|
|
21353
21505
|
console.log(pc30.bold("Kit: " + result.kitId) + pc30.dim(" schema v" + result.schemaVersion));
|
|
@@ -21392,13 +21544,13 @@ Examples:
|
|
|
21392
21544
|
}
|
|
21393
21545
|
|
|
21394
21546
|
// src/commands/template.ts
|
|
21395
|
-
import
|
|
21547
|
+
import path37 from "node:path";
|
|
21396
21548
|
import * as p20 from "@clack/prompts";
|
|
21397
21549
|
import pc31 from "picocolors";
|
|
21398
21550
|
|
|
21399
21551
|
// src/templates/service.ts
|
|
21400
|
-
import
|
|
21401
|
-
import
|
|
21552
|
+
import fs29 from "node:fs";
|
|
21553
|
+
import path36 from "node:path";
|
|
21402
21554
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
21403
21555
|
|
|
21404
21556
|
// src/templates/catalog.ts
|
|
@@ -21720,12 +21872,12 @@ var TEMPLATE_CATALOG = [
|
|
|
21720
21872
|
|
|
21721
21873
|
// src/templates/service.ts
|
|
21722
21874
|
function resolveSharedTemplatesRoot() {
|
|
21723
|
-
const moduleDir =
|
|
21875
|
+
const moduleDir = path36.dirname(fileURLToPath6(import.meta.url));
|
|
21724
21876
|
for (const candidate of [
|
|
21725
|
-
|
|
21726
|
-
|
|
21877
|
+
path36.resolve(moduleDir, "../../assets/shared-templates"),
|
|
21878
|
+
path36.resolve(moduleDir, "../assets/shared-templates")
|
|
21727
21879
|
]) {
|
|
21728
|
-
if (
|
|
21880
|
+
if (fs29.existsSync(candidate)) return candidate;
|
|
21729
21881
|
}
|
|
21730
21882
|
throw new Error("Shared template assets not found at cli/assets/shared-templates/");
|
|
21731
21883
|
}
|
|
@@ -21761,15 +21913,15 @@ function getArtifact(slugOrId) {
|
|
|
21761
21913
|
const artifact = resolveSlug(slugOrId);
|
|
21762
21914
|
if (!artifact) throw new Error(`Unknown template '${slugOrId}'. Run 'growthub template list' to browse.`);
|
|
21763
21915
|
const root = resolveSharedTemplatesRoot();
|
|
21764
|
-
const absolutePath =
|
|
21765
|
-
if (!
|
|
21766
|
-
return { artifact, content:
|
|
21916
|
+
const absolutePath = path36.resolve(root, artifact.path);
|
|
21917
|
+
if (!fs29.existsSync(absolutePath)) throw new Error(`Template file missing: ${absolutePath}`);
|
|
21918
|
+
return { artifact, content: fs29.readFileSync(absolutePath, "utf8"), absolutePath };
|
|
21767
21919
|
}
|
|
21768
21920
|
function copyArtifact(slugOrId, destDir) {
|
|
21769
21921
|
const resolved = getArtifact(slugOrId);
|
|
21770
|
-
|
|
21771
|
-
const destPath =
|
|
21772
|
-
|
|
21922
|
+
fs29.mkdirSync(destDir, { recursive: true });
|
|
21923
|
+
const destPath = path36.resolve(destDir, path36.basename(resolved.absolutePath));
|
|
21924
|
+
fs29.copyFileSync(resolved.absolutePath, destPath);
|
|
21773
21925
|
return destPath;
|
|
21774
21926
|
}
|
|
21775
21927
|
var GROUP_ORDER = ["ad-formats", "scene-modules/hooks", "scene-modules/body", "scene-modules/cta", "marketing-frameworks"];
|
|
@@ -22011,7 +22163,7 @@ async function runTemplatePicker(opts) {
|
|
|
22011
22163
|
p20.cancel("Cancelled.");
|
|
22012
22164
|
process.exit(0);
|
|
22013
22165
|
}
|
|
22014
|
-
const destDir =
|
|
22166
|
+
const destDir = path37.resolve(destInput.replace(/^~/, process.env["HOME"] ?? ""));
|
|
22015
22167
|
const destPath = copyArtifact(selected.id, destDir);
|
|
22016
22168
|
p20.outro(pc31.green("Copied \u2192 ") + destPath);
|
|
22017
22169
|
return "done";
|
|
@@ -22085,7 +22237,7 @@ Any agent or kit resolves them by slug.
|
|
|
22085
22237
|
return;
|
|
22086
22238
|
}
|
|
22087
22239
|
if (opts.out) {
|
|
22088
|
-
const destDir =
|
|
22240
|
+
const destDir = path37.resolve(opts.out.replace(/^~/, process.env["HOME"] ?? ""));
|
|
22089
22241
|
try {
|
|
22090
22242
|
const dest = copyArtifact(artifact.id, destDir);
|
|
22091
22243
|
console.log(pc31.green("Copied \u2192 ") + dest);
|
|
@@ -23169,8 +23321,8 @@ Examples:
|
|
|
23169
23321
|
// src/commands/pipeline.ts
|
|
23170
23322
|
init_session_store();
|
|
23171
23323
|
init_hosted_client();
|
|
23172
|
-
import
|
|
23173
|
-
import
|
|
23324
|
+
import fs33 from "node:fs";
|
|
23325
|
+
import path41 from "node:path";
|
|
23174
23326
|
import * as p22 from "@clack/prompts";
|
|
23175
23327
|
import pc34 from "picocolors";
|
|
23176
23328
|
|
|
@@ -23680,40 +23832,40 @@ function renderPreSaveReview(input) {
|
|
|
23680
23832
|
|
|
23681
23833
|
// src/runtime/artifact-contracts/index.ts
|
|
23682
23834
|
init_home();
|
|
23683
|
-
import
|
|
23684
|
-
import
|
|
23835
|
+
import fs30 from "node:fs";
|
|
23836
|
+
import path38 from "node:path";
|
|
23685
23837
|
import { randomBytes as randomBytes7 } from "node:crypto";
|
|
23686
23838
|
function generateArtifactId() {
|
|
23687
23839
|
return `art_${randomBytes7(8).toString("hex")}`;
|
|
23688
23840
|
}
|
|
23689
23841
|
function resolveArtifactsDir() {
|
|
23690
|
-
return
|
|
23842
|
+
return path38.resolve(resolvePaperclipHomeDir(), "artifacts");
|
|
23691
23843
|
}
|
|
23692
23844
|
function resolveArtifactManifestPath(artifactId) {
|
|
23693
|
-
return
|
|
23845
|
+
return path38.resolve(resolveArtifactsDir(), `${artifactId}.json`);
|
|
23694
23846
|
}
|
|
23695
23847
|
function readLocalManifest(artifactId) {
|
|
23696
23848
|
const filePath = resolveArtifactManifestPath(artifactId);
|
|
23697
|
-
if (!
|
|
23849
|
+
if (!fs30.existsSync(filePath)) return null;
|
|
23698
23850
|
try {
|
|
23699
|
-
return JSON.parse(
|
|
23851
|
+
return JSON.parse(fs30.readFileSync(filePath, "utf-8"));
|
|
23700
23852
|
} catch {
|
|
23701
23853
|
return null;
|
|
23702
23854
|
}
|
|
23703
23855
|
}
|
|
23704
23856
|
function writeLocalManifest(manifest) {
|
|
23705
23857
|
const dir = resolveArtifactsDir();
|
|
23706
|
-
|
|
23858
|
+
fs30.mkdirSync(dir, { recursive: true });
|
|
23707
23859
|
const filePath = resolveArtifactManifestPath(manifest.id);
|
|
23708
|
-
|
|
23860
|
+
fs30.writeFileSync(filePath, `${JSON.stringify(manifest, null, 2)}
|
|
23709
23861
|
`, { mode: 384 });
|
|
23710
23862
|
}
|
|
23711
23863
|
function listLocalManifests() {
|
|
23712
23864
|
const dir = resolveArtifactsDir();
|
|
23713
|
-
if (!
|
|
23714
|
-
return
|
|
23865
|
+
if (!fs30.existsSync(dir)) return [];
|
|
23866
|
+
return fs30.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => {
|
|
23715
23867
|
try {
|
|
23716
|
-
const content =
|
|
23868
|
+
const content = fs30.readFileSync(path38.resolve(dir, entry.name), "utf-8");
|
|
23717
23869
|
return JSON.parse(content);
|
|
23718
23870
|
} catch {
|
|
23719
23871
|
return null;
|
|
@@ -23791,8 +23943,8 @@ function createArtifactStore() {
|
|
|
23791
23943
|
|
|
23792
23944
|
// src/runtime/native-intelligence/index.ts
|
|
23793
23945
|
init_home();
|
|
23794
|
-
import
|
|
23795
|
-
import
|
|
23946
|
+
import fs32 from "node:fs";
|
|
23947
|
+
import path40 from "node:path";
|
|
23796
23948
|
|
|
23797
23949
|
// src/runtime/native-intelligence/contract.ts
|
|
23798
23950
|
var DEFAULT_INTELLIGENCE_CONFIG = {
|
|
@@ -25063,8 +25215,8 @@ function parseJsonSafe4(text69) {
|
|
|
25063
25215
|
}
|
|
25064
25216
|
|
|
25065
25217
|
// src/runtime/native-intelligence/marketing-context-builder.ts
|
|
25066
|
-
import
|
|
25067
|
-
import
|
|
25218
|
+
import fs31 from "node:fs";
|
|
25219
|
+
import path39 from "node:path";
|
|
25068
25220
|
var CONTEXT_BUILDER_SYSTEM_PROMPT = `You are a marketing strategist drafting a product-marketing-context document.
|
|
25069
25221
|
|
|
25070
25222
|
You receive project artifacts (README content, package.json metadata, any landing page content) and produce a structured product-marketing-context.md with 12 sections.
|
|
@@ -25083,17 +25235,17 @@ var MAX_ARTIFACT_LENGTH = 8e3;
|
|
|
25083
25235
|
function scanProjectArtifacts(projectDir, existingContext) {
|
|
25084
25236
|
const artifacts = { otherFiles: [] };
|
|
25085
25237
|
for (const name of ["README.md", "readme.md", "Readme.md", "README"]) {
|
|
25086
|
-
const readmePath =
|
|
25087
|
-
if (
|
|
25088
|
-
const content =
|
|
25238
|
+
const readmePath = path39.join(projectDir, name);
|
|
25239
|
+
if (fs31.existsSync(readmePath)) {
|
|
25240
|
+
const content = fs31.readFileSync(readmePath, "utf-8");
|
|
25089
25241
|
artifacts.readme = content.slice(0, MAX_ARTIFACT_LENGTH);
|
|
25090
25242
|
break;
|
|
25091
25243
|
}
|
|
25092
25244
|
}
|
|
25093
|
-
const pkgPath =
|
|
25094
|
-
if (
|
|
25245
|
+
const pkgPath = path39.join(projectDir, "package.json");
|
|
25246
|
+
if (fs31.existsSync(pkgPath)) {
|
|
25095
25247
|
try {
|
|
25096
|
-
artifacts.packageJson = JSON.parse(
|
|
25248
|
+
artifacts.packageJson = JSON.parse(fs31.readFileSync(pkgPath, "utf-8"));
|
|
25097
25249
|
} catch {
|
|
25098
25250
|
}
|
|
25099
25251
|
}
|
|
@@ -25105,15 +25257,15 @@ function scanProjectArtifacts(projectDir, existingContext) {
|
|
|
25105
25257
|
".claude/product-marketing-context.md",
|
|
25106
25258
|
"brands/_template/product-marketing-context.md"
|
|
25107
25259
|
]) {
|
|
25108
|
-
const ctxPath =
|
|
25109
|
-
if (
|
|
25110
|
-
artifacts.existingContext =
|
|
25260
|
+
const ctxPath = path39.join(projectDir, candidate);
|
|
25261
|
+
if (fs31.existsSync(ctxPath)) {
|
|
25262
|
+
artifacts.existingContext = fs31.readFileSync(ctxPath, "utf-8");
|
|
25111
25263
|
break;
|
|
25112
25264
|
}
|
|
25113
25265
|
}
|
|
25114
25266
|
}
|
|
25115
25267
|
for (const name of ["CONTRIBUTING.md", "AGENTS.md", "landing-page.md", "about.md"]) {
|
|
25116
|
-
if (
|
|
25268
|
+
if (fs31.existsSync(path39.join(projectDir, name))) {
|
|
25117
25269
|
artifacts.otherFiles.push(name);
|
|
25118
25270
|
}
|
|
25119
25271
|
}
|
|
@@ -25354,15 +25506,15 @@ function cleanMarkdownResponse(text69) {
|
|
|
25354
25506
|
|
|
25355
25507
|
// src/runtime/native-intelligence/index.ts
|
|
25356
25508
|
function resolveConfigPath2() {
|
|
25357
|
-
return
|
|
25509
|
+
return path40.resolve(resolvePaperclipHomeDir(), "native-intelligence", "config.json");
|
|
25358
25510
|
}
|
|
25359
25511
|
function readIntelligenceConfig() {
|
|
25360
25512
|
const configPath = resolveConfigPath2();
|
|
25361
|
-
if (!
|
|
25513
|
+
if (!fs32.existsSync(configPath)) {
|
|
25362
25514
|
return { ...DEFAULT_INTELLIGENCE_CONFIG };
|
|
25363
25515
|
}
|
|
25364
25516
|
try {
|
|
25365
|
-
const raw = JSON.parse(
|
|
25517
|
+
const raw = JSON.parse(fs32.readFileSync(configPath, "utf-8"));
|
|
25366
25518
|
return {
|
|
25367
25519
|
modelId: validateModelId(raw.modelId),
|
|
25368
25520
|
backendType: raw.backendType === "hosted" ? "hosted" : "local",
|
|
@@ -25379,8 +25531,8 @@ function readIntelligenceConfig() {
|
|
|
25379
25531
|
}
|
|
25380
25532
|
function writeIntelligenceConfig(config) {
|
|
25381
25533
|
const configPath = resolveConfigPath2();
|
|
25382
|
-
|
|
25383
|
-
|
|
25534
|
+
fs32.mkdirSync(path40.dirname(configPath), { recursive: true });
|
|
25535
|
+
fs32.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
|
|
25384
25536
|
`, "utf-8");
|
|
25385
25537
|
}
|
|
25386
25538
|
function validateModelId(id) {
|
|
@@ -25736,9 +25888,9 @@ async function runPipelineAssembler(opts) {
|
|
|
25736
25888
|
}
|
|
25737
25889
|
}
|
|
25738
25890
|
function loadPipelineFromFileOrJson(input) {
|
|
25739
|
-
const resolvedPath =
|
|
25740
|
-
if (
|
|
25741
|
-
const content =
|
|
25891
|
+
const resolvedPath = path41.resolve(input);
|
|
25892
|
+
if (fs33.existsSync(resolvedPath)) {
|
|
25893
|
+
const content = fs33.readFileSync(resolvedPath, "utf-8");
|
|
25742
25894
|
return deserializePipeline(JSON.parse(content));
|
|
25743
25895
|
}
|
|
25744
25896
|
try {
|
|
@@ -26283,8 +26435,8 @@ Examples:
|
|
|
26283
26435
|
}
|
|
26284
26436
|
|
|
26285
26437
|
// src/commands/workflow.ts
|
|
26286
|
-
import
|
|
26287
|
-
import
|
|
26438
|
+
import fs35 from "node:fs";
|
|
26439
|
+
import path43 from "node:path";
|
|
26288
26440
|
import * as p23 from "@clack/prompts";
|
|
26289
26441
|
import pc37 from "picocolors";
|
|
26290
26442
|
init_session_store();
|
|
@@ -26292,15 +26444,15 @@ init_hosted_client();
|
|
|
26292
26444
|
|
|
26293
26445
|
// src/runtime/workflow-hygiene/labels.ts
|
|
26294
26446
|
init_home();
|
|
26295
|
-
import
|
|
26296
|
-
import
|
|
26447
|
+
import fs34 from "node:fs";
|
|
26448
|
+
import path42 from "node:path";
|
|
26297
26449
|
function resolveStorePath() {
|
|
26298
|
-
return
|
|
26450
|
+
return path42.resolve(resolvePaperclipHomeDir(), "workflow-hygiene", "labels.json");
|
|
26299
26451
|
}
|
|
26300
26452
|
function readStoreFile(filePath) {
|
|
26301
|
-
if (!
|
|
26453
|
+
if (!fs34.existsSync(filePath)) return { records: [] };
|
|
26302
26454
|
try {
|
|
26303
|
-
const raw = JSON.parse(
|
|
26455
|
+
const raw = JSON.parse(fs34.readFileSync(filePath, "utf-8"));
|
|
26304
26456
|
if (!Array.isArray(raw.records)) return { records: [] };
|
|
26305
26457
|
return raw;
|
|
26306
26458
|
} catch {
|
|
@@ -26308,8 +26460,8 @@ function readStoreFile(filePath) {
|
|
|
26308
26460
|
}
|
|
26309
26461
|
}
|
|
26310
26462
|
function writeStoreFile(filePath, data) {
|
|
26311
|
-
|
|
26312
|
-
|
|
26463
|
+
fs34.mkdirSync(path42.dirname(filePath), { recursive: true });
|
|
26464
|
+
fs34.writeFileSync(filePath, `${JSON.stringify(data, null, 2)}
|
|
26313
26465
|
`, "utf-8");
|
|
26314
26466
|
}
|
|
26315
26467
|
function inferDefaultLabel(name, createdAt, versionCount) {
|
|
@@ -26410,16 +26562,16 @@ function box5(lines) {
|
|
|
26410
26562
|
return [top, ...body, bottom].join("\n");
|
|
26411
26563
|
}
|
|
26412
26564
|
function resolveSavedWorkflowsDir() {
|
|
26413
|
-
return
|
|
26565
|
+
return path43.resolve(resolvePaperclipHomeDir(), "workflows");
|
|
26414
26566
|
}
|
|
26415
26567
|
function resolveDeletedWorkflowIdsPath() {
|
|
26416
|
-
return
|
|
26568
|
+
return path43.resolve(resolvePaperclipHomeDir(), "workflow-hygiene", "deleted-workflows.json");
|
|
26417
26569
|
}
|
|
26418
26570
|
function readDeletedWorkflowIds() {
|
|
26419
26571
|
const filePath = resolveDeletedWorkflowIdsPath();
|
|
26420
|
-
if (!
|
|
26572
|
+
if (!fs35.existsSync(filePath)) return /* @__PURE__ */ new Set();
|
|
26421
26573
|
try {
|
|
26422
|
-
const raw = JSON.parse(
|
|
26574
|
+
const raw = JSON.parse(fs35.readFileSync(filePath, "utf-8"));
|
|
26423
26575
|
if (!Array.isArray(raw?.workflowIds)) return /* @__PURE__ */ new Set();
|
|
26424
26576
|
return new Set(raw.workflowIds.filter((value) => typeof value === "string"));
|
|
26425
26577
|
} catch {
|
|
@@ -26428,8 +26580,8 @@ function readDeletedWorkflowIds() {
|
|
|
26428
26580
|
}
|
|
26429
26581
|
function writeDeletedWorkflowIds(ids) {
|
|
26430
26582
|
const filePath = resolveDeletedWorkflowIdsPath();
|
|
26431
|
-
|
|
26432
|
-
|
|
26583
|
+
fs35.mkdirSync(path43.dirname(filePath), { recursive: true });
|
|
26584
|
+
fs35.writeFileSync(filePath, `${JSON.stringify({ workflowIds: [...ids] }, null, 2)}
|
|
26433
26585
|
`, "utf-8");
|
|
26434
26586
|
}
|
|
26435
26587
|
function markWorkflowDeletedLocally(workflowId) {
|
|
@@ -26455,10 +26607,10 @@ function filterLocallyDeletedWorkflows(entries) {
|
|
|
26455
26607
|
}
|
|
26456
26608
|
function listLocalSavedWorkflows() {
|
|
26457
26609
|
const dir = resolveSavedWorkflowsDir();
|
|
26458
|
-
if (!
|
|
26459
|
-
const entries =
|
|
26610
|
+
if (!fs35.existsSync(dir)) return [];
|
|
26611
|
+
const entries = fs35.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".json")).map((e) => {
|
|
26460
26612
|
try {
|
|
26461
|
-
const raw = JSON.parse(
|
|
26613
|
+
const raw = JSON.parse(fs35.readFileSync(path43.resolve(dir, e.name), "utf-8"));
|
|
26462
26614
|
const pipeline = raw.pipeline ?? raw;
|
|
26463
26615
|
return {
|
|
26464
26616
|
filename: e.name,
|
|
@@ -26519,11 +26671,11 @@ async function archiveSavedWorkflow(entry) {
|
|
|
26519
26671
|
throw new Error("Local workflow entry is missing filename.");
|
|
26520
26672
|
}
|
|
26521
26673
|
const dir = resolveSavedWorkflowsDir();
|
|
26522
|
-
const archiveDir =
|
|
26523
|
-
|
|
26524
|
-
|
|
26525
|
-
|
|
26526
|
-
|
|
26674
|
+
const archiveDir = path43.resolve(dir, "archived");
|
|
26675
|
+
fs35.mkdirSync(archiveDir, { recursive: true });
|
|
26676
|
+
fs35.renameSync(
|
|
26677
|
+
path43.resolve(dir, entry.filename),
|
|
26678
|
+
path43.resolve(archiveDir, entry.filename)
|
|
26527
26679
|
);
|
|
26528
26680
|
}
|
|
26529
26681
|
async function deleteSavedWorkflow(entry) {
|
|
@@ -26547,7 +26699,7 @@ async function deleteSavedWorkflow(entry) {
|
|
|
26547
26699
|
if (!entry.filename) {
|
|
26548
26700
|
throw new Error("Local workflow entry is missing filename.");
|
|
26549
26701
|
}
|
|
26550
|
-
|
|
26702
|
+
fs35.rmSync(path43.resolve(resolveSavedWorkflowsDir(), entry.filename), { force: true });
|
|
26551
26703
|
markWorkflowDeletedLocally(entry.workflowId);
|
|
26552
26704
|
}
|
|
26553
26705
|
async function loadSavedWorkflowDetail(entry) {
|
|
@@ -26566,7 +26718,7 @@ async function loadSavedWorkflowDetail(entry) {
|
|
|
26566
26718
|
};
|
|
26567
26719
|
}
|
|
26568
26720
|
const dir = resolveSavedWorkflowsDir();
|
|
26569
|
-
const content =
|
|
26721
|
+
const content = fs35.readFileSync(path43.resolve(dir, entry.filename), "utf-8");
|
|
26570
26722
|
const raw = JSON.parse(content);
|
|
26571
26723
|
return {
|
|
26572
26724
|
pipeline: raw.pipeline ?? raw,
|
|
@@ -27550,36 +27702,36 @@ import pc38 from "picocolors";
|
|
|
27550
27702
|
|
|
27551
27703
|
// src/runtime/agent-harness/auth-store.ts
|
|
27552
27704
|
init_home();
|
|
27553
|
-
import
|
|
27554
|
-
import
|
|
27705
|
+
import fs36 from "node:fs";
|
|
27706
|
+
import path44 from "node:path";
|
|
27555
27707
|
function resolveHarnessAuthDir() {
|
|
27556
|
-
return
|
|
27708
|
+
return path44.resolve(resolvePaperclipHomeDir(), "harness-auth");
|
|
27557
27709
|
}
|
|
27558
27710
|
function resolveHarnessAuthFile(harnessId) {
|
|
27559
|
-
return
|
|
27711
|
+
return path44.resolve(resolveHarnessAuthDir(), `${harnessId}.json`);
|
|
27560
27712
|
}
|
|
27561
27713
|
function normalizeSecret(value) {
|
|
27562
27714
|
const trimmed = value?.trim();
|
|
27563
27715
|
return trimmed && trimmed.length > 0 ? trimmed : void 0;
|
|
27564
27716
|
}
|
|
27565
27717
|
function ensureSecureDir(dirPath) {
|
|
27566
|
-
|
|
27718
|
+
fs36.mkdirSync(dirPath, { recursive: true });
|
|
27567
27719
|
try {
|
|
27568
|
-
|
|
27720
|
+
fs36.chmodSync(dirPath, 448);
|
|
27569
27721
|
} catch {
|
|
27570
27722
|
}
|
|
27571
27723
|
}
|
|
27572
27724
|
function ensureSecureFile(filePath) {
|
|
27573
27725
|
try {
|
|
27574
|
-
|
|
27726
|
+
fs36.chmodSync(filePath, 384);
|
|
27575
27727
|
} catch {
|
|
27576
27728
|
}
|
|
27577
27729
|
}
|
|
27578
27730
|
function readHarnessCredentials(harnessId) {
|
|
27579
27731
|
const filePath = resolveHarnessAuthFile(harnessId);
|
|
27580
|
-
if (!
|
|
27732
|
+
if (!fs36.existsSync(filePath)) return {};
|
|
27581
27733
|
try {
|
|
27582
|
-
const parsed = JSON.parse(
|
|
27734
|
+
const parsed = JSON.parse(fs36.readFileSync(filePath, "utf-8"));
|
|
27583
27735
|
const creds = {};
|
|
27584
27736
|
for (const [key, value] of Object.entries(parsed)) {
|
|
27585
27737
|
if (typeof value === "string" && value.trim().length > 0) {
|
|
@@ -27606,7 +27758,7 @@ function setHarnessCredential(harnessId, key, value) {
|
|
|
27606
27758
|
const dirPath = resolveHarnessAuthDir();
|
|
27607
27759
|
ensureSecureDir(dirPath);
|
|
27608
27760
|
const filePath = resolveHarnessAuthFile(harnessId);
|
|
27609
|
-
|
|
27761
|
+
fs36.writeFileSync(filePath, `${JSON.stringify(creds, null, 2)}
|
|
27610
27762
|
`, "utf-8");
|
|
27611
27763
|
ensureSecureFile(filePath);
|
|
27612
27764
|
}
|
|
@@ -27623,7 +27775,7 @@ function setHarnessCredentials(harnessId, updates) {
|
|
|
27623
27775
|
const dirPath = resolveHarnessAuthDir();
|
|
27624
27776
|
ensureSecureDir(dirPath);
|
|
27625
27777
|
const filePath = resolveHarnessAuthFile(harnessId);
|
|
27626
|
-
|
|
27778
|
+
fs36.writeFileSync(filePath, `${JSON.stringify(creds, null, 2)}
|
|
27627
27779
|
`, "utf-8");
|
|
27628
27780
|
ensureSecureFile(filePath);
|
|
27629
27781
|
}
|
|
@@ -27635,8 +27787,8 @@ function maskSecret(value) {
|
|
|
27635
27787
|
|
|
27636
27788
|
// src/runtime/open-agents/index.ts
|
|
27637
27789
|
init_home();
|
|
27638
|
-
import
|
|
27639
|
-
import
|
|
27790
|
+
import fs37 from "node:fs";
|
|
27791
|
+
import path45 from "node:path";
|
|
27640
27792
|
|
|
27641
27793
|
// src/runtime/open-agents/contract.ts
|
|
27642
27794
|
var DEFAULT_OPEN_AGENTS_CONFIG = {
|
|
@@ -27823,18 +27975,18 @@ var OpenAgentsBackendError = class extends Error {
|
|
|
27823
27975
|
|
|
27824
27976
|
// src/runtime/open-agents/index.ts
|
|
27825
27977
|
function resolveConfigPath3() {
|
|
27826
|
-
return
|
|
27978
|
+
return path45.resolve(resolvePaperclipHomeDir(), "open-agents", "config.json");
|
|
27827
27979
|
}
|
|
27828
27980
|
function readOpenAgentsConfig() {
|
|
27829
27981
|
const configPath = resolveConfigPath3();
|
|
27830
|
-
if (!
|
|
27982
|
+
if (!fs37.existsSync(configPath)) {
|
|
27831
27983
|
return {
|
|
27832
27984
|
...DEFAULT_OPEN_AGENTS_CONFIG,
|
|
27833
27985
|
apiKey: getHarnessCredential("open-agents", "apiKey")
|
|
27834
27986
|
};
|
|
27835
27987
|
}
|
|
27836
27988
|
try {
|
|
27837
|
-
const raw = JSON.parse(
|
|
27989
|
+
const raw = JSON.parse(fs37.readFileSync(configPath, "utf-8"));
|
|
27838
27990
|
const storedApiKey = getHarnessCredential("open-agents", "apiKey");
|
|
27839
27991
|
return {
|
|
27840
27992
|
backendType: validateBackendType(raw.backendType),
|
|
@@ -27855,13 +28007,13 @@ function readOpenAgentsConfig() {
|
|
|
27855
28007
|
}
|
|
27856
28008
|
function writeOpenAgentsConfig(config) {
|
|
27857
28009
|
const configPath = resolveConfigPath3();
|
|
27858
|
-
|
|
28010
|
+
fs37.mkdirSync(path45.dirname(configPath), { recursive: true });
|
|
27859
28011
|
const persisted = {
|
|
27860
28012
|
...config,
|
|
27861
28013
|
authMode: validateAuthMode(config.authMode),
|
|
27862
28014
|
apiKey: void 0
|
|
27863
28015
|
};
|
|
27864
|
-
|
|
28016
|
+
fs37.writeFileSync(configPath, `${JSON.stringify(persisted, null, 2)}
|
|
27865
28017
|
`, "utf-8");
|
|
27866
28018
|
setHarnessCredential("open-agents", "apiKey", config.apiKey);
|
|
27867
28019
|
}
|
|
@@ -28422,8 +28574,8 @@ import pc39 from "picocolors";
|
|
|
28422
28574
|
|
|
28423
28575
|
// src/runtime/qwen-code/index.ts
|
|
28424
28576
|
init_home();
|
|
28425
|
-
import
|
|
28426
|
-
import
|
|
28577
|
+
import fs38 from "node:fs";
|
|
28578
|
+
import path46 from "node:path";
|
|
28427
28579
|
|
|
28428
28580
|
// src/runtime/qwen-code/contract.ts
|
|
28429
28581
|
var QWEN_CODE_APPROVAL_MODES = [
|
|
@@ -28641,19 +28793,19 @@ function buildSetupGuidance(env) {
|
|
|
28641
28793
|
|
|
28642
28794
|
// src/runtime/qwen-code/index.ts
|
|
28643
28795
|
function resolveConfigPath4() {
|
|
28644
|
-
return
|
|
28796
|
+
return path46.resolve(resolvePaperclipHomeDir(), "qwen-code", "config.json");
|
|
28645
28797
|
}
|
|
28646
28798
|
function readQwenCodeConfig() {
|
|
28647
28799
|
const configPath = resolveConfigPath4();
|
|
28648
28800
|
const storedCredentials = readHarnessCredentials("qwen-code");
|
|
28649
|
-
if (!
|
|
28801
|
+
if (!fs38.existsSync(configPath)) {
|
|
28650
28802
|
return {
|
|
28651
28803
|
...DEFAULT_QWEN_CODE_CONFIG,
|
|
28652
28804
|
env: mergeHarnessEnv(DEFAULT_QWEN_CODE_CONFIG.env, storedCredentials)
|
|
28653
28805
|
};
|
|
28654
28806
|
}
|
|
28655
28807
|
try {
|
|
28656
|
-
const raw = JSON.parse(
|
|
28808
|
+
const raw = JSON.parse(fs38.readFileSync(configPath, "utf-8"));
|
|
28657
28809
|
return {
|
|
28658
28810
|
binaryPath: typeof raw.binaryPath === "string" ? raw.binaryPath : DEFAULT_QWEN_CODE_CONFIG.binaryPath,
|
|
28659
28811
|
defaultModel: typeof raw.defaultModel === "string" ? raw.defaultModel : DEFAULT_QWEN_CODE_CONFIG.defaultModel,
|
|
@@ -28675,7 +28827,7 @@ function readQwenCodeConfig() {
|
|
|
28675
28827
|
}
|
|
28676
28828
|
function writeQwenCodeConfig(config) {
|
|
28677
28829
|
const configPath = resolveConfigPath4();
|
|
28678
|
-
|
|
28830
|
+
fs38.mkdirSync(path46.dirname(configPath), { recursive: true });
|
|
28679
28831
|
const rawEnv = typeof config.env === "object" && config.env !== null ? config.env : {};
|
|
28680
28832
|
const credentialUpdates = {};
|
|
28681
28833
|
const publicEnv = {};
|
|
@@ -28687,7 +28839,7 @@ function writeQwenCodeConfig(config) {
|
|
|
28687
28839
|
publicEnv[key] = value;
|
|
28688
28840
|
}
|
|
28689
28841
|
setHarnessCredentials("qwen-code", credentialUpdates);
|
|
28690
|
-
|
|
28842
|
+
fs38.writeFileSync(
|
|
28691
28843
|
configPath,
|
|
28692
28844
|
`${JSON.stringify({ ...config, env: publicEnv }, null, 2)}
|
|
28693
28845
|
`,
|
|
@@ -28937,32 +29089,32 @@ import pc41 from "picocolors";
|
|
|
28937
29089
|
|
|
28938
29090
|
// src/runtime/t3code/index.ts
|
|
28939
29091
|
init_home();
|
|
28940
|
-
import
|
|
28941
|
-
import
|
|
29092
|
+
import fs40 from "node:fs";
|
|
29093
|
+
import path48 from "node:path";
|
|
28942
29094
|
|
|
28943
29095
|
// src/runtime/agent-harness/harness-profile.ts
|
|
28944
29096
|
init_home();
|
|
28945
|
-
import
|
|
28946
|
-
import
|
|
29097
|
+
import fs39 from "node:fs";
|
|
29098
|
+
import path47 from "node:path";
|
|
28947
29099
|
import * as p26 from "@clack/prompts";
|
|
28948
29100
|
import pc40 from "picocolors";
|
|
28949
29101
|
function resolveProfileDir(harnessId) {
|
|
28950
|
-
return
|
|
29102
|
+
return path47.resolve(resolvePaperclipHomeDir(), harnessId);
|
|
28951
29103
|
}
|
|
28952
29104
|
function resolveProfilePath(harnessId) {
|
|
28953
|
-
return
|
|
29105
|
+
return path47.resolve(resolveProfileDir(harnessId), "growthub-profile.json");
|
|
28954
29106
|
}
|
|
28955
29107
|
function ensureSecureFile2(filePath) {
|
|
28956
29108
|
try {
|
|
28957
|
-
|
|
29109
|
+
fs39.chmodSync(filePath, 384);
|
|
28958
29110
|
} catch {
|
|
28959
29111
|
}
|
|
28960
29112
|
}
|
|
28961
29113
|
function readHarnessProfile(harnessId) {
|
|
28962
29114
|
const filePath = resolveProfilePath(harnessId);
|
|
28963
|
-
if (!
|
|
29115
|
+
if (!fs39.existsSync(filePath)) return null;
|
|
28964
29116
|
try {
|
|
28965
|
-
const raw = JSON.parse(
|
|
29117
|
+
const raw = JSON.parse(fs39.readFileSync(filePath, "utf-8"));
|
|
28966
29118
|
if (typeof raw.workspaceId !== "string" || typeof raw.machineLabel !== "string") return null;
|
|
28967
29119
|
return {
|
|
28968
29120
|
profileVersion: 1,
|
|
@@ -28980,14 +29132,14 @@ function readHarnessProfile(harnessId) {
|
|
|
28980
29132
|
function writeHarnessProfile(harnessId, profile) {
|
|
28981
29133
|
const dirPath = resolveProfileDir(harnessId);
|
|
28982
29134
|
const filePath = resolveProfilePath(harnessId);
|
|
28983
|
-
|
|
28984
|
-
|
|
29135
|
+
fs39.mkdirSync(dirPath, { recursive: true });
|
|
29136
|
+
fs39.writeFileSync(filePath, `${JSON.stringify(profile, null, 2)}
|
|
28985
29137
|
`, "utf-8");
|
|
28986
29138
|
ensureSecureFile2(filePath);
|
|
28987
29139
|
}
|
|
28988
29140
|
function clearHarnessProfile(harnessId) {
|
|
28989
29141
|
const filePath = resolveProfilePath(harnessId);
|
|
28990
|
-
if (
|
|
29142
|
+
if (fs39.existsSync(filePath)) fs39.rmSync(filePath);
|
|
28991
29143
|
}
|
|
28992
29144
|
function buildProfileStatusLines(harnessId, harnessLabel, profile) {
|
|
28993
29145
|
if (!profile) {
|
|
@@ -29268,19 +29420,19 @@ function writeT3GrowthubProfile(profile) {
|
|
|
29268
29420
|
writeHarnessProfile(T3_HARNESS_ID, profile);
|
|
29269
29421
|
}
|
|
29270
29422
|
function resolveConfigPath5() {
|
|
29271
|
-
return
|
|
29423
|
+
return path48.resolve(resolvePaperclipHomeDir(), "t3code", "config.json");
|
|
29272
29424
|
}
|
|
29273
29425
|
function readT3CodeConfig() {
|
|
29274
29426
|
const configPath = resolveConfigPath5();
|
|
29275
29427
|
const storedCredentials = readHarnessCredentials(T3_HARNESS_ID);
|
|
29276
|
-
if (!
|
|
29428
|
+
if (!fs40.existsSync(configPath)) {
|
|
29277
29429
|
return {
|
|
29278
29430
|
...DEFAULT_T3_CODE_CONFIG,
|
|
29279
29431
|
env: mergeHarnessEnv2(DEFAULT_T3_CODE_CONFIG.env, storedCredentials)
|
|
29280
29432
|
};
|
|
29281
29433
|
}
|
|
29282
29434
|
try {
|
|
29283
|
-
const raw = JSON.parse(
|
|
29435
|
+
const raw = JSON.parse(fs40.readFileSync(configPath, "utf-8"));
|
|
29284
29436
|
const profile = readHarnessProfile(T3_HARNESS_ID);
|
|
29285
29437
|
const resolvedBinaryPath = profile?.forkBinaryPath ?? (typeof raw.binaryPath === "string" ? raw.binaryPath : DEFAULT_T3_CODE_CONFIG.binaryPath);
|
|
29286
29438
|
return {
|
|
@@ -29303,7 +29455,7 @@ function readT3CodeConfig() {
|
|
|
29303
29455
|
}
|
|
29304
29456
|
function writeT3CodeConfig(config) {
|
|
29305
29457
|
const configPath = resolveConfigPath5();
|
|
29306
|
-
|
|
29458
|
+
fs40.mkdirSync(path48.dirname(configPath), { recursive: true });
|
|
29307
29459
|
const rawEnv = typeof config.env === "object" && config.env !== null ? config.env : {};
|
|
29308
29460
|
const credentialUpdates = {};
|
|
29309
29461
|
const publicEnv = {};
|
|
@@ -29315,7 +29467,7 @@ function writeT3CodeConfig(config) {
|
|
|
29315
29467
|
}
|
|
29316
29468
|
}
|
|
29317
29469
|
setHarnessCredentials(T3_HARNESS_ID, credentialUpdates);
|
|
29318
|
-
|
|
29470
|
+
fs40.writeFileSync(
|
|
29319
29471
|
configPath,
|
|
29320
29472
|
`${JSON.stringify({ ...config, env: publicEnv }, null, 2)}
|
|
29321
29473
|
`,
|
|
@@ -29697,8 +29849,8 @@ import pc44 from "picocolors";
|
|
|
29697
29849
|
|
|
29698
29850
|
// src/status/probes.ts
|
|
29699
29851
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
29700
|
-
import
|
|
29701
|
-
import
|
|
29852
|
+
import fs41 from "node:fs";
|
|
29853
|
+
import path49 from "node:path";
|
|
29702
29854
|
var GITHUB_API = "https://api.github.com";
|
|
29703
29855
|
var NPM_REGISTRY = "https://registry.npmjs.org";
|
|
29704
29856
|
function isoNow() {
|
|
@@ -29863,7 +30015,7 @@ async function probeKitForksIndex(_timeoutMs) {
|
|
|
29863
30015
|
try {
|
|
29864
30016
|
const { resolveKitForksIndexPath: resolveKitForksIndexPath2 } = await Promise.resolve().then(() => (init_kit_forks_home(), kit_forks_home_exports));
|
|
29865
30017
|
const p35 = resolveKitForksIndexPath2();
|
|
29866
|
-
if (!
|
|
30018
|
+
if (!fs41.existsSync(p35)) {
|
|
29867
30019
|
return {
|
|
29868
30020
|
componentId: "kit-forks-index",
|
|
29869
30021
|
level: "operational",
|
|
@@ -29871,7 +30023,7 @@ async function probeKitForksIndex(_timeoutMs) {
|
|
|
29871
30023
|
lastCheckedAt: isoNow()
|
|
29872
30024
|
};
|
|
29873
30025
|
}
|
|
29874
|
-
const parsed = JSON.parse(
|
|
30026
|
+
const parsed = JSON.parse(fs41.readFileSync(p35, "utf8"));
|
|
29875
30027
|
const count = Array.isArray(parsed.entries) ? parsed.entries.length : 0;
|
|
29876
30028
|
return {
|
|
29877
30029
|
componentId: "kit-forks-index",
|
|
@@ -29940,10 +30092,10 @@ async function probeNode(_timeoutMs) {
|
|
|
29940
30092
|
};
|
|
29941
30093
|
}
|
|
29942
30094
|
async function probeReleaseBundleArtifacts(_timeoutMs) {
|
|
29943
|
-
const distPath =
|
|
29944
|
-
const installerPath =
|
|
29945
|
-
const distOk =
|
|
29946
|
-
const installerOk =
|
|
30095
|
+
const distPath = path49.resolve(process.cwd(), "cli/dist/index.js");
|
|
30096
|
+
const installerPath = path49.resolve(process.cwd(), "packages/create-growthub-local/bin/create-growthub-local.mjs");
|
|
30097
|
+
const distOk = fs41.existsSync(distPath);
|
|
30098
|
+
const installerOk = fs41.existsSync(installerPath);
|
|
29947
30099
|
const ok = distOk && installerOk;
|
|
29948
30100
|
return {
|
|
29949
30101
|
componentId: "release-bundle",
|
|
@@ -30188,15 +30340,17 @@ function registerStatusCommands(program2) {
|
|
|
30188
30340
|
}
|
|
30189
30341
|
|
|
30190
30342
|
// src/commands/starter.ts
|
|
30191
|
-
init_init();
|
|
30192
|
-
init_table_renderer();
|
|
30193
|
-
init_source_import();
|
|
30194
30343
|
import * as p31 from "@clack/prompts";
|
|
30195
30344
|
import pc45 from "picocolors";
|
|
30196
30345
|
import { pathToFileURL as pathToFileURL3 } from "node:url";
|
|
30346
|
+
init_init();
|
|
30347
|
+
init_table_renderer();
|
|
30348
|
+
init_source_import();
|
|
30197
30349
|
async function runStarterInit(opts) {
|
|
30198
30350
|
try {
|
|
30199
30351
|
const result = await initStarterWorkspace(opts);
|
|
30352
|
+
track("workspace_starter_created", { kit_id: result.kitId });
|
|
30353
|
+
printActivationNudge("workspace_created");
|
|
30200
30354
|
if (opts.json) {
|
|
30201
30355
|
console.log(JSON.stringify({ status: "ok", ...result }, null, 2));
|
|
30202
30356
|
return;
|
|
@@ -30223,13 +30377,13 @@ Next: ${pc45.dim(`growthub kit fork status ${result.forkId}`)}`
|
|
|
30223
30377
|
process.exitCode = 1;
|
|
30224
30378
|
}
|
|
30225
30379
|
}
|
|
30226
|
-
function
|
|
30380
|
+
function terminalLink3(label, href) {
|
|
30227
30381
|
return `\x1B]8;;${href}\x07${label}\x1B]8;;\x07`;
|
|
30228
30382
|
}
|
|
30229
30383
|
function folderOpenLabel2(folderPath) {
|
|
30230
30384
|
const href = pathToFileURL3(folderPath).href;
|
|
30231
30385
|
const label = process.platform === "darwin" ? "Open in Finder" : process.platform === "win32" ? "Open in Explorer" : "Open folder";
|
|
30232
|
-
return
|
|
30386
|
+
return terminalLink3(label, href);
|
|
30233
30387
|
}
|
|
30234
30388
|
function formatSecuritySummary(result) {
|
|
30235
30389
|
const findings = result.security.findings.length;
|
|
@@ -30270,6 +30424,9 @@ async function promptConfirmations(pending, securitySummary) {
|
|
|
30270
30424
|
}
|
|
30271
30425
|
async function runSourceImportCommand(opts) {
|
|
30272
30426
|
const { input, json } = opts;
|
|
30427
|
+
track(
|
|
30428
|
+
input.source.kind === "github-repo" ? "starter_import_repo_started" : "starter_import_skill_started"
|
|
30429
|
+
);
|
|
30273
30430
|
try {
|
|
30274
30431
|
const onProgressFromInput = input.onProgress;
|
|
30275
30432
|
const onProgress = (step) => {
|
|
@@ -30281,6 +30438,7 @@ async function runSourceImportCommand(opts) {
|
|
|
30281
30438
|
onProgress
|
|
30282
30439
|
});
|
|
30283
30440
|
if (job.status === "awaiting_confirmation") {
|
|
30441
|
+
track("awaiting_confirmation_reached", { source_kind: job.sourceKind });
|
|
30284
30442
|
if (json) {
|
|
30285
30443
|
console.log(
|
|
30286
30444
|
JSON.stringify(
|
|
@@ -30317,6 +30475,7 @@ async function runSourceImportCommand(opts) {
|
|
|
30317
30475
|
return;
|
|
30318
30476
|
}
|
|
30319
30477
|
if (job.status === "failed" || !result) {
|
|
30478
|
+
track("import_failed", { source_kind: job.sourceKind });
|
|
30320
30479
|
const msg = job.error ?? "Import failed.";
|
|
30321
30480
|
if (json) {
|
|
30322
30481
|
console.log(JSON.stringify({ status: "error", error: msg, job: renderJob(job) }, null, 2));
|
|
@@ -30344,6 +30503,11 @@ async function runSourceImportCommand(opts) {
|
|
|
30344
30503
|
}
|
|
30345
30504
|
}
|
|
30346
30505
|
function finalizeSuccess(result, jobId) {
|
|
30506
|
+
track(
|
|
30507
|
+
result.sourceKind === "github-repo" ? "starter_import_repo_completed" : "starter_import_skill_completed",
|
|
30508
|
+
{ import_mode: result.importMode }
|
|
30509
|
+
);
|
|
30510
|
+
printActivationNudge("import_completed");
|
|
30347
30511
|
const sourceLine = result.source.kind === "github-repo" ? `${result.source.repo.owner}/${result.source.repo.repo}` : `${result.source.skillId}@${result.source.version}`;
|
|
30348
30512
|
p31.outro(
|
|
30349
30513
|
`Imported ${sourceLine} into ${pc45.cyan(result.forkPath)}
|
|
@@ -30490,7 +30654,7 @@ import pc46 from "picocolors";
|
|
|
30490
30654
|
|
|
30491
30655
|
// src/fleet/summary.ts
|
|
30492
30656
|
init_fork_registry();
|
|
30493
|
-
import
|
|
30657
|
+
import fs51 from "node:fs";
|
|
30494
30658
|
init_fork_policy();
|
|
30495
30659
|
init_fork_trace();
|
|
30496
30660
|
function classifyHealth(drift, pendingConfirmationJobs, lastJobStatus) {
|
|
@@ -30510,7 +30674,7 @@ var REMOTE_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
|
30510
30674
|
"conflict_encountered"
|
|
30511
30675
|
]);
|
|
30512
30676
|
function buildForkSummary(reg) {
|
|
30513
|
-
if (!
|
|
30677
|
+
if (!fs51.existsSync(reg.forkPath)) {
|
|
30514
30678
|
return {
|
|
30515
30679
|
forkId: reg.forkId,
|
|
30516
30680
|
kitId: reg.kitId,
|
|
@@ -30971,8 +31135,8 @@ async function fleetApprovals(opts) {
|
|
|
30971
31135
|
p32.log.message(
|
|
30972
31136
|
` \xB7 ${pc46.cyan(entry.jobId)} fork=${entry.forkLabel ?? entry.forkId} created=${entry.createdAt.slice(0, 19)}`
|
|
30973
31137
|
);
|
|
30974
|
-
for (const
|
|
30975
|
-
p32.log.message(` ${pc46.dim("awaits")} ${
|
|
31138
|
+
for (const path62 of entry.pendingPaths.slice(0, 6)) {
|
|
31139
|
+
p32.log.message(` ${pc46.dim("awaits")} ${path62}`);
|
|
30976
31140
|
}
|
|
30977
31141
|
if (entry.pendingPaths.length > 6) {
|
|
30978
31142
|
p32.log.message(` ${pc46.dim(`\u2026 +${entry.pendingPaths.length - 6} more`)}`);
|
|
@@ -31058,21 +31222,21 @@ var DEFAULT_MEMORY_PROVIDER_CONFIG = {
|
|
|
31058
31222
|
|
|
31059
31223
|
// src/runtime/memory/store.ts
|
|
31060
31224
|
init_home();
|
|
31061
|
-
import
|
|
31062
|
-
import
|
|
31225
|
+
import fs52 from "node:fs";
|
|
31226
|
+
import path59 from "node:path";
|
|
31063
31227
|
function toProjectSlug(project) {
|
|
31064
31228
|
return project.toLowerCase().replace(/[^a-z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "default";
|
|
31065
31229
|
}
|
|
31066
31230
|
function resolveProjectPath(project) {
|
|
31067
|
-
return
|
|
31231
|
+
return path59.resolve(resolveMemoryProjectsDir(), `${toProjectSlug(project)}.json`);
|
|
31068
31232
|
}
|
|
31069
31233
|
function loadMemoryDatabase(project) {
|
|
31070
31234
|
const filePath = resolveProjectPath(project);
|
|
31071
|
-
if (!
|
|
31235
|
+
if (!fs52.existsSync(filePath)) {
|
|
31072
31236
|
return { version: 1, project, observations: [], summaries: [], nextObservationId: 1, nextSummaryId: 1 };
|
|
31073
31237
|
}
|
|
31074
31238
|
try {
|
|
31075
|
-
const raw = JSON.parse(
|
|
31239
|
+
const raw = JSON.parse(fs52.readFileSync(filePath, "utf-8"));
|
|
31076
31240
|
return {
|
|
31077
31241
|
version: 1,
|
|
31078
31242
|
project,
|
|
@@ -31087,9 +31251,9 @@ function loadMemoryDatabase(project) {
|
|
|
31087
31251
|
}
|
|
31088
31252
|
function saveMemoryDatabase(db) {
|
|
31089
31253
|
const dir = resolveMemoryProjectsDir();
|
|
31090
|
-
|
|
31254
|
+
fs52.mkdirSync(dir, { recursive: true });
|
|
31091
31255
|
const filePath = resolveProjectPath(db.project);
|
|
31092
|
-
|
|
31256
|
+
fs52.writeFileSync(filePath, `${JSON.stringify(db, null, 2)}
|
|
31093
31257
|
`, "utf-8");
|
|
31094
31258
|
}
|
|
31095
31259
|
function addObservation(project, input) {
|
|
@@ -31183,8 +31347,8 @@ function incrementRelevanceCount(project, observationId) {
|
|
|
31183
31347
|
}
|
|
31184
31348
|
function listMemoryProjects() {
|
|
31185
31349
|
const dir = resolveMemoryProjectsDir();
|
|
31186
|
-
if (!
|
|
31187
|
-
return
|
|
31350
|
+
if (!fs52.existsSync(dir)) return [];
|
|
31351
|
+
return fs52.readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => f.replace(/\.json$/, "")).sort();
|
|
31188
31352
|
}
|
|
31189
31353
|
function getMemoryStats(project) {
|
|
31190
31354
|
const db = loadMemoryDatabase(project);
|
|
@@ -31196,15 +31360,15 @@ function getMemoryStats(project) {
|
|
|
31196
31360
|
};
|
|
31197
31361
|
}
|
|
31198
31362
|
function resolveProviderConfigPath() {
|
|
31199
|
-
return
|
|
31363
|
+
return path59.resolve(resolveMemoryDir(), "provider-config.json");
|
|
31200
31364
|
}
|
|
31201
31365
|
function readProviderConfig() {
|
|
31202
31366
|
const filePath = resolveProviderConfigPath();
|
|
31203
|
-
if (!
|
|
31367
|
+
if (!fs52.existsSync(filePath)) {
|
|
31204
31368
|
return { ...DEFAULT_MEMORY_PROVIDER_CONFIG };
|
|
31205
31369
|
}
|
|
31206
31370
|
try {
|
|
31207
|
-
const raw = JSON.parse(
|
|
31371
|
+
const raw = JSON.parse(fs52.readFileSync(filePath, "utf-8"));
|
|
31208
31372
|
return {
|
|
31209
31373
|
provider: validateProvider(raw.provider),
|
|
31210
31374
|
apiKey: typeof raw.apiKey === "string" ? raw.apiKey : void 0,
|
|
@@ -31217,9 +31381,9 @@ function readProviderConfig() {
|
|
|
31217
31381
|
}
|
|
31218
31382
|
function writeProviderConfig(config) {
|
|
31219
31383
|
const dir = resolveMemoryDir();
|
|
31220
|
-
|
|
31384
|
+
fs52.mkdirSync(dir, { recursive: true });
|
|
31221
31385
|
const filePath = resolveProviderConfigPath();
|
|
31222
|
-
|
|
31386
|
+
fs52.writeFileSync(filePath, `${JSON.stringify(config, null, 2)}
|
|
31223
31387
|
`, { mode: 384 });
|
|
31224
31388
|
}
|
|
31225
31389
|
function validateProvider(value) {
|
|
@@ -31598,14 +31762,14 @@ async function syncMemoriesToHosted(project, options) {
|
|
|
31598
31762
|
init_llm();
|
|
31599
31763
|
function resolveCliVersion() {
|
|
31600
31764
|
try {
|
|
31601
|
-
const moduleDir =
|
|
31765
|
+
const moduleDir = path61.dirname(fileURLToPath7(import.meta.url));
|
|
31602
31766
|
const candidates = [
|
|
31603
|
-
|
|
31604
|
-
|
|
31767
|
+
path61.resolve(moduleDir, "../package.json"),
|
|
31768
|
+
path61.resolve(moduleDir, "../../package.json")
|
|
31605
31769
|
];
|
|
31606
31770
|
for (const candidate of candidates) {
|
|
31607
|
-
if (!
|
|
31608
|
-
const parsed = JSON.parse(
|
|
31771
|
+
if (!fs54.existsSync(candidate)) continue;
|
|
31772
|
+
const parsed = JSON.parse(fs54.readFileSync(candidate, "utf8"));
|
|
31609
31773
|
if (parsed?.name === "@growthub/cli" && typeof parsed.version === "string") return parsed.version;
|
|
31610
31774
|
}
|
|
31611
31775
|
} catch {
|
|
@@ -31832,7 +31996,7 @@ async function runMarketingContextBuilder(baseUrl, model) {
|
|
|
31832
31996
|
});
|
|
31833
31997
|
if (p34.isCancel(projectDir)) return;
|
|
31834
31998
|
const dir = String(projectDir).trim() || process.cwd();
|
|
31835
|
-
if (!
|
|
31999
|
+
if (!fs54.existsSync(dir)) {
|
|
31836
32000
|
p34.note(`Directory not found: ${dir}`, "Marketing Context Builder");
|
|
31837
32001
|
return;
|
|
31838
32002
|
}
|
|
@@ -31868,10 +32032,10 @@ async function runMarketingContextBuilder(baseUrl, model) {
|
|
|
31868
32032
|
p34.note("Draft was not saved. You can copy it from the output above.", "Marketing Context Builder");
|
|
31869
32033
|
return;
|
|
31870
32034
|
}
|
|
31871
|
-
const outDir =
|
|
31872
|
-
|
|
31873
|
-
const outPath =
|
|
31874
|
-
|
|
32035
|
+
const outDir = path61.resolve(dir, ".agents");
|
|
32036
|
+
fs54.mkdirSync(outDir, { recursive: true });
|
|
32037
|
+
const outPath = path61.resolve(outDir, "product-marketing-context.md");
|
|
32038
|
+
fs54.writeFileSync(outPath, result.contextMarkdown, "utf-8");
|
|
31875
32039
|
p34.note(`Saved to: ${outPath}
|
|
31876
32040
|
|
|
31877
32041
|
Review the file and replace [NEEDS INPUT] placeholders with real data.`, "Marketing Context Builder");
|
|
@@ -32080,39 +32244,39 @@ function captureSessionSummary(project, sessionId, messages) {
|
|
|
32080
32244
|
}
|
|
32081
32245
|
}
|
|
32082
32246
|
function resolveLocalThreadsDir() {
|
|
32083
|
-
return
|
|
32247
|
+
return path61.resolve(resolvePaperclipHomeDir(), "native-intelligence", "threads");
|
|
32084
32248
|
}
|
|
32085
32249
|
function loadOrCreateLocalThread() {
|
|
32086
32250
|
const dir = resolveLocalThreadsDir();
|
|
32087
|
-
|
|
32088
|
-
const activePath =
|
|
32089
|
-
if (
|
|
32251
|
+
fs54.mkdirSync(dir, { recursive: true });
|
|
32252
|
+
const activePath = path61.resolve(dir, "active-thread.json");
|
|
32253
|
+
if (fs54.existsSync(activePath)) {
|
|
32090
32254
|
try {
|
|
32091
|
-
const parsed = JSON.parse(
|
|
32255
|
+
const parsed = JSON.parse(fs54.readFileSync(activePath, "utf-8"));
|
|
32092
32256
|
const id2 = typeof parsed.id === "string" && parsed.id.length > 0 ? parsed.id : `thread-${Date.now()}`;
|
|
32093
|
-
const threadFile =
|
|
32257
|
+
const threadFile = path61.resolve(dir, `${id2}.json`);
|
|
32094
32258
|
const messages = Array.isArray(parsed.messages) ? parsed.messages : [];
|
|
32095
32259
|
return { id: id2, filePath: threadFile, messages };
|
|
32096
32260
|
} catch {
|
|
32097
32261
|
}
|
|
32098
32262
|
}
|
|
32099
32263
|
const id = `thread-${Date.now()}`;
|
|
32100
|
-
const filePath =
|
|
32264
|
+
const filePath = path61.resolve(dir, `${id}.json`);
|
|
32101
32265
|
const thread = { id, filePath, messages: [] };
|
|
32102
32266
|
saveLocalThread(thread);
|
|
32103
32267
|
return thread;
|
|
32104
32268
|
}
|
|
32105
32269
|
function saveLocalThread(thread) {
|
|
32106
32270
|
const dir = resolveLocalThreadsDir();
|
|
32107
|
-
|
|
32108
|
-
|
|
32271
|
+
fs54.mkdirSync(dir, { recursive: true });
|
|
32272
|
+
fs54.writeFileSync(
|
|
32109
32273
|
thread.filePath,
|
|
32110
32274
|
`${JSON.stringify({ id: thread.id, messages: thread.messages }, null, 2)}
|
|
32111
32275
|
`,
|
|
32112
32276
|
"utf-8"
|
|
32113
32277
|
);
|
|
32114
|
-
const activePath =
|
|
32115
|
-
|
|
32278
|
+
const activePath = path61.resolve(dir, "active-thread.json");
|
|
32279
|
+
fs54.writeFileSync(
|
|
32116
32280
|
activePath,
|
|
32117
32281
|
`${JSON.stringify({ id: thread.id, messages: thread.messages }, null, 2)}
|
|
32118
32282
|
`,
|
|
@@ -32258,7 +32422,7 @@ async function collectBindingsFromContract(contract, promptSeed) {
|
|
|
32258
32422
|
return bindings;
|
|
32259
32423
|
}
|
|
32260
32424
|
function resolveCurrentProject() {
|
|
32261
|
-
return
|
|
32425
|
+
return path61.basename(process.cwd());
|
|
32262
32426
|
}
|
|
32263
32427
|
async function runMemoryKnowledgeHub() {
|
|
32264
32428
|
const project = resolveCurrentProject();
|
|
@@ -32410,6 +32574,7 @@ API key: ${result.apiKey ? "configured" : "not needed"}`,
|
|
|
32410
32574
|
}
|
|
32411
32575
|
}
|
|
32412
32576
|
async function runDiscoveryHub(opts) {
|
|
32577
|
+
track("discover_opened");
|
|
32413
32578
|
printPaperclipCliBanner();
|
|
32414
32579
|
p34.intro("Growthub Local");
|
|
32415
32580
|
while (true) {
|
|
@@ -32837,12 +33002,12 @@ function isInstallerMode() {
|
|
|
32837
33002
|
}
|
|
32838
33003
|
function listLocalSurfaces() {
|
|
32839
33004
|
const homeDir = resolvePaperclipHomeDir();
|
|
32840
|
-
const instancesDir =
|
|
32841
|
-
if (!
|
|
32842
|
-
return
|
|
33005
|
+
const instancesDir = path61.resolve(homeDir, "instances");
|
|
33006
|
+
if (!fs54.existsSync(instancesDir)) return [];
|
|
33007
|
+
return fs54.readdirSync(instancesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => {
|
|
32843
33008
|
const instanceId = entry.name;
|
|
32844
|
-
const configPath =
|
|
32845
|
-
if (!
|
|
33009
|
+
const configPath = path61.resolve(instancesDir, instanceId, "config.json");
|
|
33010
|
+
if (!fs54.existsSync(configPath)) return null;
|
|
32846
33011
|
try {
|
|
32847
33012
|
const config = readConfig(configPath);
|
|
32848
33013
|
if (!config) return null;
|
|
@@ -33010,6 +33175,7 @@ if (surfaceRuntime.capabilities.dxEnabled) {
|
|
|
33010
33175
|
} else {
|
|
33011
33176
|
registerGtmCommands(program);
|
|
33012
33177
|
}
|
|
33178
|
+
trackCliStart();
|
|
33013
33179
|
program.parseAsync().catch((err) => {
|
|
33014
33180
|
console.error(err instanceof Error ? err.message : String(err));
|
|
33015
33181
|
process.exit(1);
|