@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/AGENTS.md +4 -0
- package/README.md +13 -0
- package/dist/src/cli.js +838 -383
- package/dist/src/client.d.ts +16 -1
- package/dist/src/client.js +30 -2
- package/dist/src/invocation.d.ts +51 -0
- package/dist/src/invocation.js +142 -0
- package/docs/cli_reference.md +1 -1
- package/package.json +1 -1
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(
|
|
26826
|
-
utils_1.forEach(
|
|
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 =
|
|
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,
|
|
28593
|
+
function AbstractNextPossibleTokensWalker2(topProd, path10) {
|
|
28594
28594
|
var _this = _super.call(this) || this;
|
|
28595
28595
|
_this.topProd = topProd;
|
|
28596
|
-
_this.path =
|
|
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,
|
|
28651
|
-
var _this = _super.call(this, topProd,
|
|
28652
|
-
_this.path =
|
|
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(
|
|
29349
|
+
function pathToHashKeys(path10) {
|
|
29350
29350
|
var keys3 = [""];
|
|
29351
|
-
for (var i2 = 0; i2 <
|
|
29352
|
-
var tokType =
|
|
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,
|
|
29665
|
-
if (
|
|
29666
|
-
|
|
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:
|
|
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,
|
|
29685
|
+
var validNextSteps = utils2.difference(nextNonTerminals, path10.concat([topRule]));
|
|
29686
29686
|
var errorsFromNextSteps = utils2.map(validNextSteps, function(currRefRule) {
|
|
29687
|
-
var newPath = utils2.cloneArr(
|
|
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(
|
|
37297
|
-
var parts =
|
|
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(
|
|
43151
|
-
if (
|
|
43152
|
-
|
|
43150
|
+
var parentFolder = function(path10) {
|
|
43151
|
+
if (path10.slice(-1) === "/") {
|
|
43152
|
+
path10 = path10.substring(0, path10.length - 1);
|
|
43153
43153
|
}
|
|
43154
|
-
var lastSlash =
|
|
43155
|
-
return lastSlash > 0 ?
|
|
43154
|
+
var lastSlash = path10.lastIndexOf("/");
|
|
43155
|
+
return lastSlash > 0 ? path10.substring(0, lastSlash) : "";
|
|
43156
43156
|
};
|
|
43157
|
-
var forceTrailingSlash = function(
|
|
43158
|
-
if (
|
|
43159
|
-
|
|
43157
|
+
var forceTrailingSlash = function(path10) {
|
|
43158
|
+
if (path10.slice(-1) !== "/") {
|
|
43159
|
+
path10 += "/";
|
|
43160
43160
|
}
|
|
43161
|
-
return
|
|
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
|
|
44164
|
-
import
|
|
44165
|
-
import
|
|
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 =
|
|
44253
|
+
const full = path2.join(dir, name2);
|
|
44189
44254
|
if (reserved?.has(full)) return true;
|
|
44190
|
-
return
|
|
44255
|
+
return fs2.existsSync(full);
|
|
44191
44256
|
};
|
|
44192
44257
|
const claim = (name2) => {
|
|
44193
|
-
reserved?.add(
|
|
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 =
|
|
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,
|
|
44266
|
-
const url2 = `${this.baseUrl}${
|
|
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", {
|
|
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 =
|
|
44985
|
-
await
|
|
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 ?
|
|
45091
|
+
const dir = outputDir ? path2.resolve(outputDir) : process.cwd();
|
|
45002
45092
|
const filename = findAvailableFilename(dir, originalFilename, options?.reserved);
|
|
45003
|
-
const absolutePath =
|
|
45004
|
-
await
|
|
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 =
|
|
45033
|
-
const buffer = await
|
|
45034
|
-
const filename = options?.filenames?.[i2] ??
|
|
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
|
|
45052
|
-
import
|
|
45053
|
-
import
|
|
45306
|
+
import fs4 from "node:fs";
|
|
45307
|
+
import path4 from "node:path";
|
|
45308
|
+
import os3 from "node:os";
|
|
45054
45309
|
function globalConfigFile() {
|
|
45055
|
-
return
|
|
45310
|
+
return path4.join(os3.homedir(), ".lotics", "config.json");
|
|
45056
45311
|
}
|
|
45057
45312
|
function configFileForScope(scope) {
|
|
45058
45313
|
if (scope === "local") {
|
|
45059
|
-
return
|
|
45314
|
+
return path4.join(process.cwd(), ".lotics", "config.json");
|
|
45060
45315
|
}
|
|
45061
45316
|
let dir = process.cwd();
|
|
45062
45317
|
for (; ; ) {
|
|
45063
|
-
const candidate =
|
|
45064
|
-
if (
|
|
45318
|
+
const candidate = path4.join(dir, ".lotics", "config.json");
|
|
45319
|
+
if (fs4.existsSync(candidate)) {
|
|
45065
45320
|
return candidate;
|
|
45066
45321
|
}
|
|
45067
|
-
const parent =
|
|
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 =
|
|
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
|
-
|
|
45339
|
+
fs4.mkdirSync(path4.dirname(file2), { recursive: true, mode: 448 });
|
|
45085
45340
|
const tmp = `${file2}.${process.pid}.tmp`;
|
|
45086
|
-
|
|
45087
|
-
|
|
45088
|
-
|
|
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
|
-
|
|
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
|
|
45323
|
-
var pkgPath =
|
|
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
|
|
45329
|
-
import
|
|
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) =>
|
|
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
|
|
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() -
|
|
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
|
|
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() -
|
|
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
|
|
47537
|
-
import
|
|
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,
|
|
48310
|
-
if (!
|
|
48564
|
+
function getElementAtPath(obj, path10) {
|
|
48565
|
+
if (!path10)
|
|
48311
48566
|
return obj;
|
|
48312
|
-
return
|
|
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(
|
|
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(
|
|
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,
|
|
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 }, [...
|
|
49130
|
+
issue2.errors.map((issues) => processError({ issues }, [...path10, ...issue2.path]));
|
|
48876
49131
|
} else if (issue2.code === "invalid_key") {
|
|
48877
|
-
processError({ issues: issue2.issues }, [...
|
|
49132
|
+
processError({ issues: issue2.issues }, [...path10, ...issue2.path]);
|
|
48878
49133
|
} else if (issue2.code === "invalid_element") {
|
|
48879
|
-
processError({ issues: issue2.issues }, [...
|
|
49134
|
+
processError({ issues: issue2.issues }, [...path10, ...issue2.path]);
|
|
48880
49135
|
} else {
|
|
48881
|
-
const fullpath = [...
|
|
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,
|
|
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 }, [...
|
|
49167
|
+
issue2.errors.map((issues) => processError({ issues }, [...path10, ...issue2.path]));
|
|
48913
49168
|
} else if (issue2.code === "invalid_key") {
|
|
48914
|
-
processError({ issues: issue2.issues }, [...
|
|
49169
|
+
processError({ issues: issue2.issues }, [...path10, ...issue2.path]);
|
|
48915
49170
|
} else if (issue2.code === "invalid_element") {
|
|
48916
|
-
processError({ issues: issue2.issues }, [...
|
|
49171
|
+
processError({ issues: issue2.issues }, [...path10, ...issue2.path]);
|
|
48917
49172
|
} else {
|
|
48918
|
-
const fullpath = [...
|
|
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
|
|
48951
|
-
for (const seg of
|
|
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
|
|
61644
|
-
if (
|
|
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 (
|
|
61649
|
-
const key =
|
|
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
|
|
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() -
|
|
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,
|
|
66578
|
+
function getNestedValue(obj, path10) {
|
|
66317
66579
|
if (obj == null) return void 0;
|
|
66318
|
-
const parts =
|
|
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,
|
|
66358
|
-
if (typeof
|
|
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,
|
|
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
|
|
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
|
-
|
|
68509
|
+
path10.push({ at: "index", index: seg.index });
|
|
68248
68510
|
} else if (seg.kind === "bracket-expr") {
|
|
68249
|
-
|
|
68511
|
+
path10.push({ at: "index", index: seg.expr });
|
|
68250
68512
|
} else {
|
|
68251
|
-
|
|
68513
|
+
path10.push({ at: "key", key: seg.key });
|
|
68252
68514
|
}
|
|
68253
68515
|
}
|
|
68254
|
-
return { kind: "read", source: { from: "trigger_changes" }, path:
|
|
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
|
|
68534
|
+
const path10 = [];
|
|
68273
68535
|
for (const seg of segments) {
|
|
68274
68536
|
if (seg.kind === "bracket-number") {
|
|
68275
|
-
|
|
68537
|
+
path10.push({ at: "index", index: seg.index });
|
|
68276
68538
|
} else if (seg.kind === "bracket-expr") {
|
|
68277
|
-
|
|
68539
|
+
path10.push({ at: "index", index: seg.expr });
|
|
68278
68540
|
} else {
|
|
68279
|
-
|
|
68541
|
+
path10.push({ at: "key", key: seg.key });
|
|
68280
68542
|
}
|
|
68281
68543
|
}
|
|
68282
|
-
return { kind: "read", source: { from: "trigger" }, path:
|
|
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
|
|
68566
|
+
const path10 = [];
|
|
68305
68567
|
for (const seg of segments.slice(1)) {
|
|
68306
68568
|
if (seg.kind === "bracket-number") {
|
|
68307
|
-
|
|
68569
|
+
path10.push({ at: "index", index: seg.index });
|
|
68308
68570
|
} else if (seg.kind === "bracket-expr") {
|
|
68309
|
-
|
|
68571
|
+
path10.push({ at: "index", index: seg.expr });
|
|
68310
68572
|
} else {
|
|
68311
|
-
|
|
68573
|
+
path10.push({ at: "key", key: seg.key });
|
|
68312
68574
|
}
|
|
68313
68575
|
}
|
|
68314
|
-
return { kind: "read", source, path:
|
|
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
|
|
68612
|
+
const path10 = [];
|
|
68351
68613
|
for (const seg of segments) {
|
|
68352
68614
|
if (seg.kind === "bracket-number") {
|
|
68353
|
-
|
|
68615
|
+
path10.push({ at: "index", index: seg.index });
|
|
68354
68616
|
} else if (seg.kind === "bracket-expr") {
|
|
68355
|
-
|
|
68617
|
+
path10.push({ at: "index", index: seg.expr });
|
|
68356
68618
|
} else {
|
|
68357
|
-
|
|
68619
|
+
path10.push({ at: "key", key: seg.key });
|
|
68358
68620
|
}
|
|
68359
68621
|
}
|
|
68360
|
-
return { kind: "read", source: { from: "step_output", step_id: stepId }, path:
|
|
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(
|
|
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 =
|
|
70289
|
+
const resolved = path6.resolve(p);
|
|
70028
70290
|
const prefixed = resolved.startsWith("/") ? resolved : `/${resolved}`;
|
|
70029
|
-
return `file://${prefixed.split(
|
|
70291
|
+
return `file://${prefixed.split(path6.sep).join("/")}`;
|
|
70030
70292
|
}
|
|
70031
70293
|
function checkOneWorkflowBody(tsApi, input) {
|
|
70032
|
-
const wrapped =
|
|
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(
|
|
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
|
-
|
|
70185
|
-
|
|
70186
|
-
|
|
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 =
|
|
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
|
|
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
|
|
70519
|
+
return path7.join(projectDir, WORKFLOW_GLOBALS_DIR, `${alias}${GLOBALS_SUFFIX}`);
|
|
70219
70520
|
}
|
|
70220
70521
|
function pruneOrphanWorkflowGlobals(projectDir, declared) {
|
|
70221
|
-
const dir =
|
|
70222
|
-
if (!
|
|
70522
|
+
const dir = path7.join(projectDir, WORKFLOW_GLOBALS_DIR);
|
|
70523
|
+
if (!fs6.existsSync(dir)) return [];
|
|
70223
70524
|
const removed = [];
|
|
70224
|
-
for (const entry of
|
|
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
|
-
|
|
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 =
|
|
70235
|
-
if (!
|
|
70236
|
-
return
|
|
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 =
|
|
70240
|
-
|
|
70540
|
+
const dir = path7.join(projectDir, WORKFLOW_GLOBALS_DIR);
|
|
70541
|
+
fs6.mkdirSync(dir, { recursive: true });
|
|
70241
70542
|
const file2 = workflowGlobalsPath(projectDir, alias);
|
|
70242
|
-
|
|
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 =
|
|
70248
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
70565
|
+
fs6.mkdirSync(path7.join(projectDir, AGENTS_DIR), { recursive: true });
|
|
70265
70566
|
const file2 = agentFilePath2(projectDir, alias);
|
|
70266
|
-
|
|
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 =
|
|
70327
|
-
const pkg2 = JSON.parse(
|
|
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
|
-
|
|
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
|
-
|
|
70352
|
-
|
|
70353
|
-
|
|
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 =
|
|
70387
|
-
if (!
|
|
70388
|
-
const pkg2 = JSON.parse(
|
|
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 =
|
|
70396
|
-
if (!
|
|
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(
|
|
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 =
|
|
70428
|
-
const pkg2 = JSON.parse(
|
|
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
|
-
|
|
70733
|
+
fs6.writeFileSync(pkgPath2, JSON.stringify(pkg2, null, 2) + "\n");
|
|
70436
70734
|
}
|
|
70437
70735
|
function writeWorkflowOutputs(projectDir, alias, outputs) {
|
|
70438
|
-
const pkgPath2 =
|
|
70439
|
-
const pkg2 = JSON.parse(
|
|
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
|
-
|
|
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 =
|
|
70451
|
-
if (!
|
|
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(
|
|
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
|
-
|
|
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 =
|
|
70503
|
-
const runtimeDir =
|
|
70504
|
-
const target =
|
|
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 =
|
|
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
|
-
|
|
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 ?
|
|
70528
|
-
const target =
|
|
70529
|
-
if (!
|
|
70530
|
-
if (
|
|
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
|
-
|
|
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
|
-
` +
|
|
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 =
|
|
70546
|
-
if (!
|
|
70547
|
-
const parsed = JSON.parse(
|
|
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 =
|
|
70553
|
-
|
|
70874
|
+
const dotLotics = path7.join(projectDir, ".lotics");
|
|
70875
|
+
fs6.mkdirSync(dotLotics, { recursive: true });
|
|
70554
70876
|
const written = [
|
|
70555
|
-
[
|
|
70556
|
-
[
|
|
70557
|
-
[
|
|
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)
|
|
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(
|
|
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 =
|
|
70580
|
-
|
|
70581
|
-
const file2 =
|
|
70582
|
-
|
|
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 =
|
|
70587
|
-
|
|
70588
|
-
const file2 =
|
|
70589
|
-
|
|
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 (
|
|
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 =
|
|
70634
|
-
const config2 =
|
|
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 =
|
|
70653
|
-
if (!
|
|
70654
|
-
|
|
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 (!
|
|
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 =
|
|
70663
|
-
if (!
|
|
70664
|
-
const viteConfig =
|
|
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 =
|
|
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 ${
|
|
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 (!
|
|
70738
|
-
const body = stripWorkflowHeader(
|
|
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
|
-
|
|
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 =
|
|
70776
|
-
if (
|
|
70777
|
-
const entries2 =
|
|
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
|
-
|
|
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 =
|
|
70798
|
-
|
|
70799
|
-
|
|
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 ${
|
|
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 =
|
|
70870
|
-
if (
|
|
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(
|
|
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 =
|
|
70893
|
-
|
|
70894
|
-
const tmpFile =
|
|
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 (
|
|
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 ${
|
|
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 =
|
|
70946
|
-
if (!
|
|
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
|
|
70950
|
-
const full =
|
|
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(
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
70987
|
-
if (!
|
|
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 =
|
|
70993
|
-
const tmpDist =
|
|
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:
|
|
71018
|
-
dist_archive:
|
|
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 (
|
|
71064
|
-
if (
|
|
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
|
|
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
|
-
\
|
|
71097
|
-
A deploy does not write them
|
|
71098
|
-
(or one alias at a time),
|
|
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 =
|
|
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
|
-
|
|
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)
|
|
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
|
|
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 (!
|
|
71131
|
-
const local = stripAgentHeader(
|
|
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
|
-
\
|
|
71136
|
-
A deploy does not push prose
|
|
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 =
|
|
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,
|
|
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,
|
|
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
|
|
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:
|
|
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,
|
|
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 ${
|
|
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: ${
|
|
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 (!
|
|
71775
|
+
if (!fs6.existsSync(file2)) {
|
|
71414
71776
|
console.error(
|
|
71415
|
-
`No instructions at ${
|
|
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(
|
|
71781
|
+
const instructions = stripAgentHeader(fs6.readFileSync(file2, "utf-8"));
|
|
71420
71782
|
if (instructions === "") {
|
|
71421
71783
|
console.error(
|
|
71422
|
-
`${
|
|
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, {
|
|
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 (!
|
|
71814
|
+
if (!fs6.existsSync(file2)) {
|
|
71447
71815
|
console.error(
|
|
71448
|
-
`No workflow body at ${
|
|
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
|
-
|
|
71820
|
+
noteScope({ alias: args.alias });
|
|
71821
|
+
const source = stripWorkflowHeader(fs6.readFileSync(file2, "utf-8"));
|
|
71453
71822
|
if (source.trim() === "") {
|
|
71454
|
-
console.error(`Workflow body ${
|
|
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(
|
|
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 (!
|
|
71942
|
+
if (!fs6.existsSync(bodyPath)) {
|
|
71562
71943
|
console.error(
|
|
71563
|
-
`\u26A0 Skipped "${alias}" \u2014 no body at ${
|
|
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 (!
|
|
71948
|
+
if (!fs6.existsSync(globalsPath)) {
|
|
71568
71949
|
console.error(
|
|
71569
|
-
`Cannot check "${alias}" \u2014 missing types at ${
|
|
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 =
|
|
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
|
|
72173
|
+
import fs8 from "node:fs";
|
|
71793
72174
|
|
|
71794
72175
|
// src/file_command_io.ts
|
|
71795
|
-
import
|
|
71796
|
-
import
|
|
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 =
|
|
71808
|
-
const tmp =
|
|
71809
|
-
|
|
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
|
-
|
|
72202
|
+
fs7.renameSync(tmp, filePath);
|
|
71812
72203
|
} catch (error52) {
|
|
71813
72204
|
try {
|
|
71814
|
-
|
|
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
|
|
74612
|
-
return
|
|
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
|
|
74620
|
-
return
|
|
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
|
|
74629
|
-
if (
|
|
74630
|
-
return
|
|
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
|
|
74639
|
-
if (
|
|
74640
|
-
const current =
|
|
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
|
|
74667
|
-
if (
|
|
74668
|
-
return
|
|
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
|
|
74676
|
-
if (
|
|
74677
|
-
return
|
|
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,
|
|
80336
|
-
if (zipEntries[
|
|
80337
|
-
if (zipEntries[`xl/${
|
|
80338
|
-
const noSlash =
|
|
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,
|
|
81968
|
-
if (zip[
|
|
81969
|
-
const lower2 =
|
|
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
|
|
82607
|
-
if (
|
|
82608
|
-
pivotXmlPaths.push(
|
|
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 (
|
|
82611
|
-
pivotXmlPaths.push(
|
|
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(
|
|
82620
|
-
if (
|
|
82621
|
-
return `<Override PartName="/${
|
|
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 (
|
|
82624
|
-
return `<Override PartName="/${
|
|
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 (
|
|
82627
|
-
return `<Override PartName="/${
|
|
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
|
|
82701
|
-
if (
|
|
82702
|
-
regeneratedPaths.add(
|
|
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 [
|
|
82706
|
-
if (regeneratedPaths.has(
|
|
82707
|
-
const ext =
|
|
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[
|
|
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
|
|
82726
|
-
const ct = pivotContentTypeFor(
|
|
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:
|
|
91750
|
-
if (current === cell) return
|
|
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: [...
|
|
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 (!
|
|
92949
|
-
const buffer =
|
|
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 =
|
|
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
|
|
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 [
|
|
98104
|
-
if (entry.dir) delete zip.files[
|
|
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((
|
|
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(
|
|
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 [
|
|
98573
|
-
if (!parts.has(
|
|
98574
|
-
parts.set(
|
|
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 [
|
|
98585
|
-
if (
|
|
98586
|
-
zip.file(
|
|
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 (!
|
|
98674
|
-
const bytes =
|
|
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
|
|
99595
|
+
import fs10 from "node:fs";
|
|
99149
99596
|
function readBodyFile(filePath) {
|
|
99150
|
-
if (!
|
|
99151
|
-
return
|
|
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,
|
|
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(
|
|
99701
|
-
const response = await fetch(`${API_BASE_URL}${
|
|
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 =
|
|
99867
|
-
const stat =
|
|
100318
|
+
const resolved = path9.resolve(p);
|
|
100319
|
+
const stat = fs11.statSync(resolved);
|
|
99868
100320
|
if (stat.isDirectory()) {
|
|
99869
|
-
const entries2 =
|
|
100321
|
+
const entries2 = fs11.readdirSync(resolved, { withFileTypes: true });
|
|
99870
100322
|
for (const entry of entries2) {
|
|
99871
100323
|
if (entry.isFile()) {
|
|
99872
|
-
result.push(
|
|
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 =
|
|
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
|
-
|
|
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) =>
|
|
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) =>
|
|
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) =>
|
|
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) {
|