@happyvertical/smrt-workbench 0.40.66
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/AGENTS.md +28 -0
- package/CLAUDE.md +1 -0
- package/LICENSE +7 -0
- package/README.md +31 -0
- package/dist/chunks/discovery-G9-foyW8.js +979 -0
- package/dist/chunks/discovery-G9-foyW8.js.map +1 -0
- package/dist/chunks/runtime-BTiu5fuP.js +93 -0
- package/dist/chunks/runtime-BTiu5fuP.js.map +1 -0
- package/dist/discovery.d.ts +14 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/runtime.d.ts +10 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +2 -0
- package/dist/svelte/MarkdownDocument.svelte +200 -0
- package/dist/svelte/MarkdownDocument.svelte.d.ts +7 -0
- package/dist/svelte/MarkdownDocument.svelte.d.ts.map +1 -0
- package/dist/svelte/WorkbenchHost.svelte +2058 -0
- package/dist/svelte/WorkbenchHost.svelte.d.ts +13 -0
- package/dist/svelte/WorkbenchHost.svelte.d.ts.map +1 -0
- package/dist/svelte/command.d.ts +6 -0
- package/dist/svelte/command.d.ts.map +1 -0
- package/dist/svelte/command.js +18 -0
- package/dist/svelte/index.d.ts +2 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +1 -0
- package/dist/types.d.ts +202 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +0 -0
- package/dist/utils.d.ts +3 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/vite.d.ts +6 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +124 -0
- package/dist/vite.js.map +1 -0
- package/host/README.md +12 -0
- package/host/package.json +21 -0
- package/host/src/app.html +11 -0
- package/host/src/hooks.client.ts +5 -0
- package/host/src/routes/+error.svelte +44 -0
- package/host/src/routes/+page.svelte +15 -0
- package/host/svelte.config.js +12 -0
- package/host/tsconfig.json +10 -0
- package/host/vite.config.ts +202 -0
- package/package.json +96 -0
- package/src/discovery.ts +1748 -0
- package/src/runtime.ts +165 -0
- package/src/types.ts +243 -0
- package/src/utils.ts +16 -0
- package/src/vite.ts +303 -0
|
@@ -0,0 +1,979 @@
|
|
|
1
|
+
import { c as commandIdForScript, t as coerceWorkbenchModules } from "./runtime-BTiu5fuP.js";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
import fg from "fast-glob";
|
|
7
|
+
//#region src/discovery.ts
|
|
8
|
+
var require = createRequire(import.meta.url);
|
|
9
|
+
var TS_SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
10
|
+
".ts",
|
|
11
|
+
".tsx",
|
|
12
|
+
".mts",
|
|
13
|
+
".cts"
|
|
14
|
+
]);
|
|
15
|
+
var DOCUMENT_LIMIT = 2e4;
|
|
16
|
+
var EXAMPLE_LIMIT = 8e3;
|
|
17
|
+
function findWorkspaceRoot(startDir = process.cwd()) {
|
|
18
|
+
let current = resolve(startDir);
|
|
19
|
+
while (true) {
|
|
20
|
+
if (existsSync(join(current, "pnpm-workspace.yaml"))) return current;
|
|
21
|
+
const parent = dirname(current);
|
|
22
|
+
if (parent === current) return null;
|
|
23
|
+
current = parent;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function findSmrtWorkbenchWorkspaceRoot(startDir = process.cwd()) {
|
|
27
|
+
const workspaceRoot = findWorkspaceRoot(startDir);
|
|
28
|
+
if (!workspaceRoot) return null;
|
|
29
|
+
return existsSync(join(workspaceRoot, "packages", "smrt-workbench", "host", "package.json")) ? workspaceRoot : null;
|
|
30
|
+
}
|
|
31
|
+
function findProjectRoot(startDir = process.cwd()) {
|
|
32
|
+
let current = resolve(startDir);
|
|
33
|
+
while (true) {
|
|
34
|
+
if (existsSync(join(current, "package.json"))) return current;
|
|
35
|
+
const parent = dirname(current);
|
|
36
|
+
if (parent === current) return resolve(startDir);
|
|
37
|
+
current = parent;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function findPackageDir(startDir = process.cwd(), workspaceRoot) {
|
|
41
|
+
let current = resolve(startDir);
|
|
42
|
+
const boundary = workspaceRoot ? resolve(workspaceRoot) : null;
|
|
43
|
+
while (true) {
|
|
44
|
+
if (boundary && current === boundary) return null;
|
|
45
|
+
if (existsSync(join(current, "package.json"))) return current;
|
|
46
|
+
const parent = dirname(current);
|
|
47
|
+
if (parent === current) return null;
|
|
48
|
+
current = parent;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function detectWorkbenchMode(projectRoot = process.cwd()) {
|
|
52
|
+
return findSmrtWorkbenchWorkspaceRoot(projectRoot) ? "workspace" : "consumer";
|
|
53
|
+
}
|
|
54
|
+
function readJson(path) {
|
|
55
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
56
|
+
}
|
|
57
|
+
function readJsonIfExists(path) {
|
|
58
|
+
return existsSync(path) ? readJson(path) : null;
|
|
59
|
+
}
|
|
60
|
+
function detectPackageManager(projectRoot) {
|
|
61
|
+
let current = resolve(projectRoot);
|
|
62
|
+
while (true) {
|
|
63
|
+
if (existsSync(join(current, "pnpm-lock.yaml"))) return "pnpm";
|
|
64
|
+
if (existsSync(join(current, "yarn.lock"))) return "yarn";
|
|
65
|
+
const packageManager = readJsonIfExists(join(current, "package.json"))?.packageManager;
|
|
66
|
+
if (packageManager?.startsWith("pnpm@")) return "pnpm";
|
|
67
|
+
if (packageManager?.startsWith("yarn@")) return "yarn";
|
|
68
|
+
if (packageManager?.startsWith("npm@")) return "npm";
|
|
69
|
+
const parent = dirname(current);
|
|
70
|
+
if (parent === current) return "npm";
|
|
71
|
+
current = parent;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function isRecord(value) {
|
|
75
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
76
|
+
}
|
|
77
|
+
function stringRecord(value) {
|
|
78
|
+
if (!isRecord(value)) return {};
|
|
79
|
+
return Object.fromEntries(Object.entries(value).filter((entry) => typeof entry[1] === "string"));
|
|
80
|
+
}
|
|
81
|
+
function countItems(value) {
|
|
82
|
+
if (Array.isArray(value)) return value.length;
|
|
83
|
+
if (isRecord(value)) return Object.keys(value).length;
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
|
86
|
+
function truncate(value, limit) {
|
|
87
|
+
if (value.length <= limit) return {
|
|
88
|
+
content: value,
|
|
89
|
+
truncated: false
|
|
90
|
+
};
|
|
91
|
+
return {
|
|
92
|
+
content: value.slice(0, limit),
|
|
93
|
+
truncated: true
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function readDocument(packageDir, fileName, kind) {
|
|
97
|
+
const path = join(packageDir, fileName);
|
|
98
|
+
if (!existsSync(path)) return null;
|
|
99
|
+
const { content, truncated } = truncate(readFileSync(path, "utf-8"), DOCUMENT_LIMIT);
|
|
100
|
+
return {
|
|
101
|
+
kind,
|
|
102
|
+
title: fileName,
|
|
103
|
+
path,
|
|
104
|
+
content,
|
|
105
|
+
truncated
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function exportKeys(exportsField) {
|
|
109
|
+
if (!exportsField) return [];
|
|
110
|
+
if (typeof exportsField === "string") return ["."];
|
|
111
|
+
if (isRecord(exportsField)) return Object.keys(exportsField).sort();
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
114
|
+
function dependencyNames(packageJson) {
|
|
115
|
+
return Object.keys({
|
|
116
|
+
...packageJson.dependencies,
|
|
117
|
+
...packageJson.devDependencies,
|
|
118
|
+
...packageJson.peerDependencies
|
|
119
|
+
}).sort();
|
|
120
|
+
}
|
|
121
|
+
function smrtDependencyNames(packageJson) {
|
|
122
|
+
return dependencyNames(packageJson).filter((name) => name.startsWith("@happyvertical/smrt-"));
|
|
123
|
+
}
|
|
124
|
+
function sdkDependencyNames(packageJson) {
|
|
125
|
+
return dependencyNames(packageJson).filter((name) => name.startsWith("@happyvertical/") && !name.startsWith("@happyvertical/smrt-"));
|
|
126
|
+
}
|
|
127
|
+
function readKnowledgeSummary(packageDir) {
|
|
128
|
+
const knowledgeCandidates = [join(packageDir, ".smrt", "smrt-knowledge.json"), join(packageDir, "dist", "smrt-knowledge.json")];
|
|
129
|
+
const manifestCandidates = [
|
|
130
|
+
join(packageDir, ".smrt", "manifest.json"),
|
|
131
|
+
join(packageDir, "dist", "manifest.json"),
|
|
132
|
+
join(packageDir, "src", "manifest", "manifest.json")
|
|
133
|
+
];
|
|
134
|
+
const knowledgePath = knowledgeCandidates.find((path) => existsSync(path));
|
|
135
|
+
const manifestPath = manifestCandidates.find((path) => existsSync(path));
|
|
136
|
+
const knowledge = knowledgePath ? readJsonIfExists(knowledgePath) : null;
|
|
137
|
+
const manifest = manifestPath ? readJsonIfExists(manifestPath) : null;
|
|
138
|
+
const knowledgeObjects = knowledge?.objects;
|
|
139
|
+
const manifestObjects = manifest?.objects;
|
|
140
|
+
const objects = countItems(knowledgeObjects) > 0 ? knowledgeObjects : manifestObjects;
|
|
141
|
+
const objectNames = objectNamesFrom(objects);
|
|
142
|
+
return {
|
|
143
|
+
manifestPath,
|
|
144
|
+
knowledgePath,
|
|
145
|
+
objectCount: countItems(objects),
|
|
146
|
+
relationshipCount: countItems(knowledge?.relationshipsV2) || countItems(knowledge?.relationships),
|
|
147
|
+
promptCount: countItems(knowledge?.prompts),
|
|
148
|
+
mcpToolCount: countItems(knowledge?.mcpTools),
|
|
149
|
+
surfaceCount: countItems(knowledge?.surfaces),
|
|
150
|
+
tags: Array.isArray(knowledge?.tags) ? knowledge.tags.filter((tag) => typeof tag === "string") : [],
|
|
151
|
+
risks: Array.isArray(knowledge?.risks) ? knowledge.risks.filter((risk) => typeof risk === "string") : [],
|
|
152
|
+
objectNames
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function objectNamesFrom(value) {
|
|
156
|
+
if (Array.isArray(value)) return value.map((item) => {
|
|
157
|
+
if (!isRecord(item)) return null;
|
|
158
|
+
return stringValue(item.qualifiedName) || stringValue(item.name);
|
|
159
|
+
}).filter((name) => Boolean(name)).sort();
|
|
160
|
+
if (isRecord(value)) return Object.keys(value).sort();
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
function stringValue(value) {
|
|
164
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
165
|
+
}
|
|
166
|
+
function booleanValue(value) {
|
|
167
|
+
return typeof value === "boolean" ? value : void 0;
|
|
168
|
+
}
|
|
169
|
+
function objectRecordsFrom(value) {
|
|
170
|
+
if (Array.isArray(value)) return value.filter(isRecord);
|
|
171
|
+
if (!isRecord(value)) return [];
|
|
172
|
+
return Object.entries(value).map(([key, item]) => {
|
|
173
|
+
if (isRecord(item)) return {
|
|
174
|
+
key,
|
|
175
|
+
...item
|
|
176
|
+
};
|
|
177
|
+
return {
|
|
178
|
+
key,
|
|
179
|
+
name: key
|
|
180
|
+
};
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
function fieldSummariesFrom(value) {
|
|
184
|
+
if (Array.isArray(value)) return value.filter(isRecord).map((field) => ({
|
|
185
|
+
name: stringValue(field.name) || "unknown",
|
|
186
|
+
type: stringValue(field.type) || void 0,
|
|
187
|
+
required: booleanValue(field.required),
|
|
188
|
+
related: stringValue(field.related) || void 0
|
|
189
|
+
})).filter((field) => field.name !== "unknown");
|
|
190
|
+
if (!isRecord(value)) return [];
|
|
191
|
+
return Object.entries(value).map(([name, field]) => {
|
|
192
|
+
const fieldRecord = isRecord(field) ? field : {};
|
|
193
|
+
return {
|
|
194
|
+
name,
|
|
195
|
+
type: stringValue(fieldRecord.type) || void 0,
|
|
196
|
+
required: booleanValue(fieldRecord.required),
|
|
197
|
+
related: stringValue(fieldRecord.related) || void 0
|
|
198
|
+
};
|
|
199
|
+
}).sort((left, right) => left.name.localeCompare(right.name));
|
|
200
|
+
}
|
|
201
|
+
function resolveManifestSourcePath(rootDir, packageDir, sourcePath) {
|
|
202
|
+
if (!sourcePath) return;
|
|
203
|
+
if (isAbsolute(sourcePath)) return sourcePath;
|
|
204
|
+
const rootRelativePath = resolve(rootDir, sourcePath);
|
|
205
|
+
if (existsSync(rootRelativePath)) return rootRelativePath;
|
|
206
|
+
return resolve(packageDir, sourcePath);
|
|
207
|
+
}
|
|
208
|
+
function typedocPackageSlug(packageName) {
|
|
209
|
+
if (!packageName?.startsWith("@happyvertical/smrt-")) return null;
|
|
210
|
+
return packageName.replace("@happyvertical/smrt-", "");
|
|
211
|
+
}
|
|
212
|
+
function findTypedocClassPath(rootDir, packageDir, packageName, className) {
|
|
213
|
+
const slug = typedocPackageSlug(packageName);
|
|
214
|
+
return [
|
|
215
|
+
slug ? join(rootDir, "docs", "content", "api", slug, "classes", `${className}.md`) : null,
|
|
216
|
+
join(packageDir, "docs", "classes", `${className}.md`),
|
|
217
|
+
join(packageDir, "docs", `${className}.md`)
|
|
218
|
+
].filter((path) => Boolean(path)).find((path) => existsSync(path));
|
|
219
|
+
}
|
|
220
|
+
function extractTypedocSummary(content) {
|
|
221
|
+
const lines = content.split(/\r?\n/);
|
|
222
|
+
const summary = [];
|
|
223
|
+
let hasSeenTitle = false;
|
|
224
|
+
for (const line of lines) {
|
|
225
|
+
const trimmed = line.trim();
|
|
226
|
+
if (!hasSeenTitle) {
|
|
227
|
+
if (trimmed.startsWith("# ")) hasSeenTitle = true;
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (!trimmed || trimmed.startsWith("Defined in:")) continue;
|
|
231
|
+
if (trimmed.startsWith("## ")) break;
|
|
232
|
+
if (trimmed.startsWith("> ")) continue;
|
|
233
|
+
summary.push(trimmed);
|
|
234
|
+
}
|
|
235
|
+
const value = summary.join("\n").trim();
|
|
236
|
+
return value.length > 0 ? value : void 0;
|
|
237
|
+
}
|
|
238
|
+
function readTypedocSummary(path) {
|
|
239
|
+
if (!path) return;
|
|
240
|
+
return extractTypedocSummary(readFileSync(path, "utf-8"));
|
|
241
|
+
}
|
|
242
|
+
var CRUD_ACTIONS = [
|
|
243
|
+
"list",
|
|
244
|
+
"get",
|
|
245
|
+
"create",
|
|
246
|
+
"update",
|
|
247
|
+
"delete"
|
|
248
|
+
];
|
|
249
|
+
var SERVER_MANAGED_FIELDS = /* @__PURE__ */ new Set([
|
|
250
|
+
"id",
|
|
251
|
+
"tenantId",
|
|
252
|
+
"tenant_id",
|
|
253
|
+
"createdAt",
|
|
254
|
+
"created_at",
|
|
255
|
+
"updatedAt",
|
|
256
|
+
"updated_at"
|
|
257
|
+
]);
|
|
258
|
+
function arrayOfStrings(value) {
|
|
259
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
260
|
+
}
|
|
261
|
+
function configObject(value) {
|
|
262
|
+
return isRecord(value) ? value : {};
|
|
263
|
+
}
|
|
264
|
+
function parameterRequired(value) {
|
|
265
|
+
if (typeof value.required === "boolean") return value.required;
|
|
266
|
+
const meta = configObject(value._meta);
|
|
267
|
+
return typeof meta.required === "boolean" ? meta.required : void 0;
|
|
268
|
+
}
|
|
269
|
+
function parameterDescription(value) {
|
|
270
|
+
return stringValue(value.description) || stringValue(configObject(value._meta).description) || void 0;
|
|
271
|
+
}
|
|
272
|
+
function parameterDefaultValue(value) {
|
|
273
|
+
if (!Object.hasOwn(value, "default")) return;
|
|
274
|
+
const defaultValue = value.default;
|
|
275
|
+
if (typeof defaultValue === "string" || typeof defaultValue === "number" || typeof defaultValue === "boolean") return String(defaultValue);
|
|
276
|
+
if (defaultValue === null) return "null";
|
|
277
|
+
try {
|
|
278
|
+
return JSON.stringify(defaultValue);
|
|
279
|
+
} catch {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
function fieldParameter(name, field, location, requiredOverride) {
|
|
284
|
+
return {
|
|
285
|
+
name,
|
|
286
|
+
type: stringValue(field.type) || void 0,
|
|
287
|
+
required: requiredOverride ?? parameterRequired(field),
|
|
288
|
+
location,
|
|
289
|
+
description: parameterDescription(field),
|
|
290
|
+
defaultValue: parameterDefaultValue(field)
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
function writableFieldEntries(object) {
|
|
294
|
+
const fields = configObject(object.fields);
|
|
295
|
+
const apiConfig = configObject(configObject(object.decoratorConfig).api);
|
|
296
|
+
const writableAllowlist = new Set(arrayOfStrings(apiConfig.writable));
|
|
297
|
+
const hasWritableAllowlist = writableAllowlist.size > 0;
|
|
298
|
+
return Object.entries(fields).filter((entry) => isRecord(entry[1])).filter(([name, field]) => {
|
|
299
|
+
if (name.startsWith("_")) return false;
|
|
300
|
+
if (SERVER_MANAGED_FIELDS.has(name)) return false;
|
|
301
|
+
if (field.readonly === true || configObject(field._meta).readonly === true) return false;
|
|
302
|
+
return !hasWritableAllowlist || writableAllowlist.has(name);
|
|
303
|
+
}).sort(([left], [right]) => left.localeCompare(right));
|
|
304
|
+
}
|
|
305
|
+
function writableFieldParameters(object, location, requiredMode) {
|
|
306
|
+
return writableFieldEntries(object).map(([name, field]) => fieldParameter(name, field, location, requiredMode === "field" ? parameterRequired(field) : false));
|
|
307
|
+
}
|
|
308
|
+
function methodParameterSummaries(object, action, location) {
|
|
309
|
+
const parameters = methodDefinition(object, action).parameters;
|
|
310
|
+
if (!Array.isArray(parameters)) return [];
|
|
311
|
+
return parameters.filter(isRecord).map((parameter) => ({
|
|
312
|
+
name: stringValue(parameter.name) || "parameter",
|
|
313
|
+
type: stringValue(parameter.type) || void 0,
|
|
314
|
+
required: parameter.optional !== true,
|
|
315
|
+
location,
|
|
316
|
+
description: parameterDescription(parameter),
|
|
317
|
+
defaultValue: parameterDefaultValue(parameter)
|
|
318
|
+
}));
|
|
319
|
+
}
|
|
320
|
+
function pathParameterNames(path) {
|
|
321
|
+
return Array.from(path.matchAll(/\{([^}]+)\}/g)).map((match) => match[1]?.trim()).filter((name) => Boolean(name));
|
|
322
|
+
}
|
|
323
|
+
function pathParameters(path) {
|
|
324
|
+
return pathParameterNames(path).map((name) => ({
|
|
325
|
+
name,
|
|
326
|
+
type: "string",
|
|
327
|
+
required: true,
|
|
328
|
+
location: "path"
|
|
329
|
+
}));
|
|
330
|
+
}
|
|
331
|
+
function restCrudParameters(object, action, path) {
|
|
332
|
+
if (action === "list") return [{
|
|
333
|
+
name: "limit",
|
|
334
|
+
type: "integer",
|
|
335
|
+
required: false,
|
|
336
|
+
location: "query",
|
|
337
|
+
description: "Maximum number of items to return.",
|
|
338
|
+
defaultValue: "50"
|
|
339
|
+
}, {
|
|
340
|
+
name: "offset",
|
|
341
|
+
type: "integer",
|
|
342
|
+
required: false,
|
|
343
|
+
location: "query",
|
|
344
|
+
description: "Number of items to skip.",
|
|
345
|
+
defaultValue: "0"
|
|
346
|
+
}];
|
|
347
|
+
if (action === "create") return writableFieldParameters(object, "body", "field");
|
|
348
|
+
if (action === "update") return [...pathParameters(path), ...writableFieldParameters(object, "body", "optional")];
|
|
349
|
+
return pathParameters(path);
|
|
350
|
+
}
|
|
351
|
+
function restCustomParameters(object, action, method, path) {
|
|
352
|
+
const pathParams = pathParameters(path);
|
|
353
|
+
const pathParamNameSet = new Set(pathParams.map((param) => param.name));
|
|
354
|
+
const bodyOrQueryParams = methodParameterSummaries(object, action, method === "GET" ? "query" : "body").filter((param) => !pathParamNameSet.has(param.name));
|
|
355
|
+
return [...pathParams, ...bodyOrQueryParams];
|
|
356
|
+
}
|
|
357
|
+
function cliCrudParameters(object, action) {
|
|
358
|
+
if (action === "list") return [
|
|
359
|
+
{
|
|
360
|
+
name: "--limit",
|
|
361
|
+
type: "integer",
|
|
362
|
+
required: false,
|
|
363
|
+
location: "option",
|
|
364
|
+
description: "Maximum number of items to return.",
|
|
365
|
+
defaultValue: "50"
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
name: "--offset",
|
|
369
|
+
type: "integer",
|
|
370
|
+
required: false,
|
|
371
|
+
location: "option",
|
|
372
|
+
description: "Number of items to skip.",
|
|
373
|
+
defaultValue: "0"
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
name: "--order-by",
|
|
377
|
+
type: "string",
|
|
378
|
+
required: false,
|
|
379
|
+
location: "option",
|
|
380
|
+
description: "Ordering expression, for example \"created_at DESC\"."
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
name: "--where",
|
|
384
|
+
type: "object",
|
|
385
|
+
required: false,
|
|
386
|
+
location: "option",
|
|
387
|
+
description: "JSON filter conditions."
|
|
388
|
+
}
|
|
389
|
+
];
|
|
390
|
+
if (action === "get" || action === "delete") return [{
|
|
391
|
+
name: "id",
|
|
392
|
+
type: "string",
|
|
393
|
+
required: true,
|
|
394
|
+
location: "argument",
|
|
395
|
+
description: "Object ID. May also be passed as --id."
|
|
396
|
+
}];
|
|
397
|
+
const bodyParameters = writableFieldParameters(object, "option", action === "create" ? "field" : "optional").map((param) => ({
|
|
398
|
+
...param,
|
|
399
|
+
name: `--${param.name}`
|
|
400
|
+
}));
|
|
401
|
+
if (action === "create") return [{
|
|
402
|
+
name: "--from-file",
|
|
403
|
+
type: "path",
|
|
404
|
+
required: false,
|
|
405
|
+
location: "option",
|
|
406
|
+
description: "Path to a JSON object payload."
|
|
407
|
+
}, ...bodyParameters];
|
|
408
|
+
return [
|
|
409
|
+
{
|
|
410
|
+
name: "id",
|
|
411
|
+
type: "string",
|
|
412
|
+
required: true,
|
|
413
|
+
location: "argument",
|
|
414
|
+
description: "Object ID. May also be passed as --id."
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
name: "--from-file",
|
|
418
|
+
type: "path",
|
|
419
|
+
required: false,
|
|
420
|
+
location: "option",
|
|
421
|
+
description: "Path to a JSON object payload."
|
|
422
|
+
},
|
|
423
|
+
...bodyParameters
|
|
424
|
+
];
|
|
425
|
+
}
|
|
426
|
+
function cliCustomParameters(object, action) {
|
|
427
|
+
return [{
|
|
428
|
+
name: "id",
|
|
429
|
+
type: "string",
|
|
430
|
+
required: false,
|
|
431
|
+
location: "argument",
|
|
432
|
+
description: "Optional object ID for instance actions. May also be passed as --id."
|
|
433
|
+
}, ...methodParameterSummaries(object, action, "option").map((param) => ({
|
|
434
|
+
...param,
|
|
435
|
+
name: `--${param.name}`,
|
|
436
|
+
required: false
|
|
437
|
+
}))];
|
|
438
|
+
}
|
|
439
|
+
function mcpCrudParameters(object, action) {
|
|
440
|
+
if (action === "list") return [
|
|
441
|
+
{
|
|
442
|
+
name: "limit",
|
|
443
|
+
type: "integer",
|
|
444
|
+
required: false,
|
|
445
|
+
location: "input",
|
|
446
|
+
description: "Maximum number of items to return.",
|
|
447
|
+
defaultValue: "50"
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
name: "offset",
|
|
451
|
+
type: "integer",
|
|
452
|
+
required: false,
|
|
453
|
+
location: "input",
|
|
454
|
+
description: "Number of items to skip.",
|
|
455
|
+
defaultValue: "0"
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
name: "orderBy",
|
|
459
|
+
type: "string",
|
|
460
|
+
required: false,
|
|
461
|
+
location: "input",
|
|
462
|
+
description: "Ordering expression, for example \"created_at DESC\"."
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
name: "where",
|
|
466
|
+
type: "object",
|
|
467
|
+
required: false,
|
|
468
|
+
location: "input",
|
|
469
|
+
description: "Filter conditions as key-value pairs."
|
|
470
|
+
}
|
|
471
|
+
];
|
|
472
|
+
if (action === "get") return [{
|
|
473
|
+
name: "id",
|
|
474
|
+
type: "string",
|
|
475
|
+
required: true,
|
|
476
|
+
location: "input",
|
|
477
|
+
description: "Unique identifier of the object."
|
|
478
|
+
}, {
|
|
479
|
+
name: "slug",
|
|
480
|
+
type: "string",
|
|
481
|
+
required: false,
|
|
482
|
+
location: "input",
|
|
483
|
+
description: "URL-friendly identifier of the object."
|
|
484
|
+
}];
|
|
485
|
+
if (action === "delete") return [{
|
|
486
|
+
name: "id",
|
|
487
|
+
type: "string",
|
|
488
|
+
required: true,
|
|
489
|
+
location: "input",
|
|
490
|
+
description: "ID of the object to delete."
|
|
491
|
+
}];
|
|
492
|
+
if (action === "create") return writableFieldParameters(object, "input", "field");
|
|
493
|
+
return [{
|
|
494
|
+
name: "id",
|
|
495
|
+
type: "string",
|
|
496
|
+
required: true,
|
|
497
|
+
location: "input",
|
|
498
|
+
description: "ID of the object to update."
|
|
499
|
+
}, ...writableFieldParameters(object, "input", "optional")];
|
|
500
|
+
}
|
|
501
|
+
function mcpCustomParameters(_object, _action) {
|
|
502
|
+
return [{
|
|
503
|
+
name: "id",
|
|
504
|
+
type: "string",
|
|
505
|
+
required: true,
|
|
506
|
+
location: "input",
|
|
507
|
+
description: "ID of the object to execute the action on."
|
|
508
|
+
}, {
|
|
509
|
+
name: "options",
|
|
510
|
+
type: "object",
|
|
511
|
+
required: false,
|
|
512
|
+
location: "input",
|
|
513
|
+
description: "Additional options for the custom action."
|
|
514
|
+
}];
|
|
515
|
+
}
|
|
516
|
+
function enabledCrudActions(config) {
|
|
517
|
+
if (config === false) return [];
|
|
518
|
+
const include = arrayOfStrings(configObject(config).include);
|
|
519
|
+
const exclude = new Set(arrayOfStrings(configObject(config).exclude));
|
|
520
|
+
return (include.length > 0 ? include.filter((action) => CRUD_ACTIONS.includes(action)) : [...CRUD_ACTIONS]).filter((action) => !exclude.has(action));
|
|
521
|
+
}
|
|
522
|
+
function publicCustomMethodNames(object) {
|
|
523
|
+
return Object.entries(configObject(object.methods)).filter(([name, method]) => {
|
|
524
|
+
if (CRUD_ACTIONS.includes(name) || !isRecord(method)) return false;
|
|
525
|
+
return method.isPublic === true;
|
|
526
|
+
}).map(([name]) => name).sort();
|
|
527
|
+
}
|
|
528
|
+
function enabledCustomActions(object, surface) {
|
|
529
|
+
const surfaceConfig = configObject(object.decoratorConfig)[surface];
|
|
530
|
+
if (surfaceConfig === false) return [];
|
|
531
|
+
const include = arrayOfStrings(configObject(surfaceConfig).include);
|
|
532
|
+
const exclude = new Set(arrayOfStrings(configObject(surfaceConfig).exclude));
|
|
533
|
+
const publicMethods = publicCustomMethodNames(object);
|
|
534
|
+
if (surface === "mcp" && include.length > 0) return include.filter((name) => !CRUD_ACTIONS.includes(name)).filter((name) => publicMethods.includes(name)).filter((name) => !exclude.has(name));
|
|
535
|
+
if (surface === "api") return publicMethods.filter((name) => include.length === 0 || include.includes(name)).filter((name) => !exclude.has(name));
|
|
536
|
+
const customMethodsInInclude = include.filter((name) => !CRUD_ACTIONS.includes(name));
|
|
537
|
+
return publicMethods.filter((name) => customMethodsInInclude.length === 0 || customMethodsInInclude.includes(name)).filter((name) => !exclude.has(name));
|
|
538
|
+
}
|
|
539
|
+
function routeOverrides(object) {
|
|
540
|
+
const routes = configObject(configObject(configObject(object.decoratorConfig).api).routes);
|
|
541
|
+
return Object.fromEntries(Object.entries(routes).filter((entry) => isRecord(entry[1])));
|
|
542
|
+
}
|
|
543
|
+
function methodDefinition(object, action) {
|
|
544
|
+
return configObject(configObject(object.methods)[action]);
|
|
545
|
+
}
|
|
546
|
+
function customRoutePath(object, action) {
|
|
547
|
+
const collection = stringValue(object.collection) || stringValue(object.name) || action;
|
|
548
|
+
const routeConfig = routeOverrides(object)[action] || {};
|
|
549
|
+
const method = methodDefinition(object, action);
|
|
550
|
+
const normalizedPath = (stringValue(routeConfig.path) || action).split("/").map((segment) => segment.trim()).filter(Boolean).join("/").replace(/\[([^\]]+)\]/g, "{$1}");
|
|
551
|
+
return (stringValue(routeConfig.scope) || (method.isStatic === true ? "collection" : "item")) === "collection" ? `/api/v1/${collection}/${normalizedPath}` : `/api/v1/${collection}/{id}/${normalizedPath}`;
|
|
552
|
+
}
|
|
553
|
+
function restEndpointsFrom(object) {
|
|
554
|
+
const className = stringValue(object.className) || stringValue(object.name) || "Object";
|
|
555
|
+
const collection = stringValue(object.collection) || className.toLowerCase();
|
|
556
|
+
const apiConfig = configObject(object.decoratorConfig).api;
|
|
557
|
+
const endpoints = [];
|
|
558
|
+
const crudActions = stringValue(object.extends) === "SmrtCollection" ? [] : enabledCrudActions(apiConfig);
|
|
559
|
+
for (const action of crudActions) {
|
|
560
|
+
const route = action === "list" ? [
|
|
561
|
+
"GET",
|
|
562
|
+
`/api/v1/${collection}`,
|
|
563
|
+
`List ${className} objects`
|
|
564
|
+
] : action === "create" ? [
|
|
565
|
+
"POST",
|
|
566
|
+
`/api/v1/${collection}`,
|
|
567
|
+
`Create ${className}`
|
|
568
|
+
] : action === "get" ? [
|
|
569
|
+
"GET",
|
|
570
|
+
`/api/v1/${collection}/{id}`,
|
|
571
|
+
`Get ${className} by ID`
|
|
572
|
+
] : action === "update" ? [
|
|
573
|
+
"PUT",
|
|
574
|
+
`/api/v1/${collection}/{id}`,
|
|
575
|
+
`Update ${className}`
|
|
576
|
+
] : [
|
|
577
|
+
"DELETE",
|
|
578
|
+
`/api/v1/${collection}/{id}`,
|
|
579
|
+
`Delete ${className}`
|
|
580
|
+
];
|
|
581
|
+
endpoints.push({
|
|
582
|
+
objectName: className,
|
|
583
|
+
action,
|
|
584
|
+
method: route[0],
|
|
585
|
+
path: route[1],
|
|
586
|
+
description: route[2],
|
|
587
|
+
parameters: restCrudParameters(object, action, route[1])
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
const overrides = routeOverrides(object);
|
|
591
|
+
for (const action of enabledCustomActions(object, "api")) {
|
|
592
|
+
const method = stringValue((overrides[action] || {}).method) || "POST";
|
|
593
|
+
endpoints.push({
|
|
594
|
+
objectName: className,
|
|
595
|
+
action,
|
|
596
|
+
method,
|
|
597
|
+
path: customRoutePath(object, action),
|
|
598
|
+
description: `Run ${className}.${action}`,
|
|
599
|
+
parameters: restCustomParameters(object, action, method, customRoutePath(object, action))
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
return endpoints;
|
|
603
|
+
}
|
|
604
|
+
function cliCommandsFrom(object) {
|
|
605
|
+
const className = stringValue(object.className) || stringValue(object.name) || "Object";
|
|
606
|
+
const lowerName = className.toLowerCase();
|
|
607
|
+
const cliConfig = configObject(object.decoratorConfig).cli;
|
|
608
|
+
const commands = [];
|
|
609
|
+
for (const action of enabledCrudActions(cliConfig)) {
|
|
610
|
+
const description = action === "list" ? `List ${className} objects` : action === "get" ? `Get ${className} by ID or slug` : action === "create" ? `Create new ${className}` : action === "update" ? `Update ${className}` : `Delete ${className}`;
|
|
611
|
+
commands.push({
|
|
612
|
+
objectName: className,
|
|
613
|
+
action,
|
|
614
|
+
command: `${lowerName}:${action}`,
|
|
615
|
+
description,
|
|
616
|
+
parameters: cliCrudParameters(object, action)
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
for (const action of enabledCustomActions(object, "cli")) commands.push({
|
|
620
|
+
objectName: className,
|
|
621
|
+
action,
|
|
622
|
+
command: `${lowerName}:${action}`,
|
|
623
|
+
description: `Run ${className}.${action}`,
|
|
624
|
+
parameters: cliCustomParameters(object, action)
|
|
625
|
+
});
|
|
626
|
+
return commands;
|
|
627
|
+
}
|
|
628
|
+
function mcpToolsFrom(object) {
|
|
629
|
+
const className = stringValue(object.className) || stringValue(object.name) || "Object";
|
|
630
|
+
const lowerName = className.toLowerCase();
|
|
631
|
+
const mcpConfig = configObject(object.decoratorConfig).mcp;
|
|
632
|
+
const tools = [];
|
|
633
|
+
for (const action of enabledCrudActions(mcpConfig)) {
|
|
634
|
+
const description = action === "list" ? `List ${className} objects with optional filtering` : action === "get" ? `Get a specific ${className} by ID or slug` : action === "create" ? `Create a new ${className}` : action === "update" ? `Update an existing ${className}` : `Delete a ${className} by ID`;
|
|
635
|
+
tools.push({
|
|
636
|
+
objectName: className,
|
|
637
|
+
action,
|
|
638
|
+
toolName: `${lowerName}_${action}`,
|
|
639
|
+
description,
|
|
640
|
+
parameters: mcpCrudParameters(object, action)
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
for (const action of enabledCustomActions(object, "mcp")) tools.push({
|
|
644
|
+
objectName: className,
|
|
645
|
+
action,
|
|
646
|
+
toolName: `${lowerName}_${action}`.toLowerCase(),
|
|
647
|
+
description: `Execute ${action} action on ${className}`,
|
|
648
|
+
parameters: mcpCustomParameters(object, action)
|
|
649
|
+
});
|
|
650
|
+
return tools;
|
|
651
|
+
}
|
|
652
|
+
function readApiSummary(packageDir, rootDir, packageName, knowledge, routeFiles) {
|
|
653
|
+
const objectRecords = objectRecordsFrom((knowledge.manifestPath ? readJsonIfExists(knowledge.manifestPath) : null)?.objects);
|
|
654
|
+
const objects = objectRecords.map((object) => {
|
|
655
|
+
const name = stringValue(object.className) || stringValue(object.name) || stringValue(object.key) || "unknown";
|
|
656
|
+
const className = stringValue(object.className) || name;
|
|
657
|
+
const sourcePath = resolveManifestSourcePath(rootDir, packageDir, stringValue(object.filePath) || stringValue(object.sourcePath));
|
|
658
|
+
const typedocPath = findTypedocClassPath(rootDir, packageDir, packageName, className);
|
|
659
|
+
const fields = fieldSummariesFrom(object.fields);
|
|
660
|
+
return {
|
|
661
|
+
name,
|
|
662
|
+
className,
|
|
663
|
+
qualifiedName: stringValue(object.qualifiedName) || void 0,
|
|
664
|
+
collection: stringValue(object.collection) || void 0,
|
|
665
|
+
sourcePath,
|
|
666
|
+
typedocPath,
|
|
667
|
+
description: readTypedocSummary(typedocPath),
|
|
668
|
+
fields
|
|
669
|
+
};
|
|
670
|
+
});
|
|
671
|
+
const objectNames = objects.length > 0 ? objects.map((object) => object.qualifiedName || object.className || object.name).filter(Boolean).sort() : knowledge.objectNames;
|
|
672
|
+
const restEndpoints = objectRecords.flatMap(restEndpointsFrom);
|
|
673
|
+
return {
|
|
674
|
+
objectNames,
|
|
675
|
+
objects,
|
|
676
|
+
restEndpoints,
|
|
677
|
+
cliCommands: objectRecords.flatMap(cliCommandsFrom),
|
|
678
|
+
mcpTools: objectRecords.flatMap(mcpToolsFrom),
|
|
679
|
+
endpointCount: restEndpoints.length,
|
|
680
|
+
routeFiles
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
async function collectRouteFiles(packageDir) {
|
|
684
|
+
return fg(["src/routes/**/*.{ts,svelte}", "routes/**/*.{ts,svelte}"], {
|
|
685
|
+
cwd: packageDir,
|
|
686
|
+
absolute: true,
|
|
687
|
+
onlyFiles: true,
|
|
688
|
+
ignore: ["**/node_modules/**", "**/.svelte-kit/**"]
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
async function collectMigrations(packageDir) {
|
|
692
|
+
return fg(["migrations/**/*", "src/migrations/**/*"], {
|
|
693
|
+
cwd: packageDir,
|
|
694
|
+
absolute: true,
|
|
695
|
+
onlyFiles: true,
|
|
696
|
+
ignore: ["**/node_modules/**"]
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
async function collectExamples(packageDir, docs) {
|
|
700
|
+
const fileExamples = (await fg([
|
|
701
|
+
"examples/**/*.{ts,tsx,svelte,md}",
|
|
702
|
+
"src/**/*.{example,stories}.{ts,tsx,svelte}",
|
|
703
|
+
"src/**/examples/**/*.{ts,tsx,svelte,md}"
|
|
704
|
+
], {
|
|
705
|
+
cwd: packageDir,
|
|
706
|
+
absolute: true,
|
|
707
|
+
onlyFiles: true,
|
|
708
|
+
ignore: [
|
|
709
|
+
"**/node_modules/**",
|
|
710
|
+
"**/dist/**",
|
|
711
|
+
"**/.svelte-kit/**"
|
|
712
|
+
]
|
|
713
|
+
})).slice(0, 16).map((path, index) => ({
|
|
714
|
+
id: `file:${index}`,
|
|
715
|
+
title: relative(packageDir, path),
|
|
716
|
+
path,
|
|
717
|
+
source: "file"
|
|
718
|
+
}));
|
|
719
|
+
const readme = docs.find((doc) => doc.kind === "readme");
|
|
720
|
+
const readmeExamples = readme?.content ? extractReadmeCodeBlocks(readme.content).slice(0, 8) : [];
|
|
721
|
+
return [...fileExamples, ...readmeExamples];
|
|
722
|
+
}
|
|
723
|
+
function extractReadmeCodeBlocks(content) {
|
|
724
|
+
const examples = [];
|
|
725
|
+
const blockPattern = /```([A-Za-z0-9_-]*)\n([\s\S]*?)```/g;
|
|
726
|
+
let index = 0;
|
|
727
|
+
let match = blockPattern.exec(content);
|
|
728
|
+
while (match) {
|
|
729
|
+
const language = match[1] || void 0;
|
|
730
|
+
const code = match[2] || "";
|
|
731
|
+
if (!code.trim()) {
|
|
732
|
+
match = blockPattern.exec(content);
|
|
733
|
+
continue;
|
|
734
|
+
}
|
|
735
|
+
const { content: trimmedCode } = truncate(code, EXAMPLE_LIMIT);
|
|
736
|
+
examples.push({
|
|
737
|
+
id: `readme:${index}`,
|
|
738
|
+
title: `README example ${index + 1}`,
|
|
739
|
+
language,
|
|
740
|
+
code: trimmedCode,
|
|
741
|
+
source: "readme"
|
|
742
|
+
});
|
|
743
|
+
index += 1;
|
|
744
|
+
match = blockPattern.exec(content);
|
|
745
|
+
}
|
|
746
|
+
return examples;
|
|
747
|
+
}
|
|
748
|
+
async function summarizePackage(input, rootDir) {
|
|
749
|
+
const { packageDir, packageJson, source } = input;
|
|
750
|
+
const packageName = packageJson.name || relative(rootDir, packageDir) || packageDir;
|
|
751
|
+
const docs = [
|
|
752
|
+
readDocument(packageDir, "README.md", "readme"),
|
|
753
|
+
readDocument(packageDir, "AGENTS.md", "agents"),
|
|
754
|
+
readDocument(packageDir, "CHANGELOG.md", "changelog")
|
|
755
|
+
].filter((doc) => Boolean(doc));
|
|
756
|
+
const examples = await collectExamples(packageDir, docs);
|
|
757
|
+
const knowledge = readKnowledgeSummary(packageDir);
|
|
758
|
+
const routeFiles = await collectRouteFiles(packageDir);
|
|
759
|
+
const migrations = await collectMigrations(packageDir);
|
|
760
|
+
const scripts = stringRecord(packageJson.scripts);
|
|
761
|
+
const api = readApiSummary(packageDir, rootDir, packageName, knowledge, routeFiles);
|
|
762
|
+
return {
|
|
763
|
+
name: packageName,
|
|
764
|
+
version: packageJson.version,
|
|
765
|
+
description: packageJson.description,
|
|
766
|
+
source,
|
|
767
|
+
directory: packageDir,
|
|
768
|
+
relativeDirectory: relative(rootDir, packageDir) || ".",
|
|
769
|
+
scripts,
|
|
770
|
+
dependencies: stringRecord(packageJson.dependencies),
|
|
771
|
+
devDependencies: stringRecord(packageJson.devDependencies),
|
|
772
|
+
peerDependencies: stringRecord(packageJson.peerDependencies),
|
|
773
|
+
smrtDependencies: smrtDependencyNames(packageJson),
|
|
774
|
+
sdkDependencies: sdkDependencyNames(packageJson),
|
|
775
|
+
exportKeys: exportKeys(packageJson.exports),
|
|
776
|
+
docs,
|
|
777
|
+
examples,
|
|
778
|
+
knowledge,
|
|
779
|
+
api,
|
|
780
|
+
migrations,
|
|
781
|
+
routeModuleCount: 0,
|
|
782
|
+
routeCount: 0,
|
|
783
|
+
playgroundEntryCount: 0,
|
|
784
|
+
recommendedCommands: Object.keys(scripts).filter((scriptName) => [
|
|
785
|
+
"test",
|
|
786
|
+
"typecheck",
|
|
787
|
+
"check",
|
|
788
|
+
"build",
|
|
789
|
+
"dev",
|
|
790
|
+
"workbench"
|
|
791
|
+
].includes(scriptName)).sort().map((scriptName) => ({
|
|
792
|
+
id: commandIdForScript(packageName, scriptName),
|
|
793
|
+
label: scriptName,
|
|
794
|
+
command: `pnpm --filter ${packageName} ${scriptName}`
|
|
795
|
+
}))
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
async function discoverWorkspacePackageDirs(workspaceRoot) {
|
|
799
|
+
return (await fg("packages/*/package.json", {
|
|
800
|
+
cwd: workspaceRoot,
|
|
801
|
+
absolute: true,
|
|
802
|
+
onlyFiles: true
|
|
803
|
+
})).map((packageJsonPath) => ({
|
|
804
|
+
packageDir: dirname(packageJsonPath),
|
|
805
|
+
packageJson: readJson(packageJsonPath),
|
|
806
|
+
source: "workspace"
|
|
807
|
+
})).filter((item) => typeof item.packageJson.name === "string" && item.packageJson.name.startsWith("@happyvertical/smrt-")).sort((left, right) => (left.packageJson.name || "").localeCompare(right.packageJson.name || ""));
|
|
808
|
+
}
|
|
809
|
+
function resolveNodeModulePackageDir(projectRoot, packageName) {
|
|
810
|
+
const packageJsonPath = join(projectRoot, "node_modules", packageName, "package.json");
|
|
811
|
+
return existsSync(packageJsonPath) ? dirname(packageJsonPath) : null;
|
|
812
|
+
}
|
|
813
|
+
async function discoverConsumerPackageDirs(projectRoot) {
|
|
814
|
+
const packageJsonPath = join(projectRoot, "package.json");
|
|
815
|
+
if (!existsSync(packageJsonPath)) return [];
|
|
816
|
+
const packageJson = readJson(packageJsonPath);
|
|
817
|
+
const dependencies = {
|
|
818
|
+
...packageJson.dependencies,
|
|
819
|
+
...packageJson.devDependencies,
|
|
820
|
+
...packageJson.peerDependencies
|
|
821
|
+
};
|
|
822
|
+
const packageDirs = [];
|
|
823
|
+
packageDirs.push({
|
|
824
|
+
packageDir: projectRoot,
|
|
825
|
+
packageJson,
|
|
826
|
+
source: packageJson.name?.startsWith("@happyvertical/smrt-") ? "package" : "app"
|
|
827
|
+
});
|
|
828
|
+
for (const dependencyName of Object.keys(dependencies).sort()) {
|
|
829
|
+
if (!dependencyName.startsWith("@happyvertical/smrt-") || dependencyName === "@happyvertical/smrt-workbench") continue;
|
|
830
|
+
const packageDir = resolveNodeModulePackageDir(projectRoot, dependencyName);
|
|
831
|
+
if (!packageDir) continue;
|
|
832
|
+
packageDirs.push({
|
|
833
|
+
packageDir,
|
|
834
|
+
packageJson: readJson(join(packageDir, "package.json")),
|
|
835
|
+
source: "package"
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
return packageDirs;
|
|
839
|
+
}
|
|
840
|
+
function resolvePackageByName(workspaceRoot, packageName) {
|
|
841
|
+
const packagesDir = join(workspaceRoot, "packages");
|
|
842
|
+
if (!existsSync(packagesDir)) return null;
|
|
843
|
+
const packageJsonPaths = fg.sync("*/package.json", {
|
|
844
|
+
cwd: packagesDir,
|
|
845
|
+
absolute: true,
|
|
846
|
+
onlyFiles: true
|
|
847
|
+
});
|
|
848
|
+
for (const packageJsonPath of packageJsonPaths) if (readJson(packageJsonPath).name === packageName) return dirname(packageJsonPath);
|
|
849
|
+
return null;
|
|
850
|
+
}
|
|
851
|
+
function resolveWorkbenchScope(cwd = process.cwd(), options = {}) {
|
|
852
|
+
const resolvedCwd = resolve(cwd);
|
|
853
|
+
const requestedProjectRoot = options.projectRoot ? resolve(options.projectRoot) : void 0;
|
|
854
|
+
const workspaceRoot = options.workspaceRoot || (requestedProjectRoot ? findSmrtWorkbenchWorkspaceRoot(requestedProjectRoot) : null) || findSmrtWorkbenchWorkspaceRoot(resolvedCwd);
|
|
855
|
+
if (workspaceRoot) {
|
|
856
|
+
const packageDir = (options.packageName ? resolvePackageByName(workspaceRoot, options.packageName) : null) || findPackageDir(resolvedCwd, workspaceRoot) || void 0;
|
|
857
|
+
const packageJson = packageDir ? readJsonIfExists(join(packageDir, "package.json")) : null;
|
|
858
|
+
const packageName = options.packageName || packageJson?.name;
|
|
859
|
+
return {
|
|
860
|
+
mode: packageName ? "package" : "workspace",
|
|
861
|
+
cwd: resolvedCwd,
|
|
862
|
+
projectRoot: workspaceRoot,
|
|
863
|
+
workspaceRoot,
|
|
864
|
+
packageName,
|
|
865
|
+
packageDir,
|
|
866
|
+
packageManager: "pnpm"
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
const projectRoot = requestedProjectRoot || findProjectRoot(resolvedCwd);
|
|
870
|
+
return {
|
|
871
|
+
mode: "consumer",
|
|
872
|
+
cwd: resolvedCwd,
|
|
873
|
+
projectRoot,
|
|
874
|
+
packageName: options.packageName,
|
|
875
|
+
packageManager: detectPackageManager(projectRoot)
|
|
876
|
+
};
|
|
877
|
+
}
|
|
878
|
+
async function buildWorkbenchProject(scope) {
|
|
879
|
+
const packageDirs = scope.mode === "consumer" ? await discoverConsumerPackageDirs(scope.projectRoot) : await discoverWorkspacePackageDirs(scope.projectRoot);
|
|
880
|
+
const filteredPackageDirs = scope.packageName ? packageDirs.filter((item) => item.packageJson.name === scope.packageName) : packageDirs;
|
|
881
|
+
const packages = await Promise.all(filteredPackageDirs.map((item) => summarizePackage(item, scope.projectRoot)));
|
|
882
|
+
return {
|
|
883
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
884
|
+
scope,
|
|
885
|
+
packages
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
async function discoverWorkspaceWorkbenches(workspaceRoot, packagesPattern = "packages/*/src/workbench.ts") {
|
|
889
|
+
return (await fg(packagesPattern, {
|
|
890
|
+
cwd: workspaceRoot,
|
|
891
|
+
absolute: true,
|
|
892
|
+
onlyFiles: true
|
|
893
|
+
})).sort().map((sourcePath) => {
|
|
894
|
+
const packageDir = dirname(dirname(sourcePath));
|
|
895
|
+
const packageJson = readJson(join(packageDir, "package.json"));
|
|
896
|
+
const runtimePath = join(packageDir, "dist", "workbench.js");
|
|
897
|
+
return {
|
|
898
|
+
packageName: packageJson.name,
|
|
899
|
+
source: "workspace",
|
|
900
|
+
sourcePath,
|
|
901
|
+
runtimePath: existsSync(runtimePath) ? runtimePath : void 0
|
|
902
|
+
};
|
|
903
|
+
});
|
|
904
|
+
}
|
|
905
|
+
async function discoverInstalledWorkbenches(projectRoot = process.cwd()) {
|
|
906
|
+
const packageJsonPath = join(projectRoot, "package.json");
|
|
907
|
+
if (!existsSync(packageJsonPath)) return [];
|
|
908
|
+
const packageJson = readJson(packageJsonPath);
|
|
909
|
+
const dependencies = {
|
|
910
|
+
...packageJson.dependencies,
|
|
911
|
+
...packageJson.devDependencies,
|
|
912
|
+
...packageJson.peerDependencies
|
|
913
|
+
};
|
|
914
|
+
const discovered = [];
|
|
915
|
+
for (const dependencyName of Object.keys(dependencies).sort()) {
|
|
916
|
+
if (!dependencyName.startsWith("@happyvertical/smrt-") || dependencyName === "@happyvertical/smrt-workbench") continue;
|
|
917
|
+
const packageDir = resolveNodeModulePackageDir(projectRoot, dependencyName);
|
|
918
|
+
if (!packageDir) continue;
|
|
919
|
+
if (exportKeys(readJson(join(packageDir, "package.json")).exports).includes("./workbench")) discovered.push({
|
|
920
|
+
packageName: dependencyName,
|
|
921
|
+
source: "package",
|
|
922
|
+
importSpecifier: `${dependencyName}/workbench`
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
return discovered;
|
|
926
|
+
}
|
|
927
|
+
async function discoverWorkbenchTargets(projectRoot = process.cwd(), mode = "auto", localWorkbenchPath = "src/workbench.ts", packageName, packagesPattern = "packages/*/src/workbench.ts") {
|
|
928
|
+
if ((mode === "auto" ? detectWorkbenchMode(projectRoot) : mode) === "workspace") {
|
|
929
|
+
const workspaceRoot = mode === "workspace" ? findWorkspaceRoot(projectRoot) : findSmrtWorkbenchWorkspaceRoot(projectRoot);
|
|
930
|
+
if (!workspaceRoot) return [];
|
|
931
|
+
const targets2 = await discoverWorkspaceWorkbenches(workspaceRoot, packagesPattern);
|
|
932
|
+
return packageName ? targets2.filter((target) => target.packageName === packageName) : targets2;
|
|
933
|
+
}
|
|
934
|
+
const targets = await discoverInstalledWorkbenches(projectRoot);
|
|
935
|
+
const localPath = resolve(projectRoot, localWorkbenchPath);
|
|
936
|
+
if (existsSync(localPath)) {
|
|
937
|
+
const localPackageJson = readJsonIfExists(join(projectRoot, "package.json"));
|
|
938
|
+
targets.push({
|
|
939
|
+
packageName: localPackageJson?.name,
|
|
940
|
+
source: "app",
|
|
941
|
+
sourcePath: localPath
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
return packageName ? targets.filter((target) => target.packageName === packageName) : targets;
|
|
945
|
+
}
|
|
946
|
+
async function importWorkbenchModule(input) {
|
|
947
|
+
const imported = isAbsolute(input) || input.startsWith(".") ? await importPathModule(resolve(input)) : await import(
|
|
948
|
+
/* @vite-ignore */
|
|
949
|
+
input
|
|
950
|
+
);
|
|
951
|
+
const module = imported.default ?? imported.workbench ?? imported;
|
|
952
|
+
return module && typeof module === "object" ? coerceWorkbenchModules(module) : [];
|
|
953
|
+
}
|
|
954
|
+
async function importPathModule(inputPath) {
|
|
955
|
+
if (!TS_SOURCE_EXTENSIONS.has(extname(inputPath))) return import(
|
|
956
|
+
/* @vite-ignore */
|
|
957
|
+
pathToFileURL(inputPath).href
|
|
958
|
+
);
|
|
959
|
+
let tsxApiPath;
|
|
960
|
+
try {
|
|
961
|
+
tsxApiPath = require.resolve("tsx/esm/api");
|
|
962
|
+
} catch (tsxError) {
|
|
963
|
+
throw new Error(`Failed to load workbench module from ${inputPath}: source workbench discovery requires the "tsx" package.`, { cause: tsxError });
|
|
964
|
+
}
|
|
965
|
+
const { tsImport } = await import(
|
|
966
|
+
/* @vite-ignore */
|
|
967
|
+
pathToFileURL(tsxApiPath).href
|
|
968
|
+
);
|
|
969
|
+
return tsImport(pathToFileURL(inputPath).href, { parentURL: import.meta.url });
|
|
970
|
+
}
|
|
971
|
+
function describeWorkbenchSource(target, cwd = process.cwd()) {
|
|
972
|
+
if (target.source === "package") return target.importSpecifier || target.packageName || "installed package";
|
|
973
|
+
const path = target.sourcePath || target.runtimePath;
|
|
974
|
+
return path ? relative(cwd, path) || "." : target.source;
|
|
975
|
+
}
|
|
976
|
+
//#endregion
|
|
977
|
+
export { discoverWorkbenchTargets as a, findProjectRoot as c, importWorkbenchModule as d, resolveWorkbenchScope as f, discoverInstalledWorkbenches as i, findSmrtWorkbenchWorkspaceRoot as l, describeWorkbenchSource as n, discoverWorkspaceWorkbenches as o, detectWorkbenchMode as r, findPackageDir as s, buildWorkbenchProject as t, findWorkspaceRoot as u };
|
|
978
|
+
|
|
979
|
+
//# sourceMappingURL=discovery-G9-foyW8.js.map
|