@lotics/cli 0.65.0 → 0.67.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 +102 -27
- 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 +122 -32
- package/package.json +2 -2
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,
|
|
@@ -907,7 +911,16 @@ args) {
|
|
|
907
911
|
});
|
|
908
912
|
console.error(`Deployed v${result.version_number} (${result.version_id})`);
|
|
909
913
|
console.error(`Bundle size: ${(result.bundle_size_bytes / 1024).toFixed(1)} KB`);
|
|
910
|
-
|
|
914
|
+
// One post-deploy getApp feeds both advisory nudges (branding, unbound
|
|
915
|
+
// aliases). Deploy already succeeded — a failed check must not mask that.
|
|
916
|
+
try {
|
|
917
|
+
const app = await client.getApp(meta.app_id);
|
|
918
|
+
warnIfUnbranded(app);
|
|
919
|
+
warnIfUnboundAliases(app, meta.workflows ?? {}, meta.agents ?? {});
|
|
920
|
+
}
|
|
921
|
+
catch (err) {
|
|
922
|
+
console.error(`(skipped post-deploy checks: ${err.message})`);
|
|
923
|
+
}
|
|
911
924
|
}
|
|
912
925
|
catch (err) {
|
|
913
926
|
const e = err;
|
|
@@ -931,24 +944,44 @@ args) {
|
|
|
931
944
|
* generic tile in the launcher. Branding is set via `update_app` (the single
|
|
932
945
|
* setter) — this only reminds; it never fails the deploy.
|
|
933
946
|
*/
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
947
|
+
function warnIfUnbranded(app) {
|
|
948
|
+
const missing = [];
|
|
949
|
+
if (!app.icon)
|
|
950
|
+
missing.push("icon");
|
|
951
|
+
if (!app.theme?.color)
|
|
952
|
+
missing.push("color");
|
|
953
|
+
if (missing.length === 0)
|
|
954
|
+
return;
|
|
955
|
+
console.error(`\n⚠ This app has no ${missing.join(" or ")} set — it shows a generic tile in the launcher.\n` +
|
|
956
|
+
` Set it: lotics run update_app '{"app_id":"${app.id}","icon":"<lucide-name>","theme":{"color":"blue"}}'\n` +
|
|
957
|
+
` Find an icon: lotics run search_app_icons '{"query":"<word>"}'`);
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* Non-blocking nudge after a successful deploy: catch manifest workflow/agent aliases
|
|
961
|
+
* that are NOT bound on the server. Deploy ships code + queries only — it never binds
|
|
962
|
+
* workflows/agents (`set_app_workflow` / `set_app_agent` own `apps.workflows`/`apps.agents`;
|
|
963
|
+
* the manifest maps are a pulled reflection). So an alias hand-added to `lotics.workflows`
|
|
964
|
+
* / `lotics.agents` (or bound then removed) compiles + deploys clean and only throws when
|
|
965
|
+
* the app first calls `useWorkflow` / `useAgentRun`. Surface that divergence HERE — at
|
|
966
|
+
* deploy time — instead of at the user's first click. Advisory only; never fails the deploy.
|
|
967
|
+
*/
|
|
968
|
+
function warnIfUnboundAliases(app, declaredWorkflows, declaredAgents) {
|
|
969
|
+
const boundWorkflows = new Set(Object.keys(app.workflows ?? {}));
|
|
970
|
+
const boundAgents = new Set(Object.keys(app.agents ?? {}));
|
|
971
|
+
const unboundWorkflows = Object.keys(declaredWorkflows).filter((a) => !boundWorkflows.has(a));
|
|
972
|
+
const unboundAgents = Object.keys(declaredAgents).filter((a) => !boundAgents.has(a));
|
|
973
|
+
if (unboundWorkflows.length === 0 && unboundAgents.length === 0)
|
|
974
|
+
return;
|
|
975
|
+
const lines = [
|
|
976
|
+
"\n⚠ The manifest declares aliases that are NOT bound on the server. Deploy ships code +",
|
|
977
|
+
" queries only — it does NOT bind workflows/agents, so the app will throw \"has no … alias\"",
|
|
978
|
+
" the first time it calls them. Bind each one:",
|
|
979
|
+
];
|
|
980
|
+
for (const alias of unboundWorkflows)
|
|
981
|
+
lines.push(` • workflow "${alias}" → lotics app workflow set ${alias}`);
|
|
982
|
+
for (const alias of unboundAgents)
|
|
983
|
+
lines.push(` • agent "${alias}" → bind with set_app_agent (lotics run set_app_agent …)`);
|
|
984
|
+
console.error(lines.join("\n"));
|
|
952
985
|
}
|
|
953
986
|
/**
|
|
954
987
|
* `lotics app dev [path] [--port=5174] [--vite-port=5173]`
|
|
@@ -1198,6 +1231,37 @@ export async function appWorkflowSet(client, args) {
|
|
|
1198
1231
|
console.error(` result.data schema: ${JSON.stringify(result.outputs)}`);
|
|
1199
1232
|
}
|
|
1200
1233
|
}
|
|
1234
|
+
/**
|
|
1235
|
+
* `lotics app query set <alias>` — push `package.json#lotics.queries.<alias>` to
|
|
1236
|
+
* `apps.queries` through `set_app_query`, WITHOUT a deploy. The deploy-free inner
|
|
1237
|
+
* loop for named queries, parallel to `lotics app workflow set` for workflows.
|
|
1238
|
+
*
|
|
1239
|
+
* The declaration (`{ ast, params? }`) is read from the manifest — the same map
|
|
1240
|
+
* `useQuery` codegen reads and `lotics app deploy` syncs authoritatively. The
|
|
1241
|
+
* server validates it exactly as a deploy does (alias identifier, workspace-only
|
|
1242
|
+
* tables, resolvable fields, declared params). Because `apps.queries` is
|
|
1243
|
+
* manifest-authoritative, the next `lotics app deploy` overwrites this from the
|
|
1244
|
+
* manifest — so keep the manifest as the source of truth; this only skips the
|
|
1245
|
+
* build/upload round-trip while iterating. Errors (unbound alias, validation
|
|
1246
|
+
* failure) print to stderr and exit non-zero.
|
|
1247
|
+
*/
|
|
1248
|
+
export async function appQuerySet(client, args) {
|
|
1249
|
+
const projectDir = process.cwd();
|
|
1250
|
+
const meta = readAppMeta(projectDir);
|
|
1251
|
+
const declaration = meta.queries?.[args.alias];
|
|
1252
|
+
if (!declaration) {
|
|
1253
|
+
console.error(`No query "${args.alias}" in package.json#lotics.queries. ` +
|
|
1254
|
+
`Declare it there (alias → { ast, params? }) first.`);
|
|
1255
|
+
process.exit(1);
|
|
1256
|
+
}
|
|
1257
|
+
const res = await client.setAppQuery(meta.app_id, args.alias, declaration);
|
|
1258
|
+
if (res.error) {
|
|
1259
|
+
console.error(`Failed to set query "${args.alias}": ${res.error}`);
|
|
1260
|
+
process.exit(1);
|
|
1261
|
+
}
|
|
1262
|
+
console.error(`Set query "${args.alias}" on ${meta.app_id}. ` +
|
|
1263
|
+
`(apps.queries is manifest-authoritative — the next 'lotics app deploy' re-syncs it.)`);
|
|
1264
|
+
}
|
|
1201
1265
|
/**
|
|
1202
1266
|
* `lotics app workflow pull` — rewrite every `src/workflows/<alias>.ts` from the
|
|
1203
1267
|
* server without a full `lotics app pull` (no source archive, no npm install).
|
|
@@ -1358,11 +1422,18 @@ export function appUiLink(args) {
|
|
|
1358
1422
|
if (!fs.existsSync(viteConfigPath)) {
|
|
1359
1423
|
throw new Error(`No vite.config.ts at ${projectDir}. Run inside a 'lotics app' project directory.`);
|
|
1360
1424
|
}
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1425
|
+
// Resolve packages/ui/src. An explicit --ui-src / LOTICS_UI_SRC wins — that's how
|
|
1426
|
+
// an EXTERNAL app (one that consumes @lotics/ui from npm, with no monorepo above
|
|
1427
|
+
// it) links the local kit; otherwise walk up for a monorepo checkout.
|
|
1428
|
+
const explicit = args.uiSrc ?? process.env.LOTICS_UI_SRC;
|
|
1429
|
+
const uiSrc = explicit ? path.resolve(explicit) : findUiSrcDir(projectDir);
|
|
1430
|
+
if (!uiSrc || !fs.existsSync(uiSrc) || !fs.statSync(uiSrc).isDirectory()) {
|
|
1431
|
+
throw new Error(explicit
|
|
1432
|
+
? `--ui-src / LOTICS_UI_SRC points at '${explicit}', which is not a directory. ` +
|
|
1433
|
+
`Pass the absolute path to the monorepo's packages/ui/src.`
|
|
1434
|
+
: "Cannot find packages/ui/src by walking up from this directory. For an EXTERNAL app " +
|
|
1435
|
+
"(consuming @lotics/ui from npm), pass --ui-src=<abs path to packages/ui/src> or set " +
|
|
1436
|
+
"LOTICS_UI_SRC; inside a monorepo checkout it is found automatically.");
|
|
1366
1437
|
}
|
|
1367
1438
|
// Validate the named component exists in src so a typo fails loud (the alias
|
|
1368
1439
|
// itself stays package-wide — this is the advisory check the spec calls for).
|
|
@@ -1403,6 +1474,10 @@ export function appUiLink(args) {
|
|
|
1403
1474
|
fs.writeFileSync(viteConfigPath, updated);
|
|
1404
1475
|
console.error(`Dev-linked @lotics/ui → ${uiSrc} in ${viteConfigPath}.`);
|
|
1405
1476
|
console.error("Restart `lotics app dev` and rm -rf node_modules/.vite to clear cached modules.");
|
|
1477
|
+
// The app's tsc still resolves @lotics/ui from node_modules (the published .d.ts) —
|
|
1478
|
+
// the kit `src` can't be typechecked in an app because it's RN-Web (uses
|
|
1479
|
+
// react-native-web types the app resolves as base react-native). Typecheck the kit
|
|
1480
|
+
// in packages/ui; the finalize publish restores the app's own typecheck.
|
|
1406
1481
|
console.error("Finalize: PR the packages/ui change → publish → `lotics ui link <component> --remove` + bump the app's dep.");
|
|
1407
1482
|
}
|
|
1408
1483
|
/** 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,
|
|
@@ -32742,7 +32758,13 @@ async function appDeploy(client, args) {
|
|
|
32742
32758
|
});
|
|
32743
32759
|
console.error(`Deployed v${result.version_number} (${result.version_id})`);
|
|
32744
32760
|
console.error(`Bundle size: ${(result.bundle_size_bytes / 1024).toFixed(1)} KB`);
|
|
32745
|
-
|
|
32761
|
+
try {
|
|
32762
|
+
const app = await client.getApp(meta.app_id);
|
|
32763
|
+
warnIfUnbranded(app);
|
|
32764
|
+
warnIfUnboundAliases(app, meta.workflows ?? {}, meta.agents ?? {});
|
|
32765
|
+
} catch (err2) {
|
|
32766
|
+
console.error(`(skipped post-deploy checks: ${err2.message})`);
|
|
32767
|
+
}
|
|
32746
32768
|
} catch (err2) {
|
|
32747
32769
|
const e = err2;
|
|
32748
32770
|
if (e.code === "VERSION_CONFLICT") {
|
|
@@ -32758,22 +32780,32 @@ async function appDeploy(client, args) {
|
|
|
32758
32780
|
if (fs4.existsSync(tmpDist)) fs4.unlinkSync(tmpDist);
|
|
32759
32781
|
}
|
|
32760
32782
|
}
|
|
32761
|
-
|
|
32762
|
-
|
|
32763
|
-
|
|
32764
|
-
|
|
32765
|
-
|
|
32766
|
-
|
|
32767
|
-
|
|
32768
|
-
console.error(
|
|
32769
|
-
`
|
|
32783
|
+
function warnIfUnbranded(app) {
|
|
32784
|
+
const missing = [];
|
|
32785
|
+
if (!app.icon) missing.push("icon");
|
|
32786
|
+
if (!app.theme?.color) missing.push("color");
|
|
32787
|
+
if (missing.length === 0) return;
|
|
32788
|
+
console.error(
|
|
32789
|
+
`
|
|
32770
32790
|
\u26A0 This app has no ${missing.join(" or ")} set \u2014 it shows a generic tile in the launcher.
|
|
32771
|
-
Set it: lotics run update_app '{"app_id":"${
|
|
32791
|
+
Set it: lotics run update_app '{"app_id":"${app.id}","icon":"<lucide-name>","theme":{"color":"blue"}}'
|
|
32772
32792
|
Find an icon: lotics run search_app_icons '{"query":"<word>"}'`
|
|
32773
|
-
|
|
32774
|
-
|
|
32775
|
-
|
|
32776
|
-
}
|
|
32793
|
+
);
|
|
32794
|
+
}
|
|
32795
|
+
function warnIfUnboundAliases(app, declaredWorkflows, declaredAgents) {
|
|
32796
|
+
const boundWorkflows = new Set(Object.keys(app.workflows ?? {}));
|
|
32797
|
+
const boundAgents = new Set(Object.keys(app.agents ?? {}));
|
|
32798
|
+
const unboundWorkflows = Object.keys(declaredWorkflows).filter((a) => !boundWorkflows.has(a));
|
|
32799
|
+
const unboundAgents = Object.keys(declaredAgents).filter((a) => !boundAgents.has(a));
|
|
32800
|
+
if (unboundWorkflows.length === 0 && unboundAgents.length === 0) return;
|
|
32801
|
+
const lines = [
|
|
32802
|
+
"\n\u26A0 The manifest declares aliases that are NOT bound on the server. Deploy ships code +",
|
|
32803
|
+
' queries only \u2014 it does NOT bind workflows/agents, so the app will throw "has no \u2026 alias"',
|
|
32804
|
+
" the first time it calls them. Bind each one:"
|
|
32805
|
+
];
|
|
32806
|
+
for (const alias of unboundWorkflows) lines.push(` \u2022 workflow "${alias}" \u2192 lotics app workflow set ${alias}`);
|
|
32807
|
+
for (const alias of unboundAgents) lines.push(` \u2022 agent "${alias}" \u2192 bind with set_app_agent (lotics run set_app_agent \u2026)`);
|
|
32808
|
+
console.error(lines.join("\n"));
|
|
32777
32809
|
}
|
|
32778
32810
|
async function appDev(client, args) {
|
|
32779
32811
|
const projectDir = path5.resolve(args.projectDir ?? process.cwd());
|
|
@@ -32942,6 +32974,25 @@ async function appWorkflowSet(client, args) {
|
|
|
32942
32974
|
console.error(` result.data schema: ${JSON.stringify(result.outputs)}`);
|
|
32943
32975
|
}
|
|
32944
32976
|
}
|
|
32977
|
+
async function appQuerySet(client, args) {
|
|
32978
|
+
const projectDir = process.cwd();
|
|
32979
|
+
const meta = readAppMeta(projectDir);
|
|
32980
|
+
const declaration = meta.queries?.[args.alias];
|
|
32981
|
+
if (!declaration) {
|
|
32982
|
+
console.error(
|
|
32983
|
+
`No query "${args.alias}" in package.json#lotics.queries. Declare it there (alias \u2192 { ast, params? }) first.`
|
|
32984
|
+
);
|
|
32985
|
+
process.exit(1);
|
|
32986
|
+
}
|
|
32987
|
+
const res = await client.setAppQuery(meta.app_id, args.alias, declaration);
|
|
32988
|
+
if (res.error) {
|
|
32989
|
+
console.error(`Failed to set query "${args.alias}": ${res.error}`);
|
|
32990
|
+
process.exit(1);
|
|
32991
|
+
}
|
|
32992
|
+
console.error(
|
|
32993
|
+
`Set query "${args.alias}" on ${meta.app_id}. (apps.queries is manifest-authoritative \u2014 the next 'lotics app deploy' re-syncs it.)`
|
|
32994
|
+
);
|
|
32995
|
+
}
|
|
32945
32996
|
async function appWorkflowPull(client) {
|
|
32946
32997
|
const projectDir = process.cwd();
|
|
32947
32998
|
const meta = readAppMeta(projectDir);
|
|
@@ -33050,10 +33101,11 @@ function appUiLink(args) {
|
|
|
33050
33101
|
`No vite.config.ts at ${projectDir}. Run inside a 'lotics app' project directory.`
|
|
33051
33102
|
);
|
|
33052
33103
|
}
|
|
33053
|
-
const
|
|
33054
|
-
|
|
33104
|
+
const explicit = args.uiSrc ?? process.env.LOTICS_UI_SRC;
|
|
33105
|
+
const uiSrc = explicit ? path5.resolve(explicit) : findUiSrcDir(projectDir);
|
|
33106
|
+
if (!uiSrc || !fs4.existsSync(uiSrc) || !fs4.statSync(uiSrc).isDirectory()) {
|
|
33055
33107
|
throw new Error(
|
|
33056
|
-
"Cannot find packages/ui/src by walking up from this directory
|
|
33108
|
+
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
33109
|
);
|
|
33058
33110
|
}
|
|
33059
33111
|
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 +33160,7 @@ function parseArgs(argv) {
|
|
|
33108
33160
|
apiKey: void 0,
|
|
33109
33161
|
workspace: void 0,
|
|
33110
33162
|
viewAs: void 0,
|
|
33163
|
+
uiSrc: void 0,
|
|
33111
33164
|
name: void 0,
|
|
33112
33165
|
timezone: void 0,
|
|
33113
33166
|
message: void 0,
|
|
@@ -33149,6 +33202,9 @@ function parseArgs(argv) {
|
|
|
33149
33202
|
case "--view-as":
|
|
33150
33203
|
flags.viewAs = argv[++i2];
|
|
33151
33204
|
break;
|
|
33205
|
+
case "--ui-src":
|
|
33206
|
+
flags.uiSrc = argv[++i2];
|
|
33207
|
+
break;
|
|
33152
33208
|
case "--name":
|
|
33153
33209
|
flags.name = argv[++i2];
|
|
33154
33210
|
break;
|
|
@@ -42676,6 +42732,7 @@ function readCfbStreams(bytes) {
|
|
|
42676
42732
|
throw new Error("Unsupported .xls: invalid OLE2 sector size");
|
|
42677
42733
|
}
|
|
42678
42734
|
const sectorOffset = (n) => sectorSize * (n + 1);
|
|
42735
|
+
const maxSectors = Math.ceil(bytes.length / sectorSize) + 1;
|
|
42679
42736
|
const difat = [];
|
|
42680
42737
|
for (let i2 = 0; i2 < 109; i2++) {
|
|
42681
42738
|
const v = u32(76 + i2 * 4);
|
|
@@ -42683,7 +42740,9 @@ function readCfbStreams(bytes) {
|
|
|
42683
42740
|
}
|
|
42684
42741
|
let ds = firstDifatSector;
|
|
42685
42742
|
const entriesPerSector = sectorSize / 4;
|
|
42686
|
-
|
|
42743
|
+
const seenDifat = /* @__PURE__ */ new Set();
|
|
42744
|
+
while (ds !== ENDOFCHAIN && ds !== FREESECT && ds < maxSectors && !seenDifat.has(ds)) {
|
|
42745
|
+
seenDifat.add(ds);
|
|
42687
42746
|
const base = sectorOffset(ds);
|
|
42688
42747
|
for (let i2 = 0; i2 < entriesPerSector - 1; i2++) {
|
|
42689
42748
|
const v = u32(base + i2 * 4);
|
|
@@ -42693,14 +42752,18 @@ function readCfbStreams(bytes) {
|
|
|
42693
42752
|
}
|
|
42694
42753
|
const fat = [];
|
|
42695
42754
|
for (const fatSector of difat) {
|
|
42755
|
+
if (fat.length >= maxSectors) break;
|
|
42756
|
+
if (fatSector >= maxSectors) continue;
|
|
42696
42757
|
const base = sectorOffset(fatSector);
|
|
42697
|
-
for (let i2 = 0; i2 < entriesPerSector
|
|
42758
|
+
for (let i2 = 0; i2 < entriesPerSector && fat.length < maxSectors; i2++) {
|
|
42759
|
+
fat.push(u32(base + i2 * 4));
|
|
42760
|
+
}
|
|
42698
42761
|
}
|
|
42699
42762
|
const readChain = (src, start, size, ss, offOf, chain) => {
|
|
42700
|
-
const
|
|
42763
|
+
const maxSectors2 = Math.floor(src.length / ss) + 2;
|
|
42701
42764
|
const parts = [];
|
|
42702
42765
|
let s = start;
|
|
42703
|
-
for (let guard = 0; s !== ENDOFCHAIN && s !== FREESECT && s < chain.length && guard <
|
|
42766
|
+
for (let guard = 0; s !== ENDOFCHAIN && s !== FREESECT && s < chain.length && guard < maxSectors2; guard++) {
|
|
42704
42767
|
const o = offOf(s);
|
|
42705
42768
|
parts.push(src.subarray(o, o + ss));
|
|
42706
42769
|
s = chain[s];
|
|
@@ -49650,11 +49713,16 @@ COMMANDS
|
|
|
49650
49713
|
lotics app workflow pull Rewrite src/workflows/*.ts from the server
|
|
49651
49714
|
lotics app workflow check [alias] Typecheck src/workflows bodies locally (one
|
|
49652
49715
|
isolated program per alias; the app's own tsc)
|
|
49716
|
+
lotics app query set <alias> Push package.json#lotics.queries.<alias> to
|
|
49717
|
+
apps.queries via set_app_query (no deploy;
|
|
49718
|
+
re-synced by the next deploy from the manifest)
|
|
49653
49719
|
lotics app subdomain <new-subdomain> Rename the app's public <slug>.lotics.app address
|
|
49654
49720
|
lotics app rename "<new name>" Rename the app's display name (launcher title)
|
|
49655
49721
|
lotics app dev [path] Run the app locally with HMR (RPC forwarded to prod)
|
|
49656
|
-
lotics ui link <component> [--
|
|
49657
|
-
packages/ui/src
|
|
49722
|
+
lotics ui link <component> [--ui-src <path>] [--remove]
|
|
49723
|
+
Dev-link @lotics/ui to packages/ui/src (Vite alias
|
|
49724
|
+
+ tsc paths) for live HMR + typecheck. Monorepo apps
|
|
49725
|
+
auto-find it; external apps pass --ui-src / LOTICS_UI_SRC
|
|
49658
49726
|
lotics xlsx <subcommand> ... Read/write/edit .xlsx files on your local filesystem
|
|
49659
49727
|
(uses the bundled Lotics xlsx engine; prefer over
|
|
49660
49728
|
npm xlsx/exceljs for round-trip fidelity)
|
|
@@ -50018,15 +50086,16 @@ async function main() {
|
|
|
50018
50086
|
if (subcommand === "link") {
|
|
50019
50087
|
const component = toolArgs;
|
|
50020
50088
|
if (!component) {
|
|
50021
|
-
console.error("Usage: lotics ui link <component> [--remove]");
|
|
50022
|
-
console.error("Dev-links @lotics/ui to
|
|
50089
|
+
console.error("Usage: lotics ui link <component> [--ui-src <abs path>] [--remove]");
|
|
50090
|
+
console.error("Dev-links @lotics/ui to packages/ui/src (Vite + tsc) for live HMR + typecheck.");
|
|
50091
|
+
console.error("Monorepo apps find packages/ui/src automatically; external apps pass --ui-src / LOTICS_UI_SRC.");
|
|
50023
50092
|
process.exit(1);
|
|
50024
50093
|
}
|
|
50025
|
-
appUiLink({ component, remove: restArgs.includes("--remove") });
|
|
50094
|
+
appUiLink({ component, uiSrc: flags.uiSrc, remove: restArgs.includes("--remove") });
|
|
50026
50095
|
return;
|
|
50027
50096
|
}
|
|
50028
50097
|
console.error(`Unknown ui subcommand: ${subcommand ?? "(none)"}`);
|
|
50029
|
-
console.error("Usage: lotics ui link <component> [--remove]");
|
|
50098
|
+
console.error("Usage: lotics ui link <component> [--ui-src <abs path>] [--remove]");
|
|
50030
50099
|
process.exit(1);
|
|
50031
50100
|
}
|
|
50032
50101
|
if (command === "app" && subcommand === "workflow" && toolArgs === "check") {
|
|
@@ -50119,6 +50188,7 @@ async function main() {
|
|
|
50119
50188
|
console.error(" lotics app workflow set <alias> Push the edited src/workflows/<alias>.ts body");
|
|
50120
50189
|
console.error(" lotics app workflow pull Rewrite src/workflows/*.ts from the server");
|
|
50121
50190
|
console.error(" lotics app workflow check [alias] Typecheck src/workflows bodies locally");
|
|
50191
|
+
console.error(" lotics app query set <alias> Push lotics.queries.<alias> to apps.queries (no deploy)");
|
|
50122
50192
|
console.error(" lotics app subdomain <new-subdomain> Rename the app's public <slug>.lotics.app address");
|
|
50123
50193
|
console.error(` lotics app rename "<new name>" Rename the app's display name (launcher title)`);
|
|
50124
50194
|
console.error(" lotics app dev [path] Run the app locally with HMR + RPC forwarding");
|
|
@@ -50324,6 +50394,26 @@ Available workspaces:`);
|
|
|
50324
50394
|
}
|
|
50325
50395
|
workflowUsage();
|
|
50326
50396
|
}
|
|
50397
|
+
if (subcommand === "query") {
|
|
50398
|
+
const action = toolArgs;
|
|
50399
|
+
if (action === "set") {
|
|
50400
|
+
const alias = restArgs[0];
|
|
50401
|
+
if (!alias) {
|
|
50402
|
+
console.error("Usage: lotics app query set <alias>");
|
|
50403
|
+
console.error(
|
|
50404
|
+
"Pushes package.json#lotics.queries.<alias> to apps.queries via set_app_query (no deploy)."
|
|
50405
|
+
);
|
|
50406
|
+
process.exit(1);
|
|
50407
|
+
}
|
|
50408
|
+
await appQuerySet(client, { alias });
|
|
50409
|
+
return;
|
|
50410
|
+
}
|
|
50411
|
+
console.error("Usage:");
|
|
50412
|
+
console.error(
|
|
50413
|
+
" lotics app query set <alias> Push package.json#lotics.queries.<alias> to apps.queries (no deploy)"
|
|
50414
|
+
);
|
|
50415
|
+
process.exit(1);
|
|
50416
|
+
}
|
|
50327
50417
|
if (subcommand === "dev") {
|
|
50328
50418
|
const projectDir = toolArgs;
|
|
50329
50419
|
let port;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lotics/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.67.0",
|
|
4
4
|
"description": "Lotics SDK and CLI for AI agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"@types/node": "^22.15.21",
|
|
27
27
|
"esbuild": "^0.28.1",
|
|
28
28
|
"typescript": "^6.0.3",
|
|
29
|
-
"vitest": "^4.1.
|
|
29
|
+
"vitest": "^4.1.9"
|
|
30
30
|
},
|
|
31
31
|
"keywords": [
|
|
32
32
|
"lotics",
|