@lotics/cli 0.40.0 → 0.41.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.
@@ -92,10 +92,6 @@ function readAppMeta(projectDir) {
92
92
  version_number: pkg.lotics.version_number ?? null,
93
93
  workflows: pkg.lotics.workflows ?? {},
94
94
  queries: pkg.lotics.queries ?? {},
95
- // Left as `undefined` when absent so the deploy omits them and the
96
- // App row's icon/theme is preserved.
97
- icon: pkg.lotics.icon,
98
- theme: pkg.lotics.theme,
99
95
  capabilities: pkg.lotics.capabilities,
100
96
  };
101
97
  }
@@ -346,15 +342,10 @@ export async function appDeploy(client, args) {
346
342
  // Sync apps.queries from the manifest. Server validates each query
347
343
  // template (parseQueryNode, table access, param coverage).
348
344
  queries: meta.queries ?? {},
349
- // Optional icon/theme overrides only sent when the manifest
350
- // declares them (undefined omitted App row left untouched).
351
- icon: meta.icon,
352
- theme: meta.theme,
353
- // Capabilities are manifest-authoritative (like workflows/queries, not
354
- // like icon/theme): always send, defaulting to `{}` when the manifest
355
- // declares none — so deleting the `capabilities` block turns every
356
- // capability OFF on the next deploy (fail-safe; the declaration is the
357
- // grant). The manifest is the only writer of capabilities.
345
+ // Capabilities are manifest-authoritative (like workflows/queries):
346
+ // always send, defaulting to `{}` when the manifest declares none — so
347
+ // deleting the `capabilities` block turns every capability OFF on the
348
+ // next deploy (fail-safe; the declaration is the grant).
358
349
  capabilities: meta.capabilities ?? {},
359
350
  // Opt into destructive workflow removal. Default false — the server
360
351
  // rejects deploys whose manifest is missing aliases the App row has.
@@ -367,6 +358,7 @@ export async function appDeploy(client, args) {
367
358
  });
368
359
  console.error(`Deployed v${result.version_number} (${result.version_id})`);
369
360
  console.error(`Bundle size: ${(result.bundle_size_bytes / 1024).toFixed(1)} KB`);
361
+ await warnIfUnbranded(client, meta.app_id);
370
362
  }
371
363
  catch (err) {
372
364
  const e = err;
@@ -385,6 +377,30 @@ export async function appDeploy(client, args) {
385
377
  fs.unlinkSync(tmpDist);
386
378
  }
387
379
  }
380
+ /**
381
+ * Non-blocking nudge after a successful deploy: an app with no icon/color shows a
382
+ * generic tile in the launcher. Branding is set via `update_app` (the single
383
+ * setter) — this only reminds; it never fails the deploy.
384
+ */
385
+ async function warnIfUnbranded(client, appId) {
386
+ try {
387
+ const app = await client.getApp(appId);
388
+ const missing = [];
389
+ if (!app.icon)
390
+ missing.push("icon");
391
+ if (!app.theme?.color)
392
+ missing.push("color");
393
+ if (missing.length === 0)
394
+ return;
395
+ console.error(`\n⚠ This app has no ${missing.join(" or ")} set — it shows a generic tile in the launcher.\n` +
396
+ ` Set it: lotics run update_app '{"app_id":"${appId}","icon":"<lucide-name>","theme":{"color":"blue"}}'\n` +
397
+ ` Find an icon: lotics run search_app_icons '{"query":"<word>"}'`);
398
+ }
399
+ catch (err) {
400
+ // Deploy already succeeded; a failed branding check must not mask that.
401
+ console.error(`(skipped branding check: ${err.message})`);
402
+ }
403
+ }
388
404
  /**
389
405
  * `lotics app dev [path] [--port=5174] [--vite-port=5173]`
390
406
  *
package/dist/client.d.ts CHANGED
@@ -96,6 +96,12 @@ export declare class LoticsClient {
96
96
  name: string;
97
97
  workspace_id: string;
98
98
  current_version_id: string | null;
99
+ /** Launcher icon — a Lucide name, or an image ref. Null when unset. */
100
+ icon?: string | null;
101
+ /** Launcher theme — `{ color }` is a palette token. Null when unset. */
102
+ theme?: {
103
+ color?: string | null;
104
+ } | null;
99
105
  /**
100
106
  * Live alias → workflow declaration map from `apps.workflows`. Source of
101
107
  * truth for `lotics app pull` — supersedes the manifest embedded in the
@@ -232,14 +238,6 @@ export declare class LoticsClient {
232
238
  ast: unknown;
233
239
  params?: Record<string, unknown>;
234
240
  }>;
235
- /**
236
- * App icon (Lucide name) + theme `{ color }` from `package.json#lotics`.
237
- * `undefined` ⇒ not sent ⇒ the deploy leaves the App row's icon/theme
238
- * as-is. A present value is applied authoritatively. `icon` rides as a
239
- * raw form field; `theme` is an object, so it's JSON-encoded.
240
- */
241
- icon?: string;
242
- theme?: Record<string, unknown>;
243
241
  /**
244
242
  * Opt-in app capabilities from `package.json#lotics.capabilities`. The CLI
245
243
  * sends this on every deploy (defaulting to `{}`): the manifest is
package/dist/client.js CHANGED
@@ -220,16 +220,6 @@ export class LoticsClient {
220
220
  // Always send queries — empty object clears any previously-declared
221
221
  // named queries. Server validates each template.
222
222
  formData.append("queries", JSON.stringify(args.queries ?? {}));
223
- // icon/theme are optional overrides — only sent when the manifest
224
- // declares them, so a deploy never clobbers a value set in the UI.
225
- // `icon` is a plain string → raw field, like `message`. `theme` is an
226
- // object → JSON-encoded, like `workflows`/`queries`.
227
- if (args.icon !== undefined) {
228
- formData.append("icon", args.icon);
229
- }
230
- if (args.theme !== undefined) {
231
- formData.append("theme", JSON.stringify(args.theme));
232
- }
233
223
  // capabilities is manifest-authoritative — the caller always passes it
234
224
  // (`{}` when none declared), so a deploy turns off any capability the
235
225
  // manifest no longer declares. JSON-encoded.
package/dist/src/cli.js CHANGED
@@ -29814,12 +29814,6 @@ var LoticsClient = class {
29814
29814
  }
29815
29815
  formData.append("workflows", JSON.stringify(args.workflows ?? {}));
29816
29816
  formData.append("queries", JSON.stringify(args.queries ?? {}));
29817
- if (args.icon !== void 0) {
29818
- formData.append("icon", args.icon);
29819
- }
29820
- if (args.theme !== void 0) {
29821
- formData.append("theme", JSON.stringify(args.theme));
29822
- }
29823
29817
  if (args.capabilities !== void 0) {
29824
29818
  formData.append("capabilities", JSON.stringify(args.capabilities));
29825
29819
  }
@@ -31239,10 +31233,6 @@ function readAppMeta(projectDir) {
31239
31233
  version_number: pkg2.lotics.version_number ?? null,
31240
31234
  workflows: pkg2.lotics.workflows ?? {},
31241
31235
  queries: pkg2.lotics.queries ?? {},
31242
- // Left as `undefined` when absent so the deploy omits them and the
31243
- // App row's icon/theme is preserved.
31244
- icon: pkg2.lotics.icon,
31245
- theme: pkg2.lotics.theme,
31246
31236
  capabilities: pkg2.lotics.capabilities
31247
31237
  };
31248
31238
  }
@@ -31417,15 +31407,10 @@ async function appDeploy(client, args) {
31417
31407
  // Sync apps.queries from the manifest. Server validates each query
31418
31408
  // template (parseQueryNode, table access, param coverage).
31419
31409
  queries: meta.queries ?? {},
31420
- // Optional icon/theme overrides only sent when the manifest
31421
- // declares them (undefined omitted App row left untouched).
31422
- icon: meta.icon,
31423
- theme: meta.theme,
31424
- // Capabilities are manifest-authoritative (like workflows/queries, not
31425
- // like icon/theme): always send, defaulting to `{}` when the manifest
31426
- // declares none — so deleting the `capabilities` block turns every
31427
- // capability OFF on the next deploy (fail-safe; the declaration is the
31428
- // grant). The manifest is the only writer of capabilities.
31410
+ // Capabilities are manifest-authoritative (like workflows/queries):
31411
+ // always send, defaulting to `{}` when the manifest declares none — so
31412
+ // deleting the `capabilities` block turns every capability OFF on the
31413
+ // next deploy (fail-safe; the declaration is the grant).
31429
31414
  capabilities: meta.capabilities ?? {},
31430
31415
  // Opt into destructive workflow removal. Default false — the server
31431
31416
  // rejects deploys whose manifest is missing aliases the App row has.
@@ -31438,6 +31423,7 @@ async function appDeploy(client, args) {
31438
31423
  });
31439
31424
  console.error(`Deployed v${result.version_number} (${result.version_id})`);
31440
31425
  console.error(`Bundle size: ${(result.bundle_size_bytes / 1024).toFixed(1)} KB`);
31426
+ await warnIfUnbranded(client, meta.app_id);
31441
31427
  } catch (err2) {
31442
31428
  const e = err2;
31443
31429
  if (e.code === "VERSION_CONFLICT") {
@@ -31453,6 +31439,23 @@ async function appDeploy(client, args) {
31453
31439
  if (fs3.existsSync(tmpDist)) fs3.unlinkSync(tmpDist);
31454
31440
  }
31455
31441
  }
31442
+ async function warnIfUnbranded(client, appId) {
31443
+ try {
31444
+ const app = await client.getApp(appId);
31445
+ const missing = [];
31446
+ if (!app.icon) missing.push("icon");
31447
+ if (!app.theme?.color) missing.push("color");
31448
+ if (missing.length === 0) return;
31449
+ console.error(
31450
+ `
31451
+ \u26A0 This app has no ${missing.join(" or ")} set \u2014 it shows a generic tile in the launcher.
31452
+ Set it: lotics run update_app '{"app_id":"${appId}","icon":"<lucide-name>","theme":{"color":"blue"}}'
31453
+ Find an icon: lotics run search_app_icons '{"query":"<word>"}'`
31454
+ );
31455
+ } catch (err2) {
31456
+ console.error(`(skipped branding check: ${err2.message})`);
31457
+ }
31458
+ }
31456
31459
  async function appDev(client, args) {
31457
31460
  const projectDir = path4.resolve(args.projectDir ?? process.cwd());
31458
31461
  const meta = readAppMeta(projectDir);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/cli",
3
- "version": "0.40.0",
3
+ "version": "0.41.0",
4
4
  "description": "Lotics SDK and CLI for AI agents",
5
5
  "type": "module",
6
6
  "bin": {