@lotics/cli 0.75.0 → 0.76.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -1
- package/dist/app_commands.d.ts +16 -0
- package/dist/app_commands.js +1 -1
- package/dist/args.d.ts +22 -1
- package/dist/args.js +47 -0
- package/dist/cli.js +139 -34
- package/dist/cli_dispatch.test.d.ts +1 -0
- package/dist/cli_dispatch.test.js +90 -0
- package/dist/client.d.ts +230 -26
- package/dist/client.js +106 -28
- package/dist/package_commands.d.ts +219 -20
- package/dist/package_commands.js +996 -67
- package/dist/package_commands.test.js +354 -5
- package/dist/src/cli.js +19507 -1572
- package/dist/starter_template.d.ts +19 -0
- package/dist/starter_template.js +389 -0
- package/dist/starter_template.test.js +69 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -197,7 +197,7 @@ lotics ui link card --remove # finalize: PR + publish, then drop t
|
|
|
197
197
|
|
|
198
198
|
## App packages
|
|
199
199
|
|
|
200
|
-
A maintained, versioned app library: author once, install into many workspaces, upgrade per-workspace. Any org authors its own packages (publishing is owner-org-only); Lotics-backed packages carry the `official` badge. See `docs/
|
|
200
|
+
A maintained, versioned app library: author once, install into many workspaces, upgrade per-workspace. Any org authors its own packages (publishing is owner-org-only); Lotics-backed packages carry the `official` badge. See `docs/packages.md` for the model.
|
|
201
201
|
|
|
202
202
|
```bash
|
|
203
203
|
# Author (any org admin; the package is owned by your org)
|
|
@@ -210,13 +210,19 @@ lotics package sync --workspace wsp_dev # migrate the dev installation only
|
|
|
210
210
|
lotics package reset --workspace wsp_dev # DEV-ONLY: drop scaffolded tables, re-scaffold clean
|
|
211
211
|
|
|
212
212
|
# Operate installations (workspace admin)
|
|
213
|
+
lotics package show apg_... # registry metadata + version history (trust badge, channel, yank)
|
|
213
214
|
lotics package install apg_... --version 2 # scaffold + materialize + deploy + pin
|
|
215
|
+
lotics package install apg_... --config heading="Ops board" # per-knob overrides over contract defaults
|
|
216
|
+
lotics package config app_... --set show_done=false # edit a live installation's config (partial merge)
|
|
217
|
+
lotics package uninstall app_... [--archive-tables] # remove an installation; workflows stop firing
|
|
218
|
+
lotics package retire apg_... [--undo] # owner-org: refuse NEW installs (existing keep working + upgrading)
|
|
214
219
|
lotics package doctor app_... # version pin vs latest + drift + local edits (exit 1 on findings)
|
|
215
220
|
lotics package rebind-role app_... <alias> <grp_id> # re-point a package role at another group (re-materializes)
|
|
216
221
|
lotics package upgrade app_... # preview, then apply (additive; overlay preserved)
|
|
217
222
|
lotics package fleet-upgrade apg_... # upgrade EVERY org installation (clean ones apply; findings skip, exit 1)
|
|
218
223
|
lotics package upgrade app_... --resolve fields.deal.stage=recreate # resolve reported drift
|
|
219
224
|
lotics package upgrade app_... --resolve queries.tasks=keep # consent for a local edit (or =revert)
|
|
225
|
+
lotics package upgrade app_... --resolve template.quote=revert # a locally-edited template (clean ones auto-update; or =keep)
|
|
220
226
|
lotics package eject app_... # one-way: sever the package link
|
|
221
227
|
lotics workspace doctor # dangling schema references across the workspace (exit 1 on findings)
|
|
222
228
|
|
|
@@ -233,6 +239,35 @@ lotics package adopt app_... # bind the package onto the origin ap
|
|
|
233
239
|
|
|
234
240
|
Extract writes `.lotics/adopt_binding.json` (the origin pin, excluded from published bundles); `adopt` reads it back and refuses a pin recorded for a different app. After promotion the package project is the master — iterate contract-first (`package dev`/`sync` → `publish`), never extract again.
|
|
235
241
|
|
|
242
|
+
### Content packages
|
|
243
|
+
|
|
244
|
+
A **content** package ships **knowledge docs and/or document templates** — no app, just the artifacts. Knowledge is the reference corpus an agent looks up by name; templates are standalone document templates. Content comes two ways: bundled *with an app* (installing the app delivers its agents' docs) or as a standalone content package (a versioned corpus). Install delivers, upgrade refreshes — preserving stable `kdc_`/`tmpl_` ids + a version snapshot per change. See `docs/packages.md` § Content.
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
# Author a content package (scaffolds a knowledge/ dir + manifest; add templates/ + a
|
|
248
|
+
# lotics.package.templates entry per template — inline html/email or file-backed excel/word/pdf-form)
|
|
249
|
+
lotics package new my-sops --kind content
|
|
250
|
+
lotics package publish -m "v1: SOP corpus + templates"
|
|
251
|
+
|
|
252
|
+
# Install into a workspace (a same-named local DOC collides — consent with --bind-to; templates never collide)
|
|
253
|
+
lotics package install apg_... # app OR content, kind-branched
|
|
254
|
+
lotics package install apg_... --bind-to policy=kdc_... # adopt an existing doc as package-managed
|
|
255
|
+
|
|
256
|
+
# Discover installed content (the source for a pci_ id — install prints it once)
|
|
257
|
+
lotics package list-content # id · package · version · standalone vs app-bundled
|
|
258
|
+
|
|
259
|
+
# Refresh a standalone content install (pci_ id from install / list-content) — one gate over docs + templates
|
|
260
|
+
lotics package upgrade pci_... # preview, then apply
|
|
261
|
+
lotics package upgrade pci_... --resolve policy=apply # a doc: apply|keep|archive|recreate|unbind
|
|
262
|
+
lotics package upgrade pci_... --resolve quote=revert # a locally-edited template: revert|keep (clean ones auto-update)
|
|
263
|
+
lotics package upgrade pci_... --apply-all # accept upstream for every doc + template (OVERWRITES local edits)
|
|
264
|
+
lotics package uninstall pci_... --keep-content # remove the pin; keep docs + templates (default archives them)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
A package's knowledge can also arrive **bundled with an app**. Upgrading such an app (`lotics package upgrade <app_id>`) previews its bundled-knowledge changes alongside the plan and resolves them with the SAME flags: a `--resolve <alias>=apply|keep|archive|recreate|unbind` naming a bundled doc routes to the doc (anything else stays a core-artifact resolution; a name that is both errors), `--bind-to` consents an added-doc collision, `--apply-all` takes the package's version for every consent-requiring doc.
|
|
268
|
+
|
|
269
|
+
To package the knowledge an existing app's agents route to, declare it in the app project's `package.json#lotics.knowledge` (`[{ alias, doc_id }]`, + `lotics.knowledge_expects` for docs the agents assume but the package doesn't own), then `lotics package extract` / `adopt` as usual.
|
|
270
|
+
|
|
236
271
|
## SDK
|
|
237
272
|
|
|
238
273
|
```typescript
|
package/dist/app_commands.d.ts
CHANGED
|
@@ -117,6 +117,22 @@ export declare function runNpm(args: string[], cwd: string): Promise<void>;
|
|
|
117
117
|
* the author fixes the config.
|
|
118
118
|
*/
|
|
119
119
|
export declare function ensureAppTsconfig(projectDir: string): void;
|
|
120
|
+
/**
|
|
121
|
+
* Write the three `.lotics/app_{workflows,queries,agents}.d.ts` companions from
|
|
122
|
+
* the manifest's maps, then heal the app's tsconfig so they actually load. Called
|
|
123
|
+
* from `app create / pull / dev / deploy / codegen`, so the augmented `AppWorkflows`
|
|
124
|
+
* / `AppQueries` / `AppAgents` types stay in sync with the manifest.
|
|
125
|
+
*
|
|
126
|
+
* The heal is at the write boundary on purpose: a `.d.ts` written but not loaded is
|
|
127
|
+
* useless (a bare `.lotics` include is skipped by TypeScript's include-glob walk and
|
|
128
|
+
* loads zero of them), so `ensureAppTsconfig` couples "wrote the types" with "the
|
|
129
|
+
* program can see them" — no caller can do one without the other.
|
|
130
|
+
*/
|
|
131
|
+
export declare function writeAppDts(projectDir: string, manifest: {
|
|
132
|
+
workflows?: Record<string, AppWorkflowDeclaration>;
|
|
133
|
+
queries?: Record<string, AppQueryDeclaration>;
|
|
134
|
+
agents?: Record<string, AppAgentDeclaration>;
|
|
135
|
+
}): string[];
|
|
120
136
|
/**
|
|
121
137
|
* `lotics app codegen [path]` — regenerate every `.lotics/` artifact from the
|
|
122
138
|
* manifest + workspace schema, WITHOUT a deploy. The `.d.ts` companions are
|
package/dist/app_commands.js
CHANGED
|
@@ -386,7 +386,7 @@ export function ensureAppTsconfig(projectDir) {
|
|
|
386
386
|
* loads zero of them), so `ensureAppTsconfig` couples "wrote the types" with "the
|
|
387
387
|
* program can see them" — no caller can do one without the other.
|
|
388
388
|
*/
|
|
389
|
-
function writeAppDts(projectDir, manifest) {
|
|
389
|
+
export function writeAppDts(projectDir, manifest) {
|
|
390
390
|
const dotLotics = path.join(projectDir, ".lotics");
|
|
391
391
|
fs.mkdirSync(dotLotics, { recursive: true });
|
|
392
392
|
const written = [
|
package/dist/args.d.ts
CHANGED
|
@@ -50,9 +50,30 @@ export declare function parseArgs(argv: string[]): {
|
|
|
50
50
|
* `--resolve <key>=recreate|revert|keep|<id>` (repeatable): upgrade
|
|
51
51
|
* resolutions — drift entries (`<namespace.alias>`) take `recreate` or an
|
|
52
52
|
* existing id; modified-core entries (`<kind>.<alias>`) take `revert` or
|
|
53
|
-
* `keep`.
|
|
53
|
+
* `keep`. For a standalone KNOWLEDGE upgrade, `<alias>=apply|keep|archive|
|
|
54
|
+
* recreate|unbind`.
|
|
54
55
|
*/
|
|
55
56
|
resolve: string[];
|
|
57
|
+
/**
|
|
58
|
+
* `--bind-to <alias>=<kdc_id>` (repeatable): consent to adopt an existing
|
|
59
|
+
* same-named doc as package-managed — resolves a knowledge name collision at
|
|
60
|
+
* install / a created-doc collision at upgrade.
|
|
61
|
+
*/
|
|
62
|
+
bindTo: string[];
|
|
63
|
+
/** `--keep-content` (`package uninstall` of a content install): retain the package-bound docs instead of archiving them. */
|
|
64
|
+
keepContent: boolean;
|
|
65
|
+
/** `--apply-all` (knowledge `package upgrade`): accept the package's version for every consent entry. */
|
|
66
|
+
applyAll: boolean;
|
|
67
|
+
/** `--kind <app|content>` (`package new`): the package project kind to scaffold (default app). */
|
|
68
|
+
kind?: string;
|
|
69
|
+
/** `--config key=value` (repeatable): per-knob config overrides at `package install`. */
|
|
70
|
+
config: string[];
|
|
71
|
+
/** `--set key=value` (repeatable): per-knob config edits at `package config`. */
|
|
72
|
+
set: string[];
|
|
73
|
+
/** `--archive-tables` (`package uninstall` of an app install): also archive the scaffolded tables. */
|
|
74
|
+
archiveTables: boolean;
|
|
75
|
+
/** `--undo`: reverse a `package retire` / `package yank`. */
|
|
76
|
+
undo: boolean;
|
|
56
77
|
version: boolean;
|
|
57
78
|
help: boolean;
|
|
58
79
|
};
|
package/dist/args.js
CHANGED
|
@@ -31,6 +31,14 @@ export function parseArgs(argv) {
|
|
|
31
31
|
cleanup: false,
|
|
32
32
|
packageVersion: undefined,
|
|
33
33
|
resolve: [],
|
|
34
|
+
bindTo: [],
|
|
35
|
+
keepContent: false,
|
|
36
|
+
applyAll: false,
|
|
37
|
+
kind: undefined,
|
|
38
|
+
config: [],
|
|
39
|
+
set: [],
|
|
40
|
+
archiveTables: false,
|
|
41
|
+
undo: false,
|
|
34
42
|
version: false,
|
|
35
43
|
help: false,
|
|
36
44
|
};
|
|
@@ -106,6 +114,45 @@ export function parseArgs(argv) {
|
|
|
106
114
|
flags.resolve.push(value);
|
|
107
115
|
break;
|
|
108
116
|
}
|
|
117
|
+
case "--bind-to": {
|
|
118
|
+
const value = argv[++i];
|
|
119
|
+
if (value === undefined || value.startsWith("-")) {
|
|
120
|
+
throw new Error("--bind-to requires a value: <alias>=<kdc_id>.");
|
|
121
|
+
}
|
|
122
|
+
flags.bindTo.push(value);
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
case "--keep-content":
|
|
126
|
+
flags.keepContent = true;
|
|
127
|
+
break;
|
|
128
|
+
case "--apply-all":
|
|
129
|
+
flags.applyAll = true;
|
|
130
|
+
break;
|
|
131
|
+
case "--kind":
|
|
132
|
+
flags.kind = argv[++i];
|
|
133
|
+
break;
|
|
134
|
+
case "--config": {
|
|
135
|
+
const value = argv[++i];
|
|
136
|
+
if (value === undefined || value.startsWith("-")) {
|
|
137
|
+
throw new Error("--config requires a value: key=value (repeatable).");
|
|
138
|
+
}
|
|
139
|
+
flags.config.push(value);
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
case "--set": {
|
|
143
|
+
const value = argv[++i];
|
|
144
|
+
if (value === undefined || value.startsWith("-")) {
|
|
145
|
+
throw new Error("--set requires a value: key=value (repeatable).");
|
|
146
|
+
}
|
|
147
|
+
flags.set.push(value);
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
case "--archive-tables":
|
|
151
|
+
flags.archiveTables = true;
|
|
152
|
+
break;
|
|
153
|
+
case "--undo":
|
|
154
|
+
flags.undo = true;
|
|
155
|
+
break;
|
|
109
156
|
case "--version":
|
|
110
157
|
case "-v": {
|
|
111
158
|
// Inside a `package` command, `--version` carries a value (the
|
package/dist/cli.js
CHANGED
|
@@ -14,7 +14,7 @@ import { LoticsClient, API_BASE_URL } from "./client.js";
|
|
|
14
14
|
import { resolveContext, deleteConfig, getConfigPath, loadGlobalConfig, saveGlobalConfig, loadLocalConfig, upsertProfile, removeProfile, setActiveOrg, setSelectedWorkspace, resolveProfileByNameOrId, checkForUpdate, } from "./config.js";
|
|
15
15
|
import { VERSION } from "./version.js";
|
|
16
16
|
import { appCreate, appPull, appDeploy, appDev, appSetSubdomain, appRename, appVersions, appCodegen, appExecuteWorkflow, appWorkflowSet, appWorkflowPull, appWorkflowCheck, appQuerySet, appUiLink, } from "./app_commands.js";
|
|
17
|
-
import { packageInstall, packageEject, packageDoctor, packageUpgrade,
|
|
17
|
+
import { packageInstall, packageUninstall, packageListContent, packageConfig, packageRetire, packageShow, parseInstallConfigFlags, packageEject, packageDoctor, packageUpgrade, packageUpgradeKnowledge, parseBindToFlags, packageNew, packageBuild, packagePublish, packageDev, packageSync, packageReset, packageExtract, packageAdopt, packageFleetUpgrade, packageYank, } from "./package_commands.js";
|
|
18
18
|
import { parseArgs } from "./args.js";
|
|
19
19
|
import { ingestJsonArgs } from "./inputs.js";
|
|
20
20
|
import { runXlsxCommand } from "./xlsx.js";
|
|
@@ -96,22 +96,41 @@ COMMANDS
|
|
|
96
96
|
lotics app subdomain <new-subdomain> Rename the app's public <slug>.lotics.app address
|
|
97
97
|
lotics app rename "<new name>" Rename the app's display name (launcher title)
|
|
98
98
|
lotics app dev [path] Run the app locally with HMR (RPC forwarded to prod)
|
|
99
|
-
lotics package new <name> [path]
|
|
99
|
+
lotics package new <name> [path] [--kind app|content]
|
|
100
|
+
Scaffold a package project — an app (contract + source)
|
|
101
|
+
or a content corpus (docs-only, no app source)
|
|
100
102
|
lotics package build [path] Build the publishable bundle (source + dist)
|
|
101
103
|
lotics package publish [path] Publish a new immutable package version
|
|
102
104
|
lotics package dev [path] Sync the package into a dev workspace + run the app
|
|
103
105
|
dev server (--workspace <dev_ws> selects it)
|
|
104
106
|
lotics package sync [path] Re-sync (additive migrate + materialize) into a dev ws
|
|
105
107
|
lotics package reset [path] DEV-ONLY: drop scaffolded tables + re-scaffold clean
|
|
106
|
-
lotics package install <package> [--version N]
|
|
107
|
-
Install
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
108
|
+
lotics package install <package> [--version N] [--bind-to <alias>=<kdc_id> ...] [--config key=value ...]
|
|
109
|
+
Install a package (app: scaffolds/deploys/materializes,
|
|
110
|
+
--config sets its knobs; content: installs the doc corpus,
|
|
111
|
+
--bind-to consents to adopt a same-named doc on a collision)
|
|
112
|
+
lotics package uninstall <app_id|pci_id> [--archive-tables] [--keep-content]
|
|
113
|
+
Uninstall — dispatched by id. App (app_id): archives
|
|
114
|
+
artifacts (+ --archive-tables also archives scaffolded
|
|
115
|
+
tables). Content (pci_): archives its package-bound docs
|
|
116
|
+
unless --keep-content
|
|
117
|
+
lotics package config <app_id> [--set key=value ...]
|
|
118
|
+
Show or edit an installation's config knobs
|
|
119
|
+
lotics package show <package_id> Registry metadata + version history
|
|
120
|
+
lotics package retire <package_id> [--undo]
|
|
121
|
+
Retire a package (refuse new installs, hide from other
|
|
122
|
+
orgs); existing installations keep working + upgrade
|
|
123
|
+
lotics package list-content List the workspace's content installations
|
|
124
|
+
(standalone pci_ + app-bundled) — source for a pci_ id
|
|
125
|
+
lotics package upgrade <app_id|pci_id> [--version N] [--resolve <key>=... ] [--bind-to ...] [--apply-all]
|
|
126
|
+
Preview-then-apply an upgrade. App: refuses while any
|
|
127
|
+
drift/modified/bundled-knowledge finding lacks a --resolve
|
|
128
|
+
(a knowledge-alias --resolve routes to the doc). Content
|
|
129
|
+
(pci_): --resolve <alias>=apply|keep|archive|recreate|unbind;
|
|
130
|
+
--apply-all accepts the package's version for every doc
|
|
113
131
|
lotics package doctor [app_id] Installation health: version pin vs latest, binding
|
|
114
|
-
drift, locally modified core
|
|
132
|
+
drift, locally modified core, knowledge drift/edits
|
|
133
|
+
(exit 1 on findings)
|
|
115
134
|
lotics package rebind-role <app_id> <alias> <grp_id>
|
|
116
135
|
Re-point a package role at a different group
|
|
117
136
|
(re-materializes at the pinned version)
|
|
@@ -158,6 +177,12 @@ FLAGS
|
|
|
158
177
|
--local Pin the current directory (lotics org use / auth api-key)
|
|
159
178
|
--dev (lotics workspace create) Mark a throwaway app-package dev
|
|
160
179
|
workspace — required for "lotics package reset"
|
|
180
|
+
--kind <k> (lotics package new) Package kind: app (default) or content
|
|
181
|
+
--bind-to <a>=<id> (lotics package install/upgrade, repeatable) Adopt an existing
|
|
182
|
+
same-named doc as package-managed (resolves a knowledge collision)
|
|
183
|
+
--keep-content (lotics package uninstall) Keep the docs instead of archiving them
|
|
184
|
+
--apply-all (lotics package upgrade, knowledge) Accept the package's version
|
|
185
|
+
for every doc (overwrites local edits)
|
|
161
186
|
--all (lotics auth logout) Remove every saved credential
|
|
162
187
|
--version Show version
|
|
163
188
|
|
|
@@ -576,10 +601,18 @@ async function main() {
|
|
|
576
601
|
if (command === "package" && subcommand === "new") {
|
|
577
602
|
const name = toolArgs;
|
|
578
603
|
if (!name) {
|
|
579
|
-
console.error("Usage: lotics package new <name> [path]");
|
|
604
|
+
console.error("Usage: lotics package new <name> [path] [--kind app|content]");
|
|
580
605
|
process.exit(1);
|
|
581
606
|
}
|
|
582
|
-
|
|
607
|
+
if (flags.kind !== undefined && flags.kind !== "app" && flags.kind !== "content") {
|
|
608
|
+
console.error(`Invalid --kind "${flags.kind}" — expected "app" or "content".`);
|
|
609
|
+
process.exit(1);
|
|
610
|
+
}
|
|
611
|
+
await packageNew({
|
|
612
|
+
name,
|
|
613
|
+
targetPath: restArgs[0],
|
|
614
|
+
kind: flags.kind === "content" ? "content" : "app",
|
|
615
|
+
});
|
|
583
616
|
return;
|
|
584
617
|
}
|
|
585
618
|
if (command === "package" && subcommand === "build") {
|
|
@@ -694,15 +727,20 @@ async function main() {
|
|
|
694
727
|
}
|
|
695
728
|
if (command === "package" && !subcommand) {
|
|
696
729
|
console.error("Usage:");
|
|
697
|
-
console.error(" lotics package new <name> [path]
|
|
730
|
+
console.error(" lotics package new <name> [path] [--kind app|content] Scaffold a package project (app source, or docs-only)");
|
|
698
731
|
console.error(" lotics package build [path] Build the publishable bundle (source + dist)");
|
|
699
732
|
console.error(" lotics package publish [path] [-m <changelog>] Publish a new immutable package version");
|
|
700
733
|
console.error(" lotics package dev [path] [--workspace <ws>] Sync into a dev workspace + run the app dev server");
|
|
701
734
|
console.error(" lotics package sync [path] [--workspace <ws>] Re-sync (additive migrate + materialize) into a dev workspace");
|
|
702
735
|
console.error(" lotics package reset [path] [--workspace <ws>] DEV-ONLY: drop scaffolded tables + re-scaffold clean");
|
|
703
|
-
console.error(" lotics package install <package> [--version N] Install a package
|
|
704
|
-
console.error(" lotics package
|
|
705
|
-
console.error(" lotics package
|
|
736
|
+
console.error(" lotics package install <package> [--version N] [--bind-to <alias>=<kdc_id>] [--config key=value ...] Install a package (app or content)");
|
|
737
|
+
console.error(" lotics package uninstall <app_id|pci_id> [--archive-tables] [--keep-content] Uninstall — dispatched by id (app vs content)");
|
|
738
|
+
console.error(" lotics package config <app_id> [--set key=value ...] Show or edit an installation's config knobs");
|
|
739
|
+
console.error(" lotics package show <package_id> Registry metadata + version history (channel, yank, changelog)");
|
|
740
|
+
console.error(" lotics package retire <package_id> [--undo] Retire a package (refuse new installs; installs keep working)");
|
|
741
|
+
console.error(" lotics package list-content List the workspace's content installations (standalone + app-bundled)");
|
|
742
|
+
console.error(" lotics package upgrade <app_id|pci_id> [--version N] [--resolve <key>=... ] [--bind-to ...] [--apply-all] Preview + apply an upgrade");
|
|
743
|
+
console.error(" lotics package doctor [app_id] Health: version pin vs latest + binding/knowledge drift");
|
|
706
744
|
console.error(" lotics package rebind-role <app_id> <alias> <grp_id> Re-point a package role at another group");
|
|
707
745
|
console.error(" lotics package eject <app_id> Sever an installation's package link");
|
|
708
746
|
console.error(" lotics package extract <app_id> [path] Promote a bespoke app to a draft package project");
|
|
@@ -738,16 +776,6 @@ async function main() {
|
|
|
738
776
|
const { client, ctx } = requireClient(flags);
|
|
739
777
|
// lotics workspace / lotics workspace list / lotics workspace select <id>
|
|
740
778
|
if (command === "workspace") {
|
|
741
|
-
if (subcommand === "yank") {
|
|
742
|
-
const [packageId, versionRaw, maybeUndo] = toolArgs ? toolArgs.split(/\s+/) : [];
|
|
743
|
-
const version = Number(versionRaw);
|
|
744
|
-
if (!packageId || !Number.isInteger(version) || version <= 0) {
|
|
745
|
-
console.error("Usage: lotics package yank <package_id> <version> [--undo]");
|
|
746
|
-
process.exit(1);
|
|
747
|
-
}
|
|
748
|
-
await packageYank(client, { package_id: packageId, version, undo: maybeUndo === "--undo" });
|
|
749
|
-
return;
|
|
750
|
-
}
|
|
751
779
|
if (subcommand === "doctor") {
|
|
752
780
|
// The workspace command family runs before the global workspace
|
|
753
781
|
// resolution — doctor needs the same first-workspace fallback every
|
|
@@ -870,7 +898,7 @@ async function main() {
|
|
|
870
898
|
if (subcommand === "install") {
|
|
871
899
|
const packageId = toolArgs;
|
|
872
900
|
if (!packageId) {
|
|
873
|
-
console.error("Usage: lotics package install <package> [--version N]");
|
|
901
|
+
console.error("Usage: lotics package install <package> [--version N] [--bind-to <alias>=<kdc_id> ...]");
|
|
874
902
|
process.exit(1);
|
|
875
903
|
}
|
|
876
904
|
let version;
|
|
@@ -881,7 +909,60 @@ async function main() {
|
|
|
881
909
|
process.exit(1);
|
|
882
910
|
}
|
|
883
911
|
}
|
|
884
|
-
|
|
912
|
+
const config = flags.config.length > 0 ? parseInstallConfigFlags(flags.config) : undefined;
|
|
913
|
+
await packageInstall(client, {
|
|
914
|
+
package_id: packageId,
|
|
915
|
+
version,
|
|
916
|
+
bind_to: parseBindToFlags(flags.bindTo),
|
|
917
|
+
...(config !== undefined ? { config } : {}),
|
|
918
|
+
});
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
if (subcommand === "uninstall") {
|
|
922
|
+
const id = toolArgs;
|
|
923
|
+
if (!id) {
|
|
924
|
+
console.error("Usage: lotics package uninstall <app_id|pci_id> [--archive-tables] [--keep-content]");
|
|
925
|
+
console.error("Dispatched by id: an app installation (app_id; --archive-tables to also archive its " +
|
|
926
|
+
"scaffolded tables) or a standalone content installation (pci_ id from install / " +
|
|
927
|
+
"lotics package list-content; --keep-content to retain its docs).");
|
|
928
|
+
process.exit(1);
|
|
929
|
+
}
|
|
930
|
+
await packageUninstall(client, {
|
|
931
|
+
id,
|
|
932
|
+
keep_content: flags.keepContent,
|
|
933
|
+
archive_tables: flags.archiveTables,
|
|
934
|
+
});
|
|
935
|
+
return;
|
|
936
|
+
}
|
|
937
|
+
if (subcommand === "list-content") {
|
|
938
|
+
await packageListContent(client);
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
941
|
+
if (subcommand === "config") {
|
|
942
|
+
const appId = toolArgs;
|
|
943
|
+
if (!appId) {
|
|
944
|
+
console.error("Usage: lotics package config <app_id> [--set key=value ...]");
|
|
945
|
+
process.exit(1);
|
|
946
|
+
}
|
|
947
|
+
await packageConfig(client, { app_id: appId, sets: flags.set });
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
if (subcommand === "retire") {
|
|
951
|
+
const packageId = toolArgs;
|
|
952
|
+
if (!packageId) {
|
|
953
|
+
console.error("Usage: lotics package retire <package_id> [--undo]");
|
|
954
|
+
process.exit(1);
|
|
955
|
+
}
|
|
956
|
+
await packageRetire(client, { package_id: packageId, undo: flags.undo });
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
if (subcommand === "show") {
|
|
960
|
+
const packageId = toolArgs;
|
|
961
|
+
if (!packageId) {
|
|
962
|
+
console.error("Usage: lotics package show <package_id>");
|
|
963
|
+
process.exit(1);
|
|
964
|
+
}
|
|
965
|
+
await packageShow(client, { package_id: packageId });
|
|
885
966
|
return;
|
|
886
967
|
}
|
|
887
968
|
if (subcommand === "eject") {
|
|
@@ -921,6 +1002,16 @@ async function main() {
|
|
|
921
1002
|
await packageAdopt(client, { app_id: appId, version, projectDir: restArgs[0] });
|
|
922
1003
|
return;
|
|
923
1004
|
}
|
|
1005
|
+
if (subcommand === "yank") {
|
|
1006
|
+
const packageId = toolArgs;
|
|
1007
|
+
const version = Number(restArgs[0]);
|
|
1008
|
+
if (!packageId || !Number.isInteger(version) || version <= 0) {
|
|
1009
|
+
console.error("Usage: lotics package yank <package_id> <version> [--undo]");
|
|
1010
|
+
process.exit(1);
|
|
1011
|
+
}
|
|
1012
|
+
await packageYank(client, { package_id: packageId, version, undo: flags.undo });
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
924
1015
|
if (subcommand === "fleet-upgrade") {
|
|
925
1016
|
const packageId = toolArgs;
|
|
926
1017
|
if (!packageId) {
|
|
@@ -949,7 +1040,7 @@ async function main() {
|
|
|
949
1040
|
console.error("Usage: lotics package rebind-role <app_id> <role_alias> <grp_id>");
|
|
950
1041
|
process.exit(1);
|
|
951
1042
|
}
|
|
952
|
-
const result = await client.
|
|
1043
|
+
const result = await client.rebindPackageRole(appId, {
|
|
953
1044
|
role_alias: roleAlias,
|
|
954
1045
|
group_id: groupId,
|
|
955
1046
|
});
|
|
@@ -959,9 +1050,9 @@ async function main() {
|
|
|
959
1050
|
return;
|
|
960
1051
|
}
|
|
961
1052
|
if (subcommand === "upgrade") {
|
|
962
|
-
const
|
|
963
|
-
if (!
|
|
964
|
-
console.error("Usage: lotics package upgrade <app_id> [--version N] [--resolve <
|
|
1053
|
+
const target = toolArgs;
|
|
1054
|
+
if (!target) {
|
|
1055
|
+
console.error("Usage: lotics package upgrade <app_id | pci_installation_id> [--version N] [--resolve <key>=... ...]");
|
|
965
1056
|
process.exit(1);
|
|
966
1057
|
}
|
|
967
1058
|
let version;
|
|
@@ -972,10 +1063,24 @@ async function main() {
|
|
|
972
1063
|
process.exit(1);
|
|
973
1064
|
}
|
|
974
1065
|
}
|
|
1066
|
+
// A `pci_` id is a standalone content installation (its own upgrade
|
|
1067
|
+
// pipeline); everything else is an app installation.
|
|
1068
|
+
if (target.startsWith("pci_")) {
|
|
1069
|
+
await packageUpgradeKnowledge(client, {
|
|
1070
|
+
installation_id: target,
|
|
1071
|
+
version,
|
|
1072
|
+
resolve: flags.resolve,
|
|
1073
|
+
bind_to: parseBindToFlags(flags.bindTo),
|
|
1074
|
+
applyAll: flags.applyAll,
|
|
1075
|
+
});
|
|
1076
|
+
return;
|
|
1077
|
+
}
|
|
975
1078
|
await packageUpgrade(client, {
|
|
976
|
-
app_id:
|
|
1079
|
+
app_id: target,
|
|
977
1080
|
version,
|
|
978
|
-
|
|
1081
|
+
resolve: flags.resolve,
|
|
1082
|
+
bindTo: flags.bindTo,
|
|
1083
|
+
applyAll: flags.applyAll,
|
|
979
1084
|
});
|
|
980
1085
|
return;
|
|
981
1086
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dispatch smoke test over the BUILT CLI artifact — every `package` subcommand
|
|
3
|
+
* must route to its own handler, proven by its usage/refusal line appearing on
|
|
4
|
+
* a bare invocation. The dispatch in cli.ts is one hand-rolled argv walk with
|
|
5
|
+
* subcommand names repeated across command families (`doctor` exists in both
|
|
6
|
+
* `workspace` and `package`) and split positional conventions (`toolArgs` +
|
|
7
|
+
* `restArgs`), and nothing else tests it: `package yank` shipped dispatched
|
|
8
|
+
* inside the WORKSPACE family and surfaced only on the first live invocation.
|
|
9
|
+
*
|
|
10
|
+
* The fake API key satisfies `requireClient` (client construction is offline);
|
|
11
|
+
* every asserted path exits before any network call. Spawns run in an empty
|
|
12
|
+
* temp cwd so path-defaulting subcommands fail on the missing project file —
|
|
13
|
+
* which equally proves their dispatch reached the right handler.
|
|
14
|
+
*/
|
|
15
|
+
import { beforeAll, describe, expect, it } from "vitest";
|
|
16
|
+
import { execFileSync, spawnSync } from "node:child_process";
|
|
17
|
+
import fs from "node:fs";
|
|
18
|
+
import os from "node:os";
|
|
19
|
+
import path from "node:path";
|
|
20
|
+
import { fileURLToPath } from "node:url";
|
|
21
|
+
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
22
|
+
const cliBin = path.join(packageRoot, "dist", "src", "cli.js");
|
|
23
|
+
const emptyCwd = fs.mkdtempSync(path.join(os.tmpdir(), "lotics-cli-dispatch-"));
|
|
24
|
+
function runCli(args) {
|
|
25
|
+
const result = spawnSync(process.execPath, [cliBin, ...args], {
|
|
26
|
+
cwd: emptyCwd,
|
|
27
|
+
env: {
|
|
28
|
+
...process.env,
|
|
29
|
+
LOTICS_API_KEY: "ltk_dispatch_smoke_fake_key_never_used_on_wire",
|
|
30
|
+
// A pinned (fake) workspace skips the pre-dispatch auto-resolution,
|
|
31
|
+
// which would otherwise hit the network (`listWorkspaces`) before any
|
|
32
|
+
// usage gate. Nothing below ever reaches the wire.
|
|
33
|
+
LOTICS_WORKSPACE: "wsp_dispatch_smoke_fake",
|
|
34
|
+
LOTICS_ORG: "",
|
|
35
|
+
},
|
|
36
|
+
encoding: "utf-8",
|
|
37
|
+
timeout: 30_000,
|
|
38
|
+
});
|
|
39
|
+
return { status: result.status, stderr: result.stderr ?? "" };
|
|
40
|
+
}
|
|
41
|
+
beforeAll(() => {
|
|
42
|
+
// Bundle the real artifact from src (esbuild only — no tsgo; vitest's
|
|
43
|
+
// transform already type-agnostic). ~1s.
|
|
44
|
+
execFileSync(process.execPath, ["scripts/build_cli.mjs"], { cwd: packageRoot });
|
|
45
|
+
}, 120_000);
|
|
46
|
+
describe("lotics package <subcommand> dispatch", () => {
|
|
47
|
+
// Positional-requiring subcommands: a bare invocation must print the
|
|
48
|
+
// subcommand's OWN usage line — reaching it proves family routing AND that
|
|
49
|
+
// the usage gate fires before any network/filesystem work.
|
|
50
|
+
const usageCases = [
|
|
51
|
+
["install", /Usage: lotics package install/],
|
|
52
|
+
["uninstall", /Usage: lotics package uninstall/],
|
|
53
|
+
["config", /Usage: lotics package config/],
|
|
54
|
+
["retire", /Usage: lotics package retire <package_id>/],
|
|
55
|
+
["show", /Usage: lotics package show <package_id>/],
|
|
56
|
+
["upgrade", /Usage: lotics package upgrade/],
|
|
57
|
+
["yank", /Usage: lotics package yank <package_id> <version>/],
|
|
58
|
+
["rebind-role", /Usage: lotics package rebind-role/],
|
|
59
|
+
["fleet-upgrade", /Usage: lotics package fleet-upgrade/],
|
|
60
|
+
["extract", /Usage: lotics package extract/],
|
|
61
|
+
["adopt", /Usage: lotics package adopt/],
|
|
62
|
+
["new", /Usage: lotics package new/],
|
|
63
|
+
];
|
|
64
|
+
for (const [sub, usage] of usageCases) {
|
|
65
|
+
it(`routes "package ${sub}" to its handler (usage on missing args)`, () => {
|
|
66
|
+
const { status, stderr } = runCli(["package", sub]);
|
|
67
|
+
expect(stderr).toMatch(usage);
|
|
68
|
+
expect(stderr).not.toMatch(/Unknown package subcommand/);
|
|
69
|
+
expect(status).toBe(1);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
// Path-defaulting subcommands: in an empty cwd they must fail on the missing
|
|
73
|
+
// package project — their handler's message, not the dispatcher's. (`sync`
|
|
74
|
+
// is excluded: it validates the dev workspace over the network BEFORE
|
|
75
|
+
// reading the project, so it can't be smoke-tested offline.)
|
|
76
|
+
const projectCases = ["build", "publish"];
|
|
77
|
+
for (const sub of projectCases) {
|
|
78
|
+
it(`routes "package ${sub}" to its handler (missing project in empty cwd)`, () => {
|
|
79
|
+
const { status, stderr } = runCli(["package", sub]);
|
|
80
|
+
expect(stderr).toMatch(/No package\.json in|not a package project/);
|
|
81
|
+
expect(stderr).not.toMatch(/Unknown package subcommand/);
|
|
82
|
+
expect(status).toBe(1);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
it("still rejects a genuinely unknown subcommand", () => {
|
|
86
|
+
const { status, stderr } = runCli(["package", "frobnicate"]);
|
|
87
|
+
expect(stderr).toMatch(/Unknown package subcommand/);
|
|
88
|
+
expect(status).toBe(1);
|
|
89
|
+
});
|
|
90
|
+
});
|