@lotics/cli 0.100.0 → 0.100.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/cli.js +94 -42
- package/dist/src/client.d.ts +4 -6
- package/dist/src/client.js +5 -5
- package/package.json +2 -1
package/dist/src/cli.js
CHANGED
|
@@ -44769,8 +44769,8 @@ var LoticsClient = class {
|
|
|
44769
44769
|
* no caller has to remember that a partial payload silently drops
|
|
44770
44770
|
* `instructions`, `outputs` and the model pin.
|
|
44771
44771
|
*/
|
|
44772
|
-
async setAppAgent(app_id, alias,
|
|
44773
|
-
return this.execute("set_app_agent", { app_id, alias, ...
|
|
44772
|
+
async setAppAgent(app_id, alias, patch) {
|
|
44773
|
+
return this.execute("set_app_agent", { app_id, alias, ...patch });
|
|
44774
44774
|
}
|
|
44775
44775
|
/**
|
|
44776
44776
|
* Fetch one app workflow's faithful source + bound input/output schemas via
|
|
@@ -47256,6 +47256,24 @@ function undeclaredCapabilities(sourceText, declared) {
|
|
|
47256
47256
|
}
|
|
47257
47257
|
return used;
|
|
47258
47258
|
}
|
|
47259
|
+
var ALIAS_CALL_HOOKS = { workflows: "useWorkflow", agents: "useAgentRun" };
|
|
47260
|
+
var LITERAL_ALIAS_ARG = `["'\`]([A-Za-z_$][A-Za-z0-9_$]*)["'\`]`;
|
|
47261
|
+
function calledAppAliases(sourceText) {
|
|
47262
|
+
const out = { workflows: [], agents: [], dynamic: [] };
|
|
47263
|
+
for (const [kind, hook] of Object.entries(ALIAS_CALL_HOOKS)) {
|
|
47264
|
+
const seen = /* @__PURE__ */ new Set();
|
|
47265
|
+
let isDynamic = false;
|
|
47266
|
+
for (const call of sourceText.matchAll(new RegExp(`\\b${hook}\\s*\\(`, "g"))) {
|
|
47267
|
+
const rest = sourceText.slice(call.index + call[0].length);
|
|
47268
|
+
const literal2 = new RegExp(`^\\s*${LITERAL_ALIAS_ARG}`).exec(rest);
|
|
47269
|
+
if (literal2) seen.add(literal2[1]);
|
|
47270
|
+
else isDynamic = true;
|
|
47271
|
+
}
|
|
47272
|
+
out[kind] = [...seen];
|
|
47273
|
+
if (isDynamic) out.dynamic.push(hook);
|
|
47274
|
+
}
|
|
47275
|
+
return out;
|
|
47276
|
+
}
|
|
47259
47277
|
function unboundAliases(declared, bound) {
|
|
47260
47278
|
const boundWorkflows = new Set(bound.workflows ?? []);
|
|
47261
47279
|
const boundAgents = new Set(bound.agents ?? []);
|
|
@@ -69927,9 +69945,9 @@ function findPlaceableWindow(chars, from, width) {
|
|
|
69927
69945
|
return null;
|
|
69928
69946
|
}
|
|
69929
69947
|
|
|
69930
|
-
// src/workflow_envelope.ts
|
|
69931
|
-
var
|
|
69932
|
-
var
|
|
69948
|
+
// ../shared/src/workflow_envelope.ts
|
|
69949
|
+
var WORKFLOW_ENVELOPE_PREFIX = "async function __workflow(): Promise<__WorkflowReturn | void> {\n";
|
|
69950
|
+
var WORKFLOW_ENVELOPE_SUFFIX = "\n}";
|
|
69933
69951
|
var WORKFLOW_WRAPPER_OPENER = /^\s*async\s+function\s+__workflow\s*\(/;
|
|
69934
69952
|
function stripWorkflowHeader(content) {
|
|
69935
69953
|
const lines = content.split("\n");
|
|
@@ -70074,6 +70092,25 @@ function checkWorkflowBodies(tsApi, aliases) {
|
|
|
70074
70092
|
}));
|
|
70075
70093
|
}
|
|
70076
70094
|
|
|
70095
|
+
// ../shared/src/agent_instructions.ts
|
|
70096
|
+
function agentFilePath(alias) {
|
|
70097
|
+
return `src/agents/${alias}.md`;
|
|
70098
|
+
}
|
|
70099
|
+
function agentFileHeader(alias, notes = []) {
|
|
70100
|
+
return `<!-- lotics: instructions for agent "${alias}".
|
|
70101
|
+
` + notes.map((n) => ` ${n}
|
|
70102
|
+
`).join("") + ` This comment is stripped before the prose reaches the live prompt. -->`;
|
|
70103
|
+
}
|
|
70104
|
+
function wrapAgentFile(args) {
|
|
70105
|
+
return `${agentFileHeader(args.alias, args.notes)}
|
|
70106
|
+
|
|
70107
|
+
${args.instructions.replace(/\s+$/, "")}
|
|
70108
|
+
`;
|
|
70109
|
+
}
|
|
70110
|
+
function stripAgentHeader(text) {
|
|
70111
|
+
return text.replace(/<!--\s*lotics:[\s\S]*?-->\n?/g, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
70112
|
+
}
|
|
70113
|
+
|
|
70077
70114
|
// src/app_commands.ts
|
|
70078
70115
|
async function fetchLatestNpmVersion(packageName) {
|
|
70079
70116
|
try {
|
|
@@ -70105,8 +70142,11 @@ var STALE_LOTICS_INCLUDES = /* @__PURE__ */ new Set([".lotics", "./.lotics", ".l
|
|
|
70105
70142
|
function toWorkflowDtsDeclaration(d) {
|
|
70106
70143
|
return { inputs: d.inputs, outputs: d.outputs };
|
|
70107
70144
|
}
|
|
70145
|
+
function isMissingWorkflowBodyError(error51) {
|
|
70146
|
+
return /has no step source|cannot be rendered|unsupported step/i.test(error51);
|
|
70147
|
+
}
|
|
70108
70148
|
function isUnknownWorkflowAliasError(err2) {
|
|
70109
|
-
return err2 instanceof Error &&
|
|
70149
|
+
return err2 instanceof Error && err2.message.startsWith("400:") && /has no workflow alias/i.test(err2.message);
|
|
70110
70150
|
}
|
|
70111
70151
|
function workflowFileHeader(alias) {
|
|
70112
70152
|
const refPath = path5.join("..", "..", WORKFLOW_GLOBALS_DIR, `${alias}.globals.d.ts`).split(path5.sep).join("/");
|
|
@@ -70136,7 +70176,7 @@ function writeWorkflowGlobals(projectDir, alias, dts) {
|
|
|
70136
70176
|
`);
|
|
70137
70177
|
return file2;
|
|
70138
70178
|
}
|
|
70139
|
-
function writeWorkflowFile(projectDir, alias, source, envelope = { prefix:
|
|
70179
|
+
function writeWorkflowFile(projectDir, alias, source, envelope = { prefix: WORKFLOW_ENVELOPE_PREFIX, suffix: WORKFLOW_ENVELOPE_SUFFIX }) {
|
|
70140
70180
|
const dir = path5.join(projectDir, WORKFLOWS_DIR);
|
|
70141
70181
|
fs4.mkdirSync(dir, { recursive: true });
|
|
70142
70182
|
const file2 = workflowFilePath(projectDir, alias);
|
|
@@ -70146,24 +70186,20 @@ ${envelope.prefix}${body}${envelope.suffix}
|
|
|
70146
70186
|
`);
|
|
70147
70187
|
return file2;
|
|
70148
70188
|
}
|
|
70149
|
-
function
|
|
70150
|
-
return path5.join(projectDir,
|
|
70151
|
-
}
|
|
70152
|
-
function agentFileHeader(alias) {
|
|
70153
|
-
return `<!-- lotics: instructions for agent "${alias}".
|
|
70154
|
-
Pulled from the LIVE app row; edit here, then: lotics app agent set ${alias}
|
|
70155
|
-
The typed fields (inputs/outputs/tool_names/model_id) live in package.json#lotics.agents.${alias} -->`;
|
|
70156
|
-
}
|
|
70157
|
-
function stripAgentHeader(text) {
|
|
70158
|
-
return text.replace(/<!--\s*lotics:[\s\S]*?-->\n?/g, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
70189
|
+
function agentFilePath2(projectDir, alias) {
|
|
70190
|
+
return path5.join(projectDir, ...agentFilePath(alias).split("/"));
|
|
70159
70191
|
}
|
|
70192
|
+
var CLI_AGENT_NOTES = [
|
|
70193
|
+
"Pulled from the LIVE app row; edit here, then: lotics app agent set <alias>",
|
|
70194
|
+
"The typed fields (inputs/outputs/tool_names/model_id) live in package.json#lotics.agents"
|
|
70195
|
+
];
|
|
70160
70196
|
function writeAgentFile(projectDir, alias, instructions) {
|
|
70161
70197
|
fs4.mkdirSync(path5.join(projectDir, AGENTS_DIR), { recursive: true });
|
|
70162
|
-
const file2 =
|
|
70163
|
-
fs4.writeFileSync(
|
|
70164
|
-
|
|
70165
|
-
|
|
70166
|
-
|
|
70198
|
+
const file2 = agentFilePath2(projectDir, alias);
|
|
70199
|
+
fs4.writeFileSync(
|
|
70200
|
+
file2,
|
|
70201
|
+
wrapAgentFile({ alias, instructions, notes: CLI_AGENT_NOTES.map((n) => n.replace("<alias>", alias)) })
|
|
70202
|
+
);
|
|
70167
70203
|
return file2;
|
|
70168
70204
|
}
|
|
70169
70205
|
function writeAgentFiles(projectDir, agents) {
|
|
@@ -70183,10 +70219,15 @@ async function writeWorkflowFiles(client, projectDir, app_id, workflows) {
|
|
|
70183
70219
|
const written = [];
|
|
70184
70220
|
for (const [alias, declaration] of Object.entries(workflows)) {
|
|
70185
70221
|
const res = await client.getAppWorkflow(app_id, alias);
|
|
70186
|
-
|
|
70222
|
+
if (res.error && !isMissingWorkflowBodyError(res.error)) {
|
|
70223
|
+
throw new Error(
|
|
70224
|
+
`Could not read workflow "${alias}" of ${app_id}: ${res.error}. Reading app workflow source requires an admin member \u2014 check the role behind this API key.`
|
|
70225
|
+
);
|
|
70226
|
+
}
|
|
70227
|
+
const source = res.result === null || typeof res.result !== "object" ? null : res.result.source;
|
|
70187
70228
|
if (typeof source !== "string" || source.trim() === "") {
|
|
70188
70229
|
console.error(
|
|
70189
|
-
`\u26A0 Skipped src/workflows/${alias}.ts \u2014
|
|
70230
|
+
`\u26A0 Skipped src/workflows/${alias}.ts \u2014 legacy workflow with no rendered body.`
|
|
70190
70231
|
);
|
|
70191
70232
|
continue;
|
|
70192
70233
|
}
|
|
@@ -70216,7 +70257,7 @@ async function fetchWorkflowGlobals(client, projectDir, app_id, alias, declarati
|
|
|
70216
70257
|
console.error(
|
|
70217
70258
|
`\u26A0 Could not fetch workflow types for "${alias}" (${err2 instanceof Error ? err2.message : String(err2)}). Wrote the body with the fallback wrapper; its local typecheck may be degraded.`
|
|
70218
70259
|
);
|
|
70219
|
-
return { prefix:
|
|
70260
|
+
return { prefix: WORKFLOW_ENVELOPE_PREFIX, suffix: WORKFLOW_ENVELOPE_SUFFIX };
|
|
70220
70261
|
}
|
|
70221
70262
|
}
|
|
70222
70263
|
async function fetchWorkflowDts(client, app_id, alias, declaration) {
|
|
@@ -70684,7 +70725,16 @@ function readAppSourceText(projectDir) {
|
|
|
70684
70725
|
async function appDeploy(client, args) {
|
|
70685
70726
|
const projectDir = path5.resolve(args.projectDir ?? process.cwd());
|
|
70686
70727
|
const meta3 = readAppMeta(projectDir);
|
|
70687
|
-
const
|
|
70728
|
+
const sourceText = readAppSourceText(projectDir);
|
|
70729
|
+
const called = calledAppAliases(sourceText);
|
|
70730
|
+
if (called.dynamic.length > 0) {
|
|
70731
|
+
console.error(
|
|
70732
|
+
`
|
|
70733
|
+
\u26A0 This app calls ${called.dynamic.join(" / ")} with an alias it computes at runtime, which this deploy cannot read.
|
|
70734
|
+
The version records only the aliases named as literals, so removing a binding this app reaches dynamically will NOT be refused.`
|
|
70735
|
+
);
|
|
70736
|
+
}
|
|
70737
|
+
const undeclared = undeclaredCapabilities(sourceText, meta3.capabilities);
|
|
70688
70738
|
if (undeclared.length > 0) {
|
|
70689
70739
|
const block = JSON.stringify(Object.fromEntries(undeclared.map((c) => [c, true])));
|
|
70690
70740
|
console.error(
|
|
@@ -70738,12 +70788,14 @@ async function appDeploy(client, args) {
|
|
|
70738
70788
|
// (fail-safe; the declaration is the grant).
|
|
70739
70789
|
capabilities: meta3.capabilities ?? {},
|
|
70740
70790
|
// Workflow BINDINGS are NOT a deploy concern — set_app_workflow /
|
|
70741
|
-
// remove_app_workflow own apps.workflows.
|
|
70742
|
-
//
|
|
70743
|
-
//
|
|
70744
|
-
//
|
|
70745
|
-
//
|
|
70746
|
-
|
|
70791
|
+
// remove_app_workflow own apps.workflows. What IS sent is the set of
|
|
70792
|
+
// aliases this bundle CALLS (never the bindings), so remove_app_workflow
|
|
70793
|
+
// can refuse to unbind one the served version still uses. Read off the
|
|
70794
|
+
// source, the same rule the chat publish uses: the manifest's map is a
|
|
70795
|
+
// typing artifact maintained by habit, so its keys answered this question
|
|
70796
|
+
// with whatever the author last remembered to write. Stop calling an
|
|
70797
|
+
// alias + redeploy to lift the guard before removing its binding.
|
|
70798
|
+
workflow_aliases: called.workflows
|
|
70747
70799
|
});
|
|
70748
70800
|
writeAppMeta(projectDir, {
|
|
70749
70801
|
...meta3,
|
|
@@ -70755,7 +70807,7 @@ async function appDeploy(client, args) {
|
|
|
70755
70807
|
try {
|
|
70756
70808
|
const app = await client.getApp(meta3.app_id);
|
|
70757
70809
|
warnIfUnbranded(app);
|
|
70758
|
-
warnIfUnboundAliases(app,
|
|
70810
|
+
warnIfUnboundAliases(app, called);
|
|
70759
70811
|
} catch (err2) {
|
|
70760
70812
|
console.error(`(skipped post-deploy checks: ${err2.message})`);
|
|
70761
70813
|
}
|
|
@@ -70786,14 +70838,14 @@ function warnIfUnbranded(app) {
|
|
|
70786
70838
|
Find an icon: lotics run search_app_icons '{"query":"<word>"}'`
|
|
70787
70839
|
);
|
|
70788
70840
|
}
|
|
70789
|
-
function warnIfUnboundAliases(app,
|
|
70790
|
-
const { workflows: unboundWorkflows, agents: unboundAgents } = unboundAliases(
|
|
70791
|
-
|
|
70792
|
-
|
|
70793
|
-
);
|
|
70841
|
+
function warnIfUnboundAliases(app, called) {
|
|
70842
|
+
const { workflows: unboundWorkflows, agents: unboundAgents } = unboundAliases(called, {
|
|
70843
|
+
workflows: Object.keys(app.workflows ?? {}),
|
|
70844
|
+
agents: Object.keys(app.agents ?? {})
|
|
70845
|
+
});
|
|
70794
70846
|
if (unboundWorkflows.length === 0 && unboundAgents.length === 0) return;
|
|
70795
70847
|
const lines = [
|
|
70796
|
-
"\n\u26A0
|
|
70848
|
+
"\n\u26A0 This app CALLS aliases that are NOT bound on the server. Deploy ships code +",
|
|
70797
70849
|
' queries only \u2014 it does NOT bind workflows/agents, so the app will throw "has no \u2026 alias"',
|
|
70798
70850
|
" the first time it calls them. Bind each one:"
|
|
70799
70851
|
];
|
|
@@ -71031,7 +71083,7 @@ async function appAgentSet(client, args) {
|
|
|
71031
71083
|
);
|
|
71032
71084
|
process.exit(1);
|
|
71033
71085
|
}
|
|
71034
|
-
const file2 =
|
|
71086
|
+
const file2 = agentFilePath2(projectDir, args.alias);
|
|
71035
71087
|
if (!fs4.existsSync(file2)) {
|
|
71036
71088
|
console.error(
|
|
71037
71089
|
`No instructions at ${path5.relative(projectDir, file2)}. Run 'lotics app pull ${meta3.app_id}' to write ${AGENTS_DIR}/${args.alias}.md, then edit it.`
|
|
@@ -71045,13 +71097,13 @@ async function appAgentSet(client, args) {
|
|
|
71045
71097
|
);
|
|
71046
71098
|
process.exit(1);
|
|
71047
71099
|
}
|
|
71048
|
-
const res = await client.setAppAgent(meta3.app_id, args.alias, {
|
|
71100
|
+
const res = await client.setAppAgent(meta3.app_id, args.alias, { instructions });
|
|
71049
71101
|
if (res.error) {
|
|
71050
71102
|
console.error(`Failed to set agent "${args.alias}": ${res.error}`);
|
|
71051
71103
|
process.exit(1);
|
|
71052
71104
|
}
|
|
71053
71105
|
console.error(
|
|
71054
|
-
`Set agent "${args.alias}" (${instructions.length} chars of instructions` + (live.model_id ? `, ${live.model_id}` : "") + `). Typed fields
|
|
71106
|
+
`Set agent "${args.alias}" (${instructions.length} chars of instructions` + (live.model_id ? `, ${live.model_id}` : "") + `). Typed fields left as they are.`
|
|
71055
71107
|
);
|
|
71056
71108
|
}
|
|
71057
71109
|
async function appWorkflowSet(client, args) {
|
package/dist/src/client.d.ts
CHANGED
|
@@ -879,12 +879,10 @@ export declare class LoticsClient {
|
|
|
879
879
|
* `instructions`, `outputs` and the model pin.
|
|
880
880
|
*/
|
|
881
881
|
setAppAgent(app_id: string, alias: string,
|
|
882
|
-
/**
|
|
883
|
-
*
|
|
884
|
-
*
|
|
885
|
-
|
|
886
|
-
instructions: string;
|
|
887
|
-
}): Promise<ToolExecuteResult>;
|
|
882
|
+
/** ONLY the fields being changed. The server merges against the stored
|
|
883
|
+
* declaration, so nothing a caller omits is lost and a field the server adds
|
|
884
|
+
* later needs no change here. Pass `null` to clear an optional field. */
|
|
885
|
+
patch: Record<string, unknown>): Promise<ToolExecuteResult>;
|
|
888
886
|
/**
|
|
889
887
|
* Fetch one app workflow's faithful source + bound input/output schemas via
|
|
890
888
|
* `get_app_workflow`. `source` is the JS-subset body re-rendered from the
|
package/dist/src/client.js
CHANGED
|
@@ -572,11 +572,11 @@ export class LoticsClient {
|
|
|
572
572
|
* `instructions`, `outputs` and the model pin.
|
|
573
573
|
*/
|
|
574
574
|
async setAppAgent(app_id, alias,
|
|
575
|
-
/**
|
|
576
|
-
*
|
|
577
|
-
*
|
|
578
|
-
|
|
579
|
-
return this.execute("set_app_agent", { app_id, alias, ...
|
|
575
|
+
/** ONLY the fields being changed. The server merges against the stored
|
|
576
|
+
* declaration, so nothing a caller omits is lost and a field the server adds
|
|
577
|
+
* later needs no change here. Pass `null` to clear an optional field. */
|
|
578
|
+
patch) {
|
|
579
|
+
return this.execute("set_app_agent", { app_id, alias, ...patch });
|
|
580
580
|
}
|
|
581
581
|
/**
|
|
582
582
|
* Fetch one app workflow's faithful source + bound input/output schemas via
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lotics/cli",
|
|
3
|
-
"version": "0.100.
|
|
3
|
+
"version": "0.100.2",
|
|
4
4
|
"description": "Lotics SDK and CLI for AI agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"scripts": {
|
|
19
19
|
"build": "tsgo -p tsconfig.build.json && node scripts/build_cli.mjs",
|
|
20
20
|
"typecheck": "tsgo --noEmit",
|
|
21
|
+
"lint": "oxlint",
|
|
21
22
|
"test": "vitest run",
|
|
22
23
|
"prepublishOnly": "npm run build"
|
|
23
24
|
},
|