@lotics/cli 0.116.0 → 0.127.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 +1072 -451
- package/dist/src/client.d.ts +24 -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 +3 -3
- 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,20 +45574,20 @@ 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";
|
|
45333
45588
|
|
|
45334
45589
|
// src/starter_template.ts
|
|
45335
|
-
var STARTER_FALLBACK_UI_VERSION = "
|
|
45590
|
+
var STARTER_FALLBACK_UI_VERSION = "28.1.0";
|
|
45336
45591
|
var STARTER_FALLBACK_SDK_VERSION = "0.52.0";
|
|
45337
45592
|
var STARTER_REACT_NATIVE_VERSION = "0.85.3";
|
|
45338
45593
|
var VITEST_SETUP_FILENAME = "vitest.setup.ts";
|
|
@@ -45538,7 +45793,6 @@ export default defineConfig({
|
|
|
45538
45793
|
// const base = loticsResolve();
|
|
45539
45794
|
// resolve: { ...base, alias: [...base.alias, { find: "x", replacement: "y" }] }
|
|
45540
45795
|
resolve: loticsResolve(),
|
|
45541
|
-
},
|
|
45542
45796
|
optimizeDeps: {
|
|
45543
45797
|
// The dev dep-optimizer must pre-bundle @lotics/ui's RN-ecosystem + markdown
|
|
45544
45798
|
// CJS-interop deps or \`lotics app dev\` blanks the iframe ("does not provide an
|
|
@@ -46559,7 +46813,7 @@ function createUploadRelay(now = Date.now) {
|
|
|
46559
46813
|
}
|
|
46560
46814
|
|
|
46561
46815
|
// src/dev/file_relay.ts
|
|
46562
|
-
import { createHash } from "node:crypto";
|
|
46816
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
46563
46817
|
var TTL_MS = 24 * 60 * 60 * 1e3;
|
|
46564
46818
|
var MAX_ENTRIES = 5e3;
|
|
46565
46819
|
var PRESIGNED_KEYS = /* @__PURE__ */ new Set(["url", "thumbnail_url", "preview_url"]);
|
|
@@ -46567,7 +46821,7 @@ var isFileObject = (o) => typeof o.filename === "string" && typeof o.mime_type =
|
|
|
46567
46821
|
var isRemoteUrl = (v) => typeof v === "string" && /^https?:\/\//i.test(v);
|
|
46568
46822
|
function createFileRelay(wrapperOrigin, now = Date.now) {
|
|
46569
46823
|
const seen = /* @__PURE__ */ new Map();
|
|
46570
|
-
const tokenFor = (url2) =>
|
|
46824
|
+
const tokenFor = (url2) => createHash2("sha256").update(url2).digest("hex").slice(0, 24);
|
|
46571
46825
|
const remember = (url2) => {
|
|
46572
46826
|
const token = tokenFor(url2);
|
|
46573
46827
|
seen.set(token, { url: url2, at: now() });
|
|
@@ -46681,7 +46935,7 @@ async function startDevServer(args) {
|
|
|
46681
46935
|
if (req.method === "POST" && url2 === "/_rpc") {
|
|
46682
46936
|
try {
|
|
46683
46937
|
const body = await readJson(req);
|
|
46684
|
-
const
|
|
46938
|
+
const startedAt2 = Date.now();
|
|
46685
46939
|
const result = await dispatchRpc(
|
|
46686
46940
|
args.client,
|
|
46687
46941
|
{
|
|
@@ -46691,7 +46945,7 @@ async function startDevServer(args) {
|
|
|
46691
46945
|
},
|
|
46692
46946
|
{ commentsEnabled: args.commentsEnabled }
|
|
46693
46947
|
);
|
|
46694
|
-
const ms = Date.now() -
|
|
46948
|
+
const ms = Date.now() - startedAt2;
|
|
46695
46949
|
process.stderr.write(`[rpc] ${body.op} ${ms}ms
|
|
46696
46950
|
`);
|
|
46697
46951
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
@@ -46844,13 +47098,13 @@ async function startDevServer(args) {
|
|
|
46844
47098
|
const chunks = [];
|
|
46845
47099
|
for await (const chunk of req) chunks.push(chunk);
|
|
46846
47100
|
const bytes = Buffer.concat(chunks);
|
|
46847
|
-
const
|
|
47101
|
+
const startedAt2 = Date.now();
|
|
46848
47102
|
const upstream = await fetch(destination, {
|
|
46849
47103
|
method: "PUT",
|
|
46850
47104
|
body: bytes,
|
|
46851
47105
|
headers: { "Content-Type": req.headers["content-type"] ?? "application/octet-stream" }
|
|
46852
47106
|
});
|
|
46853
|
-
const ms = Date.now() -
|
|
47107
|
+
const ms = Date.now() - startedAt2;
|
|
46854
47108
|
if (upstream.ok) {
|
|
46855
47109
|
uploads.settle(fileId);
|
|
46856
47110
|
process.stderr.write(`[upload] ${fileId} ${bytes.length}B ${ms}ms
|
|
@@ -47246,6 +47500,9 @@ function calledAppAliases(sourceText) {
|
|
|
47246
47500
|
}
|
|
47247
47501
|
return out;
|
|
47248
47502
|
}
|
|
47503
|
+
function orphanedAliases(bound, called) {
|
|
47504
|
+
return unboundAliases(bound, called);
|
|
47505
|
+
}
|
|
47249
47506
|
function unboundAliases(called, bound) {
|
|
47250
47507
|
const missing = (from, against) => {
|
|
47251
47508
|
const have = new Set(against ?? []);
|
|
@@ -47533,8 +47790,8 @@ export type AppRoles = typeof ROLE;
|
|
|
47533
47790
|
}
|
|
47534
47791
|
|
|
47535
47792
|
// src/app_workflow_check.ts
|
|
47536
|
-
import
|
|
47537
|
-
import
|
|
47793
|
+
import fs5 from "node:fs";
|
|
47794
|
+
import path6 from "node:path";
|
|
47538
47795
|
import { createRequire } from "node:module";
|
|
47539
47796
|
|
|
47540
47797
|
// ../shared/src/parse_workflow_js.ts
|
|
@@ -48306,10 +48563,10 @@ function mergeDefs(...defs) {
|
|
|
48306
48563
|
function cloneDef(schema) {
|
|
48307
48564
|
return mergeDefs(schema._zod.def);
|
|
48308
48565
|
}
|
|
48309
|
-
function getElementAtPath(obj,
|
|
48310
|
-
if (!
|
|
48566
|
+
function getElementAtPath(obj, path10) {
|
|
48567
|
+
if (!path10)
|
|
48311
48568
|
return obj;
|
|
48312
|
-
return
|
|
48569
|
+
return path10.reduce((acc, key) => acc?.[key], obj);
|
|
48313
48570
|
}
|
|
48314
48571
|
function promiseAllObject(promisesObj) {
|
|
48315
48572
|
const keys3 = Object.keys(promisesObj);
|
|
@@ -48718,11 +48975,11 @@ function explicitlyAborted(x2, startIndex = 0) {
|
|
|
48718
48975
|
}
|
|
48719
48976
|
return false;
|
|
48720
48977
|
}
|
|
48721
|
-
function prefixIssues(
|
|
48978
|
+
function prefixIssues(path10, issues) {
|
|
48722
48979
|
return issues.map((iss) => {
|
|
48723
48980
|
var _a4;
|
|
48724
48981
|
(_a4 = iss).path ?? (_a4.path = []);
|
|
48725
|
-
iss.path.unshift(
|
|
48982
|
+
iss.path.unshift(path10);
|
|
48726
48983
|
return iss;
|
|
48727
48984
|
});
|
|
48728
48985
|
}
|
|
@@ -48869,16 +49126,16 @@ function flattenError(error52, mapper = (issue2) => issue2.message) {
|
|
|
48869
49126
|
}
|
|
48870
49127
|
function formatError(error52, mapper = (issue2) => issue2.message) {
|
|
48871
49128
|
const fieldErrors = { _errors: [] };
|
|
48872
|
-
const processError = (error53,
|
|
49129
|
+
const processError = (error53, path10 = []) => {
|
|
48873
49130
|
for (const issue2 of error53.issues) {
|
|
48874
49131
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
48875
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
49132
|
+
issue2.errors.map((issues) => processError({ issues }, [...path10, ...issue2.path]));
|
|
48876
49133
|
} else if (issue2.code === "invalid_key") {
|
|
48877
|
-
processError({ issues: issue2.issues }, [...
|
|
49134
|
+
processError({ issues: issue2.issues }, [...path10, ...issue2.path]);
|
|
48878
49135
|
} else if (issue2.code === "invalid_element") {
|
|
48879
|
-
processError({ issues: issue2.issues }, [...
|
|
49136
|
+
processError({ issues: issue2.issues }, [...path10, ...issue2.path]);
|
|
48880
49137
|
} else {
|
|
48881
|
-
const fullpath = [...
|
|
49138
|
+
const fullpath = [...path10, ...issue2.path];
|
|
48882
49139
|
if (fullpath.length === 0) {
|
|
48883
49140
|
fieldErrors._errors.push(mapper(issue2));
|
|
48884
49141
|
} else {
|
|
@@ -48905,17 +49162,17 @@ function formatError(error52, mapper = (issue2) => issue2.message) {
|
|
|
48905
49162
|
}
|
|
48906
49163
|
function treeifyError(error52, mapper = (issue2) => issue2.message) {
|
|
48907
49164
|
const result = { errors: [] };
|
|
48908
|
-
const processError = (error53,
|
|
49165
|
+
const processError = (error53, path10 = []) => {
|
|
48909
49166
|
var _a4, _b2;
|
|
48910
49167
|
for (const issue2 of error53.issues) {
|
|
48911
49168
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
48912
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
49169
|
+
issue2.errors.map((issues) => processError({ issues }, [...path10, ...issue2.path]));
|
|
48913
49170
|
} else if (issue2.code === "invalid_key") {
|
|
48914
|
-
processError({ issues: issue2.issues }, [...
|
|
49171
|
+
processError({ issues: issue2.issues }, [...path10, ...issue2.path]);
|
|
48915
49172
|
} else if (issue2.code === "invalid_element") {
|
|
48916
|
-
processError({ issues: issue2.issues }, [...
|
|
49173
|
+
processError({ issues: issue2.issues }, [...path10, ...issue2.path]);
|
|
48917
49174
|
} else {
|
|
48918
|
-
const fullpath = [...
|
|
49175
|
+
const fullpath = [...path10, ...issue2.path];
|
|
48919
49176
|
if (fullpath.length === 0) {
|
|
48920
49177
|
result.errors.push(mapper(issue2));
|
|
48921
49178
|
continue;
|
|
@@ -48947,8 +49204,8 @@ function treeifyError(error52, mapper = (issue2) => issue2.message) {
|
|
|
48947
49204
|
}
|
|
48948
49205
|
function toDotPath(_path) {
|
|
48949
49206
|
const segs = [];
|
|
48950
|
-
const
|
|
48951
|
-
for (const seg of
|
|
49207
|
+
const path10 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
49208
|
+
for (const seg of path10) {
|
|
48952
49209
|
if (typeof seg === "number")
|
|
48953
49210
|
segs.push(`[${seg}]`);
|
|
48954
49211
|
else if (typeof seg === "symbol")
|
|
@@ -61640,13 +61897,13 @@ function resolveRef(ref2, ctx) {
|
|
|
61640
61897
|
if (!ref2.startsWith("#")) {
|
|
61641
61898
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
61642
61899
|
}
|
|
61643
|
-
const
|
|
61644
|
-
if (
|
|
61900
|
+
const path10 = ref2.slice(1).split("/").filter(Boolean);
|
|
61901
|
+
if (path10.length === 0) {
|
|
61645
61902
|
return ctx.rootSchema;
|
|
61646
61903
|
}
|
|
61647
61904
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
61648
|
-
if (
|
|
61649
|
-
const key =
|
|
61905
|
+
if (path10[0] === defsKey) {
|
|
61906
|
+
const key = path10[1];
|
|
61650
61907
|
if (!key || !ctx.defs[key]) {
|
|
61651
61908
|
throw new Error(`Reference not found: ${ref2}`);
|
|
61652
61909
|
}
|
|
@@ -64478,6 +64735,7 @@ var PICKER_MODEL_TIERS = MODEL_TIERS.filter(
|
|
|
64478
64735
|
(tier) => tier !== UTILITY_MODEL_TIER
|
|
64479
64736
|
);
|
|
64480
64737
|
var EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
|
|
64738
|
+
var PREFIX_CACHE_TTLS = ["5m", "1h"];
|
|
64481
64739
|
|
|
64482
64740
|
// ../shared/src/schemas/apps.ts
|
|
64483
64741
|
var queryColumnTypeSchema = zod_default.enum([
|
|
@@ -64903,6 +65161,9 @@ var appWorkflowDeclarationSchema = zod_default.object({
|
|
|
64903
65161
|
),
|
|
64904
65162
|
description: zod_default.string().optional().describe(
|
|
64905
65163
|
"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."
|
|
65164
|
+
),
|
|
65165
|
+
body_sha: zod_default.string().optional().describe(
|
|
65166
|
+
"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
65167
|
)
|
|
64907
65168
|
});
|
|
64908
65169
|
var appWorkflowContractSchema = zod_default.object({
|
|
@@ -64941,6 +65202,9 @@ var appAgentDeclarationSchema = zod_default.object({
|
|
|
64941
65202
|
effort_level: zod_default.enum(EFFORT_LEVELS).optional().describe(
|
|
64942
65203
|
"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
65204
|
),
|
|
65205
|
+
prefix_cache_ttl: zod_default.enum(PREFIX_CACHE_TTLS).optional().describe(
|
|
65206
|
+
`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.`
|
|
65207
|
+
),
|
|
64944
65208
|
inputs: zod_default.record(zod_default.string(), appWorkflowInputSchema).optional().describe(
|
|
64945
65209
|
"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
65210
|
),
|
|
@@ -65782,10 +66046,10 @@ async function reduce2(arr, callback, initial2) {
|
|
|
65782
66046
|
if (arguments.length < 3) {
|
|
65783
66047
|
throw new Error("reduce: initial value is required \u2014 reduce(arr, (acc, x) => ..., initial)");
|
|
65784
66048
|
}
|
|
65785
|
-
const
|
|
66049
|
+
const startedAt2 = Date.now();
|
|
65786
66050
|
let acc = initial2;
|
|
65787
66051
|
for (let i2 = 0; i2 < arr.length; i2++) {
|
|
65788
|
-
if (i2 % 1024 === 0 && Date.now() -
|
|
66052
|
+
if (i2 % 1024 === 0 && Date.now() - startedAt2 > REDUCE_TIME_BUDGET_MS) {
|
|
65789
66053
|
throw new Error(
|
|
65790
66054
|
`reduce: exceeded time budget of ${REDUCE_TIME_BUDGET_MS}ms after ${i2} iterations`
|
|
65791
66055
|
);
|
|
@@ -66313,9 +66577,9 @@ __export(expression_object_exports, {
|
|
|
66313
66577
|
pick: () => pick3,
|
|
66314
66578
|
values: () => values2
|
|
66315
66579
|
});
|
|
66316
|
-
function getNestedValue(obj,
|
|
66580
|
+
function getNestedValue(obj, path10) {
|
|
66317
66581
|
if (obj == null) return void 0;
|
|
66318
|
-
const parts =
|
|
66582
|
+
const parts = path10.split(".");
|
|
66319
66583
|
let value2 = obj;
|
|
66320
66584
|
for (const part of parts) {
|
|
66321
66585
|
if (value2 == null) return void 0;
|
|
@@ -66354,11 +66618,11 @@ function entries(obj) {
|
|
|
66354
66618
|
}
|
|
66355
66619
|
return Object.entries(obj);
|
|
66356
66620
|
}
|
|
66357
|
-
function get(obj,
|
|
66358
|
-
if (typeof
|
|
66621
|
+
function get(obj, path10, defaultValue) {
|
|
66622
|
+
if (typeof path10 !== "string") {
|
|
66359
66623
|
throw new Error("get: path must be a string");
|
|
66360
66624
|
}
|
|
66361
|
-
const value2 = getNestedValue(obj,
|
|
66625
|
+
const value2 = getNestedValue(obj, path10);
|
|
66362
66626
|
return value2 !== void 0 ? value2 : defaultValue;
|
|
66363
66627
|
}
|
|
66364
66628
|
function pick3(obj, fields) {
|
|
@@ -68241,17 +68505,17 @@ function readFromChanges(chain2) {
|
|
|
68241
68505
|
if (first3.kind === "bracket-number" || first3.kind === "bracket-expr") {
|
|
68242
68506
|
fail(first3.node, "changes is keyed by field name, not numeric index.");
|
|
68243
68507
|
}
|
|
68244
|
-
const
|
|
68508
|
+
const path10 = [{ at: "key", key: first3.key }];
|
|
68245
68509
|
for (const seg of segments.slice(1)) {
|
|
68246
68510
|
if (seg.kind === "bracket-number") {
|
|
68247
|
-
|
|
68511
|
+
path10.push({ at: "index", index: seg.index });
|
|
68248
68512
|
} else if (seg.kind === "bracket-expr") {
|
|
68249
|
-
|
|
68513
|
+
path10.push({ at: "index", index: seg.expr });
|
|
68250
68514
|
} else {
|
|
68251
|
-
|
|
68515
|
+
path10.push({ at: "key", key: seg.key });
|
|
68252
68516
|
}
|
|
68253
68517
|
}
|
|
68254
|
-
return { kind: "read", source: { from: "trigger_changes" }, path:
|
|
68518
|
+
return { kind: "read", source: { from: "trigger_changes" }, path: path10 };
|
|
68255
68519
|
});
|
|
68256
68520
|
}
|
|
68257
68521
|
var TRIGGER_RECORD_VIEWS = /* @__PURE__ */ new Map([
|
|
@@ -68269,17 +68533,17 @@ function readFromTrigger(chain2) {
|
|
|
68269
68533
|
return view === "trigger_changes" ? readFromChanges(lowered) : readFromRecord(view, lowered);
|
|
68270
68534
|
}
|
|
68271
68535
|
return wrapOptionalGuards(chain2, (segments) => {
|
|
68272
|
-
const
|
|
68536
|
+
const path10 = [];
|
|
68273
68537
|
for (const seg of segments) {
|
|
68274
68538
|
if (seg.kind === "bracket-number") {
|
|
68275
|
-
|
|
68539
|
+
path10.push({ at: "index", index: seg.index });
|
|
68276
68540
|
} else if (seg.kind === "bracket-expr") {
|
|
68277
|
-
|
|
68541
|
+
path10.push({ at: "index", index: seg.expr });
|
|
68278
68542
|
} else {
|
|
68279
|
-
|
|
68543
|
+
path10.push({ at: "key", key: seg.key });
|
|
68280
68544
|
}
|
|
68281
68545
|
}
|
|
68282
|
-
return { kind: "read", source: { from: "trigger" }, path:
|
|
68546
|
+
return { kind: "read", source: { from: "trigger" }, path: path10 };
|
|
68283
68547
|
});
|
|
68284
68548
|
}
|
|
68285
68549
|
function readFromRuntime(chain2) {
|
|
@@ -68301,17 +68565,17 @@ function readFromRuntime(chain2) {
|
|
|
68301
68565
|
const parked = PARKED_RUNTIME_KEYS.get(key);
|
|
68302
68566
|
if (parked) fail(first3.node, parked, parkedRuntimeKeyConstruct(key));
|
|
68303
68567
|
const source = { from: "runtime", key };
|
|
68304
|
-
const
|
|
68568
|
+
const path10 = [];
|
|
68305
68569
|
for (const seg of segments.slice(1)) {
|
|
68306
68570
|
if (seg.kind === "bracket-number") {
|
|
68307
|
-
|
|
68571
|
+
path10.push({ at: "index", index: seg.index });
|
|
68308
68572
|
} else if (seg.kind === "bracket-expr") {
|
|
68309
|
-
|
|
68573
|
+
path10.push({ at: "index", index: seg.expr });
|
|
68310
68574
|
} else {
|
|
68311
|
-
|
|
68575
|
+
path10.push({ at: "key", key: seg.key });
|
|
68312
68576
|
}
|
|
68313
68577
|
}
|
|
68314
|
-
return { kind: "read", source, path:
|
|
68578
|
+
return { kind: "read", source, path: path10 };
|
|
68315
68579
|
});
|
|
68316
68580
|
}
|
|
68317
68581
|
function readFromForeachItem(chain2) {
|
|
@@ -68347,17 +68611,17 @@ function readFromLexical(name2, chain2) {
|
|
|
68347
68611
|
}
|
|
68348
68612
|
function readFromStepOutput(stepId, chain2) {
|
|
68349
68613
|
return wrapOptionalGuards(chain2, (segments) => {
|
|
68350
|
-
const
|
|
68614
|
+
const path10 = [];
|
|
68351
68615
|
for (const seg of segments) {
|
|
68352
68616
|
if (seg.kind === "bracket-number") {
|
|
68353
|
-
|
|
68617
|
+
path10.push({ at: "index", index: seg.index });
|
|
68354
68618
|
} else if (seg.kind === "bracket-expr") {
|
|
68355
|
-
|
|
68619
|
+
path10.push({ at: "index", index: seg.expr });
|
|
68356
68620
|
} else {
|
|
68357
|
-
|
|
68621
|
+
path10.push({ at: "key", key: seg.key });
|
|
68358
68622
|
}
|
|
68359
68623
|
}
|
|
68360
|
-
return { kind: "read", source: { from: "step_output", step_id: stepId }, path:
|
|
68624
|
+
return { kind: "read", source: { from: "step_output", step_id: stepId }, path: path10 };
|
|
68361
68625
|
});
|
|
68362
68626
|
}
|
|
68363
68627
|
function classifyRecordPath(segs) {
|
|
@@ -70011,7 +70275,7 @@ function subsetIssues(bodySource, offset) {
|
|
|
70011
70275
|
];
|
|
70012
70276
|
}
|
|
70013
70277
|
async function loadProjectTypescript(projectDir) {
|
|
70014
|
-
const requireFromProject = createRequire(
|
|
70278
|
+
const requireFromProject = createRequire(path6.join(projectDir, "package.json"));
|
|
70015
70279
|
let resolved;
|
|
70016
70280
|
try {
|
|
70017
70281
|
resolved = requireFromProject.resolve("typescript");
|
|
@@ -70024,12 +70288,12 @@ async function loadProjectTypescript(projectDir) {
|
|
|
70024
70288
|
return mod2.default ?? mod2;
|
|
70025
70289
|
}
|
|
70026
70290
|
function pathToImportUrl(p) {
|
|
70027
|
-
const resolved =
|
|
70291
|
+
const resolved = path6.resolve(p);
|
|
70028
70292
|
const prefixed = resolved.startsWith("/") ? resolved : `/${resolved}`;
|
|
70029
|
-
return `file://${prefixed.split(
|
|
70293
|
+
return `file://${prefixed.split(path6.sep).join("/")}`;
|
|
70030
70294
|
}
|
|
70031
70295
|
function checkOneWorkflowBody(tsApi, input) {
|
|
70032
|
-
const wrapped =
|
|
70296
|
+
const wrapped = fs5.readFileSync(input.bodyPath, "utf-8");
|
|
70033
70297
|
const offset = bodyLineOffset(wrapped);
|
|
70034
70298
|
const subset = subsetIssues(stripWorkflowHeader(wrapped), offset);
|
|
70035
70299
|
if (subset) return subset;
|
|
@@ -70076,7 +70340,7 @@ function checkOneWorkflowBody(tsApi, input) {
|
|
|
70076
70340
|
}
|
|
70077
70341
|
function createCheckedSourceHost(tsApi, options, bodyPath, bodyText) {
|
|
70078
70342
|
const base = tsApi.createCompilerHost(options, false);
|
|
70079
|
-
const key = (fileName) => base.getCanonicalFileName(
|
|
70343
|
+
const key = (fileName) => base.getCanonicalFileName(path6.resolve(fileName).replace(/\\/g, "/"));
|
|
70080
70344
|
const bodyKey = key(bodyPath);
|
|
70081
70345
|
return {
|
|
70082
70346
|
...base,
|
|
@@ -70145,6 +70409,14 @@ function orderedLike(value2, template) {
|
|
|
70145
70409
|
[...first3, ...rest2].map((k) => [k, orderedLike(from[k], tmpl[k])])
|
|
70146
70410
|
);
|
|
70147
70411
|
}
|
|
70412
|
+
function toManifestWorkflows(live) {
|
|
70413
|
+
return Object.fromEntries(
|
|
70414
|
+
Object.entries(live).map(([alias, { body_sha: _serverState, ...declaration }]) => [
|
|
70415
|
+
alias,
|
|
70416
|
+
declaration
|
|
70417
|
+
])
|
|
70418
|
+
);
|
|
70419
|
+
}
|
|
70148
70420
|
function toManifestAgents(agents) {
|
|
70149
70421
|
return Object.fromEntries(
|
|
70150
70422
|
Object.entries(agents).map(([alias, { instructions: _prose, ...typed }]) => [alias, typed])
|
|
@@ -70163,27 +70435,58 @@ function sameSchema(a, b) {
|
|
|
70163
70435
|
};
|
|
70164
70436
|
return JSON.stringify(canonical(a)) === JSON.stringify(canonical(b));
|
|
70165
70437
|
}
|
|
70166
|
-
function
|
|
70438
|
+
function workflowTypeDivergences(manifestWorkflows, liveWorkflows) {
|
|
70167
70439
|
const out = [];
|
|
70168
|
-
for (const [alias, declared] of Object.entries(
|
|
70169
|
-
const live = (
|
|
70440
|
+
for (const [alias, declared] of Object.entries(manifestWorkflows ?? {})) {
|
|
70441
|
+
const live = (liveWorkflows ?? {})[alias];
|
|
70170
70442
|
if (!live) {
|
|
70171
|
-
out.push(
|
|
70172
|
-
`${alias} \u2014 typed locally but NOT bound on the app, so useAgentRun("${alias}") 400s at runtime`
|
|
70173
|
-
);
|
|
70443
|
+
out.push({ alias });
|
|
70174
70444
|
continue;
|
|
70175
70445
|
}
|
|
70176
|
-
for (const key of
|
|
70177
|
-
if (!sameSchema(declared[key], live[key])) {
|
|
70178
|
-
out.push(`${alias}.${key} \u2014 local types disagree with the live agent`);
|
|
70179
|
-
}
|
|
70446
|
+
for (const key of ["inputs", "outputs"]) {
|
|
70447
|
+
if (!sameSchema(declared[key], live[key])) out.push({ alias, key });
|
|
70180
70448
|
}
|
|
70181
70449
|
}
|
|
70182
70450
|
return out;
|
|
70183
70451
|
}
|
|
70184
|
-
|
|
70185
|
-
|
|
70186
|
-
|
|
70452
|
+
function syncedEntry(entry) {
|
|
70453
|
+
if (entry === void 0) return void 0;
|
|
70454
|
+
return typeof entry === "string" ? { content: entry } : entry;
|
|
70455
|
+
}
|
|
70456
|
+
function readSynced(projectDir) {
|
|
70457
|
+
try {
|
|
70458
|
+
const pkg2 = JSON.parse(
|
|
70459
|
+
fs6.readFileSync(path7.join(projectDir, "package.json"), "utf-8")
|
|
70460
|
+
);
|
|
70461
|
+
const raw = pkg2.lotics?.synced ?? {};
|
|
70462
|
+
return {
|
|
70463
|
+
workflows: normalizeSyncedMap(raw.workflows),
|
|
70464
|
+
agents: normalizeSyncedMap(raw.agents),
|
|
70465
|
+
queries: normalizeSyncedMap(raw.queries)
|
|
70466
|
+
};
|
|
70467
|
+
} catch {
|
|
70468
|
+
return { workflows: {}, agents: {}, queries: {} };
|
|
70469
|
+
}
|
|
70470
|
+
}
|
|
70471
|
+
function normalizeSyncedMap(map3) {
|
|
70472
|
+
return Object.fromEntries(
|
|
70473
|
+
Object.entries(map3 ?? {}).map(([alias, entry]) => [
|
|
70474
|
+
alias,
|
|
70475
|
+
syncedEntry(entry)
|
|
70476
|
+
])
|
|
70477
|
+
);
|
|
70478
|
+
}
|
|
70479
|
+
function writeSynced(projectDir, kind, alias, entry) {
|
|
70480
|
+
const pkgPath2 = path7.join(projectDir, "package.json");
|
|
70481
|
+
const pkg2 = JSON.parse(fs6.readFileSync(pkgPath2, "utf-8"));
|
|
70482
|
+
if (!pkg2.lotics) return;
|
|
70483
|
+
pkg2.lotics.synced ??= {};
|
|
70484
|
+
pkg2.lotics.synced[kind] = { ...pkg2.lotics.synced[kind], [alias]: entry };
|
|
70485
|
+
fs6.writeFileSync(pkgPath2, JSON.stringify(pkg2, null, 2) + "\n");
|
|
70486
|
+
}
|
|
70487
|
+
var WORKFLOWS_DIR = path7.join("src", "workflows");
|
|
70488
|
+
var WORKFLOW_GLOBALS_DIR = path7.join(".lotics", "workflows");
|
|
70489
|
+
var AGENTS_DIR = path7.join("src", "agents");
|
|
70187
70490
|
var WORKFLOW_TSCONFIG_EXCLUDES = ["src/workflows", ".lotics/workflows"];
|
|
70188
70491
|
var LOTICS_INCLUDE_GLOB = ".lotics/**/*";
|
|
70189
70492
|
var STALE_LOTICS_INCLUDES = /* @__PURE__ */ new Set([".lotics", "./.lotics", ".lotics/"]);
|
|
@@ -70193,11 +70496,8 @@ function toWorkflowDtsDeclaration(d) {
|
|
|
70193
70496
|
function isMissingWorkflowBodyError(error52) {
|
|
70194
70497
|
return /has no step source|cannot be rendered|unsupported step/i.test(error52);
|
|
70195
70498
|
}
|
|
70196
|
-
function isUnknownWorkflowAliasError(err2) {
|
|
70197
|
-
return err2 instanceof Error && err2.message.startsWith("400:") && /has no workflow alias/i.test(err2.message);
|
|
70198
|
-
}
|
|
70199
70499
|
function workflowFileHeader(alias) {
|
|
70200
|
-
const refPath =
|
|
70500
|
+
const refPath = path7.join("..", "..", WORKFLOW_GLOBALS_DIR, `${alias}.globals.d.ts`).split(path7.sep).join("/");
|
|
70201
70501
|
return `/// <reference path="${refPath}" />
|
|
70202
70502
|
// Auto-pulled workflow body for "${alias}". Edit the BODY between the wrapper
|
|
70203
70503
|
// lines below, then check + push with:
|
|
@@ -70211,65 +70511,68 @@ export {};
|
|
|
70211
70511
|
`;
|
|
70212
70512
|
}
|
|
70213
70513
|
function workflowFilePath(projectDir, alias) {
|
|
70214
|
-
return
|
|
70514
|
+
return path7.join(projectDir, WORKFLOWS_DIR, `${alias}.ts`);
|
|
70215
70515
|
}
|
|
70216
70516
|
var GLOBALS_SUFFIX = ".globals.d.ts";
|
|
70217
70517
|
function workflowGlobalsPath(projectDir, alias) {
|
|
70218
|
-
return
|
|
70518
|
+
return path7.join(projectDir, WORKFLOW_GLOBALS_DIR, `${alias}${GLOBALS_SUFFIX}`);
|
|
70219
70519
|
}
|
|
70220
70520
|
function pruneOrphanWorkflowGlobals(projectDir, declared) {
|
|
70221
|
-
const dir =
|
|
70222
|
-
if (!
|
|
70521
|
+
const dir = path7.join(projectDir, WORKFLOW_GLOBALS_DIR);
|
|
70522
|
+
if (!fs6.existsSync(dir)) return [];
|
|
70223
70523
|
const removed = [];
|
|
70224
|
-
for (const entry of
|
|
70524
|
+
for (const entry of fs6.readdirSync(dir)) {
|
|
70225
70525
|
if (!entry.endsWith(GLOBALS_SUFFIX)) continue;
|
|
70226
70526
|
const alias = entry.slice(0, -GLOBALS_SUFFIX.length);
|
|
70227
70527
|
if (declared.has(alias)) continue;
|
|
70228
|
-
|
|
70528
|
+
fs6.unlinkSync(path7.join(dir, entry));
|
|
70229
70529
|
removed.push(alias);
|
|
70230
70530
|
}
|
|
70231
70531
|
return removed.sort();
|
|
70232
70532
|
}
|
|
70233
70533
|
function orphanWorkflowBodies(projectDir, declared) {
|
|
70234
|
-
const dir =
|
|
70235
|
-
if (!
|
|
70236
|
-
return
|
|
70534
|
+
const dir = path7.join(projectDir, WORKFLOWS_DIR);
|
|
70535
|
+
if (!fs6.existsSync(dir)) return [];
|
|
70536
|
+
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
70537
|
}
|
|
70238
70538
|
function writeWorkflowGlobals(projectDir, alias, dts) {
|
|
70239
|
-
const dir =
|
|
70240
|
-
|
|
70539
|
+
const dir = path7.join(projectDir, WORKFLOW_GLOBALS_DIR);
|
|
70540
|
+
fs6.mkdirSync(dir, { recursive: true });
|
|
70241
70541
|
const file2 = workflowGlobalsPath(projectDir, alias);
|
|
70242
|
-
|
|
70542
|
+
fs6.writeFileSync(file2, `${dts.replace(/\s+$/, "")}
|
|
70243
70543
|
`);
|
|
70244
70544
|
return file2;
|
|
70245
70545
|
}
|
|
70546
|
+
function normalizeWorkflowBody(source) {
|
|
70547
|
+
return source.replace(/\s+$/, "");
|
|
70548
|
+
}
|
|
70246
70549
|
function writeWorkflowFile(projectDir, alias, source, envelope = { prefix: WORKFLOW_ENVELOPE_PREFIX, suffix: WORKFLOW_ENVELOPE_SUFFIX }) {
|
|
70247
|
-
const dir =
|
|
70248
|
-
|
|
70550
|
+
const dir = path7.join(projectDir, WORKFLOWS_DIR);
|
|
70551
|
+
fs6.mkdirSync(dir, { recursive: true });
|
|
70249
70552
|
const file2 = workflowFilePath(projectDir, alias);
|
|
70250
|
-
const body = source
|
|
70251
|
-
|
|
70553
|
+
const body = normalizeWorkflowBody(source);
|
|
70554
|
+
fs6.writeFileSync(file2, `${workflowFileHeader(alias)}
|
|
70252
70555
|
${envelope.prefix}${body}${envelope.suffix}
|
|
70253
70556
|
`);
|
|
70254
70557
|
return file2;
|
|
70255
70558
|
}
|
|
70256
70559
|
function agentFilePath2(projectDir, alias) {
|
|
70257
|
-
return
|
|
70560
|
+
return path7.join(projectDir, ...agentFilePath(alias).split("/"));
|
|
70258
70561
|
}
|
|
70259
70562
|
var CLI_AGENT_NOTES = [
|
|
70260
70563
|
"Pulled from the LIVE app row; edit here, then: lotics app agent set <alias>",
|
|
70261
70564
|
"The typed fields (inputs/outputs/tool_names/model_tier) live in package.json#lotics.agents"
|
|
70262
70565
|
];
|
|
70263
70566
|
function writeAgentFile(projectDir, alias, instructions) {
|
|
70264
|
-
|
|
70567
|
+
fs6.mkdirSync(path7.join(projectDir, AGENTS_DIR), { recursive: true });
|
|
70265
70568
|
const file2 = agentFilePath2(projectDir, alias);
|
|
70266
|
-
|
|
70569
|
+
fs6.writeFileSync(
|
|
70267
70570
|
file2,
|
|
70268
70571
|
wrapAgentFile({ alias, instructions, notes: CLI_AGENT_NOTES.map((n) => n.replace("<alias>", alias)) })
|
|
70269
70572
|
);
|
|
70270
70573
|
return file2;
|
|
70271
70574
|
}
|
|
70272
|
-
function writeAgentFiles(projectDir, agents) {
|
|
70575
|
+
function writeAgentFiles(projectDir, agents, opts) {
|
|
70273
70576
|
const written = [];
|
|
70274
70577
|
for (const [alias, declaration] of Object.entries(agents)) {
|
|
70275
70578
|
const instructions = declaration.instructions;
|
|
@@ -70277,15 +70580,33 @@ function writeAgentFiles(projectDir, agents) {
|
|
|
70277
70580
|
console.error(`\u26A0 Skipped src/agents/${alias}.md \u2014 the live declaration carries no instructions.`);
|
|
70278
70581
|
continue;
|
|
70279
70582
|
}
|
|
70583
|
+
if (!opts.overwrite && localPromptUnpushed(projectDir, alias)) {
|
|
70584
|
+
opts.kept.push(`src/agents/${alias}.md`);
|
|
70585
|
+
continue;
|
|
70586
|
+
}
|
|
70280
70587
|
writeAgentFile(projectDir, alias, instructions);
|
|
70281
70588
|
written.push(alias);
|
|
70282
70589
|
}
|
|
70283
70590
|
return written;
|
|
70284
70591
|
}
|
|
70592
|
+
function localPromptUnpushed(projectDir, alias) {
|
|
70593
|
+
const file2 = agentFilePath2(projectDir, alias);
|
|
70594
|
+
if (!fs6.existsSync(file2)) return false;
|
|
70595
|
+
const baseline = readSynced(projectDir).agents[alias]?.content;
|
|
70596
|
+
if (baseline === void 0) return true;
|
|
70597
|
+
return contentSha(stripAgentHeader(fs6.readFileSync(file2, "utf-8"))) !== baseline;
|
|
70598
|
+
}
|
|
70285
70599
|
function generatedWorkflowDescription(appName, alias) {
|
|
70286
70600
|
return `Workflow invoked by app "${appName}" via useWorkflow("${alias}").`;
|
|
70287
70601
|
}
|
|
70288
|
-
|
|
70602
|
+
function localBodyUnpushed(projectDir, alias) {
|
|
70603
|
+
const file2 = workflowFilePath(projectDir, alias);
|
|
70604
|
+
if (!fs6.existsSync(file2)) return false;
|
|
70605
|
+
const baseline = readSynced(projectDir).workflows[alias]?.content;
|
|
70606
|
+
if (baseline === void 0) return true;
|
|
70607
|
+
return contentSha(stripWorkflowHeader(fs6.readFileSync(file2, "utf-8"))) !== baseline;
|
|
70608
|
+
}
|
|
70609
|
+
async function writeWorkflowFiles(client, projectDir, app_id, appName, workflows, opts) {
|
|
70289
70610
|
const written = [];
|
|
70290
70611
|
for (const [alias, declaration] of Object.entries(workflows)) {
|
|
70291
70612
|
const res = await client.getAppWorkflow(app_id, alias);
|
|
@@ -70294,7 +70615,9 @@ async function writeWorkflowFiles(client, projectDir, app_id, appName, workflows
|
|
|
70294
70615
|
`Could not read workflow "${alias}" of ${app_id}: ${res.error}. Reading app workflow source requires an admin member \u2014 check the role behind this API key.`
|
|
70295
70616
|
);
|
|
70296
70617
|
}
|
|
70297
|
-
const
|
|
70618
|
+
const result = res.result === null || typeof res.result !== "object" ? null : res.result;
|
|
70619
|
+
const source = result?.source;
|
|
70620
|
+
const body_sha = typeof result?.body_sha === "string" ? result.body_sha : void 0;
|
|
70298
70621
|
if (typeof source !== "string" || source.trim() === "") {
|
|
70299
70622
|
console.error(
|
|
70300
70623
|
`\u26A0 Skipped src/workflows/${alias}.ts \u2014 legacy workflow with no rendered body.`
|
|
@@ -70308,7 +70631,15 @@ async function writeWorkflowFiles(client, projectDir, app_id, appName, workflows
|
|
|
70308
70631
|
alias,
|
|
70309
70632
|
toWorkflowDtsDeclaration(declaration)
|
|
70310
70633
|
);
|
|
70634
|
+
if (!opts.overwrite && localBodyUnpushed(projectDir, alias)) {
|
|
70635
|
+
opts.kept.push(`src/workflows/${alias}.ts`);
|
|
70636
|
+
continue;
|
|
70637
|
+
}
|
|
70311
70638
|
writeWorkflowFile(projectDir, alias, source, envelope);
|
|
70639
|
+
writeSynced(projectDir, "workflows", alias, {
|
|
70640
|
+
content: contentSha(normalizeWorkflowBody(source)),
|
|
70641
|
+
...body_sha ? { live: body_sha } : {}
|
|
70642
|
+
});
|
|
70312
70643
|
const rowDescription = res.result.description;
|
|
70313
70644
|
if (typeof rowDescription === "string" && rowDescription !== "" && // Skip the server's GENERATED default. It is what an undescribed alias
|
|
70314
70645
|
// carries, so writing it back would commit a line of noise per workflow
|
|
@@ -70323,12 +70654,12 @@ async function writeWorkflowFiles(client, projectDir, app_id, appName, workflows
|
|
|
70323
70654
|
return written;
|
|
70324
70655
|
}
|
|
70325
70656
|
function writeWorkflowDescription(projectDir, alias, description) {
|
|
70326
|
-
const pkgPath2 =
|
|
70327
|
-
const pkg2 = JSON.parse(
|
|
70657
|
+
const pkgPath2 = path7.join(projectDir, "package.json");
|
|
70658
|
+
const pkg2 = JSON.parse(fs6.readFileSync(pkgPath2, "utf-8"));
|
|
70328
70659
|
const declaration = pkg2.lotics?.workflows?.[alias];
|
|
70329
70660
|
if (!declaration) return;
|
|
70330
70661
|
declaration.description = description;
|
|
70331
|
-
|
|
70662
|
+
fs6.writeFileSync(pkgPath2, JSON.stringify(pkg2, null, 2) + "\n");
|
|
70332
70663
|
}
|
|
70333
70664
|
async function fetchWorkflowGlobals(client, projectDir, app_id, alias, declaration) {
|
|
70334
70665
|
try {
|
|
@@ -70348,14 +70679,9 @@ async function fetchWorkflowGlobals(client, projectDir, app_id, alias, declarati
|
|
|
70348
70679
|
}
|
|
70349
70680
|
}
|
|
70350
70681
|
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
|
-
}
|
|
70682
|
+
const declaresSchema = declaration !== void 0 && (declaration.inputs !== void 0 || declaration.outputs !== void 0);
|
|
70683
|
+
if (declaresSchema) return client.getAppWorkflowDts(app_id, alias, declaration);
|
|
70684
|
+
return client.getAppWorkflowDts(app_id, alias);
|
|
70359
70685
|
}
|
|
70360
70686
|
function runTar(args, cwd) {
|
|
70361
70687
|
return new Promise((resolve2, reject2) => {
|
|
@@ -70383,25 +70709,26 @@ function runNpm(args, cwd) {
|
|
|
70383
70709
|
}
|
|
70384
70710
|
function readAppWorkspaceId(projectDir) {
|
|
70385
70711
|
try {
|
|
70386
|
-
const pkgPath2 =
|
|
70387
|
-
if (!
|
|
70388
|
-
const pkg2 = JSON.parse(
|
|
70712
|
+
const pkgPath2 = path7.join(projectDir, "package.json");
|
|
70713
|
+
if (!fs6.existsSync(pkgPath2)) return null;
|
|
70714
|
+
const pkg2 = JSON.parse(fs6.readFileSync(pkgPath2, "utf-8"));
|
|
70389
70715
|
return pkg2.lotics?.workspace_id ?? null;
|
|
70390
70716
|
} catch {
|
|
70391
70717
|
return null;
|
|
70392
70718
|
}
|
|
70393
70719
|
}
|
|
70394
70720
|
function readAppMeta(projectDir) {
|
|
70395
|
-
const pkgPath2 =
|
|
70396
|
-
if (!
|
|
70721
|
+
const pkgPath2 = path7.join(projectDir, "package.json");
|
|
70722
|
+
if (!fs6.existsSync(pkgPath2)) {
|
|
70397
70723
|
throw new Error(`No package.json found at ${projectDir}. Run 'lotics app create' first.`);
|
|
70398
70724
|
}
|
|
70399
|
-
const pkg2 = JSON.parse(
|
|
70725
|
+
const pkg2 = JSON.parse(fs6.readFileSync(pkgPath2, "utf-8"));
|
|
70400
70726
|
if (!pkg2.lotics?.app_id || !pkg2.lotics.workspace_id) {
|
|
70401
70727
|
throw new Error(
|
|
70402
70728
|
`package.json is missing the 'lotics' field with app_id and workspace_id. Was this folder created with 'lotics app create'?`
|
|
70403
70729
|
);
|
|
70404
70730
|
}
|
|
70731
|
+
noteScope({ app_id: pkg2.lotics.app_id });
|
|
70405
70732
|
return {
|
|
70406
70733
|
app_id: pkg2.lotics.app_id,
|
|
70407
70734
|
workspace_id: pkg2.lotics.workspace_id,
|
|
@@ -70424,19 +70751,22 @@ var APP_META_KEYS = Object.keys({
|
|
|
70424
70751
|
capabilities: true
|
|
70425
70752
|
});
|
|
70426
70753
|
function writeAppMeta(projectDir, meta3) {
|
|
70427
|
-
const pkgPath2 =
|
|
70428
|
-
const pkg2 = JSON.parse(
|
|
70754
|
+
const pkgPath2 = path7.join(projectDir, "package.json");
|
|
70755
|
+
const pkg2 = JSON.parse(fs6.readFileSync(pkgPath2, "utf-8"));
|
|
70429
70756
|
const existing = pkg2.lotics && typeof pkg2.lotics === "object" ? pkg2.lotics : {};
|
|
70757
|
+
const supplied = new Set(
|
|
70758
|
+
Object.entries(meta3).filter(([, value2]) => value2 !== void 0).map(([key]) => key)
|
|
70759
|
+
);
|
|
70430
70760
|
const preserved = {};
|
|
70431
70761
|
for (const [key, value2] of Object.entries(existing)) {
|
|
70432
|
-
if (!
|
|
70762
|
+
if (!supplied.has(key)) preserved[key] = value2;
|
|
70433
70763
|
}
|
|
70434
70764
|
pkg2.lotics = { ...meta3, ...preserved };
|
|
70435
|
-
|
|
70765
|
+
fs6.writeFileSync(pkgPath2, JSON.stringify(pkg2, null, 2) + "\n");
|
|
70436
70766
|
}
|
|
70437
70767
|
function writeWorkflowOutputs(projectDir, alias, outputs) {
|
|
70438
|
-
const pkgPath2 =
|
|
70439
|
-
const pkg2 = JSON.parse(
|
|
70768
|
+
const pkgPath2 = path7.join(projectDir, "package.json");
|
|
70769
|
+
const pkg2 = JSON.parse(fs6.readFileSync(pkgPath2, "utf-8"));
|
|
70440
70770
|
const declaration = pkg2.lotics?.workflows?.[alias];
|
|
70441
70771
|
if (!declaration) {
|
|
70442
70772
|
throw new Error(
|
|
@@ -70444,11 +70774,24 @@ function writeWorkflowOutputs(projectDir, alias, outputs) {
|
|
|
70444
70774
|
);
|
|
70445
70775
|
}
|
|
70446
70776
|
declaration.outputs = outputs;
|
|
70447
|
-
|
|
70777
|
+
fs6.writeFileSync(pkgPath2, JSON.stringify(pkg2, null, 2) + "\n");
|
|
70778
|
+
}
|
|
70779
|
+
function workflowBodyDrift(projectDir, workflows, synced = {}) {
|
|
70780
|
+
const drifted = [];
|
|
70781
|
+
for (const alias of Object.keys(workflows ?? {})) {
|
|
70782
|
+
const baseline = syncedEntry(synced[alias])?.content;
|
|
70783
|
+
if (baseline === void 0) continue;
|
|
70784
|
+
const file2 = workflowFilePath(projectDir, alias);
|
|
70785
|
+
if (!fs6.existsSync(file2)) continue;
|
|
70786
|
+
const source = stripWorkflowHeader(fs6.readFileSync(file2, "utf-8"));
|
|
70787
|
+
if (source.trim() === "" || contentSha(source) === baseline) continue;
|
|
70788
|
+
drifted.push(alias);
|
|
70789
|
+
}
|
|
70790
|
+
return drifted;
|
|
70448
70791
|
}
|
|
70449
70792
|
function ensureAppTsconfig(projectDir) {
|
|
70450
|
-
const tsconfigPath =
|
|
70451
|
-
if (!
|
|
70793
|
+
const tsconfigPath = path7.join(projectDir, "tsconfig.json");
|
|
70794
|
+
if (!fs6.existsSync(tsconfigPath)) {
|
|
70452
70795
|
console.error(
|
|
70453
70796
|
`\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
70797
|
);
|
|
@@ -70456,7 +70799,7 @@ function ensureAppTsconfig(projectDir) {
|
|
|
70456
70799
|
}
|
|
70457
70800
|
let parsed;
|
|
70458
70801
|
try {
|
|
70459
|
-
parsed = JSON.parse(
|
|
70802
|
+
parsed = JSON.parse(fs6.readFileSync(tsconfigPath, "utf-8"));
|
|
70460
70803
|
} catch (err2) {
|
|
70461
70804
|
console.error(
|
|
70462
70805
|
`\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 +70832,7 @@ function ensureAppTsconfig(projectDir) {
|
|
|
70489
70832
|
);
|
|
70490
70833
|
}
|
|
70491
70834
|
if (changes.length === 0) return;
|
|
70492
|
-
|
|
70835
|
+
fs6.writeFileSync(tsconfigPath, JSON.stringify(parsed, null, 2) + "\n");
|
|
70493
70836
|
console.error(`Patched tsconfig.json: ${changes.join("; ")}.`);
|
|
70494
70837
|
}
|
|
70495
70838
|
var LINK_TSCONFIG = ".lotics/tsconfig.link.json";
|
|
@@ -70499,15 +70842,15 @@ function writeDevLinkTsconfig(projectDir) {
|
|
|
70499
70842
|
if (uiSrc) {
|
|
70500
70843
|
paths["@lotics/ui/*"] = [`${uiSrc}/*`];
|
|
70501
70844
|
for (const peer of readKitPeerNames(projectDir)) {
|
|
70502
|
-
const typesDir =
|
|
70503
|
-
const runtimeDir =
|
|
70504
|
-
const target =
|
|
70845
|
+
const typesDir = path7.join(projectDir, "node_modules", typesPackageFor(peer));
|
|
70846
|
+
const runtimeDir = path7.join(projectDir, "node_modules", peer);
|
|
70847
|
+
const target = fs6.existsSync(typesDir) ? `../node_modules/${typesPackageFor(peer)}` : fs6.existsSync(runtimeDir) ? `../node_modules/${peer}` : null;
|
|
70505
70848
|
if (!target) continue;
|
|
70506
70849
|
paths[peer] = [target];
|
|
70507
70850
|
paths[`${peer}/*`] = [`${target}/*`];
|
|
70508
70851
|
}
|
|
70509
70852
|
}
|
|
70510
|
-
const file2 =
|
|
70853
|
+
const file2 = path7.join(projectDir, ".lotics", "tsconfig.link.json");
|
|
70511
70854
|
const header = uiSrc ? `// GENERATED by \`lotics app codegen\` \u2014 do not edit.
|
|
70512
70855
|
// LOTICS_UI_SRC is set, so @lotics/ui resolves to your working copy for tsc,
|
|
70513
70856
|
// vitest, eslint and your editor \u2014 the same copy Vite is bundling. The peer
|
|
@@ -70518,52 +70861,52 @@ function writeDevLinkTsconfig(projectDir) {
|
|
|
70518
70861
|
// LOTICS_UI_SRC is not set, so this is inert and @lotics/ui resolves from
|
|
70519
70862
|
// node_modules as normal.
|
|
70520
70863
|
`;
|
|
70521
|
-
|
|
70864
|
+
fs6.writeFileSync(file2, `${header}${JSON.stringify({ compilerOptions: { paths } }, null, 2)}
|
|
70522
70865
|
`);
|
|
70523
70866
|
return file2;
|
|
70524
70867
|
}
|
|
70525
70868
|
function writeKitTypeAugmentation(projectDir) {
|
|
70526
70869
|
const uiSrc = process.env.LOTICS_UI_SRC;
|
|
70527
|
-
const source = uiSrc ?
|
|
70528
|
-
const target =
|
|
70529
|
-
if (!
|
|
70530
|
-
if (
|
|
70870
|
+
const source = uiSrc ? path7.join(uiSrc, "react_native.d.ts") : path7.join(projectDir, "node_modules", "@lotics", "ui", "src", "react_native.d.ts");
|
|
70871
|
+
const target = path7.join(projectDir, ".lotics", "react_native.d.ts");
|
|
70872
|
+
if (!fs6.existsSync(source)) {
|
|
70873
|
+
if (fs6.existsSync(target)) fs6.rmSync(target);
|
|
70531
70874
|
return;
|
|
70532
70875
|
}
|
|
70533
|
-
|
|
70876
|
+
fs6.writeFileSync(
|
|
70534
70877
|
target,
|
|
70535
70878
|
`// GENERATED by \`lotics app codegen\` from @lotics/ui \u2014 do not edit.
|
|
70536
70879
|
// The kit's web-only ViewStyle/TextStyle properties, which react-native does
|
|
70537
70880
|
// not model. Delete any hand-copied src/react_native.d.ts; this replaces it.
|
|
70538
|
-
` +
|
|
70881
|
+
` + fs6.readFileSync(source, "utf-8")
|
|
70539
70882
|
);
|
|
70540
70883
|
}
|
|
70541
70884
|
function typesPackageFor(peer) {
|
|
70542
70885
|
return peer.startsWith("@") ? `@types/${peer.slice(1).replace("/", "__")}` : `@types/${peer}`;
|
|
70543
70886
|
}
|
|
70544
70887
|
function readKitPeerNames(projectDir) {
|
|
70545
|
-
const kitPkg =
|
|
70546
|
-
if (!
|
|
70547
|
-
const parsed = JSON.parse(
|
|
70888
|
+
const kitPkg = path7.join(projectDir, "node_modules", "@lotics", "ui", "package.json");
|
|
70889
|
+
if (!fs6.existsSync(kitPkg)) return [];
|
|
70890
|
+
const parsed = JSON.parse(fs6.readFileSync(kitPkg, "utf-8"));
|
|
70548
70891
|
const peers = parsed.peerDependencies;
|
|
70549
70892
|
return peers && typeof peers === "object" ? Object.keys(peers) : [];
|
|
70550
70893
|
}
|
|
70551
70894
|
function writeAppDts(projectDir, manifest) {
|
|
70552
|
-
const dotLotics =
|
|
70553
|
-
|
|
70895
|
+
const dotLotics = path7.join(projectDir, ".lotics");
|
|
70896
|
+
fs6.mkdirSync(dotLotics, { recursive: true });
|
|
70554
70897
|
const written = [
|
|
70555
|
-
[
|
|
70556
|
-
[
|
|
70557
|
-
[
|
|
70898
|
+
[path7.join(dotLotics, "app_workflows.d.ts"), generateAppWorkflowsDts(manifest.workflows)],
|
|
70899
|
+
[path7.join(dotLotics, "app_queries.d.ts"), generateAppQueriesDts(manifest.queries)],
|
|
70900
|
+
[path7.join(dotLotics, "app_agents.d.ts"), generateAppAgentsDts(manifest.agents)]
|
|
70558
70901
|
];
|
|
70559
|
-
for (const [file2, content] of written)
|
|
70902
|
+
for (const [file2, content] of written) fs6.writeFileSync(file2, content);
|
|
70560
70903
|
writeDevLinkTsconfig(projectDir);
|
|
70561
70904
|
writeKitTypeAugmentation(projectDir);
|
|
70562
70905
|
ensureAppTsconfig(projectDir);
|
|
70563
70906
|
return written.map(([file2]) => file2);
|
|
70564
70907
|
}
|
|
70565
70908
|
function readCodegenTablesAllowlist(projectDir) {
|
|
70566
|
-
const pkg2 = JSON.parse(
|
|
70909
|
+
const pkg2 = JSON.parse(fs6.readFileSync(path7.join(projectDir, "package.json"), "utf-8"));
|
|
70567
70910
|
const tables = pkg2.lotics?.codegen?.tables;
|
|
70568
70911
|
return Array.isArray(tables) ? tables.filter((t) => typeof t === "string") : [];
|
|
70569
70912
|
}
|
|
@@ -70576,17 +70919,17 @@ function resolveCodegenTableIds(projectDir, queries) {
|
|
|
70576
70919
|
return [...ids];
|
|
70577
70920
|
}
|
|
70578
70921
|
function writeAppFields(projectDir, tables) {
|
|
70579
|
-
const dotLotics =
|
|
70580
|
-
|
|
70581
|
-
const file2 =
|
|
70582
|
-
|
|
70922
|
+
const dotLotics = path7.join(projectDir, ".lotics");
|
|
70923
|
+
fs6.mkdirSync(dotLotics, { recursive: true });
|
|
70924
|
+
const file2 = path7.join(dotLotics, "app_fields.ts");
|
|
70925
|
+
fs6.writeFileSync(file2, generateAppFields(tables));
|
|
70583
70926
|
return file2;
|
|
70584
70927
|
}
|
|
70585
70928
|
function writeBindingAppFields(projectDir, binding) {
|
|
70586
|
-
const dotLotics =
|
|
70587
|
-
|
|
70588
|
-
const file2 =
|
|
70589
|
-
|
|
70929
|
+
const dotLotics = path7.join(projectDir, ".lotics");
|
|
70930
|
+
fs6.mkdirSync(dotLotics, { recursive: true });
|
|
70931
|
+
const file2 = path7.join(dotLotics, "app_fields.ts");
|
|
70932
|
+
fs6.writeFileSync(file2, generatePackageAppFields(binding));
|
|
70590
70933
|
ensureAppVitestSetup(projectDir);
|
|
70591
70934
|
return file2;
|
|
70592
70935
|
}
|
|
@@ -70608,6 +70951,15 @@ async function writeGeneratedAppFields(client, projectDir, app, queries) {
|
|
|
70608
70951
|
} else {
|
|
70609
70952
|
const tableIds = resolveCodegenTableIds(projectDir, queries);
|
|
70610
70953
|
const tables = await client.getWorkspaceSchema(tableIds);
|
|
70954
|
+
const missing = tableIds.filter((id) => !tables.some((t) => t.id === id));
|
|
70955
|
+
if (missing.length > 0) {
|
|
70956
|
+
console.error(
|
|
70957
|
+
`\u26A0 Skipped .lotics/app_fields.ts \u2014 ${missing.length} table(s) the app queries could not be read: ${missing.join(", ")}.
|
|
70958
|
+
Writing anyway would bake a narrowed F/OPT map that compiles and then throws at runtime. Check this key's access to those tables.
|
|
70959
|
+
An existing app_fields.ts is kept and still works; a project that has none yet will fail to BUILD until this resolves \u2014 which is the loud failure, not a new problem.`
|
|
70960
|
+
);
|
|
70961
|
+
return;
|
|
70962
|
+
}
|
|
70611
70963
|
const fieldsPath = writeAppFields(projectDir, tables);
|
|
70612
70964
|
console.error(
|
|
70613
70965
|
`Wrote ${fieldsPath} (baked form \u2014 bespoke; ${tables.length} table${tables.length === 1 ? "" : "s"})`
|
|
@@ -70619,7 +70971,7 @@ async function writeGeneratedAppFields(client, projectDir, app, queries) {
|
|
|
70619
70971
|
}
|
|
70620
70972
|
function warnAppFieldsUnwritten(projectDir, err2) {
|
|
70621
70973
|
const reason = err2 instanceof Error ? err2.message : String(err2);
|
|
70622
|
-
if (
|
|
70974
|
+
if (fs6.existsSync(path7.join(projectDir, ".lotics", "app_fields.ts"))) {
|
|
70623
70975
|
console.error(`\u26A0 Could not regenerate .lotics/app_fields.ts (${reason}). Kept the existing file.`);
|
|
70624
70976
|
return;
|
|
70625
70977
|
}
|
|
@@ -70630,8 +70982,8 @@ function warnAppFieldsUnwritten(projectDir, err2) {
|
|
|
70630
70982
|
function warnAboutDevLink(projectDir, command) {
|
|
70631
70983
|
const uiSrc = process.env.LOTICS_UI_SRC;
|
|
70632
70984
|
if (!uiSrc) return;
|
|
70633
|
-
const viteConfigPath =
|
|
70634
|
-
const config2 =
|
|
70985
|
+
const viteConfigPath = path7.join(projectDir, "vite.config.ts");
|
|
70986
|
+
const config2 = fs6.existsSync(viteConfigPath) ? fs6.readFileSync(viteConfigPath, "utf-8") : "";
|
|
70635
70987
|
const linked = config2.includes("loticsResolve") || config2.includes("LOTICS_UI_SRC");
|
|
70636
70988
|
if (!linked) {
|
|
70637
70989
|
console.error(
|
|
@@ -70649,19 +71001,19 @@ function warnAboutDevLink(projectDir, command) {
|
|
|
70649
71001
|
}
|
|
70650
71002
|
}
|
|
70651
71003
|
function ensureAppVitestSetup(projectDir) {
|
|
70652
|
-
const setupPath =
|
|
70653
|
-
if (!
|
|
70654
|
-
|
|
71004
|
+
const setupPath = path7.join(projectDir, VITEST_SETUP_FILENAME);
|
|
71005
|
+
if (!fs6.existsSync(setupPath)) {
|
|
71006
|
+
fs6.writeFileSync(setupPath, VITEST_SETUP_CONTENT);
|
|
70655
71007
|
console.error(`Wrote ${VITEST_SETUP_FILENAME} (stubs getAppBinding so \`npm test\` collects).`);
|
|
70656
|
-
} else if (!
|
|
71008
|
+
} else if (!fs6.readFileSync(setupPath, "utf-8").includes("getAppBinding")) {
|
|
70657
71009
|
console.error(
|
|
70658
71010
|
`\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
71011
|
${VITEST_SETUP_CONTENT}`
|
|
70660
71012
|
);
|
|
70661
71013
|
}
|
|
70662
|
-
const viteConfigPath =
|
|
70663
|
-
if (!
|
|
70664
|
-
const viteConfig =
|
|
71014
|
+
const viteConfigPath = path7.join(projectDir, "vite.config.ts");
|
|
71015
|
+
if (!fs6.existsSync(viteConfigPath)) return;
|
|
71016
|
+
const viteConfig = fs6.readFileSync(viteConfigPath, "utf-8");
|
|
70665
71017
|
if (/setupFiles\s*:/.test(viteConfig) && viteConfig.includes(VITEST_SETUP_FILENAME)) return;
|
|
70666
71018
|
console.error(
|
|
70667
71019
|
`\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 +71021,7 @@ ${VITEST_SETUP_CONTENT}`
|
|
|
70669
71021
|
);
|
|
70670
71022
|
}
|
|
70671
71023
|
async function appCodegen(args) {
|
|
70672
|
-
const projectDir =
|
|
71024
|
+
const projectDir = path7.resolve(args.projectDir ?? process.cwd());
|
|
70673
71025
|
const meta3 = readAppMeta(projectDir);
|
|
70674
71026
|
const dtsPaths = writeAppDts(projectDir, {
|
|
70675
71027
|
workflows: meta3.workflows,
|
|
@@ -70683,7 +71035,7 @@ async function appCodegen(args) {
|
|
|
70683
71035
|
}
|
|
70684
71036
|
for (const alias of orphanWorkflowBodies(projectDir, declared)) {
|
|
70685
71037
|
console.error(
|
|
70686
|
-
`\u26A0 ${
|
|
71038
|
+
`\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
71039
|
);
|
|
70688
71040
|
}
|
|
70689
71041
|
if (!args.client) {
|
|
@@ -70734,8 +71086,8 @@ async function refreshWorkflowGlobals(client, projectDir, app_id, workflows) {
|
|
|
70734
71086
|
}
|
|
70735
71087
|
async function refreshWorkflowTypes(client, projectDir, app_id, alias, declaration) {
|
|
70736
71088
|
const file2 = workflowFilePath(projectDir, alias);
|
|
70737
|
-
if (!
|
|
70738
|
-
const body = stripWorkflowHeader(
|
|
71089
|
+
if (!fs6.existsSync(file2)) return false;
|
|
71090
|
+
const body = stripWorkflowHeader(fs6.readFileSync(file2, "utf-8"));
|
|
70739
71091
|
if (body.trim() === "") return false;
|
|
70740
71092
|
const envelope = await fetchWorkflowGlobals(client, projectDir, app_id, alias, declaration);
|
|
70741
71093
|
writeWorkflowFile(projectDir, alias, body, envelope);
|
|
@@ -70747,14 +71099,14 @@ function stampPulledManifest(projectDir, args) {
|
|
|
70747
71099
|
workspace_id: args.workspace_id,
|
|
70748
71100
|
current_version_id: args.current_version_id,
|
|
70749
71101
|
version_number: args.version_number,
|
|
70750
|
-
workflows: args.workflows,
|
|
71102
|
+
workflows: toManifestWorkflows(args.workflows),
|
|
70751
71103
|
queries: args.queries,
|
|
70752
71104
|
// The prose is stripped here and only here: `args.agents` is the LIVE map
|
|
70753
71105
|
// (its instructions feed `writeAgentFiles`), the manifest gets the typed half.
|
|
70754
71106
|
agents: toManifestAgents(args.agents)
|
|
70755
71107
|
});
|
|
70756
71108
|
writeAppDts(projectDir, {
|
|
70757
|
-
workflows: args.workflows,
|
|
71109
|
+
workflows: toManifestWorkflows(args.workflows),
|
|
70758
71110
|
queries: args.queries,
|
|
70759
71111
|
agents: toManifestAgents(args.agents)
|
|
70760
71112
|
});
|
|
@@ -70765,21 +71117,21 @@ async function downloadToFile(url2, destPath) {
|
|
|
70765
71117
|
throw new Error(`Failed to download ${url2}: ${response.status}`);
|
|
70766
71118
|
}
|
|
70767
71119
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
70768
|
-
|
|
71120
|
+
fs6.writeFileSync(destPath, buffer);
|
|
70769
71121
|
}
|
|
70770
71122
|
function appDirName(name2) {
|
|
70771
71123
|
const cleaned = name2.replace(/[/\\]+/g, "-").replace(/[<>:"|?*]/g, "").replace(/\s+/g, " ").trim().replace(/^[.\s-]+|[.\s-]+$/g, "");
|
|
70772
71124
|
return cleaned || "app";
|
|
70773
71125
|
}
|
|
70774
71126
|
async function appCreate(client, args) {
|
|
70775
|
-
const targetPath =
|
|
70776
|
-
if (
|
|
70777
|
-
const entries2 =
|
|
71127
|
+
const targetPath = path7.resolve(args.targetPath ?? appDirName(args.name));
|
|
71128
|
+
if (fs6.existsSync(targetPath)) {
|
|
71129
|
+
const entries2 = fs6.readdirSync(targetPath);
|
|
70778
71130
|
if (entries2.length > 0) {
|
|
70779
71131
|
throw new Error(`Target directory ${targetPath} is not empty.`);
|
|
70780
71132
|
}
|
|
70781
71133
|
}
|
|
70782
|
-
|
|
71134
|
+
fs6.mkdirSync(targetPath, { recursive: true });
|
|
70783
71135
|
const app = await client.createApp({ name: args.name });
|
|
70784
71136
|
console.error(`Created app: ${app.name} (${app.id})`);
|
|
70785
71137
|
const [uiLatest, sdkLatest] = await Promise.all([
|
|
@@ -70794,9 +71146,9 @@ async function appCreate(client, args) {
|
|
|
70794
71146
|
sdk_version: sdkLatest ? `^${sdkLatest}` : void 0
|
|
70795
71147
|
});
|
|
70796
71148
|
for (const file2 of files) {
|
|
70797
|
-
const fullPath =
|
|
70798
|
-
|
|
70799
|
-
|
|
71149
|
+
const fullPath = path7.join(targetPath, file2.path);
|
|
71150
|
+
fs6.mkdirSync(path7.dirname(fullPath), { recursive: true });
|
|
71151
|
+
fs6.writeFileSync(fullPath, file2.content);
|
|
70800
71152
|
}
|
|
70801
71153
|
console.error(`Scaffolded ${files.length} files into ${targetPath}`);
|
|
70802
71154
|
writeAppDts(targetPath, {});
|
|
@@ -70809,7 +71161,7 @@ async function appCreate(client, args) {
|
|
|
70809
71161
|
});
|
|
70810
71162
|
console.error(`
|
|
70811
71163
|
Ready. Next steps:`);
|
|
70812
|
-
console.error(` cd ${
|
|
71164
|
+
console.error(` cd ${path7.relative(process.cwd(), targetPath) || "."}`);
|
|
70813
71165
|
console.error(` # edit src/App.tsx, then:`);
|
|
70814
71166
|
console.error(` lotics app deploy`);
|
|
70815
71167
|
}
|
|
@@ -70866,11 +71218,11 @@ async function appVersions(client, args) {
|
|
|
70866
71218
|
}
|
|
70867
71219
|
}
|
|
70868
71220
|
function defaultPullTarget(appId, appName) {
|
|
70869
|
-
const cwdPkgPath =
|
|
70870
|
-
if (
|
|
71221
|
+
const cwdPkgPath = path7.join(process.cwd(), "package.json");
|
|
71222
|
+
if (fs6.existsSync(cwdPkgPath)) {
|
|
70871
71223
|
let pkg2 = null;
|
|
70872
71224
|
try {
|
|
70873
|
-
pkg2 = JSON.parse(
|
|
71225
|
+
pkg2 = JSON.parse(fs6.readFileSync(cwdPkgPath, "utf-8"));
|
|
70874
71226
|
} catch (err2) {
|
|
70875
71227
|
console.error(
|
|
70876
71228
|
`\u26A0 Could not parse ${cwdPkgPath} (${err2 instanceof Error ? err2.message : String(err2)}) \u2014 pulling into a fresh ${appDirName(appName)}/ subdir.`
|
|
@@ -70889,16 +71241,20 @@ async function appPull(client, args) {
|
|
|
70889
71241
|
}
|
|
70890
71242
|
const version2 = await client.getAppVersion(app.id, app.current_version_id);
|
|
70891
71243
|
const sourceUrl = await client.getAppVersionSourceUrl(app.id, version2.id);
|
|
70892
|
-
const targetPath =
|
|
70893
|
-
|
|
70894
|
-
const tmpFile =
|
|
71244
|
+
const targetPath = path7.resolve(args.targetPath ?? defaultPullTarget(app.id, app.name));
|
|
71245
|
+
fs6.mkdirSync(targetPath, { recursive: true });
|
|
71246
|
+
const tmpFile = path7.join(tmpdir(), `lotics-app-${app.id}-${Date.now()}.tar.gz`);
|
|
70895
71247
|
console.error(`Downloading source archive...`);
|
|
70896
71248
|
await downloadToFile(sourceUrl, tmpFile);
|
|
71249
|
+
const keptLocal = [];
|
|
71250
|
+
const stagingDir = fs6.mkdtempSync(path7.join(tmpdir(), `lotics-pull-${app.id}-`));
|
|
70897
71251
|
try {
|
|
71252
|
+
await runTar(["-xzf", tmpFile, "-C", stagingDir], stagingDir);
|
|
70898
71253
|
console.error(`Extracting to ${targetPath}...`);
|
|
70899
|
-
|
|
71254
|
+
keptLocal.push(...copyTree(stagingDir, targetPath, { overwrite: args.force === true }));
|
|
70900
71255
|
} finally {
|
|
70901
|
-
if (
|
|
71256
|
+
if (fs6.existsSync(tmpFile)) fs6.unlinkSync(tmpFile);
|
|
71257
|
+
fs6.rmSync(stagingDir, { recursive: true, force: true });
|
|
70902
71258
|
}
|
|
70903
71259
|
stampPulledManifest(targetPath, {
|
|
70904
71260
|
app_id: app.id,
|
|
@@ -70909,6 +71265,10 @@ async function appPull(client, args) {
|
|
|
70909
71265
|
queries: app.queries ?? {},
|
|
70910
71266
|
agents: app.agents ?? {}
|
|
70911
71267
|
});
|
|
71268
|
+
for (const [alias, declaration] of Object.entries(app.queries ?? {})) {
|
|
71269
|
+
const sha = declarationSha(declaration);
|
|
71270
|
+
writeSynced(targetPath, "queries", alias, { content: sha, live: sha });
|
|
71271
|
+
}
|
|
70912
71272
|
await writeGeneratedAppFields(
|
|
70913
71273
|
client,
|
|
70914
71274
|
targetPath,
|
|
@@ -70917,7 +71277,10 @@ async function appPull(client, args) {
|
|
|
70917
71277
|
);
|
|
70918
71278
|
const workflows = app.workflows ?? {};
|
|
70919
71279
|
if (Object.keys(workflows).length > 0) {
|
|
70920
|
-
const written = await writeWorkflowFiles(client, targetPath, app.id, app.name, workflows
|
|
71280
|
+
const written = await writeWorkflowFiles(client, targetPath, app.id, app.name, workflows, {
|
|
71281
|
+
overwrite: args.force === true,
|
|
71282
|
+
kept: keptLocal
|
|
71283
|
+
});
|
|
70921
71284
|
if (written.length > 0) {
|
|
70922
71285
|
console.error(
|
|
70923
71286
|
`Wrote ${written.length} workflow ${written.length === 1 ? "body" : "bodies"} to ${WORKFLOWS_DIR}/ (${written.join(", ")})`
|
|
@@ -70926,7 +71289,16 @@ async function appPull(client, args) {
|
|
|
70926
71289
|
}
|
|
70927
71290
|
const agents = app.agents ?? {};
|
|
70928
71291
|
if (Object.keys(agents).length > 0) {
|
|
70929
|
-
const written = writeAgentFiles(targetPath, agents
|
|
71292
|
+
const written = writeAgentFiles(targetPath, agents, {
|
|
71293
|
+
overwrite: args.force === true,
|
|
71294
|
+
kept: keptLocal
|
|
71295
|
+
});
|
|
71296
|
+
for (const alias of written) {
|
|
71297
|
+
const instructions = agents[alias]?.instructions;
|
|
71298
|
+
if (instructions === void 0) continue;
|
|
71299
|
+
const sha = contentSha(instructions);
|
|
71300
|
+
writeSynced(targetPath, "agents", alias, { content: sha, live: sha });
|
|
71301
|
+
}
|
|
70930
71302
|
if (written.length > 0) {
|
|
70931
71303
|
console.error(
|
|
70932
71304
|
`Wrote ${written.length} agent instruction ${written.length === 1 ? "file" : "files"} to ${AGENTS_DIR}/ (${written.join(", ")})`
|
|
@@ -70935,62 +71307,69 @@ async function appPull(client, args) {
|
|
|
70935
71307
|
}
|
|
70936
71308
|
console.error(`Installing npm dependencies...`);
|
|
70937
71309
|
await runNpm(["install"], targetPath);
|
|
71310
|
+
reportKeptLocalFiles(keptLocal, app.id);
|
|
70938
71311
|
console.error(`
|
|
70939
71312
|
Ready. Next steps:`);
|
|
70940
|
-
console.error(` cd ${
|
|
71313
|
+
console.error(` cd ${path7.relative(process.cwd(), targetPath) || "."}`);
|
|
70941
71314
|
console.error(` # edit src/App.tsx`);
|
|
70942
71315
|
console.error(` lotics app deploy`);
|
|
70943
71316
|
}
|
|
70944
71317
|
function readAppSourceText(projectDir) {
|
|
70945
|
-
const srcDir =
|
|
70946
|
-
if (!
|
|
71318
|
+
const srcDir = path7.join(projectDir, "src");
|
|
71319
|
+
if (!fs6.existsSync(srcDir)) return "";
|
|
70947
71320
|
const parts = [];
|
|
70948
71321
|
const walk2 = (dir) => {
|
|
70949
|
-
for (const entry of
|
|
70950
|
-
const full =
|
|
71322
|
+
for (const entry of fs6.readdirSync(dir, { withFileTypes: true })) {
|
|
71323
|
+
const full = path7.join(dir, entry.name);
|
|
70951
71324
|
if (entry.isDirectory()) walk2(full);
|
|
70952
|
-
else if (/\.(ts|tsx|js|jsx)$/.test(entry.name)) parts.push(
|
|
71325
|
+
else if (/\.(ts|tsx|js|jsx)$/.test(entry.name)) parts.push(fs6.readFileSync(full, "utf8"));
|
|
70953
71326
|
}
|
|
70954
71327
|
};
|
|
70955
71328
|
walk2(srcDir);
|
|
70956
71329
|
return parts.join("\n");
|
|
70957
71330
|
}
|
|
70958
71331
|
async function appDeploy(client, args) {
|
|
70959
|
-
const projectDir =
|
|
71332
|
+
const projectDir = path7.resolve(args.projectDir ?? process.cwd());
|
|
70960
71333
|
const meta3 = readAppMeta(projectDir);
|
|
70961
71334
|
warnAboutDevLink(projectDir, "deploy");
|
|
70962
71335
|
const liveApp = await client.getApp(meta3.app_id);
|
|
70963
|
-
const
|
|
70964
|
-
|
|
70965
|
-
|
|
70966
|
-
|
|
70967
|
-
|
|
70968
|
-
|
|
70969
|
-
|
|
70970
|
-
|
|
70971
|
-
types useAgentRun \u2014 so this build was checked against an agent that does not exist.
|
|
70972
|
-
To take the app's shape: lotics app codegen (refreshes the block + the types)
|
|
70973
|
-
To CHANGE the agent instead: lotics run set_app_agent '{"app_id":"${meta3.app_id}","alias":"\u2026","outputs":{\u2026}}'
|
|
70974
|
-
(send only what changes \u2014 the server merges), then codegen.`
|
|
70975
|
-
);
|
|
70976
|
-
process.exit(1);
|
|
71336
|
+
const pending = pendingBindings({
|
|
71337
|
+
projectDir,
|
|
71338
|
+
meta: meta3,
|
|
71339
|
+
synced: readSynced(projectDir).workflows,
|
|
71340
|
+
live: liveApp
|
|
71341
|
+
});
|
|
71342
|
+
if (!nothingPending(pending)) {
|
|
71343
|
+
await pushPendingBindings(client, projectDir, pending);
|
|
70977
71344
|
}
|
|
70978
71345
|
const sourceText = readAppSourceText(projectDir);
|
|
70979
71346
|
const called = calledAppAliases(sourceText);
|
|
70980
|
-
warnIfAgentProseDiffers(projectDir, liveApp.agents);
|
|
70981
71347
|
warnIfDynamicAliases(called);
|
|
70982
71348
|
warnIfUndeclaredCapabilities(sourceText, meta3.capabilities);
|
|
70983
71349
|
writeAppDts(projectDir, { workflows: meta3.workflows, queries: meta3.queries, agents: meta3.agents });
|
|
71350
|
+
try {
|
|
71351
|
+
await writeGeneratedAppFields(
|
|
71352
|
+
client,
|
|
71353
|
+
projectDir,
|
|
71354
|
+
{ app_id: meta3.app_id, package_id: liveApp.package_id },
|
|
71355
|
+
meta3.queries ?? {}
|
|
71356
|
+
);
|
|
71357
|
+
} catch (err2) {
|
|
71358
|
+
console.error(
|
|
71359
|
+
`\u26A0 Could not regenerate .lotics/app_fields.ts (${err2.message}).
|
|
71360
|
+
The build will use whatever is on disk \u2014 run 'lotics app codegen' if this app is published.`
|
|
71361
|
+
);
|
|
71362
|
+
}
|
|
70984
71363
|
console.error("Building...");
|
|
70985
71364
|
await runNpm(["run", "build"], projectDir);
|
|
70986
|
-
const distDir =
|
|
70987
|
-
if (!
|
|
71365
|
+
const distDir = path7.join(projectDir, "dist");
|
|
71366
|
+
if (!fs6.existsSync(distDir)) {
|
|
70988
71367
|
throw new Error(
|
|
70989
71368
|
`Build did not produce a dist/ directory in ${projectDir}. Check that 'npm run build' is configured correctly (Vite/etc. defaults to dist/).`
|
|
70990
71369
|
);
|
|
70991
71370
|
}
|
|
70992
|
-
const tmpSource =
|
|
70993
|
-
const tmpDist =
|
|
71371
|
+
const tmpSource = path7.join(tmpdir(), `lotics-source-${Date.now()}.tar.gz`);
|
|
71372
|
+
const tmpDist = path7.join(tmpdir(), `lotics-dist-${Date.now()}.tar.gz`);
|
|
70994
71373
|
try {
|
|
70995
71374
|
console.error("Packaging source...");
|
|
70996
71375
|
await runTar(
|
|
@@ -71014,10 +71393,10 @@ Refusing to deploy \u2014 package.json#lotics.agents disagrees with the live app
|
|
|
71014
71393
|
const result = await client.deployAppVersion({
|
|
71015
71394
|
queries: liveQueries,
|
|
71016
71395
|
app_id: meta3.app_id,
|
|
71017
|
-
source_archive:
|
|
71018
|
-
dist_archive:
|
|
71396
|
+
source_archive: fs6.readFileSync(tmpSource),
|
|
71397
|
+
dist_archive: fs6.readFileSync(tmpDist),
|
|
71019
71398
|
prev_version_id: meta3.current_version_id,
|
|
71020
|
-
message: args.message,
|
|
71399
|
+
message: args.message ?? derivedDeployMessage(pending),
|
|
71021
71400
|
// Capabilities ARE manifest-authoritative: always send,
|
|
71022
71401
|
// defaulting to `{}` when the manifest declares none — so deleting the
|
|
71023
71402
|
// `capabilities` block turns every capability OFF on the next deploy
|
|
@@ -71032,7 +71411,8 @@ Refusing to deploy \u2014 package.json#lotics.agents disagrees with the live app
|
|
|
71032
71411
|
// with whatever the author last remembered to write. Stop calling an
|
|
71033
71412
|
// alias + redeploy to lift the guard before removing its binding.
|
|
71034
71413
|
workflow_aliases: called.workflows,
|
|
71035
|
-
query_aliases: called.queries
|
|
71414
|
+
query_aliases: called.queries,
|
|
71415
|
+
agent_aliases: called.agents
|
|
71036
71416
|
});
|
|
71037
71417
|
writeAppMeta(projectDir, {
|
|
71038
71418
|
...meta3,
|
|
@@ -71041,14 +71421,24 @@ Refusing to deploy \u2014 package.json#lotics.agents disagrees with the live app
|
|
|
71041
71421
|
});
|
|
71042
71422
|
console.error(`Deployed v${result.version_number} (${result.version_id})`);
|
|
71043
71423
|
console.error(`Bundle size: ${(result.bundle_size_bytes / 1024).toFixed(1)} KB`);
|
|
71424
|
+
let liveAfter;
|
|
71044
71425
|
try {
|
|
71045
|
-
|
|
71046
|
-
warnIfUnbranded(app);
|
|
71047
|
-
warnIfUnboundAliases(app, called);
|
|
71048
|
-
warnIfQueriesDiffer(app, meta3.queries ?? {});
|
|
71426
|
+
liveAfter = await client.getApp(meta3.app_id);
|
|
71049
71427
|
} catch (err2) {
|
|
71050
71428
|
console.error(`(skipped post-deploy checks: ${err2.message})`);
|
|
71051
71429
|
}
|
|
71430
|
+
if (liveAfter) {
|
|
71431
|
+
warnIfUnbranded(liveAfter);
|
|
71432
|
+
warnIfUnboundAliases(liveAfter, called);
|
|
71433
|
+
}
|
|
71434
|
+
if (liveAfter) {
|
|
71435
|
+
await pruneOrphanedBindings(
|
|
71436
|
+
client,
|
|
71437
|
+
meta3.app_id,
|
|
71438
|
+
orphanedBindings(called, liveAfter),
|
|
71439
|
+
called.dynamic
|
|
71440
|
+
);
|
|
71441
|
+
}
|
|
71052
71442
|
} catch (err2) {
|
|
71053
71443
|
const e = err2;
|
|
71054
71444
|
if (e.code === "VERSION_CONFLICT") {
|
|
@@ -71060,8 +71450,8 @@ Refusing to deploy \u2014 package.json#lotics.agents disagrees with the live app
|
|
|
71060
71450
|
throw err2;
|
|
71061
71451
|
}
|
|
71062
71452
|
} finally {
|
|
71063
|
-
if (
|
|
71064
|
-
if (
|
|
71453
|
+
if (fs6.existsSync(tmpSource)) fs6.unlinkSync(tmpSource);
|
|
71454
|
+
if (fs6.existsSync(tmpDist)) fs6.unlinkSync(tmpDist);
|
|
71065
71455
|
}
|
|
71066
71456
|
}
|
|
71067
71457
|
function warnIfUnbranded(app) {
|
|
@@ -71088,54 +71478,233 @@ async function readLiveQueries(client, appId) {
|
|
|
71088
71478
|
process.exit(1);
|
|
71089
71479
|
}
|
|
71090
71480
|
}
|
|
71091
|
-
function warnIfQueriesDiffer(app, manifest) {
|
|
71092
|
-
const drifted = driftedQueryAliases(manifest, app.queries);
|
|
71093
|
-
if (drifted.length === 0) return;
|
|
71094
|
-
console.error(
|
|
71095
|
-
`
|
|
71096
|
-
\u26A0 ${drifted.length} quer${drifted.length === 1 ? "y" : "ies"} in package.json differ from the app: ${drifted.join(", ")}
|
|
71097
|
-
A deploy does not write them. Push yours with 'lotics app query set --all'
|
|
71098
|
-
(or one alias at a time), or adopt the app's with 'lotics app pull'.`
|
|
71099
|
-
);
|
|
71100
|
-
}
|
|
71101
71481
|
async function appCheck(client, args = {}) {
|
|
71102
|
-
const projectDir =
|
|
71482
|
+
const projectDir = path7.resolve(args.projectDir ?? process.cwd());
|
|
71103
71483
|
const meta3 = readAppMeta(projectDir);
|
|
71104
71484
|
const app = await client.getApp(meta3.app_id);
|
|
71105
71485
|
const sourceText = readAppSourceText(projectDir);
|
|
71106
71486
|
const called = calledAppAliases(sourceText);
|
|
71107
|
-
const divergences = agentTypeDivergences(meta3.agents, app.agents);
|
|
71108
|
-
for (const d of divergences) {
|
|
71109
|
-
console.error(`\u2717 package.json#lotics.agents.${d}`);
|
|
71110
|
-
}
|
|
71111
|
-
if (divergences.length > 0) {
|
|
71112
|
-
console.error(
|
|
71113
|
-
` A deploy REFUSES this: the bundle would be typed against an agent that does not exist.
|
|
71114
|
-
Take the app's shape with 'lotics app pull ${meta3.app_id}', or change the agent with set_app_agent.`
|
|
71115
|
-
);
|
|
71116
|
-
}
|
|
71117
71487
|
warnAboutDevLink(projectDir, "deploy");
|
|
71118
|
-
|
|
71488
|
+
warnAboutOrphanedBindings(called, app);
|
|
71489
|
+
const pending = pendingBindings({
|
|
71490
|
+
projectDir,
|
|
71491
|
+
meta: meta3,
|
|
71492
|
+
synced: readSynced(projectDir).workflows,
|
|
71493
|
+
live: app
|
|
71494
|
+
});
|
|
71495
|
+
reportPending(pending);
|
|
71119
71496
|
warnIfDynamicAliases(called);
|
|
71120
71497
|
warnIfUndeclaredCapabilities(sourceText, meta3.capabilities);
|
|
71121
71498
|
warnIfUnboundAliases(app, called);
|
|
71122
|
-
warnIfQueriesDiffer(app, meta3.queries ?? {});
|
|
71123
71499
|
warnIfUnbranded(app);
|
|
71124
|
-
if (
|
|
71500
|
+
if (!nothingPending(pending)) {
|
|
71501
|
+
process.exit(1);
|
|
71502
|
+
}
|
|
71125
71503
|
console.error("Checked the app's bindings, capabilities and agent schemas \u2014 nothing blocking.");
|
|
71126
71504
|
}
|
|
71127
|
-
function
|
|
71505
|
+
async function pushPendingBindings(client, projectDir, pending) {
|
|
71506
|
+
const plan = [
|
|
71507
|
+
...pending.queries.map((alias) => `query ${alias}`),
|
|
71508
|
+
...pending.workflows.map((alias) => `workflow ${alias}`),
|
|
71509
|
+
...pending.agents.map((alias) => `agent ${alias}`)
|
|
71510
|
+
];
|
|
71511
|
+
console.error(
|
|
71512
|
+
`
|
|
71513
|
+
Pushing ${plan.length} binding${plan.length === 1 ? "" : "s"} ahead of the app: ${plan.join(", ")}`
|
|
71514
|
+
);
|
|
71515
|
+
for (const alias of pending.queries) {
|
|
71516
|
+
await appQuerySet(client, { alias, projectDir });
|
|
71517
|
+
}
|
|
71518
|
+
for (const alias of pending.workflows) {
|
|
71519
|
+
await appWorkflowSet(client, { alias, projectDir });
|
|
71520
|
+
}
|
|
71521
|
+
for (const alias of pending.agents) {
|
|
71522
|
+
await appAgentSet(client, { alias, projectDir });
|
|
71523
|
+
}
|
|
71524
|
+
}
|
|
71525
|
+
function pendingBindings(args) {
|
|
71526
|
+
const { projectDir, meta: meta3, synced, live } = args;
|
|
71527
|
+
const workflows = /* @__PURE__ */ new Set([
|
|
71528
|
+
...workflowBodyDrift(projectDir, meta3.workflows, synced),
|
|
71529
|
+
...divergedWorkflowAliases(meta3.workflows, live.workflows)
|
|
71530
|
+
]);
|
|
71531
|
+
return {
|
|
71532
|
+
workflows: [...workflows].sort(),
|
|
71533
|
+
agents: [
|
|
71534
|
+
.../* @__PURE__ */ new Set([
|
|
71535
|
+
...driftedAgentAliases(projectDir, live.agents),
|
|
71536
|
+
// A manifest declaration that disagrees with the live row is a push now,
|
|
71537
|
+
// the same as a workflow's. `agentTypeDivergences` renders one line per
|
|
71538
|
+
// (alias, key) for a human, and the alias is its first token — but an
|
|
71539
|
+
// alias that diverged on both `inputs` and `outputs` still needs exactly
|
|
71540
|
+
// one push.
|
|
71541
|
+
...divergedAgentAliases(meta3.agents, live.agents)
|
|
71542
|
+
])
|
|
71543
|
+
].sort(),
|
|
71544
|
+
// Only aliases the manifest DECLARES. `driftedQueryAliases` is symmetric —
|
|
71545
|
+
// it unions both key sets — so an alias deleted locally but still bound live
|
|
71546
|
+
// reads as drift, and queueing it sends the author to a push that refuses
|
|
71547
|
+
// ("declare it first") for a declaration they deliberately removed. A
|
|
71548
|
+
// deletion is not a push, and this detector only decides what to push;
|
|
71549
|
+
// unbinding is `remove_app_query`, which no deploy runs for any artifact.
|
|
71550
|
+
queries: [...driftedQueryAliases(meta3.queries ?? {}, live.queries)].filter((alias) => (meta3.queries ?? {})[alias] !== void 0).sort()
|
|
71551
|
+
};
|
|
71552
|
+
}
|
|
71553
|
+
function reportPending(pending) {
|
|
71554
|
+
const lines = [
|
|
71555
|
+
...pending.workflows.map((a) => ` \u2022 workflow "${a}" \u2014 lotics app workflow set ${a}`),
|
|
71556
|
+
...pending.agents.map((a) => ` \u2022 agent "${a}" \u2014 lotics app agent set ${a}`),
|
|
71557
|
+
...pending.queries.map((a) => ` \u2022 query "${a}" \u2014 lotics app query set ${a}`)
|
|
71558
|
+
];
|
|
71559
|
+
if (lines.length === 0) return;
|
|
71560
|
+
console.error(
|
|
71561
|
+
`
|
|
71562
|
+
\u2717 ${lines.length} binding${lines.length === 1 ? " is" : "s are"} ahead of the app:
|
|
71563
|
+
` + lines.join("\n") + `
|
|
71564
|
+
|
|
71565
|
+
A version shipped now would not reproduce what runs. 'lotics app deploy' pushes
|
|
71566
|
+
these for you; revert the file instead to drop the edit.`
|
|
71567
|
+
);
|
|
71568
|
+
}
|
|
71569
|
+
function nothingPending(pending) {
|
|
71570
|
+
return pending.workflows.length === 0 && pending.agents.length === 0 && pending.queries.length === 0;
|
|
71571
|
+
}
|
|
71572
|
+
function divergedAgentAliases(manifestAgents, liveAgents) {
|
|
71573
|
+
const out = [];
|
|
71574
|
+
for (const [alias, declared] of Object.entries(manifestAgents ?? {})) {
|
|
71575
|
+
const live = (liveAgents ?? {})[alias];
|
|
71576
|
+
if (!live) continue;
|
|
71577
|
+
if (AGENT_TYPED_KEYS.some((key) => !sameSchema(declared[key], live[key]))) out.push(alias);
|
|
71578
|
+
}
|
|
71579
|
+
return out;
|
|
71580
|
+
}
|
|
71581
|
+
function driftedAgentAliases(projectDir, liveAgents) {
|
|
71582
|
+
const drifted = [];
|
|
71128
71583
|
for (const [alias, live] of Object.entries(liveAgents ?? {})) {
|
|
71129
71584
|
const file2 = agentFilePath2(projectDir, alias);
|
|
71130
|
-
if (!
|
|
71131
|
-
const local = stripAgentHeader(
|
|
71585
|
+
if (!fs6.existsSync(file2)) continue;
|
|
71586
|
+
const local = stripAgentHeader(fs6.readFileSync(file2, "utf-8"));
|
|
71132
71587
|
if (local === "" || local === (live.instructions ?? "")) continue;
|
|
71588
|
+
drifted.push(alias);
|
|
71589
|
+
}
|
|
71590
|
+
return drifted.sort();
|
|
71591
|
+
}
|
|
71592
|
+
function divergedWorkflowAliases(manifestWorkflows, liveWorkflows) {
|
|
71593
|
+
return [
|
|
71594
|
+
...new Set(workflowTypeDivergences(manifestWorkflows, liveWorkflows).map((d) => d.alias))
|
|
71595
|
+
].sort();
|
|
71596
|
+
}
|
|
71597
|
+
function orphanedBindings(called, live) {
|
|
71598
|
+
const agentDeclarations = Object.values(live.agents ?? {});
|
|
71599
|
+
const referenced = {
|
|
71600
|
+
queries: [...called.queries, ...agentDeclarations.flatMap((a) => a.query_aliases ?? [])],
|
|
71601
|
+
workflows: [...called.workflows, ...agentDeclarations.flatMap((a) => a.workflow_aliases ?? [])],
|
|
71602
|
+
// Nothing declares an agent but the client bundle — an agent cannot run another.
|
|
71603
|
+
agents: called.agents
|
|
71604
|
+
};
|
|
71605
|
+
return orphanedAliases(
|
|
71606
|
+
{
|
|
71607
|
+
queries: Object.keys(live.queries ?? {}),
|
|
71608
|
+
workflows: Object.keys(live.workflows ?? {}),
|
|
71609
|
+
agents: Object.keys(live.agents ?? {})
|
|
71610
|
+
},
|
|
71611
|
+
referenced
|
|
71612
|
+
);
|
|
71613
|
+
}
|
|
71614
|
+
function warnAboutOrphanedBindings(called, live) {
|
|
71615
|
+
const orphans = orphanedBindings(called, live);
|
|
71616
|
+
const lines = [
|
|
71617
|
+
...orphans.queries.map((a) => ` \u2022 query ${a}`),
|
|
71618
|
+
...orphans.workflows.map((a) => ` \u2022 workflow ${a}`),
|
|
71619
|
+
...orphans.agents.map((a) => ` \u2022 agent ${a}`)
|
|
71620
|
+
];
|
|
71621
|
+
if (lines.length === 0) return 0;
|
|
71622
|
+
console.error(
|
|
71623
|
+
`
|
|
71624
|
+
\u26A0 ${lines.length} binding(s) are live but named nowhere in this bundle:
|
|
71625
|
+
` + lines.join("\n") + (called.dynamic.length > 0 ? `
|
|
71626
|
+
|
|
71627
|
+
This app computes an alias at runtime, so one of these may be called after
|
|
71628
|
+
all \u2014 a deploy therefore leaves them ALL in place. Make those call sites
|
|
71629
|
+
literal and a deploy will clean them up.` : `
|
|
71630
|
+
|
|
71631
|
+
A deploy unbinds these after it ships.`)
|
|
71632
|
+
);
|
|
71633
|
+
return lines.length;
|
|
71634
|
+
}
|
|
71635
|
+
async function pruneOrphanedBindings(client, appId, orphans, dynamic) {
|
|
71636
|
+
const targets = [
|
|
71637
|
+
...orphans.queries.map((alias) => ({ tool: "remove_app_query", alias, label: `query ${alias}` })),
|
|
71638
|
+
...orphans.workflows.map((alias) => ({
|
|
71639
|
+
tool: "remove_app_workflow",
|
|
71640
|
+
alias,
|
|
71641
|
+
label: `workflow ${alias}`
|
|
71642
|
+
})),
|
|
71643
|
+
...orphans.agents.map((alias) => ({ tool: "remove_app_agent", alias, label: `agent ${alias}` }))
|
|
71644
|
+
];
|
|
71645
|
+
if (targets.length === 0) return 0;
|
|
71646
|
+
if (dynamic.length > 0) {
|
|
71133
71647
|
console.error(
|
|
71134
71648
|
`
|
|
71135
|
-
\u26A0 ${
|
|
71136
|
-
|
|
71649
|
+
\u26A0 Left ${targets.length} unused binding(s) in place: this app calls ${dynamic.join(" / ")} with an alias it computes at runtime, and a static scan cannot tell which binding that reaches \u2014 so any of these may be live:
|
|
71650
|
+
` + targets.map((t) => ` \u2022 ${t.label}`).join("\n") + `
|
|
71651
|
+
Make those call sites literal and the next deploy will clean them up, or remove the ones you know are dead by hand.`
|
|
71137
71652
|
);
|
|
71653
|
+
return targets.length;
|
|
71654
|
+
}
|
|
71655
|
+
let failed = 0;
|
|
71656
|
+
for (const target of targets) {
|
|
71657
|
+
const res = await client.execute(target.tool, { app_id: appId, alias: target.alias });
|
|
71658
|
+
if (res.error) {
|
|
71659
|
+
failed += 1;
|
|
71660
|
+
console.error(` \u2717 could not unbind ${target.label} \u2014 ${res.error}`);
|
|
71661
|
+
continue;
|
|
71662
|
+
}
|
|
71663
|
+
console.error(` \u2713 unbound ${target.label}`);
|
|
71138
71664
|
}
|
|
71665
|
+
return failed;
|
|
71666
|
+
}
|
|
71667
|
+
function derivedDeployMessage(pending) {
|
|
71668
|
+
const parts = [
|
|
71669
|
+
...pending.queries.map((a) => `query ${a}`),
|
|
71670
|
+
...pending.workflows.map((a) => `workflow ${a}`),
|
|
71671
|
+
...pending.agents.map((a) => `agent ${a}`)
|
|
71672
|
+
];
|
|
71673
|
+
return parts.length === 0 ? "Deployed app code" : `Deployed app code; pushed ${parts.join(", ")}`;
|
|
71674
|
+
}
|
|
71675
|
+
function copyTree(from, to, opts, relative = "") {
|
|
71676
|
+
const kept = [];
|
|
71677
|
+
for (const entry of fs6.readdirSync(from, { withFileTypes: true })) {
|
|
71678
|
+
const src = path7.join(from, entry.name);
|
|
71679
|
+
const dest = path7.join(to, entry.name);
|
|
71680
|
+
const rel = path7.join(relative, entry.name);
|
|
71681
|
+
if (entry.isDirectory()) {
|
|
71682
|
+
fs6.mkdirSync(dest, { recursive: true });
|
|
71683
|
+
kept.push(...copyTree(src, dest, opts, rel));
|
|
71684
|
+
continue;
|
|
71685
|
+
}
|
|
71686
|
+
if (!opts.overwrite && fs6.existsSync(dest)) {
|
|
71687
|
+
if (fs6.readFileSync(src).equals(fs6.readFileSync(dest))) continue;
|
|
71688
|
+
kept.push(rel);
|
|
71689
|
+
continue;
|
|
71690
|
+
}
|
|
71691
|
+
fs6.copyFileSync(src, dest);
|
|
71692
|
+
}
|
|
71693
|
+
return kept;
|
|
71694
|
+
}
|
|
71695
|
+
function reportKeptLocalFiles(kept, appId) {
|
|
71696
|
+
if (kept.length === 0) return;
|
|
71697
|
+
console.error(
|
|
71698
|
+
`
|
|
71699
|
+
${kept.length} local file(s) differ from the app and were KEPT, not overwritten:
|
|
71700
|
+
` + kept.slice(0, 20).map((f) => ` \u2022 ${f}`).join("\n") + (kept.length > 20 ? `
|
|
71701
|
+
\u2026 and ${kept.length - 20} more` : ``) + `
|
|
71702
|
+
|
|
71703
|
+
Everything else was refreshed from the app. Your edits are intact.
|
|
71704
|
+
Ship them: lotics app deploy (pushes bodies, prompts and declarations)
|
|
71705
|
+
See the difference: lotics app check
|
|
71706
|
+
Take the app's copy: lotics app pull ${appId} --force (DISCARDS the files above)`
|
|
71707
|
+
);
|
|
71139
71708
|
}
|
|
71140
71709
|
function warnIfDynamicAliases(called) {
|
|
71141
71710
|
if (called.dynamic.length === 0) return;
|
|
@@ -71179,7 +71748,7 @@ function warnIfUnboundAliases(app, called) {
|
|
|
71179
71748
|
console.error(lines.join("\n"));
|
|
71180
71749
|
}
|
|
71181
71750
|
async function appDev(client, args) {
|
|
71182
|
-
const projectDir =
|
|
71751
|
+
const projectDir = path7.resolve(args.projectDir ?? process.cwd());
|
|
71183
71752
|
const meta3 = readAppMeta(projectDir);
|
|
71184
71753
|
warnAboutDevLink(projectDir, "dev");
|
|
71185
71754
|
writeAppDts(projectDir, { workflows: meta3.workflows, queries: meta3.queries, agents: meta3.agents });
|
|
@@ -71350,11 +71919,11 @@ var AGENT_RUN_POLL = {
|
|
|
71350
71919
|
existenceTimeoutMs: 5e3
|
|
71351
71920
|
};
|
|
71352
71921
|
var sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
71353
|
-
async function fetchSettledAgentRun(client, appId,
|
|
71922
|
+
async function fetchSettledAgentRun(client, appId, sessionId2, runId, timing) {
|
|
71354
71923
|
const settleDeadline = Date.now() + timing.settleTimeoutMs;
|
|
71355
71924
|
const existenceDeadline = Date.now() + timing.existenceTimeoutMs;
|
|
71356
71925
|
for (; ; ) {
|
|
71357
|
-
const { runs } = await client.listAgentRuns(appId,
|
|
71926
|
+
const { runs } = await client.listAgentRuns(appId, sessionId2);
|
|
71358
71927
|
const target = runId ? runs.find((r) => r.id === runId) : runs[runs.length - 1];
|
|
71359
71928
|
if (target) {
|
|
71360
71929
|
if (target.status !== "running" || Date.now() >= settleDeadline) return target;
|
|
@@ -71365,20 +71934,20 @@ async function fetchSettledAgentRun(client, appId, sessionId, runId, timing) {
|
|
|
71365
71934
|
}
|
|
71366
71935
|
}
|
|
71367
71936
|
async function appAgentRun(client, args, timing = AGENT_RUN_POLL) {
|
|
71368
|
-
const
|
|
71937
|
+
const sessionId2 = args.sessionId ?? `cli-${randomUUID()}`;
|
|
71369
71938
|
const continuing = args.sessionId !== void 0;
|
|
71370
71939
|
const res = await client.appAgentRunStream(args.app_id, args.alias, {
|
|
71371
|
-
session_id:
|
|
71940
|
+
session_id: sessionId2,
|
|
71372
71941
|
input: args.input
|
|
71373
71942
|
});
|
|
71374
71943
|
const runId = res.headers.get("x-app-agent-run-id") ?? void 0;
|
|
71375
71944
|
if (res.body) {
|
|
71376
71945
|
await streamAgentTextDeltas(res.body, (piece) => process.stderr.write(piece));
|
|
71377
71946
|
}
|
|
71378
|
-
const run = await fetchSettledAgentRun(client, args.app_id,
|
|
71947
|
+
const run = await fetchSettledAgentRun(client, args.app_id, sessionId2, runId, timing);
|
|
71379
71948
|
if (!run) {
|
|
71380
71949
|
console.error(`
|
|
71381
|
-
Could not find the settled run for session ${
|
|
71950
|
+
Could not find the settled run for session ${sessionId2} on ${args.app_id}.`);
|
|
71382
71951
|
console.error("The run may still be in progress \u2014 re-check with `lotics run` against the app's agent-runs.");
|
|
71383
71952
|
process.exit(1);
|
|
71384
71953
|
}
|
|
@@ -71394,13 +71963,14 @@ Could not find the settled run for session ${sessionId} on ${args.app_id}.`);
|
|
|
71394
71963
|
Agent "${args.alias}" run ${run.id} \u2192 ${run.status}${run.error_message ? `: ${run.error_message}` : ""}`
|
|
71395
71964
|
);
|
|
71396
71965
|
console.error(
|
|
71397
|
-
continuing ? `Session: ${
|
|
71966
|
+
continuing ? `Session: ${sessionId2}` : `Session: ${sessionId2} (fresh \u2014 pass --session ${sessionId2} to continue this thread)`
|
|
71398
71967
|
);
|
|
71399
71968
|
if (run.status !== "completed") process.exit(1);
|
|
71400
71969
|
}
|
|
71401
71970
|
async function appAgentSet(client, args) {
|
|
71402
|
-
const projectDir = process.cwd();
|
|
71971
|
+
const projectDir = args.projectDir ?? process.cwd();
|
|
71403
71972
|
const meta3 = readAppMeta(projectDir);
|
|
71973
|
+
noteScope({ alias: args.alias });
|
|
71404
71974
|
const app = await client.getApp(meta3.app_id);
|
|
71405
71975
|
const live = app.agents?.[args.alias];
|
|
71406
71976
|
if (!live) {
|
|
@@ -71409,31 +71979,51 @@ async function appAgentSet(client, args) {
|
|
|
71409
71979
|
);
|
|
71410
71980
|
process.exit(1);
|
|
71411
71981
|
}
|
|
71982
|
+
const declared = meta3.agents?.[args.alias];
|
|
71983
|
+
const hasDeclaration = declared?.inputs !== void 0 || declared?.outputs !== void 0;
|
|
71412
71984
|
const file2 = agentFilePath2(projectDir, args.alias);
|
|
71413
|
-
if (!
|
|
71985
|
+
if (!fs6.existsSync(file2) && !hasDeclaration) {
|
|
71414
71986
|
console.error(
|
|
71415
|
-
`No instructions at ${
|
|
71987
|
+
`No instructions at ${path7.relative(projectDir, file2)}, and nothing declared in package.json#lotics.agents.${args.alias} to push instead.
|
|
71988
|
+
Run 'lotics app pull ${meta3.app_id}' to write ${AGENTS_DIR}/${args.alias}.md, then edit it.`
|
|
71416
71989
|
);
|
|
71417
71990
|
process.exit(1);
|
|
71418
71991
|
}
|
|
71419
|
-
const instructions = stripAgentHeader(
|
|
71992
|
+
const instructions = fs6.existsSync(file2) ? stripAgentHeader(fs6.readFileSync(file2, "utf-8")) : void 0;
|
|
71420
71993
|
if (instructions === "") {
|
|
71421
71994
|
console.error(
|
|
71422
|
-
`${
|
|
71995
|
+
`${path7.relative(projectDir, file2)} is empty after stripping the header \u2014 refusing to push an empty prompt.`
|
|
71423
71996
|
);
|
|
71424
71997
|
process.exit(1);
|
|
71425
71998
|
}
|
|
71426
|
-
const
|
|
71999
|
+
const proseBaseline = readSynced(projectDir).agents[args.alias]?.live;
|
|
72000
|
+
const res = await client.setAppAgent(meta3.app_id, args.alias, {
|
|
72001
|
+
// Prose and its precondition ride together. Gating a declaration-only push on
|
|
72002
|
+
// the prose baseline would refuse it because someone edited a PROMPT — a
|
|
72003
|
+
// precondition must guard what this call actually writes.
|
|
72004
|
+
...instructions !== void 0 ? { instructions, ...proseBaseline ? { expected_instructions_sha: proseBaseline } : {} } : {},
|
|
72005
|
+
...declared?.inputs !== void 0 ? { inputs: declared.inputs } : {},
|
|
72006
|
+
...declared?.outputs !== void 0 ? { outputs: declared.outputs } : {}
|
|
72007
|
+
});
|
|
71427
72008
|
if (res.error) {
|
|
71428
72009
|
console.error(`Failed to set agent "${args.alias}": ${res.error}`);
|
|
71429
72010
|
process.exit(1);
|
|
71430
72011
|
}
|
|
72012
|
+
if (instructions !== void 0) {
|
|
72013
|
+
const proseSha = contentSha(instructions);
|
|
72014
|
+
writeSynced(projectDir, "agents", args.alias, { content: proseSha, live: proseSha });
|
|
72015
|
+
}
|
|
72016
|
+
const pushed = [
|
|
72017
|
+
...instructions !== void 0 ? [`${instructions.length} chars of instructions`] : [],
|
|
72018
|
+
...declared?.inputs !== void 0 ? ["inputs"] : [],
|
|
72019
|
+
...declared?.outputs !== void 0 ? ["outputs"] : []
|
|
72020
|
+
];
|
|
71431
72021
|
console.error(
|
|
71432
|
-
`Set agent "${args.alias}" (${
|
|
72022
|
+
`Set agent "${args.alias}" (${pushed.join(", ")}` + (live.model_tier ? `, ${live.model_tier}` : "") + `). Everything else left as it is.`
|
|
71433
72023
|
);
|
|
71434
72024
|
}
|
|
71435
72025
|
async function appWorkflowSet(client, args) {
|
|
71436
|
-
const projectDir = process.cwd();
|
|
72026
|
+
const projectDir = args.projectDir ?? process.cwd();
|
|
71437
72027
|
const meta3 = readAppMeta(projectDir);
|
|
71438
72028
|
const declaration = meta3.workflows?.[args.alias];
|
|
71439
72029
|
if (!declaration) {
|
|
@@ -71443,15 +72033,18 @@ async function appWorkflowSet(client, args) {
|
|
|
71443
72033
|
process.exit(1);
|
|
71444
72034
|
}
|
|
71445
72035
|
const file2 = workflowFilePath(projectDir, args.alias);
|
|
71446
|
-
if (!
|
|
72036
|
+
if (!fs6.existsSync(file2)) {
|
|
71447
72037
|
console.error(
|
|
71448
|
-
`No workflow body at ${
|
|
72038
|
+
`No workflow body at ${path7.relative(projectDir, file2)}.
|
|
72039
|
+
If "${args.alias}" is already bound: lotics app pull ${meta3.app_id} (writes the body)
|
|
72040
|
+
If it is a NEW alias you declared: create that file yourself, then re-run this.`
|
|
71449
72041
|
);
|
|
71450
72042
|
process.exit(1);
|
|
71451
72043
|
}
|
|
71452
|
-
|
|
72044
|
+
noteScope({ alias: args.alias });
|
|
72045
|
+
const source = stripWorkflowHeader(fs6.readFileSync(file2, "utf-8"));
|
|
71453
72046
|
if (source.trim() === "") {
|
|
71454
|
-
console.error(`Workflow body ${
|
|
72047
|
+
console.error(`Workflow body ${path7.relative(projectDir, file2)} is empty after stripping the header.`);
|
|
71455
72048
|
process.exit(1);
|
|
71456
72049
|
}
|
|
71457
72050
|
const res = await client.setAppWorkflow(meta3.app_id, args.alias, {
|
|
@@ -71465,12 +72058,21 @@ async function appWorkflowSet(client, args) {
|
|
|
71465
72058
|
// `Workflow invoked by app "X" via useWorkflow("y")`. Absent leaves
|
|
71466
72059
|
// whatever is on the row, so this can never blank a description set
|
|
71467
72060
|
// elsewhere.
|
|
71468
|
-
description: declaration.description
|
|
72061
|
+
description: declaration.description,
|
|
72062
|
+
// The body this edit was built on. Turns the push into a conditional write:
|
|
72063
|
+
// if the live body moved since this project last saw it, the server refuses
|
|
72064
|
+
// rather than letting this copy overwrite a change nobody here has seen.
|
|
72065
|
+
expected_body_sha: readSynced(projectDir).workflows[args.alias]?.live
|
|
71469
72066
|
});
|
|
71470
72067
|
if (res.error) {
|
|
71471
72068
|
console.error(`Failed to set workflow "${args.alias}": ${res.error}`);
|
|
71472
72069
|
process.exit(1);
|
|
71473
72070
|
}
|
|
72071
|
+
const pushedBodySha = res.result !== null && typeof res.result === "object" ? res.result.body_sha : void 0;
|
|
72072
|
+
writeSynced(projectDir, "workflows", args.alias, {
|
|
72073
|
+
content: contentSha(source),
|
|
72074
|
+
...typeof pushedBodySha === "string" ? { live: pushedBodySha } : {}
|
|
72075
|
+
});
|
|
71474
72076
|
const result = res.result ?? {};
|
|
71475
72077
|
const workflowId = typeof result.workflow_id === "string" ? result.workflow_id : "(unknown)";
|
|
71476
72078
|
console.error(`Set workflow "${args.alias}" \u2192 ${workflowId}`);
|
|
@@ -71490,10 +72092,11 @@ async function appWorkflowSet(client, args) {
|
|
|
71490
72092
|
}
|
|
71491
72093
|
}
|
|
71492
72094
|
async function appQuerySet(client, args) {
|
|
71493
|
-
const projectDir = process.cwd();
|
|
72095
|
+
const projectDir = args.projectDir ?? process.cwd();
|
|
71494
72096
|
const meta3 = readAppMeta(projectDir);
|
|
71495
72097
|
const declared = meta3.queries ?? {};
|
|
71496
72098
|
const aliases = "all" in args ? Object.keys(declared).sort() : [args.alias];
|
|
72099
|
+
if (!("all" in args)) noteScope({ alias: args.alias });
|
|
71497
72100
|
if (aliases.length === 0) {
|
|
71498
72101
|
console.error("No queries in package.json#lotics.queries \u2014 nothing to push.");
|
|
71499
72102
|
return;
|
|
@@ -71507,19 +72110,26 @@ async function appQuerySet(client, args) {
|
|
|
71507
72110
|
);
|
|
71508
72111
|
process.exit(1);
|
|
71509
72112
|
}
|
|
71510
|
-
const res = await client.setAppQuery(
|
|
72113
|
+
const res = await client.setAppQuery(
|
|
72114
|
+
meta3.app_id,
|
|
72115
|
+
alias,
|
|
72116
|
+
declaration,
|
|
72117
|
+
readSynced(projectDir).queries[alias]?.live
|
|
72118
|
+
);
|
|
71511
72119
|
if (res.error) {
|
|
71512
72120
|
if (pushed.length > 0) console.error(`Pushed before the failure: ${pushed.join(", ")}.`);
|
|
71513
72121
|
console.error(`Failed to set query "${alias}": ${res.error}`);
|
|
71514
72122
|
process.exit(1);
|
|
71515
72123
|
}
|
|
72124
|
+
const querySha = declarationSha(declaration);
|
|
72125
|
+
writeSynced(projectDir, "queries", alias, { content: querySha, live: querySha });
|
|
71516
72126
|
pushed.push(alias);
|
|
71517
72127
|
}
|
|
71518
72128
|
console.error(
|
|
71519
72129
|
`Set ${pushed.length} quer${pushed.length === 1 ? "y" : "ies"} on ${meta3.app_id}: ${pushed.join(", ")}.`
|
|
71520
72130
|
);
|
|
71521
72131
|
}
|
|
71522
|
-
async function appWorkflowPull(client) {
|
|
72132
|
+
async function appWorkflowPull(client, args = {}) {
|
|
71523
72133
|
const projectDir = process.cwd();
|
|
71524
72134
|
const meta3 = readAppMeta(projectDir);
|
|
71525
72135
|
const app = await client.getApp(meta3.app_id);
|
|
@@ -71528,10 +72138,15 @@ async function appWorkflowPull(client) {
|
|
|
71528
72138
|
console.error(`App ${meta3.app_id} has no bound workflows.`);
|
|
71529
72139
|
return;
|
|
71530
72140
|
}
|
|
71531
|
-
const
|
|
72141
|
+
const kept = [];
|
|
72142
|
+
const written = await writeWorkflowFiles(client, projectDir, meta3.app_id, app.name, workflows, {
|
|
72143
|
+
overwrite: args.force === true,
|
|
72144
|
+
kept
|
|
72145
|
+
});
|
|
71532
72146
|
console.error(
|
|
71533
72147
|
`Wrote ${written.length} workflow ${written.length === 1 ? "body" : "bodies"} to ${WORKFLOWS_DIR}/` + (written.length > 0 ? ` (${written.join(", ")})` : "")
|
|
71534
72148
|
);
|
|
72149
|
+
reportKeptLocalFiles(kept, meta3.app_id);
|
|
71535
72150
|
ensureAppTsconfig(projectDir);
|
|
71536
72151
|
}
|
|
71537
72152
|
async function appWorkflowCheck(args) {
|
|
@@ -71558,15 +72173,15 @@ async function appWorkflowCheck(args) {
|
|
|
71558
72173
|
for (const alias of aliases) {
|
|
71559
72174
|
const bodyPath = workflowFilePath(projectDir, alias);
|
|
71560
72175
|
const globalsPath = workflowGlobalsPath(projectDir, alias);
|
|
71561
|
-
if (!
|
|
72176
|
+
if (!fs6.existsSync(bodyPath)) {
|
|
71562
72177
|
console.error(
|
|
71563
|
-
`\u26A0 Skipped "${alias}" \u2014 no body at ${
|
|
72178
|
+
`\u26A0 Skipped "${alias}" \u2014 no body at ${path7.relative(projectDir, bodyPath)}. Run 'lotics app workflow pull' to write it.`
|
|
71564
72179
|
);
|
|
71565
72180
|
continue;
|
|
71566
72181
|
}
|
|
71567
|
-
if (!
|
|
72182
|
+
if (!fs6.existsSync(globalsPath)) {
|
|
71568
72183
|
console.error(
|
|
71569
|
-
`Cannot check "${alias}" \u2014 missing types at ${
|
|
72184
|
+
`Cannot check "${alias}" \u2014 missing types at ${path7.relative(projectDir, globalsPath)}. Run 'lotics app workflow pull' (or 'lotics app codegen') to fetch them.`
|
|
71570
72185
|
);
|
|
71571
72186
|
process.exit(1);
|
|
71572
72187
|
}
|
|
@@ -71585,7 +72200,7 @@ async function appWorkflowCheck(args) {
|
|
|
71585
72200
|
function printWorkflowCheckResults(projectDir, results) {
|
|
71586
72201
|
let totalErrors = 0;
|
|
71587
72202
|
for (const r of results) {
|
|
71588
|
-
const rel =
|
|
72203
|
+
const rel = path7.relative(projectDir, r.bodyPath);
|
|
71589
72204
|
if (r.issues.length === 0) {
|
|
71590
72205
|
console.error(`\u2713 ${r.alias} (${rel}) \u2014 clean`);
|
|
71591
72206
|
continue;
|
|
@@ -71614,6 +72229,7 @@ ${totalErrors} error${totalErrors === 1 ? "" : "s"} across ${results.length - pa
|
|
|
71614
72229
|
function parseArgs(argv) {
|
|
71615
72230
|
const flags = {
|
|
71616
72231
|
json: false,
|
|
72232
|
+
force: false,
|
|
71617
72233
|
timeout: void 0,
|
|
71618
72234
|
output: void 0,
|
|
71619
72235
|
as: void 0,
|
|
@@ -71646,6 +72262,9 @@ function parseArgs(argv) {
|
|
|
71646
72262
|
case "--json":
|
|
71647
72263
|
flags.json = true;
|
|
71648
72264
|
break;
|
|
72265
|
+
case "--force":
|
|
72266
|
+
flags.force = true;
|
|
72267
|
+
break;
|
|
71649
72268
|
case "--timeout":
|
|
71650
72269
|
flags.timeout = parseInt(argv[++i2], 10);
|
|
71651
72270
|
break;
|
|
@@ -71789,11 +72408,11 @@ async function validateOrgWorkspacePin(client, orgId, profile) {
|
|
|
71789
72408
|
}
|
|
71790
72409
|
|
|
71791
72410
|
// src/xlsx.ts
|
|
71792
|
-
import
|
|
72411
|
+
import fs8 from "node:fs";
|
|
71793
72412
|
|
|
71794
72413
|
// src/file_command_io.ts
|
|
71795
|
-
import
|
|
71796
|
-
import
|
|
72414
|
+
import fs7 from "node:fs";
|
|
72415
|
+
import path8 from "node:path";
|
|
71797
72416
|
var CliError = class extends Error {
|
|
71798
72417
|
constructor(message2) {
|
|
71799
72418
|
super(message2);
|
|
@@ -71814,14 +72433,14 @@ function rejectExtraArgs(rest2, arity, usage) {
|
|
|
71814
72433
|
fail2(`${usage} takes ${arity} argument${arity === 1 ? "" : "s"} after <file>, got ${rest2.length}. Unused: ${rest2.slice(arity).join(" ")}`);
|
|
71815
72434
|
}
|
|
71816
72435
|
function writeFileAtomic(filePath, bytes) {
|
|
71817
|
-
const dir =
|
|
71818
|
-
const tmp =
|
|
71819
|
-
|
|
72436
|
+
const dir = path8.dirname(path8.resolve(filePath));
|
|
72437
|
+
const tmp = path8.join(dir, `.${path8.basename(filePath)}.${process.pid}.${Date.now()}.tmp`);
|
|
72438
|
+
fs7.writeFileSync(tmp, bytes);
|
|
71820
72439
|
try {
|
|
71821
|
-
|
|
72440
|
+
fs7.renameSync(tmp, filePath);
|
|
71822
72441
|
} catch (error52) {
|
|
71823
72442
|
try {
|
|
71824
|
-
|
|
72443
|
+
fs7.unlinkSync(tmp);
|
|
71825
72444
|
} catch {
|
|
71826
72445
|
}
|
|
71827
72446
|
throw error52;
|
|
@@ -74618,16 +75237,16 @@ var MatcherView = class {
|
|
|
74618
75237
|
* @returns {string|undefined}
|
|
74619
75238
|
*/
|
|
74620
75239
|
getCurrentTag() {
|
|
74621
|
-
const
|
|
74622
|
-
return
|
|
75240
|
+
const path10 = this._matcher.path;
|
|
75241
|
+
return path10.length > 0 ? path10[path10.length - 1].tag : void 0;
|
|
74623
75242
|
}
|
|
74624
75243
|
/**
|
|
74625
75244
|
* Get current namespace.
|
|
74626
75245
|
* @returns {string|undefined}
|
|
74627
75246
|
*/
|
|
74628
75247
|
getCurrentNamespace() {
|
|
74629
|
-
const
|
|
74630
|
-
return
|
|
75248
|
+
const path10 = this._matcher.path;
|
|
75249
|
+
return path10.length > 0 ? path10[path10.length - 1].namespace : void 0;
|
|
74631
75250
|
}
|
|
74632
75251
|
/**
|
|
74633
75252
|
* Get current node's attribute value.
|
|
@@ -74635,9 +75254,9 @@ var MatcherView = class {
|
|
|
74635
75254
|
* @returns {*}
|
|
74636
75255
|
*/
|
|
74637
75256
|
getAttrValue(attrName) {
|
|
74638
|
-
const
|
|
74639
|
-
if (
|
|
74640
|
-
return
|
|
75257
|
+
const path10 = this._matcher.path;
|
|
75258
|
+
if (path10.length === 0) return void 0;
|
|
75259
|
+
return path10[path10.length - 1].values?.[attrName];
|
|
74641
75260
|
}
|
|
74642
75261
|
/**
|
|
74643
75262
|
* Check if current node has an attribute.
|
|
@@ -74645,9 +75264,9 @@ var MatcherView = class {
|
|
|
74645
75264
|
* @returns {boolean}
|
|
74646
75265
|
*/
|
|
74647
75266
|
hasAttr(attrName) {
|
|
74648
|
-
const
|
|
74649
|
-
if (
|
|
74650
|
-
const current =
|
|
75267
|
+
const path10 = this._matcher.path;
|
|
75268
|
+
if (path10.length === 0) return false;
|
|
75269
|
+
const current = path10[path10.length - 1];
|
|
74651
75270
|
return current.values !== void 0 && attrName in current.values;
|
|
74652
75271
|
}
|
|
74653
75272
|
/**
|
|
@@ -74673,18 +75292,18 @@ var MatcherView = class {
|
|
|
74673
75292
|
* @returns {number}
|
|
74674
75293
|
*/
|
|
74675
75294
|
getPosition() {
|
|
74676
|
-
const
|
|
74677
|
-
if (
|
|
74678
|
-
return
|
|
75295
|
+
const path10 = this._matcher.path;
|
|
75296
|
+
if (path10.length === 0) return -1;
|
|
75297
|
+
return path10[path10.length - 1].position ?? 0;
|
|
74679
75298
|
}
|
|
74680
75299
|
/**
|
|
74681
75300
|
* Get current node's repeat counter (occurrence count of this tag name).
|
|
74682
75301
|
* @returns {number}
|
|
74683
75302
|
*/
|
|
74684
75303
|
getCounter() {
|
|
74685
|
-
const
|
|
74686
|
-
if (
|
|
74687
|
-
return
|
|
75304
|
+
const path10 = this._matcher.path;
|
|
75305
|
+
if (path10.length === 0) return -1;
|
|
75306
|
+
return path10[path10.length - 1].counter ?? 0;
|
|
74688
75307
|
}
|
|
74689
75308
|
/**
|
|
74690
75309
|
* Get current node's sibling index (alias for getPosition).
|
|
@@ -80342,10 +80961,10 @@ function resolveRelativePath(basePath, relativePath) {
|
|
|
80342
80961
|
}
|
|
80343
80962
|
return resolved.join("/");
|
|
80344
80963
|
}
|
|
80345
|
-
function findZipEntry(zipEntries,
|
|
80346
|
-
if (zipEntries[
|
|
80347
|
-
if (zipEntries[`xl/${
|
|
80348
|
-
const noSlash =
|
|
80964
|
+
function findZipEntry(zipEntries, path10) {
|
|
80965
|
+
if (zipEntries[path10]) return zipEntries[path10];
|
|
80966
|
+
if (zipEntries[`xl/${path10}`]) return zipEntries[`xl/${path10}`];
|
|
80967
|
+
const noSlash = path10.replace(/^\//, "");
|
|
80349
80968
|
if (zipEntries[noSlash]) return zipEntries[noSlash];
|
|
80350
80969
|
return void 0;
|
|
80351
80970
|
}
|
|
@@ -81974,9 +82593,9 @@ function parseExcelFromZip(zip, options) {
|
|
|
81974
82593
|
}
|
|
81975
82594
|
return result;
|
|
81976
82595
|
}
|
|
81977
|
-
function findEntry(zip,
|
|
81978
|
-
if (zip[
|
|
81979
|
-
const lower2 =
|
|
82596
|
+
function findEntry(zip, path10) {
|
|
82597
|
+
if (zip[path10]) return zip[path10];
|
|
82598
|
+
const lower2 = path10.toLowerCase();
|
|
81980
82599
|
for (const key of Object.keys(zip)) {
|
|
81981
82600
|
if (key.toLowerCase() === lower2) return zip[key];
|
|
81982
82601
|
}
|
|
@@ -82613,12 +83232,12 @@ function extractPivotRoundTripInfo(originalZip) {
|
|
|
82613
83232
|
if (paths.length > 0) pivotTablesBySheetIndex.set(i2, paths);
|
|
82614
83233
|
}
|
|
82615
83234
|
const pivotXmlPaths = [];
|
|
82616
|
-
for (const
|
|
82617
|
-
if (
|
|
82618
|
-
pivotXmlPaths.push(
|
|
83235
|
+
for (const path10 of Object.keys(originalZip)) {
|
|
83236
|
+
if (path10.startsWith("xl/pivotTables/") && path10.endsWith(".xml")) {
|
|
83237
|
+
pivotXmlPaths.push(path10);
|
|
82619
83238
|
}
|
|
82620
|
-
if (
|
|
82621
|
-
pivotXmlPaths.push(
|
|
83239
|
+
if (path10.startsWith("xl/pivotCache/") && path10.endsWith(".xml")) {
|
|
83240
|
+
pivotXmlPaths.push(path10);
|
|
82622
83241
|
}
|
|
82623
83242
|
}
|
|
82624
83243
|
return { cachesByWorkbook, pivotTablesBySheetIndex, pivotXmlPaths };
|
|
@@ -82626,15 +83245,15 @@ function extractPivotRoundTripInfo(originalZip) {
|
|
|
82626
83245
|
function decode3(bytes) {
|
|
82627
83246
|
return new TextDecoder().decode(bytes);
|
|
82628
83247
|
}
|
|
82629
|
-
function pivotContentTypeFor(
|
|
82630
|
-
if (
|
|
82631
|
-
return `<Override PartName="/${
|
|
83248
|
+
function pivotContentTypeFor(path10) {
|
|
83249
|
+
if (path10.startsWith("xl/pivotTables/") && path10.endsWith(".xml")) {
|
|
83250
|
+
return `<Override PartName="/${path10}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml"/>`;
|
|
82632
83251
|
}
|
|
82633
|
-
if (
|
|
82634
|
-
return `<Override PartName="/${
|
|
83252
|
+
if (path10.includes("/pivotCacheDefinition") && path10.endsWith(".xml")) {
|
|
83253
|
+
return `<Override PartName="/${path10}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml"/>`;
|
|
82635
83254
|
}
|
|
82636
|
-
if (
|
|
82637
|
-
return `<Override PartName="/${
|
|
83255
|
+
if (path10.includes("/pivotCacheRecords") && path10.endsWith(".xml")) {
|
|
83256
|
+
return `<Override PartName="/${path10}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml"/>`;
|
|
82638
83257
|
}
|
|
82639
83258
|
return void 0;
|
|
82640
83259
|
}
|
|
@@ -82707,16 +83326,16 @@ function exportWorkbook(workbook, originalZip) {
|
|
|
82707
83326
|
regeneratedPaths.add(`xl/worksheets/sheet${i2 + 1}.xml`);
|
|
82708
83327
|
regeneratedPaths.add(`xl/worksheets/_rels/sheet${i2 + 1}.xml.rels`);
|
|
82709
83328
|
}
|
|
82710
|
-
for (const
|
|
82711
|
-
if (
|
|
82712
|
-
regeneratedPaths.add(
|
|
83329
|
+
for (const path10 of Object.keys(originalZip)) {
|
|
83330
|
+
if (path10.startsWith("xl/drawings/") || path10.startsWith("xl/charts/") || path10.startsWith("xl/tables/") || path10.startsWith("xl/media/")) {
|
|
83331
|
+
regeneratedPaths.add(path10);
|
|
82713
83332
|
}
|
|
82714
83333
|
}
|
|
82715
|
-
for (const [
|
|
82716
|
-
if (regeneratedPaths.has(
|
|
82717
|
-
const ext =
|
|
83334
|
+
for (const [path10, data2] of Object.entries(originalZip)) {
|
|
83335
|
+
if (regeneratedPaths.has(path10)) continue;
|
|
83336
|
+
const ext = path10.slice(path10.lastIndexOf(".") + 1).toLowerCase();
|
|
82718
83337
|
if (!DECLARED_EXTENSIONS.includes(ext)) continue;
|
|
82719
|
-
entries2[
|
|
83338
|
+
entries2[path10] = data2;
|
|
82720
83339
|
}
|
|
82721
83340
|
}
|
|
82722
83341
|
const sharedStrings = buildSharedStrings(workbook);
|
|
@@ -82732,8 +83351,8 @@ function exportWorkbook(workbook, originalZip) {
|
|
|
82732
83351
|
buildWorkbookRels(workbook, pivotInfo)
|
|
82733
83352
|
);
|
|
82734
83353
|
const extraContentTypes = [];
|
|
82735
|
-
for (const
|
|
82736
|
-
const ct = pivotContentTypeFor(
|
|
83354
|
+
for (const path10 of pivotInfo.pivotXmlPaths) {
|
|
83355
|
+
const ct = pivotContentTypeFor(path10);
|
|
82737
83356
|
if (ct) extraContentTypes.push(ct);
|
|
82738
83357
|
}
|
|
82739
83358
|
let globalChartIndex = 1;
|
|
@@ -91756,14 +92375,14 @@ var DependencyGraph = class {
|
|
|
91756
92375
|
stack.push({ cell: p, path: [cell, p] });
|
|
91757
92376
|
}
|
|
91758
92377
|
while (stack.length > 0) {
|
|
91759
|
-
const { cell: current, path:
|
|
91760
|
-
if (current === cell) return
|
|
92378
|
+
const { cell: current, path: path10 } = stack.pop();
|
|
92379
|
+
if (current === cell) return path10;
|
|
91761
92380
|
if (visited.has(current)) continue;
|
|
91762
92381
|
visited.add(current);
|
|
91763
92382
|
const nextPrec = this.precedents.get(current);
|
|
91764
92383
|
if (!nextPrec) continue;
|
|
91765
92384
|
for (const p of nextPrec) {
|
|
91766
|
-
stack.push({ cell: p, path: [...
|
|
92385
|
+
stack.push({ cell: p, path: [...path10, p] });
|
|
91767
92386
|
}
|
|
91768
92387
|
}
|
|
91769
92388
|
return null;
|
|
@@ -92975,8 +93594,8 @@ var SIMPLE_SHEET_KEYS = /* @__PURE__ */ new Set(["name", "cells", "merges", "fre
|
|
|
92975
93594
|
var MAX_COLUMN = 16384;
|
|
92976
93595
|
var MAX_ROW = 1048576;
|
|
92977
93596
|
function loadFile(filePath) {
|
|
92978
|
-
if (!
|
|
92979
|
-
const buffer =
|
|
93597
|
+
if (!fs8.existsSync(filePath)) fail2(`File not found: ${filePath}`);
|
|
93598
|
+
const buffer = fs8.readFileSync(filePath);
|
|
92980
93599
|
const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
|
|
92981
93600
|
const originalZip = unzipXlsx(arrayBuffer);
|
|
92982
93601
|
const parsed = parseExcelBuffer(arrayBuffer);
|
|
@@ -93026,7 +93645,7 @@ function lazyEngine(workbook) {
|
|
|
93026
93645
|
};
|
|
93027
93646
|
}
|
|
93028
93647
|
function readToJson(filePath, filter3, opts) {
|
|
93029
|
-
const buffer =
|
|
93648
|
+
const buffer = fs8.readFileSync(filePath);
|
|
93030
93649
|
const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
|
|
93031
93650
|
const parsed = parseExcelBuffer(arrayBuffer);
|
|
93032
93651
|
let sheets = parsed.sheets;
|
|
@@ -93539,7 +94158,7 @@ async function runXlsxCommand(subcommand, toolArgs, restArgs) {
|
|
|
93539
94158
|
}
|
|
93540
94159
|
|
|
93541
94160
|
// src/docx.ts
|
|
93542
|
-
import
|
|
94161
|
+
import fs9 from "node:fs";
|
|
93543
94162
|
|
|
93544
94163
|
// ../ooxml/src/doc/errors.ts
|
|
93545
94164
|
var DocParseError = class extends Error {
|
|
@@ -98155,8 +98774,8 @@ async function docModelToDocx(model) {
|
|
|
98155
98774
|
// ../ooxml/src/opc.ts
|
|
98156
98775
|
var import_jszip = __toESM(require_lib4(), 1);
|
|
98157
98776
|
function dropFolderEntries(zip) {
|
|
98158
|
-
for (const [
|
|
98159
|
-
if (entry.dir) delete zip.files[
|
|
98777
|
+
for (const [path10, entry] of Object.entries(zip.files)) {
|
|
98778
|
+
if (entry.dir) delete zip.files[path10];
|
|
98160
98779
|
}
|
|
98161
98780
|
}
|
|
98162
98781
|
|
|
@@ -98388,11 +99007,11 @@ async function parseDocx(buffer) {
|
|
|
98388
99007
|
const zip = await import_jszip3.default.loadAsync(buffer);
|
|
98389
99008
|
const parts = /* @__PURE__ */ new Map();
|
|
98390
99009
|
const filePromises = [];
|
|
98391
|
-
zip.forEach((
|
|
99010
|
+
zip.forEach((path10, file2) => {
|
|
98392
99011
|
if (file2.dir) return;
|
|
98393
99012
|
filePromises.push(
|
|
98394
99013
|
file2.async("uint8array").then((bytes) => {
|
|
98395
|
-
parts.set(
|
|
99014
|
+
parts.set(path10, bytes);
|
|
98396
99015
|
})
|
|
98397
99016
|
);
|
|
98398
99017
|
});
|
|
@@ -98624,9 +99243,9 @@ var DEFAULT_PARTS = [
|
|
|
98624
99243
|
["word/theme/theme1.xml", () => encoder.encode(THEME_XML)]
|
|
98625
99244
|
];
|
|
98626
99245
|
function fillDefaultParts(parts) {
|
|
98627
|
-
for (const [
|
|
98628
|
-
if (!parts.has(
|
|
98629
|
-
parts.set(
|
|
99246
|
+
for (const [path10, makeBytes] of DEFAULT_PARTS) {
|
|
99247
|
+
if (!parts.has(path10)) {
|
|
99248
|
+
parts.set(path10, makeBytes());
|
|
98630
99249
|
}
|
|
98631
99250
|
}
|
|
98632
99251
|
return parts;
|
|
@@ -98636,9 +99255,9 @@ function fillDefaultParts(parts) {
|
|
|
98636
99255
|
async function serializeDocx(doc) {
|
|
98637
99256
|
const zip = new import_jszip4.default();
|
|
98638
99257
|
const parts = fillDefaultParts(new Map(doc.parts));
|
|
98639
|
-
for (const [
|
|
98640
|
-
if (
|
|
98641
|
-
zip.file(
|
|
99258
|
+
for (const [path10, bytes] of parts) {
|
|
99259
|
+
if (path10 === "word/document.xml") continue;
|
|
99260
|
+
zip.file(path10, bytes);
|
|
98642
99261
|
}
|
|
98643
99262
|
const documentXml = serializeDocumentXml(doc);
|
|
98644
99263
|
zip.file("word/document.xml", documentXml);
|
|
@@ -98725,8 +99344,8 @@ var DEFAULT_DOC_ATTRS = {
|
|
|
98725
99344
|
"@_xmlns:w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
|
|
98726
99345
|
};
|
|
98727
99346
|
async function loadFile2(filePath) {
|
|
98728
|
-
if (!
|
|
98729
|
-
const bytes =
|
|
99347
|
+
if (!fs9.existsSync(filePath)) fail2(`File not found: ${filePath}`);
|
|
99348
|
+
const bytes = fs9.readFileSync(filePath);
|
|
98730
99349
|
if (isLegacyDoc(bytes)) {
|
|
98731
99350
|
const upgraded = await saveDocxToBuffer(await loadDocxFromBuffer(bytes));
|
|
98732
99351
|
return parseDocx(new Uint8Array(upgraded));
|
|
@@ -99211,10 +99830,10 @@ async function runDocxCommand(subcommand, toolArgs, restArgs) {
|
|
|
99211
99830
|
}
|
|
99212
99831
|
|
|
99213
99832
|
// src/knowledge.ts
|
|
99214
|
-
import
|
|
99833
|
+
import fs10 from "node:fs";
|
|
99215
99834
|
function readBodyFile(filePath) {
|
|
99216
|
-
if (!
|
|
99217
|
-
return
|
|
99835
|
+
if (!fs10.existsSync(filePath)) fail2(`File not found: ${filePath}`);
|
|
99836
|
+
return fs10.readFileSync(filePath, "utf-8");
|
|
99218
99837
|
}
|
|
99219
99838
|
function resolveBody(flags, required2) {
|
|
99220
99839
|
if (flags.from !== void 0 && flags.content !== void 0) {
|
|
@@ -99551,6 +100170,7 @@ async function runPreviewCommand(filePath, flags) {
|
|
|
99551
100170
|
}
|
|
99552
100171
|
|
|
99553
100172
|
// src/cli.ts
|
|
100173
|
+
var startedAt = Date.now();
|
|
99554
100174
|
dns.setDefaultResultOrder("ipv4first");
|
|
99555
100175
|
net2.setDefaultAutoSelectFamilyAttemptTimeout(2e3);
|
|
99556
100176
|
function printHelp() {
|
|
@@ -99614,7 +100234,8 @@ COMMANDS
|
|
|
99614
100234
|
lotics app codegen [path] Regenerate .lotics/* (types + field/option ids)
|
|
99615
100235
|
from the manifest + workspace schema \u2014 no deploy
|
|
99616
100236
|
lotics app check Run every deploy pre-flight WITHOUT building or
|
|
99617
|
-
shipping: agent schemas vs the live app,
|
|
100237
|
+
shipping: agent schemas vs the live app, workflow
|
|
100238
|
+
declarations and BODIES vs what is live, aliases
|
|
99618
100239
|
the code calls but nothing bound, undeclared
|
|
99619
100240
|
capabilities, query drift. Exits 1 on what a
|
|
99620
100241
|
deploy would refuse, so CI can gate on it
|
|
@@ -99763,8 +100384,8 @@ function prompt(question) {
|
|
|
99763
100384
|
});
|
|
99764
100385
|
});
|
|
99765
100386
|
}
|
|
99766
|
-
async function publicPost(
|
|
99767
|
-
const response = await fetch(`${API_BASE_URL}${
|
|
100387
|
+
async function publicPost(path10, body) {
|
|
100388
|
+
const response = await fetch(`${API_BASE_URL}${path10}`, {
|
|
99768
100389
|
method: "POST",
|
|
99769
100390
|
headers: { "Content-Type": "application/json" },
|
|
99770
100391
|
body: JSON.stringify(body)
|
|
@@ -99861,6 +100482,8 @@ function requireClient(flags, appWorkspaceId) {
|
|
|
99861
100482
|
process.exit(1);
|
|
99862
100483
|
}
|
|
99863
100484
|
const viewAsMemberId = flags.viewAs ?? process.env.LOTICS_VIEW_AS;
|
|
100485
|
+
noteScope({ org_id: ctx.orgId ?? void 0, workspace_id: ctx.workspaceId });
|
|
100486
|
+
flushSpool((events) => postEvents(API_BASE_URL, ctx.apiKey, events));
|
|
99864
100487
|
return {
|
|
99865
100488
|
client: new LoticsClient({ apiKey: ctx.apiKey, workspaceId: ctx.workspaceId, viewAsMemberId }),
|
|
99866
100489
|
ctx
|
|
@@ -99875,6 +100498,7 @@ var SOURCE_LABELS = {
|
|
|
99875
100498
|
global_profile: "global active profile"
|
|
99876
100499
|
};
|
|
99877
100500
|
function announceTarget(ctx, workspaceId) {
|
|
100501
|
+
noteScope({ org_id: ctx.orgId ?? void 0, workspace_id: workspaceId });
|
|
99878
100502
|
const org = ctx.orgName ?? ctx.orgId ?? "(org from key)";
|
|
99879
100503
|
if (ctx.source === "global_profile") {
|
|
99880
100504
|
console.error(
|
|
@@ -99929,13 +100553,13 @@ async function resolveWorkspace(client, ctx) {
|
|
|
99929
100553
|
function resolveUploadPaths(rawPaths) {
|
|
99930
100554
|
const result = [];
|
|
99931
100555
|
for (const p of rawPaths) {
|
|
99932
|
-
const resolved =
|
|
99933
|
-
const stat =
|
|
100556
|
+
const resolved = path9.resolve(p);
|
|
100557
|
+
const stat = fs11.statSync(resolved);
|
|
99934
100558
|
if (stat.isDirectory()) {
|
|
99935
|
-
const entries2 =
|
|
100559
|
+
const entries2 = fs11.readdirSync(resolved, { withFileTypes: true });
|
|
99936
100560
|
for (const entry of entries2) {
|
|
99937
100561
|
if (entry.isFile()) {
|
|
99938
|
-
result.push(
|
|
100562
|
+
result.push(path9.join(resolved, entry.name));
|
|
99939
100563
|
}
|
|
99940
100564
|
}
|
|
99941
100565
|
} else {
|
|
@@ -99945,6 +100569,8 @@ function resolveUploadPaths(rawPaths) {
|
|
|
99945
100569
|
return result;
|
|
99946
100570
|
}
|
|
99947
100571
|
async function main() {
|
|
100572
|
+
setInvocation(commandPath(process.argv.slice(2)), sessionId(), VERSION);
|
|
100573
|
+
installTelemetry(startedAt);
|
|
99948
100574
|
const parsed = parseArgs(process.argv.slice(2));
|
|
99949
100575
|
const { restArgs, flags } = parsed;
|
|
99950
100576
|
let { command, subcommand, toolArgs } = parsed;
|
|
@@ -100058,13 +100684,13 @@ async function main() {
|
|
|
100058
100684
|
if (command === "preview") {
|
|
100059
100685
|
if (subcommand && isStoredFileId(subcommand)) {
|
|
100060
100686
|
const { client: client2 } = requireClient(flags);
|
|
100061
|
-
const tmpDir =
|
|
100687
|
+
const tmpDir = fs11.mkdtempSync(path9.join(os4.tmpdir(), "lotics-preview-"));
|
|
100062
100688
|
try {
|
|
100063
100689
|
const { path: localPath, filename } = await client2.downloadFileById(subcommand, tmpDir);
|
|
100064
100690
|
const output = flags.output ?? defaultPreviewOutputPath(filename, process.cwd());
|
|
100065
100691
|
await runPreviewCommand(localPath, { output });
|
|
100066
100692
|
} finally {
|
|
100067
|
-
|
|
100693
|
+
fs11.rmSync(tmpDir, { recursive: true, force: true });
|
|
100068
100694
|
}
|
|
100069
100695
|
return;
|
|
100070
100696
|
}
|
|
@@ -100337,17 +100963,11 @@ Available workspaces:`);
|
|
|
100337
100963
|
process.exit(1);
|
|
100338
100964
|
}
|
|
100339
100965
|
const targetPath = restArgs[0];
|
|
100340
|
-
await appPull(client, { app_id: appId, targetPath });
|
|
100966
|
+
await appPull(client, { app_id: appId, targetPath, force: flags.force === true });
|
|
100341
100967
|
return;
|
|
100342
100968
|
}
|
|
100343
100969
|
if (subcommand === "deploy") {
|
|
100344
|
-
|
|
100345
|
-
if (!message2) {
|
|
100346
|
-
console.error('Usage: lotics app deploy -m "<what changed + why>"');
|
|
100347
|
-
console.error("A deploy message is required \u2014 it's the version's audit trail (see `lotics app versions`).");
|
|
100348
|
-
process.exit(1);
|
|
100349
|
-
}
|
|
100350
|
-
await appDeploy(client, { message: message2 });
|
|
100970
|
+
await appDeploy(client, { message: (flags.message ?? toolArgs)?.trim() || void 0 });
|
|
100351
100971
|
return;
|
|
100352
100972
|
}
|
|
100353
100973
|
if (subcommand === "subdomain") {
|
|
@@ -100402,7 +101022,7 @@ Available workspaces:`);
|
|
|
100402
101022
|
const ingested = await ingestJsonArgs({
|
|
100403
101023
|
rawArg: restArgs[1],
|
|
100404
101024
|
stdinIsTTY: process.stdin.isTTY ?? false,
|
|
100405
|
-
readFile: (p) =>
|
|
101025
|
+
readFile: (p) => fs11.readFileSync(p, "utf-8"),
|
|
100406
101026
|
readStdin
|
|
100407
101027
|
});
|
|
100408
101028
|
if (ingested.kind === "error") {
|
|
@@ -100428,7 +101048,7 @@ Available workspaces:`);
|
|
|
100428
101048
|
return;
|
|
100429
101049
|
}
|
|
100430
101050
|
if (action === "pull") {
|
|
100431
|
-
await appWorkflowPull(client);
|
|
101051
|
+
await appWorkflowPull(client, { force: flags.force === true });
|
|
100432
101052
|
return;
|
|
100433
101053
|
}
|
|
100434
101054
|
workflowUsage();
|
|
@@ -100453,7 +101073,7 @@ Available workspaces:`);
|
|
|
100453
101073
|
const ingested = await ingestJsonArgs({
|
|
100454
101074
|
rawArg: restArgs[2],
|
|
100455
101075
|
stdinIsTTY: process.stdin.isTTY ?? false,
|
|
100456
|
-
readFile: (p) =>
|
|
101076
|
+
readFile: (p) => fs11.readFileSync(p, "utf-8"),
|
|
100457
101077
|
readStdin
|
|
100458
101078
|
});
|
|
100459
101079
|
if (ingested.kind === "error") {
|
|
@@ -100609,7 +101229,7 @@ ${JSON.stringify(info.input_schema, null, 2)}`);
|
|
|
100609
101229
|
const ingested = await ingestJsonArgs({
|
|
100610
101230
|
rawArg: toolArgs,
|
|
100611
101231
|
stdinIsTTY: process.stdin.isTTY ?? false,
|
|
100612
|
-
readFile: (p) =>
|
|
101232
|
+
readFile: (p) => fs11.readFileSync(p, "utf-8"),
|
|
100613
101233
|
readStdin
|
|
100614
101234
|
});
|
|
100615
101235
|
if (ingested.kind === "error") {
|
|
@@ -100617,6 +101237,7 @@ ${JSON.stringify(info.input_schema, null, 2)}`);
|
|
|
100617
101237
|
process.exit(1);
|
|
100618
101238
|
}
|
|
100619
101239
|
const args = ingested.args;
|
|
101240
|
+
noteToolArgs(args);
|
|
100620
101241
|
const timeoutMs = flags.timeout ?? 6e4;
|
|
100621
101242
|
const result = await client.execute(toolName, args, { format: "text", timeoutMs });
|
|
100622
101243
|
if (result.error) {
|