@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.
Files changed (44) hide show
  1. package/README.md +5 -3
  2. package/dist/chunk-5T22627H.js +289 -0
  3. package/dist/chunk-H357NP7T.js +77 -0
  4. package/dist/{chunk-AUYOTENF.js → chunk-HU7RYLUF.js} +1 -1
  5. package/dist/{chunk-53NOF33P.js → chunk-SU26C4GL.js} +9 -49
  6. package/dist/{chunk-GKI2HQJC.js → chunk-UH5X43VV.js} +30 -3
  7. package/dist/{deps-ULTIIDYK.js → deps-GCOH3TXL.js} +4 -2
  8. package/dist/{dev-5JMHMS4U.js → dev-JLSTLIMZ.js} +14 -2
  9. package/dist/index.js +14 -14
  10. package/dist/{init-37XOMJLU.js → init-CBZAIERZ.js} +54 -24
  11. package/dist/{register-JJUMS4FI.js → register-N2LF3CCK.js} +1 -1
  12. package/dist/{resources-4IUZYKGX.js → resources-RHF2MDF7.js} +237 -159
  13. package/dist/{run-5ZOSPBGO.js → run-XJLB4AFD.js} +1 -1
  14. package/dist/{skills-TNJHMV4F.js → skills-REOKLNEF.js} +1 -1
  15. package/dist/{templates-M3RDNDDY.js → templates-LNUOTNLN.js} +2 -3
  16. package/package.json +1 -1
  17. package/templates/default/.agents/skills/.gitkeep +0 -0
  18. package/templates/default/AGENTS.md +151 -0
  19. package/templates/default/README.md +18 -0
  20. package/templates/default/_gitignore +14 -0
  21. package/templates/default/architecture.md +29 -0
  22. package/templates/default/biome.json +38 -0
  23. package/templates/default/components.json +21 -0
  24. package/templates/default/icon.svg +29 -0
  25. package/templates/default/index.html +16 -0
  26. package/templates/default/package.json +53 -0
  27. package/templates/default/platform/automations/.gitkeep +0 -0
  28. package/templates/default/platform/collections/.gitkeep +0 -0
  29. package/templates/default/platform/hooks/.gitkeep +0 -0
  30. package/templates/default/pyproject.toml +14 -0
  31. package/templates/default/scripts/.gitkeep +0 -0
  32. package/templates/default/src/components/layout.tsx +11 -0
  33. package/templates/default/src/lib/auth.ts +9 -0
  34. package/templates/default/src/lib/queries.ts +17 -0
  35. package/templates/default/src/lib/utils.ts +6 -0
  36. package/templates/default/src/main.tsx +130 -0
  37. package/templates/default/src/routes/__root.tsx +10 -0
  38. package/templates/default/src/routes/index.tsx +87 -0
  39. package/templates/default/src/styles.css +128 -0
  40. package/templates/default/template.json +7 -0
  41. package/templates/default/tsconfig.json +22 -0
  42. package/templates/default/vite.config.ts +28 -0
  43. package/dist/chunk-OQW5E7UT.js +0 -159
  44. 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 # Also runs pnpm 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` | `-i` | Install dependencies after scaffolding |
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 -i
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
- const requested = parsePackageManagerSpecifier(process.env.LUMERA_PACKAGE_MANAGER);
170
- if (requested && commandAvailable(runnerForPackageManager(requested)[0])) {
171
- return runnerForPackageManager(requested);
141
+ if (existsSync(resolve(projectRoot, "bun.lockb")) || existsSync(resolve(projectRoot, "bun.lock"))) {
142
+ return ["bunx"];
172
143
  }
173
- const declared = packageManagerFromPackageJson(projectRoot);
174
- if (declared && commandAvailable(runnerForPackageManager(declared)[0])) {
175
- return runnerForPackageManager(declared);
144
+ if (existsSync(resolve(projectRoot, "pnpm-lock.yaml"))) {
145
+ return ["pnpm", "exec"];
176
146
  }
177
- const lockfileManagers = [
178
- { pm: "pnpm", file: "pnpm-lock.yaml" },
179
- { pm: "bun", file: "bun.lockb" },
180
- { pm: "bun", file: "bun.lock" },
181
- { pm: "yarn", file: "yarn.lock" },
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 Error(`API request failed: ${response.status} ${text}`);
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
- // Project manifest list public collections for a project
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-XDTWVFPE.js";
5
+ } from "./chunk-5T22627H.js";
5
6
  import "./chunk-2CR762KB.js";
6
- import "./chunk-GKI2HQJC.js";
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-53NOF33P.js";
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-GKI2HQJC.js";
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-4IUZYKGX.js").then((m) => m.plan(args.slice(1)));
222
+ await import("./resources-RHF2MDF7.js").then((m) => m.plan(args.slice(1)));
223
223
  break;
224
224
  case "apply":
225
- await import("./resources-4IUZYKGX.js").then((m) => m.apply(args.slice(1)));
225
+ await import("./resources-RHF2MDF7.js").then((m) => m.apply(args.slice(1)));
226
226
  break;
227
227
  case "pull":
228
- await import("./resources-4IUZYKGX.js").then((m) => m.pull(args.slice(1)));
228
+ await import("./resources-RHF2MDF7.js").then((m) => m.pull(args.slice(1)));
229
229
  break;
230
230
  case "destroy":
231
- await import("./resources-4IUZYKGX.js").then((m) => m.destroy(args.slice(1)));
231
+ await import("./resources-RHF2MDF7.js").then((m) => m.destroy(args.slice(1)));
232
232
  break;
233
233
  case "list":
234
- await import("./resources-4IUZYKGX.js").then((m) => m.list(args.slice(1)));
234
+ await import("./resources-RHF2MDF7.js").then((m) => m.list(args.slice(1)));
235
235
  break;
236
236
  case "show":
237
- await import("./resources-4IUZYKGX.js").then((m) => m.show(args.slice(1)));
237
+ await import("./resources-RHF2MDF7.js").then((m) => m.show(args.slice(1)));
238
238
  break;
239
239
  case "diff":
240
- await import("./resources-4IUZYKGX.js").then((m) => m.diff(args.slice(1)));
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-5JMHMS4U.js").then((m) => m.dev(args.slice(1)));
244
+ await import("./dev-JLSTLIMZ.js").then((m) => m.dev(args.slice(1)));
245
245
  break;
246
246
  case "run":
247
- await import("./run-5ZOSPBGO.js").then((m) => m.run(args.slice(1)));
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-37XOMJLU.js").then((m) => m.init(args.slice(1)));
251
+ await import("./init-CBZAIERZ.js").then((m) => m.init(args.slice(1)));
252
252
  break;
253
253
  case "register":
254
- await import("./register-JJUMS4FI.js").then((m) => m.register(args.slice(1)));
254
+ await import("./register-N2LF3CCK.js").then((m) => m.register(args.slice(1)));
255
255
  break;
256
256
  case "templates":
257
- await import("./templates-M3RDNDDY.js").then((m) => m.templates(subcommand, args.slice(2)));
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-TNJHMV4F.js").then((m) => m.skills(subcommand, args.slice(2)));
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-ULTIIDYK.js").then((m) => m.deps(args.slice(1)));
271
+ await import("./deps-GCOH3TXL.js").then((m) => m.deps(args.slice(1)));
272
272
  break;
273
273
  // Auth
274
274
  case "login":