@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/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
- /** Searches items in the queue. */
1617
- search(queueId, query) {
1618
- return this.http.get(`/api/queues/${queueId}/items/search`, {
1619
- query: { q: query }
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
- { body: error }
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; ownerId?: string }): Promise<Typed> {
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>; ownerId?: string },
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?: { chatMode?: 'builder' | 'preview'; signal?: AbortSignal },
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(taskId: string, message: string | UIMessage) {
2443
- return client.tasks.message(agentId, taskId, message);
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;