@lotics/cli 0.62.1 → 0.64.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 +2 -0
- package/dist/app_commands.d.ts +12 -1
- package/dist/app_commands.js +49 -1
- package/dist/app_commands.test.js +66 -1
- package/dist/cli.js +22 -4
- package/dist/client.d.ts +17 -0
- package/dist/client.js +10 -0
- package/dist/src/cli.js +67 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -156,6 +156,8 @@ Run `lotics xlsx` or `lotics docx` with no subcommand for the full list.
|
|
|
156
156
|
lotics app create "Sales Desk" # scaffold + deploy v1
|
|
157
157
|
lotics app pull app_... # bootstrap an existing app locally
|
|
158
158
|
lotics app deploy -m "Add quote drawer" # build + upload a new version
|
|
159
|
+
lotics app versions # deploy history: version, when, who, -m message (* = live)
|
|
160
|
+
lotics app versions app_... # ...for any app, without pulling it first
|
|
159
161
|
|
|
160
162
|
# Regenerate .lotics/* WITHOUT a deploy: the .d.ts type companions (always) +
|
|
161
163
|
# the runtime app_fields.ts (when authenticated) — F/OPT maps that address
|
package/dist/app_commands.d.ts
CHANGED
|
@@ -196,6 +196,17 @@ export declare function appSetSubdomain(client: LoticsClient, args: {
|
|
|
196
196
|
export declare function appRename(client: LoticsClient, args: {
|
|
197
197
|
name: string;
|
|
198
198
|
}): Promise<void>;
|
|
199
|
+
/**
|
|
200
|
+
* `lotics app versions [app_id]` — print the app's deploy history, newest
|
|
201
|
+
* first: version number, when, deployer, build status, and the `-m` message.
|
|
202
|
+
* With no `app_id`, reads it from the local `package.json` manifest (run inside
|
|
203
|
+
* the app dir); pass an explicit `app_id` to inspect any app without pulling it.
|
|
204
|
+
* The currently served version is marked with `*`. Admin-only server-side.
|
|
205
|
+
*/
|
|
206
|
+
export declare function appVersions(client: LoticsClient, args: {
|
|
207
|
+
app_id?: string;
|
|
208
|
+
limit?: number;
|
|
209
|
+
}): Promise<void>;
|
|
199
210
|
/**
|
|
200
211
|
* Where `lotics app pull <app_id>` lands when given NO explicit path. If the cwd
|
|
201
212
|
* IS already this app's own project (its manifest `app_id` matches), refresh in
|
|
@@ -224,7 +235,7 @@ export declare function undeclaredCapabilities(sourceText: string, declared: Rec
|
|
|
224
235
|
*/
|
|
225
236
|
export declare function appDeploy(client: LoticsClient, args: {
|
|
226
237
|
projectDir?: string;
|
|
227
|
-
message
|
|
238
|
+
message: string;
|
|
228
239
|
}): Promise<void>;
|
|
229
240
|
/**
|
|
230
241
|
* `lotics app dev [path] [--port=5174] [--vite-port=5173]`
|
package/dist/app_commands.js
CHANGED
|
@@ -630,6 +630,50 @@ export async function appRename(client, args) {
|
|
|
630
630
|
throw new Error(res.error);
|
|
631
631
|
console.error(`App renamed: "${args.name}" (${meta.app_id})`);
|
|
632
632
|
}
|
|
633
|
+
/**
|
|
634
|
+
* `lotics app versions [app_id]` — print the app's deploy history, newest
|
|
635
|
+
* first: version number, when, deployer, build status, and the `-m` message.
|
|
636
|
+
* With no `app_id`, reads it from the local `package.json` manifest (run inside
|
|
637
|
+
* the app dir); pass an explicit `app_id` to inspect any app without pulling it.
|
|
638
|
+
* The currently served version is marked with `*`. Admin-only server-side.
|
|
639
|
+
*/
|
|
640
|
+
export async function appVersions(client, args) {
|
|
641
|
+
const appId = args.app_id ?? readAppMeta(process.cwd()).app_id;
|
|
642
|
+
const { current_version_id, versions } = await client.listAppVersions(appId, {
|
|
643
|
+
limit: args.limit,
|
|
644
|
+
});
|
|
645
|
+
if (versions.length === 0) {
|
|
646
|
+
console.error(`No versions found for ${appId}.`);
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
const fmtWhen = (iso) => {
|
|
650
|
+
const d = new Date(iso);
|
|
651
|
+
if (Number.isNaN(d.getTime()))
|
|
652
|
+
return iso;
|
|
653
|
+
const p = (n) => String(n).padStart(2, "0");
|
|
654
|
+
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`;
|
|
655
|
+
};
|
|
656
|
+
const rows = versions.map((v) => ({
|
|
657
|
+
mark: v.id === current_version_id ? "*" : " ",
|
|
658
|
+
ver: `v${v.version}`,
|
|
659
|
+
when: fmtWhen(v.created_at),
|
|
660
|
+
by: v.created_by_name ?? v.created_by ?? "—",
|
|
661
|
+
status: v.build_status,
|
|
662
|
+
message: v.message ?? "",
|
|
663
|
+
}));
|
|
664
|
+
const colWidth = (key, header) => Math.max(header.length, ...rows.map((r) => r[key].length));
|
|
665
|
+
const wVer = colWidth("ver", "VER");
|
|
666
|
+
const wWhen = colWidth("when", "WHEN");
|
|
667
|
+
const wBy = colWidth("by", "DEPLOYER");
|
|
668
|
+
const wStatus = colWidth("status", "STATUS");
|
|
669
|
+
const pad = (s, n) => s.padEnd(n);
|
|
670
|
+
// Title → stderr (status); the table → stdout (data), so it stays pipeable.
|
|
671
|
+
console.error(`Deploy history — ${appId} (${versions.length} version${versions.length === 1 ? "" : "s"}, newest first; * = currently served)`);
|
|
672
|
+
console.log(` ${pad("VER", wVer)} ${pad("WHEN", wWhen)} ${pad("DEPLOYER", wBy)} ${pad("STATUS", wStatus)} MESSAGE`);
|
|
673
|
+
for (const r of rows) {
|
|
674
|
+
console.log(`${r.mark} ${pad(r.ver, wVer)} ${pad(r.when, wWhen)} ${pad(r.by, wBy)} ${pad(r.status, wStatus)} ${r.message}`.trimEnd());
|
|
675
|
+
}
|
|
676
|
+
}
|
|
633
677
|
/**
|
|
634
678
|
* Where `lotics app pull <app_id>` lands when given NO explicit path. If the cwd
|
|
635
679
|
* IS already this app's own project (its manifest `app_id` matches), refresh in
|
|
@@ -762,7 +806,11 @@ function readAppSourceText(projectDir) {
|
|
|
762
806
|
* both archives in R2, creates an app_versions row, and atomically advances
|
|
763
807
|
* the app's current_version pointer.
|
|
764
808
|
*/
|
|
765
|
-
export async function appDeploy(client,
|
|
809
|
+
export async function appDeploy(client,
|
|
810
|
+
// `message` is required — each deploy is a version row read back by
|
|
811
|
+
// `lotics app versions`; a blank message loses the audit trail. The CLI
|
|
812
|
+
// enforces non-empty at the dispatch; the type enforces it for every caller.
|
|
813
|
+
args) {
|
|
766
814
|
const projectDir = path.resolve(args.projectDir ?? process.cwd());
|
|
767
815
|
const meta = readAppMeta(projectDir);
|
|
768
816
|
// Pre-flight (GAP-29): a capability the code calls but the manifest doesn't
|
|
@@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
|
-
import { stampPulledManifest, undeclaredCapabilities, appDirName, defaultPullTarget, ensureWorkflowTsconfigExcludes, appCodegen, appUiLink, appWorkflowSet, appWorkflowPull, appExecuteWorkflow, writeWorkflowFile, writeWorkflowGlobals, stripWorkflowHeader, FALLBACK_ENVELOPE_PREFIX, FALLBACK_ENVELOPE_SUFFIX, } from "./app_commands.js";
|
|
5
|
+
import { stampPulledManifest, undeclaredCapabilities, appDirName, defaultPullTarget, ensureWorkflowTsconfigExcludes, appCodegen, appUiLink, appVersions, appWorkflowSet, appWorkflowPull, appExecuteWorkflow, writeWorkflowFile, writeWorkflowGlobals, stripWorkflowHeader, FALLBACK_ENVELOPE_PREFIX, FALLBACK_ENVELOPE_SUFFIX, } from "./app_commands.js";
|
|
6
6
|
/**
|
|
7
7
|
* `appPull` reads workflows from the live App row (server response), NOT from
|
|
8
8
|
* the manifest embedded in the extracted source archive. The frozen archive
|
|
@@ -724,3 +724,68 @@ describe("ensureWorkflowTsconfigExcludes", () => {
|
|
|
724
724
|
expect(fs.readFileSync(tsconfigPath(), "utf-8")).toBe("{ not json,, }");
|
|
725
725
|
});
|
|
726
726
|
});
|
|
727
|
+
describe("appVersions", () => {
|
|
728
|
+
let logLines;
|
|
729
|
+
let errLines;
|
|
730
|
+
beforeEach(() => {
|
|
731
|
+
logLines = [];
|
|
732
|
+
errLines = [];
|
|
733
|
+
vi.spyOn(console, "log").mockImplementation((...args) => {
|
|
734
|
+
logLines.push(String(args[0]));
|
|
735
|
+
});
|
|
736
|
+
vi.spyOn(console, "error").mockImplementation((...args) => {
|
|
737
|
+
errLines.push(String(args[0]));
|
|
738
|
+
});
|
|
739
|
+
});
|
|
740
|
+
afterEach(() => {
|
|
741
|
+
vi.restoreAllMocks();
|
|
742
|
+
});
|
|
743
|
+
const clientWith = (payload) => ({ listAppVersions: vi.fn().mockResolvedValue(payload) });
|
|
744
|
+
it("renders history newest-first, marks the current version, and prefers the deployer name", async () => {
|
|
745
|
+
const client = clientWith({
|
|
746
|
+
current_version_id: "apv_2",
|
|
747
|
+
versions: [
|
|
748
|
+
{
|
|
749
|
+
id: "apv_2",
|
|
750
|
+
version: 2,
|
|
751
|
+
message: "add receipt gate",
|
|
752
|
+
build_status: "success",
|
|
753
|
+
bundle_size_bytes: 1234,
|
|
754
|
+
created_at: "2026-06-24T03:04:41Z",
|
|
755
|
+
created_by: "mem_a",
|
|
756
|
+
created_by_name: "Minh Vu",
|
|
757
|
+
},
|
|
758
|
+
{
|
|
759
|
+
id: "apv_1",
|
|
760
|
+
version: 1,
|
|
761
|
+
message: "initial deploy",
|
|
762
|
+
build_status: "success",
|
|
763
|
+
bundle_size_bytes: 1000,
|
|
764
|
+
created_at: "2026-06-23T08:00:00Z",
|
|
765
|
+
created_by: "mem_b",
|
|
766
|
+
created_by_name: null,
|
|
767
|
+
},
|
|
768
|
+
],
|
|
769
|
+
});
|
|
770
|
+
// Explicit app_id → no manifest/package.json in cwd required.
|
|
771
|
+
await appVersions(client, { app_id: "app_x" });
|
|
772
|
+
// One header row + one row per version, all on stdout.
|
|
773
|
+
expect(logLines.length).toBe(3);
|
|
774
|
+
const v2Row = logLines.find((l) => l.includes("v2"));
|
|
775
|
+
const v1Row = logLines.find((l) => l.includes("v1"));
|
|
776
|
+
// Currently-served version is marked with `*`; the other is not.
|
|
777
|
+
expect(v2Row.startsWith("*")).toBe(true);
|
|
778
|
+
expect(v1Row.startsWith("*")).toBe(false);
|
|
779
|
+
// Messages render; the deployer name is preferred, falling back to the id.
|
|
780
|
+
expect(v2Row).toContain("add receipt gate");
|
|
781
|
+
expect(v2Row).toContain("Minh Vu");
|
|
782
|
+
expect(v1Row).toContain("initial deploy");
|
|
783
|
+
expect(v1Row).toContain("mem_b");
|
|
784
|
+
});
|
|
785
|
+
it("reports an empty history to stderr without printing a table", async () => {
|
|
786
|
+
const client = clientWith({ current_version_id: null, versions: [] });
|
|
787
|
+
await appVersions(client, { app_id: "app_empty" });
|
|
788
|
+
expect(logLines).toEqual([]);
|
|
789
|
+
expect(errLines.join("\n")).toContain("No versions found");
|
|
790
|
+
});
|
|
791
|
+
});
|
package/dist/cli.js
CHANGED
|
@@ -13,7 +13,7 @@ net.setDefaultAutoSelectFamilyAttemptTimeout(2000);
|
|
|
13
13
|
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
|
-
import { appCreate, appPull, appDeploy, appDev, appSetSubdomain, appRename, appCodegen, appExecuteWorkflow, appWorkflowSet, appWorkflowPull, appWorkflowCheck, appUiLink, } from "./app_commands.js";
|
|
16
|
+
import { appCreate, appPull, appDeploy, appDev, appSetSubdomain, appRename, appVersions, appCodegen, appExecuteWorkflow, appWorkflowSet, appWorkflowPull, appWorkflowCheck, appUiLink, } from "./app_commands.js";
|
|
17
17
|
import { parseArgs } from "./args.js";
|
|
18
18
|
import { ingestJsonArgs } from "./inputs.js";
|
|
19
19
|
import { runXlsxCommand } from "./xlsx.js";
|
|
@@ -71,9 +71,13 @@ COMMANDS
|
|
|
71
71
|
Download all files on a record file field
|
|
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
|
|
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)
|
|
75
76
|
(code + queries only — workflow bindings are
|
|
76
77
|
managed by set_app_workflow / remove_app_workflow)
|
|
78
|
+
lotics app versions [app_id] Show deploy history newest-first (version,
|
|
79
|
+
timestamp, deployer, build status, -m message;
|
|
80
|
+
* marks the currently served version)
|
|
77
81
|
lotics app codegen [path] Regenerate .lotics/* (types + field/option ids)
|
|
78
82
|
from the manifest + workspace schema — no deploy
|
|
79
83
|
lotics app workflow run <alias> '<json>' Execute a bound app workflow end-to-end
|
|
@@ -595,7 +599,8 @@ async function main() {
|
|
|
595
599
|
console.error("Usage:");
|
|
596
600
|
console.error(" lotics app create <name> [path] Scaffold a new app locally");
|
|
597
601
|
console.error(" lotics app pull <app_id> [path] Pull an existing app for local editing");
|
|
598
|
-
console.error(" lotics app deploy
|
|
602
|
+
console.error(" lotics app deploy -m <message> Build + upload the current directory (-m required)");
|
|
603
|
+
console.error(" lotics app versions [app_id] Show deploy history (version, when, who, message)");
|
|
599
604
|
console.error(" lotics app codegen [path] Regenerate .lotics/* (types + field ids) — no deploy");
|
|
600
605
|
console.error(" lotics app workflow run <alias> '<json>' Execute a bound app workflow end-to-end");
|
|
601
606
|
console.error(" lotics app workflow set <alias> Push the edited src/workflows/<alias>.ts body");
|
|
@@ -725,7 +730,14 @@ async function main() {
|
|
|
725
730
|
}
|
|
726
731
|
if (subcommand === "deploy") {
|
|
727
732
|
// The message is either `-m <message>` or a bare positional arg after `deploy`.
|
|
728
|
-
|
|
733
|
+
// Required: every deploy is a version row, and `lotics app versions` reads
|
|
734
|
+
// the message back — a blank one makes the deploy history useless.
|
|
735
|
+
const message = (flags.message ?? toolArgs)?.trim();
|
|
736
|
+
if (!message) {
|
|
737
|
+
console.error('Usage: lotics app deploy -m "<what changed + why>"');
|
|
738
|
+
console.error("A deploy message is required — it's the version's audit trail (see `lotics app versions`).");
|
|
739
|
+
process.exit(1);
|
|
740
|
+
}
|
|
729
741
|
await appDeploy(client, { message });
|
|
730
742
|
return;
|
|
731
743
|
}
|
|
@@ -749,6 +761,12 @@ async function main() {
|
|
|
749
761
|
await appRename(client, { name: newName });
|
|
750
762
|
return;
|
|
751
763
|
}
|
|
764
|
+
if (subcommand === "versions") {
|
|
765
|
+
// Optional positional app_id (inspect any app); else read the local manifest.
|
|
766
|
+
const appId = toolArgs || undefined;
|
|
767
|
+
await appVersions(client, { app_id: appId });
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
752
770
|
if (subcommand === "workflow") {
|
|
753
771
|
// `lotics app workflow <run|set|pull> …` — disambiguated subcommands so an
|
|
754
772
|
// alias can never collide with the verb. `toolArgs` is the verb; `restArgs`
|
package/dist/client.d.ts
CHANGED
|
@@ -199,6 +199,23 @@ export declare class LoticsClient {
|
|
|
199
199
|
build_status: string;
|
|
200
200
|
}>;
|
|
201
201
|
getAppVersionSourceUrl(app_id: string, version_id: string): Promise<string>;
|
|
202
|
+
/** Deploy history for an app — newest first. Backs `lotics app versions`. */
|
|
203
|
+
listAppVersions(app_id: string, opts?: {
|
|
204
|
+
limit?: number;
|
|
205
|
+
offset?: number;
|
|
206
|
+
}): Promise<{
|
|
207
|
+
current_version_id: string | null;
|
|
208
|
+
versions: Array<{
|
|
209
|
+
id: string;
|
|
210
|
+
version: number;
|
|
211
|
+
message: string | null;
|
|
212
|
+
build_status: string;
|
|
213
|
+
bundle_size_bytes: number | null;
|
|
214
|
+
created_at: string;
|
|
215
|
+
created_by: string | null;
|
|
216
|
+
created_by_name: string | null;
|
|
217
|
+
}>;
|
|
218
|
+
}>;
|
|
202
219
|
/**
|
|
203
220
|
* Run a named query declared in the app's manifest, scoped to the app's IAM
|
|
204
221
|
* principal. Mirrors POST /v1/apps/{app_id}/query.
|
package/dist/client.js
CHANGED
|
@@ -244,6 +244,16 @@ export class LoticsClient {
|
|
|
244
244
|
const result = await this.request("GET", `/v1/apps/${encodeURIComponent(app_id)}/versions/${encodeURIComponent(version_id)}/source`);
|
|
245
245
|
return result.url;
|
|
246
246
|
}
|
|
247
|
+
/** Deploy history for an app — newest first. Backs `lotics app versions`. */
|
|
248
|
+
async listAppVersions(app_id, opts) {
|
|
249
|
+
const qs = new URLSearchParams();
|
|
250
|
+
if (opts?.limit != null)
|
|
251
|
+
qs.set("limit", String(opts.limit));
|
|
252
|
+
if (opts?.offset != null)
|
|
253
|
+
qs.set("offset", String(opts.offset));
|
|
254
|
+
const suffix = qs.toString() ? `?${qs.toString()}` : "";
|
|
255
|
+
return this.request("GET", `/v1/apps/${encodeURIComponent(app_id)}/versions${suffix}`);
|
|
256
|
+
}
|
|
247
257
|
// ── App iframe RPC endpoints ──────────────────────────────────────────────
|
|
248
258
|
// These mirror the two ops handled by frontend/features/app_ui/app_iframe_host.tsx.
|
|
249
259
|
// The deployed iframe sends postMessage to the parent frontend, which calls
|
package/dist/src/cli.js
CHANGED
|
@@ -29803,6 +29803,14 @@ var LoticsClient = class {
|
|
|
29803
29803
|
);
|
|
29804
29804
|
return result.url;
|
|
29805
29805
|
}
|
|
29806
|
+
/** Deploy history for an app — newest first. Backs `lotics app versions`. */
|
|
29807
|
+
async listAppVersions(app_id, opts) {
|
|
29808
|
+
const qs = new URLSearchParams();
|
|
29809
|
+
if (opts?.limit != null) qs.set("limit", String(opts.limit));
|
|
29810
|
+
if (opts?.offset != null) qs.set("offset", String(opts.offset));
|
|
29811
|
+
const suffix = qs.toString() ? `?${qs.toString()}` : "";
|
|
29812
|
+
return this.request("GET", `/v1/apps/${encodeURIComponent(app_id)}/versions${suffix}`);
|
|
29813
|
+
}
|
|
29806
29814
|
// ── App iframe RPC endpoints ──────────────────────────────────────────────
|
|
29807
29815
|
// These mirror the two ops handled by frontend/features/app_ui/app_iframe_host.tsx.
|
|
29808
29816
|
// The deployed iframe sends postMessage to the parent frontend, which calls
|
|
@@ -32529,6 +32537,47 @@ async function appRename(client, args) {
|
|
|
32529
32537
|
if (res.error) throw new Error(res.error);
|
|
32530
32538
|
console.error(`App renamed: "${args.name}" (${meta.app_id})`);
|
|
32531
32539
|
}
|
|
32540
|
+
async function appVersions(client, args) {
|
|
32541
|
+
const appId = args.app_id ?? readAppMeta(process.cwd()).app_id;
|
|
32542
|
+
const { current_version_id, versions } = await client.listAppVersions(appId, {
|
|
32543
|
+
limit: args.limit
|
|
32544
|
+
});
|
|
32545
|
+
if (versions.length === 0) {
|
|
32546
|
+
console.error(`No versions found for ${appId}.`);
|
|
32547
|
+
return;
|
|
32548
|
+
}
|
|
32549
|
+
const fmtWhen = (iso) => {
|
|
32550
|
+
const d = new Date(iso);
|
|
32551
|
+
if (Number.isNaN(d.getTime())) return iso;
|
|
32552
|
+
const p = (n) => String(n).padStart(2, "0");
|
|
32553
|
+
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`;
|
|
32554
|
+
};
|
|
32555
|
+
const rows = versions.map((v) => ({
|
|
32556
|
+
mark: v.id === current_version_id ? "*" : " ",
|
|
32557
|
+
ver: `v${v.version}`,
|
|
32558
|
+
when: fmtWhen(v.created_at),
|
|
32559
|
+
by: v.created_by_name ?? v.created_by ?? "\u2014",
|
|
32560
|
+
status: v.build_status,
|
|
32561
|
+
message: v.message ?? ""
|
|
32562
|
+
}));
|
|
32563
|
+
const colWidth = (key, header) => Math.max(header.length, ...rows.map((r) => r[key].length));
|
|
32564
|
+
const wVer = colWidth("ver", "VER");
|
|
32565
|
+
const wWhen = colWidth("when", "WHEN");
|
|
32566
|
+
const wBy = colWidth("by", "DEPLOYER");
|
|
32567
|
+
const wStatus = colWidth("status", "STATUS");
|
|
32568
|
+
const pad = (s, n) => s.padEnd(n);
|
|
32569
|
+
console.error(
|
|
32570
|
+
`Deploy history \u2014 ${appId} (${versions.length} version${versions.length === 1 ? "" : "s"}, newest first; * = currently served)`
|
|
32571
|
+
);
|
|
32572
|
+
console.log(
|
|
32573
|
+
` ${pad("VER", wVer)} ${pad("WHEN", wWhen)} ${pad("DEPLOYER", wBy)} ${pad("STATUS", wStatus)} MESSAGE`
|
|
32574
|
+
);
|
|
32575
|
+
for (const r of rows) {
|
|
32576
|
+
console.log(
|
|
32577
|
+
`${r.mark} ${pad(r.ver, wVer)} ${pad(r.when, wWhen)} ${pad(r.by, wBy)} ${pad(r.status, wStatus)} ${r.message}`.trimEnd()
|
|
32578
|
+
);
|
|
32579
|
+
}
|
|
32580
|
+
}
|
|
32532
32581
|
function defaultPullTarget(appId, appName) {
|
|
32533
32582
|
const cwdPkgPath = path5.join(process.cwd(), "package.json");
|
|
32534
32583
|
if (fs4.existsSync(cwdPkgPath)) {
|
|
@@ -49154,9 +49203,13 @@ COMMANDS
|
|
|
49154
49203
|
Download all files on a record file field
|
|
49155
49204
|
lotics app create <name> [path] Create a new custom-code app + scaffold locally
|
|
49156
49205
|
lotics app pull <app_id> [path] Bootstrap full local env (source + npm install + types)
|
|
49157
|
-
lotics app deploy
|
|
49206
|
+
lotics app deploy -m <message> Build + upload current dir as a new version
|
|
49207
|
+
(-m is required \u2014 it's the version's audit trail)
|
|
49158
49208
|
(code + queries only \u2014 workflow bindings are
|
|
49159
49209
|
managed by set_app_workflow / remove_app_workflow)
|
|
49210
|
+
lotics app versions [app_id] Show deploy history newest-first (version,
|
|
49211
|
+
timestamp, deployer, build status, -m message;
|
|
49212
|
+
* marks the currently served version)
|
|
49160
49213
|
lotics app codegen [path] Regenerate .lotics/* (types + field/option ids)
|
|
49161
49214
|
from the manifest + workspace schema \u2014 no deploy
|
|
49162
49215
|
lotics app workflow run <alias> '<json>' Execute a bound app workflow end-to-end
|
|
@@ -49631,7 +49684,8 @@ async function main() {
|
|
|
49631
49684
|
console.error("Usage:");
|
|
49632
49685
|
console.error(" lotics app create <name> [path] Scaffold a new app locally");
|
|
49633
49686
|
console.error(" lotics app pull <app_id> [path] Pull an existing app for local editing");
|
|
49634
|
-
console.error(" lotics app deploy
|
|
49687
|
+
console.error(" lotics app deploy -m <message> Build + upload the current directory (-m required)");
|
|
49688
|
+
console.error(" lotics app versions [app_id] Show deploy history (version, when, who, message)");
|
|
49635
49689
|
console.error(" lotics app codegen [path] Regenerate .lotics/* (types + field ids) \u2014 no deploy");
|
|
49636
49690
|
console.error(" lotics app workflow run <alias> '<json>' Execute a bound app workflow end-to-end");
|
|
49637
49691
|
console.error(" lotics app workflow set <alias> Push the edited src/workflows/<alias>.ts body");
|
|
@@ -49754,7 +49808,12 @@ Available workspaces:`);
|
|
|
49754
49808
|
return;
|
|
49755
49809
|
}
|
|
49756
49810
|
if (subcommand === "deploy") {
|
|
49757
|
-
const message = flags.message ?? toolArgs;
|
|
49811
|
+
const message = (flags.message ?? toolArgs)?.trim();
|
|
49812
|
+
if (!message) {
|
|
49813
|
+
console.error('Usage: lotics app deploy -m "<what changed + why>"');
|
|
49814
|
+
console.error("A deploy message is required \u2014 it's the version's audit trail (see `lotics app versions`).");
|
|
49815
|
+
process.exit(1);
|
|
49816
|
+
}
|
|
49758
49817
|
await appDeploy(client, { message });
|
|
49759
49818
|
return;
|
|
49760
49819
|
}
|
|
@@ -49778,6 +49837,11 @@ Available workspaces:`);
|
|
|
49778
49837
|
await appRename(client, { name: newName });
|
|
49779
49838
|
return;
|
|
49780
49839
|
}
|
|
49840
|
+
if (subcommand === "versions") {
|
|
49841
|
+
const appId = toolArgs || void 0;
|
|
49842
|
+
await appVersions(client, { app_id: appId });
|
|
49843
|
+
return;
|
|
49844
|
+
}
|
|
49781
49845
|
if (subcommand === "workflow") {
|
|
49782
49846
|
const action = toolArgs;
|
|
49783
49847
|
const workflowUsage = () => {
|