@lotics/cli 0.114.0 → 0.123.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.
package/dist/src/cli.js CHANGED
@@ -26822,12 +26822,12 @@ var require_tokens = __commonJS({
26822
26822
  });
26823
26823
  }
26824
26824
  exports2.assignCategoriesMapProp = assignCategoriesMapProp;
26825
- function singleAssignCategoriesToksMap(path8, nextNode) {
26826
- utils_1.forEach(path8, function(pathNode) {
26825
+ function singleAssignCategoriesToksMap(path10, nextNode) {
26826
+ utils_1.forEach(path10, function(pathNode) {
26827
26827
  nextNode.categoryMatchesMap[pathNode.tokenTypeIdx] = true;
26828
26828
  });
26829
26829
  utils_1.forEach(nextNode.CATEGORIES, function(nextCategory) {
26830
- var newPath = path8.concat(nextNode);
26830
+ var newPath = path10.concat(nextNode);
26831
26831
  if (!utils_1.contains(newPath, nextCategory)) {
26832
26832
  singleAssignCategoriesToksMap(newPath, nextCategory);
26833
26833
  }
@@ -28590,10 +28590,10 @@ var require_interpreter = __commonJS({
28590
28590
  /** @class */
28591
28591
  (function(_super) {
28592
28592
  __extends(AbstractNextPossibleTokensWalker2, _super);
28593
- function AbstractNextPossibleTokensWalker2(topProd, path8) {
28593
+ function AbstractNextPossibleTokensWalker2(topProd, path10) {
28594
28594
  var _this = _super.call(this) || this;
28595
28595
  _this.topProd = topProd;
28596
- _this.path = path8;
28596
+ _this.path = path10;
28597
28597
  _this.possibleTokTypes = [];
28598
28598
  _this.nextProductionName = "";
28599
28599
  _this.nextProductionOccurrence = 0;
@@ -28647,9 +28647,9 @@ var require_interpreter = __commonJS({
28647
28647
  /** @class */
28648
28648
  (function(_super) {
28649
28649
  __extends(NextAfterTokenWalker2, _super);
28650
- function NextAfterTokenWalker2(topProd, path8) {
28651
- var _this = _super.call(this, topProd, path8) || this;
28652
- _this.path = path8;
28650
+ function NextAfterTokenWalker2(topProd, path10) {
28651
+ var _this = _super.call(this, topProd, path10) || this;
28652
+ _this.path = path10;
28653
28653
  _this.nextTerminalName = "";
28654
28654
  _this.nextTerminalOccurrence = 0;
28655
28655
  _this.nextTerminalName = _this.path.lastTok.name;
@@ -29346,10 +29346,10 @@ var require_lookahead = __commonJS({
29346
29346
  }
29347
29347
  return result;
29348
29348
  }
29349
- function pathToHashKeys(path8) {
29349
+ function pathToHashKeys(path10) {
29350
29350
  var keys3 = [""];
29351
- for (var i2 = 0; i2 < path8.length; i2++) {
29352
- var tokType = path8[i2];
29351
+ for (var i2 = 0; i2 < path10.length; i2++) {
29352
+ var tokType = path10[i2];
29353
29353
  var longerKeys = [];
29354
29354
  for (var j = 0; j < keys3.length; j++) {
29355
29355
  var currShorterKey = keys3[j];
@@ -29661,9 +29661,9 @@ var require_checks = __commonJS({
29661
29661
  return errors2;
29662
29662
  }
29663
29663
  exports2.validateRuleIsOverridden = validateRuleIsOverridden;
29664
- function validateNoLeftRecursion(topRule, currRule, errMsgProvider, path8) {
29665
- if (path8 === void 0) {
29666
- path8 = [];
29664
+ function validateNoLeftRecursion(topRule, currRule, errMsgProvider, path10) {
29665
+ if (path10 === void 0) {
29666
+ path10 = [];
29667
29667
  }
29668
29668
  var errors2 = [];
29669
29669
  var nextNonTerminals = getFirstNoneTerminal(currRule.definition);
@@ -29676,15 +29676,15 @@ var require_checks = __commonJS({
29676
29676
  errors2.push({
29677
29677
  message: errMsgProvider.buildLeftRecursionError({
29678
29678
  topLevelRule: topRule,
29679
- leftRecursionPath: path8
29679
+ leftRecursionPath: path10
29680
29680
  }),
29681
29681
  type: parser_1.ParserDefinitionErrorType.LEFT_RECURSION,
29682
29682
  ruleName
29683
29683
  });
29684
29684
  }
29685
- var validNextSteps = utils2.difference(nextNonTerminals, path8.concat([topRule]));
29685
+ var validNextSteps = utils2.difference(nextNonTerminals, path10.concat([topRule]));
29686
29686
  var errorsFromNextSteps = utils2.map(validNextSteps, function(currRefRule) {
29687
- var newPath = utils2.cloneArr(path8);
29687
+ var newPath = utils2.cloneArr(path10);
29688
29688
  newPath.push(currRefRule);
29689
29689
  return validateNoLeftRecursion(topRule, currRefRule, errMsgProvider, newPath);
29690
29690
  });
@@ -37293,8 +37293,8 @@ var require_utils4 = __commonJS({
37293
37293
  var result = transform2[inputType][outputType](input);
37294
37294
  return result;
37295
37295
  };
37296
- exports2.resolve = function(path8) {
37297
- var parts = path8.split("/");
37296
+ exports2.resolve = function(path10) {
37297
+ var parts = path10.split("/");
37298
37298
  var result = [];
37299
37299
  for (var index = 0; index < parts.length; index++) {
37300
37300
  var part = parts[index];
@@ -43147,18 +43147,18 @@ var require_object = __commonJS({
43147
43147
  var object2 = new ZipObject(name2, zipObjectContent, o);
43148
43148
  this.files[name2] = object2;
43149
43149
  };
43150
- var parentFolder = function(path8) {
43151
- if (path8.slice(-1) === "/") {
43152
- path8 = path8.substring(0, path8.length - 1);
43150
+ var parentFolder = function(path10) {
43151
+ if (path10.slice(-1) === "/") {
43152
+ path10 = path10.substring(0, path10.length - 1);
43153
43153
  }
43154
- var lastSlash = path8.lastIndexOf("/");
43155
- return lastSlash > 0 ? path8.substring(0, lastSlash) : "";
43154
+ var lastSlash = path10.lastIndexOf("/");
43155
+ return lastSlash > 0 ? path10.substring(0, lastSlash) : "";
43156
43156
  };
43157
- var forceTrailingSlash = function(path8) {
43158
- if (path8.slice(-1) !== "/") {
43159
- path8 += "/";
43157
+ var forceTrailingSlash = function(path10) {
43158
+ if (path10.slice(-1) !== "/") {
43159
+ path10 += "/";
43160
43160
  }
43161
- return path8;
43161
+ return path10;
43162
43162
  };
43163
43163
  var folderAdd = function(name2, createFolders) {
43164
43164
  createFolders = typeof createFolders !== "undefined" ? createFolders : defaults2.createFolders;
@@ -44160,9 +44160,9 @@ var require_lib4 = __commonJS({
44160
44160
  // src/cli.ts
44161
44161
  import dns from "node:dns";
44162
44162
  import net2 from "node:net";
44163
- import fs9 from "node:fs";
44164
- import os2 from "node:os";
44165
- import path7 from "node:path";
44163
+ import fs11 from "node:fs";
44164
+ import os4 from "node:os";
44165
+ import path9 from "node:path";
44166
44166
  import readline from "node:readline";
44167
44167
 
44168
44168
  // ../shared/src/transport_error.ts
@@ -44181,16 +44181,81 @@ function transportErrorMessage(status, parsed) {
44181
44181
  }
44182
44182
 
44183
44183
  // src/client.ts
44184
+ import fs2 from "node:fs";
44185
+ import path2 from "node:path";
44186
+
44187
+ // src/invocation.ts
44184
44188
  import fs from "node:fs";
44189
+ import os from "node:os";
44185
44190
  import path from "node:path";
44191
+ function telemetryEnabled(env = process.env) {
44192
+ return env.LOTICS_TELEMETRY === "1";
44193
+ }
44194
+ function commandPath(argv) {
44195
+ const parts = [];
44196
+ for (const raw of argv) {
44197
+ if (parts.length >= 3) break;
44198
+ if (raw.startsWith("-") || raw.startsWith("@") || raw.startsWith("{")) break;
44199
+ if (raw.includes("/") || raw.includes(".")) break;
44200
+ if (!/^[a-z][a-z0-9_-]*$/i.test(raw)) break;
44201
+ if (isResourceId(raw)) break;
44202
+ parts.push(raw);
44203
+ }
44204
+ return parts.length > 0 ? parts.join(".") : "unknown";
44205
+ }
44206
+ function isResourceId(token) {
44207
+ const match2 = /^[a-z]{2,5}_([A-Za-z0-9]{8,})$/.exec(token);
44208
+ return match2 !== null && /[A-Z0-9]/.test(match2[1]);
44209
+ }
44210
+ function userAgent(version2) {
44211
+ return `lotics-cli/${version2} node/${process.version} ${os.platform()}`;
44212
+ }
44213
+ var invocation = null;
44214
+ function setInvocation(command, session, version2) {
44215
+ invocation = { command, session, userAgent: userAgent(version2) };
44216
+ }
44217
+ function getInvocation() {
44218
+ return invocation;
44219
+ }
44220
+ var IDLE_GAP_MS = 30 * 60 * 1e3;
44221
+ function sessionStorePath() {
44222
+ return path.join(os.homedir(), ".lotics", "session.json");
44223
+ }
44224
+ function readSession(file2) {
44225
+ try {
44226
+ const parsed = JSON.parse(fs.readFileSync(file2, "utf-8"));
44227
+ if (typeof parsed !== "object" || parsed === null) return null;
44228
+ const { id, last_seen } = parsed;
44229
+ if (typeof id !== "string" || typeof last_seen !== "number") return null;
44230
+ return { id, last_seen };
44231
+ } catch {
44232
+ return null;
44233
+ }
44234
+ }
44235
+ function sessionId(now = Date.now(), env = process.env) {
44236
+ if (!telemetryEnabled(env)) return null;
44237
+ const harnessSession = env.CLAUDE_CODE_SESSION_ID;
44238
+ if (harnessSession !== void 0 && harnessSession !== "") return harnessSession;
44239
+ const file2 = sessionStorePath();
44240
+ const stored = readSession(file2);
44241
+ const id = stored !== null && now - stored.last_seen < IDLE_GAP_MS ? stored.id : `cli_${now.toString(36)}${Math.random().toString(36).slice(2, 10)}`;
44242
+ try {
44243
+ fs.mkdirSync(path.dirname(file2), { recursive: true });
44244
+ fs.writeFileSync(file2, JSON.stringify({ id, last_seen: now }), { mode: 384 });
44245
+ } catch {
44246
+ }
44247
+ return id;
44248
+ }
44249
+
44250
+ // src/client.ts
44186
44251
  function findAvailableFilename(dir, filename, reserved) {
44187
44252
  const isTaken = (name2) => {
44188
- const full = path.join(dir, name2);
44253
+ const full = path2.join(dir, name2);
44189
44254
  if (reserved?.has(full)) return true;
44190
- return fs.existsSync(full);
44255
+ return fs2.existsSync(full);
44191
44256
  };
44192
44257
  const claim = (name2) => {
44193
- reserved?.add(path.join(dir, name2));
44258
+ reserved?.add(path2.join(dir, name2));
44194
44259
  return name2;
44195
44260
  };
44196
44261
  if (!isTaken(filename)) return claim(filename);
@@ -44219,7 +44284,7 @@ var MIME_MAP = {
44219
44284
  ".zip": "application/zip"
44220
44285
  };
44221
44286
  function getMimeType(filename) {
44222
- const ext = path.extname(filename).toLowerCase();
44287
+ const ext = path2.extname(filename).toLowerCase();
44223
44288
  return MIME_MAP[ext] ?? "application/octet-stream";
44224
44289
  }
44225
44290
  var API_BASE_URL = process.env.LOTICS_API_URL ?? "https://api.lotics.ai";
@@ -44250,9 +44315,21 @@ var LoticsClient = class {
44250
44315
  }
44251
44316
  throw new Error(`${response.status}: ${message2}`);
44252
44317
  }
44318
+ /**
44319
+ * The backend's `log()` middleware registers `user-agent` and
44320
+ * `x-posthog-session-id` onto the per-request Logger, so they ride EVERY log
44321
+ * line that request emits — the validation 400, the tool error, the timing.
44322
+ * Sending them is therefore the whole of the correlation work: it turns an
44323
+ * anonymous API-key request into "`app workflow set`, from cli 0.117.0, the
44324
+ * fourth command of this session".
44325
+ */
44253
44326
  buildHeaders() {
44327
+ const invocation2 = getInvocation();
44254
44328
  const headers = {
44255
- "Authorization": `Bearer ${this.apiKey}`
44329
+ "Authorization": `Bearer ${this.apiKey}`,
44330
+ // Unversioned when the SDK is used as a library — there is no CLI process
44331
+ // whose version to name, and a wrong one is worse than none.
44332
+ "user-agent": invocation2?.userAgent ?? "lotics-cli"
44256
44333
  };
44257
44334
  if (this.workspaceId) {
44258
44335
  headers["x-workspace-id"] = this.workspaceId;
@@ -44260,10 +44337,16 @@ var LoticsClient = class {
44260
44337
  if (this.viewAsMemberId) {
44261
44338
  headers["x-view-as-member-id"] = this.viewAsMemberId;
44262
44339
  }
44340
+ if (invocation2) {
44341
+ headers["x-lotics-cli-command"] = invocation2.command;
44342
+ if (invocation2.session !== null) {
44343
+ headers["x-posthog-session-id"] = invocation2.session;
44344
+ }
44345
+ }
44263
44346
  return headers;
44264
44347
  }
44265
- async request(method, path8, body) {
44266
- const url2 = `${this.baseUrl}${path8}`;
44348
+ async request(method, path10, body) {
44349
+ const url2 = `${this.baseUrl}${path10}`;
44267
44350
  const headers = this.buildHeaders();
44268
44351
  const init = { method, headers };
44269
44352
  if (body !== void 0) {
@@ -44744,7 +44827,8 @@ var LoticsClient = class {
44744
44827
  ...body.inputs ? { inputs: body.inputs } : {},
44745
44828
  ...body.outputs ? { outputs: body.outputs } : {},
44746
44829
  ...body.name ? { name: body.name } : {},
44747
- ...body.description ? { description: body.description } : {}
44830
+ ...body.description ? { description: body.description } : {},
44831
+ ...body.expected_body_sha ? { expected_body_sha: body.expected_body_sha } : {}
44748
44832
  });
44749
44833
  }
44750
44834
  /**
@@ -44755,8 +44839,13 @@ var LoticsClient = class {
44755
44839
  * deploy validates the manifest. Note: `apps.queries` is manifest-authoritative,
44756
44840
  * so the next `lotics app deploy` overwrites this from the manifest.
44757
44841
  */
44758
- async setAppQuery(app_id, alias, declaration) {
44759
- return this.execute("set_app_query", { app_id, alias, declaration });
44842
+ async setAppQuery(app_id, alias, declaration, expected_sha) {
44843
+ return this.execute("set_app_query", {
44844
+ app_id,
44845
+ alias,
44846
+ declaration,
44847
+ ...expected_sha ? { expected_sha } : {}
44848
+ });
44760
44849
  }
44761
44850
  /**
44762
44851
  * Bind (create or replace) an app agent by alias via the `set_app_agent` tool
@@ -44955,6 +45044,7 @@ var LoticsClient = class {
44955
45044
  formData.append("capabilities", JSON.stringify(args.capabilities));
44956
45045
  }
44957
45046
  formData.append("workflow_aliases", JSON.stringify(args.workflow_aliases ?? []));
45047
+ formData.append("agent_aliases", JSON.stringify(args.agent_aliases ?? []));
44958
45048
  formData.append("query_aliases", JSON.stringify(args.query_aliases ?? []));
44959
45049
  const url2 = `${this.baseUrl}/v1/apps/${encodeURIComponent(args.app_id)}/versions`;
44960
45050
  const response = await fetch(url2, {
@@ -44981,8 +45071,8 @@ var LoticsClient = class {
44981
45071
  throw new Error(`${response.status}: Failed to download file`);
44982
45072
  }
44983
45073
  const buffer = Buffer.from(await response.arrayBuffer());
44984
- const absolutePath = path.resolve(outputPath);
44985
- await fs.promises.writeFile(absolutePath, buffer);
45074
+ const absolutePath = path2.resolve(outputPath);
45075
+ await fs2.promises.writeFile(absolutePath, buffer);
44986
45076
  return absolutePath;
44987
45077
  }
44988
45078
  async downloadFileById(fileId, outputDir, options) {
@@ -44998,10 +45088,10 @@ var LoticsClient = class {
44998
45088
  const match2 = disposition.match(/filename="?([^";\n]+)"?/);
44999
45089
  const originalFilename = match2?.[1] ?? fileId;
45000
45090
  const buffer = Buffer.from(await response.arrayBuffer());
45001
- const dir = outputDir ? path.resolve(outputDir) : process.cwd();
45091
+ const dir = outputDir ? path2.resolve(outputDir) : process.cwd();
45002
45092
  const filename = findAvailableFilename(dir, originalFilename, options?.reserved);
45003
- const absolutePath = path.join(dir, filename);
45004
- await fs.promises.writeFile(absolutePath, buffer);
45093
+ const absolutePath = path2.join(dir, filename);
45094
+ await fs2.promises.writeFile(absolutePath, buffer);
45005
45095
  return { path: absolutePath, filename };
45006
45096
  }
45007
45097
  async downloadRecordFiles(recordId, fieldKey, outputDir) {
@@ -45029,9 +45119,9 @@ var LoticsClient = class {
45029
45119
  async uploadFiles(filePaths, options) {
45030
45120
  const formData = new FormData();
45031
45121
  for (let i2 = 0; i2 < filePaths.length; i2++) {
45032
- const absolutePath = path.resolve(filePaths[i2]);
45033
- const buffer = await fs.promises.readFile(absolutePath);
45034
- const filename = options?.filenames?.[i2] ?? path.basename(absolutePath);
45122
+ const absolutePath = path2.resolve(filePaths[i2]);
45123
+ const buffer = await fs2.promises.readFile(absolutePath);
45124
+ const filename = options?.filenames?.[i2] ?? path2.basename(absolutePath);
45035
45125
  const mimeType = getMimeType(filename);
45036
45126
  const blob = new Blob([buffer], { type: mimeType });
45037
45127
  formData.append("file", blob, filename);
@@ -45047,24 +45137,189 @@ var LoticsClient = class {
45047
45137
  }
45048
45138
  };
45049
45139
 
45140
+ // src/telemetry.ts
45141
+ import crypto from "node:crypto";
45142
+ import fs3 from "node:fs";
45143
+ import os2 from "node:os";
45144
+ import path3 from "node:path";
45145
+
45146
+ // ../shared/src/content_sha.ts
45147
+ import { createHash } from "node:crypto";
45148
+ function contentSha(text) {
45149
+ return createHash("sha256").update(text).digest("hex").slice(0, 16);
45150
+ }
45151
+ function declarationSha(value2) {
45152
+ return contentSha(JSON.stringify(canonicalize(value2)));
45153
+ }
45154
+ function canonicalize(value2) {
45155
+ if (Array.isArray(value2)) return value2.map(canonicalize);
45156
+ if (typeof value2 === "object" && value2 !== null) {
45157
+ const out = {};
45158
+ for (const key of Object.keys(value2).sort()) {
45159
+ out[key] = canonicalize(value2[key]);
45160
+ }
45161
+ return out;
45162
+ }
45163
+ return value2;
45164
+ }
45165
+
45166
+ // src/telemetry.ts
45167
+ var MAX_ERROR_CHARS = 2e3;
45168
+ var MAX_SPOOL_BYTES = 1024 * 1024;
45169
+ var MAX_FLUSH_EVENTS = 500;
45170
+ function spoolPath() {
45171
+ return path3.join(os2.homedir(), ".lotics", "telemetry.ndjson");
45172
+ }
45173
+ var stderrTail = "";
45174
+ function recordStderr(text) {
45175
+ stderrTail = (stderrTail + text).slice(-MAX_ERROR_CHARS);
45176
+ }
45177
+ function installStderrCapture() {
45178
+ const original = process.stderr.write.bind(process.stderr);
45179
+ process.stderr.write = ((chunk, encoding, cb) => {
45180
+ recordStderr(typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf-8"));
45181
+ return typeof encoding === "function" ? original(chunk, encoding) : original(chunk, encoding, cb);
45182
+ });
45183
+ }
45184
+ function stripNarration(tail) {
45185
+ return tail.split("\n").filter((line) => !line.startsWith("lotics \u2192 ")).join("\n").trim();
45186
+ }
45187
+ function argsShape(args, prefix = "", depth = 0) {
45188
+ if (depth > 6) return [];
45189
+ if (Array.isArray(args)) {
45190
+ const head = args.length > 0 ? argsShape(args[0], `${prefix}[]`, depth + 1) : [];
45191
+ return [`${prefix}:array`, ...head];
45192
+ }
45193
+ if (typeof args === "object" && args !== null) {
45194
+ const out = prefix === "" ? [] : [`${prefix}:object`];
45195
+ for (const [key, value2] of Object.entries(args)) {
45196
+ out.push(...argsShape(value2, prefix === "" ? key : `${prefix}.${key}`, depth + 1));
45197
+ }
45198
+ return out;
45199
+ }
45200
+ return prefix === "" ? [] : [`${prefix}:${args === null ? "null" : typeof args}`];
45201
+ }
45202
+ var pendingArgs = null;
45203
+ function noteToolArgs(args) {
45204
+ if (!telemetryEnabled()) return;
45205
+ pendingArgs = { args_sha: declarationSha(args), args_shape: argsShape(args) };
45206
+ }
45207
+ var pendingScope = {};
45208
+ function noteScope(scope) {
45209
+ if (!telemetryEnabled()) return;
45210
+ pendingScope = { ...pendingScope, ...scope };
45211
+ }
45212
+ function buildEvent(exitCode, durationMs, now) {
45213
+ const invocation2 = getInvocation();
45214
+ if (invocation2 === null || invocation2.session === null) return null;
45215
+ const message2 = exitCode === 0 ? "" : stripNarration(stderrTail);
45216
+ return {
45217
+ event_id: crypto.randomUUID(),
45218
+ ts: now.toISOString(),
45219
+ cli_session_id: invocation2.session,
45220
+ cli_command: invocation2.command,
45221
+ // `--flag=value` splits — the name is a fact about the command, the value is data.
45222
+ cli_flags: process.argv.slice(2).filter((a) => a.startsWith("-")).map((a) => a.split("=")[0]),
45223
+ exit_code: exitCode,
45224
+ duration_ms: durationMs,
45225
+ cli_version: invocation2.userAgent.split(" ")[0].replace("lotics-cli/", ""),
45226
+ ...message2 !== "" && { error_message: message2 },
45227
+ ...pendingArgs,
45228
+ ...pendingScope
45229
+ };
45230
+ }
45231
+ function appendEvent(event) {
45232
+ const file2 = spoolPath();
45233
+ try {
45234
+ fs3.mkdirSync(path3.dirname(file2), { recursive: true });
45235
+ if ((fs3.statSync(file2, { throwIfNoEntry: false })?.size ?? 0) > MAX_SPOOL_BYTES) {
45236
+ fs3.rmSync(file2, { force: true });
45237
+ }
45238
+ fs3.appendFileSync(file2, `${JSON.stringify(event)}
45239
+ `, { mode: 384 });
45240
+ } catch {
45241
+ }
45242
+ }
45243
+ function readSpool() {
45244
+ try {
45245
+ const events = [];
45246
+ const lines = [];
45247
+ for (const line of fs3.readFileSync(spoolPath(), "utf-8").split("\n")) {
45248
+ if (line.trim() === "") continue;
45249
+ try {
45250
+ events.push(JSON.parse(line));
45251
+ lines.push(line);
45252
+ } catch {
45253
+ }
45254
+ }
45255
+ return { events, lines };
45256
+ } catch {
45257
+ return { events: [], lines: [] };
45258
+ }
45259
+ }
45260
+ function clearSpool(sent) {
45261
+ try {
45262
+ const current = fs3.readFileSync(spoolPath(), "utf-8");
45263
+ fs3.writeFileSync(spoolPath(), current.startsWith(sent) ? current.slice(sent.length) : "", {
45264
+ mode: 384
45265
+ });
45266
+ } catch {
45267
+ }
45268
+ }
45269
+ function installTelemetry(startedAt2 = Date.now()) {
45270
+ if (!telemetryEnabled()) return;
45271
+ installStderrCapture();
45272
+ process.on("exit", (code) => {
45273
+ const event = buildEvent(code, Date.now() - startedAt2, /* @__PURE__ */ new Date());
45274
+ if (event !== null) appendEvent(event);
45275
+ });
45276
+ }
45277
+ function flushSpool(post, minEvents = 5) {
45278
+ if (!telemetryEnabled()) return;
45279
+ const { events, lines } = readSpool();
45280
+ if (events.length < minEvents) return;
45281
+ const batch = events.slice(0, MAX_FLUSH_EVENTS);
45282
+ const sent = `${lines.slice(0, batch.length).join("\n")}
45283
+ `;
45284
+ void post(batch).then(() => {
45285
+ clearSpool(sent);
45286
+ }).catch(() => {
45287
+ });
45288
+ }
45289
+ async function postEvents(baseUrl, apiKey, events, timeoutMs = 5e3) {
45290
+ const controller = new AbortController();
45291
+ const timer2 = setTimeout(() => controller.abort(), timeoutMs);
45292
+ try {
45293
+ const res = await fetch(`${baseUrl}/v1/cli/telemetry`, {
45294
+ method: "POST",
45295
+ headers: { "Authorization": `Bearer ${apiKey}`, "Content-Type": "application/json" },
45296
+ body: JSON.stringify({ events }),
45297
+ signal: controller.signal
45298
+ });
45299
+ if (!res.ok) throw new Error(`telemetry flush: ${res.status}`);
45300
+ } finally {
45301
+ clearTimeout(timer2);
45302
+ }
45303
+ }
45304
+
45050
45305
  // src/config.ts
45051
- import fs2 from "node:fs";
45052
- import path2 from "node:path";
45053
- import os from "node:os";
45306
+ import fs4 from "node:fs";
45307
+ import path4 from "node:path";
45308
+ import os3 from "node:os";
45054
45309
  function globalConfigFile() {
45055
- return path2.join(os.homedir(), ".lotics", "config.json");
45310
+ return path4.join(os3.homedir(), ".lotics", "config.json");
45056
45311
  }
45057
45312
  function configFileForScope(scope) {
45058
45313
  if (scope === "local") {
45059
- return path2.join(process.cwd(), ".lotics", "config.json");
45314
+ return path4.join(process.cwd(), ".lotics", "config.json");
45060
45315
  }
45061
45316
  let dir = process.cwd();
45062
45317
  for (; ; ) {
45063
- const candidate = path2.join(dir, ".lotics", "config.json");
45064
- if (fs2.existsSync(candidate)) {
45318
+ const candidate = path4.join(dir, ".lotics", "config.json");
45319
+ if (fs4.existsSync(candidate)) {
45065
45320
  return candidate;
45066
45321
  }
45067
- const parent = path2.dirname(dir);
45322
+ const parent = path4.dirname(dir);
45068
45323
  if (parent === dir) {
45069
45324
  break;
45070
45325
  }
@@ -45074,25 +45329,25 @@ function configFileForScope(scope) {
45074
45329
  }
45075
45330
  function readConfigFile(file2) {
45076
45331
  try {
45077
- const raw = fs2.readFileSync(file2, "utf-8");
45332
+ const raw = fs4.readFileSync(file2, "utf-8");
45078
45333
  return JSON.parse(raw);
45079
45334
  } catch {
45080
45335
  return null;
45081
45336
  }
45082
45337
  }
45083
45338
  function writeConfigFile(file2, config2) {
45084
- fs2.mkdirSync(path2.dirname(file2), { recursive: true, mode: 448 });
45339
+ fs4.mkdirSync(path4.dirname(file2), { recursive: true, mode: 448 });
45085
45340
  const tmp = `${file2}.${process.pid}.tmp`;
45086
- fs2.writeFileSync(tmp, JSON.stringify(config2, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
45087
- fs2.chmodSync(tmp, 384);
45088
- fs2.renameSync(tmp, file2);
45341
+ fs4.writeFileSync(tmp, JSON.stringify(config2, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
45342
+ fs4.chmodSync(tmp, 384);
45343
+ fs4.renameSync(tmp, file2);
45089
45344
  }
45090
45345
  function saveConfig(config2, scope = "auto") {
45091
45346
  writeConfigFile(configFileForScope(scope), config2);
45092
45347
  }
45093
45348
  function deleteConfig(scope = "auto") {
45094
45349
  try {
45095
- fs2.unlinkSync(configFileForScope(scope));
45350
+ fs4.unlinkSync(configFileForScope(scope));
45096
45351
  } catch {
45097
45352
  }
45098
45353
  }
@@ -45319,14 +45574,14 @@ Update available: ${current} \u2192 ${latest}`);
45319
45574
  // src/version.ts
45320
45575
  import { readFileSync } from "node:fs";
45321
45576
  import { fileURLToPath } from "node:url";
45322
- import path3 from "node:path";
45323
- var pkgPath = path3.resolve(path3.dirname(fileURLToPath(import.meta.url)), "../../package.json");
45577
+ import path5 from "node:path";
45578
+ var pkgPath = path5.resolve(path5.dirname(fileURLToPath(import.meta.url)), "../../package.json");
45324
45579
  var pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
45325
45580
  var VERSION = pkg.version;
45326
45581
 
45327
45582
  // src/app_commands.ts
45328
- import fs4 from "node:fs";
45329
- import path5 from "node:path";
45583
+ import fs6 from "node:fs";
45584
+ import path7 from "node:path";
45330
45585
  import { spawn as spawn2 } from "node:child_process";
45331
45586
  import { randomUUID } from "node:crypto";
45332
45587
  import { tmpdir } from "node:os";
@@ -46559,7 +46814,7 @@ function createUploadRelay(now = Date.now) {
46559
46814
  }
46560
46815
 
46561
46816
  // src/dev/file_relay.ts
46562
- import { createHash } from "node:crypto";
46817
+ import { createHash as createHash2 } from "node:crypto";
46563
46818
  var TTL_MS = 24 * 60 * 60 * 1e3;
46564
46819
  var MAX_ENTRIES = 5e3;
46565
46820
  var PRESIGNED_KEYS = /* @__PURE__ */ new Set(["url", "thumbnail_url", "preview_url"]);
@@ -46567,7 +46822,7 @@ var isFileObject = (o) => typeof o.filename === "string" && typeof o.mime_type =
46567
46822
  var isRemoteUrl = (v) => typeof v === "string" && /^https?:\/\//i.test(v);
46568
46823
  function createFileRelay(wrapperOrigin, now = Date.now) {
46569
46824
  const seen = /* @__PURE__ */ new Map();
46570
- const tokenFor = (url2) => createHash("sha256").update(url2).digest("hex").slice(0, 24);
46825
+ const tokenFor = (url2) => createHash2("sha256").update(url2).digest("hex").slice(0, 24);
46571
46826
  const remember = (url2) => {
46572
46827
  const token = tokenFor(url2);
46573
46828
  seen.set(token, { url: url2, at: now() });
@@ -46681,7 +46936,7 @@ async function startDevServer(args) {
46681
46936
  if (req.method === "POST" && url2 === "/_rpc") {
46682
46937
  try {
46683
46938
  const body = await readJson(req);
46684
- const startedAt = Date.now();
46939
+ const startedAt2 = Date.now();
46685
46940
  const result = await dispatchRpc(
46686
46941
  args.client,
46687
46942
  {
@@ -46691,7 +46946,7 @@ async function startDevServer(args) {
46691
46946
  },
46692
46947
  { commentsEnabled: args.commentsEnabled }
46693
46948
  );
46694
- const ms = Date.now() - startedAt;
46949
+ const ms = Date.now() - startedAt2;
46695
46950
  process.stderr.write(`[rpc] ${body.op} ${ms}ms
46696
46951
  `);
46697
46952
  res.writeHead(200, { "Content-Type": "application/json" });
@@ -46844,13 +47099,13 @@ async function startDevServer(args) {
46844
47099
  const chunks = [];
46845
47100
  for await (const chunk of req) chunks.push(chunk);
46846
47101
  const bytes = Buffer.concat(chunks);
46847
- const startedAt = Date.now();
47102
+ const startedAt2 = Date.now();
46848
47103
  const upstream = await fetch(destination, {
46849
47104
  method: "PUT",
46850
47105
  body: bytes,
46851
47106
  headers: { "Content-Type": req.headers["content-type"] ?? "application/octet-stream" }
46852
47107
  });
46853
- const ms = Date.now() - startedAt;
47108
+ const ms = Date.now() - startedAt2;
46854
47109
  if (upstream.ok) {
46855
47110
  uploads.settle(fileId);
46856
47111
  process.stderr.write(`[upload] ${fileId} ${bytes.length}B ${ms}ms
@@ -47533,8 +47788,8 @@ export type AppRoles = typeof ROLE;
47533
47788
  }
47534
47789
 
47535
47790
  // src/app_workflow_check.ts
47536
- import fs3 from "node:fs";
47537
- import path4 from "node:path";
47791
+ import fs5 from "node:fs";
47792
+ import path6 from "node:path";
47538
47793
  import { createRequire } from "node:module";
47539
47794
 
47540
47795
  // ../shared/src/parse_workflow_js.ts
@@ -48306,10 +48561,10 @@ function mergeDefs(...defs) {
48306
48561
  function cloneDef(schema) {
48307
48562
  return mergeDefs(schema._zod.def);
48308
48563
  }
48309
- function getElementAtPath(obj, path8) {
48310
- if (!path8)
48564
+ function getElementAtPath(obj, path10) {
48565
+ if (!path10)
48311
48566
  return obj;
48312
- return path8.reduce((acc, key) => acc?.[key], obj);
48567
+ return path10.reduce((acc, key) => acc?.[key], obj);
48313
48568
  }
48314
48569
  function promiseAllObject(promisesObj) {
48315
48570
  const keys3 = Object.keys(promisesObj);
@@ -48718,11 +48973,11 @@ function explicitlyAborted(x2, startIndex = 0) {
48718
48973
  }
48719
48974
  return false;
48720
48975
  }
48721
- function prefixIssues(path8, issues) {
48976
+ function prefixIssues(path10, issues) {
48722
48977
  return issues.map((iss) => {
48723
48978
  var _a4;
48724
48979
  (_a4 = iss).path ?? (_a4.path = []);
48725
- iss.path.unshift(path8);
48980
+ iss.path.unshift(path10);
48726
48981
  return iss;
48727
48982
  });
48728
48983
  }
@@ -48869,16 +49124,16 @@ function flattenError(error52, mapper = (issue2) => issue2.message) {
48869
49124
  }
48870
49125
  function formatError(error52, mapper = (issue2) => issue2.message) {
48871
49126
  const fieldErrors = { _errors: [] };
48872
- const processError = (error53, path8 = []) => {
49127
+ const processError = (error53, path10 = []) => {
48873
49128
  for (const issue2 of error53.issues) {
48874
49129
  if (issue2.code === "invalid_union" && issue2.errors.length) {
48875
- issue2.errors.map((issues) => processError({ issues }, [...path8, ...issue2.path]));
49130
+ issue2.errors.map((issues) => processError({ issues }, [...path10, ...issue2.path]));
48876
49131
  } else if (issue2.code === "invalid_key") {
48877
- processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
49132
+ processError({ issues: issue2.issues }, [...path10, ...issue2.path]);
48878
49133
  } else if (issue2.code === "invalid_element") {
48879
- processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
49134
+ processError({ issues: issue2.issues }, [...path10, ...issue2.path]);
48880
49135
  } else {
48881
- const fullpath = [...path8, ...issue2.path];
49136
+ const fullpath = [...path10, ...issue2.path];
48882
49137
  if (fullpath.length === 0) {
48883
49138
  fieldErrors._errors.push(mapper(issue2));
48884
49139
  } else {
@@ -48905,17 +49160,17 @@ function formatError(error52, mapper = (issue2) => issue2.message) {
48905
49160
  }
48906
49161
  function treeifyError(error52, mapper = (issue2) => issue2.message) {
48907
49162
  const result = { errors: [] };
48908
- const processError = (error53, path8 = []) => {
49163
+ const processError = (error53, path10 = []) => {
48909
49164
  var _a4, _b2;
48910
49165
  for (const issue2 of error53.issues) {
48911
49166
  if (issue2.code === "invalid_union" && issue2.errors.length) {
48912
- issue2.errors.map((issues) => processError({ issues }, [...path8, ...issue2.path]));
49167
+ issue2.errors.map((issues) => processError({ issues }, [...path10, ...issue2.path]));
48913
49168
  } else if (issue2.code === "invalid_key") {
48914
- processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
49169
+ processError({ issues: issue2.issues }, [...path10, ...issue2.path]);
48915
49170
  } else if (issue2.code === "invalid_element") {
48916
- processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
49171
+ processError({ issues: issue2.issues }, [...path10, ...issue2.path]);
48917
49172
  } else {
48918
- const fullpath = [...path8, ...issue2.path];
49173
+ const fullpath = [...path10, ...issue2.path];
48919
49174
  if (fullpath.length === 0) {
48920
49175
  result.errors.push(mapper(issue2));
48921
49176
  continue;
@@ -48947,8 +49202,8 @@ function treeifyError(error52, mapper = (issue2) => issue2.message) {
48947
49202
  }
48948
49203
  function toDotPath(_path) {
48949
49204
  const segs = [];
48950
- const path8 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
48951
- for (const seg of path8) {
49205
+ const path10 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
49206
+ for (const seg of path10) {
48952
49207
  if (typeof seg === "number")
48953
49208
  segs.push(`[${seg}]`);
48954
49209
  else if (typeof seg === "symbol")
@@ -61640,13 +61895,13 @@ function resolveRef(ref2, ctx) {
61640
61895
  if (!ref2.startsWith("#")) {
61641
61896
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
61642
61897
  }
61643
- const path8 = ref2.slice(1).split("/").filter(Boolean);
61644
- if (path8.length === 0) {
61898
+ const path10 = ref2.slice(1).split("/").filter(Boolean);
61899
+ if (path10.length === 0) {
61645
61900
  return ctx.rootSchema;
61646
61901
  }
61647
61902
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
61648
- if (path8[0] === defsKey) {
61649
- const key = path8[1];
61903
+ if (path10[0] === defsKey) {
61904
+ const key = path10[1];
61650
61905
  if (!key || !ctx.defs[key]) {
61651
61906
  throw new Error(`Reference not found: ${ref2}`);
61652
61907
  }
@@ -64478,6 +64733,7 @@ var PICKER_MODEL_TIERS = MODEL_TIERS.filter(
64478
64733
  (tier) => tier !== UTILITY_MODEL_TIER
64479
64734
  );
64480
64735
  var EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
64736
+ var PREFIX_CACHE_TTLS = ["5m", "1h"];
64481
64737
 
64482
64738
  // ../shared/src/schemas/apps.ts
64483
64739
  var queryColumnTypeSchema = zod_default.enum([
@@ -64903,6 +65159,9 @@ var appWorkflowDeclarationSchema = zod_default.object({
64903
65159
  ),
64904
65160
  description: zod_default.string().optional().describe(
64905
65161
  "What this workflow does, in one line \u2014 read by an agent choosing between the app's aliases, the same job a query's `description` does. `lotics app workflow set` sends it, so it lives beside the body in version control; omitted, the workflow keeps the description already on it."
65162
+ ),
65163
+ body_sha: zod_default.string().optional().describe(
65164
+ "Fingerprint of the body currently bound to this alias, written by the server on every accepted `set_app_workflow`. Never sent by a caller \u2014 it is the value a caller's `expected_body_sha` is checked against, so that a push built on a stale copy of the body is refused instead of silently overwriting whatever replaced it. Absent on a binding last written before the field existed, where no comparison is possible."
64906
65165
  )
64907
65166
  });
64908
65167
  var appWorkflowContractSchema = zod_default.object({
@@ -64941,6 +65200,9 @@ var appAgentDeclarationSchema = zod_default.object({
64941
65200
  effort_level: zod_default.enum(EFFORT_LEVELS).optional().describe(
64942
65201
  "Reasoning depth for adaptive-thinking tiers \u2014 one of the chosen tier's supported levels (validated against model_tier at declare time). Omit to use the model default; ignored on tiers without adaptive thinking."
64943
65202
  ),
65203
+ prefix_cache_ttl: zod_default.enum(PREFIX_CACHE_TTLS).optional().describe(
65204
+ `How long this agent's prompt-cache prefix (tools + system block) is kept warm. Omit \u2014 the default (5m, Anthropic's own) is right for essentially every agent. "1h" doubles the write price (2x the input rate against 1.25x) to buy only the 5m..1h band, and an entry nothing re-reads inside the hour is paid for twice over; declining it still leaves the prefix cached at the default. Set it only from measured cadence, never a guess.`
65205
+ ),
64944
65206
  inputs: zod_default.record(zod_default.string(), appWorkflowInputSchema).optional().describe(
64945
65207
  "Typed input schema for one run. Keys are input names; values declare type + constraints. The server validates the run payload against this before invoking; CLI codegen emits a typed `useAgentRun<alias>` signature. Omit for an untyped payload."
64946
65208
  ),
@@ -65782,10 +66044,10 @@ async function reduce2(arr, callback, initial2) {
65782
66044
  if (arguments.length < 3) {
65783
66045
  throw new Error("reduce: initial value is required \u2014 reduce(arr, (acc, x) => ..., initial)");
65784
66046
  }
65785
- const startedAt = Date.now();
66047
+ const startedAt2 = Date.now();
65786
66048
  let acc = initial2;
65787
66049
  for (let i2 = 0; i2 < arr.length; i2++) {
65788
- if (i2 % 1024 === 0 && Date.now() - startedAt > REDUCE_TIME_BUDGET_MS) {
66050
+ if (i2 % 1024 === 0 && Date.now() - startedAt2 > REDUCE_TIME_BUDGET_MS) {
65789
66051
  throw new Error(
65790
66052
  `reduce: exceeded time budget of ${REDUCE_TIME_BUDGET_MS}ms after ${i2} iterations`
65791
66053
  );
@@ -66313,9 +66575,9 @@ __export(expression_object_exports, {
66313
66575
  pick: () => pick3,
66314
66576
  values: () => values2
66315
66577
  });
66316
- function getNestedValue(obj, path8) {
66578
+ function getNestedValue(obj, path10) {
66317
66579
  if (obj == null) return void 0;
66318
- const parts = path8.split(".");
66580
+ const parts = path10.split(".");
66319
66581
  let value2 = obj;
66320
66582
  for (const part of parts) {
66321
66583
  if (value2 == null) return void 0;
@@ -66354,11 +66616,11 @@ function entries(obj) {
66354
66616
  }
66355
66617
  return Object.entries(obj);
66356
66618
  }
66357
- function get(obj, path8, defaultValue) {
66358
- if (typeof path8 !== "string") {
66619
+ function get(obj, path10, defaultValue) {
66620
+ if (typeof path10 !== "string") {
66359
66621
  throw new Error("get: path must be a string");
66360
66622
  }
66361
- const value2 = getNestedValue(obj, path8);
66623
+ const value2 = getNestedValue(obj, path10);
66362
66624
  return value2 !== void 0 ? value2 : defaultValue;
66363
66625
  }
66364
66626
  function pick3(obj, fields) {
@@ -68241,17 +68503,17 @@ function readFromChanges(chain2) {
68241
68503
  if (first3.kind === "bracket-number" || first3.kind === "bracket-expr") {
68242
68504
  fail(first3.node, "changes is keyed by field name, not numeric index.");
68243
68505
  }
68244
- const path8 = [{ at: "key", key: first3.key }];
68506
+ const path10 = [{ at: "key", key: first3.key }];
68245
68507
  for (const seg of segments.slice(1)) {
68246
68508
  if (seg.kind === "bracket-number") {
68247
- path8.push({ at: "index", index: seg.index });
68509
+ path10.push({ at: "index", index: seg.index });
68248
68510
  } else if (seg.kind === "bracket-expr") {
68249
- path8.push({ at: "index", index: seg.expr });
68511
+ path10.push({ at: "index", index: seg.expr });
68250
68512
  } else {
68251
- path8.push({ at: "key", key: seg.key });
68513
+ path10.push({ at: "key", key: seg.key });
68252
68514
  }
68253
68515
  }
68254
- return { kind: "read", source: { from: "trigger_changes" }, path: path8 };
68516
+ return { kind: "read", source: { from: "trigger_changes" }, path: path10 };
68255
68517
  });
68256
68518
  }
68257
68519
  var TRIGGER_RECORD_VIEWS = /* @__PURE__ */ new Map([
@@ -68269,17 +68531,17 @@ function readFromTrigger(chain2) {
68269
68531
  return view === "trigger_changes" ? readFromChanges(lowered) : readFromRecord(view, lowered);
68270
68532
  }
68271
68533
  return wrapOptionalGuards(chain2, (segments) => {
68272
- const path8 = [];
68534
+ const path10 = [];
68273
68535
  for (const seg of segments) {
68274
68536
  if (seg.kind === "bracket-number") {
68275
- path8.push({ at: "index", index: seg.index });
68537
+ path10.push({ at: "index", index: seg.index });
68276
68538
  } else if (seg.kind === "bracket-expr") {
68277
- path8.push({ at: "index", index: seg.expr });
68539
+ path10.push({ at: "index", index: seg.expr });
68278
68540
  } else {
68279
- path8.push({ at: "key", key: seg.key });
68541
+ path10.push({ at: "key", key: seg.key });
68280
68542
  }
68281
68543
  }
68282
- return { kind: "read", source: { from: "trigger" }, path: path8 };
68544
+ return { kind: "read", source: { from: "trigger" }, path: path10 };
68283
68545
  });
68284
68546
  }
68285
68547
  function readFromRuntime(chain2) {
@@ -68301,17 +68563,17 @@ function readFromRuntime(chain2) {
68301
68563
  const parked = PARKED_RUNTIME_KEYS.get(key);
68302
68564
  if (parked) fail(first3.node, parked, parkedRuntimeKeyConstruct(key));
68303
68565
  const source = { from: "runtime", key };
68304
- const path8 = [];
68566
+ const path10 = [];
68305
68567
  for (const seg of segments.slice(1)) {
68306
68568
  if (seg.kind === "bracket-number") {
68307
- path8.push({ at: "index", index: seg.index });
68569
+ path10.push({ at: "index", index: seg.index });
68308
68570
  } else if (seg.kind === "bracket-expr") {
68309
- path8.push({ at: "index", index: seg.expr });
68571
+ path10.push({ at: "index", index: seg.expr });
68310
68572
  } else {
68311
- path8.push({ at: "key", key: seg.key });
68573
+ path10.push({ at: "key", key: seg.key });
68312
68574
  }
68313
68575
  }
68314
- return { kind: "read", source, path: path8 };
68576
+ return { kind: "read", source, path: path10 };
68315
68577
  });
68316
68578
  }
68317
68579
  function readFromForeachItem(chain2) {
@@ -68347,17 +68609,17 @@ function readFromLexical(name2, chain2) {
68347
68609
  }
68348
68610
  function readFromStepOutput(stepId, chain2) {
68349
68611
  return wrapOptionalGuards(chain2, (segments) => {
68350
- const path8 = [];
68612
+ const path10 = [];
68351
68613
  for (const seg of segments) {
68352
68614
  if (seg.kind === "bracket-number") {
68353
- path8.push({ at: "index", index: seg.index });
68615
+ path10.push({ at: "index", index: seg.index });
68354
68616
  } else if (seg.kind === "bracket-expr") {
68355
- path8.push({ at: "index", index: seg.expr });
68617
+ path10.push({ at: "index", index: seg.expr });
68356
68618
  } else {
68357
- path8.push({ at: "key", key: seg.key });
68619
+ path10.push({ at: "key", key: seg.key });
68358
68620
  }
68359
68621
  }
68360
- return { kind: "read", source: { from: "step_output", step_id: stepId }, path: path8 };
68622
+ return { kind: "read", source: { from: "step_output", step_id: stepId }, path: path10 };
68361
68623
  });
68362
68624
  }
68363
68625
  function classifyRecordPath(segs) {
@@ -70011,7 +70273,7 @@ function subsetIssues(bodySource, offset) {
70011
70273
  ];
70012
70274
  }
70013
70275
  async function loadProjectTypescript(projectDir) {
70014
- const requireFromProject = createRequire(path4.join(projectDir, "package.json"));
70276
+ const requireFromProject = createRequire(path6.join(projectDir, "package.json"));
70015
70277
  let resolved;
70016
70278
  try {
70017
70279
  resolved = requireFromProject.resolve("typescript");
@@ -70024,12 +70286,12 @@ async function loadProjectTypescript(projectDir) {
70024
70286
  return mod2.default ?? mod2;
70025
70287
  }
70026
70288
  function pathToImportUrl(p) {
70027
- const resolved = path4.resolve(p);
70289
+ const resolved = path6.resolve(p);
70028
70290
  const prefixed = resolved.startsWith("/") ? resolved : `/${resolved}`;
70029
- return `file://${prefixed.split(path4.sep).join("/")}`;
70291
+ return `file://${prefixed.split(path6.sep).join("/")}`;
70030
70292
  }
70031
70293
  function checkOneWorkflowBody(tsApi, input) {
70032
- const wrapped = fs3.readFileSync(input.bodyPath, "utf-8");
70294
+ const wrapped = fs5.readFileSync(input.bodyPath, "utf-8");
70033
70295
  const offset = bodyLineOffset(wrapped);
70034
70296
  const subset = subsetIssues(stripWorkflowHeader(wrapped), offset);
70035
70297
  if (subset) return subset;
@@ -70076,7 +70338,7 @@ function checkOneWorkflowBody(tsApi, input) {
70076
70338
  }
70077
70339
  function createCheckedSourceHost(tsApi, options, bodyPath, bodyText) {
70078
70340
  const base = tsApi.createCompilerHost(options, false);
70079
- const key = (fileName) => base.getCanonicalFileName(path4.resolve(fileName).replace(/\\/g, "/"));
70341
+ const key = (fileName) => base.getCanonicalFileName(path6.resolve(fileName).replace(/\\/g, "/"));
70080
70342
  const bodyKey = key(bodyPath);
70081
70343
  return {
70082
70344
  ...base,
@@ -70145,6 +70407,14 @@ function orderedLike(value2, template) {
70145
70407
  [...first3, ...rest2].map((k) => [k, orderedLike(from[k], tmpl[k])])
70146
70408
  );
70147
70409
  }
70410
+ function toManifestWorkflows(live) {
70411
+ return Object.fromEntries(
70412
+ Object.entries(live).map(([alias, { body_sha: _serverState, ...declaration }]) => [
70413
+ alias,
70414
+ declaration
70415
+ ])
70416
+ );
70417
+ }
70148
70418
  function toManifestAgents(agents) {
70149
70419
  return Object.fromEntries(
70150
70420
  Object.entries(agents).map(([alias, { instructions: _prose, ...typed }]) => [alias, typed])
@@ -70181,9 +70451,43 @@ function agentTypeDivergences(manifestAgents, liveAgents) {
70181
70451
  }
70182
70452
  return out;
70183
70453
  }
70184
- var WORKFLOWS_DIR = path5.join("src", "workflows");
70185
- var WORKFLOW_GLOBALS_DIR = path5.join(".lotics", "workflows");
70186
- var AGENTS_DIR = path5.join("src", "agents");
70454
+ function workflowTypeDivergences(manifestWorkflows, liveWorkflows) {
70455
+ const out = [];
70456
+ for (const [alias, declared] of Object.entries(manifestWorkflows ?? {})) {
70457
+ const live = (liveWorkflows ?? {})[alias];
70458
+ if (!live) {
70459
+ out.push(`${alias} \u2014 declared locally but NOT bound on the app`);
70460
+ continue;
70461
+ }
70462
+ for (const key of ["inputs", "outputs"]) {
70463
+ if (!sameSchema(declared[key], live[key])) {
70464
+ out.push(`${alias}.${key} \u2014 the manifest declares a schema the live binding does not have`);
70465
+ }
70466
+ }
70467
+ }
70468
+ return out;
70469
+ }
70470
+ function readSynced(projectDir) {
70471
+ try {
70472
+ const pkg2 = JSON.parse(
70473
+ fs6.readFileSync(path7.join(projectDir, "package.json"), "utf-8")
70474
+ );
70475
+ return pkg2.lotics?.synced ?? {};
70476
+ } catch {
70477
+ return {};
70478
+ }
70479
+ }
70480
+ function writeSynced(projectDir, kind, alias, sha) {
70481
+ const pkgPath2 = path7.join(projectDir, "package.json");
70482
+ const pkg2 = JSON.parse(fs6.readFileSync(pkgPath2, "utf-8"));
70483
+ if (!pkg2.lotics) return;
70484
+ pkg2.lotics.synced ??= {};
70485
+ pkg2.lotics.synced[kind] = { ...pkg2.lotics.synced[kind], [alias]: sha };
70486
+ fs6.writeFileSync(pkgPath2, JSON.stringify(pkg2, null, 2) + "\n");
70487
+ }
70488
+ var WORKFLOWS_DIR = path7.join("src", "workflows");
70489
+ var WORKFLOW_GLOBALS_DIR = path7.join(".lotics", "workflows");
70490
+ var AGENTS_DIR = path7.join("src", "agents");
70187
70491
  var WORKFLOW_TSCONFIG_EXCLUDES = ["src/workflows", ".lotics/workflows"];
70188
70492
  var LOTICS_INCLUDE_GLOB = ".lotics/**/*";
70189
70493
  var STALE_LOTICS_INCLUDES = /* @__PURE__ */ new Set([".lotics", "./.lotics", ".lotics/"]);
@@ -70193,11 +70497,8 @@ function toWorkflowDtsDeclaration(d) {
70193
70497
  function isMissingWorkflowBodyError(error52) {
70194
70498
  return /has no step source|cannot be rendered|unsupported step/i.test(error52);
70195
70499
  }
70196
- function isUnknownWorkflowAliasError(err2) {
70197
- return err2 instanceof Error && err2.message.startsWith("400:") && /has no workflow alias/i.test(err2.message);
70198
- }
70199
70500
  function workflowFileHeader(alias) {
70200
- const refPath = path5.join("..", "..", WORKFLOW_GLOBALS_DIR, `${alias}.globals.d.ts`).split(path5.sep).join("/");
70501
+ const refPath = path7.join("..", "..", WORKFLOW_GLOBALS_DIR, `${alias}.globals.d.ts`).split(path7.sep).join("/");
70201
70502
  return `/// <reference path="${refPath}" />
70202
70503
  // Auto-pulled workflow body for "${alias}". Edit the BODY between the wrapper
70203
70504
  // lines below, then check + push with:
@@ -70211,59 +70512,59 @@ export {};
70211
70512
  `;
70212
70513
  }
70213
70514
  function workflowFilePath(projectDir, alias) {
70214
- return path5.join(projectDir, WORKFLOWS_DIR, `${alias}.ts`);
70515
+ return path7.join(projectDir, WORKFLOWS_DIR, `${alias}.ts`);
70215
70516
  }
70216
70517
  var GLOBALS_SUFFIX = ".globals.d.ts";
70217
70518
  function workflowGlobalsPath(projectDir, alias) {
70218
- return path5.join(projectDir, WORKFLOW_GLOBALS_DIR, `${alias}${GLOBALS_SUFFIX}`);
70519
+ return path7.join(projectDir, WORKFLOW_GLOBALS_DIR, `${alias}${GLOBALS_SUFFIX}`);
70219
70520
  }
70220
70521
  function pruneOrphanWorkflowGlobals(projectDir, declared) {
70221
- const dir = path5.join(projectDir, WORKFLOW_GLOBALS_DIR);
70222
- if (!fs4.existsSync(dir)) return [];
70522
+ const dir = path7.join(projectDir, WORKFLOW_GLOBALS_DIR);
70523
+ if (!fs6.existsSync(dir)) return [];
70223
70524
  const removed = [];
70224
- for (const entry of fs4.readdirSync(dir)) {
70525
+ for (const entry of fs6.readdirSync(dir)) {
70225
70526
  if (!entry.endsWith(GLOBALS_SUFFIX)) continue;
70226
70527
  const alias = entry.slice(0, -GLOBALS_SUFFIX.length);
70227
70528
  if (declared.has(alias)) continue;
70228
- fs4.unlinkSync(path5.join(dir, entry));
70529
+ fs6.unlinkSync(path7.join(dir, entry));
70229
70530
  removed.push(alias);
70230
70531
  }
70231
70532
  return removed.sort();
70232
70533
  }
70233
70534
  function orphanWorkflowBodies(projectDir, declared) {
70234
- const dir = path5.join(projectDir, WORKFLOWS_DIR);
70235
- if (!fs4.existsSync(dir)) return [];
70236
- return fs4.readdirSync(dir).filter((e) => e.endsWith(".ts") && !e.endsWith(".d.ts") && !e.endsWith(".test.ts")).map((e) => e.slice(0, -".ts".length)).filter((alias) => !declared.has(alias)).sort();
70535
+ const dir = path7.join(projectDir, WORKFLOWS_DIR);
70536
+ if (!fs6.existsSync(dir)) return [];
70537
+ return fs6.readdirSync(dir).filter((e) => e.endsWith(".ts") && !e.endsWith(".d.ts") && !e.endsWith(".test.ts")).map((e) => e.slice(0, -".ts".length)).filter((alias) => !declared.has(alias)).sort();
70237
70538
  }
70238
70539
  function writeWorkflowGlobals(projectDir, alias, dts) {
70239
- const dir = path5.join(projectDir, WORKFLOW_GLOBALS_DIR);
70240
- fs4.mkdirSync(dir, { recursive: true });
70540
+ const dir = path7.join(projectDir, WORKFLOW_GLOBALS_DIR);
70541
+ fs6.mkdirSync(dir, { recursive: true });
70241
70542
  const file2 = workflowGlobalsPath(projectDir, alias);
70242
- fs4.writeFileSync(file2, `${dts.replace(/\s+$/, "")}
70543
+ fs6.writeFileSync(file2, `${dts.replace(/\s+$/, "")}
70243
70544
  `);
70244
70545
  return file2;
70245
70546
  }
70246
70547
  function writeWorkflowFile(projectDir, alias, source, envelope = { prefix: WORKFLOW_ENVELOPE_PREFIX, suffix: WORKFLOW_ENVELOPE_SUFFIX }) {
70247
- const dir = path5.join(projectDir, WORKFLOWS_DIR);
70248
- fs4.mkdirSync(dir, { recursive: true });
70548
+ const dir = path7.join(projectDir, WORKFLOWS_DIR);
70549
+ fs6.mkdirSync(dir, { recursive: true });
70249
70550
  const file2 = workflowFilePath(projectDir, alias);
70250
70551
  const body = source.replace(/\s+$/, "");
70251
- fs4.writeFileSync(file2, `${workflowFileHeader(alias)}
70552
+ fs6.writeFileSync(file2, `${workflowFileHeader(alias)}
70252
70553
  ${envelope.prefix}${body}${envelope.suffix}
70253
70554
  `);
70254
70555
  return file2;
70255
70556
  }
70256
70557
  function agentFilePath2(projectDir, alias) {
70257
- return path5.join(projectDir, ...agentFilePath(alias).split("/"));
70558
+ return path7.join(projectDir, ...agentFilePath(alias).split("/"));
70258
70559
  }
70259
70560
  var CLI_AGENT_NOTES = [
70260
70561
  "Pulled from the LIVE app row; edit here, then: lotics app agent set <alias>",
70261
70562
  "The typed fields (inputs/outputs/tool_names/model_tier) live in package.json#lotics.agents"
70262
70563
  ];
70263
70564
  function writeAgentFile(projectDir, alias, instructions) {
70264
- fs4.mkdirSync(path5.join(projectDir, AGENTS_DIR), { recursive: true });
70565
+ fs6.mkdirSync(path7.join(projectDir, AGENTS_DIR), { recursive: true });
70265
70566
  const file2 = agentFilePath2(projectDir, alias);
70266
- fs4.writeFileSync(
70567
+ fs6.writeFileSync(
70267
70568
  file2,
70268
70569
  wrapAgentFile({ alias, instructions, notes: CLI_AGENT_NOTES.map((n) => n.replace("<alias>", alias)) })
70269
70570
  );
@@ -70309,6 +70610,7 @@ async function writeWorkflowFiles(client, projectDir, app_id, appName, workflows
70309
70610
  toWorkflowDtsDeclaration(declaration)
70310
70611
  );
70311
70612
  writeWorkflowFile(projectDir, alias, source, envelope);
70613
+ writeSynced(projectDir, "workflows", alias, contentSha(source));
70312
70614
  const rowDescription = res.result.description;
70313
70615
  if (typeof rowDescription === "string" && rowDescription !== "" && // Skip the server's GENERATED default. It is what an undescribed alias
70314
70616
  // carries, so writing it back would commit a line of noise per workflow
@@ -70323,12 +70625,12 @@ async function writeWorkflowFiles(client, projectDir, app_id, appName, workflows
70323
70625
  return written;
70324
70626
  }
70325
70627
  function writeWorkflowDescription(projectDir, alias, description) {
70326
- const pkgPath2 = path5.join(projectDir, "package.json");
70327
- const pkg2 = JSON.parse(fs4.readFileSync(pkgPath2, "utf-8"));
70628
+ const pkgPath2 = path7.join(projectDir, "package.json");
70629
+ const pkg2 = JSON.parse(fs6.readFileSync(pkgPath2, "utf-8"));
70328
70630
  const declaration = pkg2.lotics?.workflows?.[alias];
70329
70631
  if (!declaration) return;
70330
70632
  declaration.description = description;
70331
- fs4.writeFileSync(pkgPath2, JSON.stringify(pkg2, null, 2) + "\n");
70633
+ fs6.writeFileSync(pkgPath2, JSON.stringify(pkg2, null, 2) + "\n");
70332
70634
  }
70333
70635
  async function fetchWorkflowGlobals(client, projectDir, app_id, alias, declaration) {
70334
70636
  try {
@@ -70348,14 +70650,9 @@ async function fetchWorkflowGlobals(client, projectDir, app_id, alias, declarati
70348
70650
  }
70349
70651
  }
70350
70652
  async function fetchWorkflowDts(client, app_id, alias, declaration) {
70351
- try {
70352
- return await client.getAppWorkflowDts(app_id, alias);
70353
- } catch (err2) {
70354
- if (declaration && isUnknownWorkflowAliasError(err2)) {
70355
- return client.getAppWorkflowDts(app_id, alias, declaration);
70356
- }
70357
- throw err2;
70358
- }
70653
+ const declaresSchema = declaration !== void 0 && (declaration.inputs !== void 0 || declaration.outputs !== void 0);
70654
+ if (declaresSchema) return client.getAppWorkflowDts(app_id, alias, declaration);
70655
+ return client.getAppWorkflowDts(app_id, alias);
70359
70656
  }
70360
70657
  function runTar(args, cwd) {
70361
70658
  return new Promise((resolve2, reject2) => {
@@ -70383,25 +70680,26 @@ function runNpm(args, cwd) {
70383
70680
  }
70384
70681
  function readAppWorkspaceId(projectDir) {
70385
70682
  try {
70386
- const pkgPath2 = path5.join(projectDir, "package.json");
70387
- if (!fs4.existsSync(pkgPath2)) return null;
70388
- const pkg2 = JSON.parse(fs4.readFileSync(pkgPath2, "utf-8"));
70683
+ const pkgPath2 = path7.join(projectDir, "package.json");
70684
+ if (!fs6.existsSync(pkgPath2)) return null;
70685
+ const pkg2 = JSON.parse(fs6.readFileSync(pkgPath2, "utf-8"));
70389
70686
  return pkg2.lotics?.workspace_id ?? null;
70390
70687
  } catch {
70391
70688
  return null;
70392
70689
  }
70393
70690
  }
70394
70691
  function readAppMeta(projectDir) {
70395
- const pkgPath2 = path5.join(projectDir, "package.json");
70396
- if (!fs4.existsSync(pkgPath2)) {
70692
+ const pkgPath2 = path7.join(projectDir, "package.json");
70693
+ if (!fs6.existsSync(pkgPath2)) {
70397
70694
  throw new Error(`No package.json found at ${projectDir}. Run 'lotics app create' first.`);
70398
70695
  }
70399
- const pkg2 = JSON.parse(fs4.readFileSync(pkgPath2, "utf-8"));
70696
+ const pkg2 = JSON.parse(fs6.readFileSync(pkgPath2, "utf-8"));
70400
70697
  if (!pkg2.lotics?.app_id || !pkg2.lotics.workspace_id) {
70401
70698
  throw new Error(
70402
70699
  `package.json is missing the 'lotics' field with app_id and workspace_id. Was this folder created with 'lotics app create'?`
70403
70700
  );
70404
70701
  }
70702
+ noteScope({ app_id: pkg2.lotics.app_id });
70405
70703
  return {
70406
70704
  app_id: pkg2.lotics.app_id,
70407
70705
  workspace_id: pkg2.lotics.workspace_id,
@@ -70424,19 +70722,19 @@ var APP_META_KEYS = Object.keys({
70424
70722
  capabilities: true
70425
70723
  });
70426
70724
  function writeAppMeta(projectDir, meta3) {
70427
- const pkgPath2 = path5.join(projectDir, "package.json");
70428
- const pkg2 = JSON.parse(fs4.readFileSync(pkgPath2, "utf-8"));
70725
+ const pkgPath2 = path7.join(projectDir, "package.json");
70726
+ const pkg2 = JSON.parse(fs6.readFileSync(pkgPath2, "utf-8"));
70429
70727
  const existing = pkg2.lotics && typeof pkg2.lotics === "object" ? pkg2.lotics : {};
70430
70728
  const preserved = {};
70431
70729
  for (const [key, value2] of Object.entries(existing)) {
70432
70730
  if (!APP_META_KEYS.includes(key)) preserved[key] = value2;
70433
70731
  }
70434
70732
  pkg2.lotics = { ...meta3, ...preserved };
70435
- fs4.writeFileSync(pkgPath2, JSON.stringify(pkg2, null, 2) + "\n");
70733
+ fs6.writeFileSync(pkgPath2, JSON.stringify(pkg2, null, 2) + "\n");
70436
70734
  }
70437
70735
  function writeWorkflowOutputs(projectDir, alias, outputs) {
70438
- const pkgPath2 = path5.join(projectDir, "package.json");
70439
- const pkg2 = JSON.parse(fs4.readFileSync(pkgPath2, "utf-8"));
70736
+ const pkgPath2 = path7.join(projectDir, "package.json");
70737
+ const pkg2 = JSON.parse(fs6.readFileSync(pkgPath2, "utf-8"));
70440
70738
  const declaration = pkg2.lotics?.workflows?.[alias];
70441
70739
  if (!declaration) {
70442
70740
  throw new Error(
@@ -70444,11 +70742,35 @@ function writeWorkflowOutputs(projectDir, alias, outputs) {
70444
70742
  );
70445
70743
  }
70446
70744
  declaration.outputs = outputs;
70447
- fs4.writeFileSync(pkgPath2, JSON.stringify(pkg2, null, 2) + "\n");
70745
+ fs6.writeFileSync(pkgPath2, JSON.stringify(pkg2, null, 2) + "\n");
70746
+ }
70747
+ function workflowBodyDrift(projectDir, workflows, synced = {}) {
70748
+ const drifted = [];
70749
+ for (const alias of Object.keys(workflows ?? {})) {
70750
+ const baseline = synced[alias];
70751
+ if (baseline === void 0) continue;
70752
+ const file2 = workflowFilePath(projectDir, alias);
70753
+ if (!fs6.existsSync(file2)) continue;
70754
+ const source = stripWorkflowHeader(fs6.readFileSync(file2, "utf-8"));
70755
+ if (source.trim() === "" || contentSha(source) === baseline) continue;
70756
+ drifted.push(alias);
70757
+ }
70758
+ return drifted;
70759
+ }
70760
+ function reportWorkflowBodyDrift(projectDir, workflows, synced = {}) {
70761
+ const drifted = workflowBodyDrift(projectDir, workflows, synced);
70762
+ if (drifted.length === 0) return false;
70763
+ console.error(
70764
+ `
70765
+ \u2717 ${drifted.length === 1 ? "This workflow body differs" : "These workflow bodies differ"} from what is live: ${drifted.join(", ")}
70766
+ A deploy does not push bodies, so this version would not reproduce what runs.
70767
+ Send each with 'lotics app workflow set <alias>' \u2014 or revert the file to drop the edit.`
70768
+ );
70769
+ return true;
70448
70770
  }
70449
70771
  function ensureAppTsconfig(projectDir) {
70450
- const tsconfigPath = path5.join(projectDir, "tsconfig.json");
70451
- if (!fs4.existsSync(tsconfigPath)) {
70772
+ const tsconfigPath = path7.join(projectDir, "tsconfig.json");
70773
+ if (!fs6.existsSync(tsconfigPath)) {
70452
70774
  console.error(
70453
70775
  `\u26A0 No tsconfig.json at ${projectDir} \u2014 could not ensure "include" has "${LOTICS_INCLUDE_GLOB}" (a bare .lotics loads none of the generated types) or "exclude" has ${WORKFLOW_TSCONFIG_EXCLUDES.join(", ")}.`
70454
70776
  );
@@ -70456,7 +70778,7 @@ function ensureAppTsconfig(projectDir) {
70456
70778
  }
70457
70779
  let parsed;
70458
70780
  try {
70459
- parsed = JSON.parse(fs4.readFileSync(tsconfigPath, "utf-8"));
70781
+ parsed = JSON.parse(fs6.readFileSync(tsconfigPath, "utf-8"));
70460
70782
  } catch (err2) {
70461
70783
  console.error(
70462
70784
  `\u26A0 Could not parse tsconfig.json (${err2 instanceof Error ? err2.message : String(err2)}) \u2014 ensure "include" has "${LOTICS_INCLUDE_GLOB}" and "exclude" has ${WORKFLOW_TSCONFIG_EXCLUDES.join(", ")} manually.`
@@ -70489,7 +70811,7 @@ function ensureAppTsconfig(projectDir) {
70489
70811
  );
70490
70812
  }
70491
70813
  if (changes.length === 0) return;
70492
- fs4.writeFileSync(tsconfigPath, JSON.stringify(parsed, null, 2) + "\n");
70814
+ fs6.writeFileSync(tsconfigPath, JSON.stringify(parsed, null, 2) + "\n");
70493
70815
  console.error(`Patched tsconfig.json: ${changes.join("; ")}.`);
70494
70816
  }
70495
70817
  var LINK_TSCONFIG = ".lotics/tsconfig.link.json";
@@ -70499,15 +70821,15 @@ function writeDevLinkTsconfig(projectDir) {
70499
70821
  if (uiSrc) {
70500
70822
  paths["@lotics/ui/*"] = [`${uiSrc}/*`];
70501
70823
  for (const peer of readKitPeerNames(projectDir)) {
70502
- const typesDir = path5.join(projectDir, "node_modules", typesPackageFor(peer));
70503
- const runtimeDir = path5.join(projectDir, "node_modules", peer);
70504
- const target = fs4.existsSync(typesDir) ? `../node_modules/${typesPackageFor(peer)}` : fs4.existsSync(runtimeDir) ? `../node_modules/${peer}` : null;
70824
+ const typesDir = path7.join(projectDir, "node_modules", typesPackageFor(peer));
70825
+ const runtimeDir = path7.join(projectDir, "node_modules", peer);
70826
+ const target = fs6.existsSync(typesDir) ? `../node_modules/${typesPackageFor(peer)}` : fs6.existsSync(runtimeDir) ? `../node_modules/${peer}` : null;
70505
70827
  if (!target) continue;
70506
70828
  paths[peer] = [target];
70507
70829
  paths[`${peer}/*`] = [`${target}/*`];
70508
70830
  }
70509
70831
  }
70510
- const file2 = path5.join(projectDir, ".lotics", "tsconfig.link.json");
70832
+ const file2 = path7.join(projectDir, ".lotics", "tsconfig.link.json");
70511
70833
  const header = uiSrc ? `// GENERATED by \`lotics app codegen\` \u2014 do not edit.
70512
70834
  // LOTICS_UI_SRC is set, so @lotics/ui resolves to your working copy for tsc,
70513
70835
  // vitest, eslint and your editor \u2014 the same copy Vite is bundling. The peer
@@ -70518,52 +70840,52 @@ function writeDevLinkTsconfig(projectDir) {
70518
70840
  // LOTICS_UI_SRC is not set, so this is inert and @lotics/ui resolves from
70519
70841
  // node_modules as normal.
70520
70842
  `;
70521
- fs4.writeFileSync(file2, `${header}${JSON.stringify({ compilerOptions: { paths } }, null, 2)}
70843
+ fs6.writeFileSync(file2, `${header}${JSON.stringify({ compilerOptions: { paths } }, null, 2)}
70522
70844
  `);
70523
70845
  return file2;
70524
70846
  }
70525
70847
  function writeKitTypeAugmentation(projectDir) {
70526
70848
  const uiSrc = process.env.LOTICS_UI_SRC;
70527
- const source = uiSrc ? path5.join(uiSrc, "react_native.d.ts") : path5.join(projectDir, "node_modules", "@lotics", "ui", "src", "react_native.d.ts");
70528
- const target = path5.join(projectDir, ".lotics", "react_native.d.ts");
70529
- if (!fs4.existsSync(source)) {
70530
- if (fs4.existsSync(target)) fs4.rmSync(target);
70849
+ const source = uiSrc ? path7.join(uiSrc, "react_native.d.ts") : path7.join(projectDir, "node_modules", "@lotics", "ui", "src", "react_native.d.ts");
70850
+ const target = path7.join(projectDir, ".lotics", "react_native.d.ts");
70851
+ if (!fs6.existsSync(source)) {
70852
+ if (fs6.existsSync(target)) fs6.rmSync(target);
70531
70853
  return;
70532
70854
  }
70533
- fs4.writeFileSync(
70855
+ fs6.writeFileSync(
70534
70856
  target,
70535
70857
  `// GENERATED by \`lotics app codegen\` from @lotics/ui \u2014 do not edit.
70536
70858
  // The kit's web-only ViewStyle/TextStyle properties, which react-native does
70537
70859
  // not model. Delete any hand-copied src/react_native.d.ts; this replaces it.
70538
- ` + fs4.readFileSync(source, "utf-8")
70860
+ ` + fs6.readFileSync(source, "utf-8")
70539
70861
  );
70540
70862
  }
70541
70863
  function typesPackageFor(peer) {
70542
70864
  return peer.startsWith("@") ? `@types/${peer.slice(1).replace("/", "__")}` : `@types/${peer}`;
70543
70865
  }
70544
70866
  function readKitPeerNames(projectDir) {
70545
- const kitPkg = path5.join(projectDir, "node_modules", "@lotics", "ui", "package.json");
70546
- if (!fs4.existsSync(kitPkg)) return [];
70547
- const parsed = JSON.parse(fs4.readFileSync(kitPkg, "utf-8"));
70867
+ const kitPkg = path7.join(projectDir, "node_modules", "@lotics", "ui", "package.json");
70868
+ if (!fs6.existsSync(kitPkg)) return [];
70869
+ const parsed = JSON.parse(fs6.readFileSync(kitPkg, "utf-8"));
70548
70870
  const peers = parsed.peerDependencies;
70549
70871
  return peers && typeof peers === "object" ? Object.keys(peers) : [];
70550
70872
  }
70551
70873
  function writeAppDts(projectDir, manifest) {
70552
- const dotLotics = path5.join(projectDir, ".lotics");
70553
- fs4.mkdirSync(dotLotics, { recursive: true });
70874
+ const dotLotics = path7.join(projectDir, ".lotics");
70875
+ fs6.mkdirSync(dotLotics, { recursive: true });
70554
70876
  const written = [
70555
- [path5.join(dotLotics, "app_workflows.d.ts"), generateAppWorkflowsDts(manifest.workflows)],
70556
- [path5.join(dotLotics, "app_queries.d.ts"), generateAppQueriesDts(manifest.queries)],
70557
- [path5.join(dotLotics, "app_agents.d.ts"), generateAppAgentsDts(manifest.agents)]
70877
+ [path7.join(dotLotics, "app_workflows.d.ts"), generateAppWorkflowsDts(manifest.workflows)],
70878
+ [path7.join(dotLotics, "app_queries.d.ts"), generateAppQueriesDts(manifest.queries)],
70879
+ [path7.join(dotLotics, "app_agents.d.ts"), generateAppAgentsDts(manifest.agents)]
70558
70880
  ];
70559
- for (const [file2, content] of written) fs4.writeFileSync(file2, content);
70881
+ for (const [file2, content] of written) fs6.writeFileSync(file2, content);
70560
70882
  writeDevLinkTsconfig(projectDir);
70561
70883
  writeKitTypeAugmentation(projectDir);
70562
70884
  ensureAppTsconfig(projectDir);
70563
70885
  return written.map(([file2]) => file2);
70564
70886
  }
70565
70887
  function readCodegenTablesAllowlist(projectDir) {
70566
- const pkg2 = JSON.parse(fs4.readFileSync(path5.join(projectDir, "package.json"), "utf-8"));
70888
+ const pkg2 = JSON.parse(fs6.readFileSync(path7.join(projectDir, "package.json"), "utf-8"));
70567
70889
  const tables = pkg2.lotics?.codegen?.tables;
70568
70890
  return Array.isArray(tables) ? tables.filter((t) => typeof t === "string") : [];
70569
70891
  }
@@ -70576,17 +70898,17 @@ function resolveCodegenTableIds(projectDir, queries) {
70576
70898
  return [...ids];
70577
70899
  }
70578
70900
  function writeAppFields(projectDir, tables) {
70579
- const dotLotics = path5.join(projectDir, ".lotics");
70580
- fs4.mkdirSync(dotLotics, { recursive: true });
70581
- const file2 = path5.join(dotLotics, "app_fields.ts");
70582
- fs4.writeFileSync(file2, generateAppFields(tables));
70901
+ const dotLotics = path7.join(projectDir, ".lotics");
70902
+ fs6.mkdirSync(dotLotics, { recursive: true });
70903
+ const file2 = path7.join(dotLotics, "app_fields.ts");
70904
+ fs6.writeFileSync(file2, generateAppFields(tables));
70583
70905
  return file2;
70584
70906
  }
70585
70907
  function writeBindingAppFields(projectDir, binding) {
70586
- const dotLotics = path5.join(projectDir, ".lotics");
70587
- fs4.mkdirSync(dotLotics, { recursive: true });
70588
- const file2 = path5.join(dotLotics, "app_fields.ts");
70589
- fs4.writeFileSync(file2, generatePackageAppFields(binding));
70908
+ const dotLotics = path7.join(projectDir, ".lotics");
70909
+ fs6.mkdirSync(dotLotics, { recursive: true });
70910
+ const file2 = path7.join(dotLotics, "app_fields.ts");
70911
+ fs6.writeFileSync(file2, generatePackageAppFields(binding));
70590
70912
  ensureAppVitestSetup(projectDir);
70591
70913
  return file2;
70592
70914
  }
@@ -70619,7 +70941,7 @@ async function writeGeneratedAppFields(client, projectDir, app, queries) {
70619
70941
  }
70620
70942
  function warnAppFieldsUnwritten(projectDir, err2) {
70621
70943
  const reason = err2 instanceof Error ? err2.message : String(err2);
70622
- if (fs4.existsSync(path5.join(projectDir, ".lotics", "app_fields.ts"))) {
70944
+ if (fs6.existsSync(path7.join(projectDir, ".lotics", "app_fields.ts"))) {
70623
70945
  console.error(`\u26A0 Could not regenerate .lotics/app_fields.ts (${reason}). Kept the existing file.`);
70624
70946
  return;
70625
70947
  }
@@ -70630,8 +70952,8 @@ function warnAppFieldsUnwritten(projectDir, err2) {
70630
70952
  function warnAboutDevLink(projectDir, command) {
70631
70953
  const uiSrc = process.env.LOTICS_UI_SRC;
70632
70954
  if (!uiSrc) return;
70633
- const viteConfigPath = path5.join(projectDir, "vite.config.ts");
70634
- const config2 = fs4.existsSync(viteConfigPath) ? fs4.readFileSync(viteConfigPath, "utf-8") : "";
70955
+ const viteConfigPath = path7.join(projectDir, "vite.config.ts");
70956
+ const config2 = fs6.existsSync(viteConfigPath) ? fs6.readFileSync(viteConfigPath, "utf-8") : "";
70635
70957
  const linked = config2.includes("loticsResolve") || config2.includes("LOTICS_UI_SRC");
70636
70958
  if (!linked) {
70637
70959
  console.error(
@@ -70649,19 +70971,19 @@ function warnAboutDevLink(projectDir, command) {
70649
70971
  }
70650
70972
  }
70651
70973
  function ensureAppVitestSetup(projectDir) {
70652
- const setupPath = path5.join(projectDir, VITEST_SETUP_FILENAME);
70653
- if (!fs4.existsSync(setupPath)) {
70654
- fs4.writeFileSync(setupPath, VITEST_SETUP_CONTENT);
70974
+ const setupPath = path7.join(projectDir, VITEST_SETUP_FILENAME);
70975
+ if (!fs6.existsSync(setupPath)) {
70976
+ fs6.writeFileSync(setupPath, VITEST_SETUP_CONTENT);
70655
70977
  console.error(`Wrote ${VITEST_SETUP_FILENAME} (stubs getAppBinding so \`npm test\` collects).`);
70656
- } else if (!fs4.readFileSync(setupPath, "utf-8").includes("getAppBinding")) {
70978
+ } else if (!fs6.readFileSync(setupPath, "utf-8").includes("getAppBinding")) {
70657
70979
  console.error(
70658
70980
  `\u26A0 ${VITEST_SETUP_FILENAME} exists but does not stub getAppBinding \u2014 \`npm test\` will fail to collect any test that imports the app graph (the binding-form .lotics/app_fields.ts awaits a network call). Add the stub to it:
70659
70981
  ${VITEST_SETUP_CONTENT}`
70660
70982
  );
70661
70983
  }
70662
- const viteConfigPath = path5.join(projectDir, "vite.config.ts");
70663
- if (!fs4.existsSync(viteConfigPath)) return;
70664
- const viteConfig = fs4.readFileSync(viteConfigPath, "utf-8");
70984
+ const viteConfigPath = path7.join(projectDir, "vite.config.ts");
70985
+ if (!fs6.existsSync(viteConfigPath)) return;
70986
+ const viteConfig = fs6.readFileSync(viteConfigPath, "utf-8");
70665
70987
  if (/setupFiles\s*:/.test(viteConfig) && viteConfig.includes(VITEST_SETUP_FILENAME)) return;
70666
70988
  console.error(
70667
70989
  `\u26A0 vite.config.ts does not wire ${VITEST_SETUP_FILENAME} \u2014 \`npm test\` will fail to collect any test that imports the app graph (the binding-form .lotics/app_fields.ts awaits a network call). Add it to the \`test\` block:
@@ -70669,7 +70991,7 @@ ${VITEST_SETUP_CONTENT}`
70669
70991
  );
70670
70992
  }
70671
70993
  async function appCodegen(args) {
70672
- const projectDir = path5.resolve(args.projectDir ?? process.cwd());
70994
+ const projectDir = path7.resolve(args.projectDir ?? process.cwd());
70673
70995
  const meta3 = readAppMeta(projectDir);
70674
70996
  const dtsPaths = writeAppDts(projectDir, {
70675
70997
  workflows: meta3.workflows,
@@ -70683,7 +71005,7 @@ async function appCodegen(args) {
70683
71005
  }
70684
71006
  for (const alias of orphanWorkflowBodies(projectDir, declared)) {
70685
71007
  console.error(
70686
- `\u26A0 ${path5.join(WORKFLOWS_DIR, `${alias}.ts`)} is not declared in package.json#lotics.workflows \u2014 'workflow check' and 'workflow set' both skip it. Declare the alias or delete the file.`
71008
+ `\u26A0 ${path7.join(WORKFLOWS_DIR, `${alias}.ts`)} is not declared in package.json#lotics.workflows \u2014 'workflow check' and 'workflow set' both skip it. Declare the alias or delete the file.`
70687
71009
  );
70688
71010
  }
70689
71011
  if (!args.client) {
@@ -70734,8 +71056,8 @@ async function refreshWorkflowGlobals(client, projectDir, app_id, workflows) {
70734
71056
  }
70735
71057
  async function refreshWorkflowTypes(client, projectDir, app_id, alias, declaration) {
70736
71058
  const file2 = workflowFilePath(projectDir, alias);
70737
- if (!fs4.existsSync(file2)) return false;
70738
- const body = stripWorkflowHeader(fs4.readFileSync(file2, "utf-8"));
71059
+ if (!fs6.existsSync(file2)) return false;
71060
+ const body = stripWorkflowHeader(fs6.readFileSync(file2, "utf-8"));
70739
71061
  if (body.trim() === "") return false;
70740
71062
  const envelope = await fetchWorkflowGlobals(client, projectDir, app_id, alias, declaration);
70741
71063
  writeWorkflowFile(projectDir, alias, body, envelope);
@@ -70747,14 +71069,14 @@ function stampPulledManifest(projectDir, args) {
70747
71069
  workspace_id: args.workspace_id,
70748
71070
  current_version_id: args.current_version_id,
70749
71071
  version_number: args.version_number,
70750
- workflows: args.workflows,
71072
+ workflows: toManifestWorkflows(args.workflows),
70751
71073
  queries: args.queries,
70752
71074
  // The prose is stripped here and only here: `args.agents` is the LIVE map
70753
71075
  // (its instructions feed `writeAgentFiles`), the manifest gets the typed half.
70754
71076
  agents: toManifestAgents(args.agents)
70755
71077
  });
70756
71078
  writeAppDts(projectDir, {
70757
- workflows: args.workflows,
71079
+ workflows: toManifestWorkflows(args.workflows),
70758
71080
  queries: args.queries,
70759
71081
  agents: toManifestAgents(args.agents)
70760
71082
  });
@@ -70765,21 +71087,21 @@ async function downloadToFile(url2, destPath) {
70765
71087
  throw new Error(`Failed to download ${url2}: ${response.status}`);
70766
71088
  }
70767
71089
  const buffer = Buffer.from(await response.arrayBuffer());
70768
- fs4.writeFileSync(destPath, buffer);
71090
+ fs6.writeFileSync(destPath, buffer);
70769
71091
  }
70770
71092
  function appDirName(name2) {
70771
71093
  const cleaned = name2.replace(/[/\\]+/g, "-").replace(/[<>:"|?*]/g, "").replace(/\s+/g, " ").trim().replace(/^[.\s-]+|[.\s-]+$/g, "");
70772
71094
  return cleaned || "app";
70773
71095
  }
70774
71096
  async function appCreate(client, args) {
70775
- const targetPath = path5.resolve(args.targetPath ?? appDirName(args.name));
70776
- if (fs4.existsSync(targetPath)) {
70777
- const entries2 = fs4.readdirSync(targetPath);
71097
+ const targetPath = path7.resolve(args.targetPath ?? appDirName(args.name));
71098
+ if (fs6.existsSync(targetPath)) {
71099
+ const entries2 = fs6.readdirSync(targetPath);
70778
71100
  if (entries2.length > 0) {
70779
71101
  throw new Error(`Target directory ${targetPath} is not empty.`);
70780
71102
  }
70781
71103
  }
70782
- fs4.mkdirSync(targetPath, { recursive: true });
71104
+ fs6.mkdirSync(targetPath, { recursive: true });
70783
71105
  const app = await client.createApp({ name: args.name });
70784
71106
  console.error(`Created app: ${app.name} (${app.id})`);
70785
71107
  const [uiLatest, sdkLatest] = await Promise.all([
@@ -70794,9 +71116,9 @@ async function appCreate(client, args) {
70794
71116
  sdk_version: sdkLatest ? `^${sdkLatest}` : void 0
70795
71117
  });
70796
71118
  for (const file2 of files) {
70797
- const fullPath = path5.join(targetPath, file2.path);
70798
- fs4.mkdirSync(path5.dirname(fullPath), { recursive: true });
70799
- fs4.writeFileSync(fullPath, file2.content);
71119
+ const fullPath = path7.join(targetPath, file2.path);
71120
+ fs6.mkdirSync(path7.dirname(fullPath), { recursive: true });
71121
+ fs6.writeFileSync(fullPath, file2.content);
70800
71122
  }
70801
71123
  console.error(`Scaffolded ${files.length} files into ${targetPath}`);
70802
71124
  writeAppDts(targetPath, {});
@@ -70809,7 +71131,7 @@ async function appCreate(client, args) {
70809
71131
  });
70810
71132
  console.error(`
70811
71133
  Ready. Next steps:`);
70812
- console.error(` cd ${path5.relative(process.cwd(), targetPath) || "."}`);
71134
+ console.error(` cd ${path7.relative(process.cwd(), targetPath) || "."}`);
70813
71135
  console.error(` # edit src/App.tsx, then:`);
70814
71136
  console.error(` lotics app deploy`);
70815
71137
  }
@@ -70866,11 +71188,11 @@ async function appVersions(client, args) {
70866
71188
  }
70867
71189
  }
70868
71190
  function defaultPullTarget(appId, appName) {
70869
- const cwdPkgPath = path5.join(process.cwd(), "package.json");
70870
- if (fs4.existsSync(cwdPkgPath)) {
71191
+ const cwdPkgPath = path7.join(process.cwd(), "package.json");
71192
+ if (fs6.existsSync(cwdPkgPath)) {
70871
71193
  let pkg2 = null;
70872
71194
  try {
70873
- pkg2 = JSON.parse(fs4.readFileSync(cwdPkgPath, "utf-8"));
71195
+ pkg2 = JSON.parse(fs6.readFileSync(cwdPkgPath, "utf-8"));
70874
71196
  } catch (err2) {
70875
71197
  console.error(
70876
71198
  `\u26A0 Could not parse ${cwdPkgPath} (${err2 instanceof Error ? err2.message : String(err2)}) \u2014 pulling into a fresh ${appDirName(appName)}/ subdir.`
@@ -70889,16 +71211,16 @@ async function appPull(client, args) {
70889
71211
  }
70890
71212
  const version2 = await client.getAppVersion(app.id, app.current_version_id);
70891
71213
  const sourceUrl = await client.getAppVersionSourceUrl(app.id, version2.id);
70892
- const targetPath = path5.resolve(args.targetPath ?? defaultPullTarget(app.id, app.name));
70893
- fs4.mkdirSync(targetPath, { recursive: true });
70894
- const tmpFile = path5.join(tmpdir(), `lotics-app-${app.id}-${Date.now()}.tar.gz`);
71214
+ const targetPath = path7.resolve(args.targetPath ?? defaultPullTarget(app.id, app.name));
71215
+ fs6.mkdirSync(targetPath, { recursive: true });
71216
+ const tmpFile = path7.join(tmpdir(), `lotics-app-${app.id}-${Date.now()}.tar.gz`);
70895
71217
  console.error(`Downloading source archive...`);
70896
71218
  await downloadToFile(sourceUrl, tmpFile);
70897
71219
  try {
70898
71220
  console.error(`Extracting to ${targetPath}...`);
70899
71221
  await runTar(["-xzf", tmpFile, "-C", targetPath], targetPath);
70900
71222
  } finally {
70901
- if (fs4.existsSync(tmpFile)) fs4.unlinkSync(tmpFile);
71223
+ if (fs6.existsSync(tmpFile)) fs6.unlinkSync(tmpFile);
70902
71224
  }
70903
71225
  stampPulledManifest(targetPath, {
70904
71226
  app_id: app.id,
@@ -70909,6 +71231,9 @@ async function appPull(client, args) {
70909
71231
  queries: app.queries ?? {},
70910
71232
  agents: app.agents ?? {}
70911
71233
  });
71234
+ for (const [alias, declaration] of Object.entries(app.queries ?? {})) {
71235
+ writeSynced(targetPath, "queries", alias, declarationSha(declaration));
71236
+ }
70912
71237
  await writeGeneratedAppFields(
70913
71238
  client,
70914
71239
  targetPath,
@@ -70927,6 +71252,10 @@ async function appPull(client, args) {
70927
71252
  const agents = app.agents ?? {};
70928
71253
  if (Object.keys(agents).length > 0) {
70929
71254
  const written = writeAgentFiles(targetPath, agents);
71255
+ for (const alias of written) {
71256
+ const instructions = agents[alias]?.instructions;
71257
+ if (instructions !== void 0) writeSynced(targetPath, "agents", alias, contentSha(instructions));
71258
+ }
70930
71259
  if (written.length > 0) {
70931
71260
  console.error(
70932
71261
  `Wrote ${written.length} agent instruction ${written.length === 1 ? "file" : "files"} to ${AGENTS_DIR}/ (${written.join(", ")})`
@@ -70937,26 +71266,26 @@ async function appPull(client, args) {
70937
71266
  await runNpm(["install"], targetPath);
70938
71267
  console.error(`
70939
71268
  Ready. Next steps:`);
70940
- console.error(` cd ${path5.relative(process.cwd(), targetPath) || "."}`);
71269
+ console.error(` cd ${path7.relative(process.cwd(), targetPath) || "."}`);
70941
71270
  console.error(` # edit src/App.tsx`);
70942
71271
  console.error(` lotics app deploy`);
70943
71272
  }
70944
71273
  function readAppSourceText(projectDir) {
70945
- const srcDir = path5.join(projectDir, "src");
70946
- if (!fs4.existsSync(srcDir)) return "";
71274
+ const srcDir = path7.join(projectDir, "src");
71275
+ if (!fs6.existsSync(srcDir)) return "";
70947
71276
  const parts = [];
70948
71277
  const walk2 = (dir) => {
70949
- for (const entry of fs4.readdirSync(dir, { withFileTypes: true })) {
70950
- const full = path5.join(dir, entry.name);
71278
+ for (const entry of fs6.readdirSync(dir, { withFileTypes: true })) {
71279
+ const full = path7.join(dir, entry.name);
70951
71280
  if (entry.isDirectory()) walk2(full);
70952
- else if (/\.(ts|tsx|js|jsx)$/.test(entry.name)) parts.push(fs4.readFileSync(full, "utf8"));
71281
+ else if (/\.(ts|tsx|js|jsx)$/.test(entry.name)) parts.push(fs6.readFileSync(full, "utf8"));
70953
71282
  }
70954
71283
  };
70955
71284
  walk2(srcDir);
70956
71285
  return parts.join("\n");
70957
71286
  }
70958
71287
  async function appDeploy(client, args) {
70959
- const projectDir = path5.resolve(args.projectDir ?? process.cwd());
71288
+ const projectDir = path7.resolve(args.projectDir ?? process.cwd());
70960
71289
  const meta3 = readAppMeta(projectDir);
70961
71290
  warnAboutDevLink(projectDir, "deploy");
70962
71291
  const liveApp = await client.getApp(meta3.app_id);
@@ -70975,22 +71304,30 @@ Refusing to deploy \u2014 package.json#lotics.agents disagrees with the live app
70975
71304
  );
70976
71305
  process.exit(1);
70977
71306
  }
71307
+ const declarationDrift = reportWorkflowDeclarationDrift(meta3.workflows, liveApp.workflows);
70978
71308
  const sourceText = readAppSourceText(projectDir);
70979
71309
  const called = calledAppAliases(sourceText);
70980
- warnIfAgentProseDiffers(projectDir, liveApp.agents);
71310
+ const proseDrift = reportAgentProseDrift(projectDir, liveApp.agents);
71311
+ const bodyDrift = reportWorkflowBodyDrift(projectDir, meta3.workflows, readSynced(projectDir).workflows);
71312
+ const queryDrift = reportQueryDrift(liveApp, meta3.queries ?? {});
71313
+ if (bodyDrift || declarationDrift || proseDrift || queryDrift) {
71314
+ console.error(`
71315
+ Nothing was deployed.`);
71316
+ process.exit(1);
71317
+ }
70981
71318
  warnIfDynamicAliases(called);
70982
71319
  warnIfUndeclaredCapabilities(sourceText, meta3.capabilities);
70983
71320
  writeAppDts(projectDir, { workflows: meta3.workflows, queries: meta3.queries, agents: meta3.agents });
70984
71321
  console.error("Building...");
70985
71322
  await runNpm(["run", "build"], projectDir);
70986
- const distDir = path5.join(projectDir, "dist");
70987
- if (!fs4.existsSync(distDir)) {
71323
+ const distDir = path7.join(projectDir, "dist");
71324
+ if (!fs6.existsSync(distDir)) {
70988
71325
  throw new Error(
70989
71326
  `Build did not produce a dist/ directory in ${projectDir}. Check that 'npm run build' is configured correctly (Vite/etc. defaults to dist/).`
70990
71327
  );
70991
71328
  }
70992
- const tmpSource = path5.join(tmpdir(), `lotics-source-${Date.now()}.tar.gz`);
70993
- const tmpDist = path5.join(tmpdir(), `lotics-dist-${Date.now()}.tar.gz`);
71329
+ const tmpSource = path7.join(tmpdir(), `lotics-source-${Date.now()}.tar.gz`);
71330
+ const tmpDist = path7.join(tmpdir(), `lotics-dist-${Date.now()}.tar.gz`);
70994
71331
  try {
70995
71332
  console.error("Packaging source...");
70996
71333
  await runTar(
@@ -71014,8 +71351,8 @@ Refusing to deploy \u2014 package.json#lotics.agents disagrees with the live app
71014
71351
  const result = await client.deployAppVersion({
71015
71352
  queries: liveQueries,
71016
71353
  app_id: meta3.app_id,
71017
- source_archive: fs4.readFileSync(tmpSource),
71018
- dist_archive: fs4.readFileSync(tmpDist),
71354
+ source_archive: fs6.readFileSync(tmpSource),
71355
+ dist_archive: fs6.readFileSync(tmpDist),
71019
71356
  prev_version_id: meta3.current_version_id,
71020
71357
  message: args.message,
71021
71358
  // Capabilities ARE manifest-authoritative: always send,
@@ -71032,7 +71369,8 @@ Refusing to deploy \u2014 package.json#lotics.agents disagrees with the live app
71032
71369
  // with whatever the author last remembered to write. Stop calling an
71033
71370
  // alias + redeploy to lift the guard before removing its binding.
71034
71371
  workflow_aliases: called.workflows,
71035
- query_aliases: called.queries
71372
+ query_aliases: called.queries,
71373
+ agent_aliases: called.agents
71036
71374
  });
71037
71375
  writeAppMeta(projectDir, {
71038
71376
  ...meta3,
@@ -71045,7 +71383,6 @@ Refusing to deploy \u2014 package.json#lotics.agents disagrees with the live app
71045
71383
  const app = await client.getApp(meta3.app_id);
71046
71384
  warnIfUnbranded(app);
71047
71385
  warnIfUnboundAliases(app, called);
71048
- warnIfQueriesDiffer(app, meta3.queries ?? {});
71049
71386
  } catch (err2) {
71050
71387
  console.error(`(skipped post-deploy checks: ${err2.message})`);
71051
71388
  }
@@ -71060,8 +71397,8 @@ Refusing to deploy \u2014 package.json#lotics.agents disagrees with the live app
71060
71397
  throw err2;
71061
71398
  }
71062
71399
  } finally {
71063
- if (fs4.existsSync(tmpSource)) fs4.unlinkSync(tmpSource);
71064
- if (fs4.existsSync(tmpDist)) fs4.unlinkSync(tmpDist);
71400
+ if (fs6.existsSync(tmpSource)) fs6.unlinkSync(tmpSource);
71401
+ if (fs6.existsSync(tmpDist)) fs6.unlinkSync(tmpDist);
71065
71402
  }
71066
71403
  }
71067
71404
  function warnIfUnbranded(app) {
@@ -71088,18 +71425,20 @@ async function readLiveQueries(client, appId) {
71088
71425
  process.exit(1);
71089
71426
  }
71090
71427
  }
71091
- function warnIfQueriesDiffer(app, manifest) {
71428
+ function reportQueryDrift(app, manifest) {
71092
71429
  const drifted = driftedQueryAliases(manifest, app.queries);
71093
- if (drifted.length === 0) return;
71430
+ if (drifted.length === 0) return false;
71094
71431
  console.error(
71095
71432
  `
71096
- \u26A0 ${drifted.length} quer${drifted.length === 1 ? "y" : "ies"} in package.json differ from the app: ${drifted.join(", ")}
71097
- A deploy does not write them. Push yours with 'lotics app query set --all'
71098
- (or one alias at a time), or adopt the app's with 'lotics app pull'.`
71433
+ \u2717 ${drifted.length} quer${drifted.length === 1 ? "y" : "ies"} in package.json differ from the app: ${drifted.join(", ")}
71434
+ A deploy does not write them, so this version would not reproduce what runs.
71435
+ Push yours with 'lotics app query set --all' (or one alias at a time),
71436
+ or adopt the app's with 'lotics app pull'.`
71099
71437
  );
71438
+ return drifted.length > 0;
71100
71439
  }
71101
71440
  async function appCheck(client, args = {}) {
71102
- const projectDir = path5.resolve(args.projectDir ?? process.cwd());
71441
+ const projectDir = path7.resolve(args.projectDir ?? process.cwd());
71103
71442
  const meta3 = readAppMeta(projectDir);
71104
71443
  const app = await client.getApp(meta3.app_id);
71105
71444
  const sourceText = readAppSourceText(projectDir);
@@ -71115,27 +71454,49 @@ async function appCheck(client, args = {}) {
71115
71454
  );
71116
71455
  }
71117
71456
  warnAboutDevLink(projectDir, "deploy");
71118
- warnIfAgentProseDiffers(projectDir, app.agents);
71457
+ const proseDrift = reportAgentProseDrift(projectDir, app.agents);
71458
+ const bodyDrift = reportWorkflowBodyDrift(projectDir, meta3.workflows, readSynced(projectDir).workflows);
71459
+ const declarationDrift = reportWorkflowDeclarationDrift(meta3.workflows, app.workflows);
71460
+ const queryDrift = reportQueryDrift(app, meta3.queries ?? {});
71119
71461
  warnIfDynamicAliases(called);
71120
71462
  warnIfUndeclaredCapabilities(sourceText, meta3.capabilities);
71121
71463
  warnIfUnboundAliases(app, called);
71122
- warnIfQueriesDiffer(app, meta3.queries ?? {});
71123
71464
  warnIfUnbranded(app);
71124
- if (divergences.length > 0) process.exit(1);
71465
+ if (divergences.length > 0 || bodyDrift || declarationDrift || proseDrift || queryDrift) {
71466
+ process.exit(1);
71467
+ }
71125
71468
  console.error("Checked the app's bindings, capabilities and agent schemas \u2014 nothing blocking.");
71126
71469
  }
71127
- function warnIfAgentProseDiffers(projectDir, liveAgents) {
71470
+ function reportAgentProseDrift(projectDir, liveAgents) {
71471
+ let drifted = false;
71128
71472
  for (const [alias, live] of Object.entries(liveAgents ?? {})) {
71129
71473
  const file2 = agentFilePath2(projectDir, alias);
71130
- if (!fs4.existsSync(file2)) continue;
71131
- const local = stripAgentHeader(fs4.readFileSync(file2, "utf-8"));
71474
+ if (!fs6.existsSync(file2)) continue;
71475
+ const local = stripAgentHeader(fs6.readFileSync(file2, "utf-8"));
71132
71476
  if (local === "" || local === (live.instructions ?? "")) continue;
71477
+ drifted = true;
71133
71478
  console.error(
71134
71479
  `
71135
- \u26A0 ${path5.relative(projectDir, file2)} differs from the live agent's instructions.
71136
- A deploy does not push prose. Send it with 'lotics app agent set ${alias}'.`
71480
+ \u2717 ${path7.relative(projectDir, file2)} differs from the live agent's instructions.
71481
+ A deploy does not push prose, so this version would not reproduce what runs.
71482
+ Send it with 'lotics app agent set ${alias}' \u2014 or revert the file to drop the edit.`
71137
71483
  );
71138
71484
  }
71485
+ return drifted;
71486
+ }
71487
+ function reportWorkflowDeclarationDrift(manifestWorkflows, liveWorkflows) {
71488
+ const divergences = workflowTypeDivergences(manifestWorkflows, liveWorkflows);
71489
+ if (divergences.length === 0) return false;
71490
+ console.error(
71491
+ `
71492
+ \u2717 package.json#lotics.workflows disagrees with the live app:
71493
+ ` + divergences.map((d) => ` \u2022 ${d}`).join("\n") + `
71494
+
71495
+ This build's types came from the manifest, so the mismatch is invisible to tsc \u2014
71496
+ the bundle would ship typed against a schema the live binding rejects.
71497
+ Push the declarations: lotics app workflow set <alias> (a deploy never binds one)`
71498
+ );
71499
+ return true;
71139
71500
  }
71140
71501
  function warnIfDynamicAliases(called) {
71141
71502
  if (called.dynamic.length === 0) return;
@@ -71179,7 +71540,7 @@ function warnIfUnboundAliases(app, called) {
71179
71540
  console.error(lines.join("\n"));
71180
71541
  }
71181
71542
  async function appDev(client, args) {
71182
- const projectDir = path5.resolve(args.projectDir ?? process.cwd());
71543
+ const projectDir = path7.resolve(args.projectDir ?? process.cwd());
71183
71544
  const meta3 = readAppMeta(projectDir);
71184
71545
  warnAboutDevLink(projectDir, "dev");
71185
71546
  writeAppDts(projectDir, { workflows: meta3.workflows, queries: meta3.queries, agents: meta3.agents });
@@ -71350,11 +71711,11 @@ var AGENT_RUN_POLL = {
71350
71711
  existenceTimeoutMs: 5e3
71351
71712
  };
71352
71713
  var sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
71353
- async function fetchSettledAgentRun(client, appId, sessionId, runId, timing) {
71714
+ async function fetchSettledAgentRun(client, appId, sessionId2, runId, timing) {
71354
71715
  const settleDeadline = Date.now() + timing.settleTimeoutMs;
71355
71716
  const existenceDeadline = Date.now() + timing.existenceTimeoutMs;
71356
71717
  for (; ; ) {
71357
- const { runs } = await client.listAgentRuns(appId, sessionId);
71718
+ const { runs } = await client.listAgentRuns(appId, sessionId2);
71358
71719
  const target = runId ? runs.find((r) => r.id === runId) : runs[runs.length - 1];
71359
71720
  if (target) {
71360
71721
  if (target.status !== "running" || Date.now() >= settleDeadline) return target;
@@ -71365,20 +71726,20 @@ async function fetchSettledAgentRun(client, appId, sessionId, runId, timing) {
71365
71726
  }
71366
71727
  }
71367
71728
  async function appAgentRun(client, args, timing = AGENT_RUN_POLL) {
71368
- const sessionId = args.sessionId ?? `cli-${randomUUID()}`;
71729
+ const sessionId2 = args.sessionId ?? `cli-${randomUUID()}`;
71369
71730
  const continuing = args.sessionId !== void 0;
71370
71731
  const res = await client.appAgentRunStream(args.app_id, args.alias, {
71371
- session_id: sessionId,
71732
+ session_id: sessionId2,
71372
71733
  input: args.input
71373
71734
  });
71374
71735
  const runId = res.headers.get("x-app-agent-run-id") ?? void 0;
71375
71736
  if (res.body) {
71376
71737
  await streamAgentTextDeltas(res.body, (piece) => process.stderr.write(piece));
71377
71738
  }
71378
- const run = await fetchSettledAgentRun(client, args.app_id, sessionId, runId, timing);
71739
+ const run = await fetchSettledAgentRun(client, args.app_id, sessionId2, runId, timing);
71379
71740
  if (!run) {
71380
71741
  console.error(`
71381
- Could not find the settled run for session ${sessionId} on ${args.app_id}.`);
71742
+ Could not find the settled run for session ${sessionId2} on ${args.app_id}.`);
71382
71743
  console.error("The run may still be in progress \u2014 re-check with `lotics run` against the app's agent-runs.");
71383
71744
  process.exit(1);
71384
71745
  }
@@ -71394,13 +71755,14 @@ Could not find the settled run for session ${sessionId} on ${args.app_id}.`);
71394
71755
  Agent "${args.alias}" run ${run.id} \u2192 ${run.status}${run.error_message ? `: ${run.error_message}` : ""}`
71395
71756
  );
71396
71757
  console.error(
71397
- continuing ? `Session: ${sessionId}` : `Session: ${sessionId} (fresh \u2014 pass --session ${sessionId} to continue this thread)`
71758
+ continuing ? `Session: ${sessionId2}` : `Session: ${sessionId2} (fresh \u2014 pass --session ${sessionId2} to continue this thread)`
71398
71759
  );
71399
71760
  if (run.status !== "completed") process.exit(1);
71400
71761
  }
71401
71762
  async function appAgentSet(client, args) {
71402
71763
  const projectDir = process.cwd();
71403
71764
  const meta3 = readAppMeta(projectDir);
71765
+ noteScope({ alias: args.alias });
71404
71766
  const app = await client.getApp(meta3.app_id);
71405
71767
  const live = app.agents?.[args.alias];
71406
71768
  if (!live) {
@@ -71410,24 +71772,30 @@ async function appAgentSet(client, args) {
71410
71772
  process.exit(1);
71411
71773
  }
71412
71774
  const file2 = agentFilePath2(projectDir, args.alias);
71413
- if (!fs4.existsSync(file2)) {
71775
+ if (!fs6.existsSync(file2)) {
71414
71776
  console.error(
71415
- `No instructions at ${path5.relative(projectDir, file2)}. Run 'lotics app pull ${meta3.app_id}' to write ${AGENTS_DIR}/${args.alias}.md, then edit it.`
71777
+ `No instructions at ${path7.relative(projectDir, file2)}. Run 'lotics app pull ${meta3.app_id}' to write ${AGENTS_DIR}/${args.alias}.md, then edit it.`
71416
71778
  );
71417
71779
  process.exit(1);
71418
71780
  }
71419
- const instructions = stripAgentHeader(fs4.readFileSync(file2, "utf-8"));
71781
+ const instructions = stripAgentHeader(fs6.readFileSync(file2, "utf-8"));
71420
71782
  if (instructions === "") {
71421
71783
  console.error(
71422
- `${path5.relative(projectDir, file2)} is empty after stripping the header \u2014 refusing to push an empty prompt.`
71784
+ `${path7.relative(projectDir, file2)} is empty after stripping the header \u2014 refusing to push an empty prompt.`
71423
71785
  );
71424
71786
  process.exit(1);
71425
71787
  }
71426
- const res = await client.setAppAgent(meta3.app_id, args.alias, { instructions });
71788
+ const res = await client.setAppAgent(meta3.app_id, args.alias, {
71789
+ instructions,
71790
+ // Conditional on the prose this edit was built from, so a push from a stale
71791
+ // checkout is refused instead of overwriting an edit made elsewhere.
71792
+ ...readSynced(projectDir).agents?.[args.alias] ? { expected_instructions_sha: readSynced(projectDir).agents?.[args.alias] } : {}
71793
+ });
71427
71794
  if (res.error) {
71428
71795
  console.error(`Failed to set agent "${args.alias}": ${res.error}`);
71429
71796
  process.exit(1);
71430
71797
  }
71798
+ writeSynced(projectDir, "agents", args.alias, contentSha(instructions));
71431
71799
  console.error(
71432
71800
  `Set agent "${args.alias}" (${instructions.length} chars of instructions` + (live.model_tier ? `, ${live.model_tier}` : "") + `). Typed fields left as they are.`
71433
71801
  );
@@ -71443,15 +71811,16 @@ async function appWorkflowSet(client, args) {
71443
71811
  process.exit(1);
71444
71812
  }
71445
71813
  const file2 = workflowFilePath(projectDir, args.alias);
71446
- if (!fs4.existsSync(file2)) {
71814
+ if (!fs6.existsSync(file2)) {
71447
71815
  console.error(
71448
- `No workflow body at ${path5.relative(projectDir, file2)}. Run 'lotics app pull ${meta3.app_id}' to write src/workflows/${args.alias}.ts, then edit it.`
71816
+ `No workflow body at ${path7.relative(projectDir, file2)}. Run 'lotics app pull ${meta3.app_id}' to write src/workflows/${args.alias}.ts, then edit it.`
71449
71817
  );
71450
71818
  process.exit(1);
71451
71819
  }
71452
- const source = stripWorkflowHeader(fs4.readFileSync(file2, "utf-8"));
71820
+ noteScope({ alias: args.alias });
71821
+ const source = stripWorkflowHeader(fs6.readFileSync(file2, "utf-8"));
71453
71822
  if (source.trim() === "") {
71454
- console.error(`Workflow body ${path5.relative(projectDir, file2)} is empty after stripping the header.`);
71823
+ console.error(`Workflow body ${path7.relative(projectDir, file2)} is empty after stripping the header.`);
71455
71824
  process.exit(1);
71456
71825
  }
71457
71826
  const res = await client.setAppWorkflow(meta3.app_id, args.alias, {
@@ -71465,12 +71834,17 @@ async function appWorkflowSet(client, args) {
71465
71834
  // `Workflow invoked by app "X" via useWorkflow("y")`. Absent leaves
71466
71835
  // whatever is on the row, so this can never blank a description set
71467
71836
  // elsewhere.
71468
- description: declaration.description
71837
+ description: declaration.description,
71838
+ // The body this edit was built on. Turns the push into a conditional write:
71839
+ // if the live body moved since this project last saw it, the server refuses
71840
+ // rather than letting this copy overwrite a change nobody here has seen.
71841
+ expected_body_sha: readSynced(projectDir).workflows?.[args.alias]
71469
71842
  });
71470
71843
  if (res.error) {
71471
71844
  console.error(`Failed to set workflow "${args.alias}": ${res.error}`);
71472
71845
  process.exit(1);
71473
71846
  }
71847
+ writeSynced(projectDir, "workflows", args.alias, contentSha(source));
71474
71848
  const result = res.result ?? {};
71475
71849
  const workflowId = typeof result.workflow_id === "string" ? result.workflow_id : "(unknown)";
71476
71850
  console.error(`Set workflow "${args.alias}" \u2192 ${workflowId}`);
@@ -71494,6 +71868,7 @@ async function appQuerySet(client, args) {
71494
71868
  const meta3 = readAppMeta(projectDir);
71495
71869
  const declared = meta3.queries ?? {};
71496
71870
  const aliases = "all" in args ? Object.keys(declared).sort() : [args.alias];
71871
+ if (!("all" in args)) noteScope({ alias: args.alias });
71497
71872
  if (aliases.length === 0) {
71498
71873
  console.error("No queries in package.json#lotics.queries \u2014 nothing to push.");
71499
71874
  return;
@@ -71507,12 +71882,18 @@ async function appQuerySet(client, args) {
71507
71882
  );
71508
71883
  process.exit(1);
71509
71884
  }
71510
- const res = await client.setAppQuery(meta3.app_id, alias, declaration);
71885
+ const res = await client.setAppQuery(
71886
+ meta3.app_id,
71887
+ alias,
71888
+ declaration,
71889
+ readSynced(projectDir).queries?.[alias]
71890
+ );
71511
71891
  if (res.error) {
71512
71892
  if (pushed.length > 0) console.error(`Pushed before the failure: ${pushed.join(", ")}.`);
71513
71893
  console.error(`Failed to set query "${alias}": ${res.error}`);
71514
71894
  process.exit(1);
71515
71895
  }
71896
+ writeSynced(projectDir, "queries", alias, declarationSha(declaration));
71516
71897
  pushed.push(alias);
71517
71898
  }
71518
71899
  console.error(
@@ -71558,15 +71939,15 @@ async function appWorkflowCheck(args) {
71558
71939
  for (const alias of aliases) {
71559
71940
  const bodyPath = workflowFilePath(projectDir, alias);
71560
71941
  const globalsPath = workflowGlobalsPath(projectDir, alias);
71561
- if (!fs4.existsSync(bodyPath)) {
71942
+ if (!fs6.existsSync(bodyPath)) {
71562
71943
  console.error(
71563
- `\u26A0 Skipped "${alias}" \u2014 no body at ${path5.relative(projectDir, bodyPath)}. Run 'lotics app workflow pull' to write it.`
71944
+ `\u26A0 Skipped "${alias}" \u2014 no body at ${path7.relative(projectDir, bodyPath)}. Run 'lotics app workflow pull' to write it.`
71564
71945
  );
71565
71946
  continue;
71566
71947
  }
71567
- if (!fs4.existsSync(globalsPath)) {
71948
+ if (!fs6.existsSync(globalsPath)) {
71568
71949
  console.error(
71569
- `Cannot check "${alias}" \u2014 missing types at ${path5.relative(projectDir, globalsPath)}. Run 'lotics app workflow pull' (or 'lotics app codegen') to fetch them.`
71950
+ `Cannot check "${alias}" \u2014 missing types at ${path7.relative(projectDir, globalsPath)}. Run 'lotics app workflow pull' (or 'lotics app codegen') to fetch them.`
71570
71951
  );
71571
71952
  process.exit(1);
71572
71953
  }
@@ -71585,7 +71966,7 @@ async function appWorkflowCheck(args) {
71585
71966
  function printWorkflowCheckResults(projectDir, results) {
71586
71967
  let totalErrors = 0;
71587
71968
  for (const r of results) {
71588
- const rel = path5.relative(projectDir, r.bodyPath);
71969
+ const rel = path7.relative(projectDir, r.bodyPath);
71589
71970
  if (r.issues.length === 0) {
71590
71971
  console.error(`\u2713 ${r.alias} (${rel}) \u2014 clean`);
71591
71972
  continue;
@@ -71789,11 +72170,11 @@ async function validateOrgWorkspacePin(client, orgId, profile) {
71789
72170
  }
71790
72171
 
71791
72172
  // src/xlsx.ts
71792
- import fs6 from "node:fs";
72173
+ import fs8 from "node:fs";
71793
72174
 
71794
72175
  // src/file_command_io.ts
71795
- import fs5 from "node:fs";
71796
- import path6 from "node:path";
72176
+ import fs7 from "node:fs";
72177
+ import path8 from "node:path";
71797
72178
  var CliError = class extends Error {
71798
72179
  constructor(message2) {
71799
72180
  super(message2);
@@ -71803,15 +72184,25 @@ var CliError = class extends Error {
71803
72184
  function fail2(message2) {
71804
72185
  throw new CliError(message2);
71805
72186
  }
72187
+ function rejectUnknownFlags(rest2, known, usage) {
72188
+ const matches = (a) => known.some((k) => k.endsWith("=") ? a.startsWith(k) : a === k);
72189
+ const unknown2 = rest2.filter((a) => a.startsWith("--") && !matches(a));
72190
+ if (unknown2.length === 0) return;
72191
+ fail2(`Unknown ${unknown2.length > 1 ? "flags" : "flag"} for ${usage}: ${unknown2.join(", ")}. Supported: ${known.join(", ")}.`);
72192
+ }
72193
+ function rejectExtraArgs(rest2, arity, usage) {
72194
+ if (rest2.length <= arity) return;
72195
+ fail2(`${usage} takes ${arity} argument${arity === 1 ? "" : "s"} after <file>, got ${rest2.length}. Unused: ${rest2.slice(arity).join(" ")}`);
72196
+ }
71806
72197
  function writeFileAtomic(filePath, bytes) {
71807
- const dir = path6.dirname(path6.resolve(filePath));
71808
- const tmp = path6.join(dir, `.${path6.basename(filePath)}.${process.pid}.${Date.now()}.tmp`);
71809
- fs5.writeFileSync(tmp, bytes);
72198
+ const dir = path8.dirname(path8.resolve(filePath));
72199
+ const tmp = path8.join(dir, `.${path8.basename(filePath)}.${process.pid}.${Date.now()}.tmp`);
72200
+ fs7.writeFileSync(tmp, bytes);
71810
72201
  try {
71811
- fs5.renameSync(tmp, filePath);
72202
+ fs7.renameSync(tmp, filePath);
71812
72203
  } catch (error52) {
71813
72204
  try {
71814
- fs5.unlinkSync(tmp);
72205
+ fs7.unlinkSync(tmp);
71815
72206
  } catch {
71816
72207
  }
71817
72208
  throw error52;
@@ -74608,16 +74999,16 @@ var MatcherView = class {
74608
74999
  * @returns {string|undefined}
74609
75000
  */
74610
75001
  getCurrentTag() {
74611
- const path8 = this._matcher.path;
74612
- return path8.length > 0 ? path8[path8.length - 1].tag : void 0;
75002
+ const path10 = this._matcher.path;
75003
+ return path10.length > 0 ? path10[path10.length - 1].tag : void 0;
74613
75004
  }
74614
75005
  /**
74615
75006
  * Get current namespace.
74616
75007
  * @returns {string|undefined}
74617
75008
  */
74618
75009
  getCurrentNamespace() {
74619
- const path8 = this._matcher.path;
74620
- return path8.length > 0 ? path8[path8.length - 1].namespace : void 0;
75010
+ const path10 = this._matcher.path;
75011
+ return path10.length > 0 ? path10[path10.length - 1].namespace : void 0;
74621
75012
  }
74622
75013
  /**
74623
75014
  * Get current node's attribute value.
@@ -74625,9 +75016,9 @@ var MatcherView = class {
74625
75016
  * @returns {*}
74626
75017
  */
74627
75018
  getAttrValue(attrName) {
74628
- const path8 = this._matcher.path;
74629
- if (path8.length === 0) return void 0;
74630
- return path8[path8.length - 1].values?.[attrName];
75019
+ const path10 = this._matcher.path;
75020
+ if (path10.length === 0) return void 0;
75021
+ return path10[path10.length - 1].values?.[attrName];
74631
75022
  }
74632
75023
  /**
74633
75024
  * Check if current node has an attribute.
@@ -74635,9 +75026,9 @@ var MatcherView = class {
74635
75026
  * @returns {boolean}
74636
75027
  */
74637
75028
  hasAttr(attrName) {
74638
- const path8 = this._matcher.path;
74639
- if (path8.length === 0) return false;
74640
- const current = path8[path8.length - 1];
75029
+ const path10 = this._matcher.path;
75030
+ if (path10.length === 0) return false;
75031
+ const current = path10[path10.length - 1];
74641
75032
  return current.values !== void 0 && attrName in current.values;
74642
75033
  }
74643
75034
  /**
@@ -74663,18 +75054,18 @@ var MatcherView = class {
74663
75054
  * @returns {number}
74664
75055
  */
74665
75056
  getPosition() {
74666
- const path8 = this._matcher.path;
74667
- if (path8.length === 0) return -1;
74668
- return path8[path8.length - 1].position ?? 0;
75057
+ const path10 = this._matcher.path;
75058
+ if (path10.length === 0) return -1;
75059
+ return path10[path10.length - 1].position ?? 0;
74669
75060
  }
74670
75061
  /**
74671
75062
  * Get current node's repeat counter (occurrence count of this tag name).
74672
75063
  * @returns {number}
74673
75064
  */
74674
75065
  getCounter() {
74675
- const path8 = this._matcher.path;
74676
- if (path8.length === 0) return -1;
74677
- return path8[path8.length - 1].counter ?? 0;
75066
+ const path10 = this._matcher.path;
75067
+ if (path10.length === 0) return -1;
75068
+ return path10[path10.length - 1].counter ?? 0;
74678
75069
  }
74679
75070
  /**
74680
75071
  * Get current node's sibling index (alias for getPosition).
@@ -80332,10 +80723,10 @@ function resolveRelativePath(basePath, relativePath) {
80332
80723
  }
80333
80724
  return resolved.join("/");
80334
80725
  }
80335
- function findZipEntry(zipEntries, path8) {
80336
- if (zipEntries[path8]) return zipEntries[path8];
80337
- if (zipEntries[`xl/${path8}`]) return zipEntries[`xl/${path8}`];
80338
- const noSlash = path8.replace(/^\//, "");
80726
+ function findZipEntry(zipEntries, path10) {
80727
+ if (zipEntries[path10]) return zipEntries[path10];
80728
+ if (zipEntries[`xl/${path10}`]) return zipEntries[`xl/${path10}`];
80729
+ const noSlash = path10.replace(/^\//, "");
80339
80730
  if (zipEntries[noSlash]) return zipEntries[noSlash];
80340
80731
  return void 0;
80341
80732
  }
@@ -81964,9 +82355,9 @@ function parseExcelFromZip(zip, options) {
81964
82355
  }
81965
82356
  return result;
81966
82357
  }
81967
- function findEntry(zip, path8) {
81968
- if (zip[path8]) return zip[path8];
81969
- const lower2 = path8.toLowerCase();
82358
+ function findEntry(zip, path10) {
82359
+ if (zip[path10]) return zip[path10];
82360
+ const lower2 = path10.toLowerCase();
81970
82361
  for (const key of Object.keys(zip)) {
81971
82362
  if (key.toLowerCase() === lower2) return zip[key];
81972
82363
  }
@@ -82603,12 +82994,12 @@ function extractPivotRoundTripInfo(originalZip) {
82603
82994
  if (paths.length > 0) pivotTablesBySheetIndex.set(i2, paths);
82604
82995
  }
82605
82996
  const pivotXmlPaths = [];
82606
- for (const path8 of Object.keys(originalZip)) {
82607
- if (path8.startsWith("xl/pivotTables/") && path8.endsWith(".xml")) {
82608
- pivotXmlPaths.push(path8);
82997
+ for (const path10 of Object.keys(originalZip)) {
82998
+ if (path10.startsWith("xl/pivotTables/") && path10.endsWith(".xml")) {
82999
+ pivotXmlPaths.push(path10);
82609
83000
  }
82610
- if (path8.startsWith("xl/pivotCache/") && path8.endsWith(".xml")) {
82611
- pivotXmlPaths.push(path8);
83001
+ if (path10.startsWith("xl/pivotCache/") && path10.endsWith(".xml")) {
83002
+ pivotXmlPaths.push(path10);
82612
83003
  }
82613
83004
  }
82614
83005
  return { cachesByWorkbook, pivotTablesBySheetIndex, pivotXmlPaths };
@@ -82616,15 +83007,15 @@ function extractPivotRoundTripInfo(originalZip) {
82616
83007
  function decode3(bytes) {
82617
83008
  return new TextDecoder().decode(bytes);
82618
83009
  }
82619
- function pivotContentTypeFor(path8) {
82620
- if (path8.startsWith("xl/pivotTables/") && path8.endsWith(".xml")) {
82621
- return `<Override PartName="/${path8}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml"/>`;
83010
+ function pivotContentTypeFor(path10) {
83011
+ if (path10.startsWith("xl/pivotTables/") && path10.endsWith(".xml")) {
83012
+ return `<Override PartName="/${path10}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml"/>`;
82622
83013
  }
82623
- if (path8.includes("/pivotCacheDefinition") && path8.endsWith(".xml")) {
82624
- return `<Override PartName="/${path8}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml"/>`;
83014
+ if (path10.includes("/pivotCacheDefinition") && path10.endsWith(".xml")) {
83015
+ return `<Override PartName="/${path10}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml"/>`;
82625
83016
  }
82626
- if (path8.includes("/pivotCacheRecords") && path8.endsWith(".xml")) {
82627
- return `<Override PartName="/${path8}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml"/>`;
83017
+ if (path10.includes("/pivotCacheRecords") && path10.endsWith(".xml")) {
83018
+ return `<Override PartName="/${path10}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml"/>`;
82628
83019
  }
82629
83020
  return void 0;
82630
83021
  }
@@ -82697,16 +83088,16 @@ function exportWorkbook(workbook, originalZip) {
82697
83088
  regeneratedPaths.add(`xl/worksheets/sheet${i2 + 1}.xml`);
82698
83089
  regeneratedPaths.add(`xl/worksheets/_rels/sheet${i2 + 1}.xml.rels`);
82699
83090
  }
82700
- for (const path8 of Object.keys(originalZip)) {
82701
- if (path8.startsWith("xl/drawings/") || path8.startsWith("xl/charts/") || path8.startsWith("xl/tables/") || path8.startsWith("xl/media/")) {
82702
- regeneratedPaths.add(path8);
83091
+ for (const path10 of Object.keys(originalZip)) {
83092
+ if (path10.startsWith("xl/drawings/") || path10.startsWith("xl/charts/") || path10.startsWith("xl/tables/") || path10.startsWith("xl/media/")) {
83093
+ regeneratedPaths.add(path10);
82703
83094
  }
82704
83095
  }
82705
- for (const [path8, data2] of Object.entries(originalZip)) {
82706
- if (regeneratedPaths.has(path8)) continue;
82707
- const ext = path8.slice(path8.lastIndexOf(".") + 1).toLowerCase();
83096
+ for (const [path10, data2] of Object.entries(originalZip)) {
83097
+ if (regeneratedPaths.has(path10)) continue;
83098
+ const ext = path10.slice(path10.lastIndexOf(".") + 1).toLowerCase();
82708
83099
  if (!DECLARED_EXTENSIONS.includes(ext)) continue;
82709
- entries2[path8] = data2;
83100
+ entries2[path10] = data2;
82710
83101
  }
82711
83102
  }
82712
83103
  const sharedStrings = buildSharedStrings(workbook);
@@ -82722,8 +83113,8 @@ function exportWorkbook(workbook, originalZip) {
82722
83113
  buildWorkbookRels(workbook, pivotInfo)
82723
83114
  );
82724
83115
  const extraContentTypes = [];
82725
- for (const path8 of pivotInfo.pivotXmlPaths) {
82726
- const ct = pivotContentTypeFor(path8);
83116
+ for (const path10 of pivotInfo.pivotXmlPaths) {
83117
+ const ct = pivotContentTypeFor(path10);
82727
83118
  if (ct) extraContentTypes.push(ct);
82728
83119
  }
82729
83120
  let globalChartIndex = 1;
@@ -91746,14 +92137,14 @@ var DependencyGraph = class {
91746
92137
  stack.push({ cell: p, path: [cell, p] });
91747
92138
  }
91748
92139
  while (stack.length > 0) {
91749
- const { cell: current, path: path8 } = stack.pop();
91750
- if (current === cell) return path8;
92140
+ const { cell: current, path: path10 } = stack.pop();
92141
+ if (current === cell) return path10;
91751
92142
  if (visited.has(current)) continue;
91752
92143
  visited.add(current);
91753
92144
  const nextPrec = this.precedents.get(current);
91754
92145
  if (!nextPrec) continue;
91755
92146
  for (const p of nextPrec) {
91756
- stack.push({ cell: p, path: [...path8, p] });
92147
+ stack.push({ cell: p, path: [...path10, p] });
91757
92148
  }
91758
92149
  }
91759
92150
  return null;
@@ -92196,6 +92587,26 @@ function computeDisplay(value2, numFmtCode, date1904) {
92196
92587
  if (typeof value2 === "string") return value2;
92197
92588
  return formatNumFmt(value2, numFmtCode, date1904).text;
92198
92589
  }
92590
+ function recomputeAll(workbook) {
92591
+ const engine = new FormulaEngine(workbook);
92592
+ engine.buildGraph();
92593
+ engine.evaluateAll();
92594
+ return collectRecomputeReport(workbook);
92595
+ }
92596
+ function collectRecomputeReport(workbook) {
92597
+ let total = 0;
92598
+ const unevaluated = [];
92599
+ for (const sheet of workbook.sheets) {
92600
+ for (const [ref2, cell] of sheet.cells) {
92601
+ if (!cell.formula) continue;
92602
+ total++;
92603
+ if (cell.value === null) {
92604
+ unevaluated.push({ sheet: sheet.name, ref: ref2, formula: cell.formula, error: cell.error ?? "#ERROR!" });
92605
+ }
92606
+ }
92607
+ }
92608
+ return { total, unevaluated };
92609
+ }
92199
92610
 
92200
92611
  // ../xlsx/src/clipboard.ts
92201
92612
  function adjustFormulaRefsForRowShift(formula, atRow, delta) {
@@ -92945,8 +93356,8 @@ var SIMPLE_SHEET_KEYS = /* @__PURE__ */ new Set(["name", "cells", "merges", "fre
92945
93356
  var MAX_COLUMN = 16384;
92946
93357
  var MAX_ROW = 1048576;
92947
93358
  function loadFile(filePath) {
92948
- if (!fs6.existsSync(filePath)) fail2(`File not found: ${filePath}`);
92949
- const buffer = fs6.readFileSync(filePath);
93359
+ if (!fs8.existsSync(filePath)) fail2(`File not found: ${filePath}`);
93360
+ const buffer = fs8.readFileSync(filePath);
92950
93361
  const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
92951
93362
  const originalZip = unzipXlsx(arrayBuffer);
92952
93363
  const parsed = parseExcelBuffer(arrayBuffer);
@@ -92996,7 +93407,7 @@ function lazyEngine(workbook) {
92996
93407
  };
92997
93408
  }
92998
93409
  function readToJson(filePath, filter3, opts) {
92999
- const buffer = fs6.readFileSync(filePath);
93410
+ const buffer = fs8.readFileSync(filePath);
93000
93411
  const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
93001
93412
  const parsed = parseExcelBuffer(arrayBuffer);
93002
93413
  let sheets = parsed.sheets;
@@ -93163,6 +93574,7 @@ function splitOptionalSheetRef(spec) {
93163
93574
  }
93164
93575
  function xlsxRead(filePath, rest2) {
93165
93576
  if (!filePath) fail2("Usage: lotics xlsx read <file> [--sheet <name>] [--range <sheet>!<A1:G60>] [--with-format]");
93577
+ rejectUnknownFlags(rest2, ["--sheet", "--range", "--with-format"], "xlsx read");
93166
93578
  const rangeArg = readOptionFlag(rest2, "--range");
93167
93579
  const sheetArg = readOptionFlag(rest2, "--sheet");
93168
93580
  const withFormat = rest2.includes("--with-format");
@@ -93189,6 +93601,12 @@ function xlsxWrite(filePath, json2) {
93189
93601
  fail2(`Invalid JSON: ${e instanceof Error ? e.message : String(e)}`);
93190
93602
  }
93191
93603
  const workbook = buildWorkbookFromJson(parsed);
93604
+ const report = recomputeAll(workbook);
93605
+ if (report.unevaluated.length > 0) {
93606
+ const shown = report.unevaluated.slice(0, 5).map((u) => `${u.sheet}!${u.ref} (${u.formula}): ${u.error}`).join("; ");
93607
+ const rest2 = report.unevaluated.length - Math.min(5, report.unevaluated.length);
93608
+ console.error(`Warning: ${report.unevaluated.length} of ${report.total} formula(s) could not be evaluated \u2014 written without a cached value: ${shown}${rest2 > 0 ? `; +${rest2} more` : ""}`);
93609
+ }
93192
93610
  const bytes = exportWorkbook(workbook);
93193
93611
  writeFileAtomic(filePath, bytes);
93194
93612
  }
@@ -93442,7 +93860,25 @@ Uses Lotics' own xlsx engine; round-trips faithfully with the Lotics editor and
93442
93860
 
93443
93861
  Edit ops mutate the file atomically (temp file + rename).`);
93444
93862
  }
93863
+ var FIXED_ARITY = /* @__PURE__ */ new Map([
93864
+ ["write", 1],
93865
+ ["set-cell", 2],
93866
+ ["clear-range", 1],
93867
+ ["merge", 1],
93868
+ ["unmerge", 1],
93869
+ ["add-sheet", 1],
93870
+ ["delete-sheet", 1],
93871
+ ["rename-sheet", 2],
93872
+ ["insert-rows", 3],
93873
+ ["delete-rows", 3],
93874
+ ["insert-cols", 3],
93875
+ ["delete-cols", 3],
93876
+ ["set-style", 2],
93877
+ ["batch", 1]
93878
+ ]);
93445
93879
  async function runXlsxCommand(subcommand, toolArgs, restArgs) {
93880
+ const arity = FIXED_ARITY.get(subcommand ?? "");
93881
+ if (arity !== void 0) rejectExtraArgs(restArgs, arity, `lotics xlsx ${subcommand}`);
93446
93882
  switch (subcommand) {
93447
93883
  case "read":
93448
93884
  return xlsxRead(toolArgs, restArgs);
@@ -93484,7 +93920,7 @@ async function runXlsxCommand(subcommand, toolArgs, restArgs) {
93484
93920
  }
93485
93921
 
93486
93922
  // src/docx.ts
93487
- import fs7 from "node:fs";
93923
+ import fs9 from "node:fs";
93488
93924
 
93489
93925
  // ../ooxml/src/doc/errors.ts
93490
93926
  var DocParseError = class extends Error {
@@ -98100,8 +98536,8 @@ async function docModelToDocx(model) {
98100
98536
  // ../ooxml/src/opc.ts
98101
98537
  var import_jszip = __toESM(require_lib4(), 1);
98102
98538
  function dropFolderEntries(zip) {
98103
- for (const [path8, entry] of Object.entries(zip.files)) {
98104
- if (entry.dir) delete zip.files[path8];
98539
+ for (const [path10, entry] of Object.entries(zip.files)) {
98540
+ if (entry.dir) delete zip.files[path10];
98105
98541
  }
98106
98542
  }
98107
98543
 
@@ -98333,11 +98769,11 @@ async function parseDocx(buffer) {
98333
98769
  const zip = await import_jszip3.default.loadAsync(buffer);
98334
98770
  const parts = /* @__PURE__ */ new Map();
98335
98771
  const filePromises = [];
98336
- zip.forEach((path8, file2) => {
98772
+ zip.forEach((path10, file2) => {
98337
98773
  if (file2.dir) return;
98338
98774
  filePromises.push(
98339
98775
  file2.async("uint8array").then((bytes) => {
98340
- parts.set(path8, bytes);
98776
+ parts.set(path10, bytes);
98341
98777
  })
98342
98778
  );
98343
98779
  });
@@ -98569,9 +99005,9 @@ var DEFAULT_PARTS = [
98569
99005
  ["word/theme/theme1.xml", () => encoder.encode(THEME_XML)]
98570
99006
  ];
98571
99007
  function fillDefaultParts(parts) {
98572
- for (const [path8, makeBytes] of DEFAULT_PARTS) {
98573
- if (!parts.has(path8)) {
98574
- parts.set(path8, makeBytes());
99008
+ for (const [path10, makeBytes] of DEFAULT_PARTS) {
99009
+ if (!parts.has(path10)) {
99010
+ parts.set(path10, makeBytes());
98575
99011
  }
98576
99012
  }
98577
99013
  return parts;
@@ -98581,9 +99017,9 @@ function fillDefaultParts(parts) {
98581
99017
  async function serializeDocx(doc) {
98582
99018
  const zip = new import_jszip4.default();
98583
99019
  const parts = fillDefaultParts(new Map(doc.parts));
98584
- for (const [path8, bytes] of parts) {
98585
- if (path8 === "word/document.xml") continue;
98586
- zip.file(path8, bytes);
99020
+ for (const [path10, bytes] of parts) {
99021
+ if (path10 === "word/document.xml") continue;
99022
+ zip.file(path10, bytes);
98587
99023
  }
98588
99024
  const documentXml = serializeDocumentXml(doc);
98589
99025
  zip.file("word/document.xml", documentXml);
@@ -98670,8 +99106,8 @@ var DEFAULT_DOC_ATTRS = {
98670
99106
  "@_xmlns:w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
98671
99107
  };
98672
99108
  async function loadFile2(filePath) {
98673
- if (!fs7.existsSync(filePath)) fail2(`File not found: ${filePath}`);
98674
- const bytes = fs7.readFileSync(filePath);
99109
+ if (!fs9.existsSync(filePath)) fail2(`File not found: ${filePath}`);
99110
+ const bytes = fs9.readFileSync(filePath);
98675
99111
  if (isLegacyDoc(bytes)) {
98676
99112
  const upgraded = await saveDocxToBuffer(await loadDocxFromBuffer(bytes));
98677
99113
  return parseDocx(new Uint8Array(upgraded));
@@ -98783,6 +99219,7 @@ function buildDoc(input) {
98783
99219
  }
98784
99220
  async function docxRead(filePath, rest2) {
98785
99221
  if (!filePath) fail2("Usage: lotics docx read <file> [--include-opaque]");
99222
+ rejectUnknownFlags(rest2, ["--include-opaque"], "docx read");
98786
99223
  const includeOpaque = rest2.includes("--include-opaque");
98787
99224
  const doc = await loadFile2(filePath);
98788
99225
  console.log(JSON.stringify(readToJson2(doc, includeOpaque), null, 2));
@@ -98817,6 +99254,7 @@ function firstPositional(rest2) {
98817
99254
  return rest2.find((a) => !a.startsWith("--"));
98818
99255
  }
98819
99256
  async function docxAppendParagraph(filePath, rest2) {
99257
+ rejectUnknownFlags(rest2, ["--style="], "docx append-paragraph");
98820
99258
  const text = firstPositional(rest2);
98821
99259
  if (!filePath || text === void 0) fail2("Usage: lotics docx append-paragraph <file> '<text>' [--style=NAME]");
98822
99260
  const style = getStyleFlag(rest2);
@@ -98826,6 +99264,7 @@ async function docxAppendParagraph(filePath, rest2) {
98826
99264
  await writeDoc(filePath, replaceChildren(doc, newChildren));
98827
99265
  }
98828
99266
  async function docxInsertParagraph(filePath, rest2) {
99267
+ rejectUnknownFlags(rest2, ["--style=", "--at="], "docx insert-paragraph");
98829
99268
  const text = firstPositional(rest2);
98830
99269
  const at2 = getAtFlag(rest2);
98831
99270
  if (!filePath || text === void 0 || at2 === void 0) {
@@ -98842,6 +99281,7 @@ async function docxInsertParagraph(filePath, rest2) {
98842
99281
  await writeDoc(filePath, replaceChildren(doc, newChildren));
98843
99282
  }
98844
99283
  async function docxDeleteBlock(filePath, rest2) {
99284
+ rejectUnknownFlags(rest2, ["--at="], "docx delete-block");
98845
99285
  const at2 = getAtFlag(rest2);
98846
99286
  if (!filePath || at2 === void 0) fail2("Usage: lotics docx delete-block <file> --at=<index>");
98847
99287
  const doc = await loadFile2(filePath);
@@ -99119,7 +99559,14 @@ join across something that occupies space in the text (a line break, tab, symbol
99119
99559
  because the joined string does not represent it. The same rule applies inside a table cell as outside it. When a
99120
99560
  search fails but the words ARE on the page, the error names the block and the mark that split them.`);
99121
99561
  }
99562
+ var DOCX_FIXED_ARITY = /* @__PURE__ */ new Map([
99563
+ ["write", 1],
99564
+ ["replace-text", 2],
99565
+ ["batch", 1]
99566
+ ]);
99122
99567
  async function runDocxCommand(subcommand, toolArgs, restArgs) {
99568
+ const arity = DOCX_FIXED_ARITY.get(subcommand ?? "");
99569
+ if (arity !== void 0) rejectExtraArgs(restArgs, arity, `lotics docx ${subcommand}`);
99123
99570
  switch (subcommand) {
99124
99571
  case "read":
99125
99572
  return docxRead(toolArgs, restArgs);
@@ -99145,10 +99592,10 @@ async function runDocxCommand(subcommand, toolArgs, restArgs) {
99145
99592
  }
99146
99593
 
99147
99594
  // src/knowledge.ts
99148
- import fs8 from "node:fs";
99595
+ import fs10 from "node:fs";
99149
99596
  function readBodyFile(filePath) {
99150
- if (!fs8.existsSync(filePath)) fail2(`File not found: ${filePath}`);
99151
- return fs8.readFileSync(filePath, "utf-8");
99597
+ if (!fs10.existsSync(filePath)) fail2(`File not found: ${filePath}`);
99598
+ return fs10.readFileSync(filePath, "utf-8");
99152
99599
  }
99153
99600
  function resolveBody(flags, required2) {
99154
99601
  if (flags.from !== void 0 && flags.content !== void 0) {
@@ -99485,6 +99932,7 @@ async function runPreviewCommand(filePath, flags) {
99485
99932
  }
99486
99933
 
99487
99934
  // src/cli.ts
99935
+ var startedAt = Date.now();
99488
99936
  dns.setDefaultResultOrder("ipv4first");
99489
99937
  net2.setDefaultAutoSelectFamilyAttemptTimeout(2e3);
99490
99938
  function printHelp() {
@@ -99548,7 +99996,8 @@ COMMANDS
99548
99996
  lotics app codegen [path] Regenerate .lotics/* (types + field/option ids)
99549
99997
  from the manifest + workspace schema \u2014 no deploy
99550
99998
  lotics app check Run every deploy pre-flight WITHOUT building or
99551
- shipping: agent schemas vs the live app, aliases
99999
+ shipping: agent schemas vs the live app, workflow
100000
+ declarations and BODIES vs what is live, aliases
99552
100001
  the code calls but nothing bound, undeclared
99553
100002
  capabilities, query drift. Exits 1 on what a
99554
100003
  deploy would refuse, so CI can gate on it
@@ -99697,8 +100146,8 @@ function prompt(question) {
99697
100146
  });
99698
100147
  });
99699
100148
  }
99700
- async function publicPost(path8, body) {
99701
- const response = await fetch(`${API_BASE_URL}${path8}`, {
100149
+ async function publicPost(path10, body) {
100150
+ const response = await fetch(`${API_BASE_URL}${path10}`, {
99702
100151
  method: "POST",
99703
100152
  headers: { "Content-Type": "application/json" },
99704
100153
  body: JSON.stringify(body)
@@ -99795,6 +100244,8 @@ function requireClient(flags, appWorkspaceId) {
99795
100244
  process.exit(1);
99796
100245
  }
99797
100246
  const viewAsMemberId = flags.viewAs ?? process.env.LOTICS_VIEW_AS;
100247
+ noteScope({ org_id: ctx.orgId ?? void 0, workspace_id: ctx.workspaceId });
100248
+ flushSpool((events) => postEvents(API_BASE_URL, ctx.apiKey, events));
99798
100249
  return {
99799
100250
  client: new LoticsClient({ apiKey: ctx.apiKey, workspaceId: ctx.workspaceId, viewAsMemberId }),
99800
100251
  ctx
@@ -99809,6 +100260,7 @@ var SOURCE_LABELS = {
99809
100260
  global_profile: "global active profile"
99810
100261
  };
99811
100262
  function announceTarget(ctx, workspaceId) {
100263
+ noteScope({ org_id: ctx.orgId ?? void 0, workspace_id: workspaceId });
99812
100264
  const org = ctx.orgName ?? ctx.orgId ?? "(org from key)";
99813
100265
  if (ctx.source === "global_profile") {
99814
100266
  console.error(
@@ -99863,13 +100315,13 @@ async function resolveWorkspace(client, ctx) {
99863
100315
  function resolveUploadPaths(rawPaths) {
99864
100316
  const result = [];
99865
100317
  for (const p of rawPaths) {
99866
- const resolved = path7.resolve(p);
99867
- const stat = fs9.statSync(resolved);
100318
+ const resolved = path9.resolve(p);
100319
+ const stat = fs11.statSync(resolved);
99868
100320
  if (stat.isDirectory()) {
99869
- const entries2 = fs9.readdirSync(resolved, { withFileTypes: true });
100321
+ const entries2 = fs11.readdirSync(resolved, { withFileTypes: true });
99870
100322
  for (const entry of entries2) {
99871
100323
  if (entry.isFile()) {
99872
- result.push(path7.join(resolved, entry.name));
100324
+ result.push(path9.join(resolved, entry.name));
99873
100325
  }
99874
100326
  }
99875
100327
  } else {
@@ -99879,6 +100331,8 @@ function resolveUploadPaths(rawPaths) {
99879
100331
  return result;
99880
100332
  }
99881
100333
  async function main() {
100334
+ setInvocation(commandPath(process.argv.slice(2)), sessionId(), VERSION);
100335
+ installTelemetry(startedAt);
99882
100336
  const parsed = parseArgs(process.argv.slice(2));
99883
100337
  const { restArgs, flags } = parsed;
99884
100338
  let { command, subcommand, toolArgs } = parsed;
@@ -99992,13 +100446,13 @@ async function main() {
99992
100446
  if (command === "preview") {
99993
100447
  if (subcommand && isStoredFileId(subcommand)) {
99994
100448
  const { client: client2 } = requireClient(flags);
99995
- const tmpDir = fs9.mkdtempSync(path7.join(os2.tmpdir(), "lotics-preview-"));
100449
+ const tmpDir = fs11.mkdtempSync(path9.join(os4.tmpdir(), "lotics-preview-"));
99996
100450
  try {
99997
100451
  const { path: localPath, filename } = await client2.downloadFileById(subcommand, tmpDir);
99998
100452
  const output = flags.output ?? defaultPreviewOutputPath(filename, process.cwd());
99999
100453
  await runPreviewCommand(localPath, { output });
100000
100454
  } finally {
100001
- fs9.rmSync(tmpDir, { recursive: true, force: true });
100455
+ fs11.rmSync(tmpDir, { recursive: true, force: true });
100002
100456
  }
100003
100457
  return;
100004
100458
  }
@@ -100336,7 +100790,7 @@ Available workspaces:`);
100336
100790
  const ingested = await ingestJsonArgs({
100337
100791
  rawArg: restArgs[1],
100338
100792
  stdinIsTTY: process.stdin.isTTY ?? false,
100339
- readFile: (p) => fs9.readFileSync(p, "utf-8"),
100793
+ readFile: (p) => fs11.readFileSync(p, "utf-8"),
100340
100794
  readStdin
100341
100795
  });
100342
100796
  if (ingested.kind === "error") {
@@ -100387,7 +100841,7 @@ Available workspaces:`);
100387
100841
  const ingested = await ingestJsonArgs({
100388
100842
  rawArg: restArgs[2],
100389
100843
  stdinIsTTY: process.stdin.isTTY ?? false,
100390
- readFile: (p) => fs9.readFileSync(p, "utf-8"),
100844
+ readFile: (p) => fs11.readFileSync(p, "utf-8"),
100391
100845
  readStdin
100392
100846
  });
100393
100847
  if (ingested.kind === "error") {
@@ -100543,7 +100997,7 @@ ${JSON.stringify(info.input_schema, null, 2)}`);
100543
100997
  const ingested = await ingestJsonArgs({
100544
100998
  rawArg: toolArgs,
100545
100999
  stdinIsTTY: process.stdin.isTTY ?? false,
100546
- readFile: (p) => fs9.readFileSync(p, "utf-8"),
101000
+ readFile: (p) => fs11.readFileSync(p, "utf-8"),
100547
101001
  readStdin
100548
101002
  });
100549
101003
  if (ingested.kind === "error") {
@@ -100551,6 +101005,7 @@ ${JSON.stringify(info.input_schema, null, 2)}`);
100551
101005
  process.exit(1);
100552
101006
  }
100553
101007
  const args = ingested.args;
101008
+ noteToolArgs(args);
100554
101009
  const timeoutMs = flags.timeout ?? 6e4;
100555
101010
  const result = await client.execute(toolName, args, { format: "text", timeoutMs });
100556
101011
  if (result.error) {