@lotics/cli 0.76.1 → 0.83.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/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, 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";
17
+ import { packageInstall, packageUninstall, packageListContent, packageConfig, packageShow, parseInstallConfigFlags, packageEject, packageDoctor, packageUpgrade, packageUpgradeKnowledge, parseBindToFlags, appPublish, appRelease, appUnpublish, 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";
@@ -71,10 +71,22 @@ COMMANDS
71
71
  cat args.json | lotics run <tool> Read JSON args from stdin (large payloads)
72
72
  lotics app create <name> [path] Create a new custom-code app + scaffold locally
73
73
  lotics app pull <app_id> [path] Bootstrap full local env (source + npm install + types)
74
- lotics app deploy -m <message> Build + upload current dir as a new version
75
- (-m is required — it's the version's audit trail)
76
- (code + queries only — workflow bindings are
74
+ lotics app deploy -m <message> Build + upload current dir as a new version — COMMIT
75
+ (-m is required — it's the version's audit trail;
76
+ carries code + queries only — workflow bindings are
77
77
  managed by set_app_workflow / remove_app_workflow)
78
+ lotics app publish [app_id|.] [--rename old=new ...] [-m <changelog>] [--yes]
79
+ Make the app DISTRIBUTABLE — first-release it (v1) as a
80
+ package; later versions ship via "lotics app release".
81
+ Previews the auto-minted aliases + findings; --yes applies
82
+ (extract → create → publish v1 → pin origin). --rename fixes
83
+ an alias before v1 freezes; -m is an optional v1 changelog
84
+ lotics app release [app_id|.] -m <changelog> [--yes]
85
+ RELEASE the next package version from the origin app you
86
+ are happy with (preview; --yes publishes + re-pins)
87
+ lotics app unpublish <app_id|package_id> [--undo]
88
+ Take the published package off the shelf (installations
89
+ keep working); --undo restores it
78
90
  lotics app versions [app_id] Show deploy history newest-first (version,
79
91
  timestamp, deployer, build status, -m message;
80
92
  * marks the currently served version)
@@ -96,57 +108,40 @@ COMMANDS
96
108
  lotics app subdomain <new-subdomain> Rename the app's public <slug>.lotics.app address
97
109
  lotics app rename "<new name>" Rename the app's display name (launcher title)
98
110
  lotics app dev [path] Run the app locally with HMR (RPC forwarded to prod)
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)
102
- lotics package build [path] Build the publishable bundle (source + dist)
103
- lotics package publish [path] Publish a new immutable package version
104
- lotics package dev [path] Sync the package into a dev workspace + run the app
105
- dev server (--workspace <dev_ws> selects it)
106
- lotics package sync [path] Re-sync (additive migrate + materialize) into a dev ws
107
- lotics package reset [path] DEV-ONLY: drop scaffolded tables + re-scaffold clean
108
- lotics package install <package> [--version N] [--bind-to <alias>=<kdc_id> ...] [--config key=value ...]
111
+ # PACKAGES consumer verbs are top-level; low-traffic ops live on "lotics package"
112
+ # (author verbs live on "lotics app")
113
+ lotics install <package_id> [--version N] [--bind-to <alias>=<kdc_id> ...] [--config key=value ...]
109
114
  Install a package (app: scaffolds/deploys/materializes,
110
115
  --config sets its knobs; content: installs the doc corpus,
111
116
  --bind-to consents to adopt a same-named doc on a collision)
112
- lotics package uninstall <app_id|pci_id> [--archive-tables] [--keep-content]
117
+ lotics uninstall <app_id|pci_id> [--archive-tables] [--keep-content]
113
118
  Uninstall — dispatched by id. App (app_id): archives
114
119
  artifacts (+ --archive-tables also archives scaffolded
115
120
  tables). Content (pci_): archives its package-bound docs
116
121
  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
122
+ lotics upgrade <app_id|pci_id|package_id> [--version N] [--resolve <key>=... ] [--bind-to ...] [--apply-all]
123
+ Preview-then-apply an upgrade, dispatched by id. App
124
+ (app_id): refuses while any drift/modified/bundled-knowledge
125
+ finding lacks a --resolve. Content (pci_): --resolve
126
+ <alias>=apply|keep|archive|recreate|unbind. Package (apg_):
127
+ FLEET every installation across your org (clean apply,
128
+ findings skip). --apply-all accepts the package's version
131
129
  lotics package doctor [app_id] Installation health: version pin vs latest, binding
132
130
  drift, locally modified core, knowledge drift/edits
133
131
  (exit 1 on findings)
134
- lotics package rebind-role <app_id> <alias> <grp_id>
135
- Re-point a package role at a different group
136
- (re-materializes at the pinned version)
132
+ lotics package config <app_id> [--set key=value ...]
133
+ Show or edit an installation's config knobs
137
134
  lotics package eject <app_id> Sever an installation's package link
138
135
  (re-deploys the pinned source as a bespoke app)
139
- lotics package extract <app_id> [path] Promote a bespoke app to a draft package
140
- project (contract + binding + templates); always
141
- writes the draft, exits 1 on error findings
142
- lotics package adopt <app_id> [path] Bind the published package project onto the
143
- origin app (reads .lotics/adopt_binding.json;
144
- --version N pins a specific version)
145
- lotics package fleet-upgrade <package_id> [--version N]
136
+ lotics package show <package_id> Registry metadata + version history
137
+ lotics package list-content List the workspace's content installations
138
+ (standalone content installs) source for a pci_ id
146
139
  lotics package yank <package_id> <version> [--undo]
147
- Upgrade EVERY installation of the package across
148
- your org: applies where the preview is clean,
149
- skips + reports findings (exit 1 unless all current)
140
+ Refuse new installs/upgrades of a broken published
141
+ version (pinned installations keep running)
142
+ lotics run publish_content '{"name":"…","knowledge_doc_ids":[…],"template_ids":[…]}'
143
+ Publish a SET of live docs + templates as a content
144
+ package (v1 + origin pin); release_content cuts the next
150
145
  lotics ui link <component> [--ui-src <path>] [--remove]
151
146
  Dev-link @lotics/ui to packages/ui/src (Vite alias
152
147
  + tsc paths) for live HMR + typecheck. Monorepo apps
@@ -175,13 +170,12 @@ FLAGS
175
170
  is_current_member / row-scoping resolve to them (also
176
171
  LOTICS_VIEW_AS env; admin key only; writes stay yours)
177
172
  --local Pin the current directory (lotics org use / auth api-key)
178
- --dev (lotics workspace create) Mark a throwaway app-package dev
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
173
+ --rename <old>=<new> (lotics app publish, repeatable) Fix an auto-minted alias before
174
+ the package's v1 contract freezes it
175
+ --bind-to <a>=<id> (lotics install/upgrade, repeatable) Adopt an existing
182
176
  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
177
+ --keep-content (lotics uninstall) Keep the docs instead of archiving them
178
+ --apply-all (lotics upgrade, knowledge) Accept the package's version
185
179
  for every doc (overwrites local edits)
186
180
  --all (lotics auth logout) Remove every saved credential
187
181
  --version Show version
@@ -597,28 +591,6 @@ async function main() {
597
591
  console.error("Usage: lotics ui link <component> [--ui-src <abs path>] [--remove]");
598
592
  process.exit(1);
599
593
  }
600
- // --- lotics package new / build — local scaffold + build, no auth required ---
601
- if (command === "package" && subcommand === "new") {
602
- const name = toolArgs;
603
- if (!name) {
604
- console.error("Usage: lotics package new <name> [path] [--kind app|content]");
605
- process.exit(1);
606
- }
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
- });
616
- return;
617
- }
618
- if (command === "package" && subcommand === "build") {
619
- await packageBuild({ projectDir: toolArgs });
620
- return;
621
- }
622
594
  // --- lotics app workflow check [alias] — local typecheck, no auth, no network ---
623
595
  // Reads src/workflows/*.ts + .lotics/workflows/*.globals.d.ts and the app's own
624
596
  // typescript; builds one isolated program per alias. Handled before the auth
@@ -701,7 +673,16 @@ async function main() {
701
673
  return;
702
674
  }
703
675
  // --- Validate command before auth ---
704
- if (command !== "tools" && command !== "upload" && command !== "run" && command !== "download" && command !== "workspace" && command !== "app" && command !== "package") {
676
+ if (command !== "tools" &&
677
+ command !== "upload" &&
678
+ command !== "run" &&
679
+ command !== "download" &&
680
+ command !== "workspace" &&
681
+ command !== "app" &&
682
+ command !== "package" &&
683
+ command !== "install" &&
684
+ command !== "uninstall" &&
685
+ command !== "upgrade") {
705
686
  console.error(`Unknown command: ${command}`);
706
687
  console.error('Run "lotics --help" for usage.');
707
688
  process.exit(1);
@@ -723,30 +704,27 @@ async function main() {
723
704
  console.error(" lotics app subdomain <new-subdomain> Rename the app's public <slug>.lotics.app address");
724
705
  console.error(" lotics app rename \"<new name>\" Rename the app's display name (launcher title)");
725
706
  console.error(" lotics app dev [path] Run the app locally with HMR + RPC forwarding");
707
+ console.error(" lotics app publish [app_id|.] [--rename old=new ...] [-m <changelog>] [--yes] First-release the app as a package (v1; preview, --yes applies)");
708
+ console.error(" lotics app release [app_id|.] -m <changelog> [--yes] Release the next package version from the origin");
709
+ console.error(" lotics app unpublish <app_id|package_id> [--undo] Take a published package off the shelf (any kind — app or content)");
726
710
  process.exit(1);
727
711
  }
728
712
  if (command === "package" && !subcommand) {
729
- console.error("Usage:");
730
- console.error(" lotics package new <name> [path] [--kind app|content] Scaffold a package project (app source, or docs-only)");
731
- console.error(" lotics package build [path] Build the publishable bundle (source + dist)");
732
- console.error(" lotics package publish [path] [-m <changelog>] Publish a new immutable package version");
733
- console.error(" lotics package dev [path] [--workspace <ws>] Sync into a dev workspace + run the app dev server");
734
- console.error(" lotics package sync [path] [--workspace <ws>] Re-sync (additive migrate + materialize) into a dev workspace");
735
- console.error(" lotics package reset [path] [--workspace <ws>] DEV-ONLY: drop scaffolded tables + re-scaffold clean");
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");
713
+ console.error("Usage (low-traffic consumer / registry ops — author verbs live on `lotics app`):");
714
+ console.error(" The high-traffic consumer verbs are top-level:");
715
+ console.error(" lotics install <package_id> [--version N] [--bind-to <alias>=<kdc_id>] [--config key=value ...] Install a package (app or content)");
716
+ console.error(" lotics uninstall <app_id|pci_id> [--archive-tables] [--keep-content] Uninstall dispatched by id (app vs content)");
717
+ console.error(" lotics upgrade <app_id|pci_id|package_id> [--version N] [--resolve <key>=... ] [--bind-to ...] [--apply-all] Upgrade app / content / whole fleet (apg_)");
743
718
  console.error(" lotics package doctor [app_id] Health: version pin vs latest + binding/knowledge drift");
744
- console.error(" lotics package rebind-role <app_id> <alias> <grp_id> Re-point a package role at another group");
719
+ console.error(" lotics package config <app_id> [--set key=value ...] Show or edit an installation's config knobs");
745
720
  console.error(" lotics package eject <app_id> Sever an installation's package link");
746
- console.error(" lotics package extract <app_id> [path] Promote a bespoke app to a draft package project");
747
- console.error(" lotics package adopt <app_id> [path] Bind the published project onto the origin app");
748
- console.error(" lotics package fleet-upgrade <package_id> [--version N] Upgrade every org installation (clean ones apply; findings skip)");
721
+ console.error(" lotics package show <package_id> Registry metadata + version history (channel, yank, changelog)");
722
+ console.error(" lotics package list-content List the workspace's content installations (standalone content installs)");
749
723
  console.error(" lotics package yank <package_id> <version> [--undo] Refuse new installs/upgrades of a broken published version (pinned installations keep running)");
724
+ console.error(" A content package (a SET of live docs + templates) — publish + release with the tools, retire on `lotics app`:");
725
+ console.error(" lotics run publish_content '{\"name\":\"…\",\"knowledge_doc_ids\":[…],\"template_ids\":[…]}' Publish v1 (a fixed set)");
726
+ console.error(" lotics run release_content '{\"package_id\":\"apg_…\",\"changelog\":\"…\"}' Cut the next version (add \"dry_run\":true to preview)");
727
+ console.error(" lotics app unpublish <package_id> Take the whole package off the shelf (kind-agnostic — retires content too)");
750
728
  process.exit(1);
751
729
  }
752
730
  if (command === "run" && !subcommand) {
@@ -820,11 +798,11 @@ async function main() {
820
798
  if (subcommand === "create") {
821
799
  const name = toolArgs;
822
800
  if (!name) {
823
- console.error('Usage: lotics workspace create <name> [--timezone <tz>] [--dev]');
801
+ console.error('Usage: lotics workspace create <name> [--timezone <tz>]');
824
802
  process.exit(1);
825
803
  }
826
804
  const timezone = flags.timezone;
827
- const created = await client.createWorkspace({ name, timezone, is_dev: flags.dev || undefined });
805
+ const created = await client.createWorkspace({ name, timezone });
828
806
  setSelectedWorkspace(created.id);
829
807
  client.setWorkspaceId(created.id);
830
808
  if (flags.json) {
@@ -893,47 +871,96 @@ async function main() {
893
871
  }
894
872
  // Ensure workspace is resolved for all remaining commands
895
873
  await resolveWorkspace(client, ctx);
896
- // lotics package install <package> [--version N]
897
- if (command === "package") {
898
- if (subcommand === "install") {
899
- const packageId = toolArgs;
900
- if (!packageId) {
901
- console.error("Usage: lotics package install <package> [--version N] [--bind-to <alias>=<kdc_id> ...]");
874
+ // --- lotics install / uninstall / upgrade — top-level CONSUMER verbs ---
875
+ // The high-traffic operator surface reads noun-less: `lotics install <pkg>`,
876
+ // `lotics uninstall <id>`, `lotics upgrade <id>`. Low-traffic ops keep their
877
+ // `lotics package <verb>` home. The package id is the SECOND positional at top
878
+ // level, which the parser puts in `subcommand` (not `toolArgs`).
879
+ if (command === "install") {
880
+ const packageId = subcommand;
881
+ if (!packageId) {
882
+ console.error("Usage: lotics install <package_id> [--version N] [--bind-to <alias>=<kdc_id> ...] [--config key=value ...]");
883
+ process.exit(1);
884
+ }
885
+ let version;
886
+ if (flags.packageVersion !== undefined) {
887
+ version = Number(flags.packageVersion);
888
+ if (!Number.isInteger(version) || version <= 0) {
889
+ console.error(`Invalid --version "${flags.packageVersion}" — expected a positive integer.`);
902
890
  process.exit(1);
903
891
  }
904
- let version;
905
- if (flags.packageVersion !== undefined) {
906
- version = Number(flags.packageVersion);
907
- if (!Number.isInteger(version) || version <= 0) {
908
- console.error(`Invalid --version "${flags.packageVersion}" — expected a positive integer.`);
909
- process.exit(1);
910
- }
911
- }
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
892
  }
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).");
893
+ const config = flags.config.length > 0 ? parseInstallConfigFlags(flags.config) : undefined;
894
+ await packageInstall(client, {
895
+ package_id: packageId,
896
+ version,
897
+ bind_to: parseBindToFlags(flags.bindTo),
898
+ ...(config !== undefined ? { config } : {}),
899
+ });
900
+ return;
901
+ }
902
+ if (command === "uninstall") {
903
+ const id = subcommand;
904
+ if (!id) {
905
+ console.error("Usage: lotics uninstall <app_id|pci_id> [--archive-tables] [--keep-content]");
906
+ console.error("Dispatched by id: an app installation (app_id; --archive-tables to also archive its " +
907
+ "scaffolded tables) or a standalone content installation (pci_ id from `lotics install` / " +
908
+ "lotics package list-content; --keep-content to retain its docs).");
909
+ process.exit(1);
910
+ }
911
+ await packageUninstall(client, {
912
+ id,
913
+ keep_content: flags.keepContent,
914
+ archive_tables: flags.archiveTables,
915
+ });
916
+ return;
917
+ }
918
+ if (command === "upgrade") {
919
+ const target = subcommand;
920
+ if (!target) {
921
+ console.error("Usage: lotics upgrade <app_id | pci_id | package_id> [--version N] [--resolve <key>=... ...] [--bind-to ...] [--apply-all]");
922
+ console.error("Dispatched by id: an app installation (app_id), a standalone content installation (pci_ id), " +
923
+ "or a package id (apg_ — fleet-upgrades every installation across your org).");
924
+ process.exit(1);
925
+ }
926
+ let version;
927
+ if (flags.packageVersion !== undefined) {
928
+ version = Number(flags.packageVersion);
929
+ if (!Number.isInteger(version) || version <= 0) {
930
+ console.error(`Invalid --version "${flags.packageVersion}" — expected a positive integer.`);
928
931
  process.exit(1);
929
932
  }
930
- await packageUninstall(client, {
931
- id,
932
- keep_content: flags.keepContent,
933
- archive_tables: flags.archiveTables,
933
+ }
934
+ // A package id fleet-upgrades every installation across the org; a `pci_` id
935
+ // is a standalone content installation; anything else is an app installation.
936
+ if (target.startsWith("apg_")) {
937
+ await packageFleetUpgrade(client, { package_id: target, version });
938
+ return;
939
+ }
940
+ if (target.startsWith("pci_")) {
941
+ await packageUpgradeKnowledge(client, {
942
+ installation_id: target,
943
+ version,
944
+ resolve: flags.resolve,
945
+ bind_to: parseBindToFlags(flags.bindTo),
946
+ applyAll: flags.applyAll,
934
947
  });
935
948
  return;
936
949
  }
950
+ await packageUpgrade(client, {
951
+ app_id: target,
952
+ version,
953
+ resolve: flags.resolve,
954
+ bindTo: flags.bindTo,
955
+ applyAll: flags.applyAll,
956
+ });
957
+ return;
958
+ }
959
+ // lotics package <verb> — the low-traffic consumer / registry surface. The
960
+ // high-traffic install / uninstall / upgrade / fleet-upgrade verbs moved to
961
+ // the top level above; they are refused here (below) so the old forms fail
962
+ // loudly instead of silently doing nothing.
963
+ if (command === "package") {
937
964
  if (subcommand === "list-content") {
938
965
  await packageListContent(client);
939
966
  return;
@@ -947,15 +974,6 @@ async function main() {
947
974
  await packageConfig(client, { app_id: appId, sets: flags.set });
948
975
  return;
949
976
  }
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
977
  if (subcommand === "show") {
960
978
  const packageId = toolArgs;
961
979
  if (!packageId) {
@@ -974,34 +992,6 @@ async function main() {
974
992
  await packageEject(client, { app_id: appId });
975
993
  return;
976
994
  }
977
- if (subcommand === "extract") {
978
- const appId = toolArgs;
979
- if (!appId) {
980
- console.error("Usage: lotics package extract <app_id> [path]");
981
- console.error("Promotes a bespoke app to a draft package project (contract + binding + templates).");
982
- process.exit(1);
983
- }
984
- await packageExtract(client, { app_id: appId, targetPath: restArgs[0] });
985
- return;
986
- }
987
- if (subcommand === "adopt") {
988
- const appId = toolArgs;
989
- if (!appId) {
990
- console.error("Usage: lotics package adopt <app_id> [path]");
991
- console.error("Binds the published package project (this dir, or [path]) onto the origin app.");
992
- process.exit(1);
993
- }
994
- let version;
995
- if (flags.packageVersion !== undefined) {
996
- version = Number(flags.packageVersion);
997
- if (!Number.isInteger(version) || version <= 0) {
998
- console.error(`Invalid --version "${flags.packageVersion}" — expected a positive integer.`);
999
- process.exit(1);
1000
- }
1001
- }
1002
- await packageAdopt(client, { app_id: appId, version, projectDir: restArgs[0] });
1003
- return;
1004
- }
1005
995
  if (subcommand === "yank") {
1006
996
  const packageId = toolArgs;
1007
997
  const version = Number(restArgs[0]);
@@ -1012,106 +1002,30 @@ async function main() {
1012
1002
  await packageYank(client, { package_id: packageId, version, undo: flags.undo });
1013
1003
  return;
1014
1004
  }
1015
- if (subcommand === "fleet-upgrade") {
1016
- const packageId = toolArgs;
1017
- if (!packageId) {
1018
- console.error("Usage: lotics package fleet-upgrade <package_id> [--version N]");
1019
- process.exit(1);
1020
- }
1021
- let version;
1022
- if (flags.packageVersion !== undefined) {
1023
- version = Number(flags.packageVersion);
1024
- if (!Number.isInteger(version) || version <= 0) {
1025
- console.error(`Invalid --version "${flags.packageVersion}" — expected a positive integer.`);
1026
- process.exit(1);
1027
- }
1028
- }
1029
- await packageFleetUpgrade(client, { package_id: packageId, version });
1030
- return;
1031
- }
1032
1005
  if (subcommand === "doctor") {
1033
1006
  await packageDoctor(client, { app_id: toolArgs });
1034
1007
  return;
1035
1008
  }
1009
+ // Re-pointing a live role rides the upgrade's ONE resolutions grammar —
1010
+ // redirect the removed verb loudly, never a silent unknown-command fall-through.
1036
1011
  if (subcommand === "rebind-role") {
1037
1012
  const appId = toolArgs;
1038
1013
  const [roleAlias, groupId] = restArgs;
1039
- if (!appId || !roleAlias || !groupId) {
1040
- console.error("Usage: lotics package rebind-role <app_id> <role_alias> <grp_id>");
1041
- process.exit(1);
1042
- }
1043
- const result = await client.rebindPackageRole(appId, {
1044
- role_alias: roleAlias,
1045
- group_id: groupId,
1046
- });
1047
- console.error(`Rebound role "${result.role_alias}" → ${result.group_id}` +
1048
- (result.previous_group_id ? ` (was ${result.previous_group_id})` : "") +
1049
- ". Package workflows/agents were re-materialized against the new group.");
1050
- return;
1051
- }
1052
- if (subcommand === "upgrade") {
1053
- const target = toolArgs;
1054
- if (!target) {
1055
- console.error("Usage: lotics package upgrade <app_id | pci_installation_id> [--version N] [--resolve <key>=... ...]");
1056
- process.exit(1);
1057
- }
1058
- let version;
1059
- if (flags.packageVersion !== undefined) {
1060
- version = Number(flags.packageVersion);
1061
- if (!Number.isInteger(version) || version <= 0) {
1062
- console.error(`Invalid --version "${flags.packageVersion}" — expected a positive integer.`);
1063
- process.exit(1);
1064
- }
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
- }
1078
- await packageUpgrade(client, {
1079
- app_id: target,
1080
- version,
1081
- resolve: flags.resolve,
1082
- bindTo: flags.bindTo,
1083
- applyAll: flags.applyAll,
1084
- });
1085
- return;
1086
- }
1087
- if (subcommand === "publish") {
1088
- await packagePublish(client, { projectDir: toolArgs, changelog: flags.message });
1089
- return;
1090
- }
1091
- if (subcommand === "sync") {
1092
- await packageSync(client, { projectDir: toolArgs });
1093
- return;
1094
- }
1095
- if (subcommand === "reset") {
1096
- await packageReset(client, { projectDir: toolArgs });
1097
- return;
1014
+ console.error("`lotics package rebind-role` was folded into the upgrade grammar. Re-point a live role with:\n" +
1015
+ ` lotics upgrade ${appId ?? "<app_id>"} --resolve roles.${roleAlias ?? "<alias>"}=${groupId ?? "<grp_id>"}`);
1016
+ process.exit(1);
1098
1017
  }
1099
- if (subcommand === "dev") {
1100
- // First positional is an optional project path; --port / --vite-port
1101
- // override the wrapper / Vite ports (same shape as `lotics app dev`).
1102
- const projectDir = toolArgs;
1103
- let port;
1104
- let vitePort;
1105
- for (const a of restArgs) {
1106
- const portMatch = /^--port=(\d+)$/.exec(a);
1107
- const vitePortMatch = /^--vite-port=(\d+)$/.exec(a);
1108
- if (portMatch)
1109
- port = Number(portMatch[1]);
1110
- else if (vitePortMatch)
1111
- vitePort = Number(vitePortMatch[1]);
1112
- }
1113
- await packageDev(client, { projectDir, port, vitePort });
1114
- return;
1018
+ // The high-traffic consumer verbs moved to the top level. Refuse the old
1019
+ // `lotics package <verb>` forms loudly with a redirect, never a silent no-op.
1020
+ const movedVerbs = {
1021
+ install: "lotics install <package_id>",
1022
+ uninstall: "lotics uninstall <app_id|pci_id>",
1023
+ upgrade: "lotics upgrade <app_id|pci_id|package_id>",
1024
+ "fleet-upgrade": "lotics upgrade <package_id>",
1025
+ };
1026
+ if (subcommand !== undefined && subcommand in movedVerbs) {
1027
+ console.error(`\`lotics package ${subcommand}\` moved to the top level — run \`${movedVerbs[subcommand]}\` instead.`);
1028
+ process.exit(1);
1115
1029
  }
1116
1030
  console.error(`Unknown package subcommand: ${subcommand}`);
1117
1031
  console.error("Run 'lotics package' for usage.");
@@ -1152,6 +1066,40 @@ async function main() {
1152
1066
  await appDeploy(client, { message });
1153
1067
  return;
1154
1068
  }
1069
+ if (subcommand === "publish") {
1070
+ // First-release a bespoke app AS a package. Positional app id (`.`/omitted →
1071
+ // the local app project manifest). Previews the minted aliases + findings,
1072
+ // then applies only with `--yes` (mirrors `app release`). `--rename old=new`
1073
+ // fixes an auto-minted alias before v1 freezes it; `-m` is an optional v1 changelog.
1074
+ await appPublish(client, {
1075
+ app_id: toolArgs,
1076
+ renames: flags.rename,
1077
+ changelog: flags.message,
1078
+ yes: flags.yes,
1079
+ });
1080
+ return;
1081
+ }
1082
+ if (subcommand === "release") {
1083
+ // -m <changelog> is REQUIRED — each release is a version row read back by
1084
+ // `lotics package show`, so a blank changelog loses the audit trail.
1085
+ if (!flags.message) {
1086
+ console.error("Usage: lotics app release [app_id|.] -m <changelog> [--yes]");
1087
+ console.error("Snapshots the origin app into its next package version (preview, then --yes to publish).");
1088
+ process.exit(1);
1089
+ }
1090
+ await appRelease(client, { app_id: toolArgs, changelog: flags.message, yes: flags.yes });
1091
+ return;
1092
+ }
1093
+ if (subcommand === "unpublish") {
1094
+ const id = toolArgs;
1095
+ if (!id) {
1096
+ console.error("Usage: lotics app unpublish <app_id|package_id> [--undo]");
1097
+ console.error("Takes a published package off the shelf; existing installations keep working.");
1098
+ process.exit(1);
1099
+ }
1100
+ await appUnpublish(client, { id, undo: flags.undo });
1101
+ return;
1102
+ }
1155
1103
  if (subcommand === "subdomain") {
1156
1104
  const newSubdomain = toolArgs;
1157
1105
  if (!newSubdomain) {