@lotics/cli 0.65.0 → 0.66.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 +8 -2
- package/dist/app_commands.d.ts +18 -0
- package/dist/app_commands.js +54 -8
- package/dist/app_commands.test.js +19 -4
- package/dist/args.d.ts +2 -0
- package/dist/args.js +4 -0
- package/dist/cli.js +35 -8
- package/dist/client.d.ts +20 -0
- package/dist/client.js +15 -0
- package/dist/src/cli.js +80 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -181,8 +181,14 @@ lotics app workflow pull # rewrite src/workflows/*.ts + globa
|
|
|
181
181
|
lotics app workflow check # typecheck every body locally ([alias] for one)
|
|
182
182
|
lotics app workflow set issueInvoice # push the edited src/workflows/issueInvoice.ts
|
|
183
183
|
|
|
184
|
-
#
|
|
185
|
-
|
|
184
|
+
# Iterate on a named query WITHOUT a deploy: push package.json#lotics.queries.<alias>
|
|
185
|
+
# to apps.queries (server-validated like a deploy). apps.queries is manifest-
|
|
186
|
+
# authoritative, so the next `app deploy` re-syncs it — keep the manifest current.
|
|
187
|
+
lotics app query set openInvoices # push package.json#lotics.queries.openInvoices
|
|
188
|
+
|
|
189
|
+
# Dev-link @lotics/ui to packages/ui/src for live HMR (Vite alias; deploy bundles it)
|
|
190
|
+
lotics ui link card # monorepo: packages/ui/src found automatically
|
|
191
|
+
lotics ui link card --ui-src /abs/monorepo/packages/ui/src # external app (e.g. ~/lotics_apps)
|
|
186
192
|
lotics ui link card --remove # finalize: PR + publish, then drop the alias
|
|
187
193
|
```
|
|
188
194
|
|
package/dist/app_commands.d.ts
CHANGED
|
@@ -303,6 +303,23 @@ export declare function appExecuteWorkflow(client: LoticsClient, args: {
|
|
|
303
303
|
export declare function appWorkflowSet(client: LoticsClient, args: {
|
|
304
304
|
alias: string;
|
|
305
305
|
}): Promise<void>;
|
|
306
|
+
/**
|
|
307
|
+
* `lotics app query set <alias>` — push `package.json#lotics.queries.<alias>` to
|
|
308
|
+
* `apps.queries` through `set_app_query`, WITHOUT a deploy. The deploy-free inner
|
|
309
|
+
* loop for named queries, parallel to `lotics app workflow set` for workflows.
|
|
310
|
+
*
|
|
311
|
+
* The declaration (`{ ast, params? }`) is read from the manifest — the same map
|
|
312
|
+
* `useQuery` codegen reads and `lotics app deploy` syncs authoritatively. The
|
|
313
|
+
* server validates it exactly as a deploy does (alias identifier, workspace-only
|
|
314
|
+
* tables, resolvable fields, declared params). Because `apps.queries` is
|
|
315
|
+
* manifest-authoritative, the next `lotics app deploy` overwrites this from the
|
|
316
|
+
* manifest — so keep the manifest as the source of truth; this only skips the
|
|
317
|
+
* build/upload round-trip while iterating. Errors (unbound alias, validation
|
|
318
|
+
* failure) print to stderr and exit non-zero.
|
|
319
|
+
*/
|
|
320
|
+
export declare function appQuerySet(client: LoticsClient, args: {
|
|
321
|
+
alias: string;
|
|
322
|
+
}): Promise<void>;
|
|
306
323
|
/**
|
|
307
324
|
* `lotics app workflow pull` — rewrite every `src/workflows/<alias>.ts` from the
|
|
308
325
|
* server without a full `lotics app pull` (no source archive, no npm install).
|
|
@@ -346,4 +363,5 @@ export declare function appUiLink(args: {
|
|
|
346
363
|
projectDir?: string;
|
|
347
364
|
component: string;
|
|
348
365
|
remove?: boolean;
|
|
366
|
+
uiSrc?: string;
|
|
349
367
|
}): void;
|
package/dist/app_commands.js
CHANGED
|
@@ -896,9 +896,13 @@ args) {
|
|
|
896
896
|
// `capabilities` block turns every capability OFF on the next deploy
|
|
897
897
|
// (fail-safe; the declaration is the grant).
|
|
898
898
|
capabilities: meta.capabilities ?? {},
|
|
899
|
-
// Workflow
|
|
900
|
-
// remove_app_workflow own apps.workflows.
|
|
901
|
-
//
|
|
899
|
+
// Workflow BINDINGS are NOT a deploy concern — set_app_workflow /
|
|
900
|
+
// remove_app_workflow own apps.workflows. But the alias KEYS of the
|
|
901
|
+
// manifest's `workflows` map ARE sent (never the bindings): they record
|
|
902
|
+
// which aliases this bundle declares, so remove_app_workflow can refuse
|
|
903
|
+
// to unbind an alias the served version still calls. Drop an alias from
|
|
904
|
+
// the manifest + redeploy to lift that guard before removing its binding.
|
|
905
|
+
workflow_aliases: Object.keys(meta.workflows ?? {}),
|
|
902
906
|
});
|
|
903
907
|
writeAppMeta(projectDir, {
|
|
904
908
|
...meta,
|
|
@@ -1198,6 +1202,37 @@ export async function appWorkflowSet(client, args) {
|
|
|
1198
1202
|
console.error(` result.data schema: ${JSON.stringify(result.outputs)}`);
|
|
1199
1203
|
}
|
|
1200
1204
|
}
|
|
1205
|
+
/**
|
|
1206
|
+
* `lotics app query set <alias>` — push `package.json#lotics.queries.<alias>` to
|
|
1207
|
+
* `apps.queries` through `set_app_query`, WITHOUT a deploy. The deploy-free inner
|
|
1208
|
+
* loop for named queries, parallel to `lotics app workflow set` for workflows.
|
|
1209
|
+
*
|
|
1210
|
+
* The declaration (`{ ast, params? }`) is read from the manifest — the same map
|
|
1211
|
+
* `useQuery` codegen reads and `lotics app deploy` syncs authoritatively. The
|
|
1212
|
+
* server validates it exactly as a deploy does (alias identifier, workspace-only
|
|
1213
|
+
* tables, resolvable fields, declared params). Because `apps.queries` is
|
|
1214
|
+
* manifest-authoritative, the next `lotics app deploy` overwrites this from the
|
|
1215
|
+
* manifest — so keep the manifest as the source of truth; this only skips the
|
|
1216
|
+
* build/upload round-trip while iterating. Errors (unbound alias, validation
|
|
1217
|
+
* failure) print to stderr and exit non-zero.
|
|
1218
|
+
*/
|
|
1219
|
+
export async function appQuerySet(client, args) {
|
|
1220
|
+
const projectDir = process.cwd();
|
|
1221
|
+
const meta = readAppMeta(projectDir);
|
|
1222
|
+
const declaration = meta.queries?.[args.alias];
|
|
1223
|
+
if (!declaration) {
|
|
1224
|
+
console.error(`No query "${args.alias}" in package.json#lotics.queries. ` +
|
|
1225
|
+
`Declare it there (alias → { ast, params? }) first.`);
|
|
1226
|
+
process.exit(1);
|
|
1227
|
+
}
|
|
1228
|
+
const res = await client.setAppQuery(meta.app_id, args.alias, declaration);
|
|
1229
|
+
if (res.error) {
|
|
1230
|
+
console.error(`Failed to set query "${args.alias}": ${res.error}`);
|
|
1231
|
+
process.exit(1);
|
|
1232
|
+
}
|
|
1233
|
+
console.error(`Set query "${args.alias}" on ${meta.app_id}. ` +
|
|
1234
|
+
`(apps.queries is manifest-authoritative — the next 'lotics app deploy' re-syncs it.)`);
|
|
1235
|
+
}
|
|
1201
1236
|
/**
|
|
1202
1237
|
* `lotics app workflow pull` — rewrite every `src/workflows/<alias>.ts` from the
|
|
1203
1238
|
* server without a full `lotics app pull` (no source archive, no npm install).
|
|
@@ -1358,11 +1393,18 @@ export function appUiLink(args) {
|
|
|
1358
1393
|
if (!fs.existsSync(viteConfigPath)) {
|
|
1359
1394
|
throw new Error(`No vite.config.ts at ${projectDir}. Run inside a 'lotics app' project directory.`);
|
|
1360
1395
|
}
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1396
|
+
// Resolve packages/ui/src. An explicit --ui-src / LOTICS_UI_SRC wins — that's how
|
|
1397
|
+
// an EXTERNAL app (one that consumes @lotics/ui from npm, with no monorepo above
|
|
1398
|
+
// it) links the local kit; otherwise walk up for a monorepo checkout.
|
|
1399
|
+
const explicit = args.uiSrc ?? process.env.LOTICS_UI_SRC;
|
|
1400
|
+
const uiSrc = explicit ? path.resolve(explicit) : findUiSrcDir(projectDir);
|
|
1401
|
+
if (!uiSrc || !fs.existsSync(uiSrc) || !fs.statSync(uiSrc).isDirectory()) {
|
|
1402
|
+
throw new Error(explicit
|
|
1403
|
+
? `--ui-src / LOTICS_UI_SRC points at '${explicit}', which is not a directory. ` +
|
|
1404
|
+
`Pass the absolute path to the monorepo's packages/ui/src.`
|
|
1405
|
+
: "Cannot find packages/ui/src by walking up from this directory. For an EXTERNAL app " +
|
|
1406
|
+
"(consuming @lotics/ui from npm), pass --ui-src=<abs path to packages/ui/src> or set " +
|
|
1407
|
+
"LOTICS_UI_SRC; inside a monorepo checkout it is found automatically.");
|
|
1366
1408
|
}
|
|
1367
1409
|
// Validate the named component exists in src so a typo fails loud (the alias
|
|
1368
1410
|
// itself stays package-wide — this is the advisory check the spec calls for).
|
|
@@ -1403,6 +1445,10 @@ export function appUiLink(args) {
|
|
|
1403
1445
|
fs.writeFileSync(viteConfigPath, updated);
|
|
1404
1446
|
console.error(`Dev-linked @lotics/ui → ${uiSrc} in ${viteConfigPath}.`);
|
|
1405
1447
|
console.error("Restart `lotics app dev` and rm -rf node_modules/.vite to clear cached modules.");
|
|
1448
|
+
// The app's tsc still resolves @lotics/ui from node_modules (the published .d.ts) —
|
|
1449
|
+
// the kit `src` can't be typechecked in an app because it's RN-Web (uses
|
|
1450
|
+
// react-native-web types the app resolves as base react-native). Typecheck the kit
|
|
1451
|
+
// in packages/ui; the finalize publish restores the app's own typecheck.
|
|
1406
1452
|
console.error("Finalize: PR the packages/ui change → publish → `lotics ui link <component> --remove` + bump the app's dep.");
|
|
1407
1453
|
}
|
|
1408
1454
|
/** Escape a string for literal use inside a RegExp. */
|
|
@@ -208,8 +208,9 @@ describe("appCodegen (.d.ts-only path)", () => {
|
|
|
208
208
|
});
|
|
209
209
|
/**
|
|
210
210
|
* `appUiLink` edits the app's vite.config.ts resolve.alias to dev-link
|
|
211
|
-
* @lotics/ui at
|
|
212
|
-
*
|
|
211
|
+
* @lotics/ui at packages/ui/src — auto-found in a monorepo checkout, or given
|
|
212
|
+
* explicitly via --ui-src for an external app. It validates the src + component,
|
|
213
|
+
* fails loud when neither resolves, and insert/remove is idempotent.
|
|
213
214
|
*/
|
|
214
215
|
describe("appUiLink", () => {
|
|
215
216
|
let root;
|
|
@@ -264,16 +265,30 @@ describe("appUiLink", () => {
|
|
|
264
265
|
it("fails loud when the named component does not exist in packages/ui/src", () => {
|
|
265
266
|
expect(() => appUiLink({ projectDir: appDir, component: "nonexistent" })).toThrow(/nonexistent/);
|
|
266
267
|
});
|
|
267
|
-
it("fails loud when there is no
|
|
268
|
+
it("fails loud when there is no packages/ui/src and no --ui-src override", () => {
|
|
268
269
|
const lonely = fs.mkdtempSync(path.join(tmpdir(), "lotics-lonely-app-"));
|
|
269
270
|
fs.writeFileSync(path.join(lonely, "vite.config.ts"), "export default { resolve: { alias: [] } };");
|
|
270
271
|
try {
|
|
271
|
-
expect(() => appUiLink({ projectDir: lonely, component: "card" })).toThrow(/
|
|
272
|
+
expect(() => appUiLink({ projectDir: lonely, component: "card" })).toThrow(/--ui-src/);
|
|
272
273
|
}
|
|
273
274
|
finally {
|
|
274
275
|
fs.rmSync(lonely, { recursive: true, force: true });
|
|
275
276
|
}
|
|
276
277
|
});
|
|
278
|
+
it("links an EXTERNAL app (no monorepo above) via an explicit --ui-src", () => {
|
|
279
|
+
const ext = fs.mkdtempSync(path.join(tmpdir(), "lotics-external-app-"));
|
|
280
|
+
fs.writeFileSync(path.join(ext, "vite.config.ts"), "export default { resolve: { alias: [] } };");
|
|
281
|
+
try {
|
|
282
|
+
appUiLink({ projectDir: ext, component: "card", uiSrc });
|
|
283
|
+
expect(fs.readFileSync(path.join(ext, "vite.config.ts"), "utf-8")).toContain(uiSrc);
|
|
284
|
+
}
|
|
285
|
+
finally {
|
|
286
|
+
fs.rmSync(ext, { recursive: true, force: true });
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
it("fails loud when --ui-src is not a directory", () => {
|
|
290
|
+
expect(() => appUiLink({ projectDir: appDir, component: "card", uiSrc: path.join(root, "nope") })).toThrow(/not a directory/);
|
|
291
|
+
});
|
|
277
292
|
});
|
|
278
293
|
/**
|
|
279
294
|
* The workflow body files are how AA-1 Option B becomes "open a file → edit →
|
package/dist/args.d.ts
CHANGED
|
@@ -23,6 +23,8 @@ export declare function parseArgs(argv: string[]): {
|
|
|
23
23
|
apiKey?: string;
|
|
24
24
|
workspace?: string;
|
|
25
25
|
viewAs?: string;
|
|
26
|
+
/** `--ui-src <path>`: absolute packages/ui/src for `ui link` in an external app. */
|
|
27
|
+
uiSrc?: string;
|
|
26
28
|
name?: string;
|
|
27
29
|
timezone?: string;
|
|
28
30
|
message?: string;
|
package/dist/args.js
CHANGED
|
@@ -19,6 +19,7 @@ export function parseArgs(argv) {
|
|
|
19
19
|
apiKey: undefined,
|
|
20
20
|
workspace: undefined,
|
|
21
21
|
viewAs: undefined,
|
|
22
|
+
uiSrc: undefined,
|
|
22
23
|
name: undefined,
|
|
23
24
|
timezone: undefined,
|
|
24
25
|
message: undefined,
|
|
@@ -60,6 +61,9 @@ export function parseArgs(argv) {
|
|
|
60
61
|
case "--view-as":
|
|
61
62
|
flags.viewAs = argv[++i];
|
|
62
63
|
break;
|
|
64
|
+
case "--ui-src":
|
|
65
|
+
flags.uiSrc = argv[++i];
|
|
66
|
+
break;
|
|
63
67
|
case "--name":
|
|
64
68
|
flags.name = argv[++i];
|
|
65
69
|
break;
|
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, appVersions, appCodegen, appExecuteWorkflow, appWorkflowSet, appWorkflowPull, appWorkflowCheck, appUiLink, } from "./app_commands.js";
|
|
16
|
+
import { appCreate, appPull, appDeploy, appDev, appSetSubdomain, appRename, appVersions, appCodegen, appExecuteWorkflow, appWorkflowSet, appWorkflowPull, appWorkflowCheck, appQuerySet, 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";
|
|
@@ -90,11 +90,16 @@ COMMANDS
|
|
|
90
90
|
lotics app workflow pull Rewrite src/workflows/*.ts from the server
|
|
91
91
|
lotics app workflow check [alias] Typecheck src/workflows bodies locally (one
|
|
92
92
|
isolated program per alias; the app's own tsc)
|
|
93
|
+
lotics app query set <alias> Push package.json#lotics.queries.<alias> to
|
|
94
|
+
apps.queries via set_app_query (no deploy;
|
|
95
|
+
re-synced by the next deploy from the manifest)
|
|
93
96
|
lotics app subdomain <new-subdomain> Rename the app's public <slug>.lotics.app address
|
|
94
97
|
lotics app rename "<new name>" Rename the app's display name (launcher title)
|
|
95
98
|
lotics app dev [path] Run the app locally with HMR (RPC forwarded to prod)
|
|
96
|
-
lotics ui link <component> [--
|
|
97
|
-
packages/ui/src
|
|
99
|
+
lotics ui link <component> [--ui-src <path>] [--remove]
|
|
100
|
+
Dev-link @lotics/ui to packages/ui/src (Vite alias
|
|
101
|
+
+ tsc paths) for live HMR + typecheck. Monorepo apps
|
|
102
|
+
auto-find it; external apps pass --ui-src / LOTICS_UI_SRC
|
|
98
103
|
lotics xlsx <subcommand> ... Read/write/edit .xlsx files on your local filesystem
|
|
99
104
|
(uses the bundled Lotics xlsx engine; prefer over
|
|
100
105
|
npm xlsx/exceljs for round-trip fidelity)
|
|
@@ -493,17 +498,19 @@ async function main() {
|
|
|
493
498
|
if (subcommand === "link") {
|
|
494
499
|
const component = toolArgs;
|
|
495
500
|
if (!component) {
|
|
496
|
-
console.error("Usage: lotics ui link <component> [--remove]");
|
|
497
|
-
console.error("Dev-links @lotics/ui to
|
|
501
|
+
console.error("Usage: lotics ui link <component> [--ui-src <abs path>] [--remove]");
|
|
502
|
+
console.error("Dev-links @lotics/ui to packages/ui/src (Vite + tsc) for live HMR + typecheck.");
|
|
503
|
+
console.error("Monorepo apps find packages/ui/src automatically; external apps pass --ui-src / LOTICS_UI_SRC.");
|
|
498
504
|
process.exit(1);
|
|
499
505
|
}
|
|
500
506
|
// `--remove` isn't a value-taking flag, so the parser leaves it as a
|
|
501
|
-
// trailing positional (the component took `toolArgs`)
|
|
502
|
-
|
|
507
|
+
// trailing positional (the component took `toolArgs`); `--ui-src` IS a
|
|
508
|
+
// value flag (flags.uiSrc), falling back to LOTICS_UI_SRC inside appUiLink.
|
|
509
|
+
appUiLink({ component, uiSrc: flags.uiSrc, remove: restArgs.includes("--remove") });
|
|
503
510
|
return;
|
|
504
511
|
}
|
|
505
512
|
console.error(`Unknown ui subcommand: ${subcommand ?? "(none)"}`);
|
|
506
|
-
console.error("Usage: lotics ui link <component> [--remove]");
|
|
513
|
+
console.error("Usage: lotics ui link <component> [--ui-src <abs path>] [--remove]");
|
|
507
514
|
process.exit(1);
|
|
508
515
|
}
|
|
509
516
|
// --- lotics app workflow check [alias] — local typecheck, no auth, no network ---
|
|
@@ -606,6 +613,7 @@ async function main() {
|
|
|
606
613
|
console.error(" lotics app workflow set <alias> Push the edited src/workflows/<alias>.ts body");
|
|
607
614
|
console.error(" lotics app workflow pull Rewrite src/workflows/*.ts from the server");
|
|
608
615
|
console.error(" lotics app workflow check [alias] Typecheck src/workflows bodies locally");
|
|
616
|
+
console.error(" lotics app query set <alias> Push lotics.queries.<alias> to apps.queries (no deploy)");
|
|
609
617
|
console.error(" lotics app subdomain <new-subdomain> Rename the app's public <slug>.lotics.app address");
|
|
610
618
|
console.error(" lotics app rename \"<new name>\" Rename the app's display name (launcher title)");
|
|
611
619
|
console.error(" lotics app dev [path] Run the app locally with HMR + RPC forwarding");
|
|
@@ -826,6 +834,25 @@ async function main() {
|
|
|
826
834
|
}
|
|
827
835
|
workflowUsage();
|
|
828
836
|
}
|
|
837
|
+
if (subcommand === "query") {
|
|
838
|
+
// `lotics app query set <alias>` — push package.json#lotics.queries.<alias>
|
|
839
|
+
// to apps.queries via set_app_query, no deploy. `toolArgs` is the verb;
|
|
840
|
+
// `restArgs` carries the alias.
|
|
841
|
+
const action = toolArgs;
|
|
842
|
+
if (action === "set") {
|
|
843
|
+
const alias = restArgs[0];
|
|
844
|
+
if (!alias) {
|
|
845
|
+
console.error("Usage: lotics app query set <alias>");
|
|
846
|
+
console.error("Pushes package.json#lotics.queries.<alias> to apps.queries via set_app_query (no deploy).");
|
|
847
|
+
process.exit(1);
|
|
848
|
+
}
|
|
849
|
+
await appQuerySet(client, { alias });
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
console.error("Usage:");
|
|
853
|
+
console.error(" lotics app query set <alias> Push package.json#lotics.queries.<alias> to apps.queries (no deploy)");
|
|
854
|
+
process.exit(1);
|
|
855
|
+
}
|
|
829
856
|
if (subcommand === "dev") {
|
|
830
857
|
// First positional is an optional project path (defaults to cwd).
|
|
831
858
|
// --port and --vite-port can override the wrapper / Vite ports.
|
package/dist/client.d.ts
CHANGED
|
@@ -277,6 +277,18 @@ export declare class LoticsClient {
|
|
|
277
277
|
name?: string;
|
|
278
278
|
description?: string;
|
|
279
279
|
}): Promise<ToolExecuteResult>;
|
|
280
|
+
/**
|
|
281
|
+
* Bind (create or replace) an app query by alias via the `set_app_query` tool
|
|
282
|
+
* — the deploy-free authoring path for `apps.queries`, parallel to
|
|
283
|
+
* `setAppWorkflow`. `declaration` is the `{ ast, params? }` from
|
|
284
|
+
* `package.json#lotics.queries.<alias>`. The server validates it exactly as a
|
|
285
|
+
* deploy validates the manifest. Note: `apps.queries` is manifest-authoritative,
|
|
286
|
+
* so the next `lotics app deploy` overwrites this from the manifest.
|
|
287
|
+
*/
|
|
288
|
+
setAppQuery(app_id: string, alias: string, declaration: {
|
|
289
|
+
ast: unknown;
|
|
290
|
+
params?: Record<string, unknown>;
|
|
291
|
+
}): Promise<ToolExecuteResult>;
|
|
280
292
|
/**
|
|
281
293
|
* Fetch one app workflow's faithful source + bound input/output schemas via
|
|
282
294
|
* `get_app_workflow`. `source` is the JS-subset body re-rendered from the
|
|
@@ -375,6 +387,14 @@ export declare class LoticsClient {
|
|
|
375
387
|
capabilities?: {
|
|
376
388
|
comments?: boolean;
|
|
377
389
|
};
|
|
390
|
+
/**
|
|
391
|
+
* The manifest's `lotics.workflows` KEYS — the workflow aliases the deployed
|
|
392
|
+
* code declares (NOT the bindings; `set_app_workflow` / `remove_app_workflow`
|
|
393
|
+
* own `apps.workflows`). Recorded on the new version so `remove_app_workflow`
|
|
394
|
+
* refuses to unbind an alias the served version still calls. Always sent
|
|
395
|
+
* (empty array when none declared).
|
|
396
|
+
*/
|
|
397
|
+
workflow_aliases?: string[];
|
|
378
398
|
}): Promise<{
|
|
379
399
|
version_id: string;
|
|
380
400
|
version_number: number;
|
package/dist/client.js
CHANGED
|
@@ -333,6 +333,17 @@ export class LoticsClient {
|
|
|
333
333
|
...(body.description ? { description: body.description } : {}),
|
|
334
334
|
});
|
|
335
335
|
}
|
|
336
|
+
/**
|
|
337
|
+
* Bind (create or replace) an app query by alias via the `set_app_query` tool
|
|
338
|
+
* — the deploy-free authoring path for `apps.queries`, parallel to
|
|
339
|
+
* `setAppWorkflow`. `declaration` is the `{ ast, params? }` from
|
|
340
|
+
* `package.json#lotics.queries.<alias>`. The server validates it exactly as a
|
|
341
|
+
* deploy validates the manifest. Note: `apps.queries` is manifest-authoritative,
|
|
342
|
+
* so the next `lotics app deploy` overwrites this from the manifest.
|
|
343
|
+
*/
|
|
344
|
+
async setAppQuery(app_id, alias, declaration) {
|
|
345
|
+
return this.execute("set_app_query", { app_id, alias, declaration });
|
|
346
|
+
}
|
|
336
347
|
/**
|
|
337
348
|
* Fetch one app workflow's faithful source + bound input/output schemas via
|
|
338
349
|
* `get_app_workflow`. `source` is the JS-subset body re-rendered from the
|
|
@@ -427,6 +438,10 @@ export class LoticsClient {
|
|
|
427
438
|
if (args.capabilities !== undefined) {
|
|
428
439
|
formData.append("capabilities", JSON.stringify(args.capabilities));
|
|
429
440
|
}
|
|
441
|
+
// Always send the declared workflow aliases (empty array when none) so the
|
|
442
|
+
// server records what the served version calls — the remove_app_workflow
|
|
443
|
+
// guard reads this back. These are the manifest KEYS only, never bindings.
|
|
444
|
+
formData.append("workflow_aliases", JSON.stringify(args.workflow_aliases ?? []));
|
|
430
445
|
const url = `${this.baseUrl}/v1/apps/${encodeURIComponent(args.app_id)}/versions`;
|
|
431
446
|
const response = await fetch(url, {
|
|
432
447
|
method: "POST",
|
package/dist/src/cli.js
CHANGED
|
@@ -29881,6 +29881,17 @@ var LoticsClient = class {
|
|
|
29881
29881
|
...body.description ? { description: body.description } : {}
|
|
29882
29882
|
});
|
|
29883
29883
|
}
|
|
29884
|
+
/**
|
|
29885
|
+
* Bind (create or replace) an app query by alias via the `set_app_query` tool
|
|
29886
|
+
* — the deploy-free authoring path for `apps.queries`, parallel to
|
|
29887
|
+
* `setAppWorkflow`. `declaration` is the `{ ast, params? }` from
|
|
29888
|
+
* `package.json#lotics.queries.<alias>`. The server validates it exactly as a
|
|
29889
|
+
* deploy validates the manifest. Note: `apps.queries` is manifest-authoritative,
|
|
29890
|
+
* so the next `lotics app deploy` overwrites this from the manifest.
|
|
29891
|
+
*/
|
|
29892
|
+
async setAppQuery(app_id, alias, declaration) {
|
|
29893
|
+
return this.execute("set_app_query", { app_id, alias, declaration });
|
|
29894
|
+
}
|
|
29884
29895
|
/**
|
|
29885
29896
|
* Fetch one app workflow's faithful source + bound input/output schemas via
|
|
29886
29897
|
* `get_app_workflow`. `source` is the JS-subset body re-rendered from the
|
|
@@ -30004,6 +30015,7 @@ var LoticsClient = class {
|
|
|
30004
30015
|
if (args.capabilities !== void 0) {
|
|
30005
30016
|
formData.append("capabilities", JSON.stringify(args.capabilities));
|
|
30006
30017
|
}
|
|
30018
|
+
formData.append("workflow_aliases", JSON.stringify(args.workflow_aliases ?? []));
|
|
30007
30019
|
const url = `${this.baseUrl}/v1/apps/${encodeURIComponent(args.app_id)}/versions`;
|
|
30008
30020
|
const response = await fetch(url, {
|
|
30009
30021
|
method: "POST",
|
|
@@ -32730,10 +32742,14 @@ async function appDeploy(client, args) {
|
|
|
32730
32742
|
// defaulting to `{}` when the manifest declares none — so deleting the
|
|
32731
32743
|
// `capabilities` block turns every capability OFF on the next deploy
|
|
32732
32744
|
// (fail-safe; the declaration is the grant).
|
|
32733
|
-
capabilities: meta.capabilities ?? {}
|
|
32734
|
-
// Workflow
|
|
32735
|
-
// remove_app_workflow own apps.workflows.
|
|
32736
|
-
//
|
|
32745
|
+
capabilities: meta.capabilities ?? {},
|
|
32746
|
+
// Workflow BINDINGS are NOT a deploy concern — set_app_workflow /
|
|
32747
|
+
// remove_app_workflow own apps.workflows. But the alias KEYS of the
|
|
32748
|
+
// manifest's `workflows` map ARE sent (never the bindings): they record
|
|
32749
|
+
// which aliases this bundle declares, so remove_app_workflow can refuse
|
|
32750
|
+
// to unbind an alias the served version still calls. Drop an alias from
|
|
32751
|
+
// the manifest + redeploy to lift that guard before removing its binding.
|
|
32752
|
+
workflow_aliases: Object.keys(meta.workflows ?? {})
|
|
32737
32753
|
});
|
|
32738
32754
|
writeAppMeta(projectDir, {
|
|
32739
32755
|
...meta,
|
|
@@ -32942,6 +32958,25 @@ async function appWorkflowSet(client, args) {
|
|
|
32942
32958
|
console.error(` result.data schema: ${JSON.stringify(result.outputs)}`);
|
|
32943
32959
|
}
|
|
32944
32960
|
}
|
|
32961
|
+
async function appQuerySet(client, args) {
|
|
32962
|
+
const projectDir = process.cwd();
|
|
32963
|
+
const meta = readAppMeta(projectDir);
|
|
32964
|
+
const declaration = meta.queries?.[args.alias];
|
|
32965
|
+
if (!declaration) {
|
|
32966
|
+
console.error(
|
|
32967
|
+
`No query "${args.alias}" in package.json#lotics.queries. Declare it there (alias \u2192 { ast, params? }) first.`
|
|
32968
|
+
);
|
|
32969
|
+
process.exit(1);
|
|
32970
|
+
}
|
|
32971
|
+
const res = await client.setAppQuery(meta.app_id, args.alias, declaration);
|
|
32972
|
+
if (res.error) {
|
|
32973
|
+
console.error(`Failed to set query "${args.alias}": ${res.error}`);
|
|
32974
|
+
process.exit(1);
|
|
32975
|
+
}
|
|
32976
|
+
console.error(
|
|
32977
|
+
`Set query "${args.alias}" on ${meta.app_id}. (apps.queries is manifest-authoritative \u2014 the next 'lotics app deploy' re-syncs it.)`
|
|
32978
|
+
);
|
|
32979
|
+
}
|
|
32945
32980
|
async function appWorkflowPull(client) {
|
|
32946
32981
|
const projectDir = process.cwd();
|
|
32947
32982
|
const meta = readAppMeta(projectDir);
|
|
@@ -33050,10 +33085,11 @@ function appUiLink(args) {
|
|
|
33050
33085
|
`No vite.config.ts at ${projectDir}. Run inside a 'lotics app' project directory.`
|
|
33051
33086
|
);
|
|
33052
33087
|
}
|
|
33053
|
-
const
|
|
33054
|
-
|
|
33088
|
+
const explicit = args.uiSrc ?? process.env.LOTICS_UI_SRC;
|
|
33089
|
+
const uiSrc = explicit ? path5.resolve(explicit) : findUiSrcDir(projectDir);
|
|
33090
|
+
if (!uiSrc || !fs4.existsSync(uiSrc) || !fs4.statSync(uiSrc).isDirectory()) {
|
|
33055
33091
|
throw new Error(
|
|
33056
|
-
"Cannot find packages/ui/src by walking up from this directory
|
|
33092
|
+
explicit ? `--ui-src / LOTICS_UI_SRC points at '${explicit}', which is not a directory. Pass the absolute path to the monorepo's packages/ui/src.` : "Cannot find packages/ui/src by walking up from this directory. For an EXTERNAL app (consuming @lotics/ui from npm), pass --ui-src=<abs path to packages/ui/src> or set LOTICS_UI_SRC; inside a monorepo checkout it is found automatically."
|
|
33057
33093
|
);
|
|
33058
33094
|
}
|
|
33059
33095
|
const hasComponent = fs4.existsSync(path5.join(uiSrc, `${args.component}.tsx`)) || fs4.existsSync(path5.join(uiSrc, `${args.component}.ts`)) || fs4.existsSync(path5.join(uiSrc, args.component));
|
|
@@ -33108,6 +33144,7 @@ function parseArgs(argv) {
|
|
|
33108
33144
|
apiKey: void 0,
|
|
33109
33145
|
workspace: void 0,
|
|
33110
33146
|
viewAs: void 0,
|
|
33147
|
+
uiSrc: void 0,
|
|
33111
33148
|
name: void 0,
|
|
33112
33149
|
timezone: void 0,
|
|
33113
33150
|
message: void 0,
|
|
@@ -33149,6 +33186,9 @@ function parseArgs(argv) {
|
|
|
33149
33186
|
case "--view-as":
|
|
33150
33187
|
flags.viewAs = argv[++i2];
|
|
33151
33188
|
break;
|
|
33189
|
+
case "--ui-src":
|
|
33190
|
+
flags.uiSrc = argv[++i2];
|
|
33191
|
+
break;
|
|
33152
33192
|
case "--name":
|
|
33153
33193
|
flags.name = argv[++i2];
|
|
33154
33194
|
break;
|
|
@@ -49650,11 +49690,16 @@ COMMANDS
|
|
|
49650
49690
|
lotics app workflow pull Rewrite src/workflows/*.ts from the server
|
|
49651
49691
|
lotics app workflow check [alias] Typecheck src/workflows bodies locally (one
|
|
49652
49692
|
isolated program per alias; the app's own tsc)
|
|
49693
|
+
lotics app query set <alias> Push package.json#lotics.queries.<alias> to
|
|
49694
|
+
apps.queries via set_app_query (no deploy;
|
|
49695
|
+
re-synced by the next deploy from the manifest)
|
|
49653
49696
|
lotics app subdomain <new-subdomain> Rename the app's public <slug>.lotics.app address
|
|
49654
49697
|
lotics app rename "<new name>" Rename the app's display name (launcher title)
|
|
49655
49698
|
lotics app dev [path] Run the app locally with HMR (RPC forwarded to prod)
|
|
49656
|
-
lotics ui link <component> [--
|
|
49657
|
-
packages/ui/src
|
|
49699
|
+
lotics ui link <component> [--ui-src <path>] [--remove]
|
|
49700
|
+
Dev-link @lotics/ui to packages/ui/src (Vite alias
|
|
49701
|
+
+ tsc paths) for live HMR + typecheck. Monorepo apps
|
|
49702
|
+
auto-find it; external apps pass --ui-src / LOTICS_UI_SRC
|
|
49658
49703
|
lotics xlsx <subcommand> ... Read/write/edit .xlsx files on your local filesystem
|
|
49659
49704
|
(uses the bundled Lotics xlsx engine; prefer over
|
|
49660
49705
|
npm xlsx/exceljs for round-trip fidelity)
|
|
@@ -50018,15 +50063,16 @@ async function main() {
|
|
|
50018
50063
|
if (subcommand === "link") {
|
|
50019
50064
|
const component = toolArgs;
|
|
50020
50065
|
if (!component) {
|
|
50021
|
-
console.error("Usage: lotics ui link <component> [--remove]");
|
|
50022
|
-
console.error("Dev-links @lotics/ui to
|
|
50066
|
+
console.error("Usage: lotics ui link <component> [--ui-src <abs path>] [--remove]");
|
|
50067
|
+
console.error("Dev-links @lotics/ui to packages/ui/src (Vite + tsc) for live HMR + typecheck.");
|
|
50068
|
+
console.error("Monorepo apps find packages/ui/src automatically; external apps pass --ui-src / LOTICS_UI_SRC.");
|
|
50023
50069
|
process.exit(1);
|
|
50024
50070
|
}
|
|
50025
|
-
appUiLink({ component, remove: restArgs.includes("--remove") });
|
|
50071
|
+
appUiLink({ component, uiSrc: flags.uiSrc, remove: restArgs.includes("--remove") });
|
|
50026
50072
|
return;
|
|
50027
50073
|
}
|
|
50028
50074
|
console.error(`Unknown ui subcommand: ${subcommand ?? "(none)"}`);
|
|
50029
|
-
console.error("Usage: lotics ui link <component> [--remove]");
|
|
50075
|
+
console.error("Usage: lotics ui link <component> [--ui-src <abs path>] [--remove]");
|
|
50030
50076
|
process.exit(1);
|
|
50031
50077
|
}
|
|
50032
50078
|
if (command === "app" && subcommand === "workflow" && toolArgs === "check") {
|
|
@@ -50119,6 +50165,7 @@ async function main() {
|
|
|
50119
50165
|
console.error(" lotics app workflow set <alias> Push the edited src/workflows/<alias>.ts body");
|
|
50120
50166
|
console.error(" lotics app workflow pull Rewrite src/workflows/*.ts from the server");
|
|
50121
50167
|
console.error(" lotics app workflow check [alias] Typecheck src/workflows bodies locally");
|
|
50168
|
+
console.error(" lotics app query set <alias> Push lotics.queries.<alias> to apps.queries (no deploy)");
|
|
50122
50169
|
console.error(" lotics app subdomain <new-subdomain> Rename the app's public <slug>.lotics.app address");
|
|
50123
50170
|
console.error(` lotics app rename "<new name>" Rename the app's display name (launcher title)`);
|
|
50124
50171
|
console.error(" lotics app dev [path] Run the app locally with HMR + RPC forwarding");
|
|
@@ -50324,6 +50371,26 @@ Available workspaces:`);
|
|
|
50324
50371
|
}
|
|
50325
50372
|
workflowUsage();
|
|
50326
50373
|
}
|
|
50374
|
+
if (subcommand === "query") {
|
|
50375
|
+
const action = toolArgs;
|
|
50376
|
+
if (action === "set") {
|
|
50377
|
+
const alias = restArgs[0];
|
|
50378
|
+
if (!alias) {
|
|
50379
|
+
console.error("Usage: lotics app query set <alias>");
|
|
50380
|
+
console.error(
|
|
50381
|
+
"Pushes package.json#lotics.queries.<alias> to apps.queries via set_app_query (no deploy)."
|
|
50382
|
+
);
|
|
50383
|
+
process.exit(1);
|
|
50384
|
+
}
|
|
50385
|
+
await appQuerySet(client, { alias });
|
|
50386
|
+
return;
|
|
50387
|
+
}
|
|
50388
|
+
console.error("Usage:");
|
|
50389
|
+
console.error(
|
|
50390
|
+
" lotics app query set <alias> Push package.json#lotics.queries.<alias> to apps.queries (no deploy)"
|
|
50391
|
+
);
|
|
50392
|
+
process.exit(1);
|
|
50393
|
+
}
|
|
50327
50394
|
if (subcommand === "dev") {
|
|
50328
50395
|
const projectDir = toolArgs;
|
|
50329
50396
|
let port;
|