@lumerahq/cli 0.19.3-dev.0 → 0.19.4
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/README.md +5 -3
- package/dist/chunk-5T22627H.js +289 -0
- package/dist/chunk-H357NP7T.js +77 -0
- package/dist/{chunk-AUYOTENF.js → chunk-HU7RYLUF.js} +1 -1
- package/dist/{chunk-53NOF33P.js → chunk-SU26C4GL.js} +9 -49
- package/dist/{chunk-GKI2HQJC.js → chunk-UH5X43VV.js} +30 -3
- package/dist/{deps-ULTIIDYK.js → deps-GCOH3TXL.js} +4 -2
- package/dist/{dev-5JMHMS4U.js → dev-JLSTLIMZ.js} +14 -2
- package/dist/index.js +14 -14
- package/dist/{init-37XOMJLU.js → init-CBZAIERZ.js} +54 -24
- package/dist/{register-JJUMS4FI.js → register-N2LF3CCK.js} +1 -1
- package/dist/{resources-4IUZYKGX.js → resources-RHF2MDF7.js} +237 -159
- package/dist/{run-5ZOSPBGO.js → run-XJLB4AFD.js} +1 -1
- package/dist/{skills-TNJHMV4F.js → skills-REOKLNEF.js} +1 -1
- package/dist/{templates-M3RDNDDY.js → templates-LNUOTNLN.js} +2 -3
- package/package.json +1 -1
- package/templates/default/.agents/skills/.gitkeep +0 -0
- package/templates/default/AGENTS.md +151 -0
- package/templates/default/README.md +18 -0
- package/templates/default/_gitignore +14 -0
- package/templates/default/architecture.md +29 -0
- package/templates/default/biome.json +38 -0
- package/templates/default/components.json +21 -0
- package/templates/default/icon.svg +29 -0
- package/templates/default/index.html +16 -0
- package/templates/default/package.json +53 -0
- package/templates/default/platform/automations/.gitkeep +0 -0
- package/templates/default/platform/collections/.gitkeep +0 -0
- package/templates/default/platform/hooks/.gitkeep +0 -0
- package/templates/default/pyproject.toml +14 -0
- package/templates/default/scripts/.gitkeep +0 -0
- package/templates/default/src/components/layout.tsx +11 -0
- package/templates/default/src/lib/auth.ts +9 -0
- package/templates/default/src/lib/queries.ts +17 -0
- package/templates/default/src/lib/utils.ts +6 -0
- package/templates/default/src/main.tsx +130 -0
- package/templates/default/src/routes/__root.tsx +10 -0
- package/templates/default/src/routes/index.tsx +87 -0
- package/templates/default/src/styles.css +128 -0
- package/templates/default/template.json +7 -0
- package/templates/default/tsconfig.json +22 -0
- package/templates/default/vite.config.ts +28 -0
- package/dist/chunk-OQW5E7UT.js +0 -159
- package/dist/chunk-XDTWVFPE.js +0 -120
package/README.md
CHANGED
|
@@ -51,24 +51,26 @@ For CI/CD or scripted environments, use `-y` (or `--yes`) flag:
|
|
|
51
51
|
lumera init my-app -y # Creates ./my-app
|
|
52
52
|
lumera init my-app -y --dir ./apps # Creates ./apps
|
|
53
53
|
lumera init my-app -y --force # Overwrites if directory exists
|
|
54
|
-
lumera init my-app -y --install
|
|
54
|
+
lumera init my-app -y --no-install # Skip pnpm install
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
### Init Options
|
|
58
58
|
|
|
59
59
|
| Flag | Short | Description |
|
|
60
60
|
|------|-------|-------------|
|
|
61
|
+
| `--template <name>` | `-t` | Template to use (currently bundled: `default`) |
|
|
61
62
|
| `--yes` | `-y` | Non-interactive mode (project name required) |
|
|
62
63
|
| `--dir <path>` | `-d` | Target directory (defaults to project name) |
|
|
63
64
|
| `--force` | `-f` | Overwrite existing directory without prompting |
|
|
64
|
-
| `--install` |
|
|
65
|
+
| `--no-install` | | Skip dependency installation |
|
|
66
|
+
| `--open` | `-o` | Open project in editor after scaffolding |
|
|
65
67
|
| `--help` | `-h` | Show help |
|
|
66
68
|
|
|
67
69
|
### CI/CD Example
|
|
68
70
|
|
|
69
71
|
```bash
|
|
70
72
|
# Full non-interactive setup
|
|
71
|
-
lumera init my-app -y -f
|
|
73
|
+
lumera init my-app -y -f
|
|
72
74
|
```
|
|
73
75
|
|
|
74
76
|
## Authentication
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import {
|
|
2
|
+
loadEnv
|
|
3
|
+
} from "./chunk-2CR762KB.js";
|
|
4
|
+
import {
|
|
5
|
+
createApiClient,
|
|
6
|
+
isApiErrorStatus
|
|
7
|
+
} from "./chunk-UH5X43VV.js";
|
|
8
|
+
import {
|
|
9
|
+
findProjectRoot,
|
|
10
|
+
getAppName
|
|
11
|
+
} from "./chunk-ZH3NVYEQ.js";
|
|
12
|
+
|
|
13
|
+
// src/commands/deps.ts
|
|
14
|
+
import pc from "picocolors";
|
|
15
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync } from "fs";
|
|
16
|
+
import { join } from "path";
|
|
17
|
+
var LEGACY_DEPS_FILE = "platform/project_deps.json";
|
|
18
|
+
var SHARED_COLLECTIONS_DIR = "platform/collections/shared";
|
|
19
|
+
function loadLegacyDeps(projectRoot) {
|
|
20
|
+
const depsPath = join(projectRoot, LEGACY_DEPS_FILE);
|
|
21
|
+
if (!existsSync(depsPath)) return null;
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(readFileSync(depsPath, "utf-8"));
|
|
24
|
+
} catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function sharedCollectionsDir(projectRoot) {
|
|
29
|
+
return join(projectRoot, SHARED_COLLECTIONS_DIR);
|
|
30
|
+
}
|
|
31
|
+
function safeShareFilenamePart(value) {
|
|
32
|
+
return value.trim().replace(/[^a-zA-Z0-9._-]+/g, "_").replace(/^_+|_+$/g, "") || "unknown";
|
|
33
|
+
}
|
|
34
|
+
function shareCollectionName(share) {
|
|
35
|
+
return share.object_name || share.object_display_name || share.object_id;
|
|
36
|
+
}
|
|
37
|
+
function shareFileName(share) {
|
|
38
|
+
const source = safeShareFilenamePart(share.source_project.external_id || share.source_project.name || "source");
|
|
39
|
+
const collection = safeShareFilenamePart(shareCollectionName(share));
|
|
40
|
+
return `${source}__${collection}.json`;
|
|
41
|
+
}
|
|
42
|
+
function readSharedCollectionFile(path) {
|
|
43
|
+
try {
|
|
44
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
45
|
+
} catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function loadSharedCollectionDeps(projectRoot) {
|
|
50
|
+
const dir = sharedCollectionsDir(projectRoot);
|
|
51
|
+
if (!existsSync(dir)) return [];
|
|
52
|
+
const deps2 = [];
|
|
53
|
+
for (const entry of readdirSync(dir)) {
|
|
54
|
+
if (!entry.endsWith(".json")) continue;
|
|
55
|
+
const filePath = join(dir, entry);
|
|
56
|
+
const dep = readSharedCollectionFile(filePath);
|
|
57
|
+
if (dep) deps2.push({ file: entry, dep });
|
|
58
|
+
}
|
|
59
|
+
return deps2.sort((a, b) => a.file.localeCompare(b.file));
|
|
60
|
+
}
|
|
61
|
+
async function projectPermissionsModeEnabled(api) {
|
|
62
|
+
try {
|
|
63
|
+
return await api.isProjectPermissionsModeEnabled();
|
|
64
|
+
} catch {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function declarationForShare(share, existing) {
|
|
69
|
+
return {
|
|
70
|
+
...existing ?? {},
|
|
71
|
+
source_project: share.source_project.external_id,
|
|
72
|
+
collection: shareCollectionName(share),
|
|
73
|
+
privilege: share.privilege,
|
|
74
|
+
reason: typeof existing?.reason === "string" ? existing.reason : ""
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
async function listIncomingCollectionShares(api, projectExternalId) {
|
|
78
|
+
const result = await api.listProjectResourceShares(projectExternalId, "incoming");
|
|
79
|
+
return (result.shares ?? []).filter((share) => share.object_type === "collection").sort((a, b) => {
|
|
80
|
+
const bySource = a.source_project.external_id.localeCompare(b.source_project.external_id);
|
|
81
|
+
if (bySource !== 0) return bySource;
|
|
82
|
+
return shareCollectionName(a).localeCompare(shareCollectionName(b));
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
async function syncResourceShareDeps(projectRoot, api, projectExternalId, opts) {
|
|
86
|
+
let shares;
|
|
87
|
+
try {
|
|
88
|
+
shares = await listIncomingCollectionShares(api, projectExternalId);
|
|
89
|
+
} catch (e) {
|
|
90
|
+
if (opts.ignorePermissionDenied && isApiErrorStatus(e, 403)) {
|
|
91
|
+
console.log(pc.yellow(" \u26A0"), "Skipping resource-share sync \u2014 current token cannot list project shares (403).");
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
if (!opts.quiet) {
|
|
95
|
+
console.log(pc.red(" \u2717"), `Failed to list incoming resource shares \u2014 ${e}`);
|
|
96
|
+
}
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
if (shares.length === 0) {
|
|
100
|
+
if (!opts.quiet) console.log(pc.dim(" No incoming resource shares."));
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
const dir = sharedCollectionsDir(projectRoot);
|
|
104
|
+
const missing = [];
|
|
105
|
+
if (opts.write) {
|
|
106
|
+
mkdirSync(dir, { recursive: true });
|
|
107
|
+
}
|
|
108
|
+
for (const share of shares) {
|
|
109
|
+
const fileName = shareFileName(share);
|
|
110
|
+
const filePath = join(dir, fileName);
|
|
111
|
+
const exists = existsSync(filePath);
|
|
112
|
+
if (!opts.write) {
|
|
113
|
+
if (!exists) missing.push(fileName);
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const existing = exists ? readSharedCollectionFile(filePath) ?? void 0 : void 0;
|
|
117
|
+
const declaration = declarationForShare(share, existing);
|
|
118
|
+
writeFileSync(filePath, JSON.stringify(declaration, null, 2) + "\n");
|
|
119
|
+
if (!opts.quiet) {
|
|
120
|
+
const action = exists ? pc.yellow("\u21BB") : pc.green("+");
|
|
121
|
+
console.log(` ${action} collections/shared/${fileName}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (!opts.write && missing.length > 0) {
|
|
125
|
+
if (!opts.quiet) {
|
|
126
|
+
console.log(pc.yellow(` \u26A0 ${missing.length} incoming resource share${missing.length === 1 ? "" : "s"} not synced locally.`));
|
|
127
|
+
for (const file of missing) console.log(pc.dim(` collections/shared/${file}`));
|
|
128
|
+
console.log(pc.dim(` Run ${pc.cyan("lumera deps sync")} to write shared resource declarations.`));
|
|
129
|
+
}
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
if (!opts.quiet && !opts.write) {
|
|
133
|
+
console.log(pc.green(" \u2713"), "Incoming resource shares are synced locally.");
|
|
134
|
+
}
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
async function syncLegacyDeps(projectRoot, api, quiet = false) {
|
|
138
|
+
const deps2 = loadLegacyDeps(projectRoot);
|
|
139
|
+
if (!deps2 || !deps2.dependencies || Object.keys(deps2.dependencies).length === 0) {
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
let allOk = true;
|
|
143
|
+
for (const [depProject, config] of Object.entries(deps2.dependencies)) {
|
|
144
|
+
try {
|
|
145
|
+
const manifest = await api.getProjectManifest(depProject);
|
|
146
|
+
const remoteNames = new Set(manifest.collections.map((c) => c.name));
|
|
147
|
+
for (const col of config.collections) {
|
|
148
|
+
if (remoteNames.has(col)) {
|
|
149
|
+
if (!quiet) console.log(pc.green(" \u2713"), `${depProject}/${col}`);
|
|
150
|
+
} else {
|
|
151
|
+
if (!quiet) console.log(pc.red(" \u2717"), `${depProject}/${col} \u2014 not found in project manifest`);
|
|
152
|
+
allOk = false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
} catch (e) {
|
|
156
|
+
if (!quiet) console.log(pc.red(" \u2717"), `${depProject}: failed to fetch manifest \u2014 ${e}`);
|
|
157
|
+
allOk = false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return allOk;
|
|
161
|
+
}
|
|
162
|
+
async function projectResourceDepsEnabled(projectRoot) {
|
|
163
|
+
const root = projectRoot ?? findProjectRoot();
|
|
164
|
+
loadEnv(root);
|
|
165
|
+
const appName = getAppName(root);
|
|
166
|
+
const api = createApiClient(void 0, void 0, appName);
|
|
167
|
+
return projectPermissionsModeEnabled(api);
|
|
168
|
+
}
|
|
169
|
+
async function syncDeps(projectRoot, options = {}) {
|
|
170
|
+
const root = projectRoot ?? findProjectRoot();
|
|
171
|
+
loadEnv(root);
|
|
172
|
+
const appName = getAppName(root);
|
|
173
|
+
const api = createApiClient(void 0, void 0, appName);
|
|
174
|
+
const flagOn = await projectPermissionsModeEnabled(api);
|
|
175
|
+
const quiet = options.quiet === true;
|
|
176
|
+
const write = options.write === true;
|
|
177
|
+
if (flagOn) {
|
|
178
|
+
return syncResourceShareDeps(root, api, appName, {
|
|
179
|
+
write,
|
|
180
|
+
quiet,
|
|
181
|
+
ignorePermissionDenied: options.ignorePermissionDenied === true
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
if (options.legacyWhenDisabled === false) {
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
return syncLegacyDeps(root, api, quiet);
|
|
188
|
+
}
|
|
189
|
+
async function deps(args) {
|
|
190
|
+
const sub = args[0];
|
|
191
|
+
const projectRoot = findProjectRoot();
|
|
192
|
+
loadEnv(projectRoot);
|
|
193
|
+
let cachedFlagOn;
|
|
194
|
+
async function flagOn() {
|
|
195
|
+
if (cachedFlagOn !== void 0) return cachedFlagOn;
|
|
196
|
+
cachedFlagOn = await projectResourceDepsEnabled(projectRoot);
|
|
197
|
+
return cachedFlagOn;
|
|
198
|
+
}
|
|
199
|
+
if (sub === "sync") {
|
|
200
|
+
const enabled = await flagOn();
|
|
201
|
+
console.log();
|
|
202
|
+
console.log(pc.cyan(pc.bold(" Deps Sync")));
|
|
203
|
+
console.log(pc.dim(enabled ? " Syncing incoming resource shares into platform/collections/shared..." : " Verifying legacy cross-project dependencies..."));
|
|
204
|
+
console.log();
|
|
205
|
+
const ok = await syncDeps(projectRoot, { write: enabled });
|
|
206
|
+
if (!ok) {
|
|
207
|
+
console.log();
|
|
208
|
+
console.log(pc.red(enabled ? " Some resource-share dependencies could not be synced." : " Some dependencies could not be resolved."));
|
|
209
|
+
process.exit(1);
|
|
210
|
+
}
|
|
211
|
+
console.log();
|
|
212
|
+
console.log(pc.green(enabled ? " Resource shares synced." : " All dependencies verified."));
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (sub === "list") {
|
|
216
|
+
if (await flagOn()) {
|
|
217
|
+
const sharedDeps = loadSharedCollectionDeps(projectRoot);
|
|
218
|
+
if (sharedDeps.length === 0) {
|
|
219
|
+
console.log(pc.dim(" No shared collection declarations in platform/collections/shared"));
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
console.log();
|
|
223
|
+
console.log(pc.cyan(pc.bold(" Shared Collection Dependencies")));
|
|
224
|
+
console.log();
|
|
225
|
+
for (const { file, dep } of sharedDeps) {
|
|
226
|
+
const source = dep.source_project ?? "unknown-source";
|
|
227
|
+
const collection = dep.collection ?? "unknown-table";
|
|
228
|
+
const privilege = dep.privilege ?? "collection.read";
|
|
229
|
+
console.log(` ${pc.bold(file)}`);
|
|
230
|
+
console.log(` ${source}/${collection} ${pc.dim(`(${privilege})`)}`);
|
|
231
|
+
}
|
|
232
|
+
console.log();
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
const depsData = loadLegacyDeps(projectRoot);
|
|
236
|
+
if (!depsData || Object.keys(depsData.dependencies).length === 0) {
|
|
237
|
+
console.log(pc.dim(" No dependencies declared in platform/project_deps.json"));
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
console.log();
|
|
241
|
+
console.log(pc.cyan(pc.bold(" Project Dependencies")));
|
|
242
|
+
console.log();
|
|
243
|
+
for (const [depProject, config] of Object.entries(depsData.dependencies)) {
|
|
244
|
+
console.log(` ${pc.bold(depProject)}`);
|
|
245
|
+
for (const col of config.collections) {
|
|
246
|
+
console.log(` \u2022 ${col}`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
console.log();
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if (sub === "init") {
|
|
253
|
+
if (await flagOn()) {
|
|
254
|
+
const dir = sharedCollectionsDir(projectRoot);
|
|
255
|
+
mkdirSync(dir, { recursive: true });
|
|
256
|
+
console.log(pc.green(" \u2713"), "Created platform/collections/shared/");
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const depsPath = join(projectRoot, LEGACY_DEPS_FILE);
|
|
260
|
+
if (existsSync(depsPath)) {
|
|
261
|
+
console.log(pc.yellow(" platform/project_deps.json already exists."));
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
const platformDir = join(projectRoot, "platform");
|
|
265
|
+
mkdirSync(platformDir, { recursive: true });
|
|
266
|
+
const initial = { dependencies: {} };
|
|
267
|
+
writeFileSync(depsPath, JSON.stringify(initial, null, 2) + "\n");
|
|
268
|
+
console.log(pc.green(" \u2713"), "Created platform/project_deps.json");
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
console.log(`
|
|
272
|
+
${pc.bold("lumera deps")} \u2014 manage cross-project dependencies
|
|
273
|
+
|
|
274
|
+
${pc.bold("Commands:")}
|
|
275
|
+
sync Sync dependencies. With project permissions on, writes incoming
|
|
276
|
+
resource shares to platform/collections/shared/*.json
|
|
277
|
+
list Show declared dependencies
|
|
278
|
+
init Create dependency storage for the current feature mode
|
|
279
|
+
|
|
280
|
+
${pc.bold("Project permissions mode:")} platform/collections/shared/*.json
|
|
281
|
+
${pc.bold("Legacy mode:")} platform/project_deps.json
|
|
282
|
+
`);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export {
|
|
286
|
+
projectResourceDepsEnabled,
|
|
287
|
+
syncDeps,
|
|
288
|
+
deps
|
|
289
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// src/lib/templates.ts
|
|
2
|
+
import { existsSync, readdirSync, readFileSync } from "fs";
|
|
3
|
+
import { dirname, join } from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
function getBundledTemplatesDir() {
|
|
6
|
+
if (process.env.LUMERA_TEMPLATES_DIR) {
|
|
7
|
+
return process.env.LUMERA_TEMPLATES_DIR;
|
|
8
|
+
}
|
|
9
|
+
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const candidates = [
|
|
11
|
+
// Built package: dist/*.js -> templates/
|
|
12
|
+
join(moduleDir, "..", "templates"),
|
|
13
|
+
// Source/dev execution: src/lib/*.ts -> templates/
|
|
14
|
+
join(moduleDir, "..", "..", "templates")
|
|
15
|
+
];
|
|
16
|
+
return candidates.find((candidate) => existsSync(candidate)) ?? candidates[0];
|
|
17
|
+
}
|
|
18
|
+
function parseTemplateRef(input) {
|
|
19
|
+
const atIndex = input.lastIndexOf("@");
|
|
20
|
+
if (atIndex > 0) {
|
|
21
|
+
return {
|
|
22
|
+
name: input.slice(0, atIndex),
|
|
23
|
+
ref: input.slice(atIndex + 1)
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
return { name: input, ref: null };
|
|
27
|
+
}
|
|
28
|
+
function listTemplates(baseDir) {
|
|
29
|
+
if (!existsSync(baseDir)) return [];
|
|
30
|
+
const templates = [];
|
|
31
|
+
for (const entry of readdirSync(baseDir, { withFileTypes: true })) {
|
|
32
|
+
if (!entry.isDirectory()) continue;
|
|
33
|
+
const metaPath = join(baseDir, entry.name, "template.json");
|
|
34
|
+
if (!existsSync(metaPath)) continue;
|
|
35
|
+
try {
|
|
36
|
+
const raw = JSON.parse(readFileSync(metaPath, "utf-8"));
|
|
37
|
+
if (!raw.name || !raw.title || !raw.description || !raw.category) {
|
|
38
|
+
console.warn(`Warning: Skipping template "${entry.name}" \u2014 template.json is missing required fields (name, title, description, category)`);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
templates.push(raw);
|
|
42
|
+
} catch {
|
|
43
|
+
console.warn(`Warning: Skipping template "${entry.name}" \u2014 invalid template.json`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return templates.sort((a, b) => {
|
|
47
|
+
if (a.name === "default") return -1;
|
|
48
|
+
if (b.name === "default") return 1;
|
|
49
|
+
return a.title.localeCompare(b.title);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
async function resolveTemplate(nameWithRef) {
|
|
53
|
+
const { name, ref } = parseTemplateRef(nameWithRef);
|
|
54
|
+
const templatesDir = getBundledTemplatesDir();
|
|
55
|
+
const available = listTemplates(templatesDir).map((t) => t.name).join(", ");
|
|
56
|
+
if (ref) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
`Template refs are no longer downloaded by the CLI. Use one of the bundled templates: ${available || "none"}`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
const dir = join(templatesDir, name);
|
|
62
|
+
if (!existsSync(dir) || !existsSync(join(dir, "template.json"))) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
`Template "${name}" not found. Available: ${available || "none"}
|
|
65
|
+
Templates location: ${templatesDir}`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
return dir;
|
|
69
|
+
}
|
|
70
|
+
async function listAllTemplates() {
|
|
71
|
+
return listTemplates(getBundledTemplatesDir());
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export {
|
|
75
|
+
resolveTemplate,
|
|
76
|
+
listAllTemplates
|
|
77
|
+
};
|
|
@@ -31,7 +31,7 @@ async function fetchSkillsList() {
|
|
|
31
31
|
if (!listRes.ok) {
|
|
32
32
|
throw new Error(`Failed to fetch skills list: ${listRes.status}`);
|
|
33
33
|
}
|
|
34
|
-
return listRes.json();
|
|
34
|
+
return await listRes.json();
|
|
35
35
|
}
|
|
36
36
|
async function fetchSkillContent(slug) {
|
|
37
37
|
const baseUrl = getBaseUrl();
|
|
@@ -137,59 +137,19 @@ function isPortInUse(port, host = "127.0.0.1") {
|
|
|
137
137
|
});
|
|
138
138
|
});
|
|
139
139
|
}
|
|
140
|
-
function commandAvailable(command) {
|
|
141
|
-
try {
|
|
142
|
-
execSync(`${command} --version`, { stdio: "ignore" });
|
|
143
|
-
return true;
|
|
144
|
-
} catch {
|
|
145
|
-
return false;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
function parsePackageManagerSpecifier(value) {
|
|
149
|
-
const raw = (value || "").trim();
|
|
150
|
-
if (!raw) return void 0;
|
|
151
|
-
const name = raw.split("@")[0];
|
|
152
|
-
return ["bun", "pnpm", "yarn", "npm"].includes(name) ? name : void 0;
|
|
153
|
-
}
|
|
154
|
-
function packageManagerFromPackageJson(projectRoot) {
|
|
155
|
-
try {
|
|
156
|
-
const pkg = JSON.parse(readFileSync(resolve(projectRoot, "package.json"), "utf-8"));
|
|
157
|
-
return parsePackageManagerSpecifier(pkg.packageManager);
|
|
158
|
-
} catch {
|
|
159
|
-
return void 0;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
function runnerForPackageManager(pm) {
|
|
163
|
-
if (pm === "pnpm") return ["pnpm", "exec"];
|
|
164
|
-
if (pm === "bun") return ["bunx"];
|
|
165
|
-
if (pm === "yarn") return ["yarn"];
|
|
166
|
-
return ["npx"];
|
|
167
|
-
}
|
|
168
140
|
function detectRunner(projectRoot) {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
return runnerForPackageManager(requested);
|
|
141
|
+
if (existsSync(resolve(projectRoot, "bun.lockb")) || existsSync(resolve(projectRoot, "bun.lock"))) {
|
|
142
|
+
return ["bunx"];
|
|
172
143
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
return runnerForPackageManager(declared);
|
|
144
|
+
if (existsSync(resolve(projectRoot, "pnpm-lock.yaml"))) {
|
|
145
|
+
return ["pnpm", "exec"];
|
|
176
146
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
{ pm: "npm", file: "package-lock.json" }
|
|
183
|
-
];
|
|
184
|
-
for (const { pm, file } of lockfileManagers) {
|
|
185
|
-
const runner = runnerForPackageManager(pm);
|
|
186
|
-
if (existsSync(resolve(projectRoot, file)) && commandAvailable(runner[0])) return runner;
|
|
187
|
-
}
|
|
188
|
-
for (const pm of ["pnpm", "bun", "yarn", "npm"]) {
|
|
189
|
-
const runner = runnerForPackageManager(pm);
|
|
190
|
-
if (commandAvailable(runner[0])) return runner;
|
|
147
|
+
try {
|
|
148
|
+
execSync("pnpm --version", { stdio: "ignore" });
|
|
149
|
+
return ["pnpm", "exec"];
|
|
150
|
+
} catch {
|
|
151
|
+
return ["bunx"];
|
|
191
152
|
}
|
|
192
|
-
return ["npx"];
|
|
193
153
|
}
|
|
194
154
|
function getParentOrigin(apiUrl) {
|
|
195
155
|
return apiUrl.replace(/\/api\/?$/, "").replace(/\/$/, "");
|
|
@@ -25,6 +25,19 @@ while (__pkgDir !== "/") {
|
|
|
25
25
|
}
|
|
26
26
|
var pkg = JSON.parse(readFileSync(resolve(__pkgDir, "package.json"), "utf-8"));
|
|
27
27
|
var CLI_USER_AGENT = `lumera-cli/${pkg.version}`;
|
|
28
|
+
var ApiError = class extends Error {
|
|
29
|
+
status;
|
|
30
|
+
body;
|
|
31
|
+
constructor(status, body) {
|
|
32
|
+
super(`API request failed: ${status} ${body}`);
|
|
33
|
+
this.name = "ApiError";
|
|
34
|
+
this.status = status;
|
|
35
|
+
this.body = body;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
function isApiErrorStatus(error, status) {
|
|
39
|
+
return error instanceof ApiError && error.status === status;
|
|
40
|
+
}
|
|
28
41
|
var ApiClient = class {
|
|
29
42
|
baseUrl;
|
|
30
43
|
token;
|
|
@@ -66,14 +79,21 @@ var ApiClient = class {
|
|
|
66
79
|
});
|
|
67
80
|
if (!response.ok) {
|
|
68
81
|
const text = await response.text();
|
|
69
|
-
throw new
|
|
82
|
+
throw new ApiError(response.status, text);
|
|
70
83
|
}
|
|
71
84
|
if (response.status === 204) {
|
|
72
85
|
return {};
|
|
73
86
|
}
|
|
74
|
-
return response.json();
|
|
87
|
+
return await response.json();
|
|
75
88
|
}
|
|
76
89
|
// Collections
|
|
90
|
+
async getMe() {
|
|
91
|
+
return this.request("/api/me");
|
|
92
|
+
}
|
|
93
|
+
async isProjectPermissionsModeEnabled() {
|
|
94
|
+
const me = await this.getMe();
|
|
95
|
+
return me.user?.feature_flags?.project_permissions_mode === true;
|
|
96
|
+
}
|
|
77
97
|
async listCollections() {
|
|
78
98
|
const result = await this.request("/api/pb/collections");
|
|
79
99
|
return result.items;
|
|
@@ -302,7 +322,13 @@ var ApiClient = class {
|
|
|
302
322
|
updated: rec.updated
|
|
303
323
|
};
|
|
304
324
|
}
|
|
305
|
-
|
|
325
|
+
async listProjectResourceShares(projectExternalId, direction = "all") {
|
|
326
|
+
const qs = direction && direction !== "all" ? `?direction=${encodeURIComponent(direction)}` : "";
|
|
327
|
+
return this.request(
|
|
328
|
+
`/api/projects/${encodeURIComponent(projectExternalId)}/resource-shares${qs}`
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
// Project manifest — legacy collection list for project_deps.json.
|
|
306
332
|
async getProjectManifest(externalId) {
|
|
307
333
|
return this.request(`/api/pb/projects/${encodeURIComponent(externalId)}/manifest`);
|
|
308
334
|
}
|
|
@@ -312,5 +338,6 @@ function createApiClient(token, baseUrl, projectExternalId) {
|
|
|
312
338
|
}
|
|
313
339
|
|
|
314
340
|
export {
|
|
341
|
+
isApiErrorStatus,
|
|
315
342
|
createApiClient
|
|
316
343
|
};
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
deps,
|
|
3
|
+
projectResourceDepsEnabled,
|
|
3
4
|
syncDeps
|
|
4
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-5T22627H.js";
|
|
5
6
|
import "./chunk-2CR762KB.js";
|
|
6
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-UH5X43VV.js";
|
|
7
8
|
import "./chunk-ZH3NVYEQ.js";
|
|
8
9
|
import "./chunk-FJFIWC7G.js";
|
|
9
10
|
import "./chunk-PNKVD2UK.js";
|
|
10
11
|
export {
|
|
11
12
|
deps,
|
|
13
|
+
projectResourceDepsEnabled,
|
|
12
14
|
syncDeps
|
|
13
15
|
};
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
dev
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-SU26C4GL.js";
|
|
4
|
+
import {
|
|
5
|
+
projectResourceDepsEnabled,
|
|
6
|
+
syncDeps
|
|
7
|
+
} from "./chunk-5T22627H.js";
|
|
4
8
|
import {
|
|
5
9
|
loadEnv
|
|
6
10
|
} from "./chunk-2CR762KB.js";
|
|
7
11
|
import {
|
|
8
12
|
createApiClient
|
|
9
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-UH5X43VV.js";
|
|
10
14
|
import {
|
|
11
15
|
findProjectRoot,
|
|
12
16
|
getApiUrl,
|
|
@@ -121,6 +125,14 @@ async function dev2(args) {
|
|
|
121
125
|
const appName = getAppName(projectRoot);
|
|
122
126
|
const appTitle = getAppTitle(projectRoot);
|
|
123
127
|
const apiUrl = getApiUrl();
|
|
128
|
+
if (await projectResourceDepsEnabled(projectRoot)) {
|
|
129
|
+
await syncDeps(projectRoot, {
|
|
130
|
+
write: true,
|
|
131
|
+
quiet: true,
|
|
132
|
+
legacyWhenDisabled: false,
|
|
133
|
+
ignorePermissionDenied: true
|
|
134
|
+
});
|
|
135
|
+
}
|
|
124
136
|
if (!flags["skip-setup"]) {
|
|
125
137
|
const fresh = await isFreshProject(projectRoot);
|
|
126
138
|
if (fresh) {
|
package/dist/index.js
CHANGED
|
@@ -219,42 +219,42 @@ async function main() {
|
|
|
219
219
|
switch (command) {
|
|
220
220
|
// Resource commands
|
|
221
221
|
case "plan":
|
|
222
|
-
await import("./resources-
|
|
222
|
+
await import("./resources-RHF2MDF7.js").then((m) => m.plan(args.slice(1)));
|
|
223
223
|
break;
|
|
224
224
|
case "apply":
|
|
225
|
-
await import("./resources-
|
|
225
|
+
await import("./resources-RHF2MDF7.js").then((m) => m.apply(args.slice(1)));
|
|
226
226
|
break;
|
|
227
227
|
case "pull":
|
|
228
|
-
await import("./resources-
|
|
228
|
+
await import("./resources-RHF2MDF7.js").then((m) => m.pull(args.slice(1)));
|
|
229
229
|
break;
|
|
230
230
|
case "destroy":
|
|
231
|
-
await import("./resources-
|
|
231
|
+
await import("./resources-RHF2MDF7.js").then((m) => m.destroy(args.slice(1)));
|
|
232
232
|
break;
|
|
233
233
|
case "list":
|
|
234
|
-
await import("./resources-
|
|
234
|
+
await import("./resources-RHF2MDF7.js").then((m) => m.list(args.slice(1)));
|
|
235
235
|
break;
|
|
236
236
|
case "show":
|
|
237
|
-
await import("./resources-
|
|
237
|
+
await import("./resources-RHF2MDF7.js").then((m) => m.show(args.slice(1)));
|
|
238
238
|
break;
|
|
239
239
|
case "diff":
|
|
240
|
-
await import("./resources-
|
|
240
|
+
await import("./resources-RHF2MDF7.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-JLSTLIMZ.js").then((m) => m.dev(args.slice(1)));
|
|
245
245
|
break;
|
|
246
246
|
case "run":
|
|
247
|
-
await import("./run-
|
|
247
|
+
await import("./run-XJLB4AFD.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-CBZAIERZ.js").then((m) => m.init(args.slice(1)));
|
|
252
252
|
break;
|
|
253
253
|
case "register":
|
|
254
|
-
await import("./register-
|
|
254
|
+
await import("./register-N2LF3CCK.js").then((m) => m.register(args.slice(1)));
|
|
255
255
|
break;
|
|
256
256
|
case "templates":
|
|
257
|
-
await import("./templates-
|
|
257
|
+
await import("./templates-LNUOTNLN.js").then((m) => m.templates(subcommand, args.slice(2)));
|
|
258
258
|
break;
|
|
259
259
|
case "status":
|
|
260
260
|
await import("./status-X7WQYX2L.js").then((m) => m.status(args.slice(1)));
|
|
@@ -264,11 +264,11 @@ async function main() {
|
|
|
264
264
|
break;
|
|
265
265
|
// Skills
|
|
266
266
|
case "skills":
|
|
267
|
-
await import("./skills-
|
|
267
|
+
await import("./skills-REOKLNEF.js").then((m) => m.skills(subcommand, args.slice(2)));
|
|
268
268
|
break;
|
|
269
269
|
// Dependencies
|
|
270
270
|
case "deps":
|
|
271
|
-
await import("./deps-
|
|
271
|
+
await import("./deps-GCOH3TXL.js").then((m) => m.deps(args.slice(1)));
|
|
272
272
|
break;
|
|
273
273
|
// Auth
|
|
274
274
|
case "login":
|