@lunnoa/client 1.1.0 → 1.3.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 +22 -0
- package/dist/cli.js +821 -91
- package/dist/index.cjs +71 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +132 -41
- package/dist/index.d.ts +132 -41
- package/dist/index.js +71 -21
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +70 -22
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +129 -34
- package/dist/react.d.ts +129 -34
- package/dist/react.js +70 -22
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
- package/skills/lunnoa-client/SKILL.md +21 -5
- package/skills/lunnoa-solution-design/SKILL.md +9 -7
package/dist/cli.js
CHANGED
|
@@ -430,10 +430,7 @@ var AgentChatClient = class {
|
|
|
430
430
|
raw: true,
|
|
431
431
|
signal: options.signal,
|
|
432
432
|
headers: { Accept: "text/event-stream" },
|
|
433
|
-
body:
|
|
434
|
-
messages: [uiMessage],
|
|
435
|
-
...options.chatMode ? { chatMode: options.chatMode } : {}
|
|
436
|
-
}
|
|
433
|
+
body: messageTaskBody(uiMessage, options)
|
|
437
434
|
}
|
|
438
435
|
);
|
|
439
436
|
return new AgentChatStream(response, { signal: options.signal });
|
|
@@ -472,6 +469,14 @@ var AgentChatClient = class {
|
|
|
472
469
|
);
|
|
473
470
|
}
|
|
474
471
|
};
|
|
472
|
+
function messageTaskBody(message, options = {}) {
|
|
473
|
+
return {
|
|
474
|
+
messages: [message],
|
|
475
|
+
...options.chatMode ? { chatMode: options.chatMode } : {},
|
|
476
|
+
...options.data ? { data: options.data } : {},
|
|
477
|
+
...options.piiResolution ? { piiResolution: options.piiResolution } : {}
|
|
478
|
+
};
|
|
479
|
+
}
|
|
475
480
|
function generateMessageId() {
|
|
476
481
|
const cryptoObj = globalThis.crypto;
|
|
477
482
|
if (cryptoObj?.randomUUID) {
|
|
@@ -1005,7 +1010,6 @@ var EntitiesResource = class {
|
|
|
1005
1010
|
objectTypeSlug: options.objectTypeSlug,
|
|
1006
1011
|
state: options.state,
|
|
1007
1012
|
states: options.states?.join(","),
|
|
1008
|
-
ownerId: options.ownerId,
|
|
1009
1013
|
search: options.search,
|
|
1010
1014
|
sortBy: options.sortBy,
|
|
1011
1015
|
sortOrder: options.sortOrder,
|
|
@@ -1601,10 +1605,11 @@ var QueueItemsResource = class {
|
|
|
1601
1605
|
listRetryable(queueId) {
|
|
1602
1606
|
return this.http.get(`/api/queues/${queueId}/items/retry`);
|
|
1603
1607
|
}
|
|
1604
|
-
/** Returns items with the given status. */
|
|
1605
|
-
listByStatus(queueId, status) {
|
|
1608
|
+
/** Returns a page of items with the given status. */
|
|
1609
|
+
listByStatus(queueId, status, options = {}) {
|
|
1606
1610
|
return this.http.get(
|
|
1607
|
-
`/api/queues/${queueId}/items/status/${status}
|
|
1611
|
+
`/api/queues/${queueId}/items/status/${status}`,
|
|
1612
|
+
{ query: { limit: options.limit, offset: options.offset } }
|
|
1608
1613
|
);
|
|
1609
1614
|
}
|
|
1610
1615
|
/** Looks an item up by the external ID your system supplied. */
|
|
@@ -1613,11 +1618,22 @@ var QueueItemsResource = class {
|
|
|
1613
1618
|
`/api/queues/${queueId}/items/external/${externalId}`
|
|
1614
1619
|
);
|
|
1615
1620
|
}
|
|
1616
|
-
/**
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
+
/**
|
|
1622
|
+
* Searches items by external id, item data, and metadata.
|
|
1623
|
+
* Returns one page, same shape as `list`.
|
|
1624
|
+
*/
|
|
1625
|
+
search(queueId, query, options = {}) {
|
|
1626
|
+
return this.http.get(
|
|
1627
|
+
`/api/queues/${queueId}/items/search`,
|
|
1628
|
+
{
|
|
1629
|
+
query: {
|
|
1630
|
+
limit: options.limit,
|
|
1631
|
+
offset: options.offset,
|
|
1632
|
+
expansion: serializeCsv(options.expansion),
|
|
1633
|
+
filterBy: serializeFilterBy({ search: query })
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
);
|
|
1621
1637
|
}
|
|
1622
1638
|
/** Returns per-status and per-priority counts for the queue. */
|
|
1623
1639
|
stats(queueId) {
|
|
@@ -1653,10 +1669,21 @@ var QueueItemsResource = class {
|
|
|
1653
1669
|
addError(queueId, itemId, error) {
|
|
1654
1670
|
return this.http.post(
|
|
1655
1671
|
`/api/queues/${queueId}/items/${itemId}/error`,
|
|
1656
|
-
{
|
|
1672
|
+
{
|
|
1673
|
+
body: {
|
|
1674
|
+
error: error.error,
|
|
1675
|
+
errorDetails: serializeErrorDetails(error.errorDetails)
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1657
1678
|
);
|
|
1658
1679
|
}
|
|
1659
1680
|
};
|
|
1681
|
+
function serializeErrorDetails(value) {
|
|
1682
|
+
if (value === void 0) {
|
|
1683
|
+
return void 0;
|
|
1684
|
+
}
|
|
1685
|
+
return typeof value === "string" ? value : JSON.stringify(value);
|
|
1686
|
+
}
|
|
1660
1687
|
|
|
1661
1688
|
// src/resources/queues.ts
|
|
1662
1689
|
var QueuesResource = class {
|
|
@@ -1783,6 +1810,32 @@ var RunsResource = class {
|
|
|
1783
1810
|
}
|
|
1784
1811
|
};
|
|
1785
1812
|
|
|
1813
|
+
// src/resources/scorecards.ts
|
|
1814
|
+
var ScorecardsResource = class {
|
|
1815
|
+
constructor(http) {
|
|
1816
|
+
this.http = http;
|
|
1817
|
+
}
|
|
1818
|
+
http;
|
|
1819
|
+
/** Lists scorecards in the workspace. */
|
|
1820
|
+
list() {
|
|
1821
|
+
return this.http.get("/api/scorecards");
|
|
1822
|
+
}
|
|
1823
|
+
/** Returns a scorecard with objectives, measures, latest values and status. */
|
|
1824
|
+
get(scorecardId) {
|
|
1825
|
+
return this.http.get(`/api/scorecards/${scorecardId}`);
|
|
1826
|
+
}
|
|
1827
|
+
/**
|
|
1828
|
+
* Records a measure value. A value for the same period replaces the current
|
|
1829
|
+
* one; the old value is kept as superseded history.
|
|
1830
|
+
*/
|
|
1831
|
+
recordValue(measureId, data) {
|
|
1832
|
+
return this.http.post(
|
|
1833
|
+
`/api/scorecards/measures/${measureId}/values`,
|
|
1834
|
+
{ body: data }
|
|
1835
|
+
);
|
|
1836
|
+
}
|
|
1837
|
+
};
|
|
1838
|
+
|
|
1786
1839
|
// src/resources/tasks.ts
|
|
1787
1840
|
var TasksResource = class {
|
|
1788
1841
|
constructor(http) {
|
|
@@ -1833,12 +1886,7 @@ var TasksResource = class {
|
|
|
1833
1886
|
} : message;
|
|
1834
1887
|
return this.http.post(
|
|
1835
1888
|
`/api/agents/${agentId}/tasks/${taskId}/message`,
|
|
1836
|
-
{
|
|
1837
|
-
body: {
|
|
1838
|
-
messages: [uiMessage],
|
|
1839
|
-
...options.chatMode ? { chatMode: options.chatMode } : {}
|
|
1840
|
-
}
|
|
1841
|
-
}
|
|
1889
|
+
{ body: messageTaskBody(uiMessage, options) }
|
|
1842
1890
|
);
|
|
1843
1891
|
}
|
|
1844
1892
|
};
|
|
@@ -2021,6 +2069,7 @@ var LunnoaClient = class {
|
|
|
2021
2069
|
queues;
|
|
2022
2070
|
queueItems;
|
|
2023
2071
|
variables;
|
|
2072
|
+
scorecards;
|
|
2024
2073
|
connections;
|
|
2025
2074
|
projects;
|
|
2026
2075
|
workflowApps;
|
|
@@ -2043,12 +2092,653 @@ var LunnoaClient = class {
|
|
|
2043
2092
|
this.queues = new QueuesResource(this.http);
|
|
2044
2093
|
this.queueItems = new QueueItemsResource(this.http);
|
|
2045
2094
|
this.variables = new VariablesResource(this.http);
|
|
2095
|
+
this.scorecards = new ScorecardsResource(this.http);
|
|
2046
2096
|
this.connections = new ConnectionsResource(this.http);
|
|
2047
2097
|
this.projects = new ProjectsResource(this.http);
|
|
2048
2098
|
this.discovery = new DiscoveryResource(this.http);
|
|
2049
2099
|
}
|
|
2050
2100
|
};
|
|
2051
2101
|
|
|
2102
|
+
// src/cli/args.ts
|
|
2103
|
+
var GROUPS_WITH_SUBCOMMAND = /* @__PURE__ */ new Set([
|
|
2104
|
+
"workflows",
|
|
2105
|
+
"agents",
|
|
2106
|
+
"entity-types",
|
|
2107
|
+
"tools",
|
|
2108
|
+
"apps",
|
|
2109
|
+
"connections",
|
|
2110
|
+
"projects"
|
|
2111
|
+
]);
|
|
2112
|
+
var VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
2113
|
+
"--url",
|
|
2114
|
+
"--api-key",
|
|
2115
|
+
"--access-token",
|
|
2116
|
+
"--project-id",
|
|
2117
|
+
"--out",
|
|
2118
|
+
"--app"
|
|
2119
|
+
]);
|
|
2120
|
+
function requireFlagValue(flag, value) {
|
|
2121
|
+
if (!value || value.startsWith("--")) {
|
|
2122
|
+
throw new Error(`Missing value for ${flag}`);
|
|
2123
|
+
}
|
|
2124
|
+
return value;
|
|
2125
|
+
}
|
|
2126
|
+
function parseArgs(argv) {
|
|
2127
|
+
const args = {
|
|
2128
|
+
command: argv[0],
|
|
2129
|
+
out: "./src/lunnoa",
|
|
2130
|
+
json: false,
|
|
2131
|
+
forAgent: false,
|
|
2132
|
+
help: false
|
|
2133
|
+
};
|
|
2134
|
+
let i = 1;
|
|
2135
|
+
if (args.command && GROUPS_WITH_SUBCOMMAND.has(args.command)) {
|
|
2136
|
+
const next = argv[1];
|
|
2137
|
+
if (next && !next.startsWith("--")) {
|
|
2138
|
+
args.subcommand = next;
|
|
2139
|
+
i = 2;
|
|
2140
|
+
if (argv[2] && !argv[2].startsWith("--")) {
|
|
2141
|
+
args.path = argv[2];
|
|
2142
|
+
i = 3;
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
}
|
|
2146
|
+
for (; i < argv.length; i += 1) {
|
|
2147
|
+
const flag = argv[i];
|
|
2148
|
+
if (flag === "--help" || flag === "-h") {
|
|
2149
|
+
args.help = true;
|
|
2150
|
+
continue;
|
|
2151
|
+
}
|
|
2152
|
+
if (flag === "--json") {
|
|
2153
|
+
args.json = true;
|
|
2154
|
+
continue;
|
|
2155
|
+
}
|
|
2156
|
+
if (flag === "--for-agent") {
|
|
2157
|
+
args.forAgent = true;
|
|
2158
|
+
continue;
|
|
2159
|
+
}
|
|
2160
|
+
if (flag && VALUE_FLAGS.has(flag)) {
|
|
2161
|
+
const value = requireFlagValue(flag, argv[i + 1]);
|
|
2162
|
+
switch (flag) {
|
|
2163
|
+
case "--url":
|
|
2164
|
+
args.url = value;
|
|
2165
|
+
break;
|
|
2166
|
+
case "--api-key":
|
|
2167
|
+
args.apiKey = value;
|
|
2168
|
+
break;
|
|
2169
|
+
case "--access-token":
|
|
2170
|
+
args.accessToken = value;
|
|
2171
|
+
break;
|
|
2172
|
+
case "--project-id":
|
|
2173
|
+
args.projectId = value;
|
|
2174
|
+
break;
|
|
2175
|
+
case "--out":
|
|
2176
|
+
args.out = value;
|
|
2177
|
+
break;
|
|
2178
|
+
case "--app":
|
|
2179
|
+
args.app = value;
|
|
2180
|
+
break;
|
|
2181
|
+
default:
|
|
2182
|
+
break;
|
|
2183
|
+
}
|
|
2184
|
+
i += 1;
|
|
2185
|
+
continue;
|
|
2186
|
+
}
|
|
2187
|
+
if (flag && !flag.startsWith("--") && !args.path) {
|
|
2188
|
+
args.path = flag;
|
|
2189
|
+
continue;
|
|
2190
|
+
}
|
|
2191
|
+
throw new Error(`Unknown option: ${flag}`);
|
|
2192
|
+
}
|
|
2193
|
+
return args;
|
|
2194
|
+
}
|
|
2195
|
+
var USAGE = `Usage:
|
|
2196
|
+
npx @lunnoa/client codegen --url <url> --api-key <lna_key> [--out <dir>]
|
|
2197
|
+
|
|
2198
|
+
Discover (do not invent catalogue action ids):
|
|
2199
|
+
npx @lunnoa/client tools list [--app <appId>] [--for-agent] [--json]
|
|
2200
|
+
npx @lunnoa/client tools get <actionId> [--app <appId>] [--json]
|
|
2201
|
+
npx @lunnoa/client tools search <query> [--app <appId>] [--for-agent] [--json]
|
|
2202
|
+
npx @lunnoa/client apps list [--for-agent] [--json]
|
|
2203
|
+
npx @lunnoa/client apps get <appId> [--json]
|
|
2204
|
+
npx @lunnoa/client connections list [--app <appId>] [--json]
|
|
2205
|
+
npx @lunnoa/client connections get <connectionId> [--json]
|
|
2206
|
+
npx @lunnoa/client agents list [--json]
|
|
2207
|
+
npx @lunnoa/client workflows list [--json]
|
|
2208
|
+
npx @lunnoa/client entity-types list [--json]
|
|
2209
|
+
npx @lunnoa/client projects list [--json]
|
|
2210
|
+
npx @lunnoa/client features [--json]
|
|
2211
|
+
|
|
2212
|
+
Deploy (upsert by slug):
|
|
2213
|
+
npx @lunnoa/client workflows deploy <path> --url <url> --api-key <key> --project-id <uuid>
|
|
2214
|
+
npx @lunnoa/client agents deploy <path> --url <url> --api-key <key> --project-id <uuid>
|
|
2215
|
+
npx @lunnoa/client entity-types deploy <path> --url <url> --api-key <key>
|
|
2216
|
+
|
|
2217
|
+
Options:
|
|
2218
|
+
--url Base URL of the Lunnoa Automate deployment
|
|
2219
|
+
--api-key Machine API key (lna_...)
|
|
2220
|
+
--access-token User JWT as an alternative to --api-key
|
|
2221
|
+
--project-id Project UUID (required for workflow/agent deploy)
|
|
2222
|
+
--out Output directory for lunnoa.generated.ts (codegen; default: ./src/lunnoa)
|
|
2223
|
+
--app Restrict tools/connections to one workflow app id
|
|
2224
|
+
--for-agent Only catalogue entries with availableForAgent
|
|
2225
|
+
--json Machine-readable JSON (preferred for coding agents)
|
|
2226
|
+
--help Show this help
|
|
2227
|
+
|
|
2228
|
+
Environment: LUNNOA_URL, LUNNOA_API_KEY, LUNNOA_PROJECT_ID
|
|
2229
|
+
Optional config file: lunnoa.config.ts | .mjs | .js | .json
|
|
2230
|
+
|
|
2231
|
+
tools get prints inputConfig and a defineAction() sketch. tools list is compact;
|
|
2232
|
+
run tools get for the schema. Entity-type deploy is workspace-scoped (no
|
|
2233
|
+
--project-id). Unknown instance attributes are rejected unless
|
|
2234
|
+
allowUnknownAttributes is true; use extensions.
|
|
2235
|
+
`;
|
|
2236
|
+
|
|
2237
|
+
// src/cli/catalog.ts
|
|
2238
|
+
function asRecord(value) {
|
|
2239
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
2240
|
+
return value;
|
|
2241
|
+
}
|
|
2242
|
+
return void 0;
|
|
2243
|
+
}
|
|
2244
|
+
function flattenActions(apps) {
|
|
2245
|
+
const out = [];
|
|
2246
|
+
for (const app of apps) {
|
|
2247
|
+
const appId = typeof app.id === "string" ? app.id : "";
|
|
2248
|
+
if (!appId) {
|
|
2249
|
+
continue;
|
|
2250
|
+
}
|
|
2251
|
+
const actions = Array.isArray(app.actions) ? app.actions : [];
|
|
2252
|
+
for (const raw of actions) {
|
|
2253
|
+
const action = asRecord(raw);
|
|
2254
|
+
if (!action || typeof action.id !== "string" || !action.id) {
|
|
2255
|
+
continue;
|
|
2256
|
+
}
|
|
2257
|
+
const actionId = action.id;
|
|
2258
|
+
out.push({
|
|
2259
|
+
appId,
|
|
2260
|
+
appName: typeof app.name === "string" ? app.name : appId,
|
|
2261
|
+
actionId,
|
|
2262
|
+
name: typeof action.name === "string" ? action.name : actionId,
|
|
2263
|
+
description: typeof action.description === "string" ? action.description : "",
|
|
2264
|
+
availableForAgent: typeof action.availableForAgent === "boolean" ? action.availableForAgent : Boolean(app.availableForAgent),
|
|
2265
|
+
needsConnection: typeof action.needsConnection === "boolean" ? action.needsConnection : Boolean(app.needsConnection),
|
|
2266
|
+
inputConfig: action.inputConfig
|
|
2267
|
+
});
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
return out;
|
|
2271
|
+
}
|
|
2272
|
+
function filterActions(actions, filter = {}) {
|
|
2273
|
+
const appId = filter.appId?.trim();
|
|
2274
|
+
const query = filter.query?.trim().toLowerCase();
|
|
2275
|
+
return actions.filter((action) => {
|
|
2276
|
+
if (appId && action.appId !== appId) {
|
|
2277
|
+
return false;
|
|
2278
|
+
}
|
|
2279
|
+
if (filter.forAgent && !action.availableForAgent) {
|
|
2280
|
+
return false;
|
|
2281
|
+
}
|
|
2282
|
+
if (query) {
|
|
2283
|
+
const haystack = [
|
|
2284
|
+
action.appId,
|
|
2285
|
+
action.appName,
|
|
2286
|
+
action.actionId,
|
|
2287
|
+
action.name,
|
|
2288
|
+
action.description
|
|
2289
|
+
].join(" ").toLowerCase();
|
|
2290
|
+
if (!haystack.includes(query)) {
|
|
2291
|
+
return false;
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2294
|
+
return true;
|
|
2295
|
+
});
|
|
2296
|
+
}
|
|
2297
|
+
function parseActionRef(raw) {
|
|
2298
|
+
const trimmed = raw.trim();
|
|
2299
|
+
const dot = trimmed.indexOf(".");
|
|
2300
|
+
if (dot > 0) {
|
|
2301
|
+
const maybeApp = trimmed.slice(0, dot);
|
|
2302
|
+
const maybeAction = trimmed.slice(dot + 1);
|
|
2303
|
+
if (maybeApp && maybeAction && !maybeApp.includes("_action_")) {
|
|
2304
|
+
return { appId: maybeApp, actionId: maybeAction };
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
return { actionId: trimmed };
|
|
2308
|
+
}
|
|
2309
|
+
function resolveAction(actions, rawRef, appId) {
|
|
2310
|
+
const parsed = parseActionRef(rawRef);
|
|
2311
|
+
const scope = appId?.trim() || parsed.appId;
|
|
2312
|
+
const matches = actions.filter((action) => {
|
|
2313
|
+
if (action.actionId !== parsed.actionId) {
|
|
2314
|
+
return false;
|
|
2315
|
+
}
|
|
2316
|
+
return scope ? action.appId === scope : true;
|
|
2317
|
+
});
|
|
2318
|
+
if (matches.length === 1) {
|
|
2319
|
+
return matches[0];
|
|
2320
|
+
}
|
|
2321
|
+
if (matches.length === 0) {
|
|
2322
|
+
const hint = scope ? `No action "${parsed.actionId}" on app "${scope}". Try: tools search ${parsed.actionId}` : `Unknown action "${parsed.actionId}". Try: tools search ${parsed.actionId}`;
|
|
2323
|
+
throw new Error(hint);
|
|
2324
|
+
}
|
|
2325
|
+
const apps = matches.map((m) => m.appId).join(", ");
|
|
2326
|
+
throw new Error(
|
|
2327
|
+
`Action "${parsed.actionId}" exists on multiple apps (${apps}). Pass --app <appId>.`
|
|
2328
|
+
);
|
|
2329
|
+
}
|
|
2330
|
+
function extractInputFields(inputConfig) {
|
|
2331
|
+
if (!inputConfig) {
|
|
2332
|
+
return [];
|
|
2333
|
+
}
|
|
2334
|
+
if (Array.isArray(inputConfig)) {
|
|
2335
|
+
return inputConfig.map((item) => fieldFromUnknown(item)).filter((field) => Boolean(field));
|
|
2336
|
+
}
|
|
2337
|
+
const record = asRecord(inputConfig);
|
|
2338
|
+
if (!record) {
|
|
2339
|
+
return [];
|
|
2340
|
+
}
|
|
2341
|
+
if (Array.isArray(record.fields)) {
|
|
2342
|
+
return extractInputFields(record.fields);
|
|
2343
|
+
}
|
|
2344
|
+
if (Array.isArray(record.sections)) {
|
|
2345
|
+
return record.sections.flatMap((section) => {
|
|
2346
|
+
const sectionRecord = asRecord(section);
|
|
2347
|
+
return extractInputFields(sectionRecord?.fields ?? section);
|
|
2348
|
+
});
|
|
2349
|
+
}
|
|
2350
|
+
return Object.entries(record).filter(([, value]) => value === null || typeof value !== "object" || !Array.isArray(value)).map(([id, value]) => {
|
|
2351
|
+
const field = asRecord(value);
|
|
2352
|
+
if (!field) {
|
|
2353
|
+
return { id };
|
|
2354
|
+
}
|
|
2355
|
+
return {
|
|
2356
|
+
id: typeof field.id === "string" ? field.id : id,
|
|
2357
|
+
label: typeof field.label === "string" ? field.label : void 0,
|
|
2358
|
+
type: typeof field.type === "string" ? field.type : typeof field.inputType === "string" ? field.inputType : void 0,
|
|
2359
|
+
required: typeof field.required === "boolean" ? field.required : void 0
|
|
2360
|
+
};
|
|
2361
|
+
});
|
|
2362
|
+
}
|
|
2363
|
+
function fieldFromUnknown(value) {
|
|
2364
|
+
const field = asRecord(value);
|
|
2365
|
+
if (!field || typeof field.id !== "string" || !field.id) {
|
|
2366
|
+
return void 0;
|
|
2367
|
+
}
|
|
2368
|
+
return {
|
|
2369
|
+
id: field.id,
|
|
2370
|
+
label: typeof field.label === "string" ? field.label : void 0,
|
|
2371
|
+
type: typeof field.type === "string" ? field.type : typeof field.inputType === "string" ? field.inputType : void 0,
|
|
2372
|
+
required: typeof field.required === "boolean" ? field.required : void 0
|
|
2373
|
+
};
|
|
2374
|
+
}
|
|
2375
|
+
function summarizeAction(action) {
|
|
2376
|
+
return {
|
|
2377
|
+
appId: action.appId,
|
|
2378
|
+
actionId: action.actionId,
|
|
2379
|
+
name: action.name,
|
|
2380
|
+
description: action.description,
|
|
2381
|
+
availableForAgent: action.availableForAgent,
|
|
2382
|
+
needsConnection: action.needsConnection
|
|
2383
|
+
};
|
|
2384
|
+
}
|
|
2385
|
+
function formatDefineActionSnippet(action) {
|
|
2386
|
+
const fields = extractInputFields(action.inputConfig);
|
|
2387
|
+
const inputBlock = fields.length === 0 ? " // no inputConfig fields advertised" : fields.map((field) => {
|
|
2388
|
+
const notes = [
|
|
2389
|
+
field.label && field.label !== field.id ? field.label : void 0,
|
|
2390
|
+
field.type,
|
|
2391
|
+
field.required ? "required" : void 0
|
|
2392
|
+
].filter(Boolean).join(", ");
|
|
2393
|
+
return notes ? ` // ${field.id}${notes ? ` \u2014 ${notes}` : ""}` : ` // ${field.id}`;
|
|
2394
|
+
}).join("\n");
|
|
2395
|
+
return [
|
|
2396
|
+
"defineAction({",
|
|
2397
|
+
` id: '${action.actionId}',`,
|
|
2398
|
+
` appId: '${action.appId}',`,
|
|
2399
|
+
" input: {",
|
|
2400
|
+
inputBlock,
|
|
2401
|
+
" },",
|
|
2402
|
+
"})"
|
|
2403
|
+
].join("\n");
|
|
2404
|
+
}
|
|
2405
|
+
function actionDetail(action) {
|
|
2406
|
+
return {
|
|
2407
|
+
...summarizeAction(action),
|
|
2408
|
+
inputFields: extractInputFields(action.inputConfig),
|
|
2409
|
+
inputConfig: action.inputConfig ?? null,
|
|
2410
|
+
defineAction: formatDefineActionSnippet(action)
|
|
2411
|
+
};
|
|
2412
|
+
}
|
|
2413
|
+
|
|
2414
|
+
// src/cli/print.ts
|
|
2415
|
+
function printJson(value) {
|
|
2416
|
+
console.log(JSON.stringify(value, null, 2));
|
|
2417
|
+
}
|
|
2418
|
+
function formatTable(rows, columns) {
|
|
2419
|
+
if (rows.length === 0) {
|
|
2420
|
+
return "No results.";
|
|
2421
|
+
}
|
|
2422
|
+
const widths = columns.map((col) => {
|
|
2423
|
+
let width = col.header.length;
|
|
2424
|
+
for (const row of rows) {
|
|
2425
|
+
width = Math.max(width, stringifyCell(row[col.key]).length);
|
|
2426
|
+
}
|
|
2427
|
+
return width;
|
|
2428
|
+
});
|
|
2429
|
+
const header = columns.map((col, i) => pad(col.header, widths[i])).join(" ");
|
|
2430
|
+
const divider = widths.map((w) => "-".repeat(w)).join(" ");
|
|
2431
|
+
const body = rows.map(
|
|
2432
|
+
(row) => columns.map((col, i) => pad(stringifyCell(row[col.key]), widths[i])).join(" ")
|
|
2433
|
+
);
|
|
2434
|
+
return [header, divider, ...body].join("\n");
|
|
2435
|
+
}
|
|
2436
|
+
function printTable(rows, columns) {
|
|
2437
|
+
console.log(formatTable(rows, columns));
|
|
2438
|
+
}
|
|
2439
|
+
function stringifyCell(value) {
|
|
2440
|
+
if (value === null || value === void 0) {
|
|
2441
|
+
return "";
|
|
2442
|
+
}
|
|
2443
|
+
if (typeof value === "boolean") {
|
|
2444
|
+
return value ? "yes" : "no";
|
|
2445
|
+
}
|
|
2446
|
+
return String(value);
|
|
2447
|
+
}
|
|
2448
|
+
function pad(value, width) {
|
|
2449
|
+
return value.length >= width ? value : `${value}${" ".repeat(width - value.length)}`;
|
|
2450
|
+
}
|
|
2451
|
+
|
|
2452
|
+
// src/cli/discover.ts
|
|
2453
|
+
function requireTarget(args, label) {
|
|
2454
|
+
const value = args.path?.trim();
|
|
2455
|
+
if (!value) {
|
|
2456
|
+
throw new Error(`Missing ${label}.`);
|
|
2457
|
+
}
|
|
2458
|
+
return value;
|
|
2459
|
+
}
|
|
2460
|
+
async function loadCatalog(client) {
|
|
2461
|
+
return flattenActions(await client.workflowApps.list());
|
|
2462
|
+
}
|
|
2463
|
+
async function runToolsList(client, args) {
|
|
2464
|
+
const actions = filterActions(await loadCatalog(client), {
|
|
2465
|
+
appId: args.app,
|
|
2466
|
+
forAgent: args.forAgent
|
|
2467
|
+
});
|
|
2468
|
+
const rows = actions.map(summarizeAction);
|
|
2469
|
+
if (args.json) {
|
|
2470
|
+
printJson(rows);
|
|
2471
|
+
return;
|
|
2472
|
+
}
|
|
2473
|
+
printTable(rows, [
|
|
2474
|
+
{ key: "appId", header: "APP" },
|
|
2475
|
+
{ key: "actionId", header: "ACTION" },
|
|
2476
|
+
{ key: "name", header: "NAME" },
|
|
2477
|
+
{ key: "availableForAgent", header: "FOR_AGENT" },
|
|
2478
|
+
{ key: "needsConnection", header: "NEEDS_CONN" }
|
|
2479
|
+
]);
|
|
2480
|
+
}
|
|
2481
|
+
async function runToolsGet(client, args) {
|
|
2482
|
+
const ref = requireTarget(args, "action id (example: tools get http_action_send-request)");
|
|
2483
|
+
const action = resolveAction(await loadCatalog(client), ref, args.app);
|
|
2484
|
+
const detail = actionDetail(action);
|
|
2485
|
+
if (args.json) {
|
|
2486
|
+
printJson(detail);
|
|
2487
|
+
return;
|
|
2488
|
+
}
|
|
2489
|
+
console.log(`appId: ${detail.appId}`);
|
|
2490
|
+
console.log(`actionId: ${detail.actionId}`);
|
|
2491
|
+
console.log(`name: ${detail.name}`);
|
|
2492
|
+
if (detail.description) {
|
|
2493
|
+
console.log(`description: ${detail.description}`);
|
|
2494
|
+
}
|
|
2495
|
+
console.log(`availableForAgent: ${detail.availableForAgent ? "yes" : "no"}`);
|
|
2496
|
+
console.log(`needsConnection: ${detail.needsConnection ? "yes" : "no"}`);
|
|
2497
|
+
console.log("");
|
|
2498
|
+
console.log("inputFields:");
|
|
2499
|
+
if (detail.inputFields.length === 0) {
|
|
2500
|
+
console.log(" (none advertised)");
|
|
2501
|
+
} else {
|
|
2502
|
+
for (const field of detail.inputFields) {
|
|
2503
|
+
const bits = [
|
|
2504
|
+
field.label && field.label !== field.id ? field.label : void 0,
|
|
2505
|
+
field.type,
|
|
2506
|
+
field.required ? "required" : void 0
|
|
2507
|
+
].filter(Boolean);
|
|
2508
|
+
console.log(` ${field.id}${bits.length ? ` (${bits.join(", ")})` : ""}`);
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
console.log("");
|
|
2512
|
+
console.log("defineAction:");
|
|
2513
|
+
console.log(detail.defineAction);
|
|
2514
|
+
}
|
|
2515
|
+
async function runToolsSearch(client, args) {
|
|
2516
|
+
const query = requireTarget(args, "search query (example: tools search email)");
|
|
2517
|
+
const actions = filterActions(await loadCatalog(client), {
|
|
2518
|
+
appId: args.app,
|
|
2519
|
+
forAgent: args.forAgent,
|
|
2520
|
+
query
|
|
2521
|
+
});
|
|
2522
|
+
const rows = actions.map(summarizeAction);
|
|
2523
|
+
if (args.json) {
|
|
2524
|
+
printJson(rows);
|
|
2525
|
+
return;
|
|
2526
|
+
}
|
|
2527
|
+
printTable(rows, [
|
|
2528
|
+
{ key: "appId", header: "APP" },
|
|
2529
|
+
{ key: "actionId", header: "ACTION" },
|
|
2530
|
+
{ key: "name", header: "NAME" },
|
|
2531
|
+
{ key: "availableForAgent", header: "FOR_AGENT" },
|
|
2532
|
+
{ key: "needsConnection", header: "NEEDS_CONN" }
|
|
2533
|
+
]);
|
|
2534
|
+
}
|
|
2535
|
+
async function runAppsList(client, args) {
|
|
2536
|
+
const apps = await client.workflowApps.list();
|
|
2537
|
+
const rows = apps.filter((app) => args.forAgent ? Boolean(app.availableForAgent) : true).filter((app) => args.app ? app.id === args.app : true).map((app) => ({
|
|
2538
|
+
id: app.id ?? "",
|
|
2539
|
+
name: app.name ?? app.id ?? "",
|
|
2540
|
+
availableForAgent: Boolean(app.availableForAgent),
|
|
2541
|
+
needsConnection: Boolean(app.needsConnection),
|
|
2542
|
+
actions: Array.isArray(app.actions) ? app.actions.length : 0
|
|
2543
|
+
}));
|
|
2544
|
+
if (args.json) {
|
|
2545
|
+
printJson(rows);
|
|
2546
|
+
return;
|
|
2547
|
+
}
|
|
2548
|
+
printTable(rows, [
|
|
2549
|
+
{ key: "id", header: "APP" },
|
|
2550
|
+
{ key: "name", header: "NAME" },
|
|
2551
|
+
{ key: "availableForAgent", header: "FOR_AGENT" },
|
|
2552
|
+
{ key: "needsConnection", header: "NEEDS_CONN" },
|
|
2553
|
+
{ key: "actions", header: "ACTIONS" }
|
|
2554
|
+
]);
|
|
2555
|
+
}
|
|
2556
|
+
async function runAppsGet(client, args) {
|
|
2557
|
+
const appId = requireTarget(args, "app id (example: apps get http)");
|
|
2558
|
+
const apps = await client.workflowApps.list();
|
|
2559
|
+
const app = apps.find((item) => item.id === appId);
|
|
2560
|
+
if (!app) {
|
|
2561
|
+
throw new Error(`Unknown app "${appId}". Try: apps list`);
|
|
2562
|
+
}
|
|
2563
|
+
const actions = flattenActions([app]).map(summarizeAction);
|
|
2564
|
+
const payload = {
|
|
2565
|
+
id: app.id,
|
|
2566
|
+
name: app.name,
|
|
2567
|
+
description: app.description ?? "",
|
|
2568
|
+
availableForAgent: Boolean(app.availableForAgent),
|
|
2569
|
+
needsConnection: Boolean(app.needsConnection),
|
|
2570
|
+
isPublished: Boolean(app.isPublished),
|
|
2571
|
+
actions,
|
|
2572
|
+
connectionTypes: Array.isArray(app.connections) ? app.connections : []
|
|
2573
|
+
};
|
|
2574
|
+
if (args.json) {
|
|
2575
|
+
printJson(payload);
|
|
2576
|
+
return;
|
|
2577
|
+
}
|
|
2578
|
+
console.log(`id: ${payload.id}`);
|
|
2579
|
+
console.log(`name: ${payload.name}`);
|
|
2580
|
+
if (payload.description) {
|
|
2581
|
+
console.log(`description: ${payload.description}`);
|
|
2582
|
+
}
|
|
2583
|
+
console.log(`availableForAgent: ${payload.availableForAgent ? "yes" : "no"}`);
|
|
2584
|
+
console.log(`needsConnection: ${payload.needsConnection ? "yes" : "no"}`);
|
|
2585
|
+
console.log("");
|
|
2586
|
+
printTable(actions, [
|
|
2587
|
+
{ key: "actionId", header: "ACTION" },
|
|
2588
|
+
{ key: "name", header: "NAME" },
|
|
2589
|
+
{ key: "availableForAgent", header: "FOR_AGENT" },
|
|
2590
|
+
{ key: "needsConnection", header: "NEEDS_CONN" }
|
|
2591
|
+
]);
|
|
2592
|
+
}
|
|
2593
|
+
async function runConnectionsList(client, args) {
|
|
2594
|
+
const connections = await client.connections.list({
|
|
2595
|
+
expansion: ["workflowAppId", "connectionId", "createdAt"]
|
|
2596
|
+
});
|
|
2597
|
+
const rows = connections.filter(
|
|
2598
|
+
(connection) => args.app ? connection.workflowAppId === args.app : true
|
|
2599
|
+
).map((connection) => ({
|
|
2600
|
+
id: connection.id,
|
|
2601
|
+
name: connection.name,
|
|
2602
|
+
workflowAppId: connection.workflowAppId ?? "",
|
|
2603
|
+
connectionId: connection.connectionId ?? ""
|
|
2604
|
+
}));
|
|
2605
|
+
if (args.json) {
|
|
2606
|
+
printJson(rows);
|
|
2607
|
+
return;
|
|
2608
|
+
}
|
|
2609
|
+
printTable(rows, [
|
|
2610
|
+
{ key: "id", header: "ID" },
|
|
2611
|
+
{ key: "name", header: "NAME" },
|
|
2612
|
+
{ key: "workflowAppId", header: "APP" },
|
|
2613
|
+
{ key: "connectionId", header: "TYPE" }
|
|
2614
|
+
]);
|
|
2615
|
+
}
|
|
2616
|
+
async function runConnectionsGet(client, args) {
|
|
2617
|
+
const id = requireTarget(args, "connection id (example: connections get <uuid>)");
|
|
2618
|
+
const connection = await client.connections.get(id, {
|
|
2619
|
+
expansion: ["workflowAppId", "connectionId", "createdAt", "updatedAt"]
|
|
2620
|
+
});
|
|
2621
|
+
const payload = {
|
|
2622
|
+
id: connection.id,
|
|
2623
|
+
name: connection.name,
|
|
2624
|
+
workflowAppId: connection.workflowAppId ?? null,
|
|
2625
|
+
connectionId: connection.connectionId ?? null,
|
|
2626
|
+
createdAt: connection.createdAt ?? null,
|
|
2627
|
+
updatedAt: connection.updatedAt ?? null
|
|
2628
|
+
};
|
|
2629
|
+
if (args.json) {
|
|
2630
|
+
printJson(payload);
|
|
2631
|
+
return;
|
|
2632
|
+
}
|
|
2633
|
+
console.log(`id: ${payload.id}`);
|
|
2634
|
+
console.log(`name: ${payload.name}`);
|
|
2635
|
+
console.log(`workflowAppId: ${payload.workflowAppId ?? ""}`);
|
|
2636
|
+
console.log(`connectionId: ${payload.connectionId ?? ""}`);
|
|
2637
|
+
}
|
|
2638
|
+
async function runAgentsList(client, args) {
|
|
2639
|
+
const agents = await client.agents.list({
|
|
2640
|
+
expansion: ["description", "slug", "managedByCode"]
|
|
2641
|
+
});
|
|
2642
|
+
const rows = agents.map((agent) => ({
|
|
2643
|
+
id: agent.id,
|
|
2644
|
+
name: agent.name,
|
|
2645
|
+
slug: agent.slug ?? "",
|
|
2646
|
+
managedByCode: Boolean(agent.managedByCode),
|
|
2647
|
+
description: agent.description ?? ""
|
|
2648
|
+
}));
|
|
2649
|
+
if (args.json) {
|
|
2650
|
+
printJson(rows);
|
|
2651
|
+
return;
|
|
2652
|
+
}
|
|
2653
|
+
printTable(rows, [
|
|
2654
|
+
{ key: "id", header: "ID" },
|
|
2655
|
+
{ key: "slug", header: "SLUG" },
|
|
2656
|
+
{ key: "name", header: "NAME" },
|
|
2657
|
+
{ key: "managedByCode", header: "CODE" }
|
|
2658
|
+
]);
|
|
2659
|
+
}
|
|
2660
|
+
async function runWorkflowsList(client, args) {
|
|
2661
|
+
const workflows = await client.workflows.list({
|
|
2662
|
+
expansion: ["description", "isActive", "slug", "managedByCode"]
|
|
2663
|
+
});
|
|
2664
|
+
const rows = workflows.map((workflow) => ({
|
|
2665
|
+
id: workflow.id,
|
|
2666
|
+
name: workflow.name,
|
|
2667
|
+
slug: workflow.slug ?? "",
|
|
2668
|
+
isActive: Boolean(workflow.isActive),
|
|
2669
|
+
managedByCode: Boolean(workflow.managedByCode)
|
|
2670
|
+
}));
|
|
2671
|
+
if (args.json) {
|
|
2672
|
+
printJson(rows);
|
|
2673
|
+
return;
|
|
2674
|
+
}
|
|
2675
|
+
printTable(rows, [
|
|
2676
|
+
{ key: "id", header: "ID" },
|
|
2677
|
+
{ key: "slug", header: "SLUG" },
|
|
2678
|
+
{ key: "name", header: "NAME" },
|
|
2679
|
+
{ key: "isActive", header: "ACTIVE" },
|
|
2680
|
+
{ key: "managedByCode", header: "CODE" }
|
|
2681
|
+
]);
|
|
2682
|
+
}
|
|
2683
|
+
async function runEntityTypesList(client, args) {
|
|
2684
|
+
const types = await client.entityTypes.list({
|
|
2685
|
+
expansion: ["description"]
|
|
2686
|
+
});
|
|
2687
|
+
const rows = types.map((type) => ({
|
|
2688
|
+
id: type.id,
|
|
2689
|
+
slug: type.slug,
|
|
2690
|
+
name: type.name,
|
|
2691
|
+
managedByCode: Boolean(type.managedByCode)
|
|
2692
|
+
}));
|
|
2693
|
+
if (args.json) {
|
|
2694
|
+
printJson(rows);
|
|
2695
|
+
return;
|
|
2696
|
+
}
|
|
2697
|
+
printTable(rows, [
|
|
2698
|
+
{ key: "id", header: "ID" },
|
|
2699
|
+
{ key: "slug", header: "SLUG" },
|
|
2700
|
+
{ key: "name", header: "NAME" },
|
|
2701
|
+
{ key: "managedByCode", header: "CODE" }
|
|
2702
|
+
]);
|
|
2703
|
+
}
|
|
2704
|
+
async function runProjectsList(client, args) {
|
|
2705
|
+
const projects = await client.projects.list({
|
|
2706
|
+
expansion: ["description", "countAgents", "countWorkflows"]
|
|
2707
|
+
});
|
|
2708
|
+
const rows = projects.map((project) => ({
|
|
2709
|
+
id: project.id,
|
|
2710
|
+
name: project.name,
|
|
2711
|
+
description: project.description ?? "",
|
|
2712
|
+
agents: project._count?.agents ?? "",
|
|
2713
|
+
workflows: project._count?.workflows ?? ""
|
|
2714
|
+
}));
|
|
2715
|
+
if (args.json) {
|
|
2716
|
+
printJson(rows);
|
|
2717
|
+
return;
|
|
2718
|
+
}
|
|
2719
|
+
printTable(rows, [
|
|
2720
|
+
{ key: "id", header: "ID" },
|
|
2721
|
+
{ key: "name", header: "NAME" },
|
|
2722
|
+
{ key: "agents", header: "AGENTS" },
|
|
2723
|
+
{ key: "workflows", header: "WORKFLOWS" }
|
|
2724
|
+
]);
|
|
2725
|
+
}
|
|
2726
|
+
async function runFeatures(client, args) {
|
|
2727
|
+
const features = await client.discovery.enabledFeatures();
|
|
2728
|
+
if (args.json) {
|
|
2729
|
+
printJson(features);
|
|
2730
|
+
return;
|
|
2731
|
+
}
|
|
2732
|
+
const rows = Object.entries(features).filter(([, value]) => typeof value === "boolean").map(([key, value]) => ({
|
|
2733
|
+
feature: key,
|
|
2734
|
+
enabled: value
|
|
2735
|
+
}));
|
|
2736
|
+
printTable(rows, [
|
|
2737
|
+
{ key: "feature", header: "FEATURE" },
|
|
2738
|
+
{ key: "enabled", header: "ENABLED" }
|
|
2739
|
+
]);
|
|
2740
|
+
}
|
|
2741
|
+
|
|
2052
2742
|
// src/cli/generate.ts
|
|
2053
2743
|
function computeDeploymentSchemaFingerprint(entityTypes) {
|
|
2054
2744
|
const parts = [...entityTypes].map((type) => {
|
|
@@ -2191,7 +2881,7 @@ function generateDeploymentModule(definitions) {
|
|
|
2191
2881
|
*
|
|
2192
2882
|
* Schema fingerprint: ${schemaFingerprint}
|
|
2193
2883
|
*/
|
|
2194
|
-
import type { LunnoaClient, Entity, Execution, ExecuteWorkflowResult, PaginatedEntities, Task, AgentChatStream, WaitUntilFinishedOptions, ListEntitiesOptions, UIMessage } from '@lunnoa/client';
|
|
2884
|
+
import type { LunnoaClient, Entity, Execution, ExecuteWorkflowResult, PaginatedEntities, Task, AgentChatStream, WaitUntilFinishedOptions, ListEntitiesOptions, StreamMessageOptions, UIMessage } from '@lunnoa/client';
|
|
2195
2885
|
|
|
2196
2886
|
/** Fingerprint of entity-type schemas at codegen time (slug:revision:allowUnknown). */
|
|
2197
2887
|
export const DEPLOYMENT_SCHEMA_FINGERPRINT = ${JSON.stringify(schemaFingerprint)};
|
|
@@ -2379,7 +3069,7 @@ function typedEntityAccessor<A, S extends string>(
|
|
|
2379
3069
|
})) as Typed;
|
|
2380
3070
|
},
|
|
2381
3071
|
/** Creates an entity of this type with compile-time checked attributes. */
|
|
2382
|
-
async create(data: { name: string; attributes?: A; state?: S
|
|
3072
|
+
async create(data: { name: string; attributes?: A; state?: S }): Promise<Typed> {
|
|
2383
3073
|
return (await client.entities.create(
|
|
2384
3074
|
{ ...data, objectTypeId } as never,
|
|
2385
3075
|
{ expansion: ['attributes'] },
|
|
@@ -2388,7 +3078,7 @@ function typedEntityAccessor<A, S extends string>(
|
|
|
2388
3078
|
/** Partially updates an entity of this type. */
|
|
2389
3079
|
async update(
|
|
2390
3080
|
entityId: string,
|
|
2391
|
-
data: { name?: string; attributes?: Partial<A
|
|
3081
|
+
data: { name?: string; attributes?: Partial<A> },
|
|
2392
3082
|
): Promise<Typed> {
|
|
2393
3083
|
return (await client.entities.update(entityId, data as never, {
|
|
2394
3084
|
expansion: ['attributes'],
|
|
@@ -2434,13 +3124,17 @@ function typedAgentAccessor(client: LunnoaClient, agentId: string) {
|
|
|
2434
3124
|
streamMessage(
|
|
2435
3125
|
taskId: string,
|
|
2436
3126
|
message: string | UIMessage,
|
|
2437
|
-
options?:
|
|
3127
|
+
options?: StreamMessageOptions,
|
|
2438
3128
|
): Promise<AgentChatStream> {
|
|
2439
3129
|
return client.agentChat.streamMessage(agentId, taskId, message, options);
|
|
2440
3130
|
},
|
|
2441
3131
|
/** Sends a message and waits for the full turn (non-streaming). */
|
|
2442
|
-
message(
|
|
2443
|
-
|
|
3132
|
+
message(
|
|
3133
|
+
taskId: string,
|
|
3134
|
+
message: string | UIMessage,
|
|
3135
|
+
options?: Omit<StreamMessageOptions, 'signal'>,
|
|
3136
|
+
) {
|
|
3137
|
+
return client.tasks.message(agentId, taskId, message, options);
|
|
2444
3138
|
},
|
|
2445
3139
|
};
|
|
2446
3140
|
}
|
|
@@ -2537,70 +3231,6 @@ async function loadOptionalConfig() {
|
|
|
2537
3231
|
}
|
|
2538
3232
|
|
|
2539
3233
|
// src/cli/index.ts
|
|
2540
|
-
function parseArgs(argv) {
|
|
2541
|
-
const args = { command: argv[0], out: "./src/lunnoa" };
|
|
2542
|
-
let i = 1;
|
|
2543
|
-
if (args.command === "workflows" || args.command === "agents" || args.command === "entity-types") {
|
|
2544
|
-
args.subcommand = argv[1];
|
|
2545
|
-
i = 2;
|
|
2546
|
-
if (argv[2] && !argv[2].startsWith("--")) {
|
|
2547
|
-
args.path = argv[2];
|
|
2548
|
-
i = 3;
|
|
2549
|
-
}
|
|
2550
|
-
}
|
|
2551
|
-
for (; i < argv.length; i += 1) {
|
|
2552
|
-
const flag = argv[i];
|
|
2553
|
-
const value = argv[i + 1];
|
|
2554
|
-
switch (flag) {
|
|
2555
|
-
case "--url":
|
|
2556
|
-
args.url = value;
|
|
2557
|
-
i += 1;
|
|
2558
|
-
break;
|
|
2559
|
-
case "--api-key":
|
|
2560
|
-
args.apiKey = value;
|
|
2561
|
-
i += 1;
|
|
2562
|
-
break;
|
|
2563
|
-
case "--access-token":
|
|
2564
|
-
args.accessToken = value;
|
|
2565
|
-
i += 1;
|
|
2566
|
-
break;
|
|
2567
|
-
case "--project-id":
|
|
2568
|
-
args.projectId = value;
|
|
2569
|
-
i += 1;
|
|
2570
|
-
break;
|
|
2571
|
-
case "--out":
|
|
2572
|
-
args.out = value;
|
|
2573
|
-
i += 1;
|
|
2574
|
-
break;
|
|
2575
|
-
default:
|
|
2576
|
-
if (!args.path && !flag.startsWith("--")) {
|
|
2577
|
-
args.path = flag;
|
|
2578
|
-
break;
|
|
2579
|
-
}
|
|
2580
|
-
throw new Error(`Unknown option: ${flag}`);
|
|
2581
|
-
}
|
|
2582
|
-
}
|
|
2583
|
-
return args;
|
|
2584
|
-
}
|
|
2585
|
-
var USAGE = `Usage:
|
|
2586
|
-
npx @lunnoa/client codegen --url <deployment-url> --api-key <lna_key> [--out <dir>]
|
|
2587
|
-
npx @lunnoa/client workflows deploy <path> --url <url> --api-key <key> --project-id <uuid>
|
|
2588
|
-
npx @lunnoa/client agents deploy <path> --url <url> --api-key <key> --project-id <uuid>
|
|
2589
|
-
npx @lunnoa/client entity-types deploy <path> --url <url> --api-key <key>
|
|
2590
|
-
|
|
2591
|
-
Options:
|
|
2592
|
-
--url Base URL of the Lunnoa Automate deployment
|
|
2593
|
-
--api-key Machine API key (lna_...)
|
|
2594
|
-
--access-token User JWT as an alternative to --api-key
|
|
2595
|
-
--project-id Project UUID (required for workflow/agent deploy)
|
|
2596
|
-
--out Output directory for lunnoa.generated.ts (codegen; default: ./src/lunnoa)
|
|
2597
|
-
|
|
2598
|
-
Environment: LUNNOA_URL, LUNNOA_API_KEY, LUNNOA_PROJECT_ID
|
|
2599
|
-
Optional config file: lunnoa.config.ts | .mjs | .js | .json
|
|
2600
|
-
|
|
2601
|
-
Entity-type deploy is workspace-scoped (no --project-id). Unknown instance
|
|
2602
|
-
attributes are rejected unless allowUnknownAttributes is true; use extensions.
|
|
2603
|
-
`;
|
|
2604
3234
|
async function bindClient(args) {
|
|
2605
3235
|
const fileConfig = await loadOptionalConfig();
|
|
2606
3236
|
const url = args.url ?? process.env.LUNNOA_URL ?? fileConfig.url;
|
|
@@ -2726,9 +3356,106 @@ async function deployEntityType(args) {
|
|
|
2726
3356
|
`Upserted entity type ${result.id} (${payload.slug})` + (typeof result.schemaRevision === "number" ? ` schemaRevision=${result.schemaRevision}` : "")
|
|
2727
3357
|
);
|
|
2728
3358
|
}
|
|
3359
|
+
var DISCOVERY_SUBCOMMANDS = {
|
|
3360
|
+
tools: ["list", "get", "search"],
|
|
3361
|
+
apps: ["list", "get"],
|
|
3362
|
+
connections: ["list", "get"],
|
|
3363
|
+
features: void 0,
|
|
3364
|
+
projects: ["list"]
|
|
3365
|
+
};
|
|
3366
|
+
function isDiscoveryCommand(args) {
|
|
3367
|
+
const { command, subcommand } = args;
|
|
3368
|
+
if (!command) {
|
|
3369
|
+
return false;
|
|
3370
|
+
}
|
|
3371
|
+
if (command === "features") {
|
|
3372
|
+
return true;
|
|
3373
|
+
}
|
|
3374
|
+
if (command === "projects" && (subcommand === "list" || !subcommand)) {
|
|
3375
|
+
return true;
|
|
3376
|
+
}
|
|
3377
|
+
if ((command === "agents" || command === "workflows" || command === "entity-types") && subcommand === "list") {
|
|
3378
|
+
return true;
|
|
3379
|
+
}
|
|
3380
|
+
const allowed = DISCOVERY_SUBCOMMANDS[command];
|
|
3381
|
+
return Array.isArray(allowed) && Boolean(subcommand && allowed.includes(subcommand));
|
|
3382
|
+
}
|
|
3383
|
+
function assertKnownDiscoveryShape(args) {
|
|
3384
|
+
const { command, subcommand } = args;
|
|
3385
|
+
const allowed = command ? DISCOVERY_SUBCOMMANDS[command] : void 0;
|
|
3386
|
+
if (!command || allowed === void 0) {
|
|
3387
|
+
return;
|
|
3388
|
+
}
|
|
3389
|
+
if (!subcommand || !allowed.includes(subcommand)) {
|
|
3390
|
+
throw new Error(
|
|
3391
|
+
`Unknown command: ${command} ${subcommand ?? ""}
|
|
3392
|
+
|
|
3393
|
+
${USAGE}`
|
|
3394
|
+
);
|
|
3395
|
+
}
|
|
3396
|
+
}
|
|
3397
|
+
async function runDiscovery(args) {
|
|
3398
|
+
const { command, subcommand } = args;
|
|
3399
|
+
if (command === "tools" || command === "apps" || command === "connections") {
|
|
3400
|
+
assertKnownDiscoveryShape(args);
|
|
3401
|
+
}
|
|
3402
|
+
if (!isDiscoveryCommand(args)) {
|
|
3403
|
+
return false;
|
|
3404
|
+
}
|
|
3405
|
+
const { client } = await bindClient(args);
|
|
3406
|
+
if (command === "tools" && subcommand === "list") {
|
|
3407
|
+
await runToolsList(client, args);
|
|
3408
|
+
return true;
|
|
3409
|
+
}
|
|
3410
|
+
if (command === "tools" && subcommand === "get") {
|
|
3411
|
+
await runToolsGet(client, args);
|
|
3412
|
+
return true;
|
|
3413
|
+
}
|
|
3414
|
+
if (command === "tools" && subcommand === "search") {
|
|
3415
|
+
await runToolsSearch(client, args);
|
|
3416
|
+
return true;
|
|
3417
|
+
}
|
|
3418
|
+
if (command === "apps" && subcommand === "list") {
|
|
3419
|
+
await runAppsList(client, args);
|
|
3420
|
+
return true;
|
|
3421
|
+
}
|
|
3422
|
+
if (command === "apps" && subcommand === "get") {
|
|
3423
|
+
await runAppsGet(client, args);
|
|
3424
|
+
return true;
|
|
3425
|
+
}
|
|
3426
|
+
if (command === "connections" && subcommand === "list") {
|
|
3427
|
+
await runConnectionsList(client, args);
|
|
3428
|
+
return true;
|
|
3429
|
+
}
|
|
3430
|
+
if (command === "connections" && subcommand === "get") {
|
|
3431
|
+
await runConnectionsGet(client, args);
|
|
3432
|
+
return true;
|
|
3433
|
+
}
|
|
3434
|
+
if (command === "agents" && subcommand === "list") {
|
|
3435
|
+
await runAgentsList(client, args);
|
|
3436
|
+
return true;
|
|
3437
|
+
}
|
|
3438
|
+
if (command === "workflows" && subcommand === "list") {
|
|
3439
|
+
await runWorkflowsList(client, args);
|
|
3440
|
+
return true;
|
|
3441
|
+
}
|
|
3442
|
+
if (command === "entity-types" && subcommand === "list") {
|
|
3443
|
+
await runEntityTypesList(client, args);
|
|
3444
|
+
return true;
|
|
3445
|
+
}
|
|
3446
|
+
if (command === "projects" && (subcommand === "list" || !subcommand)) {
|
|
3447
|
+
await runProjectsList(client, args);
|
|
3448
|
+
return true;
|
|
3449
|
+
}
|
|
3450
|
+
if (command === "features") {
|
|
3451
|
+
await runFeatures(client, args);
|
|
3452
|
+
return true;
|
|
3453
|
+
}
|
|
3454
|
+
return false;
|
|
3455
|
+
}
|
|
2729
3456
|
async function main() {
|
|
2730
3457
|
const args = parseArgs(process.argv.slice(2));
|
|
2731
|
-
if (!args.command || args.command === "--help" || args.command === "help") {
|
|
3458
|
+
if (!args.command || args.command === "--help" || args.command === "help" || args.help) {
|
|
2732
3459
|
console.log(USAGE);
|
|
2733
3460
|
return;
|
|
2734
3461
|
}
|
|
@@ -2736,6 +3463,9 @@ async function main() {
|
|
|
2736
3463
|
await runCodegen(args);
|
|
2737
3464
|
return;
|
|
2738
3465
|
}
|
|
3466
|
+
if (await runDiscovery(args)) {
|
|
3467
|
+
return;
|
|
3468
|
+
}
|
|
2739
3469
|
if (args.command === "workflows" && args.subcommand === "deploy") {
|
|
2740
3470
|
await deployWorkflow(args);
|
|
2741
3471
|
return;
|