@lotics/cli 0.66.0 → 0.67.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.
@@ -911,7 +911,16 @@ args) {
911
911
  });
912
912
  console.error(`Deployed v${result.version_number} (${result.version_id})`);
913
913
  console.error(`Bundle size: ${(result.bundle_size_bytes / 1024).toFixed(1)} KB`);
914
- await warnIfUnbranded(client, meta.app_id);
914
+ // One post-deploy getApp feeds both advisory nudges (branding, unbound
915
+ // aliases). Deploy already succeeded — a failed check must not mask that.
916
+ try {
917
+ const app = await client.getApp(meta.app_id);
918
+ warnIfUnbranded(app);
919
+ warnIfUnboundAliases(app, meta.workflows ?? {}, meta.agents ?? {});
920
+ }
921
+ catch (err) {
922
+ console.error(`(skipped post-deploy checks: ${err.message})`);
923
+ }
915
924
  }
916
925
  catch (err) {
917
926
  const e = err;
@@ -935,24 +944,44 @@ args) {
935
944
  * generic tile in the launcher. Branding is set via `update_app` (the single
936
945
  * setter) — this only reminds; it never fails the deploy.
937
946
  */
938
- async function warnIfUnbranded(client, appId) {
939
- try {
940
- const app = await client.getApp(appId);
941
- const missing = [];
942
- if (!app.icon)
943
- missing.push("icon");
944
- if (!app.theme?.color)
945
- missing.push("color");
946
- if (missing.length === 0)
947
- return;
948
- console.error(`\n⚠ This app has no ${missing.join(" or ")} set — it shows a generic tile in the launcher.\n` +
949
- ` Set it: lotics run update_app '{"app_id":"${appId}","icon":"<lucide-name>","theme":{"color":"blue"}}'\n` +
950
- ` Find an icon: lotics run search_app_icons '{"query":"<word>"}'`);
951
- }
952
- catch (err) {
953
- // Deploy already succeeded; a failed branding check must not mask that.
954
- console.error(`(skipped branding check: ${err.message})`);
955
- }
947
+ function warnIfUnbranded(app) {
948
+ const missing = [];
949
+ if (!app.icon)
950
+ missing.push("icon");
951
+ if (!app.theme?.color)
952
+ missing.push("color");
953
+ if (missing.length === 0)
954
+ return;
955
+ console.error(`\n⚠ This app has no ${missing.join(" or ")} set — it shows a generic tile in the launcher.\n` +
956
+ ` Set it: lotics run update_app '{"app_id":"${app.id}","icon":"<lucide-name>","theme":{"color":"blue"}}'\n` +
957
+ ` Find an icon: lotics run search_app_icons '{"query":"<word>"}'`);
958
+ }
959
+ /**
960
+ * Non-blocking nudge after a successful deploy: catch manifest workflow/agent aliases
961
+ * that are NOT bound on the server. Deploy ships code + queries only — it never binds
962
+ * workflows/agents (`set_app_workflow` / `set_app_agent` own `apps.workflows`/`apps.agents`;
963
+ * the manifest maps are a pulled reflection). So an alias hand-added to `lotics.workflows`
964
+ * / `lotics.agents` (or bound then removed) compiles + deploys clean and only throws when
965
+ * the app first calls `useWorkflow` / `useAgentRun`. Surface that divergence HERE — at
966
+ * deploy time — instead of at the user's first click. Advisory only; never fails the deploy.
967
+ */
968
+ function warnIfUnboundAliases(app, declaredWorkflows, declaredAgents) {
969
+ const boundWorkflows = new Set(Object.keys(app.workflows ?? {}));
970
+ const boundAgents = new Set(Object.keys(app.agents ?? {}));
971
+ const unboundWorkflows = Object.keys(declaredWorkflows).filter((a) => !boundWorkflows.has(a));
972
+ const unboundAgents = Object.keys(declaredAgents).filter((a) => !boundAgents.has(a));
973
+ if (unboundWorkflows.length === 0 && unboundAgents.length === 0)
974
+ return;
975
+ const lines = [
976
+ "\n⚠ The manifest declares aliases that are NOT bound on the server. Deploy ships code +",
977
+ " queries only — it does NOT bind workflows/agents, so the app will throw \"has no … alias\"",
978
+ " the first time it calls them. Bind each one:",
979
+ ];
980
+ for (const alias of unboundWorkflows)
981
+ lines.push(` • workflow "${alias}" → lotics app workflow set ${alias}`);
982
+ for (const alias of unboundAgents)
983
+ lines.push(` • agent "${alias}" → bind with set_app_agent (lotics run set_app_agent …)`);
984
+ console.error(lines.join("\n"));
956
985
  }
957
986
  /**
958
987
  * `lotics app dev [path] [--port=5174] [--vite-port=5173]`
package/dist/src/cli.js CHANGED
@@ -32758,7 +32758,13 @@ async function appDeploy(client, args) {
32758
32758
  });
32759
32759
  console.error(`Deployed v${result.version_number} (${result.version_id})`);
32760
32760
  console.error(`Bundle size: ${(result.bundle_size_bytes / 1024).toFixed(1)} KB`);
32761
- await warnIfUnbranded(client, meta.app_id);
32761
+ try {
32762
+ const app = await client.getApp(meta.app_id);
32763
+ warnIfUnbranded(app);
32764
+ warnIfUnboundAliases(app, meta.workflows ?? {}, meta.agents ?? {});
32765
+ } catch (err2) {
32766
+ console.error(`(skipped post-deploy checks: ${err2.message})`);
32767
+ }
32762
32768
  } catch (err2) {
32763
32769
  const e = err2;
32764
32770
  if (e.code === "VERSION_CONFLICT") {
@@ -32774,22 +32780,32 @@ async function appDeploy(client, args) {
32774
32780
  if (fs4.existsSync(tmpDist)) fs4.unlinkSync(tmpDist);
32775
32781
  }
32776
32782
  }
32777
- async function warnIfUnbranded(client, appId) {
32778
- try {
32779
- const app = await client.getApp(appId);
32780
- const missing = [];
32781
- if (!app.icon) missing.push("icon");
32782
- if (!app.theme?.color) missing.push("color");
32783
- if (missing.length === 0) return;
32784
- console.error(
32785
- `
32783
+ function warnIfUnbranded(app) {
32784
+ const missing = [];
32785
+ if (!app.icon) missing.push("icon");
32786
+ if (!app.theme?.color) missing.push("color");
32787
+ if (missing.length === 0) return;
32788
+ console.error(
32789
+ `
32786
32790
  \u26A0 This app has no ${missing.join(" or ")} set \u2014 it shows a generic tile in the launcher.
32787
- Set it: lotics run update_app '{"app_id":"${appId}","icon":"<lucide-name>","theme":{"color":"blue"}}'
32791
+ Set it: lotics run update_app '{"app_id":"${app.id}","icon":"<lucide-name>","theme":{"color":"blue"}}'
32788
32792
  Find an icon: lotics run search_app_icons '{"query":"<word>"}'`
32789
- );
32790
- } catch (err2) {
32791
- console.error(`(skipped branding check: ${err2.message})`);
32792
- }
32793
+ );
32794
+ }
32795
+ function warnIfUnboundAliases(app, declaredWorkflows, declaredAgents) {
32796
+ const boundWorkflows = new Set(Object.keys(app.workflows ?? {}));
32797
+ const boundAgents = new Set(Object.keys(app.agents ?? {}));
32798
+ const unboundWorkflows = Object.keys(declaredWorkflows).filter((a) => !boundWorkflows.has(a));
32799
+ const unboundAgents = Object.keys(declaredAgents).filter((a) => !boundAgents.has(a));
32800
+ if (unboundWorkflows.length === 0 && unboundAgents.length === 0) return;
32801
+ const lines = [
32802
+ "\n\u26A0 The manifest declares aliases that are NOT bound on the server. Deploy ships code +",
32803
+ ' queries only \u2014 it does NOT bind workflows/agents, so the app will throw "has no \u2026 alias"',
32804
+ " the first time it calls them. Bind each one:"
32805
+ ];
32806
+ for (const alias of unboundWorkflows) lines.push(` \u2022 workflow "${alias}" \u2192 lotics app workflow set ${alias}`);
32807
+ for (const alias of unboundAgents) lines.push(` \u2022 agent "${alias}" \u2192 bind with set_app_agent (lotics run set_app_agent \u2026)`);
32808
+ console.error(lines.join("\n"));
32793
32809
  }
32794
32810
  async function appDev(client, args) {
32795
32811
  const projectDir = path5.resolve(args.projectDir ?? process.cwd());
@@ -42716,6 +42732,7 @@ function readCfbStreams(bytes) {
42716
42732
  throw new Error("Unsupported .xls: invalid OLE2 sector size");
42717
42733
  }
42718
42734
  const sectorOffset = (n) => sectorSize * (n + 1);
42735
+ const maxSectors = Math.ceil(bytes.length / sectorSize) + 1;
42719
42736
  const difat = [];
42720
42737
  for (let i2 = 0; i2 < 109; i2++) {
42721
42738
  const v = u32(76 + i2 * 4);
@@ -42723,7 +42740,9 @@ function readCfbStreams(bytes) {
42723
42740
  }
42724
42741
  let ds = firstDifatSector;
42725
42742
  const entriesPerSector = sectorSize / 4;
42726
- for (let guard = 0; ds !== ENDOFCHAIN && ds !== FREESECT && guard < 1 << 20; guard++) {
42743
+ const seenDifat = /* @__PURE__ */ new Set();
42744
+ while (ds !== ENDOFCHAIN && ds !== FREESECT && ds < maxSectors && !seenDifat.has(ds)) {
42745
+ seenDifat.add(ds);
42727
42746
  const base = sectorOffset(ds);
42728
42747
  for (let i2 = 0; i2 < entriesPerSector - 1; i2++) {
42729
42748
  const v = u32(base + i2 * 4);
@@ -42733,14 +42752,18 @@ function readCfbStreams(bytes) {
42733
42752
  }
42734
42753
  const fat = [];
42735
42754
  for (const fatSector of difat) {
42755
+ if (fat.length >= maxSectors) break;
42756
+ if (fatSector >= maxSectors) continue;
42736
42757
  const base = sectorOffset(fatSector);
42737
- for (let i2 = 0; i2 < entriesPerSector; i2++) fat.push(u32(base + i2 * 4));
42758
+ for (let i2 = 0; i2 < entriesPerSector && fat.length < maxSectors; i2++) {
42759
+ fat.push(u32(base + i2 * 4));
42760
+ }
42738
42761
  }
42739
42762
  const readChain = (src, start, size, ss, offOf, chain) => {
42740
- const maxSectors = Math.floor(src.length / ss) + 2;
42763
+ const maxSectors2 = Math.floor(src.length / ss) + 2;
42741
42764
  const parts = [];
42742
42765
  let s = start;
42743
- for (let guard = 0; s !== ENDOFCHAIN && s !== FREESECT && s < chain.length && guard < maxSectors; guard++) {
42766
+ for (let guard = 0; s !== ENDOFCHAIN && s !== FREESECT && s < chain.length && guard < maxSectors2; guard++) {
42744
42767
  const o = offOf(s);
42745
42768
  parts.push(src.subarray(o, o + ss));
42746
42769
  s = chain[s];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/cli",
3
- "version": "0.66.0",
3
+ "version": "0.67.0",
4
4
  "description": "Lotics SDK and CLI for AI agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -26,7 +26,7 @@
26
26
  "@types/node": "^22.15.21",
27
27
  "esbuild": "^0.28.1",
28
28
  "typescript": "^6.0.3",
29
- "vitest": "^4.1.7"
29
+ "vitest": "^4.1.9"
30
30
  },
31
31
  "keywords": [
32
32
  "lotics",