@lotics/cli 0.101.0 → 0.101.1

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.
@@ -28695,6 +28695,73 @@ ${e.toString()}`);
28695
28695
  offsetYEmu
28696
28696
  };
28697
28697
  }
28698
+ var EMU_PER_PT = 12700;
28699
+ var VML_UNIT_TO_EMU = {
28700
+ pt: EMU_PER_PT,
28701
+ in: EMU_PER_PT * 72,
28702
+ cm: EMU_PER_PT * 72 / 2.54,
28703
+ mm: EMU_PER_PT * 72 / 25.4,
28704
+ pc: EMU_PER_PT * 12,
28705
+ px: EMU_PER_PT * 72 / 96
28706
+ };
28707
+ function readVmlLength(style, property) {
28708
+ const match = new RegExp(`(?:^|;)\\s*${property}\\s*:\\s*([0-9.]+)([a-z]*)`, "i").exec(style);
28709
+ if (!match) return null;
28710
+ const value = Number.parseFloat(match[1]);
28711
+ if (!Number.isFinite(value)) return null;
28712
+ const unit = match[2].toLowerCase();
28713
+ const perUnit = unit === "" ? EMU_PER_PT : VML_UNIT_TO_EMU[unit];
28714
+ if (perUnit === void 0) return null;
28715
+ return Math.round(value * perUnit);
28716
+ }
28717
+ function parsePict(pictXml) {
28718
+ if (getTagName(pictXml) !== "w:pict") return null;
28719
+ const imagedata = findFirstByTag(pictXml, "v:imagedata");
28720
+ if (!imagedata) return null;
28721
+ const relationshipId = getAttr(imagedata, "r:id") ?? getAttr(imagedata, "r:embed") ?? null;
28722
+ if (relationshipId === null) return null;
28723
+ const shape = findFirstByTag(pictXml, "v:shape") ?? findFirstByTag(pictXml, "v:rect");
28724
+ const style = shape ? getAttr(shape, "style") ?? "" : "";
28725
+ return {
28726
+ relationshipId,
28727
+ widthEmu: readVmlLength(style, "width"),
28728
+ heightEmu: readVmlLength(style, "height"),
28729
+ alt: (shape ? getAttr(shape, "alt") : void 0) ?? "",
28730
+ // VML positioning is a CSS-ish `style`, not the wp: anchor vocabulary the
28731
+ // anchor fields describe. Reporting it as inline keeps the renderer from
28732
+ // acting on offsets that were never read.
28733
+ inline: true,
28734
+ wrap: null,
28735
+ floatSide: null,
28736
+ behindDoc: false,
28737
+ offsetXEmu: null,
28738
+ offsetYEmu: null
28739
+ };
28740
+ }
28741
+ function parseAlternateContent(el) {
28742
+ if (getTagName(el) !== "mc:AlternateContent") return null;
28743
+ const branches = getChildren(el).filter((child) => {
28744
+ const tag = getTagName(child);
28745
+ return tag === "mc:Choice" || tag === "mc:Fallback";
28746
+ });
28747
+ const ordered = [
28748
+ ...branches.filter((b) => getTagName(b) === "mc:Choice"),
28749
+ ...branches.filter((b) => getTagName(b) === "mc:Fallback")
28750
+ ];
28751
+ for (const branch of ordered) {
28752
+ const drawing = findFirstByTag(branch, "w:drawing");
28753
+ if (drawing) {
28754
+ const info = parseDrawing(drawing);
28755
+ if (info && info.relationshipId !== null) return info;
28756
+ }
28757
+ const pict = findFirstByTag(branch, "w:pict");
28758
+ if (pict) {
28759
+ const info = parsePict(pict);
28760
+ if (info) return info;
28761
+ }
28762
+ }
28763
+ return null;
28764
+ }
28698
28765
  function readPosOffset(containerEl, positionTag) {
28699
28766
  const position = findFirstByTag(containerEl, positionTag);
28700
28767
  if (!position) return null;
@@ -42936,6 +43003,24 @@ ${e.toString()}`);
42936
43003
  }
42937
43004
  };
42938
43005
  }
43006
+ function imageNode(info, originalXml, marks) {
43007
+ return docxSchema.nodes.image_inline.create(
43008
+ {
43009
+ relationshipId: info.relationshipId,
43010
+ widthEmu: info.widthEmu,
43011
+ heightEmu: info.heightEmu,
43012
+ alt: info.alt,
43013
+ originalXml,
43014
+ wrap: info.wrap,
43015
+ floatSide: info.floatSide,
43016
+ behindDoc: info.behindDoc,
43017
+ offsetXEmu: info.offsetXEmu,
43018
+ offsetYEmu: info.offsetYEmu
43019
+ },
43020
+ void 0,
43021
+ marks
43022
+ );
43023
+ }
42939
43024
  function convertRunChild(child, marks, state) {
42940
43025
  if (child.kind === "text") {
42941
43026
  if (child.value.length === 0) return [];
@@ -42963,26 +43048,15 @@ ${e.toString()}`);
42963
43048
  }
42964
43049
  if (tag === "w:drawing") {
42965
43050
  const info = parseDrawing(child.xml);
42966
- if (info) {
42967
- return [
42968
- docxSchema.nodes.image_inline.create(
42969
- {
42970
- relationshipId: info.relationshipId,
42971
- widthEmu: info.widthEmu,
42972
- heightEmu: info.heightEmu,
42973
- alt: info.alt,
42974
- originalXml: child.xml,
42975
- wrap: info.wrap,
42976
- floatSide: info.floatSide,
42977
- behindDoc: info.behindDoc,
42978
- offsetXEmu: info.offsetXEmu,
42979
- offsetYEmu: info.offsetYEmu
42980
- },
42981
- void 0,
42982
- marks
42983
- )
42984
- ];
42985
- }
43051
+ if (info) return [imageNode(info, child.xml, marks)];
43052
+ }
43053
+ if (tag === "mc:AlternateContent") {
43054
+ const info = parseAlternateContent(child.xml);
43055
+ if (info) return [imageNode(info, child.xml, marks)];
43056
+ }
43057
+ if (tag === "w:pict") {
43058
+ const info = parsePict(child.xml);
43059
+ if (info) return [imageNode(info, child.xml, marks)];
42986
43060
  }
42987
43061
  return [
42988
43062
  docxSchema.nodes.opaque_inline.create({ xml: child.xml }, void 0, marks)
package/dist/src/cli.js CHANGED
@@ -44955,6 +44955,7 @@ var LoticsClient = class {
44955
44955
  formData.append("capabilities", JSON.stringify(args.capabilities));
44956
44956
  }
44957
44957
  formData.append("workflow_aliases", JSON.stringify(args.workflow_aliases ?? []));
44958
+ formData.append("query_aliases", JSON.stringify(args.query_aliases ?? []));
44958
44959
  const url2 = `${this.baseUrl}/v1/apps/${encodeURIComponent(args.app_id)}/versions`;
44959
44960
  const response = await fetch(url2, {
44960
44961
  method: "POST",
@@ -64951,12 +64952,16 @@ var appWorkflowContractSchema = zod_default.object({
64951
64952
  inputs: zod_default.record(zod_default.string(), appWorkflowInputSchema).optional(),
64952
64953
  outputs: zod_default.record(zod_default.string(), appWorkflowOutputSchema).optional()
64953
64954
  });
64955
+ var MAX_APP_CAPABILITY_DESCRIPTION = 300;
64954
64956
  var appQueryDeclarationSchema = zod_default.object({
64955
64957
  ast: zod_default.unknown().describe(
64956
64958
  "Query AST template (a QueryNode). Validated server-side via parseQueryNode at deploy. May embed {{params.<name>}} tokens in filter value positions."
64957
64959
  ),
64958
64960
  params: zod_default.record(zod_default.string(), appWorkflowInputSchema).optional().describe(
64959
64961
  "Typed param schema. Keys are param names referenced as {{params.<name>}} in the ast; values declare type + constraints. The server validates the caller's params payload against this before interpolating. Omit for queries that take no params."
64962
+ ),
64963
+ description: zod_default.string().optional().describe(
64964
+ `What this query returns, in one line \u2014 read by an agent choosing between the app's aliases. An alias is a JS identifier, which names a query without saying what it covers. Capped at ${MAX_APP_CAPABILITY_DESCRIPTION} characters.`
64960
64965
  )
64961
64966
  });
64962
64967
  var appAgentDeclarationSchema = zod_default.object({
@@ -70822,7 +70827,8 @@ async function appDeploy(client, args) {
70822
70827
  // typing artifact maintained by habit, so its keys answered this question
70823
70828
  // with whatever the author last remembered to write. Stop calling an
70824
70829
  // alias + redeploy to lift the guard before removing its binding.
70825
- workflow_aliases: called.workflows
70830
+ workflow_aliases: called.workflows,
70831
+ query_aliases: called.queries
70826
70832
  });
70827
70833
  writeAppMeta(projectDir, {
70828
70834
  ...meta3,
@@ -89648,37 +89654,47 @@ function extractParagraphText(el) {
89648
89654
  }
89649
89655
  return parts.join("");
89650
89656
  }
89651
- function getRunsFromParagraph(el) {
89652
- const runs = [];
89657
+ function getParagraphTextSegments(el) {
89658
+ const segments = [];
89653
89659
  for (const child of getChildren(el)) {
89654
89660
  if (getTagName(child) !== "w:r") continue;
89655
- let text = "";
89656
- let hasText = false;
89661
+ let pending = [];
89662
+ const flush = () => {
89663
+ if (pending.length === 0) return;
89664
+ const text = pending.map(
89665
+ (node) => getChildren(node).reduce(
89666
+ (acc, tn) => typeof tn["#text"] === "string" ? acc + tn["#text"] : acc,
89667
+ ""
89668
+ )
89669
+ ).join("");
89670
+ segments.push({ element: child, textNodes: pending, text });
89671
+ pending = [];
89672
+ };
89657
89673
  for (const grandchild of getChildren(child)) {
89658
- if (getTagName(grandchild) !== "w:t") continue;
89659
- hasText = true;
89660
- for (const tn of getChildren(grandchild)) {
89661
- if (typeof tn["#text"] === "string") text += tn["#text"];
89674
+ if (getTagName(grandchild) === "w:t") {
89675
+ pending.push(grandchild);
89676
+ continue;
89662
89677
  }
89678
+ flush();
89663
89679
  }
89664
- if (hasText) runs.push({ element: child, text });
89680
+ flush();
89665
89681
  }
89666
- return runs;
89682
+ return segments;
89667
89683
  }
89668
- function writeRunText(runElement, newText) {
89669
- const children = getChildren(runElement);
89670
- const textNode = { "w:t": [{ "#text": newText }], ":@": { "@_xml:space": "preserve" } };
89671
- const indices = children.flatMap((c, i2) => getTagName(c) === "w:t" ? [i2] : []);
89672
- if (indices.length === 0) {
89673
- children.push(textNode);
89674
- return;
89684
+ function setSegmentText(segment, newText) {
89685
+ const children = getChildren(segment.element);
89686
+ const textNode = {
89687
+ "w:t": [{ "#text": newText }],
89688
+ ":@": { "@_xml:space": "preserve" }
89689
+ };
89690
+ const head = children.indexOf(segment.textNodes[0]);
89691
+ children[head] = textNode;
89692
+ for (let i2 = segment.textNodes.length - 1; i2 >= 1; i2--) {
89693
+ const at2 = children.indexOf(segment.textNodes[i2]);
89694
+ if (at2 >= 0) children.splice(at2, 1);
89675
89695
  }
89676
- children[indices[0]] = textNode;
89677
- for (let i2 = indices.length - 1; i2 >= 1; i2--) children.splice(indices[i2], 1);
89678
- }
89679
- function setRunText(run, newText) {
89680
- writeRunText(run.element, newText);
89681
- run.text = newText;
89696
+ segment.textNodes = [textNode];
89697
+ segment.text = newText;
89682
89698
  }
89683
89699
  function computeReplacementRanges(text, query, replacement, matchType, maxReplacements, startCount) {
89684
89700
  const ranges = [];
@@ -89713,39 +89729,39 @@ function computeReplacementRanges(text, query, replacement, matchType, maxReplac
89713
89729
  }
89714
89730
  return { ranges, count };
89715
89731
  }
89716
- function applyRangeReplacements(runs, ranges) {
89732
+ function applyRangeReplacements(segments, ranges) {
89717
89733
  if (ranges.length === 0) return;
89718
89734
  const bounds = [];
89719
89735
  let offset = 0;
89720
- for (const run of runs) {
89721
- bounds.push({ run, start: offset, end: offset + run.text.length });
89722
- offset += run.text.length;
89736
+ for (const segment of segments) {
89737
+ bounds.push({ segment, start: offset, end: offset + segment.text.length });
89738
+ offset += segment.text.length;
89723
89739
  }
89724
- const joined = runs.map((r) => r.text).join("");
89740
+ const joined = segments.map((s) => s.text).join("");
89725
89741
  const sorted = [...ranges].sort((a, b) => a.start - b.start);
89726
- for (const { run, start: runStart, end: runEnd } of bounds) {
89742
+ for (const { segment, start: segStart, end: segEnd } of bounds) {
89727
89743
  let next = "";
89728
- let cursor = runStart;
89744
+ let cursor = segStart;
89729
89745
  for (const range2 of sorted) {
89730
- if (range2.end <= runStart || range2.start >= runEnd) continue;
89731
- const keepUntil = Math.min(range2.start, runEnd);
89732
- const from = Math.max(cursor, runStart);
89746
+ if (range2.end <= segStart || range2.start >= segEnd) continue;
89747
+ const keepUntil = Math.min(range2.start, segEnd);
89748
+ const from = Math.max(cursor, segStart);
89733
89749
  if (keepUntil > from) next += joined.slice(from, keepUntil);
89734
- if (runStart <= range2.start && range2.start < runEnd) next += range2.replacement;
89750
+ if (segStart <= range2.start && range2.start < segEnd) next += range2.replacement;
89735
89751
  cursor = Math.max(cursor, range2.end);
89736
89752
  }
89737
- const tail = Math.max(cursor, runStart);
89738
- if (runEnd > tail) next += joined.slice(tail, runEnd);
89739
- if (next !== run.text) setRunText(run, next);
89753
+ const tail = Math.max(cursor, segStart);
89754
+ if (segEnd > tail) next += joined.slice(tail, segEnd);
89755
+ if (next !== segment.text) setSegmentText(segment, next);
89740
89756
  }
89741
89757
  }
89742
89758
  function replaceInParagraph(el, query, replacement, matchType, maxReplacements, replacementsMade) {
89743
89759
  if (!extractParagraphText(el)) return replacementsMade;
89744
- const runs = getRunsFromParagraph(el);
89745
- if (runs.length === 0) return replacementsMade;
89746
- const text = runs.length === 1 ? runs[0].text : runs.map((r) => r.text).join("");
89760
+ const segments = getParagraphTextSegments(el);
89761
+ if (segments.length === 0) return replacementsMade;
89762
+ const text = segments.length === 1 ? segments[0].text : segments.map((s) => s.text).join("");
89747
89763
  const { ranges, count } = computeReplacementRanges(text, query, replacement, matchType, maxReplacements, replacementsMade);
89748
- applyRangeReplacements(runs, ranges);
89764
+ applyRangeReplacements(segments, ranges);
89749
89765
  return count;
89750
89766
  }
89751
89767
  function replaceText(bodyElements, query, replacement, matchType = "contains", maxReplacements = Infinity) {
@@ -222,6 +222,7 @@ export declare class LoticsClient {
222
222
  queries?: Record<string, {
223
223
  ast: unknown;
224
224
  params?: Record<string, unknown>;
225
+ description?: string;
225
226
  }> | null;
226
227
  /**
227
228
  * Live alias → agent declaration map from `apps.agents`. Source of truth for
@@ -866,6 +867,7 @@ export declare class LoticsClient {
866
867
  setAppQuery(app_id: string, alias: string, declaration: {
867
868
  ast: unknown;
868
869
  params?: Record<string, unknown>;
870
+ description?: string;
869
871
  }): Promise<ToolExecuteResult>;
870
872
  /**
871
873
  * Bind (create or replace) an app agent by alias via the `set_app_agent` tool
@@ -1006,14 +1008,16 @@ export declare class LoticsClient {
1006
1008
  prev_version_id?: string | null;
1007
1009
  message?: string | null;
1008
1010
  /**
1009
- * Alias → query declaration map from `package.json#lotics.queries`. Each
1010
- * value is `{ ast, params? }` — a fixed query AST template and an optional
1011
- * typed param schema. Always sent (empty object when none declared) so the
1012
- * server overwrites apps.queries authoritatively.
1011
+ * Alias → query declaration map ECHOED BACK from the live row (see
1012
+ * `readLiveQueries`), not read from the manifest. Every key the row holds
1013
+ * must survive the round trip — `description` included — because a server
1014
+ * that treats a present map as authoritative writes exactly what it is
1015
+ * sent, so a lossy echo silently strips whatever it forgot.
1013
1016
  */
1014
1017
  queries?: Record<string, {
1015
1018
  ast: unknown;
1016
1019
  params?: Record<string, unknown>;
1020
+ description?: string;
1017
1021
  }>;
1018
1022
  /**
1019
1023
  * Opt-in app capabilities from `package.json#lotics.capabilities`. The CLI
@@ -1032,6 +1036,7 @@ export declare class LoticsClient {
1032
1036
  * (empty array when none declared).
1033
1037
  */
1034
1038
  workflow_aliases?: string[];
1039
+ query_aliases?: string[];
1035
1040
  }): Promise<{
1036
1041
  version_id: string;
1037
1042
  version_number: number;
@@ -723,6 +723,7 @@ export class LoticsClient {
723
723
  // server records what the served version calls — the remove_app_workflow
724
724
  // guard reads this back. These are the manifest KEYS only, never bindings.
725
725
  formData.append("workflow_aliases", JSON.stringify(args.workflow_aliases ?? []));
726
+ formData.append("query_aliases", JSON.stringify(args.query_aliases ?? []));
726
727
  const url = `${this.baseUrl}/v1/apps/${encodeURIComponent(args.app_id)}/versions`;
727
728
  const response = await fetch(url, {
728
729
  method: "POST",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/cli",
3
- "version": "0.101.0",
3
+ "version": "0.101.1",
4
4
  "description": "Lotics SDK and CLI for AI agents",
5
5
  "type": "module",
6
6
  "bin": {