@lotics/cli 0.181.1 → 0.183.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 +7 -5
- package/dist/src/cli.js +132 -256
- package/dist/src/client.d.ts +115 -170
- package/dist/src/client.js +59 -79
- package/docs/cli_reference.md +4 -4
- package/docs/knowledge_docs.md +1 -1
- package/package.json +1 -1
package/dist/src/cli.js
CHANGED
|
@@ -44516,14 +44516,13 @@ var LoticsClient = class {
|
|
|
44516
44516
|
return this.request("POST", "/v1/apps", body);
|
|
44517
44517
|
}
|
|
44518
44518
|
/**
|
|
44519
|
-
*
|
|
44520
|
-
* unpublish`
|
|
44521
|
-
*
|
|
44522
|
-
*
|
|
44523
|
-
* admin-only.
|
|
44519
|
+
* Take a starter off the shelf, or `undo` to put it back (backs `opctl
|
|
44520
|
+
* starter unpublish`). It hides from non-owning orgs and can no longer be
|
|
44521
|
+
* copied; copies already made are unaffected — they never linked back.
|
|
44522
|
+
* Owner-org admin-only.
|
|
44524
44523
|
*/
|
|
44525
|
-
async
|
|
44526
|
-
return this.request("POST", `/v1/
|
|
44524
|
+
async unpublishStarter(starter_id, body) {
|
|
44525
|
+
return this.request("POST", `/v1/starters/${encodeURIComponent(starter_id)}/unpublish`, body);
|
|
44527
44526
|
}
|
|
44528
44527
|
/**
|
|
44529
44528
|
* Edit a starter's registry listing — the name and description a stranger
|
|
@@ -44532,13 +44531,12 @@ var LoticsClient = class {
|
|
|
44532
44531
|
* A version is an immutable snapshot; the listing is not. Omit a field to
|
|
44533
44532
|
* leave it, pass `description: null` to clear it. Owner-org admin-only.
|
|
44534
44533
|
*/
|
|
44535
|
-
async
|
|
44536
|
-
return this.request("POST", `/v1/
|
|
44534
|
+
async editStarterListing(starter_id, body) {
|
|
44535
|
+
return this.request("POST", `/v1/starters/${encodeURIComponent(starter_id)}/listing`, body);
|
|
44537
44536
|
}
|
|
44538
|
-
// ---
|
|
44539
|
-
// Authoring is server-side
|
|
44540
|
-
//
|
|
44541
|
-
// client-side create-package / upload-bundle path.
|
|
44537
|
+
// --- Starters (registry reads + copies) ---
|
|
44538
|
+
// Authoring is server-side, through the publish job (`requestStarterPublish`).
|
|
44539
|
+
// There is no client-side create-starter / upload-bundle path.
|
|
44542
44540
|
/**
|
|
44543
44541
|
* The starters this organization can copy — Lotics-reviewed ones plus its own,
|
|
44544
44542
|
* never a catalogue of everything published. The server returns exactly what
|
|
@@ -44551,10 +44549,9 @@ var LoticsClient = class {
|
|
|
44551
44549
|
* Copy a starter into the current workspace.
|
|
44552
44550
|
*
|
|
44553
44551
|
* Server-side this scaffolds the schema, creates the templates, docs and
|
|
44554
|
-
* sample records, creates
|
|
44555
|
-
*
|
|
44556
|
-
*
|
|
44557
|
-
* presigned GET of that source, and finishing the job is the caller's half.
|
|
44552
|
+
* sample records, creates every app the starter carries and deploys each
|
|
44553
|
+
* from its prebuilt dist — no build anywhere. `apps` reports each deploy;
|
|
44554
|
+
* one that failed carries its `error` and the copy is complete around it.
|
|
44558
44555
|
* Admin-only.
|
|
44559
44556
|
*/
|
|
44560
44557
|
async instantiateStarter(starter_id, body) {
|
|
@@ -44598,12 +44595,12 @@ var LoticsClient = class {
|
|
|
44598
44595
|
* Fetch a registry starter's metadata (`latest_version` and the Lotics-backed
|
|
44599
44596
|
* `is_official` trust badge). Admin-only; cross-tenant by id.
|
|
44600
44597
|
*/
|
|
44601
|
-
async
|
|
44602
|
-
return this.request("GET", `/v1/
|
|
44598
|
+
async getStarter(starter_id) {
|
|
44599
|
+
return this.request("GET", `/v1/starters/${encodeURIComponent(starter_id)}`);
|
|
44603
44600
|
}
|
|
44604
|
-
/** Version history newest-first (no contract payloads) — backs `opctl
|
|
44605
|
-
async
|
|
44606
|
-
return this.request("GET", `/v1/
|
|
44601
|
+
/** Version history newest-first (no contract payloads) — backs `opctl starter show`. Admin-only. */
|
|
44602
|
+
async listStarterVersions(starter_id) {
|
|
44603
|
+
return this.request("GET", `/v1/starters/${encodeURIComponent(starter_id)}/versions`);
|
|
44607
44604
|
}
|
|
44608
44605
|
/**
|
|
44609
44606
|
* Workspace-wide dangling-reference sweep — active app/workflow artifacts
|
|
@@ -44613,74 +44610,45 @@ var LoticsClient = class {
|
|
|
44613
44610
|
async getWorkspaceDanglingReferences() {
|
|
44614
44611
|
return this.request("GET", "/v1/workspaces/dangling-references");
|
|
44615
44612
|
}
|
|
44613
|
+
// --- Starter publishing (the authoring verbs; copying is `instantiateStarter`) ---
|
|
44616
44614
|
/**
|
|
44617
|
-
* Preview a
|
|
44618
|
-
*
|
|
44619
|
-
*
|
|
44620
|
-
*
|
|
44621
|
-
*
|
|
44622
|
-
*
|
|
44623
|
-
*
|
|
44624
|
-
* owning-org only.
|
|
44615
|
+
* Preview publishing a set of this workspace's apps as one starter version —
|
|
44616
|
+
* the GET behind `opctl starter publish` (no `--yes`). The server runs the
|
|
44617
|
+
* same extraction the publish runs and reports which starter it would
|
|
44618
|
+
* release into (null: it would mint one), the next version, the aliases a
|
|
44619
|
+
* first publish can still rename, the diff against the current version, the
|
|
44620
|
+
* knowledge delta, and the findings (an `error` blocks the publish). No
|
|
44621
|
+
* writes. Admin-only.
|
|
44625
44622
|
*/
|
|
44626
|
-
async
|
|
44623
|
+
async previewStarterPublish(opts) {
|
|
44627
44624
|
const params = new URLSearchParams();
|
|
44628
|
-
|
|
44629
|
-
|
|
44630
|
-
|
|
44631
|
-
|
|
44632
|
-
|
|
44633
|
-
|
|
44634
|
-
|
|
44635
|
-
);
|
|
44625
|
+
params.set("app_ids", opts.app_ids.join(","));
|
|
44626
|
+
if (opts.knowledge_doc_ids !== void 0) params.set("knowledge_doc_ids", opts.knowledge_doc_ids.join(","));
|
|
44627
|
+
if (opts.renames !== void 0 && opts.renames.length > 0) params.set("renames", JSON.stringify(opts.renames));
|
|
44628
|
+
if (opts.name !== void 0) params.set("name", opts.name);
|
|
44629
|
+
if (opts.description !== void 0) params.set("description", opts.description);
|
|
44630
|
+
if (opts.icon !== void 0) params.set("icon", opts.icon);
|
|
44631
|
+
if (opts.color !== void 0) params.set("color", opts.color);
|
|
44632
|
+
return this.request("GET", `/v1/starters/publish-preview?${params.toString()}`);
|
|
44636
44633
|
}
|
|
44637
44634
|
/**
|
|
44638
|
-
*
|
|
44639
|
-
*
|
|
44640
|
-
*
|
|
44641
|
-
*
|
|
44642
|
-
*
|
|
44643
|
-
* the
|
|
44644
|
-
* missing/archived declared doc is a 400, a foreign-package doc a 409. Admin,
|
|
44645
|
-
* owning-org only. Backs `opctl app release --yes`.
|
|
44635
|
+
* Publish this workspace's apps as a starter version — a JOB, because every
|
|
44636
|
+
* app is built once against sentinel field keys and eleven builds outlast a
|
|
44637
|
+
* request. Everything a request can refuse is refused here with nothing
|
|
44638
|
+
* written: a blocking finding or another publish still running for this
|
|
44639
|
+
* org (409), a missing deploy or a bad declaration (400). The response is
|
|
44640
|
+
* the job to poll with `getStarterPublish`. Admin-only.
|
|
44646
44641
|
*/
|
|
44647
|
-
async
|
|
44648
|
-
|
|
44649
|
-
|
|
44650
|
-
|
|
44651
|
-
|
|
44652
|
-
|
|
44653
|
-
* server runs the same fresh-alias extract + `src/` scan the apply runs
|
|
44654
|
-
* (through any `renames`) and returns the package name it would mint, the
|
|
44655
|
-
* auto-minted RENAMABLE aliases (the exact `--rename` keys), and the extract
|
|
44656
|
-
* findings (an `error` blocks the apply). No writes. Admin-only.
|
|
44657
|
-
*/
|
|
44658
|
-
async previewPublishAppPackage(app_id, opts = {}) {
|
|
44659
|
-
const params = new URLSearchParams();
|
|
44660
|
-
if (opts.knowledge !== void 0 && opts.knowledge.length > 0) {
|
|
44661
|
-
params.set("knowledge", JSON.stringify(opts.knowledge));
|
|
44662
|
-
}
|
|
44663
|
-
if (opts.renames !== void 0 && opts.renames.length > 0) {
|
|
44664
|
-
params.set("renames", JSON.stringify(opts.renames));
|
|
44665
|
-
}
|
|
44666
|
-
const query = params.toString();
|
|
44667
|
-
return this.request(
|
|
44668
|
-
"GET",
|
|
44669
|
-
`/v1/apps/${encodeURIComponent(app_id)}/package-publish${query ? `?${query}` : ""}`
|
|
44670
|
-
);
|
|
44642
|
+
async requestStarterPublish(body) {
|
|
44643
|
+
const { color, ...rest2 } = body;
|
|
44644
|
+
return this.request("POST", "/v1/starters/publishes", {
|
|
44645
|
+
...rest2,
|
|
44646
|
+
...color !== void 0 ? { theme: { color } } : {}
|
|
44647
|
+
});
|
|
44671
44648
|
}
|
|
44672
|
-
/**
|
|
44673
|
-
|
|
44674
|
-
|
|
44675
|
-
* alias-keyed contract from the app (fresh aliases; `renames` fixes them before
|
|
44676
|
-
* v1 freezes), creates the registry package (name/description from the app),
|
|
44677
|
-
* publishes v1 from the app's deployed source + dist, and pins the origin as
|
|
44678
|
-
* installation #1. Error findings from extract surface as a 409. An
|
|
44679
|
-
* already-linked app must use `releasePackage` instead. Admin-only. Backs
|
|
44680
|
-
* `opctl app publish <app_id>`.
|
|
44681
|
-
*/
|
|
44682
|
-
async publishAppAsPackage(app_id, body) {
|
|
44683
|
-
return this.request("POST", `/v1/apps/${encodeURIComponent(app_id)}/package-publish`, body);
|
|
44649
|
+
/** The state of a publish: which app is building, and the version once every dist is in. */
|
|
44650
|
+
async getStarterPublish(publish_id) {
|
|
44651
|
+
return this.request("GET", `/v1/starters/publishes/${encodeURIComponent(publish_id)}`);
|
|
44684
44652
|
}
|
|
44685
44653
|
/**
|
|
44686
44654
|
* Resolve the display name + fields (incl. select options) of the given tables
|
|
@@ -45711,7 +45679,7 @@ function resultSideEffects(result) {
|
|
|
45711
45679
|
}
|
|
45712
45680
|
|
|
45713
45681
|
// src/version.ts
|
|
45714
|
-
var VERSION = "0.
|
|
45682
|
+
var VERSION = "0.183.0";
|
|
45715
45683
|
|
|
45716
45684
|
// src/timezone.ts
|
|
45717
45685
|
function machineTimezone() {
|
|
@@ -45881,11 +45849,11 @@ var COMMANDS = [
|
|
|
45881
45849
|
{
|
|
45882
45850
|
verbs: ["setup"],
|
|
45883
45851
|
help: [
|
|
45884
|
-
" lotics setup <starter_id>
|
|
45852
|
+
" lotics setup <starter_id> --email <you@co.com>",
|
|
45885
45853
|
" First run, one command: create an account if this",
|
|
45886
45854
|
" machine has none, then copy the starter into its",
|
|
45887
45855
|
" workspace \u2014 schema, templates, knowledge docs,",
|
|
45888
|
-
" sample records,
|
|
45856
|
+
" sample records, its apps deployed \u2014 and print a",
|
|
45889
45857
|
" one-time sign-in link. Drop --email to copy into",
|
|
45890
45858
|
" the account you already have. --json prints one",
|
|
45891
45859
|
" object and nothing else"
|
|
@@ -45922,13 +45890,13 @@ var COMMANDS = [
|
|
|
45922
45890
|
" lotics starter list Starters you can copy into this workspace. Works with",
|
|
45923
45891
|
" NO account \u2014 it then lists the published shelf, so you",
|
|
45924
45892
|
" can decide whether to copy one or build before signing up",
|
|
45925
|
-
" lotics starter show <starter_id>
|
|
45926
|
-
"
|
|
45927
|
-
"
|
|
45928
|
-
" sample records and
|
|
45929
|
-
" everything it creates is yours outright, with
|
|
45930
|
-
" link back and nothing to upgrade.
|
|
45931
|
-
"
|
|
45893
|
+
" lotics starter show <starter_id> Its name, description, current version, tile and trust",
|
|
45894
|
+
" standing \u2014 read it before copying one you did not publish",
|
|
45895
|
+
" lotics starter init <starter_id> Copy it in \u2014 schema, templates, knowledge docs,",
|
|
45896
|
+
" sample records and its apps, deployed on Lotics. A",
|
|
45897
|
+
" COPY: everything it creates is yours outright, with",
|
|
45898
|
+
" no link back and nothing to upgrade. Nothing lands",
|
|
45899
|
+
" on this machine; lotics app pull <app_id> edits one.",
|
|
45932
45900
|
" --no-sample-data skips the sample records;",
|
|
45933
45901
|
" --adopt allows a workspace that is not empty",
|
|
45934
45902
|
" lotics starter fixtures capture --entity <alias> --limit <n>",
|
|
@@ -45946,7 +45914,7 @@ var COMMANDS = [
|
|
|
45946
45914
|
{
|
|
45947
45915
|
verbs: ["docs"],
|
|
45948
45916
|
help: [
|
|
45949
|
-
" lotics docs List the reference docs
|
|
45917
|
+
" lotics docs List the reference docs the npm packages in node_modules ship",
|
|
45950
45918
|
" lotics docs <area> Print one (e.g. 'lotics docs ai')"
|
|
45951
45919
|
]
|
|
45952
45920
|
},
|
|
@@ -46495,10 +46463,10 @@ export default defineConfig({
|
|
|
46495
46463
|
// openly at the asset layer \u2014 the map would expose the very source the gate
|
|
46496
46464
|
// exists to protect. Source maps aren't needed to run the app.
|
|
46497
46465
|
sourcemap: false,
|
|
46498
|
-
//
|
|
46499
|
-
//
|
|
46500
|
-
//
|
|
46501
|
-
//
|
|
46466
|
+
// Vite's default 'modules' baseline is es2020, which is older than the
|
|
46467
|
+
// browsers a Lotics app is served to and older than what dev already
|
|
46468
|
+
// transforms at (esnext). Pinned so the prod build does not silently
|
|
46469
|
+
// down-level past a feature the dev server accepted.
|
|
46502
46470
|
target: "es2022",
|
|
46503
46471
|
},
|
|
46504
46472
|
server: {
|
|
@@ -46521,10 +46489,10 @@ export default defineConfig({
|
|
|
46521
46489
|
},
|
|
46522
46490
|
test: {
|
|
46523
46491
|
environment: "jsdom",
|
|
46524
|
-
// Global test setup, run before every test file
|
|
46525
|
-
//
|
|
46526
|
-
//
|
|
46527
|
-
// every test
|
|
46492
|
+
// Global test setup, run before every test file. Ships empty \u2014 the slot is
|
|
46493
|
+
// the author's (custom matchers, a fetch stub, timezone pinning). It must
|
|
46494
|
+
// EXIST either way: setupFiles naming a missing file makes vitest fail to
|
|
46495
|
+
// load every test. See vitest.setup.ts.
|
|
46528
46496
|
setupFiles: ["./${VITEST_SETUP_FILENAME}"],
|
|
46529
46497
|
// RN packages ship Flow (\`import typeof\`) in their native source, reached
|
|
46530
46498
|
// transitively by RN-Web components (pickers, calendars, anything touching
|
|
@@ -46892,18 +46860,12 @@ declare module "*.css";
|
|
|
46892
46860
|
{
|
|
46893
46861
|
path: "src/harness.test.ts",
|
|
46894
46862
|
content: `import { describe, test, expect } from "vitest";
|
|
46895
|
-
import { getAppBinding } from "@lotics/app-sdk";
|
|
46896
46863
|
|
|
46897
46864
|
/**
|
|
46898
46865
|
* The one test the scaffold seeds, and it is about the TEST SETUP rather than
|
|
46899
46866
|
* about your app \u2014 so editing src/App.tsx can never break it, and \`npm test\`
|
|
46900
|
-
* never has to be green over zero tests.
|
|
46901
|
-
*
|
|
46902
|
-
* What it pins: tests run under jsdom, and vitest.setup.ts's echo stub answers
|
|
46903
|
-
* for \`getAppBinding\`. That second one is the whole reason the setup file
|
|
46904
|
-
* exists: a package-linked app's generated .lotics/app_fields.ts awaits it while
|
|
46905
|
-
* a test file is still COLLECTING, so without the stub every test fails before
|
|
46906
|
-
* any of them runs \u2014 including pure logic tests that never touch a field id.
|
|
46867
|
+
* never has to be green over zero tests. It imports nothing of yours and
|
|
46868
|
+
* nothing generated, so it passes on a fresh clone before any codegen has run.
|
|
46907
46869
|
*
|
|
46908
46870
|
* Delete it once you have tests of your own, or keep it; it costs nothing.
|
|
46909
46871
|
*/
|
|
@@ -46911,13 +46873,6 @@ describe("test harness", () => {
|
|
|
46911
46873
|
test("runs in a DOM", () => {
|
|
46912
46874
|
expect(typeof document).toBe("object");
|
|
46913
46875
|
});
|
|
46914
|
-
|
|
46915
|
-
test("resolves the app binding from the setup stub, so the app graph collects", async () => {
|
|
46916
|
-
const binding = await getAppBinding();
|
|
46917
|
-
// The echo stub answers any alias with a self-identifying ':test:' id \u2014 one
|
|
46918
|
-
// that can never be mistaken for a real fld_\u2026 from the workspace.
|
|
46919
|
-
expect(binding.fields["anything.at.all"]).toContain(":test:");
|
|
46920
|
-
});
|
|
46921
46876
|
});
|
|
46922
46877
|
`
|
|
46923
46878
|
},
|
|
@@ -74054,7 +74009,6 @@ function parseArgs(argv) {
|
|
|
74054
74009
|
// src/starter_commands.ts
|
|
74055
74010
|
import fs9 from "node:fs";
|
|
74056
74011
|
import path10 from "node:path";
|
|
74057
|
-
import { tmpdir as tmpdir2 } from "node:os";
|
|
74058
74012
|
function printStarterRows(rows) {
|
|
74059
74013
|
const width = Math.max(...rows.map((row) => row.id.length));
|
|
74060
74014
|
for (const row of rows) {
|
|
@@ -74117,7 +74071,7 @@ async function starterList(client) {
|
|
|
74117
74071
|
description: s.description,
|
|
74118
74072
|
tags: [
|
|
74119
74073
|
s.is_official ? "official" : null,
|
|
74120
|
-
s.owned_by_caller
|
|
74074
|
+
s.owned_by_caller ? "yours" : null
|
|
74121
74075
|
].filter((t) => t !== null)
|
|
74122
74076
|
}))
|
|
74123
74077
|
);
|
|
@@ -74125,19 +74079,17 @@ async function starterList(client) {
|
|
|
74125
74079
|
lotics starter init <starter_id> Copy one into this workspace`);
|
|
74126
74080
|
}
|
|
74127
74081
|
async function starterShow(client, starter_id) {
|
|
74128
|
-
const starter = await client.
|
|
74082
|
+
const starter = await client.getStarter(starter_id);
|
|
74129
74083
|
console.log(`${starter.name} (${starter.id})`);
|
|
74130
74084
|
if (starter.description) console.log(starter.description);
|
|
74131
74085
|
console.log(
|
|
74132
74086
|
`
|
|
74133
|
-
version
|
|
74134
|
-
trust
|
|
74087
|
+
version ${starter.latest_version}
|
|
74088
|
+
trust ${starter.is_official ? "official \u2014 reviewed by Lotics" : starter.owned_by_caller ? "your organization's own" : "not copyable from this organization"}`
|
|
74135
74089
|
);
|
|
74136
|
-
|
|
74137
|
-
console.log(`icon ${starter.icon ?? "none \u2014 copies show a generic tile"}`);
|
|
74138
|
-
}
|
|
74090
|
+
console.log(`icon ${starter.icon ?? "none \u2014 the shelf shows a generic tile"}`);
|
|
74139
74091
|
if (starter.retired_at !== null) {
|
|
74140
|
-
console.log(`
|
|
74092
|
+
console.log(`unpublished ${starter.retired_at} \u2014 can no longer be copied`);
|
|
74141
74093
|
}
|
|
74142
74094
|
}
|
|
74143
74095
|
function reportKnowledgeWarnings(warnings) {
|
|
@@ -74151,130 +74103,58 @@ function reportKnowledgeWarnings(warnings) {
|
|
|
74151
74103
|
answering from nowhere. Create them with those names, or edit the agent.`
|
|
74152
74104
|
);
|
|
74153
74105
|
}
|
|
74154
|
-
function instantiateBody(args) {
|
|
74155
|
-
return {
|
|
74156
|
-
...args.noSampleData === true ? { no_sample_data: true } : {},
|
|
74157
|
-
...args.adopt === true ? { adopt: true } : {},
|
|
74158
|
-
build_on_server: true
|
|
74159
|
-
};
|
|
74160
|
-
}
|
|
74161
74106
|
async function starterInit(client, args) {
|
|
74162
|
-
const starter = await client.
|
|
74163
|
-
const targetPath = path10.resolve(args.targetPath ?? appDirName(starter.name));
|
|
74164
|
-
if (fs9.existsSync(targetPath) && fs9.readdirSync(targetPath).length > 0) {
|
|
74165
|
-
throw new Error(`Target directory ${targetPath} is not empty. Pass an empty path and re-run.`);
|
|
74166
|
-
}
|
|
74107
|
+
const starter = await client.getStarter(args.starter_id);
|
|
74167
74108
|
note(
|
|
74168
74109
|
`Copying ${starter.name}${starter.is_official ? " (official)" : ""} into this workspace\u2026`
|
|
74169
74110
|
);
|
|
74170
|
-
const result = await client.instantiateStarter(args.starter_id,
|
|
74111
|
+
const result = await client.instantiateStarter(args.starter_id, {
|
|
74112
|
+
...args.noSampleData === true ? { no_sample_data: true } : {},
|
|
74113
|
+
...args.adopt === true ? { adopt: true } : {}
|
|
74114
|
+
});
|
|
74171
74115
|
const tables = Object.keys(result.binding.entities ?? {}).sort();
|
|
74172
74116
|
const knowledgeDocs = Object.keys(result.binding.knowledge ?? {}).sort();
|
|
74173
74117
|
const templates = Object.keys(result.binding.templates ?? {}).sort();
|
|
74174
74118
|
const records = Object.values(result.sample_record_ids).reduce((n, ids) => n + ids.length, 0);
|
|
74175
|
-
const created = {
|
|
74176
|
-
|
|
74177
|
-
|
|
74178
|
-
|
|
74179
|
-
|
|
74180
|
-
|
|
74119
|
+
const created = { tables, templates, knowledge_docs: knowledgeDocs, sample_records: records };
|
|
74120
|
+
const apps = result.apps.map((app) => ({
|
|
74121
|
+
alias: app.alias,
|
|
74122
|
+
app_id: app.app_id,
|
|
74123
|
+
name: app.name,
|
|
74124
|
+
version_number: app.deployed?.version_number ?? null,
|
|
74125
|
+
error: app.error
|
|
74126
|
+
}));
|
|
74181
74127
|
note(
|
|
74182
|
-
` Created ${tables.length} table${tables.length === 1 ? "" : "s"}, ${templates.length} template${templates.length === 1 ? "" : "s"}, ${knowledgeDocs.length} knowledge doc${knowledgeDocs.length === 1 ? "" : "s"}, ${records} sample record${records === 1 ? "" : "s"}.`
|
|
74128
|
+
` Created ${tables.length} table${tables.length === 1 ? "" : "s"}, ${templates.length} template${templates.length === 1 ? "" : "s"}, ${knowledgeDocs.length} knowledge doc${knowledgeDocs.length === 1 ? "" : "s"}, ${records} sample record${records === 1 ? "" : "s"}, ${apps.length} app${apps.length === 1 ? "" : "s"}.`
|
|
74183
74129
|
);
|
|
74184
74130
|
reportKnowledgeWarnings(result.knowledge_warnings);
|
|
74185
|
-
if (
|
|
74186
|
-
note(`
|
|
74187
|
-
Done \u2014 these are yours now, with no link back to the starter.`);
|
|
74188
|
-
noteCopiedContent(templates, knowledgeDocs);
|
|
74189
|
-
return {
|
|
74190
|
-
starter_id: args.starter_id,
|
|
74191
|
-
starter_name: starter.name,
|
|
74192
|
-
version: result.version,
|
|
74193
|
-
app_id: null,
|
|
74194
|
-
project_dir: null,
|
|
74195
|
-
created,
|
|
74196
|
-
signin_url: null
|
|
74197
|
-
};
|
|
74198
|
-
}
|
|
74199
|
-
fs9.mkdirSync(targetPath, { recursive: true });
|
|
74200
|
-
note(`Downloading source\u2026`);
|
|
74201
|
-
const bundleFile = path10.join(tmpdir2(), `lotics-starter-${args.starter_id}-${process.pid}.tar.gz`);
|
|
74202
|
-
const wrapperDir = fs9.mkdtempSync(path10.join(tmpdir2(), `lotics-starter-`));
|
|
74203
|
-
try {
|
|
74204
|
-
await downloadToFile(result.bundle_url, bundleFile);
|
|
74205
|
-
await runTar(["-xzf", bundleFile, "-C", wrapperDir], wrapperDir);
|
|
74206
|
-
const sourceArchive = path10.join(wrapperDir, "source.tar.gz");
|
|
74207
|
-
if (!fs9.existsSync(sourceArchive)) {
|
|
74208
|
-
throw new Error(
|
|
74209
|
-
`Starter bundle carried no source.tar.gz \u2014 the app (${result.app_id}) exists but has no code. Report this to the starter's publisher.`
|
|
74210
|
-
);
|
|
74211
|
-
}
|
|
74212
|
-
await runTar(["-xzf", sourceArchive, "-C", targetPath], targetPath);
|
|
74213
|
-
} finally {
|
|
74214
|
-
if (fs9.existsSync(bundleFile)) fs9.unlinkSync(bundleFile);
|
|
74215
|
-
fs9.rmSync(wrapperDir, { recursive: true, force: true });
|
|
74216
|
-
}
|
|
74217
|
-
for (const dir of ["knowledge", "fixtures"]) {
|
|
74218
|
-
fs9.rmSync(path10.join(targetPath, dir), { recursive: true, force: true });
|
|
74219
|
-
}
|
|
74220
|
-
const app = await client.getApp(result.app_id);
|
|
74221
|
-
const kept = [];
|
|
74222
|
-
await hydrateAppProject(client, targetPath, app, {
|
|
74223
|
-
// Nothing is deployed yet; the build below is what mints v1.
|
|
74224
|
-
stamp: { id: null, number: null },
|
|
74225
|
-
kept
|
|
74226
|
-
});
|
|
74227
|
-
const serverDeployed = result.deployed ?? null;
|
|
74228
|
-
if (starter.owned_by_caller !== true) {
|
|
74131
|
+
if (!starter.owned_by_caller) {
|
|
74229
74132
|
warn(
|
|
74230
|
-
|
|
74231
|
-
|
|
74232
|
-
|
|
74233
|
-
Building ${starter.name} \u2014 this runs its build on your machine (its package scripts
|
|
74234
|
-
and vite config are the publisher's code, executed as you).${starter.is_official ? " Lotics reviewed this starter." : ""}`
|
|
74133
|
+
`
|
|
74134
|
+
${starter.name} is the publisher's code \u2014 its apps, workflows and agents \u2014 and runs in
|
|
74135
|
+
this workspace as you.${starter.is_official ? " Lotics reviewed this starter." : ""}`
|
|
74235
74136
|
);
|
|
74236
|
-
} else if (serverDeployed === null) {
|
|
74237
|
-
note(`Building and deploying\u2026`);
|
|
74238
74137
|
}
|
|
74239
|
-
|
|
74240
|
-
|
|
74241
|
-
`
|
|
74242
|
-
Lotics could not build ${starter.name} \u2014 building here instead.
|
|
74243
|
-
${result.build_error.split("\n")[0]}
|
|
74244
|
-
The copy is fine: the tables, the records and the app all landed. Do NOT
|
|
74245
|
-
copy again, and do not write a replacement app \u2014 only the build is missing.`
|
|
74138
|
+
for (const app of apps) {
|
|
74139
|
+
note(
|
|
74140
|
+
app.error === null ? ` \u2713 ${app.name} (${app.app_id}, v${app.version_number})` : ` \u2717 ${app.name} (${app.app_id}): ${app.error.split("\n")[0]}`
|
|
74246
74141
|
);
|
|
74247
74142
|
}
|
|
74248
|
-
const
|
|
74249
|
-
|
|
74250
|
-
|
|
74251
|
-
|
|
74252
|
-
|
|
74253
|
-
|
|
74254
|
-
|
|
74255
|
-
projectDir: targetPath,
|
|
74256
|
-
message: `Copied from starter ${starter.name}`
|
|
74257
|
-
});
|
|
74258
|
-
}
|
|
74259
|
-
} catch (error52) {
|
|
74260
|
-
const reason = error52 instanceof Error ? error52.message : String(error52);
|
|
74261
|
-
throw new Error(
|
|
74262
|
-
`${reason}
|
|
74263
|
-
|
|
74264
|
-
The copy itself is DONE and nothing needs repeating: the tables, the sample
|
|
74265
|
-
records and the project all landed. Only the deploy is left.
|
|
74266
|
-
|
|
74267
|
-
Finish it: cd ${resumeDir} && lotics app deploy -m "Copied from starter ${starter.name}"
|
|
74268
|
-
|
|
74269
|
-
Do NOT re-run the copy \u2014 it would adopt the tables this one just created and
|
|
74270
|
-
insert the sample records again.`
|
|
74143
|
+
const failed = apps.filter((app) => app.error !== null);
|
|
74144
|
+
if (failed.length > 0) {
|
|
74145
|
+
warn(
|
|
74146
|
+
`
|
|
74147
|
+
${failed.length} app${failed.length === 1 ? "" : "s"} landed without a version: ${failed.map((app) => app.name).join(", ")}.
|
|
74148
|
+
The copy is otherwise complete \u2014 do NOT copy again, and do not write a replacement
|
|
74149
|
+
app. Report this to the starter's publisher; a fixed starter is copied into a fresh workspace.`
|
|
74271
74150
|
);
|
|
74151
|
+
process.exitCode = 1;
|
|
74272
74152
|
}
|
|
74273
74153
|
let signInUrl = null;
|
|
74274
74154
|
try {
|
|
74275
74155
|
const link = await client.login({
|
|
74276
74156
|
return_link: true,
|
|
74277
|
-
redirect_path: `/apps/${
|
|
74157
|
+
redirect_path: apps.length === 1 ? `/apps/${apps[0].app_id}` : "/apps"
|
|
74278
74158
|
});
|
|
74279
74159
|
signInUrl = link.url ?? null;
|
|
74280
74160
|
} catch (error52) {
|
|
@@ -74286,17 +74166,15 @@ insert the sample records again.`
|
|
|
74286
74166
|
signInUrl = null;
|
|
74287
74167
|
}
|
|
74288
74168
|
note(
|
|
74289
|
-
`
|
|
74169
|
+
(apps.length > 0 && failed.length === apps.length ? `
|
|
74170
|
+
${starter.name} landed, but none of its ${apps.length} app${apps.length === 1 ? "" : "s"} is live.
|
|
74171
|
+
` : `
|
|
74290
74172
|
Done \u2014 ${starter.name} is live and yours.
|
|
74291
|
-
|
|
74292
|
-
|
|
74293
|
-
|
|
74294
|
-
|
|
74295
|
-
|
|
74296
|
-
Ship a change: cd ${path10.relative(process.cwd(), targetPath) || "."} && lotics app deploy -m "<what changed>"
|
|
74297
|
-
|
|
74298
|
-
How to change it: lotics docs building_an_app (then \`lotics docs\` for the rest \u2014
|
|
74299
|
-
the SDK and UI references are installed in this project, so they match its versions)`
|
|
74173
|
+
`) + `
|
|
74174
|
+
Edit anything: the tables, the apps, the templates, the docs. There is no link
|
|
74175
|
+
back to the starter and nothing to upgrade \u2014 this is your workspace now.
|
|
74176
|
+
Change an app's code: lotics app pull <app_id> (then lotics app deploy -m "<what changed>")
|
|
74177
|
+
How to change it: lotics docs building_an_app (then \`lotics docs\` for the rest)`
|
|
74300
74178
|
);
|
|
74301
74179
|
if (records > 0) {
|
|
74302
74180
|
note(
|
|
@@ -74307,18 +74185,16 @@ Done \u2014 ${starter.name} is live and yours.
|
|
|
74307
74185
|
}
|
|
74308
74186
|
noteCopiedContent(templates, knowledgeDocs);
|
|
74309
74187
|
if (signInUrl !== null) {
|
|
74310
|
-
note(
|
|
74311
|
-
`
|
|
74188
|
+
note(`
|
|
74312
74189
|
Open it \u2014 one-time sign-in link, expires in 15 minutes:
|
|
74313
|
-
${signInUrl}`
|
|
74314
|
-
);
|
|
74190
|
+
${signInUrl}`);
|
|
74315
74191
|
}
|
|
74316
74192
|
return {
|
|
74317
74193
|
starter_id: args.starter_id,
|
|
74318
74194
|
starter_name: starter.name,
|
|
74319
74195
|
version: result.version,
|
|
74320
|
-
|
|
74321
|
-
|
|
74196
|
+
app_ids: Object.fromEntries(apps.map((app) => [app.alias, app.app_id])),
|
|
74197
|
+
apps,
|
|
74322
74198
|
created,
|
|
74323
74199
|
signin_url: signInUrl
|
|
74324
74200
|
};
|
|
@@ -103064,7 +102940,7 @@ async function runKnowledgeCommand(client, subcommand, toolArgs, flags, restArgs
|
|
|
103064
102940
|
import { spawn as spawn3 } from "node:child_process";
|
|
103065
102941
|
import { createServer } from "node:http";
|
|
103066
102942
|
import { readFileSync, writeFileSync, existsSync as existsSync2, mkdtempSync, rmSync, readdirSync } from "node:fs";
|
|
103067
|
-
import { tmpdir as
|
|
102943
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
103068
102944
|
import { join as join2, dirname, resolve, extname, basename } from "node:path";
|
|
103069
102945
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
103070
102946
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
@@ -103163,7 +103039,7 @@ async function runPreviewCommand(filePath, flags) {
|
|
|
103163
103039
|
});
|
|
103164
103040
|
await new Promise((r) => server.listen(0, "127.0.0.1", () => r()));
|
|
103165
103041
|
const httpPort = server.address().port;
|
|
103166
|
-
const udd = mkdtempSync(join2(
|
|
103042
|
+
const udd = mkdtempSync(join2(tmpdir2(), "lotics-render-"));
|
|
103167
103043
|
const child = spawn3(chrome, [
|
|
103168
103044
|
"--headless=new",
|
|
103169
103045
|
"--disable-gpu",
|
|
@@ -103675,7 +103551,7 @@ async function main() {
|
|
|
103675
103551
|
console.error(
|
|
103676
103552
|
`--version prints this CLI's version and takes no argument; it cannot be combined with a command.
|
|
103677
103553
|
The CLI version: lotics --version
|
|
103678
|
-
A
|
|
103554
|
+
A starter's version: a copy always takes the latest; there is no flag for an older one.`
|
|
103679
103555
|
);
|
|
103680
103556
|
process.exit(1);
|
|
103681
103557
|
}
|
|
@@ -103686,10 +103562,10 @@ async function main() {
|
|
|
103686
103562
|
if (command === "setup") {
|
|
103687
103563
|
const starterId = subcommand;
|
|
103688
103564
|
if (!starterId) {
|
|
103689
|
-
console.error("Usage: lotics setup <starter_id> [
|
|
103565
|
+
console.error("Usage: lotics setup <starter_id> [--email <you@co.com>] [--json]");
|
|
103690
103566
|
console.error(" Creates an account if this machine has none, then copies the starter");
|
|
103691
103567
|
console.error(" into its workspace: schema, templates, knowledge docs, sample records");
|
|
103692
|
-
console.error(" and
|
|
103568
|
+
console.error(" and its apps, deployed. Ends with a one-time sign-in link.");
|
|
103693
103569
|
console.error(" --json prints one object and nothing else.");
|
|
103694
103570
|
process.exit(1);
|
|
103695
103571
|
}
|
|
@@ -103699,7 +103575,7 @@ async function main() {
|
|
|
103699
103575
|
const email3 = flags.email ?? (process.stdin.isTTY ? await prompt("Email: ") : "");
|
|
103700
103576
|
if (!email3) {
|
|
103701
103577
|
console.error(
|
|
103702
|
-
"This machine has no Lotics credential yet, so `
|
|
103578
|
+
"This machine has no Lotics credential yet, so `setup` needs an email to create one:\n lotics setup " + starterId + " --email you@company.com"
|
|
103703
103579
|
);
|
|
103704
103580
|
process.exit(1);
|
|
103705
103581
|
}
|
|
@@ -103725,9 +103601,9 @@ async function main() {
|
|
|
103725
103601
|
}
|
|
103726
103602
|
const { client: client2, ctx: ctx2 } = requireClient(flags);
|
|
103727
103603
|
await resolveWorkspace(client2, ctx2);
|
|
103604
|
+
if (toolArgs !== void 0) warn(`A path is no longer needed \u2014 a copy writes nothing to disk. Ignoring "${toolArgs}".`);
|
|
103728
103605
|
const result = await starterInit(client2, {
|
|
103729
103606
|
starter_id: starterId,
|
|
103730
|
-
...toolArgs !== void 0 ? { targetPath: toolArgs } : {},
|
|
103731
103607
|
...flags.noSampleData === true ? { noSampleData: true } : {},
|
|
103732
103608
|
...flags.adopt === true ? { adopt: true } : {}
|
|
103733
103609
|
});
|
|
@@ -103759,18 +103635,18 @@ async function main() {
|
|
|
103759
103635
|
}
|
|
103760
103636
|
if (subcommand === "init") {
|
|
103761
103637
|
if (!toolArgs) {
|
|
103762
|
-
console.error("Usage: lotics starter init <starter_id> [
|
|
103638
|
+
console.error("Usage: lotics starter init <starter_id> [--no-sample-data] [--adopt]");
|
|
103763
103639
|
console.error(" Copies a starter into this workspace: schema, templates, knowledge");
|
|
103764
|
-
console.error(" docs, sample records and
|
|
103765
|
-
console.error("
|
|
103640
|
+
console.error(" docs, sample records and its apps, deployed on Lotics. Nothing is");
|
|
103641
|
+
console.error(" written here; edit an app afterwards with lotics app pull <app_id>.");
|
|
103766
103642
|
process.exit(1);
|
|
103767
103643
|
}
|
|
103768
103644
|
if (flags.json) setMachineOutput(true);
|
|
103769
103645
|
const { client: client2, ctx: ctx2 } = requireClient(flags);
|
|
103770
103646
|
await resolveWorkspace(client2, ctx2);
|
|
103647
|
+
if (restArgs[0] !== void 0) warn(`A path is no longer needed \u2014 a copy writes nothing to disk. Ignoring "${restArgs[0]}".`);
|
|
103771
103648
|
const result = await starterInit(client2, {
|
|
103772
103649
|
starter_id: toolArgs,
|
|
103773
|
-
...restArgs[0] !== void 0 ? { targetPath: restArgs[0] } : {},
|
|
103774
103650
|
...flags.noSampleData === true ? { noSampleData: true } : {},
|
|
103775
103651
|
...flags.adopt === true ? { adopt: true } : {}
|
|
103776
103652
|
});
|
|
@@ -103804,7 +103680,7 @@ async function main() {
|
|
|
103804
103680
|
return;
|
|
103805
103681
|
}
|
|
103806
103682
|
console.error(
|
|
103807
|
-
"Usage: lotics starter list | show <starter_id> | init <starter_id>
|
|
103683
|
+
"Usage: lotics starter list | show <starter_id> | init <starter_id> | fixtures capture"
|
|
103808
103684
|
);
|
|
103809
103685
|
process.exit(1);
|
|
103810
103686
|
}
|