@lumerahq/cli 0.18.6 → 0.18.8

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.
@@ -4,6 +4,9 @@ import {
4
4
  getTokenSource,
5
5
  init_auth
6
6
  } from "./chunk-ZH3NVYEQ.js";
7
+ import {
8
+ fetchWithRetry
9
+ } from "./chunk-FJFIWC7G.js";
7
10
  import "./chunk-PNKVD2UK.js";
8
11
 
9
12
  // src/commands/auth.ts
@@ -47,7 +50,7 @@ async function findAvailablePort(startPort) {
47
50
  async function fetchUserInfo(token) {
48
51
  const baseUrl = getBaseUrl();
49
52
  try {
50
- const response = await fetch(`${baseUrl}/api/me`, {
53
+ const response = await fetchWithRetry(`${baseUrl}/api/me`, {
51
54
  headers: {
52
55
  Authorization: `Bearer ${token}`
53
56
  }
@@ -1,3 +1,7 @@
1
+ import {
2
+ fetchWithRetry
3
+ } from "./chunk-FJFIWC7G.js";
4
+
1
5
  // src/lib/deploy.ts
2
6
  import { execSync, spawn } from "child_process";
3
7
  import { createConnection } from "net";
@@ -47,7 +51,7 @@ async function createTarball(distDir) {
47
51
  }
48
52
  async function ensureAppRecord(apiBase, token, appName, appTitle) {
49
53
  const filterParam = encodeURIComponent(JSON.stringify({ external_id: appName }));
50
- const searchRes = await fetch(
54
+ const searchRes = await fetchWithRetry(
51
55
  `${apiBase}/pb/collections/lm_custom_apps/records?filter=${filterParam}`,
52
56
  { headers: { Authorization: `Bearer ${token}` } }
53
57
  );
@@ -1,25 +1,31 @@
1
1
  import {
2
2
  getBaseUrl
3
3
  } from "./chunk-ZH3NVYEQ.js";
4
+ import {
5
+ fetchWithRetry
6
+ } from "./chunk-FJFIWC7G.js";
4
7
 
5
8
  // src/lib/skills.ts
6
9
  import { createHash } from "crypto";
7
- import { existsSync, mkdirSync, readdirSync, readFileSync, symlinkSync, lstatSync, writeFileSync } from "fs";
10
+ import { existsSync, mkdirSync, readdirSync, readFileSync, symlinkSync, lstatSync, statSync, writeFileSync } from "fs";
8
11
  import { join, relative } from "path";
9
12
  import pc from "picocolors";
13
+ function slugToDirName(slug) {
14
+ return slug.replace(/-/g, "_");
15
+ }
16
+ function dirNameToSlug(dirName) {
17
+ return dirName.replace(/_/g, "-");
18
+ }
10
19
  function slugToFilename(slug) {
11
20
  return `${slug.replace(/-/g, "_")}.md`;
12
21
  }
13
- function filenameToSlug(filename) {
14
- return filename.replace(/\.md$/, "").replace(/_/g, "-");
15
- }
16
22
  function hashContent(content) {
17
23
  return createHash("md5").update(content).digest("hex");
18
24
  }
19
25
  async function fetchSkillsList() {
20
26
  const baseUrl = getBaseUrl();
21
27
  const skillsApiUrl = `${baseUrl}/api/public/skills`;
22
- const listRes = await fetch(skillsApiUrl);
28
+ const listRes = await fetchWithRetry(skillsApiUrl);
23
29
  if (!listRes.ok) {
24
30
  throw new Error(`Failed to fetch skills list: ${listRes.status}`);
25
31
  }
@@ -28,7 +34,7 @@ async function fetchSkillsList() {
28
34
  async function fetchSkillContent(slug) {
29
35
  const baseUrl = getBaseUrl();
30
36
  const skillsApiUrl = `${baseUrl}/api/public/skills`;
31
- const mdRes = await fetch(`${skillsApiUrl}/${slug}.md`);
37
+ const mdRes = await fetchWithRetry(`${skillsApiUrl}/${slug}.md`);
32
38
  if (!mdRes.ok) {
33
39
  return null;
34
40
  }
@@ -39,11 +45,26 @@ function getLocalSkills(skillsDir) {
39
45
  if (!existsSync(skillsDir)) {
40
46
  return localSkills;
41
47
  }
42
- for (const file of readdirSync(skillsDir)) {
43
- if (file.endsWith(".md")) {
44
- const content = readFileSync(join(skillsDir, file), "utf-8");
45
- const slug = filenameToSlug(file);
46
- localSkills.set(slug, hashContent(content));
48
+ for (const entry of readdirSync(skillsDir)) {
49
+ const fullPath = join(skillsDir, entry);
50
+ const skillFile = join(fullPath, "SKILL.md");
51
+ if (existsSync(skillFile)) {
52
+ try {
53
+ if (statSync(fullPath).isDirectory()) {
54
+ const content = readFileSync(skillFile, "utf-8");
55
+ const slug = dirNameToSlug(entry);
56
+ localSkills.set(slug, hashContent(content));
57
+ continue;
58
+ }
59
+ } catch {
60
+ }
61
+ }
62
+ if (entry.endsWith(".md")) {
63
+ const content = readFileSync(fullPath, "utf-8");
64
+ const slug = entry.replace(/\.md$/, "").replace(/_/g, "-");
65
+ if (!localSkills.has(slug)) {
66
+ localSkills.set(slug, hashContent(content));
67
+ }
47
68
  }
48
69
  }
49
70
  return localSkills;
@@ -115,10 +136,12 @@ async function installAllSkills(targetDir, options) {
115
136
  for (const result of results) {
116
137
  if (result.status === "fulfilled" && result.value.content) {
117
138
  const { skill, content } = result.value;
118
- const filename = slugToFilename(skill.slug);
119
- writeFileSync(join(skillsDir, filename), content);
139
+ const dirName = slugToDirName(skill.slug);
140
+ const skillDir = join(skillsDir, dirName);
141
+ mkdirSync(skillDir, { recursive: true });
142
+ writeFileSync(join(skillDir, "SKILL.md"), content);
120
143
  if (verbose) {
121
- console.log(pc.green(" \u2713"), pc.dim(filename));
144
+ console.log(pc.green(" \u2713"), pc.dim(`${dirName}/SKILL.md`));
122
145
  }
123
146
  installed++;
124
147
  } else {
@@ -149,12 +172,27 @@ function syncClaudeMd(projectRoot) {
149
172
  }
150
173
  const skillEntries = [];
151
174
  if (existsSync(skillsDir)) {
152
- for (const file of readdirSync(skillsDir).sort()) {
153
- if (!file.endsWith(".md")) continue;
154
- const content = readFileSync(join(skillsDir, file), "utf-8");
155
- const { title, summary } = parseSkillSummary(content);
156
- const slug = filenameToSlug(file);
157
- skillEntries.push({ slug, title, summary });
175
+ for (const entry of readdirSync(skillsDir).sort()) {
176
+ const fullPath = join(skillsDir, entry);
177
+ const skillFile = join(fullPath, "SKILL.md");
178
+ if (existsSync(skillFile)) {
179
+ try {
180
+ if (statSync(fullPath).isDirectory()) {
181
+ const content = readFileSync(skillFile, "utf-8");
182
+ const { title, summary } = parseSkillSummary(content);
183
+ const slug = dirNameToSlug(entry);
184
+ skillEntries.push({ slug, title, summary });
185
+ continue;
186
+ }
187
+ } catch {
188
+ }
189
+ }
190
+ if (entry.endsWith(".md")) {
191
+ const content = readFileSync(fullPath, "utf-8");
192
+ const { title, summary } = parseSkillSummary(content);
193
+ const slug = entry.replace(/\.md$/, "").replace(/_/g, "-");
194
+ skillEntries.push({ slug, title, summary });
195
+ }
158
196
  }
159
197
  }
160
198
  let generated;
@@ -172,6 +210,7 @@ ${after}`;
172
210
  }
173
211
 
174
212
  export {
213
+ slugToDirName,
175
214
  slugToFilename,
176
215
  hashContent,
177
216
  fetchSkillsList,
@@ -0,0 +1,76 @@
1
+ // src/lib/http.ts
2
+ var DEFAULT_RETRY_STATUSES = /* @__PURE__ */ new Set([408, 425, 429, 500, 502, 503, 504]);
3
+ var DEFAULT_RETRY_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD"]);
4
+ function requestMethod(input, init) {
5
+ const method = init?.method || (input instanceof Request ? input.method : "GET");
6
+ return method.toUpperCase();
7
+ }
8
+ function shouldRetryMethod(method, retryMethods) {
9
+ const allowed = new Set((retryMethods || Array.from(DEFAULT_RETRY_METHODS)).map((value) => value.toUpperCase()));
10
+ return allowed.has(method);
11
+ }
12
+ function shouldRetryStatus(status, retryStatuses) {
13
+ const allowed = new Set(retryStatuses || Array.from(DEFAULT_RETRY_STATUSES));
14
+ return allowed.has(status);
15
+ }
16
+ function isAbortError(err) {
17
+ return err instanceof Error && err.name === "AbortError";
18
+ }
19
+ function cloneInput(input) {
20
+ return input instanceof Request ? input.clone() : input;
21
+ }
22
+ function parseRetryAfter(value) {
23
+ if (!value) return null;
24
+ const seconds = Number(value);
25
+ if (!Number.isNaN(seconds)) {
26
+ return Math.max(0, seconds * 1e3);
27
+ }
28
+ const when = Date.parse(value);
29
+ if (!Number.isNaN(when)) {
30
+ return Math.max(0, when - Date.now());
31
+ }
32
+ return null;
33
+ }
34
+ function retryDelayMs(attempt, retryAfter) {
35
+ const parsed = parseRetryAfter(retryAfter);
36
+ if (parsed !== null) return Math.min(parsed, 3e4);
37
+ return Math.min(attempt * 1e3, 5e3);
38
+ }
39
+ async function waitForRetry(delayMs) {
40
+ if (delayMs <= 0) return;
41
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
42
+ }
43
+ async function discardResponse(response) {
44
+ try {
45
+ await response.body?.cancel();
46
+ } catch {
47
+ }
48
+ }
49
+ async function fetchWithRetry(input, init = {}, options = {}) {
50
+ const attempts = Math.max(1, options.attempts ?? 3);
51
+ const method = requestMethod(input, init);
52
+ const retryableMethod = shouldRetryMethod(method, options.retryMethods);
53
+ let lastError;
54
+ for (let attempt = 1; attempt <= attempts; attempt++) {
55
+ try {
56
+ const response = await fetch(cloneInput(input), init);
57
+ if (!retryableMethod || !shouldRetryStatus(response.status, options.retryStatuses) || attempt === attempts) {
58
+ return response;
59
+ }
60
+ await discardResponse(response);
61
+ await waitForRetry(retryDelayMs(attempt, response.headers.get("Retry-After")));
62
+ continue;
63
+ } catch (err) {
64
+ lastError = err;
65
+ if (!retryableMethod || isAbortError(err) || attempt === attempts) {
66
+ throw err;
67
+ }
68
+ await waitForRetry(retryDelayMs(attempt, null));
69
+ }
70
+ }
71
+ throw lastError instanceof Error ? lastError : new Error("Request failed");
72
+ }
73
+
74
+ export {
75
+ fetchWithRetry
76
+ };
@@ -1,3 +1,7 @@
1
+ import {
2
+ fetchWithRetry
3
+ } from "./chunk-FJFIWC7G.js";
4
+
1
5
  // src/lib/templates.ts
2
6
  import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, unlinkSync, writeFileSync } from "fs";
3
7
  import { homedir, tmpdir } from "os";
@@ -20,7 +24,7 @@ function readCacheMeta() {
20
24
  }
21
25
  async function fetchLatestSha() {
22
26
  const url = `https://api.github.com/repos/${GITHUB_OWNER}/${GITHUB_REPO}/commits/${GITHUB_REF}`;
23
- const res = await fetch(url, {
27
+ const res = await fetchWithRetry(url, {
24
28
  headers: { Accept: "application/vnd.github.sha" }
25
29
  });
26
30
  if (!res.ok) throw new Error(`GitHub API error: ${res.status}`);
@@ -29,7 +33,7 @@ async function fetchLatestSha() {
29
33
  async function downloadAndExtract(commitSha) {
30
34
  const cacheDir = getCacheDir();
31
35
  const tarballUrl = `https://github.com/${GITHUB_OWNER}/${GITHUB_REPO}/archive/refs/heads/${GITHUB_REF}.tar.gz`;
32
- const res = await fetch(tarballUrl);
36
+ const res = await fetchWithRetry(tarballUrl);
33
37
  if (!res.ok) throw new Error(`Failed to download templates: ${res.status}`);
34
38
  const tmpFile = join(tmpdir(), `lumera-templates-${Date.now()}.tar.gz`);
35
39
  const buffer = Buffer.from(await res.arrayBuffer());
@@ -79,7 +83,7 @@ async function ensureRemoteCacheForRef(ref) {
79
83
  ];
80
84
  let res = null;
81
85
  for (const url of urls) {
82
- const attempt = await fetch(url);
86
+ const attempt = await fetchWithRetry(url);
83
87
  if (attempt.ok) {
84
88
  res = attempt;
85
89
  break;
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-2CR762KB.js";
4
4
  import {
5
5
  createApiClient
6
- } from "./chunk-FX257H2D.js";
6
+ } from "./chunk-VWC6RXDA.js";
7
7
  import {
8
8
  findProjectRoot,
9
9
  getAppName
@@ -2,6 +2,9 @@ import {
2
2
  getToken,
3
3
  init_auth
4
4
  } from "./chunk-ZH3NVYEQ.js";
5
+ import {
6
+ fetchWithRetry
7
+ } from "./chunk-FJFIWC7G.js";
5
8
 
6
9
  // src/lib/api.ts
7
10
  init_auth();
@@ -44,9 +47,13 @@ var ApiClient = class {
44
47
  ...this.projectExternalId ? { "X-Lumera-Project": this.projectExternalId } : {},
45
48
  ...options.headers
46
49
  };
47
- const response = await fetch(url, {
50
+ const method = (options.method || "GET").toUpperCase();
51
+ const response = await fetchWithRetry(url, {
48
52
  ...options,
49
53
  headers
54
+ }, {
55
+ retryMethods: ["GET", "HEAD"],
56
+ attempts: method === "GET" || method === "HEAD" ? 3 : 1
50
57
  });
51
58
  if (!response.ok) {
52
59
  const text = await response.text();
@@ -81,10 +88,18 @@ var ApiClient = class {
81
88
  const qs = new URLSearchParams();
82
89
  if (params?.external_id) qs.set("external_id", params.external_id);
83
90
  if (params?.include_code) qs.set("include_code", "true");
84
- const query = qs.toString();
85
- const path = `/api/automations${query ? `?${query}` : ""}`;
86
- const result = await this.request(path);
87
- return result.automations || [];
91
+ qs.set("limit", "100");
92
+ const all = [];
93
+ let offset = 0;
94
+ for (; ; ) {
95
+ qs.set("offset", String(offset));
96
+ const result = await this.request(`/api/automations?${qs}`);
97
+ const items = result.automations || [];
98
+ all.push(...items);
99
+ if (!result.has_more || items.length === 0) break;
100
+ offset += items.length;
101
+ }
102
+ return all;
88
103
  }
89
104
  async getAutomation(id) {
90
105
  return this.request(`/api/automations/${id}`);
@@ -206,30 +221,43 @@ var ApiClient = class {
206
221
  // Projects — register/lookup via PocketBase records API
207
222
  async registerProject(name, externalId) {
208
223
  const extId = externalId || name;
209
- const filterParam = encodeURIComponent(JSON.stringify({ name }));
210
- const searchRes = await this.request(
211
- `/api/pb/collections/lm_projects/records?filter=${filterParam}`
212
- );
213
- const existing = searchRes.items?.[0];
224
+ const existing = await this.getProjectByExternalId(extId);
214
225
  if (existing) {
226
+ return existing;
227
+ }
228
+ try {
229
+ const rec = await this.request(
230
+ "/api/pb/collections/lm_projects/records",
231
+ {
232
+ method: "POST",
233
+ body: JSON.stringify({ name, external_id: extId })
234
+ }
235
+ );
215
236
  return {
216
- id: existing.id,
217
- name: existing.name,
218
- external_id: existing.external_id,
219
- template: existing.template,
220
- status: existing.status,
221
- owner_id: existing.owner_id,
222
- created: existing.created,
223
- updated: existing.updated
237
+ id: rec.id,
238
+ name: rec.name,
239
+ external_id: rec.external_id,
240
+ template: rec.template,
241
+ status: rec.status,
242
+ owner_id: rec.owner_id,
243
+ created: rec.created,
244
+ updated: rec.updated
224
245
  };
225
- }
226
- const rec = await this.request(
227
- "/api/pb/collections/lm_projects/records",
228
- {
229
- method: "POST",
230
- body: JSON.stringify({ name, external_id: extId })
246
+ } catch (error) {
247
+ const created = await this.getProjectByExternalId(extId);
248
+ if (created) {
249
+ return created;
231
250
  }
251
+ throw error;
252
+ }
253
+ }
254
+ async getProjectByExternalId(externalId) {
255
+ const filterParam = encodeURIComponent(JSON.stringify({ external_id: externalId }));
256
+ const res = await this.request(
257
+ `/api/pb/collections/lm_projects/records?filter=${filterParam}`
232
258
  );
259
+ const rec = res.items?.[0];
260
+ if (!rec) return null;
233
261
  return {
234
262
  id: rec.id,
235
263
  name: rec.name,
@@ -1,10 +1,11 @@
1
1
  import {
2
2
  deps,
3
3
  syncDeps
4
- } from "./chunk-NBEXK55Z.js";
4
+ } from "./chunk-P44XM3FB.js";
5
5
  import "./chunk-2CR762KB.js";
6
- import "./chunk-FX257H2D.js";
6
+ import "./chunk-VWC6RXDA.js";
7
7
  import "./chunk-ZH3NVYEQ.js";
8
+ import "./chunk-FJFIWC7G.js";
8
9
  import "./chunk-PNKVD2UK.js";
9
10
  export {
10
11
  deps,
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  dev
3
- } from "./chunk-EOLZMVP4.js";
3
+ } from "./chunk-5EBNGMIE.js";
4
4
  import {
5
5
  loadEnv
6
6
  } from "./chunk-2CR762KB.js";
7
7
  import {
8
8
  createApiClient
9
- } from "./chunk-FX257H2D.js";
9
+ } from "./chunk-VWC6RXDA.js";
10
10
  import {
11
11
  findProjectRoot,
12
12
  getApiUrl,
@@ -15,6 +15,7 @@ import {
15
15
  getToken,
16
16
  init_auth
17
17
  } from "./chunk-ZH3NVYEQ.js";
18
+ import "./chunk-FJFIWC7G.js";
18
19
  import "./chunk-PNKVD2UK.js";
19
20
 
20
21
  // src/commands/dev.ts
package/dist/index.js CHANGED
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ fetchWithRetry
4
+ } from "./chunk-FJFIWC7G.js";
2
5
  import "./chunk-PNKVD2UK.js";
3
6
 
4
7
  // src/index.ts
@@ -51,7 +54,7 @@ async function checkForUpdate(currentVersion) {
51
54
  return null;
52
55
  }
53
56
  }
54
- const res = await fetch("https://registry.npmjs.org/@lumerahq/cli/latest");
57
+ const res = await fetchWithRetry("https://registry.npmjs.org/@lumerahq/cli/latest");
55
58
  if (!res.ok) return null;
56
59
  const data = await res.json();
57
60
  const latest = data.version;
@@ -216,66 +219,66 @@ async function main() {
216
219
  switch (command) {
217
220
  // Resource commands
218
221
  case "plan":
219
- await import("./resources-BSEQZA6O.js").then((m) => m.plan(args.slice(1)));
222
+ await import("./resources-AMQH3SJW.js").then((m) => m.plan(args.slice(1)));
220
223
  break;
221
224
  case "apply":
222
- await import("./resources-BSEQZA6O.js").then((m) => m.apply(args.slice(1)));
225
+ await import("./resources-AMQH3SJW.js").then((m) => m.apply(args.slice(1)));
223
226
  break;
224
227
  case "pull":
225
- await import("./resources-BSEQZA6O.js").then((m) => m.pull(args.slice(1)));
228
+ await import("./resources-AMQH3SJW.js").then((m) => m.pull(args.slice(1)));
226
229
  break;
227
230
  case "destroy":
228
- await import("./resources-BSEQZA6O.js").then((m) => m.destroy(args.slice(1)));
231
+ await import("./resources-AMQH3SJW.js").then((m) => m.destroy(args.slice(1)));
229
232
  break;
230
233
  case "list":
231
- await import("./resources-BSEQZA6O.js").then((m) => m.list(args.slice(1)));
234
+ await import("./resources-AMQH3SJW.js").then((m) => m.list(args.slice(1)));
232
235
  break;
233
236
  case "show":
234
- await import("./resources-BSEQZA6O.js").then((m) => m.show(args.slice(1)));
237
+ await import("./resources-AMQH3SJW.js").then((m) => m.show(args.slice(1)));
235
238
  break;
236
239
  case "diff":
237
- await import("./resources-BSEQZA6O.js").then((m) => m.diff(args.slice(1)));
240
+ await import("./resources-AMQH3SJW.js").then((m) => m.diff(args.slice(1)));
238
241
  break;
239
242
  // Development
240
243
  case "dev":
241
- await import("./dev-RJSD2D6X.js").then((m) => m.dev(args.slice(1)));
244
+ await import("./dev-VONB7YDG.js").then((m) => m.dev(args.slice(1)));
242
245
  break;
243
246
  case "run":
244
- await import("./run-GCGFVDSL.js").then((m) => m.run(args.slice(1)));
247
+ await import("./run-RT6ZCUWC.js").then((m) => m.run(args.slice(1)));
245
248
  break;
246
249
  // Project
247
250
  case "init":
248
- await import("./init-4YEIRRDO.js").then((m) => m.init(args.slice(1)));
251
+ await import("./init-ZKG24YZH.js").then((m) => m.init(args.slice(1)));
249
252
  break;
250
253
  case "register":
251
- await import("./register-MUONGCPF.js").then((m) => m.register(args.slice(1)));
254
+ await import("./register-FRH5EDMD.js").then((m) => m.register(args.slice(1)));
252
255
  break;
253
256
  case "templates":
254
- await import("./templates-ESFQ4QO4.js").then((m) => m.templates(subcommand, args.slice(2)));
257
+ await import("./templates-M3RDNDDY.js").then((m) => m.templates(subcommand, args.slice(2)));
255
258
  break;
256
259
  case "status":
257
- await import("./status-EW5I7QAU.js").then((m) => m.status(args.slice(1)));
260
+ await import("./status-LP73L4IV.js").then((m) => m.status(args.slice(1)));
258
261
  break;
259
262
  case "migrate":
260
263
  await import("./migrate-NCZKP7FM.js").then((m) => m.migrate(args.slice(1)));
261
264
  break;
262
265
  // Skills
263
266
  case "skills":
264
- await import("./skills-VY42VAXX.js").then((m) => m.skills(subcommand, args.slice(2)));
267
+ await import("./skills-YWYBMOVO.js").then((m) => m.skills(subcommand, args.slice(2)));
265
268
  break;
266
269
  // Dependencies
267
270
  case "deps":
268
- await import("./deps-XI64AAZY.js").then((m) => m.deps(args.slice(1)));
271
+ await import("./deps-AWWIUASS.js").then((m) => m.deps(args.slice(1)));
269
272
  break;
270
273
  // Auth
271
274
  case "login":
272
- await import("./auth-KFXSNCJB.js").then((m) => m.login(args.slice(1)));
275
+ await import("./auth-PDTSQN7X.js").then((m) => m.login(args.slice(1)));
273
276
  break;
274
277
  case "logout":
275
- await import("./auth-KFXSNCJB.js").then((m) => m.logout(args.slice(1)));
278
+ await import("./auth-PDTSQN7X.js").then((m) => m.logout(args.slice(1)));
276
279
  break;
277
280
  case "whoami":
278
- await import("./auth-KFXSNCJB.js").then((m) => m.whoami());
281
+ await import("./auth-PDTSQN7X.js").then((m) => m.whoami());
279
282
  break;
280
283
  // Convenience aliases
281
284
  case "help":
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  installAllSkills,
3
3
  syncClaudeMd
4
- } from "./chunk-W4XQ5TKC.js";
4
+ } from "./chunk-CCP66T22.js";
5
5
  import {
6
6
  spinner
7
7
  } from "./chunk-BHYDYR75.js";
8
8
  import {
9
9
  createApiClient
10
- } from "./chunk-FX257H2D.js";
10
+ } from "./chunk-VWC6RXDA.js";
11
11
  import {
12
12
  getToken,
13
13
  init_auth,
@@ -16,7 +16,8 @@ import {
16
16
  import {
17
17
  listAllTemplates,
18
18
  resolveTemplate
19
- } from "./chunk-CHRKCAIZ.js";
19
+ } from "./chunk-OQW5E7UT.js";
20
+ import "./chunk-FJFIWC7G.js";
20
21
  import "./chunk-PNKVD2UK.js";
21
22
 
22
23
  // src/commands/init.ts
@@ -6,13 +6,14 @@ import {
6
6
  } from "./chunk-BHYDYR75.js";
7
7
  import {
8
8
  createApiClient
9
- } from "./chunk-FX257H2D.js";
9
+ } from "./chunk-VWC6RXDA.js";
10
10
  import {
11
11
  findProjectRoot,
12
12
  getAppName,
13
13
  getProjectId,
14
14
  setProjectId
15
15
  } from "./chunk-ZH3NVYEQ.js";
16
+ import "./chunk-FJFIWC7G.js";
16
17
  import "./chunk-PNKVD2UK.js";
17
18
 
18
19
  // src/commands/register.ts
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  syncDeps
3
- } from "./chunk-NBEXK55Z.js";
3
+ } from "./chunk-P44XM3FB.js";
4
4
  import {
5
5
  deploy
6
- } from "./chunk-EOLZMVP4.js";
6
+ } from "./chunk-5EBNGMIE.js";
7
7
  import {
8
8
  loadEnv
9
9
  } from "./chunk-2CR762KB.js";
10
10
  import {
11
11
  createApiClient
12
- } from "./chunk-FX257H2D.js";
12
+ } from "./chunk-VWC6RXDA.js";
13
13
  import {
14
14
  findProjectRoot,
15
15
  getApiUrl,
@@ -19,6 +19,9 @@ import {
19
19
  getToken,
20
20
  init_auth
21
21
  } from "./chunk-ZH3NVYEQ.js";
22
+ import {
23
+ fetchWithRetry
24
+ } from "./chunk-FJFIWC7G.js";
22
25
  import "./chunk-PNKVD2UK.js";
23
26
 
24
27
  // src/commands/resources.ts
@@ -1868,7 +1871,7 @@ async function destroyApp(skipConfirm) {
1868
1871
  const appName = getAppName(projectRoot);
1869
1872
  let apiUrl = getApiUrl().replace(/\/+$/, "").replace(/\/api$/, "");
1870
1873
  const filterParam = encodeURIComponent(JSON.stringify({ external_id: appName }));
1871
- const searchRes = await fetch(
1874
+ const searchRes = await fetchWithRetry(
1872
1875
  `${apiUrl}/api/pb/collections/lm_custom_apps/records?filter=${filterParam}`,
1873
1876
  { headers: { Authorization: `Bearer ${token}` } }
1874
1877
  );
@@ -2059,7 +2062,7 @@ async function showResource(api, platformDir, resourceType, resourceName, appNam
2059
2062
  console.log();
2060
2063
  console.log(` External ID: ${appName2}`);
2061
2064
  const filterParam = encodeURIComponent(JSON.stringify({ external_id: appName2 }));
2062
- const searchRes = await fetch(
2065
+ const searchRes = await fetchWithRetry(
2063
2066
  `${apiUrl}/api/pb/collections/lm_custom_apps/records?filter=${filterParam}`,
2064
2067
  { headers: { Authorization: `Bearer ${token}` } }
2065
2068
  );
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-2CR762KB.js";
4
4
  import {
5
5
  createApiClient
6
- } from "./chunk-FX257H2D.js";
6
+ } from "./chunk-VWC6RXDA.js";
7
7
  import {
8
8
  findProjectRoot,
9
9
  getApiUrl,
@@ -11,10 +11,14 @@ import {
11
11
  getToken,
12
12
  init_auth
13
13
  } from "./chunk-ZH3NVYEQ.js";
14
+ import {
15
+ fetchWithRetry
16
+ } from "./chunk-FJFIWC7G.js";
14
17
  import "./chunk-PNKVD2UK.js";
15
18
 
16
19
  // src/commands/run.ts
17
20
  import pc from "picocolors";
21
+ import { randomUUID } from "crypto";
18
22
  import { spawn } from "child_process";
19
23
  import { existsSync, readdirSync, readFileSync } from "fs";
20
24
  import { resolve, extname, join } from "path";
@@ -159,17 +163,22 @@ async function triggerAutomation(automationName, projectRoot, flags) {
159
163
  }
160
164
  }
161
165
  console.log();
162
- const response = await fetch(`${apiUrl}/api/pb/collections/lm_automation_runs/records`, {
166
+ const externalId = `lumera-cli-run-${randomUUID()}`;
167
+ const response = await fetchWithRetry(`${apiUrl}/api/automation-runs`, {
163
168
  method: "POST",
164
169
  headers: {
165
170
  "Authorization": `Bearer ${token}`,
166
171
  "Content-Type": "application/json"
167
172
  },
168
173
  body: JSON.stringify({
169
- automation: automation.id,
170
- inputs: JSON.stringify(inputs),
171
- status: "pending"
174
+ automation_id: automation.id,
175
+ inputs,
176
+ trigger: "manual",
177
+ external_id: externalId
172
178
  })
179
+ }, {
180
+ attempts: 3,
181
+ retryMethods: ["POST"]
173
182
  });
174
183
  if (!response.ok) {
175
184
  const error = await response.text();
@@ -5,15 +5,17 @@ import {
5
5
  getLocalSkills,
6
6
  hashContent,
7
7
  installAllSkills,
8
+ slugToDirName,
8
9
  slugToFilename,
9
10
  syncClaudeMd
10
- } from "./chunk-W4XQ5TKC.js";
11
+ } from "./chunk-CCP66T22.js";
11
12
  import "./chunk-ZH3NVYEQ.js";
13
+ import "./chunk-FJFIWC7G.js";
12
14
  import "./chunk-PNKVD2UK.js";
13
15
 
14
16
  // src/commands/skills.ts
15
17
  import pc from "picocolors";
16
- import { existsSync, readdirSync, rmSync, writeFileSync } from "fs";
18
+ import { existsSync, mkdirSync, readdirSync, rmSync, writeFileSync } from "fs";
17
19
  import { join, resolve } from "path";
18
20
  function findProjectRoot() {
19
21
  let dir = process.cwd();
@@ -214,9 +216,12 @@ async function install(flags) {
214
216
  console.log(pc.dim(` Fetching skills...`));
215
217
  }
216
218
  if (force && existsSync(skillsDir)) {
217
- for (const file of readdirSync(skillsDir)) {
218
- if (file.endsWith(".md")) {
219
- rmSync(join(skillsDir, file));
219
+ for (const entry of readdirSync(skillsDir)) {
220
+ const fullPath = join(skillsDir, entry);
221
+ if (entry.endsWith(".md")) {
222
+ rmSync(fullPath);
223
+ } else {
224
+ rmSync(fullPath, { recursive: true, force: true });
220
225
  }
221
226
  }
222
227
  }
@@ -334,8 +339,14 @@ async function update(args, flags) {
334
339
  for (const result of fetchResults) {
335
340
  if (result.status !== "fulfilled" || !result.value.content) continue;
336
341
  const { skill, content } = result.value;
337
- const filename = slugToFilename(skill.slug);
338
- writeFileSync(join(skillsDir, filename), content);
342
+ const dirName = slugToDirName(skill.slug);
343
+ const skillDir = join(skillsDir, dirName);
344
+ mkdirSync(skillDir, { recursive: true });
345
+ writeFileSync(join(skillDir, "SKILL.md"), content);
346
+ const legacyFile = join(skillsDir, slugToFilename(skill.slug));
347
+ if (existsSync(legacyFile)) {
348
+ rmSync(legacyFile);
349
+ }
339
350
  const isNew = diff.added.includes(skill);
340
351
  if (isNew) {
341
352
  console.log(pc.green(" +"), `Added ${skill.name}`);
@@ -344,12 +355,17 @@ async function update(args, flags) {
344
355
  }
345
356
  }
346
357
  for (const slug of diff.removed) {
347
- const filename = slugToFilename(slug);
348
- const filepath = join(skillsDir, filename);
349
- if (existsSync(filepath)) {
350
- rmSync(filepath);
358
+ const dirName = slugToDirName(slug);
359
+ const dirPath = join(skillsDir, dirName);
360
+ if (existsSync(dirPath)) {
361
+ rmSync(dirPath, { recursive: true, force: true });
351
362
  console.log(pc.red(" -"), `Removed ${slug}`);
352
363
  }
364
+ const legacyFile = join(skillsDir, slugToFilename(slug));
365
+ if (existsSync(legacyFile)) {
366
+ rmSync(legacyFile);
367
+ console.log(pc.red(" -"), `Removed ${slug} (legacy)`);
368
+ }
353
369
  }
354
370
  console.log();
355
371
  console.log(pc.green(" \u2713"), `Update complete (${changes.join(", ")})`);
@@ -9,6 +9,9 @@ import {
9
9
  init_auth,
10
10
  readPackageJson
11
11
  } from "./chunk-ZH3NVYEQ.js";
12
+ import {
13
+ fetchWithRetry
14
+ } from "./chunk-FJFIWC7G.js";
12
15
  import "./chunk-PNKVD2UK.js";
13
16
 
14
17
  // src/commands/status.ts
@@ -19,7 +22,7 @@ init_auth();
19
22
  async function validateToken(token) {
20
23
  const baseUrl = getBaseUrl();
21
24
  try {
22
- const response = await fetch(`${baseUrl}/api/me`, {
25
+ const response = await fetchWithRetry(`${baseUrl}/api/me`, {
23
26
  headers: { Authorization: `Bearer ${token}` }
24
27
  });
25
28
  if (!response.ok) {
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  listAllTemplates
3
- } from "./chunk-CHRKCAIZ.js";
3
+ } from "./chunk-OQW5E7UT.js";
4
+ import "./chunk-FJFIWC7G.js";
4
5
  import "./chunk-PNKVD2UK.js";
5
6
 
6
7
  // src/commands/templates.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumerahq/cli",
3
- "version": "0.18.6",
3
+ "version": "0.18.8",
4
4
  "description": "CLI for building and deploying Lumera apps",
5
5
  "type": "module",
6
6
  "engines": {