@lumerahq/cli 0.19.22 → 0.19.24-dev.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-RH4FQF6O.js → chunk-3ESHIPZH.js} +1 -1
- package/dist/{chunk-XT4EGGK7.js → chunk-HEUXW7SQ.js} +28 -20
- package/dist/{chunk-JMRD5WIY.js → chunk-HTPNRY3F.js} +17 -29
- package/dist/{deps-VTLXKCQ7.js → deps-HWO6X5WM.js} +2 -2
- package/dist/{dev-EQK27BUM.js → dev-AZ6APGU3.js} +3 -3
- package/dist/index.js +12 -12
- package/dist/{init-UF4JYQSQ.js → init-FXG2BFGT.js} +113 -2
- package/dist/{register-BQCVUPWS.js → register-ZCNHHGZH.js} +1 -1
- package/dist/{resources-Z5ZMPWTK.js → resources-33YKJE7P.js} +4 -4
- package/dist/{run-LHHX7UBN.js → run-DJXAXDD2.js} +1 -1
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
userContextHeaders,
|
|
3
3
|
validateProjectExternalId,
|
|
4
4
|
validateProjectNameLength
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-HTPNRY3F.js";
|
|
6
6
|
import {
|
|
7
7
|
fetchWithRetry
|
|
8
8
|
} from "./chunk-FJFIWC7G.js";
|
|
@@ -69,29 +69,12 @@ async function ensureAppRecord(apiBase, token, appName, appTitle) {
|
|
|
69
69
|
if (appIdError) throw new Error(appIdError);
|
|
70
70
|
const appNameError = validateProjectNameLength(appTitle, "App name");
|
|
71
71
|
if (appNameError) throw new Error(appNameError);
|
|
72
|
-
const filterParam = encodeURIComponent(JSON.stringify({ external_id: appName }));
|
|
73
|
-
const searchRes = await fetchWithRetry(
|
|
74
|
-
`${apiBase}/pb/collections/lm_custom_apps/records?filter=${filterParam}`,
|
|
75
|
-
{
|
|
76
|
-
headers: {
|
|
77
|
-
Authorization: `Bearer ${token}`,
|
|
78
|
-
"X-Lumera-Client": "lumera-cli",
|
|
79
|
-
...userContextHeaders()
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
);
|
|
83
|
-
if (!searchRes.ok) {
|
|
84
|
-
throw new Error(`Failed to check app record: ${await searchRes.text()}`);
|
|
85
|
-
}
|
|
86
|
-
const existing = (await searchRes.json()).items?.[0];
|
|
87
72
|
const appData = {
|
|
88
|
-
external_id: appName,
|
|
89
73
|
name: appTitle,
|
|
90
74
|
hosting_type: "s3"
|
|
91
75
|
};
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
method: existing ? "PATCH" : "POST",
|
|
76
|
+
const res = await fetchWithRetry(`${apiBase}/custom-apps/${encodeURIComponent(appName)}/registration`, {
|
|
77
|
+
method: "PUT",
|
|
95
78
|
headers: {
|
|
96
79
|
Authorization: `Bearer ${token}`,
|
|
97
80
|
"Content-Type": "application/json",
|
|
@@ -145,6 +128,29 @@ async function deploy(options) {
|
|
|
145
128
|
Deployed! ${launchUrl}`));
|
|
146
129
|
return { launchUrl, version };
|
|
147
130
|
}
|
|
131
|
+
async function registerDevApp(apiBase, token, appName, appTitle, appUrl) {
|
|
132
|
+
const appIdError = validateProjectExternalId(appName);
|
|
133
|
+
if (appIdError) throw new Error(appIdError);
|
|
134
|
+
const appNameError = validateProjectNameLength(appTitle, "App name");
|
|
135
|
+
if (appNameError) throw new Error(appNameError);
|
|
136
|
+
const appData = {
|
|
137
|
+
name: appTitle,
|
|
138
|
+
dev_iframe_url: appUrl
|
|
139
|
+
};
|
|
140
|
+
const res = await fetchWithRetry(`${apiBase}/custom-apps/${encodeURIComponent(appName)}/registration`, {
|
|
141
|
+
method: "PUT",
|
|
142
|
+
headers: {
|
|
143
|
+
Authorization: `Bearer ${token}`,
|
|
144
|
+
"Content-Type": "application/json",
|
|
145
|
+
"X-Lumera-Client": "lumera-cli",
|
|
146
|
+
...userContextHeaders()
|
|
147
|
+
},
|
|
148
|
+
body: JSON.stringify(appData)
|
|
149
|
+
});
|
|
150
|
+
if (!res.ok) {
|
|
151
|
+
throw new Error(`Failed to register app: ${await res.text()}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
148
154
|
function isPortInUse(port, host = "127.0.0.1") {
|
|
149
155
|
return new Promise((resolve2) => {
|
|
150
156
|
const socket = createConnection({ port, host });
|
|
@@ -236,6 +242,8 @@ async function dev(options) {
|
|
|
236
242
|
console.log(pc.yellow(` Dev server already running on port ${port}, skipping start.`));
|
|
237
243
|
return;
|
|
238
244
|
}
|
|
245
|
+
console.log(pc.dim(" Registering dev preview URL..."));
|
|
246
|
+
await registerDevApp(apiUrl, token, appName, appTitle, appUrl);
|
|
239
247
|
console.log();
|
|
240
248
|
console.log(pc.cyan(pc.bold(` ${appTitle} - Dev Server`)));
|
|
241
249
|
console.log();
|
|
@@ -328,42 +328,30 @@ var ApiClient = class {
|
|
|
328
328
|
body: JSON.stringify(body)
|
|
329
329
|
});
|
|
330
330
|
}
|
|
331
|
-
// Projects — register
|
|
331
|
+
// Projects — register through typed project API; lookup can still use PB reads.
|
|
332
332
|
async registerProject(name, externalId) {
|
|
333
333
|
const extId = externalId || name;
|
|
334
334
|
const nameError = validateProjectNameLength(name, "App name");
|
|
335
335
|
if (nameError) throw new Error(nameError);
|
|
336
336
|
const externalIdError = validateProjectExternalId(extId);
|
|
337
337
|
if (externalIdError) throw new Error(externalIdError);
|
|
338
|
-
const
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
const rec = await this.request(
|
|
344
|
-
"/api/pb/collections/lm_projects/records",
|
|
345
|
-
{
|
|
346
|
-
method: "POST",
|
|
347
|
-
body: JSON.stringify({ name, external_id: extId })
|
|
348
|
-
}
|
|
349
|
-
);
|
|
350
|
-
return {
|
|
351
|
-
id: rec.id,
|
|
352
|
-
name: rec.name,
|
|
353
|
-
external_id: rec.external_id,
|
|
354
|
-
template: rec.template,
|
|
355
|
-
status: rec.status,
|
|
356
|
-
owner_id: rec.owner_id,
|
|
357
|
-
created: rec.created,
|
|
358
|
-
updated: rec.updated
|
|
359
|
-
};
|
|
360
|
-
} catch (error) {
|
|
361
|
-
const created = await this.getProjectByExternalId(extId);
|
|
362
|
-
if (created) {
|
|
363
|
-
return created;
|
|
338
|
+
const res = await this.request(
|
|
339
|
+
"/api/projects/register",
|
|
340
|
+
{
|
|
341
|
+
method: "POST",
|
|
342
|
+
body: JSON.stringify({ name, external_id: extId })
|
|
364
343
|
}
|
|
365
|
-
|
|
366
|
-
|
|
344
|
+
);
|
|
345
|
+
return {
|
|
346
|
+
id: res.project.id,
|
|
347
|
+
name: res.project.name,
|
|
348
|
+
external_id: res.project.external_id,
|
|
349
|
+
template: res.project.template,
|
|
350
|
+
status: res.project.status,
|
|
351
|
+
owner_id: res.project.owner_id,
|
|
352
|
+
created: res.project.created,
|
|
353
|
+
updated: res.project.updated
|
|
354
|
+
};
|
|
367
355
|
}
|
|
368
356
|
async getProjectByExternalId(externalId) {
|
|
369
357
|
const filterParam = encodeURIComponent(JSON.stringify({ external_id: externalId }));
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
deps,
|
|
3
3
|
syncDeps
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-3ESHIPZH.js";
|
|
5
5
|
import "./chunk-2CR762KB.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-HTPNRY3F.js";
|
|
7
7
|
import "./chunk-ZH3NVYEQ.js";
|
|
8
8
|
import "./chunk-FJFIWC7G.js";
|
|
9
9
|
import "./chunk-PNKVD2UK.js";
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
dev
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-HEUXW7SQ.js";
|
|
4
4
|
import {
|
|
5
5
|
syncDeps
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-3ESHIPZH.js";
|
|
7
7
|
import {
|
|
8
8
|
loadEnv
|
|
9
9
|
} from "./chunk-2CR762KB.js";
|
|
10
10
|
import {
|
|
11
11
|
createApiClient
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-HTPNRY3F.js";
|
|
13
13
|
import {
|
|
14
14
|
findProjectRoot,
|
|
15
15
|
getApiUrl,
|
package/dist/index.js
CHANGED
|
@@ -219,39 +219,39 @@ async function main() {
|
|
|
219
219
|
switch (command) {
|
|
220
220
|
// Resource commands
|
|
221
221
|
case "plan":
|
|
222
|
-
await import("./resources-
|
|
222
|
+
await import("./resources-33YKJE7P.js").then((m) => m.plan(args.slice(1)));
|
|
223
223
|
break;
|
|
224
224
|
case "apply":
|
|
225
|
-
await import("./resources-
|
|
225
|
+
await import("./resources-33YKJE7P.js").then((m) => m.apply(args.slice(1)));
|
|
226
226
|
break;
|
|
227
227
|
case "pull":
|
|
228
|
-
await import("./resources-
|
|
228
|
+
await import("./resources-33YKJE7P.js").then((m) => m.pull(args.slice(1)));
|
|
229
229
|
break;
|
|
230
230
|
case "destroy":
|
|
231
|
-
await import("./resources-
|
|
231
|
+
await import("./resources-33YKJE7P.js").then((m) => m.destroy(args.slice(1)));
|
|
232
232
|
break;
|
|
233
233
|
case "list":
|
|
234
|
-
await import("./resources-
|
|
234
|
+
await import("./resources-33YKJE7P.js").then((m) => m.list(args.slice(1)));
|
|
235
235
|
break;
|
|
236
236
|
case "show":
|
|
237
|
-
await import("./resources-
|
|
237
|
+
await import("./resources-33YKJE7P.js").then((m) => m.show(args.slice(1)));
|
|
238
238
|
break;
|
|
239
239
|
case "diff":
|
|
240
|
-
await import("./resources-
|
|
240
|
+
await import("./resources-33YKJE7P.js").then((m) => m.diff(args.slice(1)));
|
|
241
241
|
break;
|
|
242
242
|
// Development
|
|
243
243
|
case "dev":
|
|
244
|
-
await import("./dev-
|
|
244
|
+
await import("./dev-AZ6APGU3.js").then((m) => m.dev(args.slice(1)));
|
|
245
245
|
break;
|
|
246
246
|
case "run":
|
|
247
|
-
await import("./run-
|
|
247
|
+
await import("./run-DJXAXDD2.js").then((m) => m.run(args.slice(1)));
|
|
248
248
|
break;
|
|
249
249
|
// Project
|
|
250
250
|
case "init":
|
|
251
|
-
await import("./init-
|
|
251
|
+
await import("./init-FXG2BFGT.js").then((m) => m.init(args.slice(1)));
|
|
252
252
|
break;
|
|
253
253
|
case "register":
|
|
254
|
-
await import("./register-
|
|
254
|
+
await import("./register-ZCNHHGZH.js").then((m) => m.register(args.slice(1)));
|
|
255
255
|
break;
|
|
256
256
|
case "templates":
|
|
257
257
|
await import("./templates-LNUOTNLN.js").then((m) => m.templates(subcommand, args.slice(2)));
|
|
@@ -268,7 +268,7 @@ async function main() {
|
|
|
268
268
|
break;
|
|
269
269
|
// Dependencies
|
|
270
270
|
case "deps":
|
|
271
|
-
await import("./deps-
|
|
271
|
+
await import("./deps-HWO6X5WM.js").then((m) => m.deps(args.slice(1)));
|
|
272
272
|
break;
|
|
273
273
|
// Auth
|
|
274
274
|
case "login":
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
import {
|
|
9
9
|
createApiClient,
|
|
10
10
|
validateProjectNameLength
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-HTPNRY3F.js";
|
|
12
12
|
import {
|
|
13
13
|
getToken,
|
|
14
14
|
init_auth,
|
|
@@ -27,7 +27,7 @@ import pc from "picocolors";
|
|
|
27
27
|
import prompts from "prompts";
|
|
28
28
|
import { execSync } from "child_process";
|
|
29
29
|
import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, rmSync, readlinkSync, symlinkSync } from "fs";
|
|
30
|
-
import { join, resolve } from "path";
|
|
30
|
+
import { dirname, join, resolve } from "path";
|
|
31
31
|
function toTitleCase(str) {
|
|
32
32
|
return str.split(/[-_]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
33
33
|
}
|
|
@@ -43,6 +43,84 @@ function processTemplate(content, replacements) {
|
|
|
43
43
|
var TEMPLATE_EXCLUDE = /* @__PURE__ */ new Set(["template.json"]);
|
|
44
44
|
var TEMPLATE_RENAMES = /* @__PURE__ */ new Map([["_gitignore", ".gitignore"]]);
|
|
45
45
|
var TEMPLATE_RAW_COPY = /* @__PURE__ */ new Set(["pnpm-lock.yaml"]);
|
|
46
|
+
var STUDIO_PROBLEM_FIRST_FLAG = "studio_problem_first";
|
|
47
|
+
var MEMORY_AGENTS_SECTION = `## Project Memory
|
|
48
|
+
|
|
49
|
+
This project may use repo-backed memory in \`memory/\`.
|
|
50
|
+
|
|
51
|
+
Before substantial work, read \`memory/README.md\` if it exists, then any
|
|
52
|
+
relevant memory files.
|
|
53
|
+
|
|
54
|
+
Update memory when the user confirms a durable project decision, workflow
|
|
55
|
+
rule, data contract, naming convention, recurring assumption, or correction
|
|
56
|
+
that should survive future sessions.
|
|
57
|
+
|
|
58
|
+
Do not store secrets, credentials, tokens, private keys, or raw sensitive
|
|
59
|
+
source files in memory.
|
|
60
|
+
|
|
61
|
+
If a memory item affects app behavior, do not rely only on repo memory.
|
|
62
|
+
Persist it to the app's runtime source of truth, such as collections or config,
|
|
63
|
+
and expose it in the app UI or a clear review/edit path.
|
|
64
|
+
|
|
65
|
+
Summarize memory updates in your final response.
|
|
66
|
+
`;
|
|
67
|
+
var MEMORY_FILES = /* @__PURE__ */ new Map([
|
|
68
|
+
["README.md", `# Project Memory
|
|
69
|
+
|
|
70
|
+
Repo-backed memory keeps durable project context available across agent
|
|
71
|
+
sessions. Keep it concise and reviewable.
|
|
72
|
+
|
|
73
|
+
Use this folder for:
|
|
74
|
+
- confirmed project decisions
|
|
75
|
+
- durable workflow rules and mappings
|
|
76
|
+
- data contracts and source-file assumptions
|
|
77
|
+
- runbooks for recurring work
|
|
78
|
+
- open questions that should carry forward
|
|
79
|
+
|
|
80
|
+
Do not store secrets, credentials, tokens, private keys, or raw sensitive source
|
|
81
|
+
files here.
|
|
82
|
+
|
|
83
|
+
If a memory item affects app behavior, also persist it to the app's runtime
|
|
84
|
+
source of truth, such as collections or config, and expose it in the app UI or a
|
|
85
|
+
clear review/edit path.
|
|
86
|
+
`],
|
|
87
|
+
["project-context.md", `# Project Context
|
|
88
|
+
|
|
89
|
+
Summarize what this app does, who uses it, and the recurring workflow it
|
|
90
|
+
supports.
|
|
91
|
+
`],
|
|
92
|
+
["decisions.md", `# Decisions
|
|
93
|
+
|
|
94
|
+
Record durable decisions that future sessions should preserve.
|
|
95
|
+
|
|
96
|
+
| Date | Decision | Reason |
|
|
97
|
+
| --- | --- | --- |
|
|
98
|
+
`],
|
|
99
|
+
["open-questions.md", `# Open Questions
|
|
100
|
+
|
|
101
|
+
Track unresolved questions that should be answered before durable workflow
|
|
102
|
+
behavior is finalized.
|
|
103
|
+
`],
|
|
104
|
+
["rules/README.md", `# Rules
|
|
105
|
+
|
|
106
|
+
Document durable business rules, validation rules, thresholds, and exception
|
|
107
|
+
handling expectations.
|
|
108
|
+
`],
|
|
109
|
+
["mappings/README.md", `# Mappings
|
|
110
|
+
|
|
111
|
+
Document source-to-target mappings, normalization rules, and user-approved
|
|
112
|
+
corrections.
|
|
113
|
+
`],
|
|
114
|
+
["data-contracts/README.md", `# Data Contracts
|
|
115
|
+
|
|
116
|
+
Document expected source files, required columns, data types, and known source
|
|
117
|
+
system assumptions.
|
|
118
|
+
`],
|
|
119
|
+
["runbooks/README.md", `# Runbooks
|
|
120
|
+
|
|
121
|
+
Document repeatable operating steps for recurring workflows.
|
|
122
|
+
`]
|
|
123
|
+
]);
|
|
46
124
|
function copyDir(src, dest, replacements, isRoot = true) {
|
|
47
125
|
if (!existsSync(dest)) {
|
|
48
126
|
mkdirSync(dest, { recursive: true });
|
|
@@ -82,6 +160,35 @@ function ensureClaudeInstructionsLink(projectRoot) {
|
|
|
82
160
|
writeFileSync(claudeMdPath, readFileSync(agentsMdPath, "utf-8"));
|
|
83
161
|
}
|
|
84
162
|
}
|
|
163
|
+
async function isStudioProblemFirstScaffoldEnabled(targetDir) {
|
|
164
|
+
try {
|
|
165
|
+
const token = getToken(targetDir);
|
|
166
|
+
const api = createApiClient(token);
|
|
167
|
+
const me = await api.getMe();
|
|
168
|
+
return me.user?.feature_flags?.[STUDIO_PROBLEM_FIRST_FLAG] === true;
|
|
169
|
+
} catch {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function writeFileIfMissing(path, content) {
|
|
174
|
+
if (existsSync(path)) return;
|
|
175
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
176
|
+
writeFileSync(path, content);
|
|
177
|
+
}
|
|
178
|
+
function applyProblemFirstMemoryScaffold(projectRoot) {
|
|
179
|
+
const agentsMdPath = join(projectRoot, "AGENTS.md");
|
|
180
|
+
if (existsSync(agentsMdPath)) {
|
|
181
|
+
const agentsMd = readFileSync(agentsMdPath, "utf-8");
|
|
182
|
+
if (!agentsMd.includes("## Project Memory")) {
|
|
183
|
+
writeFileSync(agentsMdPath, `${agentsMd.trimEnd()}
|
|
184
|
+
|
|
185
|
+
${MEMORY_AGENTS_SECTION}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
for (const [relativePath, content] of MEMORY_FILES) {
|
|
189
|
+
writeFileIfMissing(join(projectRoot, "memory", relativePath), content);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
85
192
|
function isGitInstalled() {
|
|
86
193
|
try {
|
|
87
194
|
execSync("git --version", { stdio: "ignore" });
|
|
@@ -374,7 +481,11 @@ async function init(args) {
|
|
|
374
481
|
[sourceName, finalProjectName],
|
|
375
482
|
[sourceTitle, projectTitle]
|
|
376
483
|
];
|
|
484
|
+
const problemFirstScaffold = await isStudioProblemFirstScaffoldEnabled(targetDir);
|
|
377
485
|
copyDir(templateDir, targetDir, replacements);
|
|
486
|
+
if (problemFirstScaffold) {
|
|
487
|
+
applyProblemFirstMemoryScaffold(targetDir);
|
|
488
|
+
}
|
|
378
489
|
ensureClaudeInstructionsLink(targetDir);
|
|
379
490
|
const installCommand = existsSync(join(targetDir, "pnpm-lock.yaml")) ? "pnpm install --frozen-lockfile" : "pnpm install";
|
|
380
491
|
function listFiles(dir, prefix = "") {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
deploy
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-HEUXW7SQ.js";
|
|
4
4
|
import {
|
|
5
5
|
syncDeps
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-3ESHIPZH.js";
|
|
7
7
|
import {
|
|
8
8
|
loadEnv
|
|
9
9
|
} from "./chunk-2CR762KB.js";
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
ApiError,
|
|
12
12
|
createApiClient,
|
|
13
13
|
validateCollectionNameLength
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-HTPNRY3F.js";
|
|
15
15
|
import {
|
|
16
16
|
findProjectRoot,
|
|
17
17
|
getApiUrl,
|
|
@@ -2872,7 +2872,7 @@ async function destroyApp(skipConfirm) {
|
|
|
2872
2872
|
}
|
|
2873
2873
|
}
|
|
2874
2874
|
const deleteRes = await fetchWithRetry(
|
|
2875
|
-
`${apiUrl}/api/
|
|
2875
|
+
`${apiUrl}/api/custom-apps/${encodeURIComponent(appRecord.external_id)}`,
|
|
2876
2876
|
{
|
|
2877
2877
|
method: "DELETE",
|
|
2878
2878
|
headers: {
|