@lotics/cli 0.76.1 → 0.86.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 +57 -52
- package/dist/app_commands.d.ts +9 -4
- package/dist/app_commands.js +44 -10
- package/dist/app_commands.test.js +69 -0
- package/dist/args.d.ts +6 -5
- package/dist/args.js +18 -17
- package/dist/args.test.js +25 -17
- package/dist/cli.js +214 -255
- package/dist/cli_dispatch.test.js +90 -25
- package/dist/client.d.ts +134 -176
- package/dist/client.js +72 -95
- package/dist/generate_app_workflows_dts.js +11 -2
- package/dist/generate_app_workflows_dts.test.js +3 -3
- package/dist/generate_package_fields.d.ts +39 -38
- package/dist/generate_package_fields.js +113 -60
- package/dist/generate_package_fields.test.js +30 -22
- package/dist/package_commands.d.ts +116 -332
- package/dist/package_commands.js +420 -1316
- package/dist/package_commands.test.js +194 -537
- package/dist/src/cli.js +721 -1846
- package/dist/starter_template.d.ts +0 -19
- package/dist/starter_template.js +0 -389
- package/dist/starter_template.test.js +1 -69
- package/package.json +1 -1
package/dist/src/cli.js
CHANGED
|
@@ -29709,15 +29709,6 @@ var LoticsClient = class {
|
|
|
29709
29709
|
async listWorkspaces() {
|
|
29710
29710
|
return this.request("GET", "/v1/workspaces");
|
|
29711
29711
|
}
|
|
29712
|
-
/**
|
|
29713
|
-
* Resolve one workspace's info by id from the org's workspace list (the only
|
|
29714
|
-
* API-key-accessible source carrying `is_dev`). Returns null when the
|
|
29715
|
-
* workspace isn't visible to these credentials.
|
|
29716
|
-
*/
|
|
29717
|
-
async getWorkspaceInfo(id) {
|
|
29718
|
-
const workspaces = await this.listWorkspaces();
|
|
29719
|
-
return workspaces.find((w) => w.id === id) ?? null;
|
|
29720
|
-
}
|
|
29721
29712
|
async createWorkspace(body) {
|
|
29722
29713
|
return this.request("POST", "/v1/workspaces", body);
|
|
29723
29714
|
}
|
|
@@ -29785,7 +29776,7 @@ var LoticsClient = class {
|
|
|
29785
29776
|
/**
|
|
29786
29777
|
* Uninstall a standalone content package — delete the installation row. By
|
|
29787
29778
|
* default the package-bound docs are ARCHIVED; `keep_content` retains them as
|
|
29788
|
-
* ordinary workspace docs. Admin-only. Backs `lotics
|
|
29779
|
+
* ordinary workspace docs. Admin-only. Backs `lotics uninstall`.
|
|
29789
29780
|
*/
|
|
29790
29781
|
async uninstallContentPackage(installation_id, opts = {}) {
|
|
29791
29782
|
const qs = opts.keep_content ? "?keep_content=true" : "";
|
|
@@ -29833,7 +29824,7 @@ var LoticsClient = class {
|
|
|
29833
29824
|
);
|
|
29834
29825
|
}
|
|
29835
29826
|
/**
|
|
29836
|
-
* Uninstall a package installation (backs `lotics
|
|
29827
|
+
* Uninstall a package installation (backs `lotics uninstall`). Does
|
|
29837
29828
|
* everything DELETE does plus archives the installation's lifecycle
|
|
29838
29829
|
* artifacts; with `archive_tables` it also archives the scaffolded entity
|
|
29839
29830
|
* tables — refused server-side unless this installation created them
|
|
@@ -29851,10 +29842,11 @@ var LoticsClient = class {
|
|
|
29851
29842
|
return this.request("PATCH", `/v1/apps/${encodeURIComponent(app_id)}/package-config`, body);
|
|
29852
29843
|
}
|
|
29853
29844
|
/**
|
|
29854
|
-
* Retire (or `undo` un-retire) a registry package (backs `lotics
|
|
29855
|
-
*
|
|
29856
|
-
*
|
|
29857
|
-
* Owner-org
|
|
29845
|
+
* Retire (or `undo` un-retire) a registry package (backs `lotics app
|
|
29846
|
+
* unpublish` — the endpoint/audit action keep the `retire` name to avoid API
|
|
29847
|
+
* churn). Retiring refuses NEW installs and hides the package from non-owning
|
|
29848
|
+
* orgs; existing installations keep working and may still upgrade. Owner-org
|
|
29849
|
+
* admin-only.
|
|
29858
29850
|
*/
|
|
29859
29851
|
async retirePackage(package_id, body) {
|
|
29860
29852
|
return this.request("POST", `/v1/packages/${encodeURIComponent(package_id)}/retire`, body);
|
|
@@ -29869,38 +29861,12 @@ var LoticsClient = class {
|
|
|
29869
29861
|
async ejectPackage(app_id) {
|
|
29870
29862
|
return this.request("POST", `/v1/apps/${encodeURIComponent(app_id)}/eject`);
|
|
29871
29863
|
}
|
|
29872
|
-
/**
|
|
29873
|
-
* Extract a DRAFT app package from an existing bespoke app — the promotion
|
|
29874
|
-
* read (docs/packages.md § Promotion). Pure: nothing is written. Returns
|
|
29875
|
-
* the alias-keyed draft `contract` (opaque to the CLI — the server is the
|
|
29876
|
-
* validating authority), the origin workspace's `binding` (which doubles as
|
|
29877
|
-
* the adopt binding), a findings `report` (any `error` ⇒ not publishable
|
|
29878
|
-
* as-is), and the file-backed `template_files` the CLI must stage into the
|
|
29879
|
-
* project at their `bytes_ref` paths. Backs `lotics package extract`.
|
|
29880
|
-
* Admin-only.
|
|
29881
|
-
*/
|
|
29882
|
-
async extractPackage(app_id, opts = {}) {
|
|
29883
|
-
const qs = opts.knowledge && opts.knowledge.length > 0 ? `?knowledge=${encodeURIComponent(JSON.stringify(opts.knowledge))}` : "";
|
|
29884
|
-
return this.request("GET", `/v1/apps/${encodeURIComponent(app_id)}/package-extract${qs}`);
|
|
29885
|
-
}
|
|
29886
|
-
/**
|
|
29887
|
-
* Adopt a published package onto an EXISTING (bespoke or ejected) app — the
|
|
29888
|
-
* final promotion step. The workspace already holds the concrete objects, so
|
|
29889
|
-
* nothing is scaffolded or rewritten: the server verifies the `binding` is
|
|
29890
|
-
* complete, live, and FAITHFUL to the version's contract, then writes only the
|
|
29891
|
-
* installation pin (the app becomes installation #1, upgradeable again). A
|
|
29892
|
-
* ConflictError names the aliases that diverge. `version` omitted adopts the
|
|
29893
|
-
* latest. Backs `lotics package adopt`. Admin-only.
|
|
29894
|
-
*/
|
|
29895
|
-
async adoptPackage(app_id, body) {
|
|
29896
|
-
return this.request("POST", `/v1/apps/${encodeURIComponent(app_id)}/package-adopt`, body);
|
|
29897
|
-
}
|
|
29898
29864
|
/**
|
|
29899
29865
|
* Fleet upgrade — bring every installation of a package across the CALLER'S
|
|
29900
29866
|
* org to the target version (latest when omitted) in one call. Hands-off
|
|
29901
29867
|
* applies only where the preview is clean; installations with breaking/
|
|
29902
29868
|
* drift/modified-core findings are skipped and reported for the normal
|
|
29903
|
-
* per-installation consent flow. Backs `lotics
|
|
29869
|
+
* per-installation consent flow. Backs `lotics upgrade <package_id>` (fleet).
|
|
29904
29870
|
* Admin-only; org-scoped (no workspace header needed).
|
|
29905
29871
|
*/
|
|
29906
29872
|
/**
|
|
@@ -29922,16 +29888,10 @@ var LoticsClient = class {
|
|
|
29922
29888
|
body
|
|
29923
29889
|
);
|
|
29924
29890
|
}
|
|
29925
|
-
// ---
|
|
29926
|
-
|
|
29927
|
-
|
|
29928
|
-
|
|
29929
|
-
* local manifest has no `package_id`), then publishes version 1 against the
|
|
29930
|
-
* returned id. Admin-only.
|
|
29931
|
-
*/
|
|
29932
|
-
async createPackage(body) {
|
|
29933
|
-
return this.request("POST", "/v1/packages", body);
|
|
29934
|
-
}
|
|
29891
|
+
// --- Packages (registry reads + installations) ---
|
|
29892
|
+
// Authoring is server-side: apps via `POST /v1/apps/{id}/package-publish|release`,
|
|
29893
|
+
// content packages via the `publish_content`/`release_content` tools. There is no
|
|
29894
|
+
// client-side create-package / upload-bundle path.
|
|
29935
29895
|
/**
|
|
29936
29896
|
* Fetch a registry app package's metadata (incl. `kind`, `latest_version` and
|
|
29937
29897
|
* the Lotics-backed `is_official` trust badge). Admin-only; cross-tenant by id.
|
|
@@ -29943,33 +29903,6 @@ var LoticsClient = class {
|
|
|
29943
29903
|
async listPackageVersions(package_id) {
|
|
29944
29904
|
return this.request("GET", `/v1/packages/${encodeURIComponent(package_id)}/versions`);
|
|
29945
29905
|
}
|
|
29946
|
-
/**
|
|
29947
|
-
* Publish a new immutable package version — multipart upload of the alias-keyed
|
|
29948
|
-
* contract (JSON) + the prebuilt code bundle (a gzipped tarball carrying
|
|
29949
|
-
* `source.tar.gz` + `dist.tar.gz` members). The server validates the contract +
|
|
29950
|
-
* bundle, then allocates the next monotonic version. The `contract` is opaque
|
|
29951
|
-
* JSON to the transport (the server is the validating authority). Admin-only.
|
|
29952
|
-
*/
|
|
29953
|
-
async publishPackageVersion(package_id, args) {
|
|
29954
|
-
const formData = new FormData();
|
|
29955
|
-
formData.append("contract", JSON.stringify(args.contract));
|
|
29956
|
-
formData.append(
|
|
29957
|
-
"bundle",
|
|
29958
|
-
new Blob([new Uint8Array(args.bundle)], { type: "application/gzip" }),
|
|
29959
|
-
"bundle.tar.gz"
|
|
29960
|
-
);
|
|
29961
|
-
if (args.changelog) formData.append("changelog", args.changelog);
|
|
29962
|
-
if (args.channel) formData.append("channel", args.channel);
|
|
29963
|
-
const url2 = `${this.baseUrl}/v1/packages/${encodeURIComponent(package_id)}/versions`;
|
|
29964
|
-
const response = await fetch(url2, {
|
|
29965
|
-
method: "POST",
|
|
29966
|
-
headers: this.buildHeaders(),
|
|
29967
|
-
// no Content-Type — fetch sets the multipart boundary
|
|
29968
|
-
body: formData
|
|
29969
|
-
});
|
|
29970
|
-
if (!response.ok) await this.throwResponseError(response);
|
|
29971
|
-
return response.json();
|
|
29972
|
-
}
|
|
29973
29906
|
/**
|
|
29974
29907
|
* Upgrade a package installation to a newer published version — extends the
|
|
29975
29908
|
* binding additively, re-materializes the target version's
|
|
@@ -29996,18 +29929,6 @@ var LoticsClient = class {
|
|
|
29996
29929
|
`/v1/apps/${encodeURIComponent(app_id)}/package-upgrade${query}`
|
|
29997
29930
|
);
|
|
29998
29931
|
}
|
|
29999
|
-
/**
|
|
30000
|
-
* Re-bind a package role to a different workspace group (the current group
|
|
30001
|
-
* still exists). Re-materializes at the pinned version; refuses over
|
|
30002
|
-
* modified-core findings. Admin-only.
|
|
30003
|
-
*/
|
|
30004
|
-
async rebindPackageRole(app_id, body) {
|
|
30005
|
-
return this.request(
|
|
30006
|
-
"POST",
|
|
30007
|
-
`/v1/apps/${encodeURIComponent(app_id)}/package-rebind-role`,
|
|
30008
|
-
body
|
|
30009
|
-
);
|
|
30010
|
-
}
|
|
30011
29932
|
/**
|
|
30012
29933
|
* Workspace-wide dangling-reference sweep — active app/workflow artifacts
|
|
30013
29934
|
* whose prefixed schema ids no longer resolve. Backs
|
|
@@ -30025,13 +29946,73 @@ var LoticsClient = class {
|
|
|
30025
29946
|
return this.request("GET", `/v1/apps/${encodeURIComponent(app_id)}/package-health`);
|
|
30026
29947
|
}
|
|
30027
29948
|
/**
|
|
30028
|
-
*
|
|
30029
|
-
*
|
|
30030
|
-
*
|
|
30031
|
-
*
|
|
29949
|
+
* Preview a release — the dry run behind `lotics app release`. Runs the
|
|
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, 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,
|
|
29956
|
+
* owning-org only.
|
|
29957
|
+
*/
|
|
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
|
+
);
|
|
29968
|
+
}
|
|
29969
|
+
/**
|
|
29970
|
+
* Release — snapshot the origin app into the next registry version. The server
|
|
29971
|
+
* binding-aware-extracts it, repackages its deployed source + dist as the
|
|
29972
|
+
* bundle, publishes the next `release`-channel version with the changelog, and
|
|
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,
|
|
29977
|
+
* owning-org only. Backs `lotics app release --yes`.
|
|
29978
|
+
*/
|
|
29979
|
+
async releasePackage(app_id, body) {
|
|
29980
|
+
return this.request("POST", `/v1/apps/${encodeURIComponent(app_id)}/package-release`, body);
|
|
29981
|
+
}
|
|
29982
|
+
/**
|
|
29983
|
+
* Dry-run preview of a first-release — the `GET` behind `lotics app publish`
|
|
29984
|
+
* (no `--yes`), the publish-side analogue of `previewPackageRelease`. The
|
|
29985
|
+
* server runs the same fresh-alias extract + `src/` scan the apply runs
|
|
29986
|
+
* (through any `renames`) and returns the package name it would mint, the
|
|
29987
|
+
* auto-minted RENAMABLE aliases (the exact `--rename` keys), and the extract
|
|
29988
|
+
* findings (an `error` blocks the apply). No writes. Admin-only.
|
|
29989
|
+
*/
|
|
29990
|
+
async previewPublishAppPackage(app_id, opts = {}) {
|
|
29991
|
+
const params = new URLSearchParams();
|
|
29992
|
+
if (opts.knowledge !== void 0 && opts.knowledge.length > 0) {
|
|
29993
|
+
params.set("knowledge", JSON.stringify(opts.knowledge));
|
|
29994
|
+
}
|
|
29995
|
+
if (opts.renames !== void 0 && opts.renames.length > 0) {
|
|
29996
|
+
params.set("renames", JSON.stringify(opts.renames));
|
|
29997
|
+
}
|
|
29998
|
+
const query = params.toString();
|
|
29999
|
+
return this.request(
|
|
30000
|
+
"GET",
|
|
30001
|
+
`/v1/apps/${encodeURIComponent(app_id)}/package-publish${query ? `?${query}` : ""}`
|
|
30002
|
+
);
|
|
30003
|
+
}
|
|
30004
|
+
/**
|
|
30005
|
+
* First-release apply — mint a package from a BESPOKE app and publish v1 in one
|
|
30006
|
+
* call (the `POST` behind `lotics app publish --yes`). The server extracts an
|
|
30007
|
+
* alias-keyed contract from the app (fresh aliases; `renames` fixes them before
|
|
30008
|
+
* v1 freezes), creates the registry package (name/description from the app),
|
|
30009
|
+
* publishes v1 from the app's deployed source + dist, and pins the origin as
|
|
30010
|
+
* installation #1. Error findings from extract surface as a 409. An
|
|
30011
|
+
* already-linked app must use `releasePackage` instead. Admin-only. Backs
|
|
30012
|
+
* `lotics app publish <app_id>`.
|
|
30032
30013
|
*/
|
|
30033
|
-
async
|
|
30034
|
-
return this.request("POST", `/v1/apps/${encodeURIComponent(app_id)}/package-
|
|
30014
|
+
async publishAppAsPackage(app_id, body) {
|
|
30015
|
+
return this.request("POST", `/v1/apps/${encodeURIComponent(app_id)}/package-publish`, body);
|
|
30035
30016
|
}
|
|
30036
30017
|
/**
|
|
30037
30018
|
* Resolve the display name + fields (incl. select options) of the given tables
|
|
@@ -31360,379 +31341,6 @@ See https://lotics.ai/docs/app-sdk for the SDK reference.
|
|
|
31360
31341
|
}
|
|
31361
31342
|
];
|
|
31362
31343
|
}
|
|
31363
|
-
function buildPackageStarterOverrides(args) {
|
|
31364
|
-
const nameLit = JSON.stringify(args.app_name);
|
|
31365
|
-
return [
|
|
31366
|
-
{
|
|
31367
|
-
// The package App.tsx imports `../.lotics/app_fields` — a DETERMINISTIC
|
|
31368
|
-
// pure function of contract.json (generate_package_fields.ts) — so unlike
|
|
31369
|
-
// the app starter's fully-ignored `.lotics`, it must be committed or the
|
|
31370
|
-
// scaffold's own shipped CI (npm ci → typecheck/test/build on a clean
|
|
31371
|
-
// clone) fails on the missing module. The sync-written `.d.ts` companions
|
|
31372
|
-
// stay ignored: they need a live workspace and their absence only degrades
|
|
31373
|
-
// hooks to untyped overloads, never breaks the build.
|
|
31374
|
-
path: ".gitignore",
|
|
31375
|
-
content: `node_modules
|
|
31376
|
-
dist
|
|
31377
|
-
*.tsbuildinfo
|
|
31378
|
-
.DS_Store
|
|
31379
|
-
.lotics/*
|
|
31380
|
-
!.lotics/app_fields.ts
|
|
31381
|
-
coverage
|
|
31382
|
-
`
|
|
31383
|
-
},
|
|
31384
|
-
{
|
|
31385
|
-
path: "src/App.tsx",
|
|
31386
|
-
content: `import { useMemo, type ReactNode } from "react";
|
|
31387
|
-
import { ScrollView, View } from "react-native";
|
|
31388
|
-
import { useConfig, useQuery, readSelect, row } from "@lotics/app-sdk";
|
|
31389
|
-
import { AppRouter } from "@lotics/app-sdk/router";
|
|
31390
|
-
import { Text } from "@lotics/ui/text";
|
|
31391
|
-
import { Card } from "@lotics/ui/card";
|
|
31392
|
-
import { Button } from "@lotics/ui/button";
|
|
31393
|
-
import { OptionBadge } from "@lotics/ui/option_badge";
|
|
31394
|
-
import { Skeleton } from "@lotics/ui/skeleton";
|
|
31395
|
-
import { EmptyState } from "@lotics/ui/empty_state";
|
|
31396
|
-
import { Callout, CalloutTitle, CalloutText, CalloutActions } from "@lotics/ui/callout";
|
|
31397
|
-
import { F, OPT } from "../.lotics/app_fields";
|
|
31398
|
-
|
|
31399
|
-
// This is a PACKAGE starter \u2014 a workspace-agnostic blueprint installed into many
|
|
31400
|
-
// workspaces, each binding the contract's aliases to DIFFERENT concrete ids. So a
|
|
31401
|
-
// screen never hardcodes a \`fld_\u2026\`/\`opt_\u2026\` id: it addresses a field by contract
|
|
31402
|
-
// alias through \`F\` and a select option through \`OPT\`, both from the generated
|
|
31403
|
-
// \`.lotics/app_fields.ts\` (which resolves every alias to THIS installation's id at
|
|
31404
|
-
// module load from the binding). The aliases come from contract.json \u2014 entity
|
|
31405
|
-
// \`item\`, fields name/notes/status, options open/done, query \`items\`, config knob
|
|
31406
|
-
// \`heading\`. Edit contract.json, then \`lotics package sync\` to regenerate F/OPT.
|
|
31407
|
-
|
|
31408
|
-
interface Item {
|
|
31409
|
-
id: string;
|
|
31410
|
-
name: string;
|
|
31411
|
-
notes: string;
|
|
31412
|
-
status: ReturnType<typeof readSelect>[number] | null;
|
|
31413
|
-
done: boolean;
|
|
31414
|
-
}
|
|
31415
|
-
|
|
31416
|
-
// The \`items\` query is a bare from_entity, so a row is keyed by FIELD id: read a
|
|
31417
|
-
// cell as \`r[F.ITEM.<field>]\` and decode it with the SDK's pure readers (\`row.text\`,
|
|
31418
|
-
// \`readSelect\`). \`OPT.ITEM.status.done\` is this install's \`opt_\u2026\` id, so comparing
|
|
31419
|
-
// the cell's stored option key to it marks a done row.
|
|
31420
|
-
function decode(r: Record<string, unknown>): Item {
|
|
31421
|
-
const status = readSelect(r[F.ITEM.status])[0] ?? null;
|
|
31422
|
-
return {
|
|
31423
|
-
id: row.text(r.__source_record_id),
|
|
31424
|
-
name: row.text(r[F.ITEM.name]),
|
|
31425
|
-
notes: row.text(r[F.ITEM.notes]),
|
|
31426
|
-
status,
|
|
31427
|
-
done: status?.key === OPT.ITEM.status.done,
|
|
31428
|
-
};
|
|
31429
|
-
}
|
|
31430
|
-
|
|
31431
|
-
// Outer <View flex:1> claims the iframe height (index.html sets html/body/#root to
|
|
31432
|
-
// 100% + #root is a flex column); the list scrolls beneath the heading. Keep this
|
|
31433
|
-
// flex chain plain (not @lotics/ui/stack) so a fill-remaining-space child gets height.
|
|
31434
|
-
function Screen({ children }: { children: ReactNode }) {
|
|
31435
|
-
return (
|
|
31436
|
-
<View style={{ flex: 1 }}>
|
|
31437
|
-
<ScrollView contentContainerStyle={{ padding: 24, alignItems: "center" }}>
|
|
31438
|
-
<View style={{ maxWidth: 640, width: "100%", gap: 16 }}>{children}</View>
|
|
31439
|
-
</ScrollView>
|
|
31440
|
-
</View>
|
|
31441
|
-
);
|
|
31442
|
-
}
|
|
31443
|
-
|
|
31444
|
-
function ItemsScreen() {
|
|
31445
|
-
// \`heading\` is a contract config knob \u2014 an installation overrides it and
|
|
31446
|
-
// useConfig() renders the customized value; the literal here is only the fallback.
|
|
31447
|
-
const { config } = useConfig({ heading: ${nameLit} });
|
|
31448
|
-
const itemsQ = useQuery("items");
|
|
31449
|
-
const items = useMemo(() => itemsQ.rows.map(decode), [itemsQ.rows]);
|
|
31450
|
-
// First-load only: a skeleton while the very first fetch is in flight with no rows
|
|
31451
|
-
// yet \u2014 never blank already-loaded rows to a spinner on a background refetch.
|
|
31452
|
-
const firstLoad = itemsQ.loading && items.length === 0;
|
|
31453
|
-
|
|
31454
|
-
return (
|
|
31455
|
-
<Screen>
|
|
31456
|
-
<Text size="xxl" weight="semibold" level={1}>
|
|
31457
|
-
{config.heading}
|
|
31458
|
-
</Text>
|
|
31459
|
-
|
|
31460
|
-
{itemsQ.error ? (
|
|
31461
|
-
<Callout tone="error">
|
|
31462
|
-
<CalloutTitle>Couldn't load items</CalloutTitle>
|
|
31463
|
-
<CalloutText>{itemsQ.error}</CalloutText>
|
|
31464
|
-
<CalloutActions>
|
|
31465
|
-
<Button title="Try again" onPress={() => itemsQ.refetch()} />
|
|
31466
|
-
</CalloutActions>
|
|
31467
|
-
</Callout>
|
|
31468
|
-
) : firstLoad ? (
|
|
31469
|
-
<View style={{ gap: 10 }}>
|
|
31470
|
-
{[0, 1, 2].map((i) => (
|
|
31471
|
-
<Skeleton key={i} height={64} radius={12} />
|
|
31472
|
-
))}
|
|
31473
|
-
</View>
|
|
31474
|
-
) : items.length > 0 ? (
|
|
31475
|
-
<View style={{ gap: 10 }}>
|
|
31476
|
-
{items.map((item) => (
|
|
31477
|
-
<Card key={item.id}>
|
|
31478
|
-
<View style={{ flexDirection: "row", alignItems: "center", gap: 12 }}>
|
|
31479
|
-
<View style={{ flex: 1, gap: 2 }}>
|
|
31480
|
-
<Text weight="medium" color={item.done ? "muted" : undefined}>
|
|
31481
|
-
{item.name}
|
|
31482
|
-
</Text>
|
|
31483
|
-
{item.notes.length > 0 ? (
|
|
31484
|
-
<Text size="sm" color="muted" numberOfLines={1}>
|
|
31485
|
-
{item.notes}
|
|
31486
|
-
</Text>
|
|
31487
|
-
) : null}
|
|
31488
|
-
</View>
|
|
31489
|
-
<OptionBadge value={item.status} />
|
|
31490
|
-
</View>
|
|
31491
|
-
</Card>
|
|
31492
|
-
))}
|
|
31493
|
-
</View>
|
|
31494
|
-
) : (
|
|
31495
|
-
<EmptyState
|
|
31496
|
-
icon="list-checks"
|
|
31497
|
-
message="No items yet"
|
|
31498
|
-
hint="Rows in this package's table appear here."
|
|
31499
|
-
/>
|
|
31500
|
-
)}
|
|
31501
|
-
</Screen>
|
|
31502
|
-
);
|
|
31503
|
-
}
|
|
31504
|
-
|
|
31505
|
-
const routes = [{ path: "/", element: <ItemsScreen /> }];
|
|
31506
|
-
|
|
31507
|
-
export default function App() {
|
|
31508
|
-
return <AppRouter routes={routes} />;
|
|
31509
|
-
}
|
|
31510
|
-
`
|
|
31511
|
-
},
|
|
31512
|
-
{
|
|
31513
|
-
path: "src/App.test.tsx",
|
|
31514
|
-
content: `import { describe, test, expect, beforeEach, afterEach, vi } from "vitest";
|
|
31515
|
-
import { render, screen, cleanup } from "@testing-library/react";
|
|
31516
|
-
import App from "./App";
|
|
31517
|
-
|
|
31518
|
-
// Mock @lotics/app-sdk \u2014 the app's one external boundary (data + RPC + the package
|
|
31519
|
-
// binding). \`importOriginal\` keeps the PURE cell readers (\`row\`, \`readSelect\`) real,
|
|
31520
|
-
// so mock rows decode exactly like wire rows; only the hooks and getAppBinding are
|
|
31521
|
-
// stubbed. The binding ids below are what F.ITEM.* / OPT.ITEM.status.* resolve to;
|
|
31522
|
-
// the query rows are keyed by the SAME ids.
|
|
31523
|
-
const h = vi.hoisted(() => ({
|
|
31524
|
-
state: {
|
|
31525
|
-
rows: [] as Array<Record<string, unknown>>,
|
|
31526
|
-
loading: false,
|
|
31527
|
-
error: null as string | null,
|
|
31528
|
-
},
|
|
31529
|
-
binding: {
|
|
31530
|
-
fields: { "item.name": "fld_name", "item.notes": "fld_notes", "item.status": "fld_status" },
|
|
31531
|
-
options: { "item.status:open": "opt_open", "item.status:done": "opt_done" },
|
|
31532
|
-
roles: {},
|
|
31533
|
-
},
|
|
31534
|
-
}));
|
|
31535
|
-
|
|
31536
|
-
vi.mock("@lotics/app-sdk", async (importOriginal) => {
|
|
31537
|
-
const actual = await importOriginal<typeof import("@lotics/app-sdk")>();
|
|
31538
|
-
return {
|
|
31539
|
-
...actual,
|
|
31540
|
-
getAppBinding: async () => h.binding,
|
|
31541
|
-
useConfig: (defaults: Record<string, unknown>) => ({ config: defaults, loading: false }),
|
|
31542
|
-
useQuery: () => ({
|
|
31543
|
-
rows: h.state.rows,
|
|
31544
|
-
loading: h.state.loading,
|
|
31545
|
-
isValidating: false,
|
|
31546
|
-
error: h.state.error,
|
|
31547
|
-
refetch: () => {},
|
|
31548
|
-
}),
|
|
31549
|
-
};
|
|
31550
|
-
});
|
|
31551
|
-
|
|
31552
|
-
const rowOf = (id: string, name: string, notes: string, statusKey: string) => ({
|
|
31553
|
-
__source_record_id: id,
|
|
31554
|
-
fld_name: name,
|
|
31555
|
-
fld_notes: notes,
|
|
31556
|
-
fld_status: [{ key: statusKey, label: statusKey === "opt_done" ? "Done" : "Open" }],
|
|
31557
|
-
});
|
|
31558
|
-
|
|
31559
|
-
beforeEach(() => {
|
|
31560
|
-
h.state.rows = [];
|
|
31561
|
-
h.state.loading = false;
|
|
31562
|
-
h.state.error = null;
|
|
31563
|
-
});
|
|
31564
|
-
|
|
31565
|
-
// vitest globals are off, so @testing-library/react's automatic afterEach cleanup is
|
|
31566
|
-
// never registered \u2014 clean up explicitly or renders accumulate across tests and a
|
|
31567
|
-
// second render's duplicate matches fail getByText.
|
|
31568
|
-
afterEach(cleanup);
|
|
31569
|
-
|
|
31570
|
-
describe("App", () => {
|
|
31571
|
-
test("renders the heading and the loaded items with status badges", () => {
|
|
31572
|
-
h.state.rows = [
|
|
31573
|
-
rowOf("1", "First item", "with a note", "opt_open"),
|
|
31574
|
-
rowOf("2", "Second item", "", "opt_done"),
|
|
31575
|
-
];
|
|
31576
|
-
render(<App />);
|
|
31577
|
-
|
|
31578
|
-
expect(screen.getByText(${nameLit})).toBeTruthy();
|
|
31579
|
-
expect(screen.getByText("First item")).toBeTruthy();
|
|
31580
|
-
expect(screen.getByText("Second item")).toBeTruthy();
|
|
31581
|
-
expect(screen.getByText("with a note")).toBeTruthy();
|
|
31582
|
-
expect(screen.getByText("Open")).toBeTruthy();
|
|
31583
|
-
expect(screen.getByText("Done")).toBeTruthy();
|
|
31584
|
-
expect(screen.queryByText("No items yet")).toBeNull();
|
|
31585
|
-
});
|
|
31586
|
-
|
|
31587
|
-
test("shows the empty state when there are no items", () => {
|
|
31588
|
-
render(<App />);
|
|
31589
|
-
expect(screen.getByText("No items yet")).toBeTruthy();
|
|
31590
|
-
});
|
|
31591
|
-
|
|
31592
|
-
test("does not flash the list or empty state on first load", () => {
|
|
31593
|
-
h.state.loading = true;
|
|
31594
|
-
render(<App />);
|
|
31595
|
-
expect(screen.queryByText("No items yet")).toBeNull();
|
|
31596
|
-
});
|
|
31597
|
-
|
|
31598
|
-
test("surfaces a load error loudly", () => {
|
|
31599
|
-
h.state.error = "Network unreachable";
|
|
31600
|
-
render(<App />);
|
|
31601
|
-
expect(screen.getByText("Couldn't load items")).toBeTruthy();
|
|
31602
|
-
expect(screen.getByText("Network unreachable")).toBeTruthy();
|
|
31603
|
-
});
|
|
31604
|
-
});
|
|
31605
|
-
`
|
|
31606
|
-
},
|
|
31607
|
-
{
|
|
31608
|
-
path: "README.md",
|
|
31609
|
-
content: `# ${escapeHtml(args.app_name)}
|
|
31610
|
-
|
|
31611
|
-
A Lotics **app package** \u2014 a versioned, installable blueprint (a \`contract.json\`
|
|
31612
|
-
data model + app source) that installs into many workspaces. Authored locally,
|
|
31613
|
-
published and run through the \`lotics package\` CLI. See \`docs/app_packages.md\`.
|
|
31614
|
-
|
|
31615
|
-
## Dev loop
|
|
31616
|
-
|
|
31617
|
-
\`\`\`bash
|
|
31618
|
-
lotics workspace create "${escapeHtml(args.app_name)} dev" --dev # a throwaway dev workspace
|
|
31619
|
-
lotics package dev --workspace <dev_ws> # sync into it + run the dev server
|
|
31620
|
-
# edit contract.json \u2192 re-run \`lotics package sync\` to migrate + re-materialize
|
|
31621
|
-
# edit src/* \u2192 hot reload
|
|
31622
|
-
\`\`\`
|
|
31623
|
-
|
|
31624
|
-
\`sync\` / \`dev\` regenerate \`.lotics/app_fields.ts\` (the runtime \`F\` / \`OPT\` / \`ROLE\`
|
|
31625
|
-
surface) from \`contract.json\`, plus the typed \`.lotics/app_{queries,workflows,agents}.d.ts\`
|
|
31626
|
-
companions from the live installation \u2014 so \`useQuery\` / \`useWorkflow\` stay typed in the
|
|
31627
|
-
dev loop.
|
|
31628
|
-
|
|
31629
|
-
## Quality
|
|
31630
|
-
|
|
31631
|
-
\`\`\`bash
|
|
31632
|
-
npm run typecheck
|
|
31633
|
-
npm run lint
|
|
31634
|
-
npm test
|
|
31635
|
-
\`\`\`
|
|
31636
|
-
|
|
31637
|
-
## Publish
|
|
31638
|
-
|
|
31639
|
-
\`\`\`bash
|
|
31640
|
-
lotics package publish -m "v1" # build + publish an immutable version
|
|
31641
|
-
lotics package install <package_id> # install into any workspace
|
|
31642
|
-
\`\`\`
|
|
31643
|
-
|
|
31644
|
-
## The app screen
|
|
31645
|
-
|
|
31646
|
-
\`src/App.tsx\` reads the contract BY ALIAS through \`F\` / \`OPT\` (from the generated
|
|
31647
|
-
\`.lotics/app_fields.ts\`) \u2014 never a raw \`fld_\u2026\` / \`opt_\u2026\` id, since every install binds
|
|
31648
|
-
different concrete ids. It lists the \`items\` query and renders the \`heading\` config knob.
|
|
31649
|
-
\`@lotics/ui\` primitives render via react-native-web (aliased in \`vite.config.ts\`).
|
|
31650
|
-
|
|
31651
|
-
## Contract reference
|
|
31652
|
-
|
|
31653
|
-
\`contract.json\` is the package's alias-keyed data model
|
|
31654
|
-
(schema: \`@lotics/shared/schemas/app_packages\`). Every cross-reference is by **alias**;
|
|
31655
|
-
the materializer resolves alias \u2192 this workspace's concrete id at install.
|
|
31656
|
-
|
|
31657
|
-
### Aliases
|
|
31658
|
-
|
|
31659
|
-
- Entity / field / option / role / template / config aliases are lowercase slugs
|
|
31660
|
-
matching \`^[a-z][a-z0-9_]*$\`. A field's fully-qualified key is \`<entity>.<field>\`
|
|
31661
|
-
(e.g. \`item.status\`); a select option's is \`<entity>.<field>:<option>\`
|
|
31662
|
-
(e.g. \`item.status:done\`).
|
|
31663
|
-
- Query / workflow / agent aliases are runtime lookup keys (1\u2013200 chars) \u2014 the app
|
|
31664
|
-
source invokes them verbatim (\`useQuery("items")\`), so renaming one severs the call.
|
|
31665
|
-
|
|
31666
|
-
### Entities & fields
|
|
31667
|
-
|
|
31668
|
-
\`\`\`jsonc
|
|
31669
|
-
{ "alias": "item", "label": "Item", "fields": [
|
|
31670
|
-
{ "alias": "name", "label": "Name", "type": "text", "required": true },
|
|
31671
|
-
{ "alias": "status", "label": "Status", "type": "select",
|
|
31672
|
-
"options": [ { "alias": "open", "label": "Open", "color": "blue" },
|
|
31673
|
-
{ "alias": "done", "label": "Done", "color": "green" } ] } ] }
|
|
31674
|
-
\`\`\`
|
|
31675
|
-
|
|
31676
|
-
Field \`type\`: \`text\`, \`number\`, \`date\`, \`boolean\`, \`select\` (+ \`options\`),
|
|
31677
|
-
\`select_member\`, \`select_record_link\` (\`target_entity\` = an entity alias), \`files\`,
|
|
31678
|
-
\`formula\` (expression references same-entity fields as \`{alias}\`), \`rollup\`, \`lookup\`,
|
|
31679
|
-
\`autonumber\`. \`required\` is advisory (app / workflow-layer UX only \u2014 the table model
|
|
31680
|
-
has no required constraint). Select \`options\` are \`{ alias, label, color }\`.
|
|
31681
|
-
|
|
31682
|
-
### Queries
|
|
31683
|
-
|
|
31684
|
-
Alias-form AST \u2014 the same node kinds as the runtime query engine, except a
|
|
31685
|
-
\`from_table\` node carries \`from_entity\` (an entity alias) instead of a \`table_id\`:
|
|
31686
|
-
|
|
31687
|
-
\`\`\`jsonc
|
|
31688
|
-
{ "alias": "items", "ast": {
|
|
31689
|
-
"kind": "from_table", "from_entity": "item",
|
|
31690
|
-
"sort": [ { "field_key": "name", "order": "asc" } ] } }
|
|
31691
|
-
\`\`\`
|
|
31692
|
-
|
|
31693
|
-
### Workflows
|
|
31694
|
-
|
|
31695
|
-
The sole app-side mutation path. Each workflow has a \`trigger\`, typed \`inputs\` /
|
|
31696
|
-
\`outputs\`, and a JS-subset \`body\`.
|
|
31697
|
-
|
|
31698
|
-
- Trigger \u2014 app-invoked or table-lifecycle:
|
|
31699
|
-
- \`{ "type": "app" }\` \u2014 called by alias from the app (\`useWorkflow("<alias>")\`).
|
|
31700
|
-
- \`{ "type": "entity_lifecycle", "entity": "<entity>", "event": "<event>" }\` \u2014 fires
|
|
31701
|
-
on the bound table. \`event\` \u2208 \`before_create\`, \`after_create\`, \`before_update\`,
|
|
31702
|
-
\`after_update\`, \`before_delete\`, \`after_delete\`.
|
|
31703
|
-
- Typed \`inputs\` (\`text\` / \`number\` / \`date\` / \`member\` / \`select\` / \`record_link\`,
|
|
31704
|
-
plus \`object\` / \`array\`):
|
|
31705
|
-
- \`record_link\` \u2014 its \`table_id\` names an **entity alias**.
|
|
31706
|
-
- \`member\` \u2014 its \`group\` names a **role alias**.
|
|
31707
|
-
- \`select\` \u2014 an option \`value\` in \`entity.field:option\` form binds to that option's
|
|
31708
|
-
id (a plain value is a literal, left as-is).
|
|
31709
|
-
- Body **sentinel tokens** \u2014 a body addresses package objects by reserved tokens the
|
|
31710
|
-
materializer rewrites to concrete ids at install. Always inside a **string literal**:
|
|
31711
|
-
- \`@@entity:<entity>@@\`
|
|
31712
|
-
- \`@@field:<entity>.<field>@@\`
|
|
31713
|
-
- \`@@option:<entity>.<field>:<option>@@\`
|
|
31714
|
-
- \`@@role:<role>@@\`
|
|
31715
|
-
- \`@@template:<template>@@\`
|
|
31716
|
-
|
|
31717
|
-
### Config knobs
|
|
31718
|
-
|
|
31719
|
-
Typed customization knobs the app reads via \`useConfig()\`. Each has an \`alias\`,
|
|
31720
|
-
\`label\`, \`type\`, and \`default\`:
|
|
31721
|
-
|
|
31722
|
-
- \`text\` \u2192 a string default; \`boolean\` \u2192 a boolean; \`number\` \u2192 a number;
|
|
31723
|
-
\`color\` \u2192 a palette color token; \`select\` \u2192 \`options: [{ value, label }]\` with the
|
|
31724
|
-
\`default\` being one of those \`value\`s.
|
|
31725
|
-
|
|
31726
|
-
\`\`\`jsonc
|
|
31727
|
-
{ "alias": "heading", "label": "List heading", "type": "text", "default": "Items" }
|
|
31728
|
-
\`\`\`
|
|
31729
|
-
|
|
31730
|
-
Publish validates the whole contract (alias uniqueness, every cross-reference
|
|
31731
|
-
resolves, sentinel tokens name declared objects) before storing the version.
|
|
31732
|
-
`
|
|
31733
|
-
}
|
|
31734
|
-
];
|
|
31735
|
-
}
|
|
31736
31344
|
function escapeHtml(s) {
|
|
31737
31345
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
31738
31346
|
}
|
|
@@ -32491,7 +32099,7 @@ function inputDeclToTsType(decl) {
|
|
|
32491
32099
|
}
|
|
32492
32100
|
return null;
|
|
32493
32101
|
}).filter((v) => v !== null);
|
|
32494
|
-
const inner = literals.length > 0 ? literals.join(" | ") : "string";
|
|
32102
|
+
const inner = literals.length > 0 ? `${literals.join(" | ")} | (string & {})` : "string";
|
|
32495
32103
|
return decl.multi === true ? `ReadonlyArray<${inner}>` : inner;
|
|
32496
32104
|
}
|
|
32497
32105
|
case "date_range":
|
|
@@ -32543,7 +32151,7 @@ function outputDeclToTsType(decl) {
|
|
|
32543
32151
|
const literals = options.map(
|
|
32544
32152
|
(o) => o !== null && typeof o === "object" && "value" in o && typeof o.value === "string" ? JSON.stringify(o.value) : null
|
|
32545
32153
|
).filter((v) => v !== null);
|
|
32546
|
-
const inner = literals.length > 0 ? literals.join(" | ") : "string";
|
|
32154
|
+
const inner = literals.length > 0 ? `${literals.join(" | ")} | (string & {})` : "string";
|
|
32547
32155
|
return decl.multi === true ? `ReadonlyArray<${inner}>` : inner;
|
|
32548
32156
|
}
|
|
32549
32157
|
case "object": {
|
|
@@ -32802,6 +32410,123 @@ export type AppOptions = typeof OPT;
|
|
|
32802
32410
|
`;
|
|
32803
32411
|
}
|
|
32804
32412
|
|
|
32413
|
+
// src/generate_package_fields.ts
|
|
32414
|
+
var HEADER5 = `// Auto-generated by 'lotics app codegen' (linked/published app; also app pull/dev/deploy).
|
|
32415
|
+
// DO NOT EDIT \u2014 regenerated from the installation's live binding.
|
|
32416
|
+
//
|
|
32417
|
+
// A package installation resolves F/OPT/ROLE at MODULE LOAD from its binding
|
|
32418
|
+
// (contract alias \u2192 THIS workspace's concrete id) via the SDK's \`binding\` RPC.
|
|
32419
|
+
// Top-level await: the module graph waits for the binding before any importer
|
|
32420
|
+
// evaluates, so every entry is a plain string.
|
|
32421
|
+
import { getAppBinding } from "@lotics/app-sdk";
|
|
32422
|
+
|
|
32423
|
+
const binding = await getAppBinding();
|
|
32424
|
+
|
|
32425
|
+
function bound(map: Record<string, string>, key: string, kind: string): string {
|
|
32426
|
+
const id = map[key];
|
|
32427
|
+
if (id === undefined) {
|
|
32428
|
+
throw new Error(
|
|
32429
|
+
\`app_fields: \${kind} "\${key}" is not in this installation's binding \u2014 \` +
|
|
32430
|
+
\`the generated app_fields.ts is stale relative to the installed contract version.\`,
|
|
32431
|
+
);
|
|
32432
|
+
}
|
|
32433
|
+
return id;
|
|
32434
|
+
}
|
|
32435
|
+
`;
|
|
32436
|
+
function parseBinding(binding) {
|
|
32437
|
+
const entities = [];
|
|
32438
|
+
const entityByAlias = /* @__PURE__ */ new Map();
|
|
32439
|
+
const fieldByKey = /* @__PURE__ */ new Map();
|
|
32440
|
+
for (const key of Object.keys(binding.fields)) {
|
|
32441
|
+
const dot = key.indexOf(".");
|
|
32442
|
+
if (dot <= 0 || dot >= key.length - 1) continue;
|
|
32443
|
+
const entityAlias = key.slice(0, dot);
|
|
32444
|
+
const fieldAlias = key.slice(dot + 1);
|
|
32445
|
+
let entity = entityByAlias.get(entityAlias);
|
|
32446
|
+
if (entity === void 0) {
|
|
32447
|
+
entity = { alias: entityAlias, fields: [] };
|
|
32448
|
+
entityByAlias.set(entityAlias, entity);
|
|
32449
|
+
entities.push(entity);
|
|
32450
|
+
}
|
|
32451
|
+
const field = { alias: fieldAlias, key, options: [] };
|
|
32452
|
+
entity.fields.push(field);
|
|
32453
|
+
fieldByKey.set(key, field);
|
|
32454
|
+
}
|
|
32455
|
+
for (const key of Object.keys(binding.options)) {
|
|
32456
|
+
const colon = key.indexOf(":");
|
|
32457
|
+
if (colon <= 0 || colon >= key.length - 1) continue;
|
|
32458
|
+
const fieldKey = key.slice(0, colon);
|
|
32459
|
+
const optionAlias = key.slice(colon + 1);
|
|
32460
|
+
const field = fieldByKey.get(fieldKey);
|
|
32461
|
+
if (field === void 0) continue;
|
|
32462
|
+
field.options.push({ alias: optionAlias, key });
|
|
32463
|
+
}
|
|
32464
|
+
return { entities, roles: Object.keys(binding.roles) };
|
|
32465
|
+
}
|
|
32466
|
+
function emitFieldMap2(entities) {
|
|
32467
|
+
if (entities.length === 0) return `export const F = {} as const;`;
|
|
32468
|
+
const blocks = entities.map((entity) => {
|
|
32469
|
+
const lines = entity.fields.map(
|
|
32470
|
+
(field) => ` ${propKey(field.alias)}: bound(binding.fields, ${JSON.stringify(field.key)}, "field"),`
|
|
32471
|
+
);
|
|
32472
|
+
return ` ${propKey(entity.alias.toUpperCase())}: {
|
|
32473
|
+
${lines.join("\n")}
|
|
32474
|
+
},`;
|
|
32475
|
+
});
|
|
32476
|
+
return `export const F = {
|
|
32477
|
+
${blocks.join("\n")}
|
|
32478
|
+
} as const;`;
|
|
32479
|
+
}
|
|
32480
|
+
function emitOptionMap2(entities) {
|
|
32481
|
+
const entityBlocks = [];
|
|
32482
|
+
for (const entity of entities) {
|
|
32483
|
+
const fieldBlocks = [];
|
|
32484
|
+
for (const field of entity.fields) {
|
|
32485
|
+
if (field.options.length === 0) continue;
|
|
32486
|
+
const lines = field.options.map(
|
|
32487
|
+
(option) => ` ${propKey(option.alias)}: bound(binding.options, ${JSON.stringify(option.key)}, "option"),`
|
|
32488
|
+
);
|
|
32489
|
+
fieldBlocks.push(` ${propKey(field.alias)}: {
|
|
32490
|
+
${lines.join("\n")}
|
|
32491
|
+
},`);
|
|
32492
|
+
}
|
|
32493
|
+
if (fieldBlocks.length === 0) continue;
|
|
32494
|
+
entityBlocks.push(` ${propKey(entity.alias.toUpperCase())}: {
|
|
32495
|
+
${fieldBlocks.join("\n")}
|
|
32496
|
+
},`);
|
|
32497
|
+
}
|
|
32498
|
+
if (entityBlocks.length === 0) return `export const OPT = {} as const;`;
|
|
32499
|
+
return `export const OPT = {
|
|
32500
|
+
${entityBlocks.join("\n")}
|
|
32501
|
+
} as const;`;
|
|
32502
|
+
}
|
|
32503
|
+
function emitRoleMap(roles) {
|
|
32504
|
+
if (roles.length === 0) return `export const ROLE = {} as const;`;
|
|
32505
|
+
const lines = roles.map(
|
|
32506
|
+
(role) => ` ${propKey(role)}: bound(binding.roles, ${JSON.stringify(role)}, "role"),`
|
|
32507
|
+
);
|
|
32508
|
+
return `export const ROLE = {
|
|
32509
|
+
${lines.join("\n")}
|
|
32510
|
+
} as const;`;
|
|
32511
|
+
}
|
|
32512
|
+
function generatePackageAppFields(binding) {
|
|
32513
|
+
const { entities, roles } = parseBinding(binding);
|
|
32514
|
+
return `${HEADER5}
|
|
32515
|
+
${emitFieldMap2(entities)}
|
|
32516
|
+
|
|
32517
|
+
${emitOptionMap2(entities)}
|
|
32518
|
+
|
|
32519
|
+
${emitRoleMap(roles)}
|
|
32520
|
+
|
|
32521
|
+
/** Field-id alias map: \`F[<ENTITY>][<field>]\` is this installation's \`fld_\u2026\` id. */
|
|
32522
|
+
export type AppFields = typeof F;
|
|
32523
|
+
/** Select-option alias map: \`OPT[<ENTITY>][<field>][<option>]\` is this installation's \`opt_\u2026\` id. */
|
|
32524
|
+
export type AppOptions = typeof OPT;
|
|
32525
|
+
/** Role alias map: \`ROLE[<role>]\` is this installation's \`grp_\u2026\` id. */
|
|
32526
|
+
export type AppRoles = typeof ROLE;
|
|
32527
|
+
`;
|
|
32528
|
+
}
|
|
32529
|
+
|
|
32805
32530
|
// src/app_workflow_check.ts
|
|
32806
32531
|
import fs3 from "node:fs";
|
|
32807
32532
|
import path4 from "node:path";
|
|
@@ -33128,6 +32853,13 @@ function writeAppFields(projectDir, tables) {
|
|
|
33128
32853
|
fs4.writeFileSync(file2, generateAppFields(tables));
|
|
33129
32854
|
return file2;
|
|
33130
32855
|
}
|
|
32856
|
+
function writeBindingAppFields(projectDir, binding) {
|
|
32857
|
+
const dotLotics = path5.join(projectDir, ".lotics");
|
|
32858
|
+
fs4.mkdirSync(dotLotics, { recursive: true });
|
|
32859
|
+
const file2 = path5.join(dotLotics, "app_fields.ts");
|
|
32860
|
+
fs4.writeFileSync(file2, generatePackageAppFields(binding));
|
|
32861
|
+
return file2;
|
|
32862
|
+
}
|
|
33131
32863
|
async function appCodegen(args) {
|
|
33132
32864
|
const projectDir = path5.resolve(args.projectDir ?? process.cwd());
|
|
33133
32865
|
const meta3 = readAppMeta(projectDir);
|
|
@@ -33143,14 +32875,24 @@ async function appCodegen(args) {
|
|
|
33143
32875
|
);
|
|
33144
32876
|
return;
|
|
33145
32877
|
}
|
|
33146
|
-
const tableIds = resolveCodegenTableIds(projectDir, meta3.queries ?? {});
|
|
33147
32878
|
try {
|
|
33148
|
-
const
|
|
33149
|
-
|
|
33150
|
-
|
|
32879
|
+
const app = await args.client.getApp(meta3.app_id);
|
|
32880
|
+
if (app.package_id) {
|
|
32881
|
+
const binding = await args.client.appBinding(meta3.app_id);
|
|
32882
|
+
const fieldsPath = writeBindingAppFields(projectDir, binding);
|
|
32883
|
+
const count = Object.keys(binding.fields).length;
|
|
32884
|
+
console.error(
|
|
32885
|
+
`Regenerated ${fieldsPath} (binding form \u2014 ${count} field alias${count === 1 ? "" : "es"} resolved per-install)`
|
|
32886
|
+
);
|
|
32887
|
+
} else {
|
|
32888
|
+
const tableIds = resolveCodegenTableIds(projectDir, meta3.queries ?? {});
|
|
32889
|
+
const tables = await args.client.getWorkspaceSchema(tableIds);
|
|
32890
|
+
const fieldsPath = writeAppFields(projectDir, tables);
|
|
32891
|
+
console.error(`Regenerated ${fieldsPath} (${tables.length} table${tables.length === 1 ? "" : "s"})`);
|
|
32892
|
+
}
|
|
33151
32893
|
} catch (err2) {
|
|
33152
32894
|
console.error(
|
|
33153
|
-
`\u26A0 Could not
|
|
32895
|
+
`\u26A0 Could not regenerate .lotics/app_fields.ts (${err2 instanceof Error ? err2.message : String(err2)}). Kept the existing file.`
|
|
33154
32896
|
);
|
|
33155
32897
|
}
|
|
33156
32898
|
await refreshWorkflowGlobals(args.client, projectDir, meta3.app_id, Object.keys(meta3.workflows ?? {}));
|
|
@@ -33834,10 +33576,8 @@ function escapeRegExp(s) {
|
|
|
33834
33576
|
}
|
|
33835
33577
|
|
|
33836
33578
|
// src/package_commands.ts
|
|
33837
|
-
import
|
|
33838
|
-
import
|
|
33839
|
-
import { tmpdir as tmpdir2 } from "node:os";
|
|
33840
|
-
import { createHash } from "node:crypto";
|
|
33579
|
+
import fs5 from "node:fs";
|
|
33580
|
+
import path6 from "node:path";
|
|
33841
33581
|
|
|
33842
33582
|
// ../../node_modules/zod/v4/classic/external.js
|
|
33843
33583
|
var external_exports = {};
|
|
@@ -49725,7 +49465,7 @@ var appSchema = zod_default.object({
|
|
|
49725
49465
|
// avoid a circular import; the shapes mirror Binding / AppInstallationConfig.
|
|
49726
49466
|
package_id: zod_default.string().nullable().optional().describe("App-package registry id this app was installed from; null for a bespoke app or after eject."),
|
|
49727
49467
|
package_version: zod_default.number().int().nullable().optional().describe("Installed package version (the upgrade pin); null for a bespoke app or after eject."),
|
|
49728
|
-
binding: zod_default.record(zod_default.string(), zod_default.record(zod_default.string(), zod_default.string())).nullable().optional().describe("App-package install join: per-namespace (entities/fields/options/templates/roles) alias \u2192 concrete-id maps. Null for a bespoke app."),
|
|
49468
|
+
binding: zod_default.record(zod_default.string(), zod_default.record(zod_default.string(), zod_default.string())).nullable().optional().describe("App-package install join: per-namespace (entities/fields/options/templates/roles/workflows/knowledge) alias \u2192 concrete-id maps. Null for a bespoke app."),
|
|
49729
49469
|
config: zod_default.record(zod_default.string(), zod_default.union([zod_default.string(), zod_default.number(), zod_default.boolean()])).nullable().optional().describe("Installation-level customization config values (the customization-ladder first rung). Null for a bespoke app."),
|
|
49730
49470
|
ejected_at: zod_default.string().nullable().optional().describe("When the app was ejected from its package (severing upgrades); null if never ejected."),
|
|
49731
49471
|
created_at: zod_default.string().describe("Timestamp when app was created"),
|
|
@@ -49768,7 +49508,6 @@ var workspaceSchema = zod_default.object({
|
|
|
49768
49508
|
default_currency: zod_default.string().describe("Default currency code for the workspace"),
|
|
49769
49509
|
timezone: zod_default.string().describe("IANA timezone name (e.g., 'America/New_York', 'Asia/Tokyo')"),
|
|
49770
49510
|
organization_id: zod_default.string().describe("Organization ID this workspace belongs to"),
|
|
49771
|
-
is_dev: zod_default.boolean().optional().describe("Marks a throwaway app-package dev workspace \u2014 gates `lotics package dev/sync/reset`."),
|
|
49772
49511
|
created_at: zod_default.string().describe("Timestamp when workspace was created")
|
|
49773
49512
|
});
|
|
49774
49513
|
var memberGroupSchema = zod_default.object({
|
|
@@ -50315,7 +50054,14 @@ var bindingSchema = zod_default.object({
|
|
|
50315
50054
|
// it, and hashes look rows up through it. Excluded from BINDING_NAMESPACES:
|
|
50316
50055
|
// a deleted row is a modified-core finding (fingerprints), never binding
|
|
50317
50056
|
// drift, so validateBinding/applyDriftResolutions ignore it.
|
|
50318
|
-
workflows: bindingMapSchema.default({})
|
|
50057
|
+
workflows: bindingMapSchema.default({}),
|
|
50058
|
+
// The app's BUNDLED knowledge corpus: contract knowledge alias → the
|
|
50059
|
+
// workspace's concrete `kdc_` id. One row, one pin — the app installation IS
|
|
50060
|
+
// the anchor for its bundled docs (`package_content_installations` rows are
|
|
50061
|
+
// standalone content packages only). Excluded from BINDING_NAMESPACES: a
|
|
50062
|
+
// gone/edited doc is detected by the knowledge upgrade preview (live doc +
|
|
50063
|
+
// content-sha reads), never by validateBinding's live-id sweep.
|
|
50064
|
+
knowledge: bindingMapSchema.default({})
|
|
50319
50065
|
});
|
|
50320
50066
|
var BINDING_NAMESPACES = ["entities", "fields", "options", "templates", "roles"];
|
|
50321
50067
|
var contentBindingMapSchema = zod_default.record(zod_default.string().min(1), zod_default.string().min(1));
|
|
@@ -50349,10 +50095,6 @@ var packageArtifactHashesSchema = zod_default.object({
|
|
|
50349
50095
|
agents: zod_default.record(zod_default.string(), zod_default.string())
|
|
50350
50096
|
});
|
|
50351
50097
|
var modificationResolutionSchema = zod_default.union([zod_default.literal("revert"), zod_default.literal("keep")]);
|
|
50352
|
-
var upgradeResolutionsSchema = zod_default.record(
|
|
50353
|
-
zod_default.string(),
|
|
50354
|
-
zod_default.union([driftResolutionSchema, modificationResolutionSchema])
|
|
50355
|
-
);
|
|
50356
50098
|
var KNOWLEDGE_UPGRADE_CHANGES = ["added", "changed", "removed", "drifted"];
|
|
50357
50099
|
var knowledgeUpgradeEntrySchema = zod_default.object({
|
|
50358
50100
|
alias: zod_default.string(),
|
|
@@ -50381,15 +50123,15 @@ function validKnowledgeResolutions(change) {
|
|
|
50381
50123
|
return [];
|
|
50382
50124
|
}
|
|
50383
50125
|
}
|
|
50126
|
+
var upgradeResolutionsSchema = zod_default.record(
|
|
50127
|
+
zod_default.string(),
|
|
50128
|
+
zod_default.union([driftResolutionSchema, modificationResolutionSchema, knowledgeResolutionSchema])
|
|
50129
|
+
);
|
|
50384
50130
|
var knowledgeBindConsentSchema = zod_default.record(zod_default.string(), zod_default.string());
|
|
50385
50131
|
var knowledgeUpgradeResolutionsSchema = zod_default.object({
|
|
50386
50132
|
resolutions: zod_default.record(zod_default.string(), knowledgeResolutionSchema).default({}),
|
|
50387
50133
|
bind_to: knowledgeBindConsentSchema.default({})
|
|
50388
50134
|
});
|
|
50389
|
-
var contentUpgradeResolutionsSchema = zod_default.object({
|
|
50390
|
-
knowledge: knowledgeUpgradeResolutionsSchema.default({ resolutions: {}, bind_to: {} }),
|
|
50391
|
-
templates: zod_default.record(zod_default.string(), modificationResolutionSchema).default({})
|
|
50392
|
-
});
|
|
50393
50135
|
var knowledgeBoundRefSchema = zod_default.object({
|
|
50394
50136
|
alias: zod_default.string(),
|
|
50395
50137
|
name: zod_default.string(),
|
|
@@ -50403,261 +50145,38 @@ var appInstallationConfigSchema = zod_default.record(
|
|
|
50403
50145
|
);
|
|
50404
50146
|
var appInstallationProvenanceSchema = zod_default.enum(["install", "adopt"]);
|
|
50405
50147
|
|
|
50406
|
-
// src/generate_package_fields.ts
|
|
50407
|
-
var HEADER5 = `// Auto-generated by 'lotics package new/extract/dev/sync' from contract.json.
|
|
50408
|
-
// DO NOT EDIT \u2014 regenerated whenever the contract changes.
|
|
50409
|
-
//
|
|
50410
|
-
// Package apps resolve F/OPT/ROLE at MODULE LOAD from the installation's
|
|
50411
|
-
// binding (contract alias \u2192 THIS workspace's concrete id) via the SDK's
|
|
50412
|
-
// \`binding\` RPC. Top-level await: the module graph waits for the binding
|
|
50413
|
-
// before any importer evaluates, so every entry is a plain string.
|
|
50414
|
-
import { getAppBinding } from "@lotics/app-sdk";
|
|
50415
|
-
|
|
50416
|
-
const binding = await getAppBinding();
|
|
50417
|
-
|
|
50418
|
-
function bound(map: Record<string, string>, key: string, kind: string): string {
|
|
50419
|
-
const id = map[key];
|
|
50420
|
-
if (id === undefined) {
|
|
50421
|
-
throw new Error(
|
|
50422
|
-
\`app_fields: \${kind} "\${key}" is not in this installation's binding \u2014 \` +
|
|
50423
|
-
\`the generated app_fields.ts is stale relative to the installed contract version.\`,
|
|
50424
|
-
);
|
|
50425
|
-
}
|
|
50426
|
-
return id;
|
|
50427
|
-
}
|
|
50428
|
-
`;
|
|
50429
|
-
function generatePackageAppFields(contract) {
|
|
50430
|
-
const entities = contract.entities ?? [];
|
|
50431
|
-
const roles = contract.roles ?? [];
|
|
50432
|
-
const fieldBlocks = [];
|
|
50433
|
-
for (const entity of entities) {
|
|
50434
|
-
const lines = (entity.fields ?? []).map(
|
|
50435
|
-
(field) => ` ${propKey(field.alias)}: bound(binding.fields, ${JSON.stringify(`${entity.alias}.${field.alias}`)}, "field"),`
|
|
50436
|
-
);
|
|
50437
|
-
if (lines.length === 0) continue;
|
|
50438
|
-
fieldBlocks.push(` ${propKey(entity.alias.toUpperCase())}: {
|
|
50439
|
-
${lines.join("\n")}
|
|
50440
|
-
},`);
|
|
50441
|
-
}
|
|
50442
|
-
const optionBlocks = [];
|
|
50443
|
-
for (const entity of entities) {
|
|
50444
|
-
const perField = [];
|
|
50445
|
-
for (const field of entity.fields ?? []) {
|
|
50446
|
-
const options = field.options ?? [];
|
|
50447
|
-
if (options.length === 0) continue;
|
|
50448
|
-
const lines = options.map(
|
|
50449
|
-
(option) => ` ${propKey(option.alias)}: bound(binding.options, ${JSON.stringify(`${entity.alias}.${field.alias}:${option.alias}`)}, "option"),`
|
|
50450
|
-
);
|
|
50451
|
-
perField.push(` ${propKey(field.alias)}: {
|
|
50452
|
-
${lines.join("\n")}
|
|
50453
|
-
},`);
|
|
50454
|
-
}
|
|
50455
|
-
if (perField.length > 0) {
|
|
50456
|
-
optionBlocks.push(` ${propKey(entity.alias.toUpperCase())}: {
|
|
50457
|
-
${perField.join("\n")}
|
|
50458
|
-
},`);
|
|
50459
|
-
}
|
|
50460
|
-
}
|
|
50461
|
-
const fMap = fieldBlocks.length > 0 ? `export const F = {
|
|
50462
|
-
${fieldBlocks.join("\n")}
|
|
50463
|
-
} as const;` : `export const F = {} as const;`;
|
|
50464
|
-
const optMap = optionBlocks.length > 0 ? `export const OPT = {
|
|
50465
|
-
${optionBlocks.join("\n")}
|
|
50466
|
-
} as const;` : `export const OPT = {} as const;`;
|
|
50467
|
-
const roleMap = roles.length > 0 ? `export const ROLE = {
|
|
50468
|
-
${roles.map((role) => ` ${propKey(role.alias)}: bound(binding.roles, ${JSON.stringify(role.alias)}, "role"),`).join("\n")}
|
|
50469
|
-
} as const;` : `export const ROLE = {} as const;`;
|
|
50470
|
-
return `${HEADER5}
|
|
50471
|
-
${fMap}
|
|
50472
|
-
|
|
50473
|
-
${optMap}
|
|
50474
|
-
|
|
50475
|
-
${roleMap}
|
|
50476
|
-
|
|
50477
|
-
/** Field-id alias map: \`F[<ENTITY>][<field>]\` is this installation's \`fld_\u2026\` id. */
|
|
50478
|
-
export type AppFields = typeof F;
|
|
50479
|
-
/** Select-option alias map: \`OPT[<ENTITY>][<field>][<option>]\` is this installation's \`opt_\u2026\` id. */
|
|
50480
|
-
export type AppOptions = typeof OPT;
|
|
50481
|
-
/** Role alias map: \`ROLE[<role>]\` is this installation's \`grp_\u2026\` id. */
|
|
50482
|
-
export type AppRoles = typeof ROLE;
|
|
50483
|
-
`;
|
|
50484
|
-
}
|
|
50485
|
-
|
|
50486
|
-
// src/file_command_io.ts
|
|
50487
|
-
import fs5 from "node:fs";
|
|
50488
|
-
import path6 from "node:path";
|
|
50489
|
-
var CliError = class extends Error {
|
|
50490
|
-
constructor(message) {
|
|
50491
|
-
super(message);
|
|
50492
|
-
this.name = "CliError";
|
|
50493
|
-
}
|
|
50494
|
-
};
|
|
50495
|
-
function fail(message) {
|
|
50496
|
-
throw new CliError(message);
|
|
50497
|
-
}
|
|
50498
|
-
function writeFileAtomic(filePath, bytes) {
|
|
50499
|
-
const dir = path6.dirname(path6.resolve(filePath));
|
|
50500
|
-
const tmp = path6.join(dir, `.${path6.basename(filePath)}.${process.pid}.${Date.now()}.tmp`);
|
|
50501
|
-
fs5.writeFileSync(tmp, bytes);
|
|
50502
|
-
try {
|
|
50503
|
-
fs5.renameSync(tmp, filePath);
|
|
50504
|
-
} catch (error51) {
|
|
50505
|
-
try {
|
|
50506
|
-
fs5.unlinkSync(tmp);
|
|
50507
|
-
} catch {
|
|
50508
|
-
}
|
|
50509
|
-
throw error51;
|
|
50510
|
-
}
|
|
50511
|
-
console.error(`Wrote ${filePath}`);
|
|
50512
|
-
}
|
|
50513
|
-
|
|
50514
50148
|
// src/package_commands.ts
|
|
50515
|
-
var CONTRACT_FILE = "contract.json";
|
|
50516
|
-
var ADOPT_BINDING_FILE = "adopt_binding.json";
|
|
50517
|
-
var PACKAGE_TEMPLATE_KINDS = /* @__PURE__ */ new Set([
|
|
50518
|
-
"html",
|
|
50519
|
-
"email",
|
|
50520
|
-
"excel",
|
|
50521
|
-
"word",
|
|
50522
|
-
"pdf-form"
|
|
50523
|
-
]);
|
|
50524
50149
|
function packageJsonPath(projectDir) {
|
|
50525
|
-
return
|
|
50150
|
+
return path6.join(projectDir, "package.json");
|
|
50526
50151
|
}
|
|
50527
50152
|
function isPlainObject2(value) {
|
|
50528
50153
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
50529
50154
|
}
|
|
50530
|
-
function
|
|
50155
|
+
function readLocalAppManifest(projectDir) {
|
|
50531
50156
|
const pkgPath2 = packageJsonPath(projectDir);
|
|
50532
|
-
if (!
|
|
50533
|
-
|
|
50534
|
-
|
|
50535
|
-
|
|
50536
|
-
|
|
50537
|
-
const
|
|
50538
|
-
if (
|
|
50539
|
-
|
|
50540
|
-
|
|
50541
|
-
|
|
50542
|
-
throw new Error(
|
|
50543
|
-
`${pkgPath2} has no lotics.package manifest \u2014 not a package project. Use "lotics package new <name>".`
|
|
50544
|
-
);
|
|
50545
|
-
}
|
|
50546
|
-
const dev = {};
|
|
50547
|
-
if (isPlainObject2(pkg2.dev)) {
|
|
50548
|
-
for (const [ws, entry] of Object.entries(pkg2.dev)) {
|
|
50549
|
-
if (isPlainObject2(entry) && typeof entry.app_id === "string" && typeof entry.version === "number") {
|
|
50550
|
-
dev[ws] = { app_id: entry.app_id, version: entry.version };
|
|
50551
|
-
}
|
|
50552
|
-
}
|
|
50553
|
-
}
|
|
50554
|
-
const knowledge = {};
|
|
50555
|
-
if (isPlainObject2(pkg2.knowledge)) {
|
|
50556
|
-
for (const [alias, entry] of Object.entries(pkg2.knowledge)) {
|
|
50557
|
-
if (!isPlainObject2(entry) || typeof entry.name !== "string" || entry.name.length === 0) {
|
|
50558
|
-
throw new Error(
|
|
50559
|
-
`${pkgPath2}: lotics.package.knowledge.${alias} must be an object with a non-empty "name".`
|
|
50560
|
-
);
|
|
50561
|
-
}
|
|
50562
|
-
knowledge[alias] = {
|
|
50563
|
-
name: entry.name,
|
|
50564
|
-
description: typeof entry.description === "string" ? entry.description : null,
|
|
50565
|
-
active_by_default: typeof entry.active_by_default === "boolean" ? entry.active_by_default : true
|
|
50566
|
-
};
|
|
50567
|
-
}
|
|
50568
|
-
}
|
|
50569
|
-
const templates = {};
|
|
50570
|
-
if (isPlainObject2(pkg2.templates)) {
|
|
50571
|
-
for (const [alias, entry] of Object.entries(pkg2.templates)) {
|
|
50572
|
-
if (!isPlainObject2(entry) || typeof entry.name !== "string" || entry.name.length === 0 || typeof entry.type !== "string" || !PACKAGE_TEMPLATE_KINDS.has(entry.type) || typeof entry.file !== "string" || entry.file.length === 0) {
|
|
50573
|
-
throw new Error(
|
|
50574
|
-
`${pkgPath2}: lotics.package.templates.${alias} must be an object with a non-empty "name", a "type" of html|email|excel|word|pdf-form, and a non-empty "file" (a name in templates/).`
|
|
50575
|
-
);
|
|
50157
|
+
if (!fs5.existsSync(pkgPath2)) return null;
|
|
50158
|
+
const parsed = JSON.parse(fs5.readFileSync(pkgPath2, "utf-8"));
|
|
50159
|
+
if (!isPlainObject2(parsed)) return null;
|
|
50160
|
+
const lotics = isPlainObject2(parsed.lotics) ? parsed.lotics : {};
|
|
50161
|
+
const app_id = typeof lotics.app_id === "string" ? lotics.app_id : null;
|
|
50162
|
+
const knowledge = [];
|
|
50163
|
+
if (Array.isArray(lotics.knowledge)) {
|
|
50164
|
+
for (const entry of lotics.knowledge) {
|
|
50165
|
+
if (isPlainObject2(entry) && typeof entry.alias === "string" && typeof entry.doc_id === "string") {
|
|
50166
|
+
knowledge.push({ alias: entry.alias, doc_id: entry.doc_id });
|
|
50576
50167
|
}
|
|
50577
|
-
templates[alias] = {
|
|
50578
|
-
name: entry.name,
|
|
50579
|
-
type: entry.type,
|
|
50580
|
-
file: entry.file
|
|
50581
|
-
};
|
|
50582
50168
|
}
|
|
50583
50169
|
}
|
|
50584
|
-
|
|
50585
|
-
const manifest = {
|
|
50586
|
-
id: typeof pkg2.id === "string" ? pkg2.id : null,
|
|
50587
|
-
name: pkg2.name,
|
|
50588
|
-
description: typeof pkg2.description === "string" ? pkg2.description : null,
|
|
50589
|
-
kind: pkg2.kind === "content" ? "content" : "app",
|
|
50590
|
-
version: typeof pkg2.version === "number" ? pkg2.version : null,
|
|
50591
|
-
knowledge,
|
|
50592
|
-
templates,
|
|
50593
|
-
knowledge_expects,
|
|
50594
|
-
dev
|
|
50595
|
-
};
|
|
50596
|
-
return { pkgJson: parsed, manifest };
|
|
50597
|
-
}
|
|
50598
|
-
function writePackageManifest(projectDir, project) {
|
|
50599
|
-
const next = {
|
|
50600
|
-
...project.pkgJson,
|
|
50601
|
-
lotics: {
|
|
50602
|
-
...isPlainObject2(project.pkgJson.lotics) ? project.pkgJson.lotics : {},
|
|
50603
|
-
package: project.manifest
|
|
50604
|
-
}
|
|
50605
|
-
};
|
|
50606
|
-
writeFileAtomic(
|
|
50607
|
-
packageJsonPath(projectDir),
|
|
50608
|
-
new TextEncoder().encode(JSON.stringify(next, null, 2) + "\n")
|
|
50609
|
-
);
|
|
50610
|
-
}
|
|
50611
|
-
function sanitizePackageJsonForSource(pkgJson) {
|
|
50612
|
-
const lotics = isPlainObject2(pkgJson.lotics) ? pkgJson.lotics : {};
|
|
50613
|
-
const pkg2 = isPlainObject2(lotics.package) ? lotics.package : {};
|
|
50614
|
-
const { dev: _dev, ...pkgWithoutDev } = pkg2;
|
|
50615
|
-
return {
|
|
50616
|
-
...pkgJson,
|
|
50617
|
-
lotics: { ...lotics, package: pkgWithoutDev }
|
|
50618
|
-
};
|
|
50170
|
+
return { app_id, knowledge };
|
|
50619
50171
|
}
|
|
50620
|
-
function
|
|
50621
|
-
|
|
50622
|
-
|
|
50623
|
-
|
|
50624
|
-
|
|
50625
|
-
id: null,
|
|
50626
|
-
name: args.name,
|
|
50627
|
-
description: args.description,
|
|
50628
|
-
// Extraction promotes a bespoke APP to a package — always app kind.
|
|
50629
|
-
kind: "app",
|
|
50630
|
-
version: null,
|
|
50631
|
-
knowledge: {},
|
|
50632
|
-
templates: {},
|
|
50633
|
-
knowledge_expects: [],
|
|
50634
|
-
dev: {}
|
|
50635
|
-
}
|
|
50636
|
-
};
|
|
50637
|
-
}
|
|
50638
|
-
function parseAdoptBindingFile(raw, expectedAppId) {
|
|
50639
|
-
if (!isPlainObject2(raw) || typeof raw.app_id !== "string" || typeof raw.workspace_id !== "string" || !isPlainObject2(raw.binding)) {
|
|
50640
|
-
throw new Error(
|
|
50641
|
-
"Malformed .lotics/adopt_binding.json \u2014 expected { app_id, workspace_id, binding }."
|
|
50642
|
-
);
|
|
50643
|
-
}
|
|
50644
|
-
if (raw.app_id !== expectedAppId) {
|
|
50645
|
-
throw new Error(
|
|
50646
|
-
`.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.`
|
|
50647
|
-
);
|
|
50648
|
-
}
|
|
50649
|
-
const knowledge_binding = {};
|
|
50650
|
-
if (isPlainObject2(raw.knowledge_binding)) {
|
|
50651
|
-
for (const [alias, id] of Object.entries(raw.knowledge_binding)) {
|
|
50652
|
-
if (typeof id === "string") knowledge_binding[alias] = id;
|
|
50172
|
+
function parseRenameFlags(renames) {
|
|
50173
|
+
return renames.map((entry) => {
|
|
50174
|
+
const eq = entry.indexOf("=");
|
|
50175
|
+
if (eq <= 0 || eq === entry.length - 1) {
|
|
50176
|
+
throw new Error(`Invalid --rename "${entry}" \u2014 expected old=new (an alias to rename before v1 freezes it).`);
|
|
50653
50177
|
}
|
|
50654
|
-
|
|
50655
|
-
|
|
50656
|
-
app_id: raw.app_id,
|
|
50657
|
-
workspace_id: raw.workspace_id,
|
|
50658
|
-
binding: raw.binding,
|
|
50659
|
-
knowledge_binding
|
|
50660
|
-
};
|
|
50178
|
+
return { from: entry.slice(0, eq), to: entry.slice(eq + 1) };
|
|
50179
|
+
});
|
|
50661
50180
|
}
|
|
50662
50181
|
function formatExtractReport(report) {
|
|
50663
50182
|
const order = ["error", "warning", "info"];
|
|
@@ -50666,573 +50185,32 @@ function formatExtractReport(report) {
|
|
|
50666
50185
|
);
|
|
50667
50186
|
return { lines, hasError: report.some((f) => f.severity === "error") };
|
|
50668
50187
|
}
|
|
50669
|
-
function
|
|
50670
|
-
const dir = path7.join(projectDir, ".lotics");
|
|
50671
|
-
fs6.mkdirSync(dir, { recursive: true });
|
|
50672
|
-
return dir;
|
|
50673
|
-
}
|
|
50674
|
-
function cmpVersions(a, b) {
|
|
50675
|
-
const pa = a.split(".").map(Number);
|
|
50676
|
-
const pb = b.split(".").map(Number);
|
|
50677
|
-
for (let i2 = 0; i2 < 3; i2++) {
|
|
50678
|
-
const d = (pa[i2] || 0) - (pb[i2] || 0);
|
|
50679
|
-
if (d !== 0) return d;
|
|
50680
|
-
}
|
|
50681
|
-
return 0;
|
|
50682
|
-
}
|
|
50683
|
-
function packageSdkRange(sdkLatest) {
|
|
50684
|
-
const version2 = sdkLatest !== null && cmpVersions(sdkLatest, STARTER_FALLBACK_SDK_VERSION) > 0 ? sdkLatest : STARTER_FALLBACK_SDK_VERSION;
|
|
50685
|
-
return `^${version2}`;
|
|
50686
|
-
}
|
|
50687
|
-
function readContract(projectDir) {
|
|
50688
|
-
const contractPath = path7.join(projectDir, CONTRACT_FILE);
|
|
50689
|
-
if (!fs6.existsSync(contractPath)) {
|
|
50690
|
-
throw new Error(
|
|
50691
|
-
`No ${CONTRACT_FILE} in ${projectDir}. A package project declares its data model there.`
|
|
50692
|
-
);
|
|
50693
|
-
}
|
|
50694
|
-
return JSON.parse(fs6.readFileSync(contractPath, "utf-8"));
|
|
50695
|
-
}
|
|
50696
|
-
function foldKnowledgeIntoContract(projectDir, project, contract) {
|
|
50697
|
-
const aliases = Object.keys(project.manifest.knowledge);
|
|
50698
|
-
const expects = project.manifest.knowledge_expects;
|
|
50699
|
-
if (aliases.length === 0 && expects.length === 0) return contract;
|
|
50700
|
-
const knowledge = {};
|
|
50701
|
-
for (const alias of aliases) {
|
|
50702
|
-
const entry = project.manifest.knowledge[alias];
|
|
50703
|
-
const contentRef = `knowledge/${alias}.md`;
|
|
50704
|
-
const filePath = path7.join(projectDir, "knowledge", `${alias}.md`);
|
|
50705
|
-
if (!fs6.existsSync(filePath)) {
|
|
50706
|
-
throw new Error(
|
|
50707
|
-
`Declared knowledge doc "${alias}" (lotics.package.knowledge) has no ${contentRef} in the project. Create the file, or remove the manifest entry.`
|
|
50708
|
-
);
|
|
50709
|
-
}
|
|
50710
|
-
const bytes = fs6.readFileSync(filePath);
|
|
50711
|
-
knowledge[alias] = {
|
|
50712
|
-
name: entry.name,
|
|
50713
|
-
description: entry.description ?? "",
|
|
50714
|
-
content_ref: contentRef,
|
|
50715
|
-
content_sha256: createHash("sha256").update(bytes).digest("hex"),
|
|
50716
|
-
active_by_default: entry.active_by_default
|
|
50717
|
-
};
|
|
50718
|
-
}
|
|
50719
|
-
return { ...contract, knowledge, knowledge_expects: expects };
|
|
50720
|
-
}
|
|
50721
|
-
function foldTemplatesIntoContract(projectDir, project, contract) {
|
|
50722
|
-
if (!isPlainObject2(contract)) {
|
|
50723
|
-
throw new Error(`${CONTRACT_FILE} in ${projectDir} must be a JSON object.`);
|
|
50724
|
-
}
|
|
50725
|
-
const aliases = Object.keys(project.manifest.templates);
|
|
50726
|
-
if (aliases.length === 0) return contract;
|
|
50727
|
-
const existing = Array.isArray(contract.templates) ? contract.templates : [];
|
|
50728
|
-
const existingAliases = new Set(
|
|
50729
|
-
existing.filter((t) => isPlainObject2(t)).map((t) => t.alias).filter((a) => typeof a === "string")
|
|
50730
|
-
);
|
|
50731
|
-
const folded = aliases.map((alias) => {
|
|
50732
|
-
if (existingAliases.has(alias)) {
|
|
50733
|
-
throw new Error(
|
|
50734
|
-
`Template alias "${alias}" is declared in both lotics.package.templates and ${CONTRACT_FILE} \u2014 declare each template once.`
|
|
50735
|
-
);
|
|
50736
|
-
}
|
|
50737
|
-
const entry = project.manifest.templates[alias];
|
|
50738
|
-
const filePath = path7.join(projectDir, "templates", entry.file);
|
|
50739
|
-
if (!fs6.existsSync(filePath)) {
|
|
50740
|
-
throw new Error(
|
|
50741
|
-
`Declared template "${alias}" (lotics.package.templates) has no templates/${entry.file} in the project. Stage the file, then rebuild.`
|
|
50742
|
-
);
|
|
50743
|
-
}
|
|
50744
|
-
if (entry.type === "html" || entry.type === "email") {
|
|
50745
|
-
return { alias, label: entry.name, type: entry.type, content: fs6.readFileSync(filePath, "utf-8") };
|
|
50746
|
-
}
|
|
50747
|
-
return { alias, label: entry.name, type: entry.type, bytes_ref: `templates/${entry.file}` };
|
|
50748
|
-
});
|
|
50749
|
-
return { ...contract, templates: [...existing, ...folded] };
|
|
50750
|
-
}
|
|
50751
|
-
function foldTemplateShasIntoContract(projectDir, contract) {
|
|
50752
|
-
const templates = contract.templates;
|
|
50753
|
-
if (!Array.isArray(templates) || templates.length === 0) return contract;
|
|
50754
|
-
const withShas = templates.map((template) => {
|
|
50755
|
-
if (!isPlainObject2(template) || typeof template.type !== "string" || typeof template.alias !== "string") {
|
|
50756
|
-
return template;
|
|
50757
|
-
}
|
|
50758
|
-
if (template.type === "html" || template.type === "email") {
|
|
50759
|
-
if (typeof template.content !== "string") {
|
|
50760
|
-
throw new Error(`Inline template "${template.alias}" must carry a string "content".`);
|
|
50761
|
-
}
|
|
50762
|
-
return {
|
|
50763
|
-
...template,
|
|
50764
|
-
content_sha256: createHash("sha256").update(Buffer.from(template.content, "utf-8")).digest("hex")
|
|
50765
|
-
};
|
|
50766
|
-
}
|
|
50767
|
-
if (typeof template.bytes_ref !== "string") {
|
|
50768
|
-
throw new Error(`File-backed template "${template.alias}" must carry a string "bytes_ref".`);
|
|
50769
|
-
}
|
|
50770
|
-
const filePath = path7.join(projectDir, template.bytes_ref);
|
|
50771
|
-
if (!fs6.existsSync(filePath)) {
|
|
50772
|
-
throw new Error(
|
|
50773
|
-
`Template "${template.alias}" references "${template.bytes_ref}", which is not in the project. Stage the file, then rebuild.`
|
|
50774
|
-
);
|
|
50775
|
-
}
|
|
50776
|
-
const bytes = fs6.readFileSync(filePath);
|
|
50777
|
-
return { ...template, content_sha256: createHash("sha256").update(bytes).digest("hex") };
|
|
50778
|
-
});
|
|
50779
|
-
return { ...contract, templates: withShas };
|
|
50780
|
-
}
|
|
50781
|
-
function writePackageAppFields(projectDir) {
|
|
50782
|
-
const contract = readContract(projectDir);
|
|
50783
|
-
const dotLotics = path7.join(projectDir, ".lotics");
|
|
50784
|
-
fs6.mkdirSync(dotLotics, { recursive: true });
|
|
50785
|
-
fs6.writeFileSync(path7.join(dotLotics, "app_fields.ts"), generatePackageAppFields(contract));
|
|
50786
|
-
console.error("Wrote .lotics/app_fields.ts (contract-derived, runtime-resolved)");
|
|
50787
|
-
}
|
|
50788
|
-
var SOURCE_STAGE_EXCLUDES = /* @__PURE__ */ new Set([
|
|
50789
|
-
"node_modules",
|
|
50790
|
-
"dist",
|
|
50791
|
-
".lotics",
|
|
50792
|
-
".git",
|
|
50793
|
-
"bundle.tar.gz",
|
|
50794
|
-
"package.json"
|
|
50795
|
-
]);
|
|
50796
|
-
function stagePackageSource(projectDir, sourceStage) {
|
|
50797
|
-
const project = readPackageProject(projectDir);
|
|
50798
|
-
fs6.mkdirSync(sourceStage, { recursive: true });
|
|
50799
|
-
for (const entry of fs6.readdirSync(projectDir)) {
|
|
50800
|
-
if (SOURCE_STAGE_EXCLUDES.has(entry) || entry.endsWith(".tsbuildinfo")) continue;
|
|
50801
|
-
fs6.cpSync(path7.join(projectDir, entry), path7.join(sourceStage, entry), { recursive: true });
|
|
50802
|
-
}
|
|
50803
|
-
fs6.writeFileSync(
|
|
50804
|
-
path7.join(sourceStage, "package.json"),
|
|
50805
|
-
JSON.stringify(sanitizePackageJsonForSource(project.pkgJson), null, 2) + "\n"
|
|
50806
|
-
);
|
|
50807
|
-
}
|
|
50808
|
-
async function buildPackageBundle(projectDir) {
|
|
50809
|
-
const { manifest } = readPackageProject(projectDir);
|
|
50810
|
-
const stage = fs6.mkdtempSync(path7.join(tmpdir2(), "lotics-pkg-"));
|
|
50811
|
-
const bundlePath = path7.join(tmpdir2(), `lotics-bundle-${Date.now()}.tar.gz`);
|
|
50812
|
-
try {
|
|
50813
|
-
let distDir;
|
|
50814
|
-
if (manifest.kind === "content") {
|
|
50815
|
-
console.error("Content package \u2014 skipping app build.");
|
|
50816
|
-
distDir = path7.join(stage, "empty-dist");
|
|
50817
|
-
fs6.mkdirSync(distDir, { recursive: true });
|
|
50818
|
-
fs6.writeFileSync(
|
|
50819
|
-
path7.join(distDir, ".content-package"),
|
|
50820
|
-
"Content package \u2014 no frontend bundle. Content ships in source.tar.gz under knowledge/.\n"
|
|
50821
|
-
);
|
|
50822
|
-
} else {
|
|
50823
|
-
console.error("Building...");
|
|
50824
|
-
await runNpm(["run", "build"], projectDir);
|
|
50825
|
-
distDir = path7.join(projectDir, "dist");
|
|
50826
|
-
if (!fs6.existsSync(distDir)) {
|
|
50827
|
-
throw new Error(
|
|
50828
|
-
`Build did not produce a dist/ directory in ${projectDir}. Check that 'npm run build' is configured correctly.`
|
|
50829
|
-
);
|
|
50830
|
-
}
|
|
50831
|
-
}
|
|
50832
|
-
console.error("Packaging source...");
|
|
50833
|
-
const sourceStage = path7.join(stage, "source");
|
|
50834
|
-
stagePackageSource(projectDir, sourceStage);
|
|
50835
|
-
await runTar(["-czf", path7.join(stage, "source.tar.gz"), "-C", sourceStage, "."], projectDir);
|
|
50836
|
-
console.error("Packaging dist...");
|
|
50837
|
-
await runTar(["-czf", path7.join(stage, "dist.tar.gz"), "-C", distDir, "."], projectDir);
|
|
50838
|
-
console.error("Bundling...");
|
|
50839
|
-
await runTar(["-czf", bundlePath, "source.tar.gz", "dist.tar.gz"], stage);
|
|
50840
|
-
return fs6.readFileSync(bundlePath);
|
|
50841
|
-
} finally {
|
|
50842
|
-
fs6.rmSync(stage, { recursive: true, force: true });
|
|
50843
|
-
if (fs6.existsSync(bundlePath)) fs6.unlinkSync(bundlePath);
|
|
50844
|
-
}
|
|
50845
|
-
}
|
|
50846
|
-
function starterContract(name) {
|
|
50847
|
-
return {
|
|
50848
|
-
entities: [
|
|
50849
|
-
{
|
|
50850
|
-
alias: "item",
|
|
50851
|
-
label: name,
|
|
50852
|
-
description: `Records managed by the ${name} package.`,
|
|
50853
|
-
fields: [
|
|
50854
|
-
{ alias: "name", label: "Name", type: "text", required: true },
|
|
50855
|
-
{ alias: "notes", label: "Notes", type: "text" },
|
|
50856
|
-
{
|
|
50857
|
-
alias: "status",
|
|
50858
|
-
label: "Status",
|
|
50859
|
-
type: "select",
|
|
50860
|
-
options: [
|
|
50861
|
-
{ alias: "open", label: "Open", color: "blue" },
|
|
50862
|
-
{ alias: "done", label: "Done", color: "green" }
|
|
50863
|
-
]
|
|
50864
|
-
}
|
|
50865
|
-
]
|
|
50866
|
-
}
|
|
50867
|
-
],
|
|
50868
|
-
roles: [],
|
|
50869
|
-
templates: [],
|
|
50870
|
-
queries: [
|
|
50871
|
-
{
|
|
50872
|
-
alias: "items",
|
|
50873
|
-
ast: {
|
|
50874
|
-
kind: "from_table",
|
|
50875
|
-
from_entity: "item",
|
|
50876
|
-
sort: [{ field_key: "name", order: "asc" }]
|
|
50877
|
-
}
|
|
50878
|
-
}
|
|
50879
|
-
],
|
|
50880
|
-
workflows: [],
|
|
50881
|
-
agents: [],
|
|
50882
|
-
config: [
|
|
50883
|
-
{ alias: "heading", label: "List heading", type: "text", default: name }
|
|
50884
|
-
]
|
|
50885
|
-
};
|
|
50886
|
-
}
|
|
50887
|
-
function emptyContract() {
|
|
50888
|
-
return {
|
|
50889
|
-
entities: [],
|
|
50890
|
-
roles: [],
|
|
50891
|
-
templates: [],
|
|
50892
|
-
queries: [],
|
|
50893
|
-
workflows: [],
|
|
50894
|
-
agents: [],
|
|
50895
|
-
config: []
|
|
50896
|
-
};
|
|
50897
|
-
}
|
|
50898
|
-
function scaffoldContentPackage(name, targetPath) {
|
|
50899
|
-
const exampleAlias = "overview";
|
|
50900
|
-
const manifest = {
|
|
50901
|
-
id: null,
|
|
50902
|
-
name,
|
|
50903
|
-
description: null,
|
|
50904
|
-
kind: "content",
|
|
50905
|
-
version: null,
|
|
50906
|
-
knowledge: {
|
|
50907
|
-
[exampleAlias]: {
|
|
50908
|
-
name: `${name} Overview`,
|
|
50909
|
-
description: "What this corpus covers \u2014 replace with your doc's summary.",
|
|
50910
|
-
active_by_default: true
|
|
50911
|
-
}
|
|
50912
|
-
},
|
|
50913
|
-
// A content package can also ship standalone document templates — declare them
|
|
50914
|
-
// under `lotics.package.templates` with the files in a `templates/` dir. See
|
|
50915
|
-
// the commented example printed by the scaffold + docs/packages.md § Content.
|
|
50916
|
-
templates: {},
|
|
50917
|
-
knowledge_expects: [],
|
|
50918
|
-
dev: {}
|
|
50919
|
-
};
|
|
50920
|
-
const pkgJson = {
|
|
50921
|
-
name: appDirName(name),
|
|
50922
|
-
private: true,
|
|
50923
|
-
version: "0.0.0",
|
|
50924
|
-
lotics: { package: manifest }
|
|
50925
|
-
};
|
|
50926
|
-
fs6.writeFileSync(
|
|
50927
|
-
packageJsonPath(targetPath),
|
|
50928
|
-
JSON.stringify(pkgJson, null, 2) + "\n"
|
|
50929
|
-
);
|
|
50930
|
-
fs6.writeFileSync(
|
|
50931
|
-
path7.join(targetPath, CONTRACT_FILE),
|
|
50932
|
-
JSON.stringify(emptyContract(), null, 2) + "\n"
|
|
50933
|
-
);
|
|
50934
|
-
const knowledgeDir = path7.join(targetPath, "knowledge");
|
|
50935
|
-
fs6.mkdirSync(knowledgeDir, { recursive: true });
|
|
50936
|
-
fs6.writeFileSync(
|
|
50937
|
-
path7.join(knowledgeDir, `${exampleAlias}.md`),
|
|
50938
|
-
`# ${name}
|
|
50939
|
-
|
|
50940
|
-
Replace this file with the corpus content. Structure it under clear
|
|
50941
|
-
Markdown headers so an agent can outline and read it by section.
|
|
50942
|
-
`
|
|
50943
|
-
);
|
|
50944
|
-
console.error(`Scaffolded a content package into ${targetPath}`);
|
|
50945
|
-
console.error(`
|
|
50946
|
-
Ready. Next steps:`);
|
|
50947
|
-
console.error(` cd ${path7.relative(process.cwd(), targetPath) || "."}`);
|
|
50948
|
-
console.error(` # add docs: create knowledge/<alias>.md + a lotics.package.knowledge.<alias> entry`);
|
|
50949
|
-
console.error(` # (name/description/active_by_default), then publish:`);
|
|
50950
|
-
console.error(` # add templates: create templates/<file> + a lotics.package.templates.<alias> entry:`);
|
|
50951
|
-
console.error(` # "templates": { "quote": { "name": "Quote", "type": "html", "file": "quote.html" } }`);
|
|
50952
|
-
console.error(` # inline kinds html|email read templates/<file> as content; file-backed`);
|
|
50953
|
-
console.error(` # excel|word|pdf-form pack the bytes into the bundle (bytes_ref = templates/<file>).`);
|
|
50954
|
-
console.error(` lotics package publish -m "v1"`);
|
|
50955
|
-
console.error(` lotics package install <package_id>`);
|
|
50956
|
-
}
|
|
50957
|
-
async function packageNew(args) {
|
|
50958
|
-
const targetPath = path7.resolve(args.targetPath ?? appDirName(args.name));
|
|
50959
|
-
if (fs6.existsSync(targetPath) && fs6.readdirSync(targetPath).length > 0) {
|
|
50960
|
-
throw new Error(`Target directory ${targetPath} is not empty.`);
|
|
50961
|
-
}
|
|
50962
|
-
fs6.mkdirSync(targetPath, { recursive: true });
|
|
50963
|
-
if ((args.kind ?? "app") === "content") {
|
|
50964
|
-
scaffoldContentPackage(args.name, targetPath);
|
|
50965
|
-
return;
|
|
50966
|
-
}
|
|
50967
|
-
const [uiLatest, sdkLatest] = await Promise.all([
|
|
50968
|
-
fetchLatestNpmVersion("@lotics/ui"),
|
|
50969
|
-
fetchLatestNpmVersion("@lotics/app-sdk")
|
|
50970
|
-
]);
|
|
50971
|
-
const files = buildStarterTemplate({
|
|
50972
|
-
app_name: args.name,
|
|
50973
|
-
app_id: "",
|
|
50974
|
-
workspace_id: "",
|
|
50975
|
-
ui_version: uiLatest ? `^${uiLatest}` : void 0,
|
|
50976
|
-
sdk_version: packageSdkRange(sdkLatest)
|
|
50977
|
-
});
|
|
50978
|
-
const overrides = new Map(
|
|
50979
|
-
buildPackageStarterOverrides({ app_name: args.name }).map((f) => [f.path, f.content])
|
|
50980
|
-
);
|
|
50981
|
-
for (const file2 of files) {
|
|
50982
|
-
const fullPath = path7.join(targetPath, file2.path);
|
|
50983
|
-
if (file2.path === "package.json") {
|
|
50984
|
-
const pkg2 = JSON.parse(file2.content);
|
|
50985
|
-
const manifest = {
|
|
50986
|
-
id: null,
|
|
50987
|
-
name: args.name,
|
|
50988
|
-
description: null,
|
|
50989
|
-
kind: "app",
|
|
50990
|
-
version: null,
|
|
50991
|
-
knowledge: {},
|
|
50992
|
-
templates: {},
|
|
50993
|
-
knowledge_expects: [],
|
|
50994
|
-
dev: {}
|
|
50995
|
-
};
|
|
50996
|
-
pkg2.lotics = { package: manifest };
|
|
50997
|
-
fs6.mkdirSync(path7.dirname(fullPath), { recursive: true });
|
|
50998
|
-
fs6.writeFileSync(fullPath, JSON.stringify(pkg2, null, 2) + "\n");
|
|
50999
|
-
continue;
|
|
51000
|
-
}
|
|
51001
|
-
fs6.mkdirSync(path7.dirname(fullPath), { recursive: true });
|
|
51002
|
-
fs6.writeFileSync(fullPath, overrides.get(file2.path) ?? file2.content);
|
|
51003
|
-
}
|
|
51004
|
-
fs6.writeFileSync(
|
|
51005
|
-
path7.join(targetPath, CONTRACT_FILE),
|
|
51006
|
-
JSON.stringify(starterContract(args.name), null, 2) + "\n"
|
|
51007
|
-
);
|
|
51008
|
-
writePackageAppFields(targetPath);
|
|
51009
|
-
console.error(`Scaffolded ${files.length + 1} files into ${targetPath}`);
|
|
51010
|
-
console.error("Installing npm dependencies...");
|
|
51011
|
-
await runNpm(["install"], targetPath);
|
|
51012
|
-
console.error(`
|
|
51013
|
-
Ready. Next steps:`);
|
|
51014
|
-
console.error(` cd ${path7.relative(process.cwd(), targetPath) || "."}`);
|
|
51015
|
-
console.error(` # edit contract.json + src/App.tsx, then run it against a dev workspace:`);
|
|
51016
|
-
console.error(` lotics workspace create "<name> dev" --dev`);
|
|
51017
|
-
console.error(` lotics package dev --workspace <dev_ws>`);
|
|
51018
|
-
}
|
|
51019
|
-
async function packageBuild(args) {
|
|
51020
|
-
const projectDir = path7.resolve(args.projectDir ?? process.cwd());
|
|
51021
|
-
readPackageProject(projectDir);
|
|
51022
|
-
const bundle = await buildPackageBundle(projectDir);
|
|
51023
|
-
const out = path7.join(projectDir, "bundle.tar.gz");
|
|
51024
|
-
fs6.writeFileSync(out, bundle);
|
|
51025
|
-
console.error(`Built ${out} (${(bundle.byteLength / 1024).toFixed(1)} KB)`);
|
|
51026
|
-
}
|
|
51027
|
-
async function publishVersion(client, projectDir, opts) {
|
|
51028
|
-
const project = readPackageProject(projectDir);
|
|
51029
|
-
const rawContract = readContract(projectDir);
|
|
51030
|
-
if (!isPlainObject2(rawContract)) {
|
|
51031
|
-
throw new Error(`${CONTRACT_FILE} in ${projectDir} must be a JSON object.`);
|
|
51032
|
-
}
|
|
51033
|
-
const contract = foldTemplateShasIntoContract(
|
|
51034
|
-
projectDir,
|
|
51035
|
-
foldKnowledgeIntoContract(
|
|
51036
|
-
projectDir,
|
|
51037
|
-
project,
|
|
51038
|
-
foldTemplatesIntoContract(projectDir, project, rawContract)
|
|
51039
|
-
)
|
|
51040
|
-
);
|
|
51041
|
-
let packageId = project.manifest.id;
|
|
51042
|
-
if (packageId === null) {
|
|
51043
|
-
const pkg2 = await client.createPackage({
|
|
51044
|
-
name: project.manifest.name,
|
|
51045
|
-
description: project.manifest.description,
|
|
51046
|
-
// Kind is fixed at package creation and immutable after — a knowledge
|
|
51047
|
-
// package must be created as such so publish enforces its constraint.
|
|
51048
|
-
kind: project.manifest.kind
|
|
51049
|
-
});
|
|
51050
|
-
packageId = pkg2.id;
|
|
51051
|
-
project.manifest.id = packageId;
|
|
51052
|
-
writePackageManifest(projectDir, project);
|
|
51053
|
-
console.error(`Created package ${pkg2.name} (${pkg2.id}).`);
|
|
51054
|
-
}
|
|
51055
|
-
const bundle = await buildPackageBundle(projectDir);
|
|
51056
|
-
console.error("Publishing version...");
|
|
51057
|
-
const version2 = await client.publishPackageVersion(packageId, {
|
|
51058
|
-
contract,
|
|
51059
|
-
bundle,
|
|
51060
|
-
changelog: opts.changelog ?? null,
|
|
51061
|
-
channel: opts.channel ?? "release"
|
|
51062
|
-
});
|
|
51063
|
-
project.manifest.version = version2.version;
|
|
51064
|
-
writePackageManifest(projectDir, project);
|
|
51065
|
-
return { package_id: packageId, version: version2.version };
|
|
51066
|
-
}
|
|
51067
|
-
async function packagePublish(client, args) {
|
|
51068
|
-
const projectDir = path7.resolve(args.projectDir ?? process.cwd());
|
|
51069
|
-
const { package_id, version: version2 } = await publishVersion(client, projectDir, {
|
|
51070
|
-
changelog: args.changelog
|
|
51071
|
-
});
|
|
51072
|
-
console.error(`Published ${package_id} v${version2}.`);
|
|
51073
|
-
console.error(` Install it: lotics package install ${package_id} --version ${version2}`);
|
|
51074
|
-
}
|
|
51075
|
-
function assertDevWorkspace(workspace) {
|
|
51076
|
-
if (workspace.is_dev === true) return;
|
|
51077
|
-
throw new Error(
|
|
51078
|
-
`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>.`
|
|
51079
|
-
);
|
|
51080
|
-
}
|
|
51081
|
-
async function syncToDevWorkspace(client, projectDir) {
|
|
51082
|
-
const devWorkspaceId = client.getWorkspaceId();
|
|
51083
|
-
if (!devWorkspaceId) {
|
|
51084
|
-
throw new Error(
|
|
51085
|
-
"No dev workspace selected. Pass --workspace <dev_ws> (a workspace created with --dev)."
|
|
51086
|
-
);
|
|
51087
|
-
}
|
|
51088
|
-
const workspace = await client.getWorkspaceInfo(devWorkspaceId);
|
|
51089
|
-
if (!workspace) {
|
|
51090
|
-
throw new Error(
|
|
51091
|
-
`Workspace ${devWorkspaceId} is not accessible with these credentials. Pass --workspace <dev_ws> (a workspace created with --dev).`
|
|
51092
|
-
);
|
|
51093
|
-
}
|
|
51094
|
-
assertDevWorkspace(workspace);
|
|
51095
|
-
writePackageAppFields(projectDir);
|
|
51096
|
-
const { package_id, version: version2 } = await publishVersion(client, projectDir, {
|
|
51097
|
-
changelog: "dev sync",
|
|
51098
|
-
channel: "dev"
|
|
51099
|
-
});
|
|
51100
|
-
const project = readPackageProject(projectDir);
|
|
51101
|
-
const existing = project.manifest.dev[devWorkspaceId];
|
|
51102
|
-
let appId;
|
|
51103
|
-
if (existing) {
|
|
51104
|
-
console.error(`Upgrading dev installation ${existing.app_id} \u2192 v${version2}...`);
|
|
51105
|
-
const app = await client.upgradePackage(existing.app_id, { version: version2 });
|
|
51106
|
-
appId = app.id;
|
|
51107
|
-
} else {
|
|
51108
|
-
console.error(`Installing ${package_id} v${version2} into dev workspace ${devWorkspaceId}...`);
|
|
51109
|
-
const result = await client.installPackage(package_id, { version: version2 });
|
|
51110
|
-
if (result.kind !== "app") {
|
|
51111
|
-
throw new Error(
|
|
51112
|
-
`Package ${package_id} is a content package \u2014 "lotics package dev/sync" run an app installation. Install a content package with "lotics package install ${package_id}".`
|
|
51113
|
-
);
|
|
51114
|
-
}
|
|
51115
|
-
appId = result.app.id;
|
|
51116
|
-
}
|
|
51117
|
-
project.manifest.dev[devWorkspaceId] = { app_id: appId, version: version2 };
|
|
51118
|
-
writePackageManifest(projectDir, project);
|
|
51119
|
-
const installed = await client.getApp(appId);
|
|
51120
|
-
writeAppDts(projectDir, {
|
|
51121
|
-
workflows: installed.workflows ?? void 0,
|
|
51122
|
-
queries: installed.queries ?? void 0,
|
|
51123
|
-
agents: installed.agents ?? void 0
|
|
51124
|
-
});
|
|
51125
|
-
return { app_id: appId, app_name: installed.name, workspace_id: devWorkspaceId, version: version2 };
|
|
51126
|
-
}
|
|
51127
|
-
async function packageSync(client, args) {
|
|
51128
|
-
const projectDir = path7.resolve(args.projectDir ?? process.cwd());
|
|
51129
|
-
const result = await syncToDevWorkspace(client, projectDir);
|
|
51130
|
-
console.error(
|
|
51131
|
-
`Synced ${result.app_name} v${result.version} \u2192 ${result.app_id} (workspace ${result.workspace_id}).`
|
|
51132
|
-
);
|
|
51133
|
-
}
|
|
51134
|
-
async function packageDev(client, args) {
|
|
51135
|
-
const projectDir = path7.resolve(args.projectDir ?? process.cwd());
|
|
51136
|
-
const synced = await syncToDevWorkspace(client, projectDir);
|
|
51137
|
-
const handle = await startDevServer({
|
|
51138
|
-
projectDir,
|
|
51139
|
-
app_id: synced.app_id,
|
|
51140
|
-
app_name: synced.app_name,
|
|
51141
|
-
workspace_id: synced.workspace_id,
|
|
51142
|
-
api_url: client.baseUrl,
|
|
51143
|
-
port: args.port,
|
|
51144
|
-
vitePort: args.vitePort,
|
|
51145
|
-
client
|
|
51146
|
-
});
|
|
51147
|
-
await handle.ready;
|
|
51148
|
-
const url2 = `http://localhost:${handle.port}`;
|
|
51149
|
-
console.error(`
|
|
51150
|
-
lotics package dev`);
|
|
51151
|
-
console.error(` package: ${synced.app_name} (dev installation ${synced.app_id} v${synced.version})`);
|
|
51152
|
-
console.error(` workspace: ${synced.workspace_id} (dev)`);
|
|
51153
|
-
if (client.viewAsMemberId) {
|
|
51154
|
-
console.error(` view as: ${client.viewAsMemberId}`);
|
|
51155
|
-
}
|
|
51156
|
-
console.error(` vite: http://localhost:${handle.vitePort}/`);
|
|
51157
|
-
console.error(` open: ${url2}`);
|
|
51158
|
-
console.error(` rpc: ${client.baseUrl} (via Bearer API key)
|
|
51159
|
-
`);
|
|
51160
|
-
console.error(` Edit contract.json then re-run "lotics package dev" / "lotics package sync" to migrate.`);
|
|
51161
|
-
console.error(` Ctrl-C to stop.
|
|
51162
|
-
`);
|
|
51163
|
-
openBrowser(url2);
|
|
51164
|
-
await new Promise((resolve2) => {
|
|
51165
|
-
const onSig = () => {
|
|
51166
|
-
process.off("SIGINT", onSig);
|
|
51167
|
-
process.off("SIGTERM", onSig);
|
|
51168
|
-
resolve2();
|
|
51169
|
-
};
|
|
51170
|
-
process.on("SIGINT", onSig);
|
|
51171
|
-
process.on("SIGTERM", onSig);
|
|
51172
|
-
});
|
|
51173
|
-
console.error("\nStopping\u2026");
|
|
51174
|
-
await handle.stop();
|
|
51175
|
-
}
|
|
51176
|
-
async function packageReset(client, args) {
|
|
51177
|
-
const projectDir = path7.resolve(args.projectDir ?? process.cwd());
|
|
51178
|
-
const devWorkspaceId = client.getWorkspaceId();
|
|
51179
|
-
if (!devWorkspaceId) {
|
|
51180
|
-
throw new Error(
|
|
51181
|
-
"No dev workspace selected. Pass --workspace <dev_ws> (a workspace created with --dev)."
|
|
51182
|
-
);
|
|
51183
|
-
}
|
|
51184
|
-
const { manifest } = readPackageProject(projectDir);
|
|
51185
|
-
const pin = manifest.dev[devWorkspaceId];
|
|
51186
|
-
if (!pin) {
|
|
51187
|
-
throw new Error(
|
|
51188
|
-
`No dev installation recorded for workspace ${devWorkspaceId}. Run "lotics package dev --workspace ${devWorkspaceId}" first.`
|
|
51189
|
-
);
|
|
51190
|
-
}
|
|
51191
|
-
console.error(`Resetting dev installation ${pin.app_id} in workspace ${devWorkspaceId}...`);
|
|
51192
|
-
const app = await client.resetPackage(pin.app_id);
|
|
51193
|
-
console.error(`Reset ${app.name} \u2192 ${app.id}. The scaffolded tables were dropped and re-created clean.`);
|
|
51194
|
-
}
|
|
51195
|
-
function resolveInstallationAppId(client, explicit) {
|
|
50188
|
+
function resolveInstallationAppId(explicit) {
|
|
51196
50189
|
if (explicit) return explicit;
|
|
51197
|
-
|
|
51198
|
-
if (!workspaceId) {
|
|
51199
|
-
throw new Error("Pass an app id (lotics package doctor <app_id>) or select a workspace.");
|
|
51200
|
-
}
|
|
51201
|
-
let manifest;
|
|
51202
|
-
try {
|
|
51203
|
-
({ manifest } = readPackageProject(path7.resolve(process.cwd())));
|
|
51204
|
-
} catch {
|
|
51205
|
-
throw new Error(
|
|
51206
|
-
"No app id given and the current directory is not a package project. Pass one explicitly: lotics package doctor <app_id>."
|
|
51207
|
-
);
|
|
51208
|
-
}
|
|
51209
|
-
const pin = manifest.dev[workspaceId];
|
|
51210
|
-
if (!pin) {
|
|
51211
|
-
throw new Error(
|
|
51212
|
-
`No app id given and no dev installation recorded for workspace ${workspaceId}. Pass one explicitly: lotics package doctor <app_id>.`
|
|
51213
|
-
);
|
|
51214
|
-
}
|
|
51215
|
-
return pin.app_id;
|
|
50190
|
+
throw new Error("Pass an app id \u2014 e.g. lotics package doctor <app_id>.");
|
|
51216
50191
|
}
|
|
50192
|
+
var RESOLVE_VERBS = /* @__PURE__ */ new Set(["recreate", "revert", "keep", "apply", "archive", "unbind"]);
|
|
51217
50193
|
function parseResolveFlags(resolve2) {
|
|
51218
50194
|
const resolutions = {};
|
|
51219
50195
|
for (const entry of resolve2) {
|
|
51220
50196
|
const eq = entry.indexOf("=");
|
|
51221
50197
|
if (eq <= 0 || eq === entry.length - 1) {
|
|
51222
50198
|
throw new Error(
|
|
51223
|
-
`Invalid --resolve "${entry}" \u2014 expected <key
|
|
50199
|
+
`Invalid --resolve "${entry}" \u2014 expected <key>=<verb> (recreate|revert|keep|apply|archive|unbind) or <key>=<existing_id>.`
|
|
51224
50200
|
);
|
|
51225
50201
|
}
|
|
51226
50202
|
const key = entry.slice(0, eq);
|
|
51227
50203
|
const value = entry.slice(eq + 1);
|
|
51228
|
-
resolutions[key] = value
|
|
50204
|
+
resolutions[key] = RESOLVE_VERBS.has(value) ? value : { bind_to: value };
|
|
51229
50205
|
}
|
|
51230
50206
|
return resolutions;
|
|
51231
50207
|
}
|
|
51232
50208
|
async function packageDoctor(client, args) {
|
|
51233
|
-
const app_id = resolveInstallationAppId(
|
|
50209
|
+
const app_id = resolveInstallationAppId(args.app_id);
|
|
51234
50210
|
const health = await client.getPackageHealth(app_id);
|
|
51235
|
-
console.error(
|
|
50211
|
+
console.error(
|
|
50212
|
+
`${health.package_name} \u2014 installation ${health.app_id}` + (health.is_origin ? " (origin \u2014 this app IS installation #1, the release working copy)" : "")
|
|
50213
|
+
);
|
|
51236
50214
|
console.error(
|
|
51237
50215
|
` Installed: v${health.installed_version} Latest: v${health.latest_version}` + (health.update_available ? " \u2192 update available" : "")
|
|
51238
50216
|
);
|
|
@@ -51245,12 +50223,23 @@ async function packageDoctor(client, args) {
|
|
|
51245
50223
|
}
|
|
51246
50224
|
console.error(
|
|
51247
50225
|
` Resolve while upgrading:
|
|
51248
|
-
lotics
|
|
50226
|
+
lotics upgrade ${app_id} --resolve <namespace.alias>=recreate (or =<existing_id> to re-point)`
|
|
51249
50227
|
);
|
|
51250
50228
|
process.exitCode = 1;
|
|
51251
50229
|
}
|
|
51252
50230
|
if (health.modified.length === 0) {
|
|
51253
|
-
console.error(
|
|
50231
|
+
console.error(
|
|
50232
|
+
health.is_origin ? ` Package artifacts: no changes since the last release.
|
|
50233
|
+
(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."
|
|
50234
|
+
);
|
|
50235
|
+
} else if (health.is_origin) {
|
|
50236
|
+
console.error(
|
|
50237
|
+
` Changed since v${health.installed_version} (${health.modified.length}) \u2014 a release will publish these:`
|
|
50238
|
+
);
|
|
50239
|
+
for (const m of health.modified) {
|
|
50240
|
+
console.error(` - ${m.kind}.${m.alias}`);
|
|
50241
|
+
}
|
|
50242
|
+
console.error(` Cut the next version: lotics app release ${app_id} -m "<what changed>"`);
|
|
51254
50243
|
} else {
|
|
51255
50244
|
console.error(` Locally modified package artifacts (${health.modified.length}):`);
|
|
51256
50245
|
for (const m of health.modified) {
|
|
@@ -51258,7 +50247,7 @@ async function packageDoctor(client, args) {
|
|
|
51258
50247
|
}
|
|
51259
50248
|
console.error(
|
|
51260
50249
|
` Consent while upgrading:
|
|
51261
|
-
lotics
|
|
50250
|
+
lotics upgrade ${app_id} --resolve <kind.alias>=revert (or =keep to retain the edit)`
|
|
51262
50251
|
);
|
|
51263
50252
|
process.exitCode = 1;
|
|
51264
50253
|
}
|
|
@@ -51269,9 +50258,18 @@ async function packageDoctor(client, args) {
|
|
|
51269
50258
|
}
|
|
51270
50259
|
}
|
|
51271
50260
|
if (health.knowledge_modified.length > 0) {
|
|
51272
|
-
|
|
51273
|
-
|
|
51274
|
-
|
|
50261
|
+
if (health.is_origin) {
|
|
50262
|
+
console.error(
|
|
50263
|
+
` Knowledge changed since v${health.installed_version} (${health.knowledge_modified.length}) \u2014 a release will re-snapshot these:`
|
|
50264
|
+
);
|
|
50265
|
+
for (const m of health.knowledge_modified) {
|
|
50266
|
+
console.error(` - ${m.alias} "${m.name}"`);
|
|
50267
|
+
}
|
|
50268
|
+
} else {
|
|
50269
|
+
console.error(` Locally edited package knowledge (${health.knowledge_modified.length}):`);
|
|
50270
|
+
for (const m of health.knowledge_modified) {
|
|
50271
|
+
console.error(` - ${m.alias} "${m.name}" (an upgrade overwrites this unless kept)`);
|
|
50272
|
+
}
|
|
51275
50273
|
}
|
|
51276
50274
|
}
|
|
51277
50275
|
if (health.missing_expected_docs.length > 0) {
|
|
@@ -51280,14 +50278,14 @@ async function packageDoctor(client, args) {
|
|
|
51280
50278
|
console.error(` - "${name}" (the package's agents route to this name; no matching doc exists)`);
|
|
51281
50279
|
}
|
|
51282
50280
|
}
|
|
51283
|
-
if (health.knowledge_drift.length > 0 || health.knowledge_modified.length > 0) {
|
|
50281
|
+
if (health.knowledge_drift.length > 0 || health.knowledge_modified.length > 0 && !health.is_origin) {
|
|
51284
50282
|
process.exitCode = 1;
|
|
51285
50283
|
}
|
|
51286
50284
|
if (health.knowledge_drift.length === 0 && health.knowledge_modified.length === 0 && health.missing_expected_docs.length === 0) {
|
|
51287
50285
|
console.error(" Knowledge: healthy \u2014 bound docs resolve, none locally edited, expects met.");
|
|
51288
50286
|
}
|
|
51289
50287
|
if (health.update_available) {
|
|
51290
|
-
console.error(` Upgrade: lotics
|
|
50288
|
+
console.error(` Upgrade: lotics upgrade ${app_id}`);
|
|
51291
50289
|
}
|
|
51292
50290
|
}
|
|
51293
50291
|
async function packageUpgrade(client, args) {
|
|
@@ -51322,27 +50320,17 @@ async function packageUpgrade(client, args) {
|
|
|
51322
50320
|
);
|
|
51323
50321
|
process.exit(1);
|
|
51324
50322
|
}
|
|
51325
|
-
const
|
|
51326
|
-
|
|
51327
|
-
|
|
51328
|
-
|
|
51329
|
-
const { coreResolve, knowledgeResolutions } = routeAppUpgradeResolve(
|
|
51330
|
-
args.resolve,
|
|
51331
|
-
preview.knowledge,
|
|
51332
|
-
coreKeys
|
|
51333
|
-
);
|
|
51334
|
-
const resolutions = parseResolveFlags(coreResolve);
|
|
50323
|
+
const resolutions = parseResolveFlags(args.resolve);
|
|
50324
|
+
for (const [alias, id] of Object.entries(parseBindToFlags(args.bindTo))) {
|
|
50325
|
+
resolutions[`knowledge.${alias}`] = { bind_to: id };
|
|
50326
|
+
}
|
|
51335
50327
|
if (args.applyAll) {
|
|
51336
50328
|
for (const entry of preview.knowledge) {
|
|
51337
|
-
if (knowledgeEntryNeedsConsent(entry) &&
|
|
51338
|
-
|
|
50329
|
+
if (knowledgeEntryNeedsConsent(entry) && resolutions[`knowledge.${entry.alias}`] === void 0) {
|
|
50330
|
+
resolutions[`knowledge.${entry.alias}`] = knowledgeAcceptResolution(entry.change);
|
|
51339
50331
|
}
|
|
51340
50332
|
}
|
|
51341
50333
|
}
|
|
51342
|
-
const knowledge_resolutions = {
|
|
51343
|
-
resolutions: knowledgeResolutions,
|
|
51344
|
-
bind_to: parseBindToFlags(args.bindTo)
|
|
51345
|
-
};
|
|
51346
50334
|
const unresolvedDrift = preview.drift.filter(
|
|
51347
50335
|
(d) => resolutions[`${d.namespace}.${d.alias}`] === void 0
|
|
51348
50336
|
);
|
|
@@ -51350,7 +50338,7 @@ async function packageUpgrade(client, args) {
|
|
|
51350
50338
|
(m) => resolutions[`${m.kind}.${m.alias}`] === void 0
|
|
51351
50339
|
);
|
|
51352
50340
|
const unresolvedKnowledge = preview.knowledge.filter(
|
|
51353
|
-
(e) => knowledgeEntryNeedsConsent(e) &&
|
|
50341
|
+
(e) => knowledgeEntryNeedsConsent(e) && resolutions[`knowledge.${e.alias}`] === void 0
|
|
51354
50342
|
);
|
|
51355
50343
|
if (unresolvedDrift.length > 0 || unresolvedModified.length > 0 || unresolvedKnowledge.length > 0) {
|
|
51356
50344
|
if (unresolvedDrift.length > 0) {
|
|
@@ -51379,11 +50367,9 @@ async function packageUpgrade(client, args) {
|
|
|
51379
50367
|
}
|
|
51380
50368
|
process.exit(1);
|
|
51381
50369
|
}
|
|
51382
|
-
const hasKnowledgeResolutions = Object.keys(knowledge_resolutions.resolutions).length > 0 || Object.keys(knowledge_resolutions.bind_to).length > 0;
|
|
51383
50370
|
const app = await client.upgradePackage(args.app_id, {
|
|
51384
50371
|
...args.version !== void 0 ? { version: args.version } : {},
|
|
51385
|
-
...Object.keys(resolutions).length > 0 ? { resolutions } : {}
|
|
51386
|
-
...hasKnowledgeResolutions ? { knowledge_resolutions } : {}
|
|
50372
|
+
...Object.keys(resolutions).length > 0 ? { resolutions } : {}
|
|
51387
50373
|
});
|
|
51388
50374
|
console.error(`Upgraded ${app.name} \u2192 v${app.package_version} (${app.id}).`);
|
|
51389
50375
|
}
|
|
@@ -51433,82 +50419,61 @@ function formatKnowledgeEntryLine(entry) {
|
|
|
51433
50419
|
}
|
|
51434
50420
|
function formatKnowledgeResolveHint(entry) {
|
|
51435
50421
|
const note = entry.change === "changed" || entry.change === "removed" ? " (a local edit \u2014 apply/archive overwrites it; keep retains it)" : " (bound doc is gone; recreate from the package, or unbind)";
|
|
51436
|
-
return ` --resolve
|
|
50422
|
+
return ` --resolve knowledge.${entry.alias}=${validKnowledgeResolutions(entry.change).join("|")}${note}`;
|
|
51437
50423
|
}
|
|
51438
|
-
function
|
|
51439
|
-
const
|
|
51440
|
-
|
|
51441
|
-
|
|
51442
|
-
|
|
51443
|
-
|
|
51444
|
-
|
|
51445
|
-
|
|
51446
|
-
|
|
51447
|
-
|
|
51448
|
-
const value = entry.slice(eq + 1);
|
|
51449
|
-
const known = byAlias.get(key);
|
|
51450
|
-
if (known && coreKeys.has(key)) {
|
|
51451
|
-
throw new Error(
|
|
51452
|
-
`--resolve "${key}" is ambiguous \u2014 it names both a bundled knowledge doc and a core artifact in this upgrade. Rename one alias so it is unambiguous; refusing to guess.`
|
|
51453
|
-
);
|
|
51454
|
-
}
|
|
51455
|
-
if (known) {
|
|
51456
|
-
const valid = validKnowledgeResolutions(known.change);
|
|
51457
|
-
const match = valid.find((v) => v === value);
|
|
51458
|
-
if (match === void 0) {
|
|
51459
|
-
throw new Error(
|
|
51460
|
-
`Invalid --resolve value "${value}" for knowledge doc "${key}" (${known.change}) \u2014 expected ${valid.join("|")}.`
|
|
51461
|
-
);
|
|
51462
|
-
}
|
|
51463
|
-
knowledgeResolutions[key] = match;
|
|
51464
|
-
} else {
|
|
51465
|
-
coreResolve.push(entry);
|
|
51466
|
-
}
|
|
50424
|
+
function formatTemplateResolveHint(entry) {
|
|
50425
|
+
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)";
|
|
50426
|
+
return ` --resolve template.${entry.alias}=revert|keep${note}`;
|
|
50427
|
+
}
|
|
50428
|
+
async function resolveWorkspaceContentInstallation(client, package_id) {
|
|
50429
|
+
const workspaceId = client.getWorkspaceId();
|
|
50430
|
+
if (!workspaceId) {
|
|
50431
|
+
throw new Error(
|
|
50432
|
+
"No workspace selected. Pass --workspace <ws> (or select one) to operate on its content installation."
|
|
50433
|
+
);
|
|
51467
50434
|
}
|
|
51468
|
-
|
|
50435
|
+
const installations = await client.listContentInstallations(workspaceId);
|
|
50436
|
+
const match = installations.find((inst) => inst.package_id === package_id);
|
|
50437
|
+
if (!match) {
|
|
50438
|
+
throw new Error(
|
|
50439
|
+
`Package ${package_id} is not installed in this workspace \u2014 install it first: lotics install ${package_id}`
|
|
50440
|
+
);
|
|
50441
|
+
}
|
|
50442
|
+
return match;
|
|
51469
50443
|
}
|
|
51470
|
-
function
|
|
51471
|
-
|
|
51472
|
-
|
|
51473
|
-
|
|
51474
|
-
const
|
|
51475
|
-
|
|
51476
|
-
|
|
51477
|
-
|
|
51478
|
-
|
|
51479
|
-
|
|
51480
|
-
|
|
51481
|
-
const value = entry.slice(eq + 1);
|
|
51482
|
-
const isKnowledge = knowledgeAliases.has(key);
|
|
51483
|
-
const isTemplate = templateAliases.has(key);
|
|
51484
|
-
if (isKnowledge && isTemplate) {
|
|
51485
|
-
throw new Error(
|
|
51486
|
-
`--resolve "${key}" is ambiguous \u2014 it names both a knowledge doc and a template in this upgrade. Rename one alias in the package so the two content namespaces are disjoint.`
|
|
51487
|
-
);
|
|
51488
|
-
}
|
|
51489
|
-
if (isTemplate) {
|
|
51490
|
-
if (!templateValid.has(value)) {
|
|
51491
|
-
throw new Error(`Invalid --resolve value "${value}" for template "${key}" \u2014 expected revert|keep.`);
|
|
51492
|
-
}
|
|
51493
|
-
templates[key] = value;
|
|
51494
|
-
continue;
|
|
51495
|
-
}
|
|
51496
|
-
if (isKnowledge) {
|
|
51497
|
-
if (!knowledgeValid.has(value)) {
|
|
51498
|
-
throw new Error(
|
|
51499
|
-
`Invalid --resolve value "${value}" for knowledge alias "${key}" \u2014 expected apply|keep|archive|recreate|unbind.`
|
|
51500
|
-
);
|
|
51501
|
-
}
|
|
51502
|
-
knowledge[key] = value;
|
|
51503
|
-
continue;
|
|
51504
|
-
}
|
|
51505
|
-
throw new Error(`--resolve "${key}" does not name a knowledge doc or template in this upgrade.`);
|
|
50444
|
+
async function redirectContentPciForm(client, pci_id, verb) {
|
|
50445
|
+
console.error(
|
|
50446
|
+
`Content installations are addressed by their PACKAGE id now \u2014 ${pci_id} is a server-internal resource id.`
|
|
50447
|
+
);
|
|
50448
|
+
const workspaceId = client.getWorkspaceId();
|
|
50449
|
+
const match = workspaceId ? (await client.listContentInstallations(workspaceId)).find((inst) => inst.id === pci_id) : void 0;
|
|
50450
|
+
if (match) {
|
|
50451
|
+
console.error(` Run: lotics ${verb} ${match.package_id}`);
|
|
50452
|
+
} else {
|
|
50453
|
+
console.error(" Find the package id: lotics package list-content");
|
|
50454
|
+
console.error(` Then run: lotics ${verb} <package_id>`);
|
|
51506
50455
|
}
|
|
51507
|
-
|
|
50456
|
+
process.exit(1);
|
|
51508
50457
|
}
|
|
51509
|
-
function
|
|
51510
|
-
const
|
|
51511
|
-
|
|
50458
|
+
async function packageUpgradeByPackageId(client, args) {
|
|
50459
|
+
const pkg2 = await client.getPackage(args.package_id);
|
|
50460
|
+
if (pkg2.kind === "content") {
|
|
50461
|
+
const installation = await resolveWorkspaceContentInstallation(client, args.package_id);
|
|
50462
|
+
await packageUpgradeKnowledge(client, {
|
|
50463
|
+
installation_id: installation.id,
|
|
50464
|
+
package_id: pkg2.id,
|
|
50465
|
+
package_name: pkg2.name,
|
|
50466
|
+
version: args.version,
|
|
50467
|
+
resolve: args.resolve,
|
|
50468
|
+
bind_to: parseBindToFlags(args.bindTo),
|
|
50469
|
+
applyAll: args.applyAll
|
|
50470
|
+
});
|
|
50471
|
+
return;
|
|
50472
|
+
}
|
|
50473
|
+
await packageFleetUpgrade(client, {
|
|
50474
|
+
package_id: args.package_id,
|
|
50475
|
+
...args.version !== void 0 ? { version: args.version } : {}
|
|
50476
|
+
});
|
|
51512
50477
|
}
|
|
51513
50478
|
async function packageUpgradeKnowledge(client, args) {
|
|
51514
50479
|
const preview = await client.previewContentInstallationUpgrade(args.installation_id, {
|
|
@@ -51522,7 +50487,7 @@ async function packageUpgradeKnowledge(client, args) {
|
|
|
51522
50487
|
...args.version !== void 0 ? { version: args.version } : {},
|
|
51523
50488
|
resolutions: resolutions2
|
|
51524
50489
|
});
|
|
51525
|
-
console.error(`Upgraded ${args.
|
|
50490
|
+
console.error(`Upgraded ${args.package_name} (${args.package_id}) \u2192 v${updated.package_version}.`);
|
|
51526
50491
|
};
|
|
51527
50492
|
if (preview.entries.length === 0 && preview.templates.length === 0) {
|
|
51528
50493
|
if (preview.to_version === preview.from_version) {
|
|
@@ -51532,7 +50497,7 @@ async function packageUpgradeKnowledge(client, args) {
|
|
|
51532
50497
|
console.error(
|
|
51533
50498
|
" No changes needing consent \u2014 advancing the version pin; clean template updates apply automatically."
|
|
51534
50499
|
);
|
|
51535
|
-
await apply({
|
|
50500
|
+
await apply({});
|
|
51536
50501
|
return;
|
|
51537
50502
|
}
|
|
51538
50503
|
for (const entry of preview.entries) {
|
|
@@ -51541,30 +50506,27 @@ async function packageUpgradeKnowledge(client, args) {
|
|
|
51541
50506
|
for (const entry of preview.templates) {
|
|
51542
50507
|
console.error(` [template] ${entry.alias} (modified \u2014 needs consent)`);
|
|
51543
50508
|
}
|
|
51544
|
-
const
|
|
51545
|
-
|
|
51546
|
-
|
|
51547
|
-
|
|
51548
|
-
);
|
|
51549
|
-
const resolutions = {
|
|
51550
|
-
knowledge: { resolutions: { ...routed.knowledge }, bind_to: { ...args.bind_to } },
|
|
51551
|
-
templates: { ...routed.templates }
|
|
51552
|
-
};
|
|
50509
|
+
const resolutions = parseResolveFlags(args.resolve);
|
|
50510
|
+
for (const [alias, id] of Object.entries(args.bind_to)) {
|
|
50511
|
+
resolutions[`knowledge.${alias}`] = { bind_to: id };
|
|
50512
|
+
}
|
|
51553
50513
|
if (args.applyAll) {
|
|
51554
50514
|
for (const entry of preview.entries) {
|
|
51555
|
-
if (knowledgeEntryNeedsConsent(entry) && resolutions
|
|
51556
|
-
resolutions
|
|
50515
|
+
if (knowledgeEntryNeedsConsent(entry) && resolutions[`knowledge.${entry.alias}`] === void 0) {
|
|
50516
|
+
resolutions[`knowledge.${entry.alias}`] = knowledgeAcceptResolution(entry.change);
|
|
51557
50517
|
}
|
|
51558
50518
|
}
|
|
51559
50519
|
for (const entry of preview.templates) {
|
|
51560
|
-
if (resolutions
|
|
50520
|
+
if (resolutions[`template.${entry.alias}`] === void 0) {
|
|
50521
|
+
resolutions[`template.${entry.alias}`] = "revert";
|
|
50522
|
+
}
|
|
51561
50523
|
}
|
|
51562
50524
|
}
|
|
51563
50525
|
const unresolvedKnowledge = preview.entries.filter(
|
|
51564
|
-
(entry) => knowledgeEntryNeedsConsent(entry) && resolutions
|
|
50526
|
+
(entry) => knowledgeEntryNeedsConsent(entry) && resolutions[`knowledge.${entry.alias}`] === void 0
|
|
51565
50527
|
);
|
|
51566
50528
|
const unresolvedTemplates = preview.templates.filter(
|
|
51567
|
-
(entry) => resolutions
|
|
50529
|
+
(entry) => resolutions[`template.${entry.alias}`] === void 0
|
|
51568
50530
|
);
|
|
51569
50531
|
if (unresolvedKnowledge.length > 0 || unresolvedTemplates.length > 0) {
|
|
51570
50532
|
console.error(
|
|
@@ -51597,8 +50559,8 @@ function warnMissingExpectedDocs(missing) {
|
|
|
51597
50559
|
}
|
|
51598
50560
|
async function packageInstall(client, args) {
|
|
51599
50561
|
const pkg2 = await client.getPackage(args.package_id);
|
|
51600
|
-
const
|
|
51601
|
-
console.error(`Installing ${pkg2.name} \u2014 ${trustBadge(pkg2)}
|
|
50562
|
+
const kindSuffix = pkg2.kind === "content" ? " (content \u2014 docs/templates, no app)" : "";
|
|
50563
|
+
console.error(`Installing ${pkg2.name} \u2014 ${trustBadge(pkg2)}${kindSuffix}...`);
|
|
51602
50564
|
const result = await client.installPackage(args.package_id, {
|
|
51603
50565
|
...args.version !== void 0 ? { version: args.version } : {},
|
|
51604
50566
|
...args.bind_to && Object.keys(args.bind_to).length > 0 ? { bind_to: args.bind_to } : {},
|
|
@@ -51609,7 +50571,7 @@ async function packageInstall(client, args) {
|
|
|
51609
50571
|
const docs = installation.binding.knowledge;
|
|
51610
50572
|
const templates = installation.binding.templates;
|
|
51611
50573
|
console.error(
|
|
51612
|
-
`Installed ${pkg2.name} v${installation.package_version}
|
|
50574
|
+
`Installed ${pkg2.name} v${installation.package_version} (workspace ${installation.workspace_id}).`
|
|
51613
50575
|
);
|
|
51614
50576
|
const docAliases = Object.keys(docs);
|
|
51615
50577
|
if (docAliases.length > 0) {
|
|
@@ -51624,8 +50586,8 @@ async function packageInstall(client, args) {
|
|
|
51624
50586
|
);
|
|
51625
50587
|
}
|
|
51626
50588
|
warnMissingExpectedDocs(warnings.missing_expected_docs);
|
|
51627
|
-
console.error(` Upgrade later: lotics
|
|
51628
|
-
console.error(` Uninstall: lotics
|
|
50589
|
+
console.error(` Upgrade later: lotics upgrade ${args.package_id}`);
|
|
50590
|
+
console.error(` Uninstall: lotics uninstall ${args.package_id} [--keep-content]`);
|
|
51629
50591
|
return;
|
|
51630
50592
|
}
|
|
51631
50593
|
const { app, knowledge_warnings } = result;
|
|
@@ -51633,25 +50595,34 @@ async function packageInstall(client, args) {
|
|
|
51633
50595
|
console.error(`Installed ${app.name} ${versionLabel} \u2192 ${app.id} (workspace ${app.workspace_id}).`);
|
|
51634
50596
|
console.error(" The data model, queries, workflows, and agents are live.");
|
|
51635
50597
|
warnMissingExpectedDocs(knowledge_warnings.missing_expected_docs);
|
|
51636
|
-
console.error(
|
|
50598
|
+
console.error(` Pull it for local editing: lotics app pull ${app.id}`);
|
|
50599
|
+
console.error(` Upgrade later: lotics upgrade ${app.id}`);
|
|
50600
|
+
console.error(` Health / uninstall: lotics package doctor ${app.id} \xB7 lotics uninstall ${app.id} [--archive-tables]`);
|
|
51637
50601
|
}
|
|
51638
50602
|
async function packageUninstall(client, args) {
|
|
51639
|
-
if (args.id.startsWith("
|
|
50603
|
+
if (args.id.startsWith("apg_")) {
|
|
51640
50604
|
if (args.archive_tables) {
|
|
51641
50605
|
throw new Error(
|
|
51642
|
-
"--archive-tables applies only to an app installation (<app_id>). A content
|
|
50606
|
+
"--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."
|
|
50607
|
+
);
|
|
50608
|
+
}
|
|
50609
|
+
const pkg2 = await client.getPackage(args.id);
|
|
50610
|
+
if (pkg2.kind !== "content") {
|
|
50611
|
+
throw new Error(
|
|
50612
|
+
`Package ${args.id} is an app package \u2014 uninstall an app installation by its app id: lotics uninstall <app_id>.`
|
|
51643
50613
|
);
|
|
51644
50614
|
}
|
|
51645
|
-
const
|
|
50615
|
+
const installation = await resolveWorkspaceContentInstallation(client, args.id);
|
|
50616
|
+
const result2 = await client.uninstallContentPackage(installation.id, {
|
|
51646
50617
|
keep_content: args.keep_content
|
|
51647
50618
|
});
|
|
51648
50619
|
if (args.keep_content) {
|
|
51649
50620
|
console.error(
|
|
51650
|
-
`Uninstalled
|
|
50621
|
+
`Uninstalled ${pkg2.name} (${pkg2.id}) \u2014 its docs and templates were kept as ordinary workspace content.`
|
|
51651
50622
|
);
|
|
51652
50623
|
} else {
|
|
51653
50624
|
console.error(
|
|
51654
|
-
`Uninstalled
|
|
50625
|
+
`Uninstalled ${pkg2.name} (${pkg2.id}) \u2014 archived ${result2.archived_doc_ids.length} doc(s) and ${result2.archived_template_ids.length} template(s).`
|
|
51655
50626
|
);
|
|
51656
50627
|
for (const docId of result2.archived_doc_ids) console.error(` ${docId}`);
|
|
51657
50628
|
for (const templateId of result2.archived_template_ids) console.error(` ${templateId}`);
|
|
@@ -51660,7 +50631,7 @@ async function packageUninstall(client, args) {
|
|
|
51660
50631
|
}
|
|
51661
50632
|
if (args.keep_content) {
|
|
51662
50633
|
throw new Error(
|
|
51663
|
-
"--keep-content applies only to a
|
|
50634
|
+
"--keep-content applies only to a content package (apg_). An app installation (<app_id>) uses --archive-tables to also archive its scaffolded tables."
|
|
51664
50635
|
);
|
|
51665
50636
|
}
|
|
51666
50637
|
const app = await client.getApp(args.id);
|
|
@@ -51702,10 +50673,9 @@ async function packageListContent(client) {
|
|
|
51702
50673
|
const name = inst.package_registry?.name ?? "(unknown package)";
|
|
51703
50674
|
const latest = inst.package_registry?.latest_version;
|
|
51704
50675
|
const updateAvailable = inst.package_registry?.update_available ?? false;
|
|
51705
|
-
const source = inst.app_id === null ? "standalone" : `app-bundled (${inst.app_id})`;
|
|
51706
50676
|
const versionLabel = latest !== void 0 && latest !== inst.package_version ? `v${inst.package_version} \u2192 latest v${latest}` : `v${inst.package_version}`;
|
|
51707
50677
|
console.error(
|
|
51708
|
-
` ${inst.
|
|
50678
|
+
` ${inst.package_id} ${name} ${versionLabel}` + (updateAvailable ? " \u2192 update available" : "")
|
|
51709
50679
|
);
|
|
51710
50680
|
}
|
|
51711
50681
|
}
|
|
@@ -51781,200 +50751,143 @@ async function packageConfig(client, args) {
|
|
|
51781
50751
|
console.log(` ${key} = ${JSON.stringify(config2[key])}${marker}`);
|
|
51782
50752
|
}
|
|
51783
50753
|
}
|
|
51784
|
-
async function
|
|
51785
|
-
|
|
50754
|
+
async function appUnpublish(client, args) {
|
|
50755
|
+
let packageId = args.id;
|
|
50756
|
+
if (args.id.startsWith("app_")) {
|
|
50757
|
+
const app = await client.getApp(args.id);
|
|
50758
|
+
if (!app.package_id) {
|
|
50759
|
+
throw new Error(
|
|
50760
|
+
`App ${args.id} is not a package installation \u2014 it has no package to unpublish. Pass the package id directly.`
|
|
50761
|
+
);
|
|
50762
|
+
}
|
|
50763
|
+
packageId = app.package_id;
|
|
50764
|
+
}
|
|
50765
|
+
const pkg2 = await client.retirePackage(packageId, { undo: args.undo });
|
|
51786
50766
|
if (pkg2.retired_at !== null) {
|
|
51787
50767
|
console.error(
|
|
51788
|
-
`
|
|
50768
|
+
`Unpublished ${pkg2.name} (${pkg2.id}). New installs refuse it and it is hidden from other orgs; existing installations keep working and may still upgrade. Undo: lotics app unpublish ${pkg2.id} --undo`
|
|
51789
50769
|
);
|
|
51790
50770
|
} else {
|
|
51791
|
-
console.error(`
|
|
50771
|
+
console.error(`Re-published ${pkg2.name} (${pkg2.id}) \u2014 installable again.`);
|
|
51792
50772
|
}
|
|
51793
50773
|
}
|
|
51794
|
-
function
|
|
51795
|
-
const
|
|
51796
|
-
const
|
|
51797
|
-
|
|
51798
|
-
|
|
51799
|
-
|
|
51800
|
-
knowledge.push({ alias: entry.alias, doc_id: entry.doc_id });
|
|
51801
|
-
}
|
|
51802
|
-
}
|
|
51803
|
-
}
|
|
51804
|
-
const knowledge_expects = Array.isArray(lotics.knowledge_expects) ? lotics.knowledge_expects.filter((v) => typeof v === "string") : [];
|
|
51805
|
-
return { knowledge, knowledge_expects };
|
|
51806
|
-
}
|
|
51807
|
-
function knowledgeManifestFromContract(contract) {
|
|
51808
|
-
const out = {};
|
|
51809
|
-
if (!isPlainObject2(contract) || !isPlainObject2(contract.knowledge)) return out;
|
|
51810
|
-
for (const [alias, entry] of Object.entries(contract.knowledge)) {
|
|
51811
|
-
if (!isPlainObject2(entry) || typeof entry.name !== "string") continue;
|
|
51812
|
-
out[alias] = {
|
|
51813
|
-
name: entry.name,
|
|
51814
|
-
description: typeof entry.description === "string" ? entry.description : null,
|
|
51815
|
-
active_by_default: typeof entry.active_by_default === "boolean" ? entry.active_by_default : true
|
|
51816
|
-
};
|
|
51817
|
-
}
|
|
51818
|
-
return out;
|
|
51819
|
-
}
|
|
51820
|
-
async function packageExtract(client, args) {
|
|
51821
|
-
const app = await client.getApp(args.app_id);
|
|
51822
|
-
if (!app.current_version_id) {
|
|
50774
|
+
async function appPublish(client, args) {
|
|
50775
|
+
const projectDir = path6.resolve(args.projectDir ?? process.cwd());
|
|
50776
|
+
const local = readLocalAppManifest(projectDir);
|
|
50777
|
+
const explicit = args.app_id !== void 0 && args.app_id !== "." ? args.app_id : void 0;
|
|
50778
|
+
const appId = explicit ?? local?.app_id ?? null;
|
|
50779
|
+
if (appId === null) {
|
|
51823
50780
|
throw new Error(
|
|
51824
|
-
|
|
50781
|
+
"No app id. Run `lotics app publish` from a pulled app project (lotics app pull <app_id>), or pass one: lotics app publish <app_id>."
|
|
51825
50782
|
);
|
|
51826
50783
|
}
|
|
51827
|
-
const
|
|
51828
|
-
|
|
51829
|
-
|
|
51830
|
-
}
|
|
51831
|
-
|
|
51832
|
-
const
|
|
51833
|
-
|
|
51834
|
-
|
|
51835
|
-
|
|
51836
|
-
|
|
51837
|
-
|
|
51838
|
-
|
|
51839
|
-
|
|
51840
|
-
|
|
51841
|
-
|
|
51842
|
-
|
|
51843
|
-
|
|
51844
|
-
|
|
51845
|
-
);
|
|
51846
|
-
const appKnowledge = readAppKnowledgeDeclaration(appPkgJson);
|
|
51847
|
-
const extracted = await client.extractPackage(args.app_id, {
|
|
51848
|
-
knowledge: appKnowledge.knowledge
|
|
51849
|
-
});
|
|
51850
|
-
const { lines, hasError } = formatExtractReport(extracted.report);
|
|
51851
|
-
if (lines.length > 0) {
|
|
51852
|
-
console.error(`Extraction report (${extracted.report.length}):`);
|
|
51853
|
-
for (const line of lines) console.error(line);
|
|
51854
|
-
} else {
|
|
51855
|
-
console.error("Extraction report: no findings.");
|
|
51856
|
-
}
|
|
51857
|
-
const draft = draftPackageProjectFromApp(appPkgJson, { name: app.name, description: null });
|
|
51858
|
-
draft.manifest.knowledge = knowledgeManifestFromContract(extracted.contract);
|
|
51859
|
-
draft.manifest.knowledge_expects = appKnowledge.knowledge_expects;
|
|
51860
|
-
const deps = isPlainObject2(draft.pkgJson.dependencies) ? draft.pkgJson.dependencies : {};
|
|
51861
|
-
const originRange = typeof deps["@lotics/app-sdk"] === "string" ? deps["@lotics/app-sdk"] : null;
|
|
51862
|
-
const originFloor = originRange?.replace(/^[\^~]/, "") ?? null;
|
|
51863
|
-
if (originFloor === null || cmpVersions(originFloor, STARTER_FALLBACK_SDK_VERSION) < 0) {
|
|
51864
|
-
const raised = packageSdkRange(await fetchLatestNpmVersion("@lotics/app-sdk"));
|
|
51865
|
-
draft.pkgJson.dependencies = { ...deps, "@lotics/app-sdk": raised };
|
|
51866
|
-
console.error(`Raised @lotics/app-sdk to ${raised} (generated app_fields needs getAppBinding)`);
|
|
51867
|
-
}
|
|
51868
|
-
writePackageManifest(targetPath, draft);
|
|
51869
|
-
fs6.writeFileSync(
|
|
51870
|
-
path7.join(targetPath, CONTRACT_FILE),
|
|
51871
|
-
JSON.stringify(extracted.contract, null, 2) + "\n"
|
|
51872
|
-
);
|
|
51873
|
-
console.error(`Wrote ${CONTRACT_FILE}`);
|
|
51874
|
-
writePackageAppFields(targetPath);
|
|
51875
|
-
const starterViteConfig = buildStarterTemplate({
|
|
51876
|
-
app_name: app.name,
|
|
51877
|
-
app_id: "",
|
|
51878
|
-
workspace_id: ""
|
|
51879
|
-
}).find((f) => f.path === "vite.config.ts");
|
|
51880
|
-
if (starterViteConfig === void 0) {
|
|
51881
|
-
throw new Error("starter template is missing vite.config.ts \u2014 cannot refresh the package project");
|
|
51882
|
-
}
|
|
51883
|
-
const viteConfigPath = path7.join(targetPath, "vite.config.ts");
|
|
51884
|
-
const originViteConfig = fs6.existsSync(viteConfigPath) ? fs6.readFileSync(viteConfigPath, "utf-8") : null;
|
|
51885
|
-
fs6.writeFileSync(viteConfigPath, starterViteConfig.content);
|
|
51886
|
-
if (originViteConfig !== null && originViteConfig !== starterViteConfig.content) {
|
|
51887
|
-
const stash = path7.join(dotLoticsDirEnsured(targetPath), "vite.config.origin.ts");
|
|
51888
|
-
fs6.writeFileSync(stash, originViteConfig);
|
|
50784
|
+
const knowledge = local && local.app_id === appId ? local.knowledge : [];
|
|
50785
|
+
const renames = parseRenameFlags(args.renames);
|
|
50786
|
+
const preview = await client.previewPublishAppPackage(appId, { renames, knowledge });
|
|
50787
|
+
const { lines, hasError } = formatExtractReport(preview.findings);
|
|
50788
|
+
console.error(`Publish preview \u2014 ${appId} as new package "${preview.package_name}" (v1):`);
|
|
50789
|
+
const groups = [
|
|
50790
|
+
["entities", preview.renamable_aliases.entities],
|
|
50791
|
+
["fields", preview.renamable_aliases.fields],
|
|
50792
|
+
["options", preview.renamable_aliases.options],
|
|
50793
|
+
["roles", preview.renamable_aliases.roles],
|
|
50794
|
+
["templates", preview.renamable_aliases.templates],
|
|
50795
|
+
["workflows", preview.renamable_aliases.workflows]
|
|
50796
|
+
];
|
|
50797
|
+
if (groups.some(([, vals]) => vals.length > 0)) {
|
|
50798
|
+
console.error(" Auto-minted aliases \u2014 rename any with --rename <alias>=<new> before v1 freezes them:");
|
|
50799
|
+
for (const [label, vals] of groups) {
|
|
50800
|
+
if (vals.length > 0) console.error(` ${`${label}:`.padEnd(11)} ${vals.join(", ")}`);
|
|
50801
|
+
}
|
|
51889
50802
|
console.error(
|
|
51890
|
-
"
|
|
50803
|
+
" (query / app-workflow / agent runtime aliases are fixed \u2014 the shipped source calls them verbatim.)"
|
|
51891
50804
|
);
|
|
51892
50805
|
} else {
|
|
51893
|
-
console.error("
|
|
51894
|
-
}
|
|
51895
|
-
|
|
51896
|
-
|
|
51897
|
-
|
|
51898
|
-
|
|
51899
|
-
if (path7.resolve(downloaded) !== path7.resolve(dest)) fs6.renameSync(downloaded, dest);
|
|
51900
|
-
console.error(`Wrote ${tf.bytes_ref}`);
|
|
51901
|
-
}
|
|
51902
|
-
for (const kf of extracted.knowledge_files) {
|
|
51903
|
-
const dest = path7.join(targetPath, kf.content_ref);
|
|
51904
|
-
fs6.mkdirSync(path7.dirname(dest), { recursive: true });
|
|
51905
|
-
fs6.writeFileSync(dest, kf.content);
|
|
51906
|
-
console.error(`Wrote ${kf.content_ref}`);
|
|
51907
|
-
}
|
|
51908
|
-
const dotLotics = path7.join(targetPath, ".lotics");
|
|
51909
|
-
fs6.mkdirSync(dotLotics, { recursive: true });
|
|
51910
|
-
fs6.writeFileSync(
|
|
51911
|
-
path7.join(dotLotics, ADOPT_BINDING_FILE),
|
|
51912
|
-
JSON.stringify(
|
|
51913
|
-
{
|
|
51914
|
-
app_id: app.id,
|
|
51915
|
-
workspace_id: app.workspace_id,
|
|
51916
|
-
binding: extracted.binding,
|
|
51917
|
-
knowledge_binding: extracted.knowledge_binding
|
|
51918
|
-
},
|
|
51919
|
-
null,
|
|
51920
|
-
2
|
|
51921
|
-
) + "\n"
|
|
51922
|
-
);
|
|
51923
|
-
console.error(`Wrote .lotics/${ADOPT_BINDING_FILE}`);
|
|
51924
|
-
console.error("Installing npm dependencies...");
|
|
51925
|
-
await runNpm(["install"], targetPath);
|
|
50806
|
+
console.error(" No renamable aliases.");
|
|
50807
|
+
}
|
|
50808
|
+
if (lines.length > 0) {
|
|
50809
|
+
console.error(` Findings (${preview.findings.length}):`);
|
|
50810
|
+
for (const line of lines) console.error(line);
|
|
50811
|
+
}
|
|
51926
50812
|
if (hasError) {
|
|
51927
50813
|
console.error(
|
|
51928
|
-
"\
|
|
51929
|
-
);
|
|
51930
|
-
console.error(
|
|
51931
|
-
"Fix the reported items above, then publish (which re-validates the contract)."
|
|
50814
|
+
"\nExtract found error findings \u2014 the app cannot be published as-is. Fix them in the app, redeploy, and retry."
|
|
51932
50815
|
);
|
|
51933
50816
|
process.exitCode = 1;
|
|
51934
50817
|
return;
|
|
51935
50818
|
}
|
|
51936
|
-
|
|
51937
|
-
|
|
51938
|
-
|
|
51939
|
-
|
|
51940
|
-
|
|
51941
|
-
|
|
51942
|
-
|
|
51943
|
-
|
|
51944
|
-
|
|
51945
|
-
if (manifest.id === null) {
|
|
51946
|
-
throw new Error(
|
|
51947
|
-
"This package project has never been published (no package id). Publish first:\n lotics package publish"
|
|
51948
|
-
);
|
|
50819
|
+
if (!args.yes) {
|
|
50820
|
+
const idArg = explicit ?? ".";
|
|
50821
|
+
const renameArgs = args.renames.map((r) => ` --rename ${r}`).join("");
|
|
50822
|
+
const mArg = args.changelog ? ` -m ${JSON.stringify(args.changelog)}` : "";
|
|
50823
|
+
console.error(`
|
|
50824
|
+
Re-run with --yes to publish v1:`);
|
|
50825
|
+
console.error(` lotics app publish ${idArg}${renameArgs}${mArg} --yes`);
|
|
50826
|
+
process.exitCode = 1;
|
|
50827
|
+
return;
|
|
51949
50828
|
}
|
|
51950
|
-
const
|
|
51951
|
-
|
|
50829
|
+
const result = await client.publishAppAsPackage(appId, {
|
|
50830
|
+
renames,
|
|
50831
|
+
changelog: args.changelog ?? null,
|
|
50832
|
+
knowledge
|
|
50833
|
+
});
|
|
50834
|
+
console.error(`Published ${result.package_id} v${result.version} from app ${appId}.`);
|
|
50835
|
+
console.error(` The app is now installation #1 \u2014 develop it in place, then release the next version:`);
|
|
50836
|
+
console.error(` lotics app pull ${appId} # edit, then lotics app deploy`);
|
|
50837
|
+
console.error(` lotics app release ${appId} -m "<what changed>"`);
|
|
50838
|
+
console.error(` Install it elsewhere: lotics install ${result.package_id}`);
|
|
50839
|
+
}
|
|
50840
|
+
async function appRelease(client, args) {
|
|
50841
|
+
const projectDir = path6.resolve(args.projectDir ?? process.cwd());
|
|
50842
|
+
const local = readLocalAppManifest(projectDir);
|
|
50843
|
+
const explicit = args.app_id !== void 0 && args.app_id !== "." ? args.app_id : void 0;
|
|
50844
|
+
const appId = explicit ?? local?.app_id ?? null;
|
|
50845
|
+
if (appId === null) {
|
|
51952
50846
|
throw new Error(
|
|
51953
|
-
"No
|
|
50847
|
+
"No app id. Run this from a pulled app project (lotics app pull <app_id>), or pass an app id explicitly."
|
|
51954
50848
|
);
|
|
51955
50849
|
}
|
|
51956
|
-
const
|
|
51957
|
-
|
|
51958
|
-
|
|
51959
|
-
|
|
51960
|
-
|
|
50850
|
+
const knowledge = local && local.app_id === appId && local.knowledge.length > 0 ? local.knowledge : void 0;
|
|
50851
|
+
const preview = await client.previewPackageRelease(appId, { knowledge });
|
|
50852
|
+
const { lines, hasError } = formatExtractReport(preview.findings);
|
|
50853
|
+
console.error(`Release preview \u2014 ${appId} \u2192 ${preview.package_id} v${preview.version}:`);
|
|
50854
|
+
if (preview.added_aliases.length > 0) {
|
|
50855
|
+
console.error(` New (${preview.added_aliases.length}): ${preview.added_aliases.join(", ")}`);
|
|
51961
50856
|
}
|
|
51962
|
-
|
|
51963
|
-
|
|
51964
|
-
|
|
51965
|
-
|
|
51966
|
-
|
|
51967
|
-
|
|
51968
|
-
|
|
51969
|
-
|
|
51970
|
-
|
|
51971
|
-
|
|
51972
|
-
|
|
51973
|
-
}
|
|
51974
|
-
|
|
51975
|
-
|
|
51976
|
-
|
|
51977
|
-
|
|
50857
|
+
if (preview.changed_artifacts.length > 0) {
|
|
50858
|
+
console.error(` Changed (${preview.changed_artifacts.length}): ${preview.changed_artifacts.join(", ")}`);
|
|
50859
|
+
}
|
|
50860
|
+
const k = preview.knowledge ?? { added: [], removed: [], changed: [] };
|
|
50861
|
+
if (k.added.length > 0 || k.removed.length > 0 || k.changed.length > 0) {
|
|
50862
|
+
const parts = [
|
|
50863
|
+
k.added.length > 0 ? `+${k.added.join(", ")}` : null,
|
|
50864
|
+
k.removed.length > 0 ? `dropped ${k.removed.join(", ")}` : null,
|
|
50865
|
+
k.changed.length > 0 ? `changed ${k.changed.join(", ")}` : null
|
|
50866
|
+
].filter((p) => p !== null);
|
|
50867
|
+
console.error(` Knowledge: ${parts.join("; ")}`);
|
|
50868
|
+
}
|
|
50869
|
+
if (preview.added_aliases.length === 0 && preview.changed_artifacts.length === 0 && k.added.length === 0 && k.removed.length === 0 && k.changed.length === 0) {
|
|
50870
|
+
console.error(" No contract changes since the current version (a fresh code/dist snapshot still ships).");
|
|
50871
|
+
}
|
|
50872
|
+
if (lines.length > 0) {
|
|
50873
|
+
console.error(` Findings (${preview.findings.length}):`);
|
|
50874
|
+
for (const line of lines) console.error(line);
|
|
50875
|
+
}
|
|
50876
|
+
if (hasError) {
|
|
50877
|
+
console.error("\nExtract found error findings \u2014 the origin cannot be released as-is. Fix them in the app and retry.");
|
|
50878
|
+
process.exitCode = 1;
|
|
50879
|
+
return;
|
|
50880
|
+
}
|
|
50881
|
+
if (!args.yes) {
|
|
50882
|
+
console.error(`
|
|
50883
|
+
Re-run with --yes to publish v${preview.version}:`);
|
|
50884
|
+
console.error(` lotics app release ${args.app_id ?? "."} -m ${JSON.stringify(args.changelog)} --yes`);
|
|
50885
|
+
process.exitCode = 1;
|
|
50886
|
+
return;
|
|
50887
|
+
}
|
|
50888
|
+
const result = await client.releasePackage(appId, { changelog: args.changelog, knowledge });
|
|
50889
|
+
console.error(`Released ${result.package_id} v${result.version}.`);
|
|
50890
|
+
console.error(` The origin was re-pinned to v${result.version} \u2014 verify: lotics package doctor ${appId}`);
|
|
51978
50891
|
}
|
|
51979
50892
|
async function packageYank(client, args) {
|
|
51980
50893
|
const result = await client.yankPackageVersion(args.package_id, args.version, !args.undo);
|
|
@@ -52006,7 +50919,7 @@ async function packageFleetUpgrade(client, args) {
|
|
|
52006
50919
|
`${line}: breaking=${inst.blockers.breaking} drift=${inst.blockers.drift} modified=${inst.blockers.modified}`
|
|
52007
50920
|
);
|
|
52008
50921
|
console.error(
|
|
52009
|
-
` resolve via: lotics
|
|
50922
|
+
` resolve via: lotics upgrade ${inst.app_id} --version ${result.target_version} ...`
|
|
52010
50923
|
);
|
|
52011
50924
|
} else if (inst.message) {
|
|
52012
50925
|
console.error(`${line}: ${inst.message}`);
|
|
@@ -52038,7 +50951,6 @@ function parseArgs(argv) {
|
|
|
52038
50951
|
message: void 0,
|
|
52039
50952
|
local: false,
|
|
52040
50953
|
all: false,
|
|
52041
|
-
dev: false,
|
|
52042
50954
|
yes: false,
|
|
52043
50955
|
printCreated: false,
|
|
52044
50956
|
cleanup: false,
|
|
@@ -52047,7 +50959,7 @@ function parseArgs(argv) {
|
|
|
52047
50959
|
bindTo: [],
|
|
52048
50960
|
keepContent: false,
|
|
52049
50961
|
applyAll: false,
|
|
52050
|
-
|
|
50962
|
+
rename: [],
|
|
52051
50963
|
config: [],
|
|
52052
50964
|
set: [],
|
|
52053
50965
|
archiveTables: false,
|
|
@@ -52102,9 +51014,6 @@ function parseArgs(argv) {
|
|
|
52102
51014
|
case "--local":
|
|
52103
51015
|
flags.local = true;
|
|
52104
51016
|
break;
|
|
52105
|
-
case "--dev":
|
|
52106
|
-
flags.dev = true;
|
|
52107
|
-
break;
|
|
52108
51017
|
case "--all":
|
|
52109
51018
|
flags.all = true;
|
|
52110
51019
|
break;
|
|
@@ -52143,9 +51052,14 @@ function parseArgs(argv) {
|
|
|
52143
51052
|
case "--apply-all":
|
|
52144
51053
|
flags.applyAll = true;
|
|
52145
51054
|
break;
|
|
52146
|
-
case "--
|
|
52147
|
-
|
|
51055
|
+
case "--rename": {
|
|
51056
|
+
const value = argv[++i2];
|
|
51057
|
+
if (value === void 0 || value.startsWith("-")) {
|
|
51058
|
+
throw new Error("--rename requires a value: old=new (an alias to rename before v1 freezes it).");
|
|
51059
|
+
}
|
|
51060
|
+
flags.rename.push(value);
|
|
52148
51061
|
break;
|
|
51062
|
+
}
|
|
52149
51063
|
case "--config": {
|
|
52150
51064
|
const value = argv[++i2];
|
|
52151
51065
|
if (value === void 0 || value.startsWith("-")) {
|
|
@@ -52171,10 +51085,10 @@ function parseArgs(argv) {
|
|
|
52171
51085
|
case "--version":
|
|
52172
51086
|
case "-v": {
|
|
52173
51087
|
const next = argv[i2 + 1];
|
|
52174
|
-
if (command === "package") {
|
|
51088
|
+
if (command === "package" || command === "install" || command === "upgrade") {
|
|
52175
51089
|
if (next === void 0 || next.startsWith("-")) {
|
|
52176
51090
|
throw new Error(
|
|
52177
|
-
"--version requires a version number for package commands (e.g. --version 2)."
|
|
51091
|
+
"--version requires a version number for install/upgrade/package commands (e.g. --version 2)."
|
|
52178
51092
|
);
|
|
52179
51093
|
}
|
|
52180
51094
|
flags.packageVersion = next;
|
|
@@ -52236,6 +51150,34 @@ async function ingestJsonArgs(opts) {
|
|
|
52236
51150
|
// src/xlsx.ts
|
|
52237
51151
|
import fs7 from "node:fs";
|
|
52238
51152
|
|
|
51153
|
+
// src/file_command_io.ts
|
|
51154
|
+
import fs6 from "node:fs";
|
|
51155
|
+
import path7 from "node:path";
|
|
51156
|
+
var CliError = class extends Error {
|
|
51157
|
+
constructor(message) {
|
|
51158
|
+
super(message);
|
|
51159
|
+
this.name = "CliError";
|
|
51160
|
+
}
|
|
51161
|
+
};
|
|
51162
|
+
function fail(message) {
|
|
51163
|
+
throw new CliError(message);
|
|
51164
|
+
}
|
|
51165
|
+
function writeFileAtomic(filePath, bytes) {
|
|
51166
|
+
const dir = path7.dirname(path7.resolve(filePath));
|
|
51167
|
+
const tmp = path7.join(dir, `.${path7.basename(filePath)}.${process.pid}.${Date.now()}.tmp`);
|
|
51168
|
+
fs6.writeFileSync(tmp, bytes);
|
|
51169
|
+
try {
|
|
51170
|
+
fs6.renameSync(tmp, filePath);
|
|
51171
|
+
} catch (error51) {
|
|
51172
|
+
try {
|
|
51173
|
+
fs6.unlinkSync(tmp);
|
|
51174
|
+
} catch {
|
|
51175
|
+
}
|
|
51176
|
+
throw error51;
|
|
51177
|
+
}
|
|
51178
|
+
console.error(`Wrote ${filePath}`);
|
|
51179
|
+
}
|
|
51180
|
+
|
|
52239
51181
|
// ../xlsx/node_modules/fast-xml-parser/src/util.js
|
|
52240
51182
|
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";
|
|
52241
51183
|
var nameChar = nameStartChar + "\\-.\\d\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
|
|
@@ -68566,7 +67508,7 @@ async function runDocxCommand(subcommand, toolArgs, restArgs) {
|
|
|
68566
67508
|
import { spawn as spawn3 } from "node:child_process";
|
|
68567
67509
|
import { createServer } from "node:http";
|
|
68568
67510
|
import { readFileSync as readFileSync2, writeFileSync, existsSync, mkdtempSync, rmSync, readdirSync } from "node:fs";
|
|
68569
|
-
import { tmpdir as
|
|
67511
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
68570
67512
|
import { join, dirname, resolve, extname, basename } from "node:path";
|
|
68571
67513
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
68572
67514
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
@@ -68659,7 +67601,7 @@ async function runPreviewCommand(filePath, flags) {
|
|
|
68659
67601
|
});
|
|
68660
67602
|
await new Promise((r) => server.listen(0, "127.0.0.1", () => r()));
|
|
68661
67603
|
const httpPort = server.address().port;
|
|
68662
|
-
const udd = mkdtempSync(join(
|
|
67604
|
+
const udd = mkdtempSync(join(tmpdir2(), "lotics-render-"));
|
|
68663
67605
|
const child = spawn3(chrome, [
|
|
68664
67606
|
"--headless=new",
|
|
68665
67607
|
"--disable-gpu",
|
|
@@ -68808,10 +67750,22 @@ COMMANDS
|
|
|
68808
67750
|
cat args.json | lotics run <tool> Read JSON args from stdin (large payloads)
|
|
68809
67751
|
lotics app create <name> [path] Create a new custom-code app + scaffold locally
|
|
68810
67752
|
lotics app pull <app_id> [path] Bootstrap full local env (source + npm install + types)
|
|
68811
|
-
lotics app deploy -m <message> Build + upload current dir as a new version
|
|
68812
|
-
(-m is required \u2014 it's the version's audit trail
|
|
68813
|
-
|
|
67753
|
+
lotics app deploy -m <message> Build + upload current dir as a new version \u2014 COMMIT
|
|
67754
|
+
(-m is required \u2014 it's the version's audit trail;
|
|
67755
|
+
carries code + queries only \u2014 workflow bindings are
|
|
68814
67756
|
managed by set_app_workflow / remove_app_workflow)
|
|
67757
|
+
lotics app publish [app_id|.] [--rename old=new ...] [-m <changelog>] [--yes]
|
|
67758
|
+
Make the app DISTRIBUTABLE \u2014 first-release it (v1) as a
|
|
67759
|
+
package; later versions ship via "lotics app release".
|
|
67760
|
+
Previews the auto-minted aliases + findings; --yes applies
|
|
67761
|
+
(extract \u2192 create \u2192 publish v1 \u2192 pin origin). --rename fixes
|
|
67762
|
+
an alias before v1 freezes; -m is an optional v1 changelog
|
|
67763
|
+
lotics app release [app_id|.] -m <changelog> [--yes]
|
|
67764
|
+
RELEASE the next package version from the origin app you
|
|
67765
|
+
are happy with (preview; --yes publishes + re-pins)
|
|
67766
|
+
lotics app unpublish <app_id|package_id> [--undo]
|
|
67767
|
+
Take the published package off the shelf (installations
|
|
67768
|
+
keep working); --undo restores it
|
|
68815
67769
|
lotics app versions [app_id] Show deploy history newest-first (version,
|
|
68816
67770
|
timestamp, deployer, build status, -m message;
|
|
68817
67771
|
* marks the currently served version)
|
|
@@ -68833,57 +67787,42 @@ COMMANDS
|
|
|
68833
67787
|
lotics app subdomain <new-subdomain> Rename the app's public <slug>.lotics.app address
|
|
68834
67788
|
lotics app rename "<new name>" Rename the app's display name (launcher title)
|
|
68835
67789
|
lotics app dev [path] Run the app locally with HMR (RPC forwarded to prod)
|
|
68836
|
-
|
|
68837
|
-
|
|
68838
|
-
|
|
68839
|
-
lotics package build [path] Build the publishable bundle (source + dist)
|
|
68840
|
-
lotics package publish [path] Publish a new immutable package version
|
|
68841
|
-
lotics package dev [path] Sync the package into a dev workspace + run the app
|
|
68842
|
-
dev server (--workspace <dev_ws> selects it)
|
|
68843
|
-
lotics package sync [path] Re-sync (additive migrate + materialize) into a dev ws
|
|
68844
|
-
lotics package reset [path] DEV-ONLY: drop scaffolded tables + re-scaffold clean
|
|
68845
|
-
lotics package install <package> [--version N] [--bind-to <alias>=<kdc_id> ...] [--config key=value ...]
|
|
67790
|
+
# PACKAGES \u2014 consumer verbs are top-level; low-traffic ops live on "lotics package"
|
|
67791
|
+
# (author verbs live on "lotics app")
|
|
67792
|
+
lotics install <package_id> [--version N] [--bind-to <alias>=<kdc_id> ...] [--config key=value ...]
|
|
68846
67793
|
Install a package (app: scaffolds/deploys/materializes,
|
|
68847
67794
|
--config sets its knobs; content: installs the doc corpus,
|
|
68848
67795
|
--bind-to consents to adopt a same-named doc on a collision)
|
|
68849
|
-
lotics
|
|
67796
|
+
lotics uninstall <app_id|package_id> [--archive-tables] [--keep-content]
|
|
68850
67797
|
Uninstall \u2014 dispatched by id. App (app_id): archives
|
|
68851
67798
|
artifacts (+ --archive-tables also archives scaffolded
|
|
68852
|
-
tables). Content (
|
|
68853
|
-
unless --keep-content
|
|
68854
|
-
lotics
|
|
68855
|
-
|
|
68856
|
-
|
|
68857
|
-
|
|
68858
|
-
|
|
68859
|
-
|
|
68860
|
-
|
|
68861
|
-
|
|
68862
|
-
lotics package upgrade <app_id|pci_id> [--version N] [--resolve <key>=... ] [--bind-to ...] [--apply-all]
|
|
68863
|
-
Preview-then-apply an upgrade. App: refuses while any
|
|
68864
|
-
drift/modified/bundled-knowledge finding lacks a --resolve
|
|
68865
|
-
(a knowledge-alias --resolve routes to the doc). Content
|
|
68866
|
-
(pci_): --resolve <alias>=apply|keep|archive|recreate|unbind;
|
|
68867
|
-
--apply-all accepts the package's version for every doc
|
|
67799
|
+
tables). Content package (apg_): archives its package-bound
|
|
67800
|
+
docs unless --keep-content
|
|
67801
|
+
lotics upgrade <app_id|package_id> [--version N] [--resolve <key>=... ] [--bind-to ...] [--apply-all]
|
|
67802
|
+
Preview-then-apply an upgrade, dispatched by id. App
|
|
67803
|
+
(app_id): refuses while any drift/modified/bundled-knowledge
|
|
67804
|
+
finding lacks a --resolve. Package (apg_): a CONTENT package
|
|
67805
|
+
upgrades THIS workspace's install (--resolve
|
|
67806
|
+
<alias>=apply|keep|archive|recreate|unbind); an APP package
|
|
67807
|
+
FLEET-upgrades every installation across your org (clean
|
|
67808
|
+
apply, findings skip). --apply-all accepts the package's version
|
|
68868
67809
|
lotics package doctor [app_id] Installation health: version pin vs latest, binding
|
|
68869
67810
|
drift, locally modified core, knowledge drift/edits
|
|
68870
67811
|
(exit 1 on findings)
|
|
68871
|
-
lotics package
|
|
68872
|
-
|
|
68873
|
-
(re-materializes at the pinned version)
|
|
67812
|
+
lotics package config <app_id> [--set key=value ...]
|
|
67813
|
+
Show or edit an installation's config knobs
|
|
68874
67814
|
lotics package eject <app_id> Sever an installation's package link
|
|
68875
67815
|
(re-deploys the pinned source as a bespoke app)
|
|
68876
|
-
lotics package
|
|
68877
|
-
|
|
68878
|
-
|
|
68879
|
-
|
|
68880
|
-
origin app (reads .lotics/adopt_binding.json;
|
|
68881
|
-
--version N pins a specific version)
|
|
68882
|
-
lotics package fleet-upgrade <package_id> [--version N]
|
|
67816
|
+
lotics package show <package_id> Registry metadata + version history
|
|
67817
|
+
lotics package list-content List the workspace's content installations
|
|
67818
|
+
(standalone content installs) \u2014 each row leads with
|
|
67819
|
+
the package id for upgrade/uninstall
|
|
68883
67820
|
lotics package yank <package_id> <version> [--undo]
|
|
68884
|
-
|
|
68885
|
-
|
|
68886
|
-
|
|
67821
|
+
Refuse new installs/upgrades of a broken published
|
|
67822
|
+
version (pinned installations keep running)
|
|
67823
|
+
lotics run publish_content '{"name":"\u2026","knowledge_doc_ids":[\u2026],"template_ids":[\u2026]}'
|
|
67824
|
+
Publish a SET of live docs + templates as a content
|
|
67825
|
+
package (v1 + origin pin); release_content cuts the next
|
|
68887
67826
|
lotics ui link <component> [--ui-src <path>] [--remove]
|
|
68888
67827
|
Dev-link @lotics/ui to packages/ui/src (Vite alias
|
|
68889
67828
|
+ tsc paths) for live HMR + typecheck. Monorepo apps
|
|
@@ -68912,13 +67851,12 @@ FLAGS
|
|
|
68912
67851
|
is_current_member / row-scoping resolve to them (also
|
|
68913
67852
|
LOTICS_VIEW_AS env; admin key only; writes stay yours)
|
|
68914
67853
|
--local Pin the current directory (lotics org use / auth api-key)
|
|
68915
|
-
--
|
|
68916
|
-
|
|
68917
|
-
--
|
|
68918
|
-
--bind-to <a>=<id> (lotics package install/upgrade, repeatable) Adopt an existing
|
|
67854
|
+
--rename <old>=<new> (lotics app publish, repeatable) Fix an auto-minted alias before
|
|
67855
|
+
the package's v1 contract freezes it
|
|
67856
|
+
--bind-to <a>=<id> (lotics install/upgrade, repeatable) Adopt an existing
|
|
68919
67857
|
same-named doc as package-managed (resolves a knowledge collision)
|
|
68920
|
-
--keep-content (lotics
|
|
68921
|
-
--apply-all (lotics
|
|
67858
|
+
--keep-content (lotics uninstall) Keep the docs instead of archiving them
|
|
67859
|
+
--apply-all (lotics upgrade, knowledge) Accept the package's version
|
|
68922
67860
|
for every doc (overwrites local edits)
|
|
68923
67861
|
--all (lotics auth logout) Remove every saved credential
|
|
68924
67862
|
--version Show version
|
|
@@ -69292,27 +68230,6 @@ async function main() {
|
|
|
69292
68230
|
console.error("Usage: lotics ui link <component> [--ui-src <abs path>] [--remove]");
|
|
69293
68231
|
process.exit(1);
|
|
69294
68232
|
}
|
|
69295
|
-
if (command === "package" && subcommand === "new") {
|
|
69296
|
-
const name = toolArgs;
|
|
69297
|
-
if (!name) {
|
|
69298
|
-
console.error("Usage: lotics package new <name> [path] [--kind app|content]");
|
|
69299
|
-
process.exit(1);
|
|
69300
|
-
}
|
|
69301
|
-
if (flags.kind !== void 0 && flags.kind !== "app" && flags.kind !== "content") {
|
|
69302
|
-
console.error(`Invalid --kind "${flags.kind}" \u2014 expected "app" or "content".`);
|
|
69303
|
-
process.exit(1);
|
|
69304
|
-
}
|
|
69305
|
-
await packageNew({
|
|
69306
|
-
name,
|
|
69307
|
-
targetPath: restArgs[0],
|
|
69308
|
-
kind: flags.kind === "content" ? "content" : "app"
|
|
69309
|
-
});
|
|
69310
|
-
return;
|
|
69311
|
-
}
|
|
69312
|
-
if (command === "package" && subcommand === "build") {
|
|
69313
|
-
await packageBuild({ projectDir: toolArgs });
|
|
69314
|
-
return;
|
|
69315
|
-
}
|
|
69316
68233
|
if (command === "app" && subcommand === "workflow" && toolArgs === "check") {
|
|
69317
68234
|
await appWorkflowCheck({ alias: restArgs[0] });
|
|
69318
68235
|
return;
|
|
@@ -69387,7 +68304,7 @@ async function main() {
|
|
|
69387
68304
|
}
|
|
69388
68305
|
return;
|
|
69389
68306
|
}
|
|
69390
|
-
if (command !== "tools" && command !== "upload" && command !== "run" && command !== "download" && command !== "workspace" && command !== "app" && command !== "package") {
|
|
68307
|
+
if (command !== "tools" && command !== "upload" && command !== "run" && command !== "download" && command !== "workspace" && command !== "app" && command !== "package" && command !== "install" && command !== "uninstall" && command !== "upgrade") {
|
|
69391
68308
|
console.error(`Unknown command: ${command}`);
|
|
69392
68309
|
console.error('Run "lotics --help" for usage.');
|
|
69393
68310
|
process.exit(1);
|
|
@@ -69407,30 +68324,27 @@ async function main() {
|
|
|
69407
68324
|
console.error(" lotics app subdomain <new-subdomain> Rename the app's public <slug>.lotics.app address");
|
|
69408
68325
|
console.error(` lotics app rename "<new name>" Rename the app's display name (launcher title)`);
|
|
69409
68326
|
console.error(" lotics app dev [path] Run the app locally with HMR + RPC forwarding");
|
|
68327
|
+
console.error(" lotics app publish [app_id|.] [--rename old=new ...] [-m <changelog>] [--yes] First-release the app as a package (v1; preview, --yes applies)");
|
|
68328
|
+
console.error(" lotics app release [app_id|.] -m <changelog> [--yes] Release the next package version from the origin");
|
|
68329
|
+
console.error(" lotics app unpublish <app_id|package_id> [--undo] Take a published package off the shelf (any kind \u2014 app or content)");
|
|
69410
68330
|
process.exit(1);
|
|
69411
68331
|
}
|
|
69412
68332
|
if (command === "package" && !subcommand) {
|
|
69413
|
-
console.error("Usage:");
|
|
69414
|
-
console.error("
|
|
69415
|
-
console.error("
|
|
69416
|
-
console.error("
|
|
69417
|
-
console.error("
|
|
69418
|
-
console.error(" lotics package sync [path] [--workspace <ws>] Re-sync (additive migrate + materialize) into a dev workspace");
|
|
69419
|
-
console.error(" lotics package reset [path] [--workspace <ws>] DEV-ONLY: drop scaffolded tables + re-scaffold clean");
|
|
69420
|
-
console.error(" lotics package install <package> [--version N] [--bind-to <alias>=<kdc_id>] [--config key=value ...] Install a package (app or content)");
|
|
69421
|
-
console.error(" lotics package uninstall <app_id|pci_id> [--archive-tables] [--keep-content] Uninstall \u2014 dispatched by id (app vs content)");
|
|
69422
|
-
console.error(" lotics package config <app_id> [--set key=value ...] Show or edit an installation's config knobs");
|
|
69423
|
-
console.error(" lotics package show <package_id> Registry metadata + version history (channel, yank, changelog)");
|
|
69424
|
-
console.error(" lotics package retire <package_id> [--undo] Retire a package (refuse new installs; installs keep working)");
|
|
69425
|
-
console.error(" lotics package list-content List the workspace's content installations (standalone + app-bundled)");
|
|
69426
|
-
console.error(" lotics package upgrade <app_id|pci_id> [--version N] [--resolve <key>=... ] [--bind-to ...] [--apply-all] Preview + apply an upgrade");
|
|
68333
|
+
console.error("Usage (low-traffic consumer / registry ops \u2014 author verbs live on `lotics app`):");
|
|
68334
|
+
console.error(" The high-traffic consumer verbs are top-level:");
|
|
68335
|
+
console.error(" lotics install <package_id> [--version N] [--bind-to <alias>=<kdc_id>] [--config key=value ...] Install a package (app or content)");
|
|
68336
|
+
console.error(" lotics uninstall <app_id|package_id> [--archive-tables] [--keep-content] Uninstall \u2014 dispatched by id (app vs content)");
|
|
68337
|
+
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_)");
|
|
69427
68338
|
console.error(" lotics package doctor [app_id] Health: version pin vs latest + binding/knowledge drift");
|
|
69428
|
-
console.error(" lotics package
|
|
68339
|
+
console.error(" lotics package config <app_id> [--set key=value ...] Show or edit an installation's config knobs");
|
|
69429
68340
|
console.error(" lotics package eject <app_id> Sever an installation's package link");
|
|
69430
|
-
console.error(" lotics package
|
|
69431
|
-
console.error(" lotics package
|
|
69432
|
-
console.error(" lotics package fleet-upgrade <package_id> [--version N] Upgrade every org installation (clean ones apply; findings skip)");
|
|
68341
|
+
console.error(" lotics package show <package_id> Registry metadata + version history (channel, yank, changelog)");
|
|
68342
|
+
console.error(" lotics package list-content List the workspace's content installations (standalone content installs)");
|
|
69433
68343
|
console.error(" lotics package yank <package_id> <version> [--undo] Refuse new installs/upgrades of a broken published version (pinned installations keep running)");
|
|
68344
|
+
console.error(" A content package (a SET of live docs + templates) \u2014 publish + release with the tools, retire on `lotics app`:");
|
|
68345
|
+
console.error(` lotics run publish_content '{"name":"\u2026","knowledge_doc_ids":[\u2026],"template_ids":[\u2026]}' Publish v1 (a fixed set)`);
|
|
68346
|
+
console.error(` lotics run release_content '{"package_id":"apg_\u2026","changelog":"\u2026"}' Cut the next version (add "dry_run":true to preview)`);
|
|
68347
|
+
console.error(" lotics app unpublish <package_id> Take the whole package off the shelf (kind-agnostic \u2014 retires content too)");
|
|
69434
68348
|
process.exit(1);
|
|
69435
68349
|
}
|
|
69436
68350
|
if (command === "run" && !subcommand) {
|
|
@@ -69506,11 +68420,11 @@ Available workspaces:`);
|
|
|
69506
68420
|
if (subcommand === "create") {
|
|
69507
68421
|
const name = toolArgs;
|
|
69508
68422
|
if (!name) {
|
|
69509
|
-
console.error("Usage: lotics workspace create <name> [--timezone <tz>]
|
|
68423
|
+
console.error("Usage: lotics workspace create <name> [--timezone <tz>]");
|
|
69510
68424
|
process.exit(1);
|
|
69511
68425
|
}
|
|
69512
68426
|
const timezone = flags.timezone;
|
|
69513
|
-
const created = await client.createWorkspace({ name, timezone
|
|
68427
|
+
const created = await client.createWorkspace({ name, timezone });
|
|
69514
68428
|
setSelectedWorkspace(created.id);
|
|
69515
68429
|
client.setWorkspaceId(created.id);
|
|
69516
68430
|
if (flags.json) {
|
|
@@ -69572,48 +68486,92 @@ Available workspaces:`);
|
|
|
69572
68486
|
return;
|
|
69573
68487
|
}
|
|
69574
68488
|
await resolveWorkspace(client, ctx);
|
|
69575
|
-
if (command === "
|
|
69576
|
-
|
|
69577
|
-
|
|
69578
|
-
|
|
69579
|
-
|
|
69580
|
-
|
|
69581
|
-
|
|
68489
|
+
if (command === "install") {
|
|
68490
|
+
const packageId = subcommand;
|
|
68491
|
+
if (!packageId) {
|
|
68492
|
+
console.error("Usage: lotics install <package_id> [--version N] [--bind-to <alias>=<kdc_id> ...] [--config key=value ...]");
|
|
68493
|
+
process.exit(1);
|
|
68494
|
+
}
|
|
68495
|
+
let version2;
|
|
68496
|
+
if (flags.packageVersion !== void 0) {
|
|
68497
|
+
version2 = Number(flags.packageVersion);
|
|
68498
|
+
if (!Number.isInteger(version2) || version2 <= 0) {
|
|
68499
|
+
console.error(`Invalid --version "${flags.packageVersion}" \u2014 expected a positive integer.`);
|
|
69582
68500
|
process.exit(1);
|
|
69583
68501
|
}
|
|
69584
|
-
|
|
69585
|
-
|
|
69586
|
-
|
|
69587
|
-
|
|
69588
|
-
|
|
69589
|
-
|
|
69590
|
-
|
|
69591
|
-
|
|
69592
|
-
|
|
69593
|
-
|
|
69594
|
-
|
|
69595
|
-
|
|
69596
|
-
|
|
69597
|
-
|
|
69598
|
-
|
|
68502
|
+
}
|
|
68503
|
+
const config2 = flags.config.length > 0 ? parseInstallConfigFlags(flags.config) : void 0;
|
|
68504
|
+
await packageInstall(client, {
|
|
68505
|
+
package_id: packageId,
|
|
68506
|
+
version: version2,
|
|
68507
|
+
bind_to: parseBindToFlags(flags.bindTo),
|
|
68508
|
+
...config2 !== void 0 ? { config: config2 } : {}
|
|
68509
|
+
});
|
|
68510
|
+
return;
|
|
68511
|
+
}
|
|
68512
|
+
if (command === "uninstall") {
|
|
68513
|
+
const id = subcommand;
|
|
68514
|
+
if (!id) {
|
|
68515
|
+
console.error("Usage: lotics uninstall <app_id|package_id> [--archive-tables] [--keep-content]");
|
|
68516
|
+
console.error(
|
|
68517
|
+
"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)."
|
|
68518
|
+
);
|
|
68519
|
+
process.exit(1);
|
|
68520
|
+
}
|
|
68521
|
+
if (id.startsWith("pci_")) {
|
|
68522
|
+
await redirectContentPciForm(client, id, "uninstall");
|
|
69599
68523
|
return;
|
|
69600
68524
|
}
|
|
69601
|
-
|
|
69602
|
-
|
|
69603
|
-
|
|
69604
|
-
|
|
69605
|
-
|
|
69606
|
-
|
|
69607
|
-
|
|
68525
|
+
await packageUninstall(client, {
|
|
68526
|
+
id,
|
|
68527
|
+
keep_content: flags.keepContent,
|
|
68528
|
+
archive_tables: flags.archiveTables
|
|
68529
|
+
});
|
|
68530
|
+
return;
|
|
68531
|
+
}
|
|
68532
|
+
if (command === "upgrade") {
|
|
68533
|
+
const target = subcommand;
|
|
68534
|
+
if (!target) {
|
|
68535
|
+
console.error(
|
|
68536
|
+
"Usage: lotics upgrade <app_id | package_id> [--version N] [--resolve <key>=... ...] [--bind-to ...] [--apply-all]"
|
|
68537
|
+
);
|
|
68538
|
+
console.error(
|
|
68539
|
+
"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."
|
|
68540
|
+
);
|
|
68541
|
+
process.exit(1);
|
|
68542
|
+
}
|
|
68543
|
+
let version2;
|
|
68544
|
+
if (flags.packageVersion !== void 0) {
|
|
68545
|
+
version2 = Number(flags.packageVersion);
|
|
68546
|
+
if (!Number.isInteger(version2) || version2 <= 0) {
|
|
68547
|
+
console.error(`Invalid --version "${flags.packageVersion}" \u2014 expected a positive integer.`);
|
|
69608
68548
|
process.exit(1);
|
|
69609
68549
|
}
|
|
69610
|
-
|
|
69611
|
-
|
|
69612
|
-
|
|
69613
|
-
|
|
68550
|
+
}
|
|
68551
|
+
if (target.startsWith("pci_")) {
|
|
68552
|
+
await redirectContentPciForm(client, target, "upgrade");
|
|
68553
|
+
return;
|
|
68554
|
+
}
|
|
68555
|
+
if (target.startsWith("apg_")) {
|
|
68556
|
+
await packageUpgradeByPackageId(client, {
|
|
68557
|
+
package_id: target,
|
|
68558
|
+
version: version2,
|
|
68559
|
+
resolve: flags.resolve,
|
|
68560
|
+
bindTo: flags.bindTo,
|
|
68561
|
+
applyAll: flags.applyAll
|
|
69614
68562
|
});
|
|
69615
68563
|
return;
|
|
69616
68564
|
}
|
|
68565
|
+
await packageUpgrade(client, {
|
|
68566
|
+
app_id: target,
|
|
68567
|
+
version: version2,
|
|
68568
|
+
resolve: flags.resolve,
|
|
68569
|
+
bindTo: flags.bindTo,
|
|
68570
|
+
applyAll: flags.applyAll
|
|
68571
|
+
});
|
|
68572
|
+
return;
|
|
68573
|
+
}
|
|
68574
|
+
if (command === "package") {
|
|
69617
68575
|
if (subcommand === "list-content") {
|
|
69618
68576
|
await packageListContent(client);
|
|
69619
68577
|
return;
|
|
@@ -69627,15 +68585,6 @@ Available workspaces:`);
|
|
|
69627
68585
|
await packageConfig(client, { app_id: appId, sets: flags.set });
|
|
69628
68586
|
return;
|
|
69629
68587
|
}
|
|
69630
|
-
if (subcommand === "retire") {
|
|
69631
|
-
const packageId = toolArgs;
|
|
69632
|
-
if (!packageId) {
|
|
69633
|
-
console.error("Usage: lotics package retire <package_id> [--undo]");
|
|
69634
|
-
process.exit(1);
|
|
69635
|
-
}
|
|
69636
|
-
await packageRetire(client, { package_id: packageId, undo: flags.undo });
|
|
69637
|
-
return;
|
|
69638
|
-
}
|
|
69639
68588
|
if (subcommand === "show") {
|
|
69640
68589
|
const packageId = toolArgs;
|
|
69641
68590
|
if (!packageId) {
|
|
@@ -69654,34 +68603,6 @@ Available workspaces:`);
|
|
|
69654
68603
|
await packageEject(client, { app_id: appId });
|
|
69655
68604
|
return;
|
|
69656
68605
|
}
|
|
69657
|
-
if (subcommand === "extract") {
|
|
69658
|
-
const appId = toolArgs;
|
|
69659
|
-
if (!appId) {
|
|
69660
|
-
console.error("Usage: lotics package extract <app_id> [path]");
|
|
69661
|
-
console.error("Promotes a bespoke app to a draft package project (contract + binding + templates).");
|
|
69662
|
-
process.exit(1);
|
|
69663
|
-
}
|
|
69664
|
-
await packageExtract(client, { app_id: appId, targetPath: restArgs[0] });
|
|
69665
|
-
return;
|
|
69666
|
-
}
|
|
69667
|
-
if (subcommand === "adopt") {
|
|
69668
|
-
const appId = toolArgs;
|
|
69669
|
-
if (!appId) {
|
|
69670
|
-
console.error("Usage: lotics package adopt <app_id> [path]");
|
|
69671
|
-
console.error("Binds the published package project (this dir, or [path]) onto the origin app.");
|
|
69672
|
-
process.exit(1);
|
|
69673
|
-
}
|
|
69674
|
-
let version2;
|
|
69675
|
-
if (flags.packageVersion !== void 0) {
|
|
69676
|
-
version2 = Number(flags.packageVersion);
|
|
69677
|
-
if (!Number.isInteger(version2) || version2 <= 0) {
|
|
69678
|
-
console.error(`Invalid --version "${flags.packageVersion}" \u2014 expected a positive integer.`);
|
|
69679
|
-
process.exit(1);
|
|
69680
|
-
}
|
|
69681
|
-
}
|
|
69682
|
-
await packageAdopt(client, { app_id: appId, version: version2, projectDir: restArgs[0] });
|
|
69683
|
-
return;
|
|
69684
|
-
}
|
|
69685
68606
|
if (subcommand === "yank") {
|
|
69686
68607
|
const packageId = toolArgs;
|
|
69687
68608
|
const version2 = Number(restArgs[0]);
|
|
@@ -69692,23 +68613,6 @@ Available workspaces:`);
|
|
|
69692
68613
|
await packageYank(client, { package_id: packageId, version: version2, undo: flags.undo });
|
|
69693
68614
|
return;
|
|
69694
68615
|
}
|
|
69695
|
-
if (subcommand === "fleet-upgrade") {
|
|
69696
|
-
const packageId = toolArgs;
|
|
69697
|
-
if (!packageId) {
|
|
69698
|
-
console.error("Usage: lotics package fleet-upgrade <package_id> [--version N]");
|
|
69699
|
-
process.exit(1);
|
|
69700
|
-
}
|
|
69701
|
-
let version2;
|
|
69702
|
-
if (flags.packageVersion !== void 0) {
|
|
69703
|
-
version2 = Number(flags.packageVersion);
|
|
69704
|
-
if (!Number.isInteger(version2) || version2 <= 0) {
|
|
69705
|
-
console.error(`Invalid --version "${flags.packageVersion}" \u2014 expected a positive integer.`);
|
|
69706
|
-
process.exit(1);
|
|
69707
|
-
}
|
|
69708
|
-
}
|
|
69709
|
-
await packageFleetUpgrade(client, { package_id: packageId, version: version2 });
|
|
69710
|
-
return;
|
|
69711
|
-
}
|
|
69712
68616
|
if (subcommand === "doctor") {
|
|
69713
68617
|
await packageDoctor(client, { app_id: toolArgs });
|
|
69714
68618
|
return;
|
|
@@ -69716,78 +68620,23 @@ Available workspaces:`);
|
|
|
69716
68620
|
if (subcommand === "rebind-role") {
|
|
69717
68621
|
const appId = toolArgs;
|
|
69718
68622
|
const [roleAlias, groupId] = restArgs;
|
|
69719
|
-
if (!appId || !roleAlias || !groupId) {
|
|
69720
|
-
console.error("Usage: lotics package rebind-role <app_id> <role_alias> <grp_id>");
|
|
69721
|
-
process.exit(1);
|
|
69722
|
-
}
|
|
69723
|
-
const result = await client.rebindPackageRole(appId, {
|
|
69724
|
-
role_alias: roleAlias,
|
|
69725
|
-
group_id: groupId
|
|
69726
|
-
});
|
|
69727
68623
|
console.error(
|
|
69728
|
-
|
|
68624
|
+
`\`lotics package rebind-role\` was folded into the upgrade grammar. Re-point a live role with:
|
|
68625
|
+
lotics upgrade ${appId ?? "<app_id>"} --resolve roles.${roleAlias ?? "<alias>"}=${groupId ?? "<grp_id>"}`
|
|
69729
68626
|
);
|
|
69730
|
-
|
|
69731
|
-
}
|
|
69732
|
-
if (subcommand === "upgrade") {
|
|
69733
|
-
const target = toolArgs;
|
|
69734
|
-
if (!target) {
|
|
69735
|
-
console.error(
|
|
69736
|
-
"Usage: lotics package upgrade <app_id | pci_installation_id> [--version N] [--resolve <key>=... ...]"
|
|
69737
|
-
);
|
|
69738
|
-
process.exit(1);
|
|
69739
|
-
}
|
|
69740
|
-
let version2;
|
|
69741
|
-
if (flags.packageVersion !== void 0) {
|
|
69742
|
-
version2 = Number(flags.packageVersion);
|
|
69743
|
-
if (!Number.isInteger(version2) || version2 <= 0) {
|
|
69744
|
-
console.error(`Invalid --version "${flags.packageVersion}" \u2014 expected a positive integer.`);
|
|
69745
|
-
process.exit(1);
|
|
69746
|
-
}
|
|
69747
|
-
}
|
|
69748
|
-
if (target.startsWith("pci_")) {
|
|
69749
|
-
await packageUpgradeKnowledge(client, {
|
|
69750
|
-
installation_id: target,
|
|
69751
|
-
version: version2,
|
|
69752
|
-
resolve: flags.resolve,
|
|
69753
|
-
bind_to: parseBindToFlags(flags.bindTo),
|
|
69754
|
-
applyAll: flags.applyAll
|
|
69755
|
-
});
|
|
69756
|
-
return;
|
|
69757
|
-
}
|
|
69758
|
-
await packageUpgrade(client, {
|
|
69759
|
-
app_id: target,
|
|
69760
|
-
version: version2,
|
|
69761
|
-
resolve: flags.resolve,
|
|
69762
|
-
bindTo: flags.bindTo,
|
|
69763
|
-
applyAll: flags.applyAll
|
|
69764
|
-
});
|
|
69765
|
-
return;
|
|
69766
|
-
}
|
|
69767
|
-
if (subcommand === "publish") {
|
|
69768
|
-
await packagePublish(client, { projectDir: toolArgs, changelog: flags.message });
|
|
69769
|
-
return;
|
|
69770
|
-
}
|
|
69771
|
-
if (subcommand === "sync") {
|
|
69772
|
-
await packageSync(client, { projectDir: toolArgs });
|
|
69773
|
-
return;
|
|
69774
|
-
}
|
|
69775
|
-
if (subcommand === "reset") {
|
|
69776
|
-
await packageReset(client, { projectDir: toolArgs });
|
|
69777
|
-
return;
|
|
68627
|
+
process.exit(1);
|
|
69778
68628
|
}
|
|
69779
|
-
|
|
69780
|
-
|
|
69781
|
-
|
|
69782
|
-
|
|
69783
|
-
|
|
69784
|
-
|
|
69785
|
-
|
|
69786
|
-
|
|
69787
|
-
|
|
69788
|
-
|
|
69789
|
-
|
|
69790
|
-
return;
|
|
68629
|
+
const movedVerbs = {
|
|
68630
|
+
install: "lotics install <package_id>",
|
|
68631
|
+
uninstall: "lotics uninstall <app_id|package_id>",
|
|
68632
|
+
upgrade: "lotics upgrade <app_id|package_id>",
|
|
68633
|
+
"fleet-upgrade": "lotics upgrade <package_id>"
|
|
68634
|
+
};
|
|
68635
|
+
if (subcommand !== void 0 && subcommand in movedVerbs) {
|
|
68636
|
+
console.error(
|
|
68637
|
+
`\`lotics package ${subcommand}\` moved to the top level \u2014 run \`${movedVerbs[subcommand]}\` instead.`
|
|
68638
|
+
);
|
|
68639
|
+
process.exit(1);
|
|
69791
68640
|
}
|
|
69792
68641
|
console.error(`Unknown package subcommand: ${subcommand}`);
|
|
69793
68642
|
console.error("Run 'lotics package' for usage.");
|
|
@@ -69824,6 +68673,34 @@ Available workspaces:`);
|
|
|
69824
68673
|
await appDeploy(client, { message });
|
|
69825
68674
|
return;
|
|
69826
68675
|
}
|
|
68676
|
+
if (subcommand === "publish") {
|
|
68677
|
+
await appPublish(client, {
|
|
68678
|
+
app_id: toolArgs,
|
|
68679
|
+
renames: flags.rename,
|
|
68680
|
+
changelog: flags.message,
|
|
68681
|
+
yes: flags.yes
|
|
68682
|
+
});
|
|
68683
|
+
return;
|
|
68684
|
+
}
|
|
68685
|
+
if (subcommand === "release") {
|
|
68686
|
+
if (!flags.message) {
|
|
68687
|
+
console.error("Usage: lotics app release [app_id|.] -m <changelog> [--yes]");
|
|
68688
|
+
console.error("Snapshots the origin app into its next package version (preview, then --yes to publish).");
|
|
68689
|
+
process.exit(1);
|
|
68690
|
+
}
|
|
68691
|
+
await appRelease(client, { app_id: toolArgs, changelog: flags.message, yes: flags.yes });
|
|
68692
|
+
return;
|
|
68693
|
+
}
|
|
68694
|
+
if (subcommand === "unpublish") {
|
|
68695
|
+
const id = toolArgs;
|
|
68696
|
+
if (!id) {
|
|
68697
|
+
console.error("Usage: lotics app unpublish <app_id|package_id> [--undo]");
|
|
68698
|
+
console.error("Takes a published package off the shelf; existing installations keep working.");
|
|
68699
|
+
process.exit(1);
|
|
68700
|
+
}
|
|
68701
|
+
await appUnpublish(client, { id, undo: flags.undo });
|
|
68702
|
+
return;
|
|
68703
|
+
}
|
|
69827
68704
|
if (subcommand === "subdomain") {
|
|
69828
68705
|
const newSubdomain = toolArgs;
|
|
69829
68706
|
if (!newSubdomain) {
|
|
@@ -70039,8 +68916,6 @@ ${JSON.stringify(info.input_schema, null, 2)}`);
|
|
|
70039
68916
|
console.log(JSON.stringify({ error: result.error }, null, 2));
|
|
70040
68917
|
} else {
|
|
70041
68918
|
console.error(result.error);
|
|
70042
|
-
console.error(`
|
|
70043
|
-
Hint: run "lotics tools ${toolName}" to see the expected input schema.`);
|
|
70044
68919
|
}
|
|
70045
68920
|
process.exit(1);
|
|
70046
68921
|
}
|