@lotics/cli 0.70.0 → 0.73.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/README.md +38 -0
- package/dist/app_commands.d.ts +4 -0
- package/dist/app_commands.js +2 -2
- package/dist/args.d.ts +17 -0
- package/dist/args.js +35 -2
- package/dist/args.test.js +49 -0
- package/dist/cli.js +262 -8
- package/dist/client.d.ts +315 -0
- package/dist/client.js +160 -0
- package/dist/dev/rpc_handler.js +7 -2
- package/dist/dev/rpc_handler.test.js +24 -6
- package/dist/package_commands.d.ts +227 -0
- package/dist/package_commands.js +849 -0
- package/dist/package_commands.test.d.ts +1 -0
- package/dist/package_commands.test.js +303 -0
- package/dist/preview.d.ts +3 -0
- package/dist/preview.js +233 -0
- package/dist/render_page.js +59068 -0
- package/dist/render_page.js.LEGAL.txt +14 -0
- package/dist/src/cli.js +1512 -173
- package/package.json +1 -1
package/dist/src/cli.js
CHANGED
|
@@ -12243,12 +12243,12 @@ var require_tokens = __commonJS({
|
|
|
12243
12243
|
});
|
|
12244
12244
|
}
|
|
12245
12245
|
exports2.assignCategoriesMapProp = assignCategoriesMapProp;
|
|
12246
|
-
function singleAssignCategoriesToksMap(
|
|
12247
|
-
utils_1.forEach(
|
|
12246
|
+
function singleAssignCategoriesToksMap(path9, nextNode) {
|
|
12247
|
+
utils_1.forEach(path9, function(pathNode) {
|
|
12248
12248
|
nextNode.categoryMatchesMap[pathNode.tokenTypeIdx] = true;
|
|
12249
12249
|
});
|
|
12250
12250
|
utils_1.forEach(nextNode.CATEGORIES, function(nextCategory) {
|
|
12251
|
-
var newPath =
|
|
12251
|
+
var newPath = path9.concat(nextNode);
|
|
12252
12252
|
if (!utils_1.contains(newPath, nextCategory)) {
|
|
12253
12253
|
singleAssignCategoriesToksMap(newPath, nextCategory);
|
|
12254
12254
|
}
|
|
@@ -14011,10 +14011,10 @@ var require_interpreter = __commonJS({
|
|
|
14011
14011
|
/** @class */
|
|
14012
14012
|
(function(_super) {
|
|
14013
14013
|
__extends(AbstractNextPossibleTokensWalker2, _super);
|
|
14014
|
-
function AbstractNextPossibleTokensWalker2(topProd,
|
|
14014
|
+
function AbstractNextPossibleTokensWalker2(topProd, path9) {
|
|
14015
14015
|
var _this = _super.call(this) || this;
|
|
14016
14016
|
_this.topProd = topProd;
|
|
14017
|
-
_this.path =
|
|
14017
|
+
_this.path = path9;
|
|
14018
14018
|
_this.possibleTokTypes = [];
|
|
14019
14019
|
_this.nextProductionName = "";
|
|
14020
14020
|
_this.nextProductionOccurrence = 0;
|
|
@@ -14068,9 +14068,9 @@ var require_interpreter = __commonJS({
|
|
|
14068
14068
|
/** @class */
|
|
14069
14069
|
(function(_super) {
|
|
14070
14070
|
__extends(NextAfterTokenWalker2, _super);
|
|
14071
|
-
function NextAfterTokenWalker2(topProd,
|
|
14072
|
-
var _this = _super.call(this, topProd,
|
|
14073
|
-
_this.path =
|
|
14071
|
+
function NextAfterTokenWalker2(topProd, path9) {
|
|
14072
|
+
var _this = _super.call(this, topProd, path9) || this;
|
|
14073
|
+
_this.path = path9;
|
|
14074
14074
|
_this.nextTerminalName = "";
|
|
14075
14075
|
_this.nextTerminalOccurrence = 0;
|
|
14076
14076
|
_this.nextTerminalName = _this.path.lastTok.name;
|
|
@@ -14767,10 +14767,10 @@ var require_lookahead = __commonJS({
|
|
|
14767
14767
|
}
|
|
14768
14768
|
return result;
|
|
14769
14769
|
}
|
|
14770
|
-
function pathToHashKeys(
|
|
14770
|
+
function pathToHashKeys(path9) {
|
|
14771
14771
|
var keys2 = [""];
|
|
14772
|
-
for (var i2 = 0; i2 <
|
|
14773
|
-
var tokType =
|
|
14772
|
+
for (var i2 = 0; i2 < path9.length; i2++) {
|
|
14773
|
+
var tokType = path9[i2];
|
|
14774
14774
|
var longerKeys = [];
|
|
14775
14775
|
for (var j = 0; j < keys2.length; j++) {
|
|
14776
14776
|
var currShorterKey = keys2[j];
|
|
@@ -15082,9 +15082,9 @@ var require_checks = __commonJS({
|
|
|
15082
15082
|
return errors;
|
|
15083
15083
|
}
|
|
15084
15084
|
exports2.validateRuleIsOverridden = validateRuleIsOverridden;
|
|
15085
|
-
function validateNoLeftRecursion(topRule, currRule, errMsgProvider,
|
|
15086
|
-
if (
|
|
15087
|
-
|
|
15085
|
+
function validateNoLeftRecursion(topRule, currRule, errMsgProvider, path9) {
|
|
15086
|
+
if (path9 === void 0) {
|
|
15087
|
+
path9 = [];
|
|
15088
15088
|
}
|
|
15089
15089
|
var errors = [];
|
|
15090
15090
|
var nextNonTerminals = getFirstNoneTerminal(currRule.definition);
|
|
@@ -15097,15 +15097,15 @@ var require_checks = __commonJS({
|
|
|
15097
15097
|
errors.push({
|
|
15098
15098
|
message: errMsgProvider.buildLeftRecursionError({
|
|
15099
15099
|
topLevelRule: topRule,
|
|
15100
|
-
leftRecursionPath:
|
|
15100
|
+
leftRecursionPath: path9
|
|
15101
15101
|
}),
|
|
15102
15102
|
type: parser_1.ParserDefinitionErrorType.LEFT_RECURSION,
|
|
15103
15103
|
ruleName
|
|
15104
15104
|
});
|
|
15105
15105
|
}
|
|
15106
|
-
var validNextSteps = utils.difference(nextNonTerminals,
|
|
15106
|
+
var validNextSteps = utils.difference(nextNonTerminals, path9.concat([topRule]));
|
|
15107
15107
|
var errorsFromNextSteps = utils.map(validNextSteps, function(currRefRule) {
|
|
15108
|
-
var newPath = utils.cloneArr(
|
|
15108
|
+
var newPath = utils.cloneArr(path9);
|
|
15109
15109
|
newPath.push(currRefRule);
|
|
15110
15110
|
return validateNoLeftRecursion(topRule, currRefRule, errMsgProvider, newPath);
|
|
15111
15111
|
});
|
|
@@ -20127,7 +20127,7 @@ var require_BufferList = __commonJS({
|
|
|
20127
20127
|
this.head = this.tail = null;
|
|
20128
20128
|
this.length = 0;
|
|
20129
20129
|
};
|
|
20130
|
-
BufferList.prototype.join = function
|
|
20130
|
+
BufferList.prototype.join = function join2(s) {
|
|
20131
20131
|
if (this.length === 0) return "";
|
|
20132
20132
|
var p = this.head;
|
|
20133
20133
|
var ret = "" + p.data;
|
|
@@ -22102,8 +22102,8 @@ var require_lib2 = __commonJS({
|
|
|
22102
22102
|
return this;
|
|
22103
22103
|
}
|
|
22104
22104
|
var p = this.constructor;
|
|
22105
|
-
return this.then(
|
|
22106
|
-
function
|
|
22105
|
+
return this.then(resolve3, reject3);
|
|
22106
|
+
function resolve3(value) {
|
|
22107
22107
|
function yes() {
|
|
22108
22108
|
return value;
|
|
22109
22109
|
}
|
|
@@ -22256,8 +22256,8 @@ var require_lib2 = __commonJS({
|
|
|
22256
22256
|
}
|
|
22257
22257
|
return out;
|
|
22258
22258
|
}
|
|
22259
|
-
Promise2.resolve =
|
|
22260
|
-
function
|
|
22259
|
+
Promise2.resolve = resolve2;
|
|
22260
|
+
function resolve2(value) {
|
|
22261
22261
|
if (value instanceof this) {
|
|
22262
22262
|
return value;
|
|
22263
22263
|
}
|
|
@@ -22714,8 +22714,8 @@ var require_utils4 = __commonJS({
|
|
|
22714
22714
|
var result = transform[inputType][outputType](input);
|
|
22715
22715
|
return result;
|
|
22716
22716
|
};
|
|
22717
|
-
exports2.resolve = function(
|
|
22718
|
-
var parts =
|
|
22717
|
+
exports2.resolve = function(path9) {
|
|
22718
|
+
var parts = path9.split("/");
|
|
22719
22719
|
var result = [];
|
|
22720
22720
|
for (var index = 0; index < parts.length; index++) {
|
|
22721
22721
|
var part = parts[index];
|
|
@@ -22788,10 +22788,10 @@ var require_utils4 = __commonJS({
|
|
|
22788
22788
|
var promise = external.Promise.resolve(inputData).then(function(data) {
|
|
22789
22789
|
var isBlob = support.blob && (data instanceof Blob || ["[object File]", "[object Blob]"].indexOf(Object.prototype.toString.call(data)) !== -1);
|
|
22790
22790
|
if (isBlob && typeof FileReader !== "undefined") {
|
|
22791
|
-
return new external.Promise(function(
|
|
22791
|
+
return new external.Promise(function(resolve2, reject2) {
|
|
22792
22792
|
var reader = new FileReader();
|
|
22793
22793
|
reader.onload = function(e) {
|
|
22794
|
-
|
|
22794
|
+
resolve2(e.target.result);
|
|
22795
22795
|
};
|
|
22796
22796
|
reader.onerror = function(e) {
|
|
22797
22797
|
reject2(e.target.error);
|
|
@@ -23346,7 +23346,7 @@ var require_StreamHelper = __commonJS({
|
|
|
23346
23346
|
}
|
|
23347
23347
|
}
|
|
23348
23348
|
function accumulate(helper, updateCallback) {
|
|
23349
|
-
return new external.Promise(function(
|
|
23349
|
+
return new external.Promise(function(resolve2, reject2) {
|
|
23350
23350
|
var dataArray = [];
|
|
23351
23351
|
var chunkType = helper._internalType, resultType = helper._outputType, mimeType = helper._mimeType;
|
|
23352
23352
|
helper.on("data", function(data, meta) {
|
|
@@ -23360,7 +23360,7 @@ var require_StreamHelper = __commonJS({
|
|
|
23360
23360
|
}).on("end", function() {
|
|
23361
23361
|
try {
|
|
23362
23362
|
var result = transformZipOutput(resultType, concat2(chunkType, dataArray), mimeType);
|
|
23363
|
-
|
|
23363
|
+
resolve2(result);
|
|
23364
23364
|
} catch (e) {
|
|
23365
23365
|
reject2(e);
|
|
23366
23366
|
}
|
|
@@ -28568,18 +28568,18 @@ var require_object = __commonJS({
|
|
|
28568
28568
|
var object = new ZipObject(name, zipObjectContent, o);
|
|
28569
28569
|
this.files[name] = object;
|
|
28570
28570
|
};
|
|
28571
|
-
var parentFolder = function(
|
|
28572
|
-
if (
|
|
28573
|
-
|
|
28571
|
+
var parentFolder = function(path9) {
|
|
28572
|
+
if (path9.slice(-1) === "/") {
|
|
28573
|
+
path9 = path9.substring(0, path9.length - 1);
|
|
28574
28574
|
}
|
|
28575
|
-
var lastSlash =
|
|
28576
|
-
return lastSlash > 0 ?
|
|
28575
|
+
var lastSlash = path9.lastIndexOf("/");
|
|
28576
|
+
return lastSlash > 0 ? path9.substring(0, lastSlash) : "";
|
|
28577
28577
|
};
|
|
28578
|
-
var forceTrailingSlash = function(
|
|
28579
|
-
if (
|
|
28580
|
-
|
|
28578
|
+
var forceTrailingSlash = function(path9) {
|
|
28579
|
+
if (path9.slice(-1) !== "/") {
|
|
28580
|
+
path9 += "/";
|
|
28581
28581
|
}
|
|
28582
|
-
return
|
|
28582
|
+
return path9;
|
|
28583
28583
|
};
|
|
28584
28584
|
var folderAdd = function(name, createFolders) {
|
|
28585
28585
|
createFolders = typeof createFolders !== "undefined" ? createFolders : defaults2.createFolders;
|
|
@@ -29473,7 +29473,7 @@ var require_load = __commonJS({
|
|
|
29473
29473
|
var Crc32Probe = require_Crc32Probe();
|
|
29474
29474
|
var nodejsUtils = require_nodejsUtils();
|
|
29475
29475
|
function checkEntryCRC32(zipEntry) {
|
|
29476
|
-
return new external.Promise(function(
|
|
29476
|
+
return new external.Promise(function(resolve2, reject2) {
|
|
29477
29477
|
var worker = zipEntry.decompressed.getContentWorker().pipe(new Crc32Probe());
|
|
29478
29478
|
worker.on("error", function(e) {
|
|
29479
29479
|
reject2(e);
|
|
@@ -29481,7 +29481,7 @@ var require_load = __commonJS({
|
|
|
29481
29481
|
if (worker.streamInfo.crc32 !== zipEntry.decompressed.crc32) {
|
|
29482
29482
|
reject2(new Error("Corrupted zip : CRC32 mismatch"));
|
|
29483
29483
|
} else {
|
|
29484
|
-
|
|
29484
|
+
resolve2();
|
|
29485
29485
|
}
|
|
29486
29486
|
}).resume();
|
|
29487
29487
|
});
|
|
@@ -29581,8 +29581,8 @@ var require_lib3 = __commonJS({
|
|
|
29581
29581
|
// src/cli.ts
|
|
29582
29582
|
import dns from "node:dns";
|
|
29583
29583
|
import net2 from "node:net";
|
|
29584
|
-
import
|
|
29585
|
-
import
|
|
29584
|
+
import fs9 from "node:fs";
|
|
29585
|
+
import path8 from "node:path";
|
|
29586
29586
|
import readline from "node:readline";
|
|
29587
29587
|
|
|
29588
29588
|
// src/client.ts
|
|
@@ -29680,8 +29680,8 @@ var LoticsClient = class {
|
|
|
29680
29680
|
}
|
|
29681
29681
|
return headers;
|
|
29682
29682
|
}
|
|
29683
|
-
async request(method,
|
|
29684
|
-
const url = `${this.baseUrl}${
|
|
29683
|
+
async request(method, path9, body) {
|
|
29684
|
+
const url = `${this.baseUrl}${path9}`;
|
|
29685
29685
|
const headers = this.buildHeaders();
|
|
29686
29686
|
const init = { method, headers };
|
|
29687
29687
|
if (body !== void 0) {
|
|
@@ -29698,9 +29698,22 @@ var LoticsClient = class {
|
|
|
29698
29698
|
setWorkspaceId(id) {
|
|
29699
29699
|
this.workspaceId = id;
|
|
29700
29700
|
}
|
|
29701
|
+
/** The workspace id the client targets (the `x-workspace-id` header), if resolved. */
|
|
29702
|
+
getWorkspaceId() {
|
|
29703
|
+
return this.workspaceId;
|
|
29704
|
+
}
|
|
29701
29705
|
async listWorkspaces() {
|
|
29702
29706
|
return this.request("GET", "/v1/workspaces");
|
|
29703
29707
|
}
|
|
29708
|
+
/**
|
|
29709
|
+
* Resolve one workspace's info by id from the org's workspace list (the only
|
|
29710
|
+
* API-key-accessible source carrying `is_dev`). Returns null when the
|
|
29711
|
+
* workspace isn't visible to these credentials.
|
|
29712
|
+
*/
|
|
29713
|
+
async getWorkspaceInfo(id) {
|
|
29714
|
+
const workspaces = await this.listWorkspaces();
|
|
29715
|
+
return workspaces.find((w) => w.id === id) ?? null;
|
|
29716
|
+
}
|
|
29704
29717
|
async createWorkspace(body) {
|
|
29705
29718
|
return this.request("POST", "/v1/workspaces", body);
|
|
29706
29719
|
}
|
|
@@ -29749,6 +29762,175 @@ var LoticsClient = class {
|
|
|
29749
29762
|
async createApp(body) {
|
|
29750
29763
|
return this.request("POST", "/v1/apps", body);
|
|
29751
29764
|
}
|
|
29765
|
+
/**
|
|
29766
|
+
* Install an app package version into the current workspace — scaffolds the
|
|
29767
|
+
* data model, deploys the package bundle, materializes its
|
|
29768
|
+
* queries/workflows/agents, and pins the installation. Returns the resulting
|
|
29769
|
+
* installation app. `version` omitted installs the latest published version.
|
|
29770
|
+
*/
|
|
29771
|
+
async installAppPackage(package_id, body) {
|
|
29772
|
+
return this.request(
|
|
29773
|
+
"POST",
|
|
29774
|
+
`/v1/app-packages/${encodeURIComponent(package_id)}/installations`,
|
|
29775
|
+
body
|
|
29776
|
+
);
|
|
29777
|
+
}
|
|
29778
|
+
/**
|
|
29779
|
+
* Eject an installation from its package — re-deploy the pinned version's
|
|
29780
|
+
* source as a workspace-owned app version, then sever the package link
|
|
29781
|
+
* (clears `package_id`/`package_version`, stamps `ejected_at`). One-way: the
|
|
29782
|
+
* app becomes a normal bespoke app and can no longer be upgraded. Returns the
|
|
29783
|
+
* resulting app.
|
|
29784
|
+
*/
|
|
29785
|
+
async ejectAppPackage(app_id) {
|
|
29786
|
+
return this.request("POST", `/v1/apps/${encodeURIComponent(app_id)}/eject`);
|
|
29787
|
+
}
|
|
29788
|
+
/**
|
|
29789
|
+
* Extract a DRAFT app package from an existing bespoke app — the promotion
|
|
29790
|
+
* read (docs/app_packages.md § Promotion). Pure: nothing is written. Returns
|
|
29791
|
+
* the alias-keyed draft `contract` (opaque to the CLI — the server is the
|
|
29792
|
+
* validating authority), the origin workspace's `binding` (which doubles as
|
|
29793
|
+
* the adopt binding), a findings `report` (any `error` ⇒ not publishable
|
|
29794
|
+
* as-is), and the file-backed `template_files` the CLI must stage into the
|
|
29795
|
+
* project at their `bytes_ref` paths. Backs `lotics package extract`.
|
|
29796
|
+
* Admin-only.
|
|
29797
|
+
*/
|
|
29798
|
+
async extractAppPackage(app_id) {
|
|
29799
|
+
return this.request("GET", `/v1/apps/${encodeURIComponent(app_id)}/package-extract`);
|
|
29800
|
+
}
|
|
29801
|
+
/**
|
|
29802
|
+
* Adopt a published package onto an EXISTING (bespoke or ejected) app — the
|
|
29803
|
+
* final promotion step. The workspace already holds the concrete objects, so
|
|
29804
|
+
* nothing is scaffolded or rewritten: the server verifies the `binding` is
|
|
29805
|
+
* complete, live, and FAITHFUL to the version's contract, then writes only the
|
|
29806
|
+
* installation pin (the app becomes installation #1, upgradeable again). A
|
|
29807
|
+
* ConflictError names the aliases that diverge. `version` omitted adopts the
|
|
29808
|
+
* latest. Backs `lotics package adopt`. Admin-only.
|
|
29809
|
+
*/
|
|
29810
|
+
async adoptAppPackage(app_id, body) {
|
|
29811
|
+
return this.request("POST", `/v1/apps/${encodeURIComponent(app_id)}/package-adopt`, body);
|
|
29812
|
+
}
|
|
29813
|
+
/**
|
|
29814
|
+
* Fleet upgrade — bring every installation of a package across the CALLER'S
|
|
29815
|
+
* org to the target version (latest when omitted) in one call. Hands-off
|
|
29816
|
+
* applies only where the preview is clean; installations with breaking/
|
|
29817
|
+
* drift/modified-core findings are skipped and reported for the normal
|
|
29818
|
+
* per-installation consent flow. Backs `lotics package fleet-upgrade`.
|
|
29819
|
+
* Admin-only; org-scoped (no workspace header needed).
|
|
29820
|
+
*/
|
|
29821
|
+
async fleetUpgradeAppPackage(package_id, body) {
|
|
29822
|
+
return this.request(
|
|
29823
|
+
"POST",
|
|
29824
|
+
`/v1/app-packages/${encodeURIComponent(package_id)}/fleet-upgrade`,
|
|
29825
|
+
body
|
|
29826
|
+
);
|
|
29827
|
+
}
|
|
29828
|
+
// --- App packages (registry authoring + dev harness) ---
|
|
29829
|
+
/**
|
|
29830
|
+
* Create a registry app package — the Lotics-owned, workspace-agnostic
|
|
29831
|
+
* blueprint. `lotics package publish` calls this on first publish (when the
|
|
29832
|
+
* local manifest has no `package_id`), then publishes version 1 against the
|
|
29833
|
+
* returned id. Admin-only.
|
|
29834
|
+
*/
|
|
29835
|
+
async createAppPackage(body) {
|
|
29836
|
+
return this.request("POST", "/v1/app-packages", body);
|
|
29837
|
+
}
|
|
29838
|
+
/**
|
|
29839
|
+
* Fetch a registry app package's metadata (incl. `latest_version` and the
|
|
29840
|
+
* Lotics-backed `is_official` trust badge). Admin-only; cross-tenant by id.
|
|
29841
|
+
*/
|
|
29842
|
+
async getAppPackage(package_id) {
|
|
29843
|
+
return this.request("GET", `/v1/app-packages/${encodeURIComponent(package_id)}`);
|
|
29844
|
+
}
|
|
29845
|
+
/**
|
|
29846
|
+
* Publish a new immutable package version — multipart upload of the alias-keyed
|
|
29847
|
+
* contract (JSON) + the prebuilt code bundle (a gzipped tarball carrying
|
|
29848
|
+
* `source.tar.gz` + `dist.tar.gz` members). The server validates the contract +
|
|
29849
|
+
* bundle, then allocates the next monotonic version. The `contract` is opaque
|
|
29850
|
+
* JSON to the transport (the server is the validating authority). Admin-only.
|
|
29851
|
+
*/
|
|
29852
|
+
async publishAppPackageVersion(package_id, args) {
|
|
29853
|
+
const formData = new FormData();
|
|
29854
|
+
formData.append("contract", JSON.stringify(args.contract));
|
|
29855
|
+
formData.append(
|
|
29856
|
+
"bundle",
|
|
29857
|
+
new Blob([new Uint8Array(args.bundle)], { type: "application/gzip" }),
|
|
29858
|
+
"bundle.tar.gz"
|
|
29859
|
+
);
|
|
29860
|
+
if (args.changelog) formData.append("changelog", args.changelog);
|
|
29861
|
+
const url = `${this.baseUrl}/v1/app-packages/${encodeURIComponent(package_id)}/versions`;
|
|
29862
|
+
const response = await fetch(url, {
|
|
29863
|
+
method: "POST",
|
|
29864
|
+
headers: this.buildHeaders(),
|
|
29865
|
+
// no Content-Type — fetch sets the multipart boundary
|
|
29866
|
+
body: formData
|
|
29867
|
+
});
|
|
29868
|
+
if (!response.ok) await this.throwResponseError(response);
|
|
29869
|
+
return response.json();
|
|
29870
|
+
}
|
|
29871
|
+
/**
|
|
29872
|
+
* Upgrade a package installation to a newer published version — extends the
|
|
29873
|
+
* binding additively, re-materializes the target version's
|
|
29874
|
+
* queries/workflows/agents (preserving workspace overlay), prunes dropped
|
|
29875
|
+
* package artifacts, and bumps the pin. `version` omitted upgrades to the
|
|
29876
|
+
* latest. Returns the resulting app. Admin-only.
|
|
29877
|
+
*/
|
|
29878
|
+
async upgradeAppPackage(app_id, body) {
|
|
29879
|
+
return this.request(
|
|
29880
|
+
"POST",
|
|
29881
|
+
`/v1/apps/${encodeURIComponent(app_id)}/package-upgrade`,
|
|
29882
|
+
body
|
|
29883
|
+
);
|
|
29884
|
+
}
|
|
29885
|
+
/**
|
|
29886
|
+
* Preview a package upgrade — the additive plan, informational removals,
|
|
29887
|
+
* breaking contract changes, the binding drift report, and the modified-core
|
|
29888
|
+
* report — with no writes. Admin-only.
|
|
29889
|
+
*/
|
|
29890
|
+
async previewAppPackageUpgrade(app_id, opts = {}) {
|
|
29891
|
+
const query = opts.version !== void 0 ? `?version=${opts.version}` : "";
|
|
29892
|
+
return this.request(
|
|
29893
|
+
"GET",
|
|
29894
|
+
`/v1/apps/${encodeURIComponent(app_id)}/package-upgrade${query}`
|
|
29895
|
+
);
|
|
29896
|
+
}
|
|
29897
|
+
/**
|
|
29898
|
+
* Re-bind a package role to a different workspace group (the current group
|
|
29899
|
+
* still exists). Re-materializes at the pinned version; refuses over
|
|
29900
|
+
* modified-core findings. Admin-only.
|
|
29901
|
+
*/
|
|
29902
|
+
async rebindAppPackageRole(app_id, body) {
|
|
29903
|
+
return this.request(
|
|
29904
|
+
"POST",
|
|
29905
|
+
`/v1/apps/${encodeURIComponent(app_id)}/package-rebind-role`,
|
|
29906
|
+
body
|
|
29907
|
+
);
|
|
29908
|
+
}
|
|
29909
|
+
/**
|
|
29910
|
+
* Workspace-wide dangling-reference sweep — active app/workflow artifacts
|
|
29911
|
+
* whose prefixed schema ids no longer resolve. Backs
|
|
29912
|
+
* `lotics workspace doctor`. Admin-only.
|
|
29913
|
+
*/
|
|
29914
|
+
async getWorkspaceDanglingReferences() {
|
|
29915
|
+
return this.request("GET", "/v1/workspaces/dangling-references");
|
|
29916
|
+
}
|
|
29917
|
+
/**
|
|
29918
|
+
* Health check for a package installation — version pin vs. registry latest,
|
|
29919
|
+
* binding drift, and locally modified core artifacts. Read-only; backs
|
|
29920
|
+
* `lotics package doctor`. Admin-only.
|
|
29921
|
+
*/
|
|
29922
|
+
async getAppPackageHealth(app_id) {
|
|
29923
|
+
return this.request("GET", `/v1/apps/${encodeURIComponent(app_id)}/package-health`);
|
|
29924
|
+
}
|
|
29925
|
+
/**
|
|
29926
|
+
* Reset a package installation in a DEV workspace — drop the package-owned
|
|
29927
|
+
* scaffolded tables and re-scaffold clean. Hard-gated server-side to dev
|
|
29928
|
+
* workspaces (a non-dev workspace is refused). Returns the resulting app.
|
|
29929
|
+
* Admin-only.
|
|
29930
|
+
*/
|
|
29931
|
+
async resetAppPackage(app_id) {
|
|
29932
|
+
return this.request("POST", `/v1/apps/${encodeURIComponent(app_id)}/package-reset`);
|
|
29933
|
+
}
|
|
29752
29934
|
/**
|
|
29753
29935
|
* Resolve the display name + fields (incl. select options) of the given tables
|
|
29754
29936
|
* — the schema `lotics app codegen` turns into the runtime `.lotics/app_fields.ts`
|
|
@@ -31112,10 +31294,11 @@ async function dispatchRpc(client, body, opts) {
|
|
|
31112
31294
|
}
|
|
31113
31295
|
switch (body.op) {
|
|
31114
31296
|
case "context": {
|
|
31115
|
-
const who = await client.whoami();
|
|
31297
|
+
const [who, app] = await Promise.all([client.whoami(), client.getApp(body.app_id)]);
|
|
31116
31298
|
return {
|
|
31117
31299
|
member_id: who.member_id,
|
|
31118
|
-
comments_enabled: opts?.commentsEnabled ?? false
|
|
31300
|
+
comments_enabled: opts?.commentsEnabled ?? false,
|
|
31301
|
+
config: app.config ?? {}
|
|
31119
31302
|
};
|
|
31120
31303
|
}
|
|
31121
31304
|
case "query": {
|
|
@@ -31553,8 +31736,8 @@ async function startDevServer(args) {
|
|
|
31553
31736
|
);
|
|
31554
31737
|
let stopped = false;
|
|
31555
31738
|
let stoppingResolve = null;
|
|
31556
|
-
const stoppingPromise = new Promise((
|
|
31557
|
-
stoppingResolve =
|
|
31739
|
+
const stoppingPromise = new Promise((resolve2) => {
|
|
31740
|
+
stoppingResolve = resolve2;
|
|
31558
31741
|
});
|
|
31559
31742
|
viteChild.on("exit", (code, signal) => {
|
|
31560
31743
|
if (!stopped) {
|
|
@@ -31655,11 +31838,11 @@ async function startDevServer(args) {
|
|
|
31655
31838
|
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
31656
31839
|
res.end("Not Found");
|
|
31657
31840
|
});
|
|
31658
|
-
await new Promise((
|
|
31841
|
+
await new Promise((resolve2, reject2) => {
|
|
31659
31842
|
server.once("error", reject2);
|
|
31660
31843
|
server.listen(wrapperPort, () => {
|
|
31661
31844
|
server.off("error", reject2);
|
|
31662
|
-
|
|
31845
|
+
resolve2();
|
|
31663
31846
|
});
|
|
31664
31847
|
});
|
|
31665
31848
|
async function stopAll() {
|
|
@@ -31668,11 +31851,11 @@ async function startDevServer(args) {
|
|
|
31668
31851
|
if (!viteChild.killed) {
|
|
31669
31852
|
viteChild.kill("SIGINT");
|
|
31670
31853
|
}
|
|
31671
|
-
await new Promise((
|
|
31854
|
+
await new Promise((resolve2) => server.close(() => resolve2()));
|
|
31672
31855
|
stoppingResolve?.();
|
|
31673
31856
|
}
|
|
31674
|
-
const ready = new Promise((
|
|
31675
|
-
setTimeout(
|
|
31857
|
+
const ready = new Promise((resolve2) => {
|
|
31858
|
+
setTimeout(resolve2, 800);
|
|
31676
31859
|
});
|
|
31677
31860
|
return {
|
|
31678
31861
|
port: wrapperPort,
|
|
@@ -31688,22 +31871,22 @@ async function pickPort(preferred, ...avoid) {
|
|
|
31688
31871
|
if (!avoid.includes(preferred) && await isPortFree(preferred)) {
|
|
31689
31872
|
return preferred;
|
|
31690
31873
|
}
|
|
31691
|
-
return new Promise((
|
|
31874
|
+
return new Promise((resolve2, reject2) => {
|
|
31692
31875
|
const srv = net.createServer();
|
|
31693
31876
|
srv.once("error", reject2);
|
|
31694
31877
|
srv.listen(0, () => {
|
|
31695
31878
|
const addr = srv.address();
|
|
31696
31879
|
const port = typeof addr === "object" && addr ? addr.port : 0;
|
|
31697
|
-
srv.close(() => port ?
|
|
31880
|
+
srv.close(() => port ? resolve2(port) : reject2(new Error("Failed to pick port")));
|
|
31698
31881
|
});
|
|
31699
31882
|
});
|
|
31700
31883
|
}
|
|
31701
31884
|
function isPortFree(port) {
|
|
31702
|
-
return new Promise((
|
|
31885
|
+
return new Promise((resolve2) => {
|
|
31703
31886
|
const srv = net.createServer();
|
|
31704
|
-
srv.once("error", () =>
|
|
31887
|
+
srv.once("error", () => resolve2(false));
|
|
31705
31888
|
srv.listen(port, () => {
|
|
31706
|
-
srv.close(() =>
|
|
31889
|
+
srv.close(() => resolve2(true));
|
|
31707
31890
|
});
|
|
31708
31891
|
});
|
|
31709
31892
|
}
|
|
@@ -31995,6 +32178,7 @@ function walk(node, visit) {
|
|
|
31995
32178
|
case "sort":
|
|
31996
32179
|
case "limit":
|
|
31997
32180
|
case "unpivot":
|
|
32181
|
+
case "unnest":
|
|
31998
32182
|
walk(node.from, visit);
|
|
31999
32183
|
return;
|
|
32000
32184
|
case "join":
|
|
@@ -32337,7 +32521,7 @@ async function fetchWorkflowGlobals(client, projectDir, app_id, alias) {
|
|
|
32337
32521
|
}
|
|
32338
32522
|
}
|
|
32339
32523
|
function runTar(args, cwd) {
|
|
32340
|
-
return new Promise((
|
|
32524
|
+
return new Promise((resolve2, reject2) => {
|
|
32341
32525
|
const proc = spawn2("tar", args, { cwd, stdio: ["ignore", "ignore", "pipe"] });
|
|
32342
32526
|
let stderr = "";
|
|
32343
32527
|
proc.stderr.on("data", (chunk) => {
|
|
@@ -32345,17 +32529,17 @@ function runTar(args, cwd) {
|
|
|
32345
32529
|
});
|
|
32346
32530
|
proc.on("error", reject2);
|
|
32347
32531
|
proc.on("exit", (code) => {
|
|
32348
|
-
if (code === 0)
|
|
32532
|
+
if (code === 0) resolve2();
|
|
32349
32533
|
else reject2(new Error(`tar ${args.join(" ")} failed with exit ${code}: ${stderr}`));
|
|
32350
32534
|
});
|
|
32351
32535
|
});
|
|
32352
32536
|
}
|
|
32353
32537
|
function runNpm(args, cwd) {
|
|
32354
|
-
return new Promise((
|
|
32538
|
+
return new Promise((resolve2, reject2) => {
|
|
32355
32539
|
const proc = spawn2("npm", args, { cwd, stdio: "inherit", env: ipv4ChildEnv(process.env) });
|
|
32356
32540
|
proc.on("error", reject2);
|
|
32357
32541
|
proc.on("exit", (code) => {
|
|
32358
|
-
if (code === 0)
|
|
32542
|
+
if (code === 0) resolve2();
|
|
32359
32543
|
else reject2(new Error(`npm ${args.join(" ")} exited with code ${code}`));
|
|
32360
32544
|
});
|
|
32361
32545
|
});
|
|
@@ -32851,11 +33035,11 @@ async function appDev(client, args) {
|
|
|
32851
33035
|
console.error(` Ctrl-C to stop.
|
|
32852
33036
|
`);
|
|
32853
33037
|
openBrowser(url);
|
|
32854
|
-
await new Promise((
|
|
33038
|
+
await new Promise((resolve2) => {
|
|
32855
33039
|
const onSig = () => {
|
|
32856
33040
|
process.off("SIGINT", onSig);
|
|
32857
33041
|
process.off("SIGTERM", onSig);
|
|
32858
|
-
|
|
33042
|
+
resolve2();
|
|
32859
33043
|
};
|
|
32860
33044
|
process.on("SIGINT", onSig);
|
|
32861
33045
|
process.on("SIGTERM", onSig);
|
|
@@ -33162,6 +33346,724 @@ function escapeRegExp(s) {
|
|
|
33162
33346
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
33163
33347
|
}
|
|
33164
33348
|
|
|
33349
|
+
// src/package_commands.ts
|
|
33350
|
+
import fs6 from "node:fs";
|
|
33351
|
+
import path7 from "node:path";
|
|
33352
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
33353
|
+
|
|
33354
|
+
// src/file_command_io.ts
|
|
33355
|
+
import fs5 from "node:fs";
|
|
33356
|
+
import path6 from "node:path";
|
|
33357
|
+
var CliError = class extends Error {
|
|
33358
|
+
constructor(message) {
|
|
33359
|
+
super(message);
|
|
33360
|
+
this.name = "CliError";
|
|
33361
|
+
}
|
|
33362
|
+
};
|
|
33363
|
+
function fail(message) {
|
|
33364
|
+
throw new CliError(message);
|
|
33365
|
+
}
|
|
33366
|
+
function writeFileAtomic(filePath, bytes) {
|
|
33367
|
+
const dir = path6.dirname(path6.resolve(filePath));
|
|
33368
|
+
const tmp = path6.join(dir, `.${path6.basename(filePath)}.${process.pid}.${Date.now()}.tmp`);
|
|
33369
|
+
fs5.writeFileSync(tmp, bytes);
|
|
33370
|
+
try {
|
|
33371
|
+
fs5.renameSync(tmp, filePath);
|
|
33372
|
+
} catch (error) {
|
|
33373
|
+
try {
|
|
33374
|
+
fs5.unlinkSync(tmp);
|
|
33375
|
+
} catch {
|
|
33376
|
+
}
|
|
33377
|
+
throw error;
|
|
33378
|
+
}
|
|
33379
|
+
console.error(`Wrote ${filePath}`);
|
|
33380
|
+
}
|
|
33381
|
+
|
|
33382
|
+
// src/package_commands.ts
|
|
33383
|
+
var CONTRACT_FILE = "contract.json";
|
|
33384
|
+
var ADOPT_BINDING_FILE = "adopt_binding.json";
|
|
33385
|
+
function packageJsonPath(projectDir) {
|
|
33386
|
+
return path7.join(projectDir, "package.json");
|
|
33387
|
+
}
|
|
33388
|
+
function isPlainObject(value) {
|
|
33389
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
33390
|
+
}
|
|
33391
|
+
function readPackageProject(projectDir) {
|
|
33392
|
+
const pkgPath2 = packageJsonPath(projectDir);
|
|
33393
|
+
if (!fs6.existsSync(pkgPath2)) {
|
|
33394
|
+
throw new Error(
|
|
33395
|
+
`No package.json in ${projectDir}. Run this inside a package project (lotics package new <name>).`
|
|
33396
|
+
);
|
|
33397
|
+
}
|
|
33398
|
+
const parsed = JSON.parse(fs6.readFileSync(pkgPath2, "utf-8"));
|
|
33399
|
+
if (!isPlainObject(parsed)) throw new Error(`Malformed package.json in ${projectDir}.`);
|
|
33400
|
+
const lotics = parsed.lotics;
|
|
33401
|
+
const pkg2 = isPlainObject(lotics) ? lotics.package : void 0;
|
|
33402
|
+
if (!isPlainObject(pkg2) || typeof pkg2.name !== "string") {
|
|
33403
|
+
throw new Error(
|
|
33404
|
+
`${pkgPath2} has no lotics.package manifest \u2014 not a package project. Use "lotics package new <name>".`
|
|
33405
|
+
);
|
|
33406
|
+
}
|
|
33407
|
+
const dev = {};
|
|
33408
|
+
if (isPlainObject(pkg2.dev)) {
|
|
33409
|
+
for (const [ws, entry] of Object.entries(pkg2.dev)) {
|
|
33410
|
+
if (isPlainObject(entry) && typeof entry.app_id === "string" && typeof entry.version === "number") {
|
|
33411
|
+
dev[ws] = { app_id: entry.app_id, version: entry.version };
|
|
33412
|
+
}
|
|
33413
|
+
}
|
|
33414
|
+
}
|
|
33415
|
+
const manifest = {
|
|
33416
|
+
id: typeof pkg2.id === "string" ? pkg2.id : null,
|
|
33417
|
+
name: pkg2.name,
|
|
33418
|
+
description: typeof pkg2.description === "string" ? pkg2.description : null,
|
|
33419
|
+
version: typeof pkg2.version === "number" ? pkg2.version : null,
|
|
33420
|
+
dev
|
|
33421
|
+
};
|
|
33422
|
+
return { pkgJson: parsed, manifest };
|
|
33423
|
+
}
|
|
33424
|
+
function writePackageManifest(projectDir, project) {
|
|
33425
|
+
const next = {
|
|
33426
|
+
...project.pkgJson,
|
|
33427
|
+
lotics: {
|
|
33428
|
+
...isPlainObject(project.pkgJson.lotics) ? project.pkgJson.lotics : {},
|
|
33429
|
+
package: project.manifest
|
|
33430
|
+
}
|
|
33431
|
+
};
|
|
33432
|
+
writeFileAtomic(
|
|
33433
|
+
packageJsonPath(projectDir),
|
|
33434
|
+
new TextEncoder().encode(JSON.stringify(next, null, 2) + "\n")
|
|
33435
|
+
);
|
|
33436
|
+
}
|
|
33437
|
+
function sanitizePackageJsonForSource(pkgJson) {
|
|
33438
|
+
const lotics = isPlainObject(pkgJson.lotics) ? pkgJson.lotics : {};
|
|
33439
|
+
const pkg2 = isPlainObject(lotics.package) ? lotics.package : {};
|
|
33440
|
+
const { dev: _dev, ...pkgWithoutDev } = pkg2;
|
|
33441
|
+
return {
|
|
33442
|
+
...pkgJson,
|
|
33443
|
+
lotics: { ...lotics, package: pkgWithoutDev }
|
|
33444
|
+
};
|
|
33445
|
+
}
|
|
33446
|
+
function draftPackageProjectFromApp(appPkgJson, args) {
|
|
33447
|
+
const { lotics: _appManifest, ...rest } = appPkgJson;
|
|
33448
|
+
return {
|
|
33449
|
+
pkgJson: rest,
|
|
33450
|
+
manifest: { id: null, name: args.name, description: args.description, version: null, dev: {} }
|
|
33451
|
+
};
|
|
33452
|
+
}
|
|
33453
|
+
function parseAdoptBindingFile(raw, expectedAppId) {
|
|
33454
|
+
if (!isPlainObject(raw) || typeof raw.app_id !== "string" || typeof raw.workspace_id !== "string" || !isPlainObject(raw.binding)) {
|
|
33455
|
+
throw new Error(
|
|
33456
|
+
"Malformed .lotics/adopt_binding.json \u2014 expected { app_id, workspace_id, binding }."
|
|
33457
|
+
);
|
|
33458
|
+
}
|
|
33459
|
+
if (raw.app_id !== expectedAppId) {
|
|
33460
|
+
throw new Error(
|
|
33461
|
+
`.lotics/adopt_binding.json records app ${raw.app_id}, but you are adopting ${expectedAppId}. A binding maps one app's concrete ids and must never be applied to another \u2014 run "lotics package extract ${expectedAppId}" to produce the right pin.`
|
|
33462
|
+
);
|
|
33463
|
+
}
|
|
33464
|
+
return {
|
|
33465
|
+
app_id: raw.app_id,
|
|
33466
|
+
workspace_id: raw.workspace_id,
|
|
33467
|
+
binding: raw.binding
|
|
33468
|
+
};
|
|
33469
|
+
}
|
|
33470
|
+
function formatExtractReport(report) {
|
|
33471
|
+
const order = ["error", "warning", "info"];
|
|
33472
|
+
const lines = order.flatMap(
|
|
33473
|
+
(severity) => report.filter((f) => f.severity === severity).map((f) => ` [${f.severity}] ${f.area}: ${f.message}`)
|
|
33474
|
+
);
|
|
33475
|
+
return { lines, hasError: report.some((f) => f.severity === "error") };
|
|
33476
|
+
}
|
|
33477
|
+
function readContract(projectDir) {
|
|
33478
|
+
const contractPath = path7.join(projectDir, CONTRACT_FILE);
|
|
33479
|
+
if (!fs6.existsSync(contractPath)) {
|
|
33480
|
+
throw new Error(
|
|
33481
|
+
`No ${CONTRACT_FILE} in ${projectDir}. A package project declares its data model there.`
|
|
33482
|
+
);
|
|
33483
|
+
}
|
|
33484
|
+
return JSON.parse(fs6.readFileSync(contractPath, "utf-8"));
|
|
33485
|
+
}
|
|
33486
|
+
var SOURCE_STAGE_EXCLUDES = /* @__PURE__ */ new Set([
|
|
33487
|
+
"node_modules",
|
|
33488
|
+
"dist",
|
|
33489
|
+
".lotics",
|
|
33490
|
+
".git",
|
|
33491
|
+
"bundle.tar.gz",
|
|
33492
|
+
"package.json"
|
|
33493
|
+
]);
|
|
33494
|
+
function stagePackageSource(projectDir, sourceStage) {
|
|
33495
|
+
const project = readPackageProject(projectDir);
|
|
33496
|
+
fs6.mkdirSync(sourceStage, { recursive: true });
|
|
33497
|
+
for (const entry of fs6.readdirSync(projectDir)) {
|
|
33498
|
+
if (SOURCE_STAGE_EXCLUDES.has(entry) || entry.endsWith(".tsbuildinfo")) continue;
|
|
33499
|
+
fs6.cpSync(path7.join(projectDir, entry), path7.join(sourceStage, entry), { recursive: true });
|
|
33500
|
+
}
|
|
33501
|
+
fs6.writeFileSync(
|
|
33502
|
+
path7.join(sourceStage, "package.json"),
|
|
33503
|
+
JSON.stringify(sanitizePackageJsonForSource(project.pkgJson), null, 2) + "\n"
|
|
33504
|
+
);
|
|
33505
|
+
}
|
|
33506
|
+
async function buildPackageBundle(projectDir) {
|
|
33507
|
+
console.error("Building...");
|
|
33508
|
+
await runNpm(["run", "build"], projectDir);
|
|
33509
|
+
const distDir = path7.join(projectDir, "dist");
|
|
33510
|
+
if (!fs6.existsSync(distDir)) {
|
|
33511
|
+
throw new Error(
|
|
33512
|
+
`Build did not produce a dist/ directory in ${projectDir}. Check that 'npm run build' is configured correctly.`
|
|
33513
|
+
);
|
|
33514
|
+
}
|
|
33515
|
+
const stage = fs6.mkdtempSync(path7.join(tmpdir2(), "lotics-pkg-"));
|
|
33516
|
+
const bundlePath = path7.join(tmpdir2(), `lotics-bundle-${Date.now()}.tar.gz`);
|
|
33517
|
+
try {
|
|
33518
|
+
console.error("Packaging source...");
|
|
33519
|
+
const sourceStage = path7.join(stage, "source");
|
|
33520
|
+
stagePackageSource(projectDir, sourceStage);
|
|
33521
|
+
await runTar(["-czf", path7.join(stage, "source.tar.gz"), "-C", sourceStage, "."], projectDir);
|
|
33522
|
+
console.error("Packaging dist...");
|
|
33523
|
+
await runTar(["-czf", path7.join(stage, "dist.tar.gz"), "-C", distDir, "."], projectDir);
|
|
33524
|
+
console.error("Bundling...");
|
|
33525
|
+
await runTar(["-czf", bundlePath, "source.tar.gz", "dist.tar.gz"], stage);
|
|
33526
|
+
return fs6.readFileSync(bundlePath);
|
|
33527
|
+
} finally {
|
|
33528
|
+
fs6.rmSync(stage, { recursive: true, force: true });
|
|
33529
|
+
if (fs6.existsSync(bundlePath)) fs6.unlinkSync(bundlePath);
|
|
33530
|
+
}
|
|
33531
|
+
}
|
|
33532
|
+
function starterContract(name) {
|
|
33533
|
+
return {
|
|
33534
|
+
entities: [
|
|
33535
|
+
{
|
|
33536
|
+
alias: "item",
|
|
33537
|
+
label: name,
|
|
33538
|
+
description: `Records managed by the ${name} package.`,
|
|
33539
|
+
fields: [
|
|
33540
|
+
{ alias: "name", label: "Name", type: "text", required: true },
|
|
33541
|
+
{ alias: "notes", label: "Notes", type: "text" },
|
|
33542
|
+
{
|
|
33543
|
+
alias: "status",
|
|
33544
|
+
label: "Status",
|
|
33545
|
+
type: "select",
|
|
33546
|
+
options: [
|
|
33547
|
+
{ alias: "open", label: "Open", color: "blue" },
|
|
33548
|
+
{ alias: "done", label: "Done", color: "green" }
|
|
33549
|
+
]
|
|
33550
|
+
}
|
|
33551
|
+
]
|
|
33552
|
+
}
|
|
33553
|
+
],
|
|
33554
|
+
roles: [],
|
|
33555
|
+
templates: [],
|
|
33556
|
+
queries: [
|
|
33557
|
+
{
|
|
33558
|
+
alias: "items",
|
|
33559
|
+
ast: {
|
|
33560
|
+
kind: "from_table",
|
|
33561
|
+
from_entity: "item",
|
|
33562
|
+
sort: [{ field_key: "name", order: "asc" }]
|
|
33563
|
+
}
|
|
33564
|
+
}
|
|
33565
|
+
],
|
|
33566
|
+
workflows: [],
|
|
33567
|
+
agents: [],
|
|
33568
|
+
config: [
|
|
33569
|
+
{ alias: "heading", label: "List heading", type: "text", default: name }
|
|
33570
|
+
]
|
|
33571
|
+
};
|
|
33572
|
+
}
|
|
33573
|
+
async function packageNew(args) {
|
|
33574
|
+
const targetPath = path7.resolve(args.targetPath ?? appDirName(args.name));
|
|
33575
|
+
if (fs6.existsSync(targetPath) && fs6.readdirSync(targetPath).length > 0) {
|
|
33576
|
+
throw new Error(`Target directory ${targetPath} is not empty.`);
|
|
33577
|
+
}
|
|
33578
|
+
fs6.mkdirSync(targetPath, { recursive: true });
|
|
33579
|
+
const files = buildStarterTemplate({
|
|
33580
|
+
app_name: args.name,
|
|
33581
|
+
app_id: "",
|
|
33582
|
+
workspace_id: ""
|
|
33583
|
+
});
|
|
33584
|
+
for (const file of files) {
|
|
33585
|
+
const fullPath = path7.join(targetPath, file.path);
|
|
33586
|
+
if (file.path === "package.json") {
|
|
33587
|
+
const pkg2 = JSON.parse(file.content);
|
|
33588
|
+
const manifest = {
|
|
33589
|
+
id: null,
|
|
33590
|
+
name: args.name,
|
|
33591
|
+
description: null,
|
|
33592
|
+
version: null,
|
|
33593
|
+
dev: {}
|
|
33594
|
+
};
|
|
33595
|
+
pkg2.lotics = { package: manifest };
|
|
33596
|
+
fs6.mkdirSync(path7.dirname(fullPath), { recursive: true });
|
|
33597
|
+
fs6.writeFileSync(fullPath, JSON.stringify(pkg2, null, 2) + "\n");
|
|
33598
|
+
continue;
|
|
33599
|
+
}
|
|
33600
|
+
fs6.mkdirSync(path7.dirname(fullPath), { recursive: true });
|
|
33601
|
+
fs6.writeFileSync(fullPath, file.content);
|
|
33602
|
+
}
|
|
33603
|
+
fs6.writeFileSync(
|
|
33604
|
+
path7.join(targetPath, CONTRACT_FILE),
|
|
33605
|
+
JSON.stringify(starterContract(args.name), null, 2) + "\n"
|
|
33606
|
+
);
|
|
33607
|
+
console.error(`Scaffolded ${files.length + 1} files into ${targetPath}`);
|
|
33608
|
+
console.error("Installing npm dependencies...");
|
|
33609
|
+
await runNpm(["install"], targetPath);
|
|
33610
|
+
console.error(`
|
|
33611
|
+
Ready. Next steps:`);
|
|
33612
|
+
console.error(` cd ${path7.relative(process.cwd(), targetPath) || "."}`);
|
|
33613
|
+
console.error(` # edit contract.json + src/App.tsx, then run it against a dev workspace:`);
|
|
33614
|
+
console.error(` lotics workspace create "<name> dev" --dev`);
|
|
33615
|
+
console.error(` lotics package dev --workspace <dev_ws>`);
|
|
33616
|
+
}
|
|
33617
|
+
async function packageBuild(args) {
|
|
33618
|
+
const projectDir = path7.resolve(args.projectDir ?? process.cwd());
|
|
33619
|
+
readPackageProject(projectDir);
|
|
33620
|
+
const bundle = await buildPackageBundle(projectDir);
|
|
33621
|
+
const out = path7.join(projectDir, "bundle.tar.gz");
|
|
33622
|
+
fs6.writeFileSync(out, bundle);
|
|
33623
|
+
console.error(`Built ${out} (${(bundle.byteLength / 1024).toFixed(1)} KB)`);
|
|
33624
|
+
}
|
|
33625
|
+
async function publishVersion(client, projectDir, opts) {
|
|
33626
|
+
const project = readPackageProject(projectDir);
|
|
33627
|
+
const contract = readContract(projectDir);
|
|
33628
|
+
let packageId = project.manifest.id;
|
|
33629
|
+
if (packageId === null) {
|
|
33630
|
+
const pkg2 = await client.createAppPackage({
|
|
33631
|
+
name: project.manifest.name,
|
|
33632
|
+
description: project.manifest.description
|
|
33633
|
+
});
|
|
33634
|
+
packageId = pkg2.id;
|
|
33635
|
+
project.manifest.id = packageId;
|
|
33636
|
+
writePackageManifest(projectDir, project);
|
|
33637
|
+
console.error(`Created package ${pkg2.name} (${pkg2.id}).`);
|
|
33638
|
+
}
|
|
33639
|
+
const bundle = await buildPackageBundle(projectDir);
|
|
33640
|
+
console.error("Publishing version...");
|
|
33641
|
+
const version = await client.publishAppPackageVersion(packageId, {
|
|
33642
|
+
contract,
|
|
33643
|
+
bundle,
|
|
33644
|
+
changelog: opts.changelog ?? null
|
|
33645
|
+
});
|
|
33646
|
+
project.manifest.version = version.version;
|
|
33647
|
+
writePackageManifest(projectDir, project);
|
|
33648
|
+
return { package_id: packageId, version: version.version };
|
|
33649
|
+
}
|
|
33650
|
+
async function packagePublish(client, args) {
|
|
33651
|
+
const projectDir = path7.resolve(args.projectDir ?? process.cwd());
|
|
33652
|
+
const { package_id, version } = await publishVersion(client, projectDir, {
|
|
33653
|
+
changelog: args.changelog
|
|
33654
|
+
});
|
|
33655
|
+
console.error(`Published ${package_id} v${version}.`);
|
|
33656
|
+
console.error(` Install it: lotics package install ${package_id} --version ${version}`);
|
|
33657
|
+
}
|
|
33658
|
+
function assertDevWorkspace(workspace) {
|
|
33659
|
+
if (workspace.is_dev === true) return;
|
|
33660
|
+
throw new Error(
|
|
33661
|
+
`Workspace "${workspace.name}" (${workspace.id}) is not a dev workspace. "lotics package dev"/"sync" scaffold package tables into the target workspace, so it must be a throwaway dev workspace. Create one with "lotics workspace create <name> --dev" and pass --workspace <dev_ws>.`
|
|
33662
|
+
);
|
|
33663
|
+
}
|
|
33664
|
+
async function syncToDevWorkspace(client, projectDir) {
|
|
33665
|
+
const devWorkspaceId = client.getWorkspaceId();
|
|
33666
|
+
if (!devWorkspaceId) {
|
|
33667
|
+
throw new Error(
|
|
33668
|
+
"No dev workspace selected. Pass --workspace <dev_ws> (a workspace created with --dev)."
|
|
33669
|
+
);
|
|
33670
|
+
}
|
|
33671
|
+
const workspace = await client.getWorkspaceInfo(devWorkspaceId);
|
|
33672
|
+
if (!workspace) {
|
|
33673
|
+
throw new Error(
|
|
33674
|
+
`Workspace ${devWorkspaceId} is not accessible with these credentials. Pass --workspace <dev_ws> (a workspace created with --dev).`
|
|
33675
|
+
);
|
|
33676
|
+
}
|
|
33677
|
+
assertDevWorkspace(workspace);
|
|
33678
|
+
const { package_id, version } = await publishVersion(client, projectDir, {
|
|
33679
|
+
changelog: "dev sync"
|
|
33680
|
+
});
|
|
33681
|
+
const project = readPackageProject(projectDir);
|
|
33682
|
+
const existing = project.manifest.dev[devWorkspaceId];
|
|
33683
|
+
let appId;
|
|
33684
|
+
if (existing) {
|
|
33685
|
+
console.error(`Upgrading dev installation ${existing.app_id} \u2192 v${version}...`);
|
|
33686
|
+
const app = await client.upgradeAppPackage(existing.app_id, { version });
|
|
33687
|
+
appId = app.id;
|
|
33688
|
+
} else {
|
|
33689
|
+
console.error(`Installing ${package_id} v${version} into dev workspace ${devWorkspaceId}...`);
|
|
33690
|
+
const app = await client.installAppPackage(package_id, { version });
|
|
33691
|
+
appId = app.id;
|
|
33692
|
+
}
|
|
33693
|
+
project.manifest.dev[devWorkspaceId] = { app_id: appId, version };
|
|
33694
|
+
writePackageManifest(projectDir, project);
|
|
33695
|
+
const installed = await client.getApp(appId);
|
|
33696
|
+
return { app_id: appId, app_name: installed.name, workspace_id: devWorkspaceId, version };
|
|
33697
|
+
}
|
|
33698
|
+
async function packageSync(client, args) {
|
|
33699
|
+
const projectDir = path7.resolve(args.projectDir ?? process.cwd());
|
|
33700
|
+
const result = await syncToDevWorkspace(client, projectDir);
|
|
33701
|
+
console.error(
|
|
33702
|
+
`Synced ${result.app_name} v${result.version} \u2192 ${result.app_id} (workspace ${result.workspace_id}).`
|
|
33703
|
+
);
|
|
33704
|
+
}
|
|
33705
|
+
async function packageDev(client, args) {
|
|
33706
|
+
const projectDir = path7.resolve(args.projectDir ?? process.cwd());
|
|
33707
|
+
const synced = await syncToDevWorkspace(client, projectDir);
|
|
33708
|
+
const handle = await startDevServer({
|
|
33709
|
+
projectDir,
|
|
33710
|
+
app_id: synced.app_id,
|
|
33711
|
+
app_name: synced.app_name,
|
|
33712
|
+
workspace_id: synced.workspace_id,
|
|
33713
|
+
api_url: client.baseUrl,
|
|
33714
|
+
port: args.port,
|
|
33715
|
+
vitePort: args.vitePort,
|
|
33716
|
+
client
|
|
33717
|
+
});
|
|
33718
|
+
await handle.ready;
|
|
33719
|
+
const url = `http://localhost:${handle.port}`;
|
|
33720
|
+
console.error(`
|
|
33721
|
+
lotics package dev`);
|
|
33722
|
+
console.error(` package: ${synced.app_name} (dev installation ${synced.app_id} v${synced.version})`);
|
|
33723
|
+
console.error(` workspace: ${synced.workspace_id} (dev)`);
|
|
33724
|
+
if (client.viewAsMemberId) {
|
|
33725
|
+
console.error(` view as: ${client.viewAsMemberId}`);
|
|
33726
|
+
}
|
|
33727
|
+
console.error(` vite: http://localhost:${handle.vitePort}/`);
|
|
33728
|
+
console.error(` open: ${url}`);
|
|
33729
|
+
console.error(` rpc: ${client.baseUrl} (via Bearer API key)
|
|
33730
|
+
`);
|
|
33731
|
+
console.error(` Edit contract.json then re-run "lotics package dev" / "lotics package sync" to migrate.`);
|
|
33732
|
+
console.error(` Ctrl-C to stop.
|
|
33733
|
+
`);
|
|
33734
|
+
openBrowser(url);
|
|
33735
|
+
await new Promise((resolve2) => {
|
|
33736
|
+
const onSig = () => {
|
|
33737
|
+
process.off("SIGINT", onSig);
|
|
33738
|
+
process.off("SIGTERM", onSig);
|
|
33739
|
+
resolve2();
|
|
33740
|
+
};
|
|
33741
|
+
process.on("SIGINT", onSig);
|
|
33742
|
+
process.on("SIGTERM", onSig);
|
|
33743
|
+
});
|
|
33744
|
+
console.error("\nStopping\u2026");
|
|
33745
|
+
await handle.stop();
|
|
33746
|
+
}
|
|
33747
|
+
async function packageReset(client, args) {
|
|
33748
|
+
const projectDir = path7.resolve(args.projectDir ?? process.cwd());
|
|
33749
|
+
const devWorkspaceId = client.getWorkspaceId();
|
|
33750
|
+
if (!devWorkspaceId) {
|
|
33751
|
+
throw new Error(
|
|
33752
|
+
"No dev workspace selected. Pass --workspace <dev_ws> (a workspace created with --dev)."
|
|
33753
|
+
);
|
|
33754
|
+
}
|
|
33755
|
+
const { manifest } = readPackageProject(projectDir);
|
|
33756
|
+
const pin = manifest.dev[devWorkspaceId];
|
|
33757
|
+
if (!pin) {
|
|
33758
|
+
throw new Error(
|
|
33759
|
+
`No dev installation recorded for workspace ${devWorkspaceId}. Run "lotics package dev --workspace ${devWorkspaceId}" first.`
|
|
33760
|
+
);
|
|
33761
|
+
}
|
|
33762
|
+
console.error(`Resetting dev installation ${pin.app_id} in workspace ${devWorkspaceId}...`);
|
|
33763
|
+
const app = await client.resetAppPackage(pin.app_id);
|
|
33764
|
+
console.error(`Reset ${app.name} \u2192 ${app.id}. The scaffolded tables were dropped and re-created clean.`);
|
|
33765
|
+
}
|
|
33766
|
+
function resolveInstallationAppId(client, explicit) {
|
|
33767
|
+
if (explicit) return explicit;
|
|
33768
|
+
const workspaceId = client.getWorkspaceId();
|
|
33769
|
+
if (!workspaceId) {
|
|
33770
|
+
throw new Error("Pass an app id (lotics package doctor <app_id>) or select a workspace.");
|
|
33771
|
+
}
|
|
33772
|
+
let manifest;
|
|
33773
|
+
try {
|
|
33774
|
+
({ manifest } = readPackageProject(path7.resolve(process.cwd())));
|
|
33775
|
+
} catch {
|
|
33776
|
+
throw new Error(
|
|
33777
|
+
"No app id given and the current directory is not a package project. Pass one explicitly: lotics package doctor <app_id>."
|
|
33778
|
+
);
|
|
33779
|
+
}
|
|
33780
|
+
const pin = manifest.dev[workspaceId];
|
|
33781
|
+
if (!pin) {
|
|
33782
|
+
throw new Error(
|
|
33783
|
+
`No app id given and no dev installation recorded for workspace ${workspaceId}. Pass one explicitly: lotics package doctor <app_id>.`
|
|
33784
|
+
);
|
|
33785
|
+
}
|
|
33786
|
+
return pin.app_id;
|
|
33787
|
+
}
|
|
33788
|
+
function parseResolveFlags(resolve2) {
|
|
33789
|
+
const resolutions = {};
|
|
33790
|
+
for (const entry of resolve2) {
|
|
33791
|
+
const eq = entry.indexOf("=");
|
|
33792
|
+
if (eq <= 0 || eq === entry.length - 1) {
|
|
33793
|
+
throw new Error(
|
|
33794
|
+
`Invalid --resolve "${entry}" \u2014 expected <key>=recreate|revert|keep or <key>=<existing_id>.`
|
|
33795
|
+
);
|
|
33796
|
+
}
|
|
33797
|
+
const key = entry.slice(0, eq);
|
|
33798
|
+
const value = entry.slice(eq + 1);
|
|
33799
|
+
resolutions[key] = value === "recreate" || value === "revert" || value === "keep" ? value : { bind_to: value };
|
|
33800
|
+
}
|
|
33801
|
+
return resolutions;
|
|
33802
|
+
}
|
|
33803
|
+
async function packageDoctor(client, args) {
|
|
33804
|
+
const app_id = resolveInstallationAppId(client, args.app_id);
|
|
33805
|
+
const health = await client.getAppPackageHealth(app_id);
|
|
33806
|
+
console.error(`${health.package_name} \u2014 installation ${health.app_id}`);
|
|
33807
|
+
console.error(
|
|
33808
|
+
` Installed: v${health.installed_version} Latest: v${health.latest_version}` + (health.update_available ? " \u2192 update available" : "")
|
|
33809
|
+
);
|
|
33810
|
+
if (health.drift.length === 0) {
|
|
33811
|
+
console.error(" Binding: healthy \u2014 every bound alias resolves.");
|
|
33812
|
+
} else {
|
|
33813
|
+
console.error(` Binding drift (${health.drift.length}):`);
|
|
33814
|
+
for (const d of health.drift) {
|
|
33815
|
+
console.error(` - ${d.namespace}.${d.alias} \u2192 ${d.id} (missing from the workspace)`);
|
|
33816
|
+
}
|
|
33817
|
+
console.error(
|
|
33818
|
+
` Resolve while upgrading:
|
|
33819
|
+
lotics package upgrade ${app_id} --resolve <namespace.alias>=recreate (or =<existing_id> to re-point)`
|
|
33820
|
+
);
|
|
33821
|
+
process.exitCode = 1;
|
|
33822
|
+
}
|
|
33823
|
+
if (health.modified.length === 0) {
|
|
33824
|
+
console.error(" Package artifacts: pristine \u2014 no local edits an upgrade would revert.");
|
|
33825
|
+
} else {
|
|
33826
|
+
console.error(` Locally modified package artifacts (${health.modified.length}):`);
|
|
33827
|
+
for (const m of health.modified) {
|
|
33828
|
+
console.error(` - ${m.kind}.${m.alias} (an upgrade overwrites this unless kept)`);
|
|
33829
|
+
}
|
|
33830
|
+
console.error(
|
|
33831
|
+
` Consent while upgrading:
|
|
33832
|
+
lotics package upgrade ${app_id} --resolve <kind.alias>=revert (or =keep to retain the edit)`
|
|
33833
|
+
);
|
|
33834
|
+
process.exitCode = 1;
|
|
33835
|
+
}
|
|
33836
|
+
if (health.update_available) {
|
|
33837
|
+
console.error(` Upgrade: lotics package upgrade ${app_id}`);
|
|
33838
|
+
}
|
|
33839
|
+
}
|
|
33840
|
+
async function packageUpgrade(client, args) {
|
|
33841
|
+
const preview = await client.previewAppPackageUpgrade(args.app_id, {
|
|
33842
|
+
...args.version !== void 0 ? { version: args.version } : {}
|
|
33843
|
+
});
|
|
33844
|
+
console.error(
|
|
33845
|
+
`Upgrade v${preview.from_version} \u2192 v${preview.to_version}` + (preview.changelog ? ` \u2014 ${preview.changelog}` : "")
|
|
33846
|
+
);
|
|
33847
|
+
const summarize = (sets) => Object.entries(sets).filter(([, entries]) => entries.length > 0).map(([kind, entries]) => `${entries.length} ${kind}`).join(", ");
|
|
33848
|
+
const added = summarize(preview.diff.added);
|
|
33849
|
+
const removed = summarize(preview.diff.removed);
|
|
33850
|
+
if (added) console.error(` Adds: ${added}`);
|
|
33851
|
+
if (removed) console.error(` Unbinds (workspace data kept): ${removed}`);
|
|
33852
|
+
if (preview.diff.breaking.length > 0) {
|
|
33853
|
+
console.error(
|
|
33854
|
+
" Breaking contract changes \u2014 NOT resolvable via --resolve (a bound alias's shape changed):"
|
|
33855
|
+
);
|
|
33856
|
+
for (const b of preview.diff.breaking) {
|
|
33857
|
+
console.error(` - ${b.entity}.${b.alias} (${b.kind}): ${b.from} \u2192 ${b.to}`);
|
|
33858
|
+
}
|
|
33859
|
+
console.error(
|
|
33860
|
+
` There is no in-flow resolution for this class. The package author must publish a version
|
|
33861
|
+
that keeps these aliases' shape stable, or eject this app (severing the package link)
|
|
33862
|
+
before making the change: lotics package eject ${args.app_id}`
|
|
33863
|
+
);
|
|
33864
|
+
process.exit(1);
|
|
33865
|
+
}
|
|
33866
|
+
const unresolvedDrift = preview.drift.filter(
|
|
33867
|
+
(d) => args.resolutions[`${d.namespace}.${d.alias}`] === void 0
|
|
33868
|
+
);
|
|
33869
|
+
const unresolvedModified = preview.modified.filter(
|
|
33870
|
+
(m) => args.resolutions[`${m.kind}.${m.alias}`] === void 0
|
|
33871
|
+
);
|
|
33872
|
+
if (unresolvedDrift.length > 0 || unresolvedModified.length > 0) {
|
|
33873
|
+
if (unresolvedDrift.length > 0) {
|
|
33874
|
+
console.error(" Binding drift must be resolved before upgrading:");
|
|
33875
|
+
for (const d of unresolvedDrift) {
|
|
33876
|
+
console.error(
|
|
33877
|
+
` --resolve ${d.namespace}.${d.alias}=recreate (or =<existing_id> to re-point; was ${d.id})`
|
|
33878
|
+
);
|
|
33879
|
+
}
|
|
33880
|
+
}
|
|
33881
|
+
if (unresolvedModified.length > 0) {
|
|
33882
|
+
console.error(" Locally modified package artifacts need consent before upgrading:");
|
|
33883
|
+
for (const m of unresolvedModified) {
|
|
33884
|
+
console.error(
|
|
33885
|
+
` --resolve ${m.kind}.${m.alias}=revert (overwrite the local edit; or =keep to retain it)`
|
|
33886
|
+
);
|
|
33887
|
+
}
|
|
33888
|
+
}
|
|
33889
|
+
process.exit(1);
|
|
33890
|
+
}
|
|
33891
|
+
const app = await client.upgradeAppPackage(args.app_id, {
|
|
33892
|
+
...args.version !== void 0 ? { version: args.version } : {},
|
|
33893
|
+
...Object.keys(args.resolutions).length > 0 ? { resolutions: args.resolutions } : {}
|
|
33894
|
+
});
|
|
33895
|
+
console.error(`Upgraded ${app.name} \u2192 v${app.package_version} (${app.id}).`);
|
|
33896
|
+
}
|
|
33897
|
+
async function packageInstall(client, args) {
|
|
33898
|
+
const pkg2 = await client.getAppPackage(args.package_id);
|
|
33899
|
+
console.error(
|
|
33900
|
+
pkg2.is_official ? `Installing ${pkg2.name} \u2014 official Lotics package...` : `Installing ${pkg2.name} \u2014 third-party package (runs under your authority once installed)...`
|
|
33901
|
+
);
|
|
33902
|
+
const app = await client.installAppPackage(args.package_id, {
|
|
33903
|
+
...args.version !== void 0 ? { version: args.version } : {}
|
|
33904
|
+
});
|
|
33905
|
+
const versionLabel = app.package_version !== null ? `v${app.package_version}` : "(unknown version)";
|
|
33906
|
+
console.error(
|
|
33907
|
+
`Installed ${app.name} ${versionLabel} \u2192 ${app.id} (workspace ${app.workspace_id}).`
|
|
33908
|
+
);
|
|
33909
|
+
console.error(" The data model, queries, workflows, and agents are live.");
|
|
33910
|
+
console.error(" Pull it for local editing: lotics app pull " + app.id);
|
|
33911
|
+
}
|
|
33912
|
+
async function packageEject(client, args) {
|
|
33913
|
+
const app = await client.ejectAppPackage(args.app_id);
|
|
33914
|
+
console.error(`Ejected ${app.name} \u2192 ${app.id} (workspace ${app.workspace_id}).`);
|
|
33915
|
+
console.error(" The package link is severed \u2014 it's now a normal bespoke app and can no longer be upgraded.");
|
|
33916
|
+
console.error(" Its data model, queries, workflows, and templates are unchanged.");
|
|
33917
|
+
console.error(" Pull the pinned source for local editing: lotics app pull " + app.id);
|
|
33918
|
+
}
|
|
33919
|
+
async function packageExtract(client, args) {
|
|
33920
|
+
const app = await client.getApp(args.app_id);
|
|
33921
|
+
if (!app.current_version_id) {
|
|
33922
|
+
throw new Error(
|
|
33923
|
+
`App ${app.id} has no deployed version \u2014 extract stages its source archive, so deploy it first.`
|
|
33924
|
+
);
|
|
33925
|
+
}
|
|
33926
|
+
const extracted = await client.extractAppPackage(args.app_id);
|
|
33927
|
+
const { lines, hasError } = formatExtractReport(extracted.report);
|
|
33928
|
+
if (lines.length > 0) {
|
|
33929
|
+
console.error(`Extraction report (${extracted.report.length}):`);
|
|
33930
|
+
for (const line of lines) console.error(line);
|
|
33931
|
+
} else {
|
|
33932
|
+
console.error("Extraction report: no findings.");
|
|
33933
|
+
}
|
|
33934
|
+
const targetPath = path7.resolve(args.targetPath ?? appDirName(app.name));
|
|
33935
|
+
if (fs6.existsSync(targetPath) && fs6.readdirSync(targetPath).length > 0) {
|
|
33936
|
+
throw new Error(`Target directory ${targetPath} is not empty.`);
|
|
33937
|
+
}
|
|
33938
|
+
fs6.mkdirSync(targetPath, { recursive: true });
|
|
33939
|
+
const version = await client.getAppVersion(app.id, app.current_version_id);
|
|
33940
|
+
const sourceUrl = await client.getAppVersionSourceUrl(app.id, version.id);
|
|
33941
|
+
const tmpFile = path7.join(tmpdir2(), `lotics-extract-${app.id}-${Date.now()}.tar.gz`);
|
|
33942
|
+
console.error("Downloading source archive...");
|
|
33943
|
+
await client.downloadFile(sourceUrl, tmpFile);
|
|
33944
|
+
try {
|
|
33945
|
+
console.error(`Extracting to ${targetPath}...`);
|
|
33946
|
+
await runTar(["-xzf", tmpFile, "-C", targetPath], targetPath);
|
|
33947
|
+
} finally {
|
|
33948
|
+
if (fs6.existsSync(tmpFile)) fs6.unlinkSync(tmpFile);
|
|
33949
|
+
}
|
|
33950
|
+
const appPkgJson = JSON.parse(
|
|
33951
|
+
fs6.readFileSync(packageJsonPath(targetPath), "utf-8")
|
|
33952
|
+
);
|
|
33953
|
+
writePackageManifest(
|
|
33954
|
+
targetPath,
|
|
33955
|
+
draftPackageProjectFromApp(appPkgJson, { name: app.name, description: null })
|
|
33956
|
+
);
|
|
33957
|
+
fs6.writeFileSync(
|
|
33958
|
+
path7.join(targetPath, CONTRACT_FILE),
|
|
33959
|
+
JSON.stringify(extracted.contract, null, 2) + "\n"
|
|
33960
|
+
);
|
|
33961
|
+
console.error(`Wrote ${CONTRACT_FILE}`);
|
|
33962
|
+
for (const tf of extracted.template_files) {
|
|
33963
|
+
const dest = path7.join(targetPath, tf.bytes_ref);
|
|
33964
|
+
fs6.mkdirSync(path7.dirname(dest), { recursive: true });
|
|
33965
|
+
const { path: downloaded } = await client.downloadFileById(tf.file_id, path7.dirname(dest));
|
|
33966
|
+
if (path7.resolve(downloaded) !== path7.resolve(dest)) fs6.renameSync(downloaded, dest);
|
|
33967
|
+
console.error(`Wrote ${tf.bytes_ref}`);
|
|
33968
|
+
}
|
|
33969
|
+
const dotLotics = path7.join(targetPath, ".lotics");
|
|
33970
|
+
fs6.mkdirSync(dotLotics, { recursive: true });
|
|
33971
|
+
fs6.writeFileSync(
|
|
33972
|
+
path7.join(dotLotics, ADOPT_BINDING_FILE),
|
|
33973
|
+
JSON.stringify(
|
|
33974
|
+
{ app_id: app.id, workspace_id: app.workspace_id, binding: extracted.binding },
|
|
33975
|
+
null,
|
|
33976
|
+
2
|
|
33977
|
+
) + "\n"
|
|
33978
|
+
);
|
|
33979
|
+
console.error(`Wrote .lotics/${ADOPT_BINDING_FILE}`);
|
|
33980
|
+
console.error("Installing npm dependencies...");
|
|
33981
|
+
await runNpm(["install"], targetPath);
|
|
33982
|
+
if (hasError) {
|
|
33983
|
+
console.error(
|
|
33984
|
+
"\nExtraction produced error findings \u2014 the draft is written but NOT publishable as-is."
|
|
33985
|
+
);
|
|
33986
|
+
console.error(
|
|
33987
|
+
"Fix the reported items above, then publish (which re-validates the contract)."
|
|
33988
|
+
);
|
|
33989
|
+
process.exitCode = 1;
|
|
33990
|
+
return;
|
|
33991
|
+
}
|
|
33992
|
+
console.error("\nDraft package project written. Next steps:");
|
|
33993
|
+
console.error(` cd ${path7.relative(process.cwd(), targetPath) || "."}`);
|
|
33994
|
+
console.error(" # review the aliases in contract.json, then:");
|
|
33995
|
+
console.error(` lotics package publish -m "v1"`);
|
|
33996
|
+
console.error(` lotics package adopt ${app.id} (from this project, same workspace)`);
|
|
33997
|
+
}
|
|
33998
|
+
async function packageAdopt(client, args) {
|
|
33999
|
+
const projectDir = path7.resolve(args.projectDir ?? process.cwd());
|
|
34000
|
+
const { manifest } = readPackageProject(projectDir);
|
|
34001
|
+
if (manifest.id === null) {
|
|
34002
|
+
throw new Error(
|
|
34003
|
+
"This package project has never been published (no package id). Publish first:\n lotics package publish"
|
|
34004
|
+
);
|
|
34005
|
+
}
|
|
34006
|
+
const version = args.version ?? manifest.version;
|
|
34007
|
+
if (version === null) {
|
|
34008
|
+
throw new Error(
|
|
34009
|
+
"No version to adopt \u2014 publish this project first (lotics package publish) or pass --version N."
|
|
34010
|
+
);
|
|
34011
|
+
}
|
|
34012
|
+
const bindingPath = path7.join(projectDir, ".lotics", ADOPT_BINDING_FILE);
|
|
34013
|
+
if (!fs6.existsSync(bindingPath)) {
|
|
34014
|
+
throw new Error(
|
|
34015
|
+
`No .lotics/${ADOPT_BINDING_FILE} in ${projectDir}. Adopt binds the origin app that "lotics package extract" recorded \u2014 run extract to produce this project.`
|
|
34016
|
+
);
|
|
34017
|
+
}
|
|
34018
|
+
const pin = parseAdoptBindingFile(
|
|
34019
|
+
JSON.parse(fs6.readFileSync(bindingPath, "utf-8")),
|
|
34020
|
+
args.app_id
|
|
34021
|
+
);
|
|
34022
|
+
const app = await client.adoptAppPackage(args.app_id, {
|
|
34023
|
+
package_id: manifest.id,
|
|
34024
|
+
version,
|
|
34025
|
+
binding: pin.binding
|
|
34026
|
+
});
|
|
34027
|
+
console.error(
|
|
34028
|
+
`Adopted ${app.name} \u2192 installation of ${app.package_id} v${app.package_version}.`
|
|
34029
|
+
);
|
|
34030
|
+
console.error(` Verify the installation: lotics package doctor ${app.id}`);
|
|
34031
|
+
}
|
|
34032
|
+
async function packageFleetUpgrade(client, args) {
|
|
34033
|
+
const result = await client.fleetUpgradeAppPackage(args.package_id, {
|
|
34034
|
+
...args.version !== void 0 ? { version: args.version } : {}
|
|
34035
|
+
});
|
|
34036
|
+
console.error(`Fleet upgrade of ${result.package_id} \u2192 v${result.target_version}:`);
|
|
34037
|
+
if (result.installations.length === 0) {
|
|
34038
|
+
console.error(" No installations of this package in your organization.");
|
|
34039
|
+
return;
|
|
34040
|
+
}
|
|
34041
|
+
const counts = { upgraded: 0, up_to_date: 0, skipped: 0, failed: 0 };
|
|
34042
|
+
for (const inst of result.installations) {
|
|
34043
|
+
counts[inst.outcome]++;
|
|
34044
|
+
const from = inst.from_version === null ? "?" : `v${inst.from_version}`;
|
|
34045
|
+
const line = ` [${inst.outcome}] ${inst.workspace_name} \u2014 ${inst.app_name} (${from} \u2192 v${result.target_version})`;
|
|
34046
|
+
if (inst.outcome === "skipped" && inst.blockers) {
|
|
34047
|
+
console.error(
|
|
34048
|
+
`${line}: breaking=${inst.blockers.breaking} drift=${inst.blockers.drift} modified=${inst.blockers.modified}`
|
|
34049
|
+
);
|
|
34050
|
+
console.error(
|
|
34051
|
+
` resolve via: lotics package upgrade ${inst.app_id} --version ${result.target_version} ...`
|
|
34052
|
+
);
|
|
34053
|
+
} else if (inst.message) {
|
|
34054
|
+
console.error(`${line}: ${inst.message}`);
|
|
34055
|
+
} else {
|
|
34056
|
+
console.error(line);
|
|
34057
|
+
}
|
|
34058
|
+
}
|
|
34059
|
+
console.error(
|
|
34060
|
+
`Done: ${counts.upgraded} upgraded, ${counts.up_to_date} already current, ${counts.skipped} skipped, ${counts.failed} failed.`
|
|
34061
|
+
);
|
|
34062
|
+
if (counts.skipped > 0 || counts.failed > 0) {
|
|
34063
|
+
process.exitCode = 1;
|
|
34064
|
+
}
|
|
34065
|
+
}
|
|
34066
|
+
|
|
33165
34067
|
// src/args.ts
|
|
33166
34068
|
function parseArgs(argv) {
|
|
33167
34069
|
const flags = {
|
|
@@ -33178,9 +34080,12 @@ function parseArgs(argv) {
|
|
|
33178
34080
|
message: void 0,
|
|
33179
34081
|
local: false,
|
|
33180
34082
|
all: false,
|
|
34083
|
+
dev: false,
|
|
33181
34084
|
yes: false,
|
|
33182
34085
|
printCreated: false,
|
|
33183
34086
|
cleanup: false,
|
|
34087
|
+
packageVersion: void 0,
|
|
34088
|
+
resolve: [],
|
|
33184
34089
|
version: false,
|
|
33185
34090
|
help: false
|
|
33186
34091
|
};
|
|
@@ -33231,6 +34136,9 @@ function parseArgs(argv) {
|
|
|
33231
34136
|
case "--local":
|
|
33232
34137
|
flags.local = true;
|
|
33233
34138
|
break;
|
|
34139
|
+
case "--dev":
|
|
34140
|
+
flags.dev = true;
|
|
34141
|
+
break;
|
|
33234
34142
|
case "--all":
|
|
33235
34143
|
flags.all = true;
|
|
33236
34144
|
break;
|
|
@@ -33245,10 +34153,32 @@ function parseArgs(argv) {
|
|
|
33245
34153
|
case "--cleanup":
|
|
33246
34154
|
flags.cleanup = true;
|
|
33247
34155
|
break;
|
|
34156
|
+
case "--resolve": {
|
|
34157
|
+
const value = argv[++i2];
|
|
34158
|
+
if (value === void 0 || value.startsWith("-")) {
|
|
34159
|
+
throw new Error(
|
|
34160
|
+
"--resolve requires a value: <key>=recreate|revert|keep or <key>=<existing_id> (drift: <namespace.alias>; modified core: <kind>.<alias>)."
|
|
34161
|
+
);
|
|
34162
|
+
}
|
|
34163
|
+
flags.resolve.push(value);
|
|
34164
|
+
break;
|
|
34165
|
+
}
|
|
33248
34166
|
case "--version":
|
|
33249
|
-
case "-v":
|
|
33250
|
-
|
|
34167
|
+
case "-v": {
|
|
34168
|
+
const next = argv[i2 + 1];
|
|
34169
|
+
if (command === "package") {
|
|
34170
|
+
if (next === void 0 || next.startsWith("-")) {
|
|
34171
|
+
throw new Error(
|
|
34172
|
+
"--version requires a version number for package commands (e.g. --version 2)."
|
|
34173
|
+
);
|
|
34174
|
+
}
|
|
34175
|
+
flags.packageVersion = next;
|
|
34176
|
+
i2++;
|
|
34177
|
+
} else {
|
|
34178
|
+
flags.version = true;
|
|
34179
|
+
}
|
|
33251
34180
|
break;
|
|
34181
|
+
}
|
|
33252
34182
|
case "--help":
|
|
33253
34183
|
case "-h":
|
|
33254
34184
|
flags.help = true;
|
|
@@ -33299,35 +34229,7 @@ async function ingestJsonArgs(opts) {
|
|
|
33299
34229
|
}
|
|
33300
34230
|
|
|
33301
34231
|
// src/xlsx.ts
|
|
33302
|
-
import
|
|
33303
|
-
|
|
33304
|
-
// src/file_command_io.ts
|
|
33305
|
-
import fs5 from "node:fs";
|
|
33306
|
-
import path6 from "node:path";
|
|
33307
|
-
var CliError = class extends Error {
|
|
33308
|
-
constructor(message) {
|
|
33309
|
-
super(message);
|
|
33310
|
-
this.name = "CliError";
|
|
33311
|
-
}
|
|
33312
|
-
};
|
|
33313
|
-
function fail(message) {
|
|
33314
|
-
throw new CliError(message);
|
|
33315
|
-
}
|
|
33316
|
-
function writeFileAtomic(filePath, bytes) {
|
|
33317
|
-
const dir = path6.dirname(path6.resolve(filePath));
|
|
33318
|
-
const tmp = path6.join(dir, `.${path6.basename(filePath)}.${process.pid}.${Date.now()}.tmp`);
|
|
33319
|
-
fs5.writeFileSync(tmp, bytes);
|
|
33320
|
-
try {
|
|
33321
|
-
fs5.renameSync(tmp, filePath);
|
|
33322
|
-
} catch (error) {
|
|
33323
|
-
try {
|
|
33324
|
-
fs5.unlinkSync(tmp);
|
|
33325
|
-
} catch {
|
|
33326
|
-
}
|
|
33327
|
-
throw error;
|
|
33328
|
-
}
|
|
33329
|
-
console.error(`Wrote ${filePath}`);
|
|
33330
|
-
}
|
|
34232
|
+
import fs7 from "node:fs";
|
|
33331
34233
|
|
|
33332
34234
|
// ../xlsx/node_modules/fast-xml-parser/src/util.js
|
|
33333
34235
|
var nameStartChar = ":A-Za-z_\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD";
|
|
@@ -36104,16 +37006,16 @@ var MatcherView = class {
|
|
|
36104
37006
|
* @returns {string|undefined}
|
|
36105
37007
|
*/
|
|
36106
37008
|
getCurrentTag() {
|
|
36107
|
-
const
|
|
36108
|
-
return
|
|
37009
|
+
const path9 = this._matcher.path;
|
|
37010
|
+
return path9.length > 0 ? path9[path9.length - 1].tag : void 0;
|
|
36109
37011
|
}
|
|
36110
37012
|
/**
|
|
36111
37013
|
* Get current namespace.
|
|
36112
37014
|
* @returns {string|undefined}
|
|
36113
37015
|
*/
|
|
36114
37016
|
getCurrentNamespace() {
|
|
36115
|
-
const
|
|
36116
|
-
return
|
|
37017
|
+
const path9 = this._matcher.path;
|
|
37018
|
+
return path9.length > 0 ? path9[path9.length - 1].namespace : void 0;
|
|
36117
37019
|
}
|
|
36118
37020
|
/**
|
|
36119
37021
|
* Get current node's attribute value.
|
|
@@ -36121,9 +37023,9 @@ var MatcherView = class {
|
|
|
36121
37023
|
* @returns {*}
|
|
36122
37024
|
*/
|
|
36123
37025
|
getAttrValue(attrName) {
|
|
36124
|
-
const
|
|
36125
|
-
if (
|
|
36126
|
-
return
|
|
37026
|
+
const path9 = this._matcher.path;
|
|
37027
|
+
if (path9.length === 0) return void 0;
|
|
37028
|
+
return path9[path9.length - 1].values?.[attrName];
|
|
36127
37029
|
}
|
|
36128
37030
|
/**
|
|
36129
37031
|
* Check if current node has an attribute.
|
|
@@ -36131,9 +37033,9 @@ var MatcherView = class {
|
|
|
36131
37033
|
* @returns {boolean}
|
|
36132
37034
|
*/
|
|
36133
37035
|
hasAttr(attrName) {
|
|
36134
|
-
const
|
|
36135
|
-
if (
|
|
36136
|
-
const current =
|
|
37036
|
+
const path9 = this._matcher.path;
|
|
37037
|
+
if (path9.length === 0) return false;
|
|
37038
|
+
const current = path9[path9.length - 1];
|
|
36137
37039
|
return current.values !== void 0 && attrName in current.values;
|
|
36138
37040
|
}
|
|
36139
37041
|
/**
|
|
@@ -36141,18 +37043,18 @@ var MatcherView = class {
|
|
|
36141
37043
|
* @returns {number}
|
|
36142
37044
|
*/
|
|
36143
37045
|
getPosition() {
|
|
36144
|
-
const
|
|
36145
|
-
if (
|
|
36146
|
-
return
|
|
37046
|
+
const path9 = this._matcher.path;
|
|
37047
|
+
if (path9.length === 0) return -1;
|
|
37048
|
+
return path9[path9.length - 1].position ?? 0;
|
|
36147
37049
|
}
|
|
36148
37050
|
/**
|
|
36149
37051
|
* Get current node's repeat counter (occurrence count of this tag name).
|
|
36150
37052
|
* @returns {number}
|
|
36151
37053
|
*/
|
|
36152
37054
|
getCounter() {
|
|
36153
|
-
const
|
|
36154
|
-
if (
|
|
36155
|
-
return
|
|
37055
|
+
const path9 = this._matcher.path;
|
|
37056
|
+
if (path9.length === 0) return -1;
|
|
37057
|
+
return path9[path9.length - 1].counter ?? 0;
|
|
36156
37058
|
}
|
|
36157
37059
|
/**
|
|
36158
37060
|
* Get current node's sibling index (alias for getPosition).
|
|
@@ -41608,10 +42510,10 @@ function resolveRelativePath(basePath, relativePath) {
|
|
|
41608
42510
|
}
|
|
41609
42511
|
return resolved.join("/");
|
|
41610
42512
|
}
|
|
41611
|
-
function findZipEntry(zipEntries,
|
|
41612
|
-
if (zipEntries[
|
|
41613
|
-
if (zipEntries[`xl/${
|
|
41614
|
-
const noSlash =
|
|
42513
|
+
function findZipEntry(zipEntries, path9) {
|
|
42514
|
+
if (zipEntries[path9]) return zipEntries[path9];
|
|
42515
|
+
if (zipEntries[`xl/${path9}`]) return zipEntries[`xl/${path9}`];
|
|
42516
|
+
const noSlash = path9.replace(/^\//, "");
|
|
41615
42517
|
if (zipEntries[noSlash]) return zipEntries[noSlash];
|
|
41616
42518
|
return void 0;
|
|
41617
42519
|
}
|
|
@@ -43334,9 +44236,9 @@ function parseExcelFromZip(zip, options) {
|
|
|
43334
44236
|
}
|
|
43335
44237
|
return result;
|
|
43336
44238
|
}
|
|
43337
|
-
function findEntry(zip,
|
|
43338
|
-
if (zip[
|
|
43339
|
-
const lower =
|
|
44239
|
+
function findEntry(zip, path9) {
|
|
44240
|
+
if (zip[path9]) return zip[path9];
|
|
44241
|
+
const lower = path9.toLowerCase();
|
|
43340
44242
|
for (const key of Object.keys(zip)) {
|
|
43341
44243
|
if (key.toLowerCase() === lower) return zip[key];
|
|
43342
44244
|
}
|
|
@@ -43947,12 +44849,12 @@ function extractPivotRoundTripInfo(originalZip) {
|
|
|
43947
44849
|
if (paths.length > 0) pivotTablesBySheetIndex.set(i2, paths);
|
|
43948
44850
|
}
|
|
43949
44851
|
const pivotXmlPaths = [];
|
|
43950
|
-
for (const
|
|
43951
|
-
if (
|
|
43952
|
-
pivotXmlPaths.push(
|
|
44852
|
+
for (const path9 of Object.keys(originalZip)) {
|
|
44853
|
+
if (path9.startsWith("xl/pivotTables/") && path9.endsWith(".xml")) {
|
|
44854
|
+
pivotXmlPaths.push(path9);
|
|
43953
44855
|
}
|
|
43954
|
-
if (
|
|
43955
|
-
pivotXmlPaths.push(
|
|
44856
|
+
if (path9.startsWith("xl/pivotCache/") && path9.endsWith(".xml")) {
|
|
44857
|
+
pivotXmlPaths.push(path9);
|
|
43956
44858
|
}
|
|
43957
44859
|
}
|
|
43958
44860
|
return { cachesByWorkbook, pivotTablesBySheetIndex, pivotXmlPaths };
|
|
@@ -43960,15 +44862,15 @@ function extractPivotRoundTripInfo(originalZip) {
|
|
|
43960
44862
|
function decode(bytes) {
|
|
43961
44863
|
return new TextDecoder().decode(bytes);
|
|
43962
44864
|
}
|
|
43963
|
-
function pivotContentTypeFor(
|
|
43964
|
-
if (
|
|
43965
|
-
return `<Override PartName="/${
|
|
44865
|
+
function pivotContentTypeFor(path9) {
|
|
44866
|
+
if (path9.startsWith("xl/pivotTables/") && path9.endsWith(".xml")) {
|
|
44867
|
+
return `<Override PartName="/${path9}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml"/>`;
|
|
43966
44868
|
}
|
|
43967
|
-
if (
|
|
43968
|
-
return `<Override PartName="/${
|
|
44869
|
+
if (path9.includes("/pivotCacheDefinition") && path9.endsWith(".xml")) {
|
|
44870
|
+
return `<Override PartName="/${path9}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml"/>`;
|
|
43969
44871
|
}
|
|
43970
|
-
if (
|
|
43971
|
-
return `<Override PartName="/${
|
|
44872
|
+
if (path9.includes("/pivotCacheRecords") && path9.endsWith(".xml")) {
|
|
44873
|
+
return `<Override PartName="/${path9}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml"/>`;
|
|
43972
44874
|
}
|
|
43973
44875
|
return void 0;
|
|
43974
44876
|
}
|
|
@@ -44040,13 +44942,13 @@ function exportWorkbook(workbook, originalZip) {
|
|
|
44040
44942
|
regeneratedPaths.add(`xl/worksheets/sheet${i2 + 1}.xml`);
|
|
44041
44943
|
regeneratedPaths.add(`xl/worksheets/_rels/sheet${i2 + 1}.xml.rels`);
|
|
44042
44944
|
}
|
|
44043
|
-
for (const
|
|
44044
|
-
if (
|
|
44045
|
-
regeneratedPaths.add(
|
|
44945
|
+
for (const path9 of Object.keys(originalZip)) {
|
|
44946
|
+
if (path9.startsWith("xl/drawings/") || path9.startsWith("xl/charts/") || path9.startsWith("xl/tables/") || path9.startsWith("xl/media/")) {
|
|
44947
|
+
regeneratedPaths.add(path9);
|
|
44046
44948
|
}
|
|
44047
44949
|
}
|
|
44048
|
-
for (const [
|
|
44049
|
-
if (!regeneratedPaths.has(
|
|
44950
|
+
for (const [path9, data] of Object.entries(originalZip)) {
|
|
44951
|
+
if (!regeneratedPaths.has(path9)) entries[path9] = data;
|
|
44050
44952
|
}
|
|
44051
44953
|
}
|
|
44052
44954
|
const sharedStrings = buildSharedStrings(workbook);
|
|
@@ -44061,8 +44963,8 @@ function exportWorkbook(workbook, originalZip) {
|
|
|
44061
44963
|
buildWorkbookRels(workbook, pivotInfo)
|
|
44062
44964
|
);
|
|
44063
44965
|
const extraContentTypes = [];
|
|
44064
|
-
for (const
|
|
44065
|
-
const ct = pivotContentTypeFor(
|
|
44966
|
+
for (const path9 of pivotInfo.pivotXmlPaths) {
|
|
44967
|
+
const ct = pivotContentTypeFor(path9);
|
|
44066
44968
|
if (ct) extraContentTypes.push(ct);
|
|
44067
44969
|
}
|
|
44068
44970
|
let globalChartIndex = 1;
|
|
@@ -45823,14 +46725,14 @@ var DependencyGraph = class {
|
|
|
45823
46725
|
stack.push({ cell: p, path: [cell, p] });
|
|
45824
46726
|
}
|
|
45825
46727
|
while (stack.length > 0) {
|
|
45826
|
-
const { cell: current, path:
|
|
45827
|
-
if (current === cell) return
|
|
46728
|
+
const { cell: current, path: path9 } = stack.pop();
|
|
46729
|
+
if (current === cell) return path9;
|
|
45828
46730
|
if (visited.has(current)) continue;
|
|
45829
46731
|
visited.add(current);
|
|
45830
46732
|
const nextPrec = this.precedents.get(current);
|
|
45831
46733
|
if (!nextPrec) continue;
|
|
45832
46734
|
for (const p of nextPrec) {
|
|
45833
|
-
stack.push({ cell: p, path: [...
|
|
46735
|
+
stack.push({ cell: p, path: [...path9, p] });
|
|
45834
46736
|
}
|
|
45835
46737
|
}
|
|
45836
46738
|
return null;
|
|
@@ -46821,8 +47723,8 @@ function adjustMergedCellsForColDelete(sheet, atCol, count) {
|
|
|
46821
47723
|
|
|
46822
47724
|
// src/xlsx.ts
|
|
46823
47725
|
function loadFile(filePath) {
|
|
46824
|
-
if (!
|
|
46825
|
-
const buffer =
|
|
47726
|
+
if (!fs7.existsSync(filePath)) fail(`File not found: ${filePath}`);
|
|
47727
|
+
const buffer = fs7.readFileSync(filePath);
|
|
46826
47728
|
const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
|
|
46827
47729
|
const originalZip = unzipXlsx(arrayBuffer);
|
|
46828
47730
|
const parsed = parseExcelBuffer(arrayBuffer);
|
|
@@ -46872,7 +47774,7 @@ function lazyEngine(workbook) {
|
|
|
46872
47774
|
};
|
|
46873
47775
|
}
|
|
46874
47776
|
function readToJson(filePath) {
|
|
46875
|
-
const buffer =
|
|
47777
|
+
const buffer = fs7.readFileSync(filePath);
|
|
46876
47778
|
const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
|
|
46877
47779
|
const parsed = parseExcelBuffer(arrayBuffer);
|
|
46878
47780
|
return {
|
|
@@ -47262,7 +48164,7 @@ async function runXlsxCommand(subcommand, toolArgs, restArgs) {
|
|
|
47262
48164
|
}
|
|
47263
48165
|
|
|
47264
48166
|
// src/docx.ts
|
|
47265
|
-
import
|
|
48167
|
+
import fs8 from "node:fs";
|
|
47266
48168
|
|
|
47267
48169
|
// ../docx/src/parse/parser.ts
|
|
47268
48170
|
var import_jszip = __toESM(require_lib3(), 1);
|
|
@@ -49033,11 +49935,11 @@ async function parseDocx(buffer) {
|
|
|
49033
49935
|
const zip = await import_jszip.default.loadAsync(buffer);
|
|
49034
49936
|
const parts = /* @__PURE__ */ new Map();
|
|
49035
49937
|
const filePromises = [];
|
|
49036
|
-
zip.forEach((
|
|
49938
|
+
zip.forEach((path9, file) => {
|
|
49037
49939
|
if (file.dir) return;
|
|
49038
49940
|
filePromises.push(
|
|
49039
49941
|
file.async("uint8array").then((bytes) => {
|
|
49040
|
-
parts.set(
|
|
49942
|
+
parts.set(path9, bytes);
|
|
49041
49943
|
})
|
|
49042
49944
|
);
|
|
49043
49945
|
});
|
|
@@ -49266,9 +50168,9 @@ var DEFAULT_PARTS = [
|
|
|
49266
50168
|
["word/theme/theme1.xml", () => encoder.encode(THEME_XML)]
|
|
49267
50169
|
];
|
|
49268
50170
|
function fillDefaultParts(parts) {
|
|
49269
|
-
for (const [
|
|
49270
|
-
if (!parts.has(
|
|
49271
|
-
parts.set(
|
|
50171
|
+
for (const [path9, makeBytes] of DEFAULT_PARTS) {
|
|
50172
|
+
if (!parts.has(path9)) {
|
|
50173
|
+
parts.set(path9, makeBytes());
|
|
49272
50174
|
}
|
|
49273
50175
|
}
|
|
49274
50176
|
return parts;
|
|
@@ -49278,9 +50180,9 @@ function fillDefaultParts(parts) {
|
|
|
49278
50180
|
async function serializeDocx(doc) {
|
|
49279
50181
|
const zip = new import_jszip2.default();
|
|
49280
50182
|
const parts = fillDefaultParts(new Map(doc.parts));
|
|
49281
|
-
for (const [
|
|
49282
|
-
if (
|
|
49283
|
-
zip.file(
|
|
50183
|
+
for (const [path9, bytes] of parts) {
|
|
50184
|
+
if (path9 === "word/document.xml") continue;
|
|
50185
|
+
zip.file(path9, bytes);
|
|
49284
50186
|
}
|
|
49285
50187
|
const documentXml = serializeDocumentXml(doc);
|
|
49286
50188
|
zip.file("word/document.xml", documentXml);
|
|
@@ -49366,8 +50268,8 @@ var DEFAULT_DOC_ATTRS = {
|
|
|
49366
50268
|
"@_xmlns:w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
|
|
49367
50269
|
};
|
|
49368
50270
|
async function loadFile2(filePath) {
|
|
49369
|
-
if (!
|
|
49370
|
-
const bytes =
|
|
50271
|
+
if (!fs8.existsSync(filePath)) fail(`File not found: ${filePath}`);
|
|
50272
|
+
const bytes = fs8.readFileSync(filePath);
|
|
49371
50273
|
return parseDocx(new Uint8Array(bytes));
|
|
49372
50274
|
}
|
|
49373
50275
|
async function writeDoc(filePath, doc) {
|
|
@@ -49655,6 +50557,198 @@ async function runDocxCommand(subcommand, toolArgs, restArgs) {
|
|
|
49655
50557
|
}
|
|
49656
50558
|
}
|
|
49657
50559
|
|
|
50560
|
+
// src/preview.ts
|
|
50561
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
50562
|
+
import { createServer } from "node:http";
|
|
50563
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync, mkdtempSync, rmSync, readdirSync } from "node:fs";
|
|
50564
|
+
import { tmpdir as tmpdir3 } from "node:os";
|
|
50565
|
+
import { join, dirname, resolve, extname, basename } from "node:path";
|
|
50566
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
50567
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
50568
|
+
var HERE = dirname(fileURLToPath2(import.meta.url));
|
|
50569
|
+
function fail2(msg) {
|
|
50570
|
+
console.error(msg);
|
|
50571
|
+
process.exit(1);
|
|
50572
|
+
}
|
|
50573
|
+
function findChrome() {
|
|
50574
|
+
const env = process.env.LOTICS_CHROME || process.env.CHROME_PATH;
|
|
50575
|
+
if (env && existsSync(env)) return env;
|
|
50576
|
+
const home = process.env.HOME || "";
|
|
50577
|
+
const pwDir = process.platform === "darwin" ? `${home}/Library/Caches/ms-playwright` : `${home}/.cache/ms-playwright`;
|
|
50578
|
+
if (existsSync(pwDir)) {
|
|
50579
|
+
const rel = process.platform === "darwin" ? ["chrome-mac/Chromium.app/Contents/MacOS/Chromium"] : ["chrome-linux/chrome", "chrome-linux/headless_shell"];
|
|
50580
|
+
const revs = readdirSync(pwDir).filter((n) => n.startsWith("chromium-") || n.startsWith("chromium_headless_shell-")).sort().reverse();
|
|
50581
|
+
for (const rev2 of revs) {
|
|
50582
|
+
for (const r of rel) {
|
|
50583
|
+
const bin = join(pwDir, rev2, r);
|
|
50584
|
+
if (existsSync(bin)) return bin;
|
|
50585
|
+
}
|
|
50586
|
+
}
|
|
50587
|
+
}
|
|
50588
|
+
const systemPaths = [
|
|
50589
|
+
"/usr/bin/google-chrome",
|
|
50590
|
+
"/usr/bin/google-chrome-stable",
|
|
50591
|
+
"/usr/bin/chromium",
|
|
50592
|
+
"/usr/bin/chromium-browser",
|
|
50593
|
+
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
50594
|
+
"/Applications/Chromium.app/Contents/MacOS/Chromium"
|
|
50595
|
+
];
|
|
50596
|
+
return systemPaths.find((p) => existsSync(p)) ?? null;
|
|
50597
|
+
}
|
|
50598
|
+
async function cdpConnect(wsUrl) {
|
|
50599
|
+
const ws = new WebSocket(wsUrl);
|
|
50600
|
+
await new Promise((res, rej) => {
|
|
50601
|
+
ws.onopen = () => res();
|
|
50602
|
+
ws.onerror = () => rej(new Error("CDP websocket failed to open"));
|
|
50603
|
+
});
|
|
50604
|
+
let id = 0;
|
|
50605
|
+
const pending = /* @__PURE__ */ new Map();
|
|
50606
|
+
ws.onclose = () => {
|
|
50607
|
+
for (const done of pending.values()) done(Promise.reject(new Error("CDP connection closed (Chrome exited?)")));
|
|
50608
|
+
pending.clear();
|
|
50609
|
+
};
|
|
50610
|
+
ws.onmessage = (e) => {
|
|
50611
|
+
const m = JSON.parse(String(e.data));
|
|
50612
|
+
if (m.id != null && pending.has(m.id)) {
|
|
50613
|
+
const done = pending.get(m.id);
|
|
50614
|
+
pending.delete(m.id);
|
|
50615
|
+
done(m.error ? Promise.reject(new Error(m.error.message)) : m.result);
|
|
50616
|
+
}
|
|
50617
|
+
};
|
|
50618
|
+
const send = (method, params = {}) => new Promise((res) => {
|
|
50619
|
+
const i2 = ++id;
|
|
50620
|
+
pending.set(i2, (r) => res(r));
|
|
50621
|
+
ws.send(JSON.stringify({ id: i2, method, params }));
|
|
50622
|
+
});
|
|
50623
|
+
return { send, close: () => ws.close() };
|
|
50624
|
+
}
|
|
50625
|
+
async function runPreviewCommand(filePath, flags) {
|
|
50626
|
+
if (!filePath) fail2("Usage: lotics preview <file.docx|.xlsx> [--out <file.png>]");
|
|
50627
|
+
const abs = resolve(filePath);
|
|
50628
|
+
if (!existsSync(abs)) fail2(`File not found: ${abs}`);
|
|
50629
|
+
const ext = extname(abs).toLowerCase();
|
|
50630
|
+
const type = ext === ".docx" ? "docx" : ext === ".xlsx" || ext === ".xls" || ext === ".csv" ? "xlsx" : null;
|
|
50631
|
+
if (!type) fail2(`Unsupported file type "${ext}" \u2014 preview supports .docx and .xlsx/.csv. (PDFs open directly \u2014 no preview needed.)`);
|
|
50632
|
+
if (typeof WebSocket === "undefined") {
|
|
50633
|
+
fail2("lotics preview needs Node 22+ (it drives Chrome over CDP via the built-in WebSocket). Upgrade Node and retry.");
|
|
50634
|
+
}
|
|
50635
|
+
const bundlePath = join(HERE, "..", "render_page.js");
|
|
50636
|
+
if (!existsSync(bundlePath)) fail2(`Render bundle missing at ${bundlePath} \u2014 reinstall @lotics/cli (build step failed).`);
|
|
50637
|
+
const chrome = findChrome();
|
|
50638
|
+
if (!chrome) {
|
|
50639
|
+
fail2(
|
|
50640
|
+
"No Chrome/Chromium found. Set CHROME_PATH to a Chrome binary, or install one:\n npx playwright install chromium (then it's auto-detected)\n or install Google Chrome / Chromium via your package manager."
|
|
50641
|
+
);
|
|
50642
|
+
}
|
|
50643
|
+
const b64 = readFileSync2(abs).toString("base64");
|
|
50644
|
+
const bundle = readFileSync2(bundlePath, "utf8");
|
|
50645
|
+
const html = `<!doctype html><html><head><meta charset="utf-8"><style>body{margin:0;background:#fff;font-family:sans-serif}#root{padding:20px;max-width:1240px;margin:0 auto}</style></head><body><div id="root"></div><script>window.__LOTICS_RENDER=${JSON.stringify({ type, b64 })}</script><script src="/render_page.js"></script></body></html>`;
|
|
50646
|
+
const server = createServer((req, res) => {
|
|
50647
|
+
if (req.url === "/render_page.js") {
|
|
50648
|
+
res.writeHead(200, { "content-type": "application/javascript" });
|
|
50649
|
+
res.end(bundle);
|
|
50650
|
+
} else {
|
|
50651
|
+
res.writeHead(200, { "content-type": "text/html" });
|
|
50652
|
+
res.end(html);
|
|
50653
|
+
}
|
|
50654
|
+
});
|
|
50655
|
+
await new Promise((r) => server.listen(0, "127.0.0.1", () => r()));
|
|
50656
|
+
const httpPort = server.address().port;
|
|
50657
|
+
const udd = mkdtempSync(join(tmpdir3(), "lotics-render-"));
|
|
50658
|
+
const child = spawn3(chrome, [
|
|
50659
|
+
"--headless=new",
|
|
50660
|
+
"--disable-gpu",
|
|
50661
|
+
"--no-sandbox",
|
|
50662
|
+
"--hide-scrollbars",
|
|
50663
|
+
// Large multi-sheet renders exhaust the (often tiny) /dev/shm in containers/WSL2 and
|
|
50664
|
+
// crash the tab; back shared memory with /tmp instead.
|
|
50665
|
+
"--disable-dev-shm-usage",
|
|
50666
|
+
"--force-device-scale-factor=2",
|
|
50667
|
+
"--remote-debugging-port=0",
|
|
50668
|
+
`--user-data-dir=${udd}`,
|
|
50669
|
+
"about:blank"
|
|
50670
|
+
], { stdio: "ignore" });
|
|
50671
|
+
const cleanup = () => {
|
|
50672
|
+
try {
|
|
50673
|
+
child.kill();
|
|
50674
|
+
} catch {
|
|
50675
|
+
}
|
|
50676
|
+
try {
|
|
50677
|
+
server.close();
|
|
50678
|
+
} catch {
|
|
50679
|
+
}
|
|
50680
|
+
try {
|
|
50681
|
+
rmSync(udd, { recursive: true, force: true });
|
|
50682
|
+
} catch {
|
|
50683
|
+
}
|
|
50684
|
+
};
|
|
50685
|
+
try {
|
|
50686
|
+
let cdpPort = 0;
|
|
50687
|
+
const portFile = join(udd, "DevToolsActivePort");
|
|
50688
|
+
for (let i2 = 0; i2 < 100 && !cdpPort; i2++) {
|
|
50689
|
+
if (existsSync(portFile)) {
|
|
50690
|
+
const p = parseInt(readFileSync2(portFile, "utf8").split("\n")[0], 10);
|
|
50691
|
+
if (p) cdpPort = p;
|
|
50692
|
+
}
|
|
50693
|
+
if (!cdpPort) await sleep(100);
|
|
50694
|
+
}
|
|
50695
|
+
if (!cdpPort) throw new Error("Chrome did not expose a debugging port (launch failed?).");
|
|
50696
|
+
let target;
|
|
50697
|
+
for (let i2 = 0; i2 < 60 && !target?.webSocketDebuggerUrl; i2++) {
|
|
50698
|
+
try {
|
|
50699
|
+
const list = await (await fetch(`http://127.0.0.1:${cdpPort}/json/list`)).json();
|
|
50700
|
+
target = list.find((t) => t.type === "page");
|
|
50701
|
+
} catch {
|
|
50702
|
+
}
|
|
50703
|
+
if (!target?.webSocketDebuggerUrl) await sleep(100);
|
|
50704
|
+
}
|
|
50705
|
+
if (!target?.webSocketDebuggerUrl) throw new Error("No Chrome page target available.");
|
|
50706
|
+
const cdp = await cdpConnect(target.webSocketDebuggerUrl);
|
|
50707
|
+
await cdp.send("Page.enable");
|
|
50708
|
+
await cdp.send("Runtime.enable");
|
|
50709
|
+
await cdp.send("Page.navigate", { url: `http://127.0.0.1:${httpPort}/` });
|
|
50710
|
+
let err2;
|
|
50711
|
+
let done = false;
|
|
50712
|
+
const warnings = [];
|
|
50713
|
+
for (let i2 = 0; i2 < 200; i2++) {
|
|
50714
|
+
const r = await cdp.send("Runtime.evaluate", {
|
|
50715
|
+
expression: "({done: !!window.__loticsDone, err: window.__loticsError || '', warnings: window.__loticsWarnings || []})",
|
|
50716
|
+
returnByValue: true
|
|
50717
|
+
});
|
|
50718
|
+
const v = r.result?.value;
|
|
50719
|
+
if (v?.done) {
|
|
50720
|
+
done = true;
|
|
50721
|
+
err2 = v.err || void 0;
|
|
50722
|
+
if (v.warnings?.length) warnings.push(...v.warnings);
|
|
50723
|
+
break;
|
|
50724
|
+
}
|
|
50725
|
+
await sleep(75);
|
|
50726
|
+
}
|
|
50727
|
+
if (!done) throw new Error("Render timed out (page never signaled completion).");
|
|
50728
|
+
if (err2) throw new Error(`Render engine error: ${err2}`);
|
|
50729
|
+
const metrics = await cdp.send("Page.getLayoutMetrics");
|
|
50730
|
+
const size = metrics.cssContentSize ?? metrics.contentSize ?? { width: 1240, height: 1600 };
|
|
50731
|
+
const MAX_CAPTURE = 15e3;
|
|
50732
|
+
const w = Math.ceil(size.width);
|
|
50733
|
+
const h = Math.ceil(size.height);
|
|
50734
|
+
if (w > MAX_CAPTURE || h > MAX_CAPTURE) {
|
|
50735
|
+
warnings.push(`content ${w}\xD7${h}px clipped to ${Math.min(w, MAX_CAPTURE)}\xD7${Math.min(h, MAX_CAPTURE)}px (exceeds the ${MAX_CAPTURE}px capture limit)`);
|
|
50736
|
+
}
|
|
50737
|
+
const shot = await cdp.send("Page.captureScreenshot", {
|
|
50738
|
+
format: "png",
|
|
50739
|
+
captureBeyondViewport: true,
|
|
50740
|
+
clip: { x: 0, y: 0, width: Math.min(w, MAX_CAPTURE), height: Math.min(h, MAX_CAPTURE), scale: 1 }
|
|
50741
|
+
});
|
|
50742
|
+
cdp.close();
|
|
50743
|
+
const outPath = flags.output ? resolve(flags.output) : join(dirname(abs), basename(abs, ext) + ".png");
|
|
50744
|
+
writeFileSync(outPath, Buffer.from(shot.data, "base64"));
|
|
50745
|
+
console.log(`Rendered ${basename(abs)} \u2192 ${outPath} (${Math.min(w, MAX_CAPTURE)}\xD7${Math.min(h, MAX_CAPTURE)}, via ${basename(chrome)})`);
|
|
50746
|
+
for (const warn of warnings) console.error(` \u26A0 ${warn}`);
|
|
50747
|
+
} finally {
|
|
50748
|
+
cleanup();
|
|
50749
|
+
}
|
|
50750
|
+
}
|
|
50751
|
+
|
|
49658
50752
|
// src/cli.ts
|
|
49659
50753
|
dns.setDefaultResultOrder("ipv4first");
|
|
49660
50754
|
net2.setDefaultAutoSelectFamilyAttemptTimeout(2e3);
|
|
@@ -49701,15 +50795,12 @@ COMMANDS
|
|
|
49701
50795
|
lotics workspace select <id> Switch to a different workspace
|
|
49702
50796
|
lotics workspace create <name> Create a new workspace (admin only)
|
|
49703
50797
|
lotics workspace delete <id> --yes Delete a workspace (admin only; soft delete, recoverable)
|
|
50798
|
+
lotics workspace doctor Report dangling schema references (admin only)
|
|
49704
50799
|
lotics tools List all available tools
|
|
49705
50800
|
lotics tools <name> Show tool description and input schema
|
|
49706
50801
|
lotics run <tool> '<json>' Execute a tool
|
|
49707
50802
|
lotics run <tool> @args.json Read JSON args from a file (large payloads)
|
|
49708
50803
|
cat args.json | lotics run <tool> Read JSON args from stdin (large payloads)
|
|
49709
|
-
lotics upload <file|dir...> Upload files (directories expand to their immediate files)
|
|
49710
|
-
lotics download <file_id> Download a file by ID
|
|
49711
|
-
lotics download record <record_id> <field_key>
|
|
49712
|
-
Download all files on a record file field
|
|
49713
50804
|
lotics app create <name> [path] Create a new custom-code app + scaffold locally
|
|
49714
50805
|
lotics app pull <app_id> [path] Bootstrap full local env (source + npm install + types)
|
|
49715
50806
|
lotics app deploy -m <message> Build + upload current dir as a new version
|
|
@@ -49737,6 +50828,37 @@ COMMANDS
|
|
|
49737
50828
|
lotics app subdomain <new-subdomain> Rename the app's public <slug>.lotics.app address
|
|
49738
50829
|
lotics app rename "<new name>" Rename the app's display name (launcher title)
|
|
49739
50830
|
lotics app dev [path] Run the app locally with HMR (RPC forwarded to prod)
|
|
50831
|
+
lotics package new <name> [path] Scaffold a package project (contract + app source)
|
|
50832
|
+
lotics package build [path] Build the publishable bundle (source + dist)
|
|
50833
|
+
lotics package publish [path] Publish a new immutable package version
|
|
50834
|
+
lotics package dev [path] Sync the package into a dev workspace + run the app
|
|
50835
|
+
dev server (--workspace <dev_ws> selects it)
|
|
50836
|
+
lotics package sync [path] Re-sync (additive migrate + materialize) into a dev ws
|
|
50837
|
+
lotics package reset [path] DEV-ONLY: drop scaffolded tables + re-scaffold clean
|
|
50838
|
+
lotics package install <package> [--version N]
|
|
50839
|
+
Install an app package into this workspace
|
|
50840
|
+
(scaffolds the data model + deploys + materializes)
|
|
50841
|
+
lotics package upgrade <app_id> [--version N] [--resolve <key>=recreate|revert|keep|<id> ...]
|
|
50842
|
+
Preview-then-apply a package upgrade; refuses while
|
|
50843
|
+
any drift/modified finding lacks a --resolve, and
|
|
50844
|
+
hard-stops on breaking contract changes
|
|
50845
|
+
lotics package doctor [app_id] Installation health: version pin vs latest, binding
|
|
50846
|
+
drift, locally modified core (exit 1 on findings)
|
|
50847
|
+
lotics package rebind-role <app_id> <alias> <grp_id>
|
|
50848
|
+
Re-point a package role at a different group
|
|
50849
|
+
(re-materializes at the pinned version)
|
|
50850
|
+
lotics package eject <app_id> Sever an installation's package link
|
|
50851
|
+
(re-deploys the pinned source as a bespoke app)
|
|
50852
|
+
lotics package extract <app_id> [path] Promote a bespoke app to a draft package
|
|
50853
|
+
project (contract + binding + templates); always
|
|
50854
|
+
writes the draft, exits 1 on error findings
|
|
50855
|
+
lotics package adopt <app_id> [path] Bind the published package project onto the
|
|
50856
|
+
origin app (reads .lotics/adopt_binding.json;
|
|
50857
|
+
--version N pins a specific version)
|
|
50858
|
+
lotics package fleet-upgrade <package_id> [--version N]
|
|
50859
|
+
Upgrade EVERY installation of the package across
|
|
50860
|
+
your org: applies where the preview is clean,
|
|
50861
|
+
skips + reports findings (exit 1 unless all current)
|
|
49740
50862
|
lotics ui link <component> [--ui-src <path>] [--remove]
|
|
49741
50863
|
Dev-link @lotics/ui to packages/ui/src (Vite alias
|
|
49742
50864
|
+ tsc paths) for live HMR + typecheck. Monorepo apps
|
|
@@ -49747,6 +50869,12 @@ COMMANDS
|
|
|
49747
50869
|
lotics docx <subcommand> ... Read/write/edit .docx files on your local filesystem
|
|
49748
50870
|
(uses the bundled Lotics OOXML engine; preserves
|
|
49749
50871
|
tables, images, and unknown markup verbatim)
|
|
50872
|
+
lotics file upload <file|dir...> Upload files (alias: lotics upload)
|
|
50873
|
+
lotics file download <file_id> Download a file by ID (alias: lotics download)
|
|
50874
|
+
lotics file download record <record_id> <field_key>
|
|
50875
|
+
Download all files on a record file field
|
|
50876
|
+
lotics file preview <file> [-o png] Render a .docx/.xlsx to a PNG (frontend engines;
|
|
50877
|
+
needs a Chrome/Chromium on the machine)
|
|
49750
50878
|
|
|
49751
50879
|
FLAGS
|
|
49752
50880
|
--json Full JSON output (default is human-readable text)
|
|
@@ -49759,6 +50887,8 @@ FLAGS
|
|
|
49759
50887
|
is_current_member / row-scoping resolve to them (also
|
|
49760
50888
|
LOTICS_VIEW_AS env; admin key only; writes stay yours)
|
|
49761
50889
|
--local Pin the current directory (lotics org use / auth api-key)
|
|
50890
|
+
--dev (lotics workspace create) Mark a throwaway app-package dev
|
|
50891
|
+
workspace \u2014 required for "lotics package reset"
|
|
49762
50892
|
--all (lotics auth logout) Remove every saved credential
|
|
49763
50893
|
--version Show version
|
|
49764
50894
|
|
|
@@ -49824,10 +50954,10 @@ Resolution precedence (highest first):
|
|
|
49824
50954
|
> global active profile. LOTICS_WORKSPACE / --workspace override the workspace.`);
|
|
49825
50955
|
}
|
|
49826
50956
|
function readStdin() {
|
|
49827
|
-
return new Promise((
|
|
50957
|
+
return new Promise((resolve2, reject2) => {
|
|
49828
50958
|
const chunks = [];
|
|
49829
50959
|
process.stdin.on("data", (chunk) => chunks.push(chunk));
|
|
49830
|
-
process.stdin.on("end", () =>
|
|
50960
|
+
process.stdin.on("end", () => resolve2(Buffer.concat(chunks).toString("utf-8").trim()));
|
|
49831
50961
|
process.stdin.on("error", reject2);
|
|
49832
50962
|
});
|
|
49833
50963
|
}
|
|
@@ -49836,15 +50966,15 @@ function prompt(question) {
|
|
|
49836
50966
|
input: process.stdin,
|
|
49837
50967
|
output: process.stderr
|
|
49838
50968
|
});
|
|
49839
|
-
return new Promise((
|
|
50969
|
+
return new Promise((resolve2) => {
|
|
49840
50970
|
rl.question(question, (answer) => {
|
|
49841
50971
|
rl.close();
|
|
49842
|
-
|
|
50972
|
+
resolve2(answer.trim());
|
|
49843
50973
|
});
|
|
49844
50974
|
});
|
|
49845
50975
|
}
|
|
49846
|
-
async function publicPost(
|
|
49847
|
-
const response = await fetch(`${API_BASE_URL}${
|
|
50976
|
+
async function publicPost(path9, body) {
|
|
50977
|
+
const response = await fetch(`${API_BASE_URL}${path9}`, {
|
|
49848
50978
|
method: "POST",
|
|
49849
50979
|
headers: { "Content-Type": "application/json" },
|
|
49850
50980
|
body: JSON.stringify(body)
|
|
@@ -49990,13 +51120,13 @@ async function resolveWorkspace(client, ctx) {
|
|
|
49990
51120
|
function resolveUploadPaths(rawPaths) {
|
|
49991
51121
|
const result = [];
|
|
49992
51122
|
for (const p of rawPaths) {
|
|
49993
|
-
const resolved =
|
|
49994
|
-
const stat =
|
|
51123
|
+
const resolved = path8.resolve(p);
|
|
51124
|
+
const stat = fs9.statSync(resolved);
|
|
49995
51125
|
if (stat.isDirectory()) {
|
|
49996
|
-
const entries =
|
|
51126
|
+
const entries = fs9.readdirSync(resolved, { withFileTypes: true });
|
|
49997
51127
|
for (const entry of entries) {
|
|
49998
51128
|
if (entry.isFile()) {
|
|
49999
|
-
result.push(
|
|
51129
|
+
result.push(path8.join(resolved, entry.name));
|
|
50000
51130
|
}
|
|
50001
51131
|
}
|
|
50002
51132
|
} else {
|
|
@@ -50006,7 +51136,18 @@ function resolveUploadPaths(rawPaths) {
|
|
|
50006
51136
|
return result;
|
|
50007
51137
|
}
|
|
50008
51138
|
async function main() {
|
|
50009
|
-
const
|
|
51139
|
+
const parsed = parseArgs(process.argv.slice(2));
|
|
51140
|
+
const { restArgs, flags } = parsed;
|
|
51141
|
+
let { command, subcommand, toolArgs } = parsed;
|
|
51142
|
+
if (command === "file") {
|
|
51143
|
+
if (!subcommand) {
|
|
51144
|
+
printHelp();
|
|
51145
|
+
return;
|
|
51146
|
+
}
|
|
51147
|
+
command = subcommand;
|
|
51148
|
+
subcommand = toolArgs;
|
|
51149
|
+
toolArgs = restArgs.shift();
|
|
51150
|
+
}
|
|
50010
51151
|
if (flags.help || !command && !flags.version) {
|
|
50011
51152
|
printHelp();
|
|
50012
51153
|
return;
|
|
@@ -50100,6 +51241,10 @@ async function main() {
|
|
|
50100
51241
|
await runDocxCommand(subcommand, toolArgs, restArgs);
|
|
50101
51242
|
return;
|
|
50102
51243
|
}
|
|
51244
|
+
if (command === "preview") {
|
|
51245
|
+
await runPreviewCommand(subcommand, flags);
|
|
51246
|
+
return;
|
|
51247
|
+
}
|
|
50103
51248
|
if (command === "ui") {
|
|
50104
51249
|
if (subcommand === "link") {
|
|
50105
51250
|
const component = toolArgs;
|
|
@@ -50116,6 +51261,19 @@ async function main() {
|
|
|
50116
51261
|
console.error("Usage: lotics ui link <component> [--ui-src <abs path>] [--remove]");
|
|
50117
51262
|
process.exit(1);
|
|
50118
51263
|
}
|
|
51264
|
+
if (command === "package" && subcommand === "new") {
|
|
51265
|
+
const name = toolArgs;
|
|
51266
|
+
if (!name) {
|
|
51267
|
+
console.error("Usage: lotics package new <name> [path]");
|
|
51268
|
+
process.exit(1);
|
|
51269
|
+
}
|
|
51270
|
+
await packageNew({ name, targetPath: restArgs[0] });
|
|
51271
|
+
return;
|
|
51272
|
+
}
|
|
51273
|
+
if (command === "package" && subcommand === "build") {
|
|
51274
|
+
await packageBuild({ projectDir: toolArgs });
|
|
51275
|
+
return;
|
|
51276
|
+
}
|
|
50119
51277
|
if (command === "app" && subcommand === "workflow" && toolArgs === "check") {
|
|
50120
51278
|
await appWorkflowCheck({ alias: restArgs[0] });
|
|
50121
51279
|
return;
|
|
@@ -50190,7 +51348,7 @@ async function main() {
|
|
|
50190
51348
|
}
|
|
50191
51349
|
return;
|
|
50192
51350
|
}
|
|
50193
|
-
if (command !== "tools" && command !== "upload" && command !== "run" && command !== "download" && command !== "workspace" && command !== "app") {
|
|
51351
|
+
if (command !== "tools" && command !== "upload" && command !== "run" && command !== "download" && command !== "workspace" && command !== "app" && command !== "package") {
|
|
50194
51352
|
console.error(`Unknown command: ${command}`);
|
|
50195
51353
|
console.error('Run "lotics --help" for usage.');
|
|
50196
51354
|
process.exit(1);
|
|
@@ -50212,6 +51370,24 @@ async function main() {
|
|
|
50212
51370
|
console.error(" lotics app dev [path] Run the app locally with HMR + RPC forwarding");
|
|
50213
51371
|
process.exit(1);
|
|
50214
51372
|
}
|
|
51373
|
+
if (command === "package" && !subcommand) {
|
|
51374
|
+
console.error("Usage:");
|
|
51375
|
+
console.error(" lotics package new <name> [path] Scaffold a package project (contract + app source)");
|
|
51376
|
+
console.error(" lotics package build [path] Build the publishable bundle (source + dist)");
|
|
51377
|
+
console.error(" lotics package publish [path] [-m <changelog>] Publish a new immutable package version");
|
|
51378
|
+
console.error(" lotics package dev [path] [--workspace <ws>] Sync into a dev workspace + run the app dev server");
|
|
51379
|
+
console.error(" lotics package sync [path] [--workspace <ws>] Re-sync (additive migrate + materialize) into a dev workspace");
|
|
51380
|
+
console.error(" lotics package reset [path] [--workspace <ws>] DEV-ONLY: drop scaffolded tables + re-scaffold clean");
|
|
51381
|
+
console.error(" lotics package install <package> [--version N] Install a package into this workspace");
|
|
51382
|
+
console.error(" lotics package upgrade <app_id> [--version N] [--resolve ns.alias=recreate|<id>] Preview + apply an upgrade");
|
|
51383
|
+
console.error(" lotics package doctor [app_id] Health: version pin vs latest + binding drift");
|
|
51384
|
+
console.error(" lotics package rebind-role <app_id> <alias> <grp_id> Re-point a package role at another group");
|
|
51385
|
+
console.error(" lotics package eject <app_id> Sever an installation's package link");
|
|
51386
|
+
console.error(" lotics package extract <app_id> [path] Promote a bespoke app to a draft package project");
|
|
51387
|
+
console.error(" lotics package adopt <app_id> [path] Bind the published project onto the origin app");
|
|
51388
|
+
console.error(" lotics package fleet-upgrade <package_id> [--version N] Upgrade every org installation (clean ones apply; findings skip)");
|
|
51389
|
+
process.exit(1);
|
|
51390
|
+
}
|
|
50215
51391
|
if (command === "run" && !subcommand) {
|
|
50216
51392
|
console.error(
|
|
50217
51393
|
"Usage: lotics run <tool> '<json_args>'\n lotics run <tool> @args.json (read args from a file \u2014 for large payloads)\n cat args.json | lotics run <tool> (read args from stdin)"
|
|
@@ -50238,6 +51414,25 @@ async function main() {
|
|
|
50238
51414
|
}
|
|
50239
51415
|
const { client, ctx } = requireClient(flags);
|
|
50240
51416
|
if (command === "workspace") {
|
|
51417
|
+
if (subcommand === "doctor") {
|
|
51418
|
+
await resolveWorkspace(client, ctx);
|
|
51419
|
+
const dangling = await client.getWorkspaceDanglingReferences();
|
|
51420
|
+
if (dangling.length === 0) {
|
|
51421
|
+
console.error("Workspace references: healthy \u2014 every referenced schema id resolves.");
|
|
51422
|
+
return;
|
|
51423
|
+
}
|
|
51424
|
+
console.error(`Dangling schema references (${dangling.length}):`);
|
|
51425
|
+
for (const d of dangling) {
|
|
51426
|
+
console.error(
|
|
51427
|
+
` - ${d.referent.kind} "${d.referent.name}" (${d.referent.id}) \u2192 ${d.namespace} ${d.id} (missing)`
|
|
51428
|
+
);
|
|
51429
|
+
}
|
|
51430
|
+
console.error(
|
|
51431
|
+
"Each referent points at schema that no longer exists \u2014 edit or archive it, or recreate the missing schema."
|
|
51432
|
+
);
|
|
51433
|
+
process.exitCode = 1;
|
|
51434
|
+
return;
|
|
51435
|
+
}
|
|
50241
51436
|
const workspaces = await client.listWorkspaces();
|
|
50242
51437
|
const currentWorkspaceId = ctx.workspaceId;
|
|
50243
51438
|
if (subcommand === "select") {
|
|
@@ -50266,11 +51461,11 @@ Available workspaces:`);
|
|
|
50266
51461
|
if (subcommand === "create") {
|
|
50267
51462
|
const name = toolArgs;
|
|
50268
51463
|
if (!name) {
|
|
50269
|
-
console.error("Usage: lotics workspace create <name> [--timezone <tz>]");
|
|
51464
|
+
console.error("Usage: lotics workspace create <name> [--timezone <tz>] [--dev]");
|
|
50270
51465
|
process.exit(1);
|
|
50271
51466
|
}
|
|
50272
51467
|
const timezone = flags.timezone;
|
|
50273
|
-
const created = await client.createWorkspace({ name, timezone });
|
|
51468
|
+
const created = await client.createWorkspace({ name, timezone, is_dev: flags.dev || void 0 });
|
|
50274
51469
|
setSelectedWorkspace(created.id);
|
|
50275
51470
|
client.setWorkspaceId(created.id);
|
|
50276
51471
|
if (flags.json) {
|
|
@@ -50332,6 +51527,150 @@ Available workspaces:`);
|
|
|
50332
51527
|
return;
|
|
50333
51528
|
}
|
|
50334
51529
|
await resolveWorkspace(client, ctx);
|
|
51530
|
+
if (command === "package") {
|
|
51531
|
+
if (subcommand === "install") {
|
|
51532
|
+
const packageId = toolArgs;
|
|
51533
|
+
if (!packageId) {
|
|
51534
|
+
console.error("Usage: lotics package install <package> [--version N]");
|
|
51535
|
+
process.exit(1);
|
|
51536
|
+
}
|
|
51537
|
+
let version;
|
|
51538
|
+
if (flags.packageVersion !== void 0) {
|
|
51539
|
+
version = Number(flags.packageVersion);
|
|
51540
|
+
if (!Number.isInteger(version) || version <= 0) {
|
|
51541
|
+
console.error(`Invalid --version "${flags.packageVersion}" \u2014 expected a positive integer.`);
|
|
51542
|
+
process.exit(1);
|
|
51543
|
+
}
|
|
51544
|
+
}
|
|
51545
|
+
await packageInstall(client, { package_id: packageId, version });
|
|
51546
|
+
return;
|
|
51547
|
+
}
|
|
51548
|
+
if (subcommand === "eject") {
|
|
51549
|
+
const appId = toolArgs;
|
|
51550
|
+
if (!appId) {
|
|
51551
|
+
console.error("Usage: lotics package eject <app_id>");
|
|
51552
|
+
process.exit(1);
|
|
51553
|
+
}
|
|
51554
|
+
await packageEject(client, { app_id: appId });
|
|
51555
|
+
return;
|
|
51556
|
+
}
|
|
51557
|
+
if (subcommand === "extract") {
|
|
51558
|
+
const appId = toolArgs;
|
|
51559
|
+
if (!appId) {
|
|
51560
|
+
console.error("Usage: lotics package extract <app_id> [path]");
|
|
51561
|
+
console.error("Promotes a bespoke app to a draft package project (contract + binding + templates).");
|
|
51562
|
+
process.exit(1);
|
|
51563
|
+
}
|
|
51564
|
+
await packageExtract(client, { app_id: appId, targetPath: restArgs[0] });
|
|
51565
|
+
return;
|
|
51566
|
+
}
|
|
51567
|
+
if (subcommand === "adopt") {
|
|
51568
|
+
const appId = toolArgs;
|
|
51569
|
+
if (!appId) {
|
|
51570
|
+
console.error("Usage: lotics package adopt <app_id> [path]");
|
|
51571
|
+
console.error("Binds the published package project (this dir, or [path]) onto the origin app.");
|
|
51572
|
+
process.exit(1);
|
|
51573
|
+
}
|
|
51574
|
+
let version;
|
|
51575
|
+
if (flags.packageVersion !== void 0) {
|
|
51576
|
+
version = Number(flags.packageVersion);
|
|
51577
|
+
if (!Number.isInteger(version) || version <= 0) {
|
|
51578
|
+
console.error(`Invalid --version "${flags.packageVersion}" \u2014 expected a positive integer.`);
|
|
51579
|
+
process.exit(1);
|
|
51580
|
+
}
|
|
51581
|
+
}
|
|
51582
|
+
await packageAdopt(client, { app_id: appId, version, projectDir: restArgs[0] });
|
|
51583
|
+
return;
|
|
51584
|
+
}
|
|
51585
|
+
if (subcommand === "fleet-upgrade") {
|
|
51586
|
+
const packageId = toolArgs;
|
|
51587
|
+
if (!packageId) {
|
|
51588
|
+
console.error("Usage: lotics package fleet-upgrade <package_id> [--version N]");
|
|
51589
|
+
process.exit(1);
|
|
51590
|
+
}
|
|
51591
|
+
let version;
|
|
51592
|
+
if (flags.packageVersion !== void 0) {
|
|
51593
|
+
version = Number(flags.packageVersion);
|
|
51594
|
+
if (!Number.isInteger(version) || version <= 0) {
|
|
51595
|
+
console.error(`Invalid --version "${flags.packageVersion}" \u2014 expected a positive integer.`);
|
|
51596
|
+
process.exit(1);
|
|
51597
|
+
}
|
|
51598
|
+
}
|
|
51599
|
+
await packageFleetUpgrade(client, { package_id: packageId, version });
|
|
51600
|
+
return;
|
|
51601
|
+
}
|
|
51602
|
+
if (subcommand === "doctor") {
|
|
51603
|
+
await packageDoctor(client, { app_id: toolArgs });
|
|
51604
|
+
return;
|
|
51605
|
+
}
|
|
51606
|
+
if (subcommand === "rebind-role") {
|
|
51607
|
+
const appId = toolArgs;
|
|
51608
|
+
const [roleAlias, groupId] = restArgs;
|
|
51609
|
+
if (!appId || !roleAlias || !groupId) {
|
|
51610
|
+
console.error("Usage: lotics package rebind-role <app_id> <role_alias> <grp_id>");
|
|
51611
|
+
process.exit(1);
|
|
51612
|
+
}
|
|
51613
|
+
const result = await client.rebindAppPackageRole(appId, {
|
|
51614
|
+
role_alias: roleAlias,
|
|
51615
|
+
group_id: groupId
|
|
51616
|
+
});
|
|
51617
|
+
console.error(
|
|
51618
|
+
`Rebound role "${result.role_alias}" \u2192 ${result.group_id}` + (result.previous_group_id ? ` (was ${result.previous_group_id})` : "") + ". Package workflows/agents were re-materialized against the new group."
|
|
51619
|
+
);
|
|
51620
|
+
return;
|
|
51621
|
+
}
|
|
51622
|
+
if (subcommand === "upgrade") {
|
|
51623
|
+
const appId = toolArgs;
|
|
51624
|
+
if (!appId) {
|
|
51625
|
+
console.error(
|
|
51626
|
+
"Usage: lotics package upgrade <app_id> [--version N] [--resolve <namespace.alias>=recreate|<id> ...]"
|
|
51627
|
+
);
|
|
51628
|
+
process.exit(1);
|
|
51629
|
+
}
|
|
51630
|
+
let version;
|
|
51631
|
+
if (flags.packageVersion !== void 0) {
|
|
51632
|
+
version = Number(flags.packageVersion);
|
|
51633
|
+
if (!Number.isInteger(version) || version <= 0) {
|
|
51634
|
+
console.error(`Invalid --version "${flags.packageVersion}" \u2014 expected a positive integer.`);
|
|
51635
|
+
process.exit(1);
|
|
51636
|
+
}
|
|
51637
|
+
}
|
|
51638
|
+
await packageUpgrade(client, {
|
|
51639
|
+
app_id: appId,
|
|
51640
|
+
version,
|
|
51641
|
+
resolutions: parseResolveFlags(flags.resolve)
|
|
51642
|
+
});
|
|
51643
|
+
return;
|
|
51644
|
+
}
|
|
51645
|
+
if (subcommand === "publish") {
|
|
51646
|
+
await packagePublish(client, { projectDir: toolArgs, changelog: flags.message });
|
|
51647
|
+
return;
|
|
51648
|
+
}
|
|
51649
|
+
if (subcommand === "sync") {
|
|
51650
|
+
await packageSync(client, { projectDir: toolArgs });
|
|
51651
|
+
return;
|
|
51652
|
+
}
|
|
51653
|
+
if (subcommand === "reset") {
|
|
51654
|
+
await packageReset(client, { projectDir: toolArgs });
|
|
51655
|
+
return;
|
|
51656
|
+
}
|
|
51657
|
+
if (subcommand === "dev") {
|
|
51658
|
+
const projectDir = toolArgs;
|
|
51659
|
+
let port;
|
|
51660
|
+
let vitePort;
|
|
51661
|
+
for (const a of restArgs) {
|
|
51662
|
+
const portMatch = /^--port=(\d+)$/.exec(a);
|
|
51663
|
+
const vitePortMatch = /^--vite-port=(\d+)$/.exec(a);
|
|
51664
|
+
if (portMatch) port = Number(portMatch[1]);
|
|
51665
|
+
else if (vitePortMatch) vitePort = Number(vitePortMatch[1]);
|
|
51666
|
+
}
|
|
51667
|
+
await packageDev(client, { projectDir, port, vitePort });
|
|
51668
|
+
return;
|
|
51669
|
+
}
|
|
51670
|
+
console.error(`Unknown package subcommand: ${subcommand}`);
|
|
51671
|
+
console.error("Run 'lotics package' for usage.");
|
|
51672
|
+
process.exit(1);
|
|
51673
|
+
}
|
|
50335
51674
|
if (command === "app") {
|
|
50336
51675
|
if (subcommand === "create") {
|
|
50337
51676
|
const name = toolArgs;
|
|
@@ -50411,7 +51750,7 @@ Available workspaces:`);
|
|
|
50411
51750
|
const ingested = await ingestJsonArgs({
|
|
50412
51751
|
rawArg: restArgs[1],
|
|
50413
51752
|
stdinIsTTY: process.stdin.isTTY ?? false,
|
|
50414
|
-
readFile: (p) =>
|
|
51753
|
+
readFile: (p) => fs9.readFileSync(p, "utf-8"),
|
|
50415
51754
|
readStdin
|
|
50416
51755
|
});
|
|
50417
51756
|
if (ingested.kind === "error") {
|
|
@@ -50563,7 +51902,7 @@ ${JSON.stringify(info.input_schema, null, 2)}`);
|
|
|
50563
51902
|
const ingested = await ingestJsonArgs({
|
|
50564
51903
|
rawArg: toolArgs,
|
|
50565
51904
|
stdinIsTTY: process.stdin.isTTY ?? false,
|
|
50566
|
-
readFile: (p) =>
|
|
51905
|
+
readFile: (p) => fs9.readFileSync(p, "utf-8"),
|
|
50567
51906
|
readStdin
|
|
50568
51907
|
});
|
|
50569
51908
|
if (ingested.kind === "error") {
|