@lotics/cli 0.83.0 → 0.86.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -10
- package/dist/cli.js +39 -28
- package/dist/cli_dispatch.test.js +15 -2
- package/dist/client.d.ts +33 -4
- package/dist/client.js +16 -5
- package/dist/dev/file_relay.d.ts +39 -0
- package/dist/dev/file_relay.js +87 -0
- package/dist/dev/file_relay.test.d.ts +1 -0
- package/dist/dev/file_relay.test.js +87 -0
- package/dist/dev/server.d.ts +21 -3
- package/dist/dev/server.js +175 -5
- package/dist/dev/upload_relay.d.ts +36 -0
- package/dist/dev/upload_relay.js +61 -0
- package/dist/dev/upload_relay.test.d.ts +1 -0
- package/dist/dev/upload_relay.test.js +76 -0
- package/dist/dev/wrapper_page.js +7 -1
- package/dist/generate_app_workflows_dts.js +11 -2
- package/dist/generate_app_workflows_dts.test.js +3 -3
- package/dist/package_commands.d.ts +36 -34
- package/dist/package_commands.js +143 -44
- package/dist/package_commands.test.js +135 -2
- package/dist/src/cli.js +356 -61
- package/package.json +1 -1
package/dist/src/cli.js
CHANGED
|
@@ -29948,18 +29948,32 @@ var LoticsClient = class {
|
|
|
29948
29948
|
/**
|
|
29949
29949
|
* Preview a release — the dry run behind `lotics app release`. Runs the
|
|
29950
29950
|
* binding-aware extract of the origin (aliases stable through the app's current
|
|
29951
|
-
* binding) and reports the next version number, the new + changed aliases,
|
|
29952
|
-
* any extract findings (an `error` blocks the
|
|
29951
|
+
* binding) and reports the next version number, the new + changed aliases, the
|
|
29952
|
+
* bundled-knowledge delta, and any extract findings (an `error` blocks the
|
|
29953
|
+
* apply). An optional `knowledge` declaration (from the pulled app manifest)
|
|
29954
|
+
* re-declares the bundle set — added/dropped/changed docs surface in the delta;
|
|
29955
|
+
* omitted, the current corpus is reconstructed from the pin. No writes. Admin,
|
|
29953
29956
|
* owning-org only.
|
|
29954
29957
|
*/
|
|
29955
|
-
async previewPackageRelease(app_id) {
|
|
29956
|
-
|
|
29958
|
+
async previewPackageRelease(app_id, opts = {}) {
|
|
29959
|
+
const params = new URLSearchParams();
|
|
29960
|
+
if (opts.knowledge !== void 0 && opts.knowledge.length > 0) {
|
|
29961
|
+
params.set("knowledge", JSON.stringify(opts.knowledge));
|
|
29962
|
+
}
|
|
29963
|
+
const query = params.toString();
|
|
29964
|
+
return this.request(
|
|
29965
|
+
"GET",
|
|
29966
|
+
`/v1/apps/${encodeURIComponent(app_id)}/package-release${query ? `?${query}` : ""}`
|
|
29967
|
+
);
|
|
29957
29968
|
}
|
|
29958
29969
|
/**
|
|
29959
29970
|
* Release — snapshot the origin app into the next registry version. The server
|
|
29960
29971
|
* binding-aware-extracts it, repackages its deployed source + dist as the
|
|
29961
29972
|
* bundle, publishes the next `release`-channel version with the changelog, and
|
|
29962
|
-
* re-pins the origin.
|
|
29973
|
+
* re-pins the origin. An optional `knowledge` declaration re-declares the
|
|
29974
|
+
* bundled-knowledge set (added/dropped/re-snapshotted docs; omitted preserves
|
|
29975
|
+
* the current corpus). Error findings from extract surface as a 409; a
|
|
29976
|
+
* missing/archived declared doc is a 400, a foreign-package doc a 409. Admin,
|
|
29963
29977
|
* owning-org only. Backs `lotics app release --yes`.
|
|
29964
29978
|
*/
|
|
29965
29979
|
async releasePackage(app_id, body) {
|
|
@@ -31584,7 +31598,13 @@ function buildWrapperPage(args) {
|
|
|
31584
31598
|
|
|
31585
31599
|
// The iframe SDK sends one "upload" op carrying a File. A File can't
|
|
31586
31600
|
// cross the JSON /_rpc hop, so the upload runs here in the browser:
|
|
31587
|
-
// mint
|
|
31601
|
+
// mint an upload URL, PUT the bytes, then finalize.
|
|
31602
|
+
//
|
|
31603
|
+
// In dev the minted URL is same-origin (/_upload/<file_id>) \u2014 the dev
|
|
31604
|
+
// server relays the bytes to storage on our behalf, because the prod
|
|
31605
|
+
// bucket's CORS does not admit a localhost origin. Production PUTs the
|
|
31606
|
+
// presigned storage URL directly. Same three lines either way: the page
|
|
31607
|
+
// PUTs wherever the mint points.
|
|
31588
31608
|
//
|
|
31589
31609
|
// Retry policy parity with the production SDK
|
|
31590
31610
|
// (packages/app-sdk/src/upload/transport.ts): 3 PUT attempts with
|
|
@@ -31789,6 +31809,85 @@ function escapeHtml2(s) {
|
|
|
31789
31809
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
31790
31810
|
}
|
|
31791
31811
|
|
|
31812
|
+
// src/dev/upload_relay.ts
|
|
31813
|
+
var PRESIGN_TTL_MS = 15 * 60 * 1e3;
|
|
31814
|
+
function createUploadRelay(now = Date.now) {
|
|
31815
|
+
const minted = /* @__PURE__ */ new Map();
|
|
31816
|
+
const prune = (at) => {
|
|
31817
|
+
for (const [id, entry] of minted) {
|
|
31818
|
+
if (at - entry.mintedAt > PRESIGN_TTL_MS) minted.delete(id);
|
|
31819
|
+
}
|
|
31820
|
+
};
|
|
31821
|
+
return {
|
|
31822
|
+
rewriteMint(result) {
|
|
31823
|
+
const mint = result;
|
|
31824
|
+
if (typeof mint?.file_id !== "string" || typeof mint?.upload_url !== "string") return result;
|
|
31825
|
+
const at = now();
|
|
31826
|
+
prune(at);
|
|
31827
|
+
minted.set(mint.file_id, { url: mint.upload_url, mintedAt: at });
|
|
31828
|
+
return { ...mint, upload_url: `/_upload/${encodeURIComponent(mint.file_id)}` };
|
|
31829
|
+
},
|
|
31830
|
+
destinationFor(fileId) {
|
|
31831
|
+
const entry = minted.get(fileId);
|
|
31832
|
+
if (!entry) return null;
|
|
31833
|
+
if (now() - entry.mintedAt > PRESIGN_TTL_MS) {
|
|
31834
|
+
minted.delete(fileId);
|
|
31835
|
+
return null;
|
|
31836
|
+
}
|
|
31837
|
+
return entry.url;
|
|
31838
|
+
},
|
|
31839
|
+
settle(fileId) {
|
|
31840
|
+
minted.delete(fileId);
|
|
31841
|
+
},
|
|
31842
|
+
size: () => minted.size
|
|
31843
|
+
};
|
|
31844
|
+
}
|
|
31845
|
+
|
|
31846
|
+
// src/dev/file_relay.ts
|
|
31847
|
+
import { createHash } from "node:crypto";
|
|
31848
|
+
var TTL_MS = 24 * 60 * 60 * 1e3;
|
|
31849
|
+
var MAX_ENTRIES = 5e3;
|
|
31850
|
+
var PRESIGNED_KEYS = /* @__PURE__ */ new Set(["url", "thumbnail_url", "preview_url"]);
|
|
31851
|
+
var isFileObject = (o) => typeof o.filename === "string" && typeof o.mime_type === "string";
|
|
31852
|
+
var isRemoteUrl = (v) => typeof v === "string" && /^https?:\/\//i.test(v);
|
|
31853
|
+
function createFileRelay(wrapperOrigin, now = Date.now) {
|
|
31854
|
+
const seen = /* @__PURE__ */ new Map();
|
|
31855
|
+
const tokenFor = (url2) => createHash("sha256").update(url2).digest("hex").slice(0, 24);
|
|
31856
|
+
const remember = (url2) => {
|
|
31857
|
+
const token = tokenFor(url2);
|
|
31858
|
+
seen.set(token, { url: url2, at: now() });
|
|
31859
|
+
if (seen.size > MAX_ENTRIES) {
|
|
31860
|
+
const oldest = seen.keys().next();
|
|
31861
|
+
if (!oldest.done) seen.delete(oldest.value);
|
|
31862
|
+
}
|
|
31863
|
+
return token;
|
|
31864
|
+
};
|
|
31865
|
+
const walk2 = (node) => {
|
|
31866
|
+
if (Array.isArray(node)) return node.map(walk2);
|
|
31867
|
+
if (!node || typeof node !== "object") return node;
|
|
31868
|
+
const obj = node;
|
|
31869
|
+
const file2 = isFileObject(obj);
|
|
31870
|
+
const out = {};
|
|
31871
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
31872
|
+
out[key] = file2 && PRESIGNED_KEYS.has(key) && isRemoteUrl(value) ? `${wrapperOrigin}/_file/${remember(value)}` : walk2(value);
|
|
31873
|
+
}
|
|
31874
|
+
return out;
|
|
31875
|
+
};
|
|
31876
|
+
return {
|
|
31877
|
+
rewrite: (result) => walk2(result),
|
|
31878
|
+
destinationFor(token) {
|
|
31879
|
+
const entry = seen.get(token);
|
|
31880
|
+
if (!entry) return null;
|
|
31881
|
+
if (now() - entry.at > TTL_MS) {
|
|
31882
|
+
seen.delete(token);
|
|
31883
|
+
return null;
|
|
31884
|
+
}
|
|
31885
|
+
return entry.url;
|
|
31886
|
+
},
|
|
31887
|
+
size: () => seen.size
|
|
31888
|
+
};
|
|
31889
|
+
}
|
|
31890
|
+
|
|
31792
31891
|
// src/dev/server.ts
|
|
31793
31892
|
var DEFAULT_PORT = 5174;
|
|
31794
31893
|
var DEFAULT_VITE_PORT = 5173;
|
|
@@ -31835,6 +31934,24 @@ async function startDevServer(args) {
|
|
|
31835
31934
|
}
|
|
31836
31935
|
};
|
|
31837
31936
|
process.once("exit", killViteOnExit);
|
|
31937
|
+
const uploads = createUploadRelay();
|
|
31938
|
+
const wrapperOrigin = `http://localhost:${wrapperPort}`;
|
|
31939
|
+
const viteOrigin = `http://localhost:${vitePort}`;
|
|
31940
|
+
const files = createFileRelay(wrapperOrigin);
|
|
31941
|
+
const fileCors = {
|
|
31942
|
+
"Access-Control-Allow-Origin": viteOrigin,
|
|
31943
|
+
"Access-Control-Allow-Headers": "range, content-type",
|
|
31944
|
+
"Access-Control-Expose-Headers": "content-length, content-range, accept-ranges, content-type, content-disposition, etag"
|
|
31945
|
+
};
|
|
31946
|
+
const PASS_THROUGH = [
|
|
31947
|
+
"content-type",
|
|
31948
|
+
"content-length",
|
|
31949
|
+
"content-range",
|
|
31950
|
+
"accept-ranges",
|
|
31951
|
+
"etag",
|
|
31952
|
+
"last-modified",
|
|
31953
|
+
"content-disposition"
|
|
31954
|
+
];
|
|
31838
31955
|
const server = http.createServer(async (req, res) => {
|
|
31839
31956
|
const url2 = req.url ?? "/";
|
|
31840
31957
|
const pathname = url2.split("?")[0];
|
|
@@ -31863,7 +31980,11 @@ async function startDevServer(args) {
|
|
|
31863
31980
|
process.stderr.write(`[rpc] ${body.op} ${ms}ms
|
|
31864
31981
|
`);
|
|
31865
31982
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
31866
|
-
res.end(
|
|
31983
|
+
res.end(
|
|
31984
|
+
serializeRpcResult(
|
|
31985
|
+
body.op === "upload_url" ? uploads.rewriteMint(result) : files.rewrite(result)
|
|
31986
|
+
)
|
|
31987
|
+
);
|
|
31867
31988
|
} catch (err2) {
|
|
31868
31989
|
const message = err2 instanceof Error ? err2.message : String(err2);
|
|
31869
31990
|
process.stderr.write(`[rpc] ERROR ${message}
|
|
@@ -31916,12 +32037,115 @@ async function startDevServer(args) {
|
|
|
31916
32037
|
}
|
|
31917
32038
|
return;
|
|
31918
32039
|
}
|
|
32040
|
+
if (pathname.startsWith("/_file/")) {
|
|
32041
|
+
if (req.method === "OPTIONS") {
|
|
32042
|
+
res.writeHead(204, fileCors);
|
|
32043
|
+
res.end();
|
|
32044
|
+
return;
|
|
32045
|
+
}
|
|
32046
|
+
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
32047
|
+
res.writeHead(405, { ...fileCors, Allow: "GET, HEAD, OPTIONS" });
|
|
32048
|
+
res.end();
|
|
32049
|
+
return;
|
|
32050
|
+
}
|
|
32051
|
+
const token = decodeURIComponent(pathname.slice("/_file/".length));
|
|
32052
|
+
const destination = files.destinationFor(token);
|
|
32053
|
+
if (!destination) {
|
|
32054
|
+
process.stderr.write(`[file] ERROR unknown or expired token ${token}
|
|
32055
|
+
`);
|
|
32056
|
+
res.writeHead(404, { ...fileCors, "Content-Type": "application/json" });
|
|
32057
|
+
res.end(JSON.stringify({ message: "No file for this token" }));
|
|
32058
|
+
return;
|
|
32059
|
+
}
|
|
32060
|
+
try {
|
|
32061
|
+
const range = req.headers.range;
|
|
32062
|
+
const upstream = await fetch(destination, {
|
|
32063
|
+
method: req.method,
|
|
32064
|
+
headers: range ? { Range: range } : void 0
|
|
32065
|
+
});
|
|
32066
|
+
const headers = { ...fileCors };
|
|
32067
|
+
for (const name of PASS_THROUGH) {
|
|
32068
|
+
const value = upstream.headers.get(name);
|
|
32069
|
+
if (value) headers[name] = value;
|
|
32070
|
+
}
|
|
32071
|
+
const size = headers["content-length"] ?? "?";
|
|
32072
|
+
process.stderr.write(`[file] ${token} ${upstream.status} ${size}B
|
|
32073
|
+
`);
|
|
32074
|
+
res.writeHead(upstream.status, headers);
|
|
32075
|
+
if (req.method === "HEAD" || !upstream.body) {
|
|
32076
|
+
res.end();
|
|
32077
|
+
return;
|
|
32078
|
+
}
|
|
32079
|
+
const reader = upstream.body.getReader();
|
|
32080
|
+
for (; ; ) {
|
|
32081
|
+
const { value, done } = await reader.read();
|
|
32082
|
+
if (done) break;
|
|
32083
|
+
res.write(Buffer.from(value));
|
|
32084
|
+
}
|
|
32085
|
+
res.end();
|
|
32086
|
+
} catch (err2) {
|
|
32087
|
+
const message = err2 instanceof Error ? err2.message : String(err2);
|
|
32088
|
+
process.stderr.write(`[file] ERROR ${token} ${message}
|
|
32089
|
+
`);
|
|
32090
|
+
if (!res.headersSent) {
|
|
32091
|
+
res.writeHead(502, { ...fileCors, "Content-Type": "application/json" });
|
|
32092
|
+
res.end(JSON.stringify({ message }));
|
|
32093
|
+
} else {
|
|
32094
|
+
res.end();
|
|
32095
|
+
}
|
|
32096
|
+
}
|
|
32097
|
+
return;
|
|
32098
|
+
}
|
|
32099
|
+
if (req.method === "PUT" && pathname.startsWith("/_upload/")) {
|
|
32100
|
+
const fileId = decodeURIComponent(pathname.slice("/_upload/".length));
|
|
32101
|
+
const destination = uploads.destinationFor(fileId);
|
|
32102
|
+
if (!destination) {
|
|
32103
|
+
process.stderr.write(`[upload] ERROR unknown or expired file_id ${fileId}
|
|
32104
|
+
`);
|
|
32105
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
32106
|
+
res.end(JSON.stringify({ message: `No presigned upload pending for ${fileId}` }));
|
|
32107
|
+
return;
|
|
32108
|
+
}
|
|
32109
|
+
try {
|
|
32110
|
+
const chunks = [];
|
|
32111
|
+
for await (const chunk of req) chunks.push(chunk);
|
|
32112
|
+
const bytes = Buffer.concat(chunks);
|
|
32113
|
+
const startedAt = Date.now();
|
|
32114
|
+
const upstream = await fetch(destination, {
|
|
32115
|
+
method: "PUT",
|
|
32116
|
+
body: bytes,
|
|
32117
|
+
headers: { "Content-Type": req.headers["content-type"] ?? "application/octet-stream" }
|
|
32118
|
+
});
|
|
32119
|
+
const ms = Date.now() - startedAt;
|
|
32120
|
+
if (upstream.ok) {
|
|
32121
|
+
uploads.settle(fileId);
|
|
32122
|
+
process.stderr.write(`[upload] ${fileId} ${bytes.length}B ${ms}ms
|
|
32123
|
+
`);
|
|
32124
|
+
} else {
|
|
32125
|
+
process.stderr.write(`[upload] ERROR ${fileId} storage returned ${upstream.status}
|
|
32126
|
+
`);
|
|
32127
|
+
}
|
|
32128
|
+
res.writeHead(upstream.status);
|
|
32129
|
+
res.end();
|
|
32130
|
+
} catch (err2) {
|
|
32131
|
+
const message = err2 instanceof Error ? err2.message : String(err2);
|
|
32132
|
+
process.stderr.write(`[upload] ERROR ${fileId} ${message}
|
|
32133
|
+
`);
|
|
32134
|
+
if (!res.headersSent) {
|
|
32135
|
+
res.writeHead(502, { "Content-Type": "application/json" });
|
|
32136
|
+
res.end(JSON.stringify({ message }));
|
|
32137
|
+
} else {
|
|
32138
|
+
res.end();
|
|
32139
|
+
}
|
|
32140
|
+
}
|
|
32141
|
+
return;
|
|
32142
|
+
}
|
|
31919
32143
|
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
31920
32144
|
res.end("Not Found");
|
|
31921
32145
|
});
|
|
31922
32146
|
await new Promise((resolve2, reject2) => {
|
|
31923
32147
|
server.once("error", reject2);
|
|
31924
|
-
server.listen(wrapperPort, () => {
|
|
32148
|
+
server.listen(wrapperPort, "127.0.0.1", () => {
|
|
31925
32149
|
server.off("error", reject2);
|
|
31926
32150
|
resolve2();
|
|
31927
32151
|
});
|
|
@@ -32085,7 +32309,7 @@ function inputDeclToTsType(decl) {
|
|
|
32085
32309
|
}
|
|
32086
32310
|
return null;
|
|
32087
32311
|
}).filter((v) => v !== null);
|
|
32088
|
-
const inner = literals.length > 0 ? literals.join(" | ") : "string";
|
|
32312
|
+
const inner = literals.length > 0 ? `${literals.join(" | ")} | (string & {})` : "string";
|
|
32089
32313
|
return decl.multi === true ? `ReadonlyArray<${inner}>` : inner;
|
|
32090
32314
|
}
|
|
32091
32315
|
case "date_range":
|
|
@@ -32137,7 +32361,7 @@ function outputDeclToTsType(decl) {
|
|
|
32137
32361
|
const literals = options.map(
|
|
32138
32362
|
(o) => o !== null && typeof o === "object" && "value" in o && typeof o.value === "string" ? JSON.stringify(o.value) : null
|
|
32139
32363
|
).filter((v) => v !== null);
|
|
32140
|
-
const inner = literals.length > 0 ? literals.join(" | ") : "string";
|
|
32364
|
+
const inner = literals.length > 0 ? `${literals.join(" | ")} | (string & {})` : "string";
|
|
32141
32365
|
return decl.multi === true ? `ReadonlyArray<${inner}>` : inner;
|
|
32142
32366
|
}
|
|
32143
32367
|
case "object": {
|
|
@@ -50215,7 +50439,8 @@ async function packageDoctor(client, args) {
|
|
|
50215
50439
|
}
|
|
50216
50440
|
if (health.modified.length === 0) {
|
|
50217
50441
|
console.error(
|
|
50218
|
-
health.is_origin ?
|
|
50442
|
+
health.is_origin ? ` Package artifacts: no changes since the last release.
|
|
50443
|
+
(schema additions aren't fingerprinted \u2014 preview them with: lotics app release ${app_id})` : " Package artifacts: pristine \u2014 no local edits an upgrade would revert."
|
|
50219
50444
|
);
|
|
50220
50445
|
} else if (health.is_origin) {
|
|
50221
50446
|
console.error(
|
|
@@ -50410,6 +50635,56 @@ function formatTemplateResolveHint(entry) {
|
|
|
50410
50635
|
const note = entry.baseline_unknown ? " (can't verify the local edit \u2014 older package version; revert overwrites, keep retains)" : " (a local edit \u2014 revert overwrites it with the package's version; keep retains it)";
|
|
50411
50636
|
return ` --resolve template.${entry.alias}=revert|keep${note}`;
|
|
50412
50637
|
}
|
|
50638
|
+
async function resolveWorkspaceContentInstallation(client, package_id) {
|
|
50639
|
+
const workspaceId = client.getWorkspaceId();
|
|
50640
|
+
if (!workspaceId) {
|
|
50641
|
+
throw new Error(
|
|
50642
|
+
"No workspace selected. Pass --workspace <ws> (or select one) to operate on its content installation."
|
|
50643
|
+
);
|
|
50644
|
+
}
|
|
50645
|
+
const installations = await client.listContentInstallations(workspaceId);
|
|
50646
|
+
const match = installations.find((inst) => inst.package_id === package_id);
|
|
50647
|
+
if (!match) {
|
|
50648
|
+
throw new Error(
|
|
50649
|
+
`Package ${package_id} is not installed in this workspace \u2014 install it first: lotics install ${package_id}`
|
|
50650
|
+
);
|
|
50651
|
+
}
|
|
50652
|
+
return match;
|
|
50653
|
+
}
|
|
50654
|
+
async function redirectContentPciForm(client, pci_id, verb) {
|
|
50655
|
+
console.error(
|
|
50656
|
+
`Content installations are addressed by their PACKAGE id now \u2014 ${pci_id} is a server-internal resource id.`
|
|
50657
|
+
);
|
|
50658
|
+
const workspaceId = client.getWorkspaceId();
|
|
50659
|
+
const match = workspaceId ? (await client.listContentInstallations(workspaceId)).find((inst) => inst.id === pci_id) : void 0;
|
|
50660
|
+
if (match) {
|
|
50661
|
+
console.error(` Run: lotics ${verb} ${match.package_id}`);
|
|
50662
|
+
} else {
|
|
50663
|
+
console.error(" Find the package id: lotics package list-content");
|
|
50664
|
+
console.error(` Then run: lotics ${verb} <package_id>`);
|
|
50665
|
+
}
|
|
50666
|
+
process.exit(1);
|
|
50667
|
+
}
|
|
50668
|
+
async function packageUpgradeByPackageId(client, args) {
|
|
50669
|
+
const pkg2 = await client.getPackage(args.package_id);
|
|
50670
|
+
if (pkg2.kind === "content") {
|
|
50671
|
+
const installation = await resolveWorkspaceContentInstallation(client, args.package_id);
|
|
50672
|
+
await packageUpgradeKnowledge(client, {
|
|
50673
|
+
installation_id: installation.id,
|
|
50674
|
+
package_id: pkg2.id,
|
|
50675
|
+
package_name: pkg2.name,
|
|
50676
|
+
version: args.version,
|
|
50677
|
+
resolve: args.resolve,
|
|
50678
|
+
bind_to: parseBindToFlags(args.bindTo),
|
|
50679
|
+
applyAll: args.applyAll
|
|
50680
|
+
});
|
|
50681
|
+
return;
|
|
50682
|
+
}
|
|
50683
|
+
await packageFleetUpgrade(client, {
|
|
50684
|
+
package_id: args.package_id,
|
|
50685
|
+
...args.version !== void 0 ? { version: args.version } : {}
|
|
50686
|
+
});
|
|
50687
|
+
}
|
|
50413
50688
|
async function packageUpgradeKnowledge(client, args) {
|
|
50414
50689
|
const preview = await client.previewContentInstallationUpgrade(args.installation_id, {
|
|
50415
50690
|
...args.version !== void 0 ? { version: args.version } : {}
|
|
@@ -50422,7 +50697,7 @@ async function packageUpgradeKnowledge(client, args) {
|
|
|
50422
50697
|
...args.version !== void 0 ? { version: args.version } : {},
|
|
50423
50698
|
resolutions: resolutions2
|
|
50424
50699
|
});
|
|
50425
|
-
console.error(`Upgraded ${args.
|
|
50700
|
+
console.error(`Upgraded ${args.package_name} (${args.package_id}) \u2192 v${updated.package_version}.`);
|
|
50426
50701
|
};
|
|
50427
50702
|
if (preview.entries.length === 0 && preview.templates.length === 0) {
|
|
50428
50703
|
if (preview.to_version === preview.from_version) {
|
|
@@ -50494,8 +50769,8 @@ function warnMissingExpectedDocs(missing) {
|
|
|
50494
50769
|
}
|
|
50495
50770
|
async function packageInstall(client, args) {
|
|
50496
50771
|
const pkg2 = await client.getPackage(args.package_id);
|
|
50497
|
-
const
|
|
50498
|
-
console.error(`Installing ${pkg2.name} \u2014 ${trustBadge(pkg2)}
|
|
50772
|
+
const kindSuffix = pkg2.kind === "content" ? " (content \u2014 docs/templates, no app)" : "";
|
|
50773
|
+
console.error(`Installing ${pkg2.name} \u2014 ${trustBadge(pkg2)}${kindSuffix}...`);
|
|
50499
50774
|
const result = await client.installPackage(args.package_id, {
|
|
50500
50775
|
...args.version !== void 0 ? { version: args.version } : {},
|
|
50501
50776
|
...args.bind_to && Object.keys(args.bind_to).length > 0 ? { bind_to: args.bind_to } : {},
|
|
@@ -50506,7 +50781,7 @@ async function packageInstall(client, args) {
|
|
|
50506
50781
|
const docs = installation.binding.knowledge;
|
|
50507
50782
|
const templates = installation.binding.templates;
|
|
50508
50783
|
console.error(
|
|
50509
|
-
`Installed ${pkg2.name} v${installation.package_version}
|
|
50784
|
+
`Installed ${pkg2.name} v${installation.package_version} (workspace ${installation.workspace_id}).`
|
|
50510
50785
|
);
|
|
50511
50786
|
const docAliases = Object.keys(docs);
|
|
50512
50787
|
if (docAliases.length > 0) {
|
|
@@ -50521,8 +50796,8 @@ async function packageInstall(client, args) {
|
|
|
50521
50796
|
);
|
|
50522
50797
|
}
|
|
50523
50798
|
warnMissingExpectedDocs(warnings.missing_expected_docs);
|
|
50524
|
-
console.error(` Upgrade later: lotics upgrade ${
|
|
50525
|
-
console.error(` Uninstall: lotics uninstall ${
|
|
50799
|
+
console.error(` Upgrade later: lotics upgrade ${args.package_id}`);
|
|
50800
|
+
console.error(` Uninstall: lotics uninstall ${args.package_id} [--keep-content]`);
|
|
50526
50801
|
return;
|
|
50527
50802
|
}
|
|
50528
50803
|
const { app, knowledge_warnings } = result;
|
|
@@ -50535,22 +50810,29 @@ async function packageInstall(client, args) {
|
|
|
50535
50810
|
console.error(` Health / uninstall: lotics package doctor ${app.id} \xB7 lotics uninstall ${app.id} [--archive-tables]`);
|
|
50536
50811
|
}
|
|
50537
50812
|
async function packageUninstall(client, args) {
|
|
50538
|
-
if (args.id.startsWith("
|
|
50813
|
+
if (args.id.startsWith("apg_")) {
|
|
50539
50814
|
if (args.archive_tables) {
|
|
50540
50815
|
throw new Error(
|
|
50541
|
-
"--archive-tables applies only to an app installation (<app_id>). A content
|
|
50816
|
+
"--archive-tables applies only to an app installation (<app_id>). A content package has no scaffolded tables \u2014 use --keep-content to retain its docs/templates."
|
|
50542
50817
|
);
|
|
50543
50818
|
}
|
|
50544
|
-
const
|
|
50819
|
+
const pkg2 = await client.getPackage(args.id);
|
|
50820
|
+
if (pkg2.kind !== "content") {
|
|
50821
|
+
throw new Error(
|
|
50822
|
+
`Package ${args.id} is an app package \u2014 uninstall an app installation by its app id: lotics uninstall <app_id>.`
|
|
50823
|
+
);
|
|
50824
|
+
}
|
|
50825
|
+
const installation = await resolveWorkspaceContentInstallation(client, args.id);
|
|
50826
|
+
const result2 = await client.uninstallContentPackage(installation.id, {
|
|
50545
50827
|
keep_content: args.keep_content
|
|
50546
50828
|
});
|
|
50547
50829
|
if (args.keep_content) {
|
|
50548
50830
|
console.error(
|
|
50549
|
-
`Uninstalled
|
|
50831
|
+
`Uninstalled ${pkg2.name} (${pkg2.id}) \u2014 its docs and templates were kept as ordinary workspace content.`
|
|
50550
50832
|
);
|
|
50551
50833
|
} else {
|
|
50552
50834
|
console.error(
|
|
50553
|
-
`Uninstalled
|
|
50835
|
+
`Uninstalled ${pkg2.name} (${pkg2.id}) \u2014 archived ${result2.archived_doc_ids.length} doc(s) and ${result2.archived_template_ids.length} template(s).`
|
|
50554
50836
|
);
|
|
50555
50837
|
for (const docId of result2.archived_doc_ids) console.error(` ${docId}`);
|
|
50556
50838
|
for (const templateId of result2.archived_template_ids) console.error(` ${templateId}`);
|
|
@@ -50559,7 +50841,7 @@ async function packageUninstall(client, args) {
|
|
|
50559
50841
|
}
|
|
50560
50842
|
if (args.keep_content) {
|
|
50561
50843
|
throw new Error(
|
|
50562
|
-
"--keep-content applies only to a
|
|
50844
|
+
"--keep-content applies only to a content package (apg_). An app installation (<app_id>) uses --archive-tables to also archive its scaffolded tables."
|
|
50563
50845
|
);
|
|
50564
50846
|
}
|
|
50565
50847
|
const app = await client.getApp(args.id);
|
|
@@ -50603,7 +50885,7 @@ async function packageListContent(client) {
|
|
|
50603
50885
|
const updateAvailable = inst.package_registry?.update_available ?? false;
|
|
50604
50886
|
const versionLabel = latest !== void 0 && latest !== inst.package_version ? `v${inst.package_version} \u2192 latest v${latest}` : `v${inst.package_version}`;
|
|
50605
50887
|
console.error(
|
|
50606
|
-
` ${inst.
|
|
50888
|
+
` ${inst.package_id} ${name} ${versionLabel}` + (updateAvailable ? " \u2192 update available" : "")
|
|
50607
50889
|
);
|
|
50608
50890
|
}
|
|
50609
50891
|
}
|
|
@@ -50765,18 +51047,18 @@ Re-run with --yes to publish v1:`);
|
|
|
50765
51047
|
console.error(` lotics app release ${appId} -m "<what changed>"`);
|
|
50766
51048
|
console.error(` Install it elsewhere: lotics install ${result.package_id}`);
|
|
50767
51049
|
}
|
|
50768
|
-
function resolveOriginAppId(projectDir, explicit) {
|
|
50769
|
-
if (explicit !== void 0 && explicit !== ".") return explicit;
|
|
50770
|
-
const local = readLocalAppManifest(projectDir);
|
|
50771
|
-
if (local?.app_id) return local.app_id;
|
|
50772
|
-
throw new Error(
|
|
50773
|
-
"No app id. Run this from a pulled app project (lotics app pull <app_id>), or pass an app id explicitly."
|
|
50774
|
-
);
|
|
50775
|
-
}
|
|
50776
51050
|
async function appRelease(client, args) {
|
|
50777
51051
|
const projectDir = path6.resolve(args.projectDir ?? process.cwd());
|
|
50778
|
-
const
|
|
50779
|
-
const
|
|
51052
|
+
const local = readLocalAppManifest(projectDir);
|
|
51053
|
+
const explicit = args.app_id !== void 0 && args.app_id !== "." ? args.app_id : void 0;
|
|
51054
|
+
const appId = explicit ?? local?.app_id ?? null;
|
|
51055
|
+
if (appId === null) {
|
|
51056
|
+
throw new Error(
|
|
51057
|
+
"No app id. Run this from a pulled app project (lotics app pull <app_id>), or pass an app id explicitly."
|
|
51058
|
+
);
|
|
51059
|
+
}
|
|
51060
|
+
const knowledge = local && local.app_id === appId && local.knowledge.length > 0 ? local.knowledge : void 0;
|
|
51061
|
+
const preview = await client.previewPackageRelease(appId, { knowledge });
|
|
50780
51062
|
const { lines, hasError } = formatExtractReport(preview.findings);
|
|
50781
51063
|
console.error(`Release preview \u2014 ${appId} \u2192 ${preview.package_id} v${preview.version}:`);
|
|
50782
51064
|
if (preview.added_aliases.length > 0) {
|
|
@@ -50785,7 +51067,16 @@ async function appRelease(client, args) {
|
|
|
50785
51067
|
if (preview.changed_artifacts.length > 0) {
|
|
50786
51068
|
console.error(` Changed (${preview.changed_artifacts.length}): ${preview.changed_artifacts.join(", ")}`);
|
|
50787
51069
|
}
|
|
50788
|
-
|
|
51070
|
+
const k = preview.knowledge ?? { added: [], removed: [], changed: [] };
|
|
51071
|
+
if (k.added.length > 0 || k.removed.length > 0 || k.changed.length > 0) {
|
|
51072
|
+
const parts = [
|
|
51073
|
+
k.added.length > 0 ? `+${k.added.join(", ")}` : null,
|
|
51074
|
+
k.removed.length > 0 ? `dropped ${k.removed.join(", ")}` : null,
|
|
51075
|
+
k.changed.length > 0 ? `changed ${k.changed.join(", ")}` : null
|
|
51076
|
+
].filter((p) => p !== null);
|
|
51077
|
+
console.error(` Knowledge: ${parts.join("; ")}`);
|
|
51078
|
+
}
|
|
51079
|
+
if (preview.added_aliases.length === 0 && preview.changed_artifacts.length === 0 && k.added.length === 0 && k.removed.length === 0 && k.changed.length === 0) {
|
|
50789
51080
|
console.error(" No contract changes since the current version (a fresh code/dist snapshot still ships).");
|
|
50790
51081
|
}
|
|
50791
51082
|
if (lines.length > 0) {
|
|
@@ -50804,7 +51095,7 @@ Re-run with --yes to publish v${preview.version}:`);
|
|
|
50804
51095
|
process.exitCode = 1;
|
|
50805
51096
|
return;
|
|
50806
51097
|
}
|
|
50807
|
-
const result = await client.releasePackage(appId, { changelog: args.changelog });
|
|
51098
|
+
const result = await client.releasePackage(appId, { changelog: args.changelog, knowledge });
|
|
50808
51099
|
console.error(`Released ${result.package_id} v${result.version}.`);
|
|
50809
51100
|
console.error(` The origin was re-pinned to v${result.version} \u2014 verify: lotics package doctor ${appId}`);
|
|
50810
51101
|
}
|
|
@@ -67712,18 +68003,19 @@ COMMANDS
|
|
|
67712
68003
|
Install a package (app: scaffolds/deploys/materializes,
|
|
67713
68004
|
--config sets its knobs; content: installs the doc corpus,
|
|
67714
68005
|
--bind-to consents to adopt a same-named doc on a collision)
|
|
67715
|
-
lotics uninstall <app_id|
|
|
68006
|
+
lotics uninstall <app_id|package_id> [--archive-tables] [--keep-content]
|
|
67716
68007
|
Uninstall \u2014 dispatched by id. App (app_id): archives
|
|
67717
68008
|
artifacts (+ --archive-tables also archives scaffolded
|
|
67718
|
-
tables). Content (
|
|
67719
|
-
unless --keep-content
|
|
67720
|
-
lotics upgrade <app_id|
|
|
68009
|
+
tables). Content package (apg_): archives its package-bound
|
|
68010
|
+
docs unless --keep-content
|
|
68011
|
+
lotics upgrade <app_id|package_id> [--version N] [--resolve <key>=... ] [--bind-to ...] [--apply-all]
|
|
67721
68012
|
Preview-then-apply an upgrade, dispatched by id. App
|
|
67722
68013
|
(app_id): refuses while any drift/modified/bundled-knowledge
|
|
67723
|
-
finding lacks a --resolve.
|
|
67724
|
-
|
|
67725
|
-
|
|
67726
|
-
|
|
68014
|
+
finding lacks a --resolve. Package (apg_): a CONTENT package
|
|
68015
|
+
upgrades THIS workspace's install (--resolve
|
|
68016
|
+
<alias>=apply|keep|archive|recreate|unbind); an APP package
|
|
68017
|
+
FLEET-upgrades every installation across your org (clean
|
|
68018
|
+
apply, findings skip). --apply-all accepts the package's version
|
|
67727
68019
|
lotics package doctor [app_id] Installation health: version pin vs latest, binding
|
|
67728
68020
|
drift, locally modified core, knowledge drift/edits
|
|
67729
68021
|
(exit 1 on findings)
|
|
@@ -67733,7 +68025,8 @@ COMMANDS
|
|
|
67733
68025
|
(re-deploys the pinned source as a bespoke app)
|
|
67734
68026
|
lotics package show <package_id> Registry metadata + version history
|
|
67735
68027
|
lotics package list-content List the workspace's content installations
|
|
67736
|
-
(standalone content installs) \u2014
|
|
68028
|
+
(standalone content installs) \u2014 each row leads with
|
|
68029
|
+
the package id for upgrade/uninstall
|
|
67737
68030
|
lotics package yank <package_id> <version> [--undo]
|
|
67738
68031
|
Refuse new installs/upgrades of a broken published
|
|
67739
68032
|
version (pinned installations keep running)
|
|
@@ -68250,8 +68543,8 @@ async function main() {
|
|
|
68250
68543
|
console.error("Usage (low-traffic consumer / registry ops \u2014 author verbs live on `lotics app`):");
|
|
68251
68544
|
console.error(" The high-traffic consumer verbs are top-level:");
|
|
68252
68545
|
console.error(" lotics install <package_id> [--version N] [--bind-to <alias>=<kdc_id>] [--config key=value ...] Install a package (app or content)");
|
|
68253
|
-
console.error(" lotics uninstall <app_id|
|
|
68254
|
-
console.error(" lotics upgrade <app_id|
|
|
68546
|
+
console.error(" lotics uninstall <app_id|package_id> [--archive-tables] [--keep-content] Uninstall \u2014 dispatched by id (app vs content)");
|
|
68547
|
+
console.error(" lotics upgrade <app_id|package_id> [--version N] [--resolve <key>=... ] [--bind-to ...] [--apply-all] Upgrade \u2014 app install / content install / whole app fleet (apg_)");
|
|
68255
68548
|
console.error(" lotics package doctor [app_id] Health: version pin vs latest + binding/knowledge drift");
|
|
68256
68549
|
console.error(" lotics package config <app_id> [--set key=value ...] Show or edit an installation's config knobs");
|
|
68257
68550
|
console.error(" lotics package eject <app_id> Sever an installation's package link");
|
|
@@ -68429,12 +68722,16 @@ Available workspaces:`);
|
|
|
68429
68722
|
if (command === "uninstall") {
|
|
68430
68723
|
const id = subcommand;
|
|
68431
68724
|
if (!id) {
|
|
68432
|
-
console.error("Usage: lotics uninstall <app_id|
|
|
68725
|
+
console.error("Usage: lotics uninstall <app_id|package_id> [--archive-tables] [--keep-content]");
|
|
68433
68726
|
console.error(
|
|
68434
|
-
"Dispatched by id: an app installation (app_id; --archive-tables to also archive its scaffolded tables) or a
|
|
68727
|
+
"Dispatched by id: an app installation (app_id; --archive-tables to also archive its scaffolded tables) or a content package (apg_ id from `lotics install` / lotics package list-content; --keep-content to retain its docs)."
|
|
68435
68728
|
);
|
|
68436
68729
|
process.exit(1);
|
|
68437
68730
|
}
|
|
68731
|
+
if (id.startsWith("pci_")) {
|
|
68732
|
+
await redirectContentPciForm(client, id, "uninstall");
|
|
68733
|
+
return;
|
|
68734
|
+
}
|
|
68438
68735
|
await packageUninstall(client, {
|
|
68439
68736
|
id,
|
|
68440
68737
|
keep_content: flags.keepContent,
|
|
@@ -68446,10 +68743,10 @@ Available workspaces:`);
|
|
|
68446
68743
|
const target = subcommand;
|
|
68447
68744
|
if (!target) {
|
|
68448
68745
|
console.error(
|
|
68449
|
-
"Usage: lotics upgrade <app_id |
|
|
68746
|
+
"Usage: lotics upgrade <app_id | package_id> [--version N] [--resolve <key>=... ...] [--bind-to ...] [--apply-all]"
|
|
68450
68747
|
);
|
|
68451
68748
|
console.error(
|
|
68452
|
-
"Dispatched by id: an app installation (app_id), a
|
|
68749
|
+
"Dispatched by id: an app installation (app_id), or a package id (apg_) \u2014 a CONTENT package upgrades this workspace's install; an APP package fleet-upgrades every installation across your org."
|
|
68453
68750
|
);
|
|
68454
68751
|
process.exit(1);
|
|
68455
68752
|
}
|
|
@@ -68461,16 +68758,16 @@ Available workspaces:`);
|
|
|
68461
68758
|
process.exit(1);
|
|
68462
68759
|
}
|
|
68463
68760
|
}
|
|
68464
|
-
if (target.startsWith("
|
|
68465
|
-
await
|
|
68761
|
+
if (target.startsWith("pci_")) {
|
|
68762
|
+
await redirectContentPciForm(client, target, "upgrade");
|
|
68466
68763
|
return;
|
|
68467
68764
|
}
|
|
68468
|
-
if (target.startsWith("
|
|
68469
|
-
await
|
|
68470
|
-
|
|
68765
|
+
if (target.startsWith("apg_")) {
|
|
68766
|
+
await packageUpgradeByPackageId(client, {
|
|
68767
|
+
package_id: target,
|
|
68471
68768
|
version: version2,
|
|
68472
68769
|
resolve: flags.resolve,
|
|
68473
|
-
|
|
68770
|
+
bindTo: flags.bindTo,
|
|
68474
68771
|
applyAll: flags.applyAll
|
|
68475
68772
|
});
|
|
68476
68773
|
return;
|
|
@@ -68541,8 +68838,8 @@ Available workspaces:`);
|
|
|
68541
68838
|
}
|
|
68542
68839
|
const movedVerbs = {
|
|
68543
68840
|
install: "lotics install <package_id>",
|
|
68544
|
-
uninstall: "lotics uninstall <app_id|
|
|
68545
|
-
upgrade: "lotics upgrade <app_id|
|
|
68841
|
+
uninstall: "lotics uninstall <app_id|package_id>",
|
|
68842
|
+
upgrade: "lotics upgrade <app_id|package_id>",
|
|
68546
68843
|
"fleet-upgrade": "lotics upgrade <package_id>"
|
|
68547
68844
|
};
|
|
68548
68845
|
if (subcommand !== void 0 && subcommand in movedVerbs) {
|
|
@@ -68829,8 +69126,6 @@ ${JSON.stringify(info.input_schema, null, 2)}`);
|
|
|
68829
69126
|
console.log(JSON.stringify({ error: result.error }, null, 2));
|
|
68830
69127
|
} else {
|
|
68831
69128
|
console.error(result.error);
|
|
68832
|
-
console.error(`
|
|
68833
|
-
Hint: run "lotics tools ${toolName}" to see the expected input schema.`);
|
|
68834
69129
|
}
|
|
68835
69130
|
process.exit(1);
|
|
68836
69131
|
}
|