@lunnoa/client 0.0.0-development → 1.1.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
@@ -537,7 +537,8 @@ var DEFINE_KINDS = {
537
537
  aiConnection: "aiConnection",
538
538
  action: "action",
539
539
  workflow: "workflow",
540
- agent: "agent"
540
+ agent: "agent",
541
+ entityType: "entityType"
541
542
  };
542
543
  function resolveActionAppId(action) {
543
544
  if (action.appId?.trim()) {
@@ -1016,6 +1017,21 @@ var EntitiesResource = class {
1016
1017
  };
1017
1018
 
1018
1019
  // src/resources/entity-types.ts
1020
+ function entityTypeDefinitionToUpsert(def) {
1021
+ return {
1022
+ slug: def.slug,
1023
+ name: def.name,
1024
+ namePlural: def.namePlural,
1025
+ description: def.description,
1026
+ icon: def.icon,
1027
+ color: def.color,
1028
+ attributeSchema: def.attributeSchema,
1029
+ stateSchema: def.stateSchema,
1030
+ isEnabled: def.isEnabled,
1031
+ allowUnknownAttributes: def.allowUnknownAttributes ?? false,
1032
+ managedByCode: true
1033
+ };
1034
+ }
1019
1035
  var EntityTypesResource = class {
1020
1036
  constructor(http) {
1021
1037
  this.http = http;
@@ -1046,6 +1062,19 @@ var EntityTypesResource = class {
1046
1062
  query: { expansion: serializeCsv(options.expansion) }
1047
1063
  });
1048
1064
  }
1065
+ /**
1066
+ * Creates or updates an entity type by workspace-scoped slug.
1067
+ * Prefer the CLI (`npx @lunnoa/client entity-types deploy`) for happy-path deploy.
1068
+ */
1069
+ upsertBySlug(definition) {
1070
+ const payload = "kind" in definition && definition.kind === "entityType" ? entityTypeDefinitionToUpsert(definition) : definition;
1071
+ return this.http.post("/api/object-types/upsert", {
1072
+ body: payload,
1073
+ query: {
1074
+ expansion: "attributeSchema,stateSchema,description"
1075
+ }
1076
+ });
1077
+ }
1049
1078
  };
1050
1079
 
1051
1080
  // src/resources/approvals.ts
@@ -1506,12 +1535,42 @@ var QueueItemsResource = class {
1506
1535
  `/api/queues/${queueId}/items/${itemId}`
1507
1536
  );
1508
1537
  }
1509
- /** Adds an item to a queue. */
1538
+ /** Adds an item to a queue. Pass `objectId` to link the item to an entity. */
1510
1539
  create(queueId, data) {
1511
1540
  return this.http.post(`/api/queues/${queueId}/items`, {
1512
1541
  body: data
1513
1542
  });
1514
1543
  }
1544
+ /**
1545
+ * Lists queue items linked to an Object across all queues in the workspace.
1546
+ * Use for Object detail "Work" panels.
1547
+ */
1548
+ listByObject(objectId, options = {}) {
1549
+ return this.http.get(
1550
+ `/api/objects/${objectId}/queue-items`,
1551
+ {
1552
+ query: {
1553
+ status: options.status,
1554
+ limit: options.limit,
1555
+ offset: options.offset,
1556
+ expansion: serializeCsv(options.expansion)
1557
+ }
1558
+ }
1559
+ );
1560
+ }
1561
+ /** Async-iterates every queue item linked to an Object. */
1562
+ iterateByObject(objectId, options = {}) {
1563
+ return iterateOffset(async (offset) => {
1564
+ const result = await this.listByObject(objectId, {
1565
+ ...options,
1566
+ offset
1567
+ });
1568
+ return {
1569
+ items: result.items ?? [],
1570
+ pagination: result.pagination
1571
+ };
1572
+ });
1573
+ }
1515
1574
  /** Adds many items to a queue in one call. */
1516
1575
  bulkCreate(queueId, data) {
1517
1576
  return this.http.post(
@@ -1991,6 +2050,14 @@ var LunnoaClient = class {
1991
2050
  };
1992
2051
 
1993
2052
  // src/cli/generate.ts
2053
+ function computeDeploymentSchemaFingerprint(entityTypes) {
2054
+ const parts = [...entityTypes].map((type) => {
2055
+ const revision = type.schemaRevision ?? 1;
2056
+ const allowUnknown = type.allowUnknownAttributes ?? false;
2057
+ return `${type.slug}:${revision}:${allowUnknown ? 1 : 0}`;
2058
+ }).sort();
2059
+ return parts.join("|") || "empty";
2060
+ }
1994
2061
  function toCamelCase(input) {
1995
2062
  const parts = input.replace(/[^a-zA-Z0-9]+/g, " ").trim().split(/\s+/);
1996
2063
  if (parts.length === 0 || parts[0] === "") {
@@ -2110,6 +2177,9 @@ function isFieldRequired(field) {
2110
2177
  }
2111
2178
  function generateDeploymentModule(definitions) {
2112
2179
  const chunks = [];
2180
+ const schemaFingerprint = computeDeploymentSchemaFingerprint(
2181
+ definitions.entityTypes
2182
+ );
2113
2183
  chunks.push(
2114
2184
  `/* eslint-disable */
2115
2185
  /**
@@ -2118,8 +2188,23 @@ function generateDeploymentModule(definitions) {
2118
2188
  * Generated by \`npx @lunnoa/client codegen\` \u2014 do not edit by hand.
2119
2189
  * Re-run codegen whenever entity types, workflows, or agents change on
2120
2190
  * the deployment.
2191
+ *
2192
+ * Schema fingerprint: ${schemaFingerprint}
2121
2193
  */
2122
2194
  import type { LunnoaClient, Entity, Execution, ExecuteWorkflowResult, PaginatedEntities, Task, AgentChatStream, WaitUntilFinishedOptions, ListEntitiesOptions, UIMessage } from '@lunnoa/client';
2195
+
2196
+ /** Fingerprint of entity-type schemas at codegen time (slug:revision:allowUnknown). */
2197
+ export const DEPLOYMENT_SCHEMA_FINGERPRINT = ${JSON.stringify(schemaFingerprint)};
2198
+
2199
+ /** Per-type schemaRevision values captured at codegen time. */
2200
+ export const ENTITY_TYPE_SCHEMA_REVISIONS = ${JSON.stringify(
2201
+ Object.fromEntries(
2202
+ definitions.entityTypes.map((type) => [
2203
+ type.slug,
2204
+ type.schemaRevision ?? 1
2205
+ ])
2206
+ )
2207
+ )} as const;
2123
2208
  `
2124
2209
  );
2125
2210
  chunks.push(GENERATED_RUNTIME_HELPERS);
@@ -2408,6 +2493,21 @@ function pickAgentDefinition(mod) {
2408
2493
  "No defineAgent() export found. Export the definition as default or `agent`."
2409
2494
  );
2410
2495
  }
2496
+ function pickEntityTypeDefinition(mod) {
2497
+ const candidates = [
2498
+ mod.default,
2499
+ mod.entityType,
2500
+ ...Object.values(mod)
2501
+ ];
2502
+ for (const value of candidates) {
2503
+ if (value && typeof value === "object" && value.kind === DEFINE_KINDS.entityType) {
2504
+ return value;
2505
+ }
2506
+ }
2507
+ throw new Error(
2508
+ "No defineEntityType() export found. Export the definition as default or `entityType`."
2509
+ );
2510
+ }
2411
2511
  async function loadOptionalConfig() {
2412
2512
  const candidates = [
2413
2513
  "lunnoa.config.ts",
@@ -2440,7 +2540,7 @@ async function loadOptionalConfig() {
2440
2540
  function parseArgs(argv) {
2441
2541
  const args = { command: argv[0], out: "./src/lunnoa" };
2442
2542
  let i = 1;
2443
- if (args.command === "workflows" || args.command === "agents") {
2543
+ if (args.command === "workflows" || args.command === "agents" || args.command === "entity-types") {
2444
2544
  args.subcommand = argv[1];
2445
2545
  i = 2;
2446
2546
  if (argv[2] && !argv[2].startsWith("--")) {
@@ -2486,16 +2586,20 @@ var USAGE = `Usage:
2486
2586
  npx @lunnoa/client codegen --url <deployment-url> --api-key <lna_key> [--out <dir>]
2487
2587
  npx @lunnoa/client workflows deploy <path> --url <url> --api-key <key> --project-id <uuid>
2488
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>
2489
2590
 
2490
2591
  Options:
2491
2592
  --url Base URL of the Lunnoa Automate deployment
2492
2593
  --api-key Machine API key (lna_...)
2493
2594
  --access-token User JWT as an alternative to --api-key
2494
- --project-id Project UUID (required for deploy)
2595
+ --project-id Project UUID (required for workflow/agent deploy)
2495
2596
  --out Output directory for lunnoa.generated.ts (codegen; default: ./src/lunnoa)
2496
2597
 
2497
2598
  Environment: LUNNOA_URL, LUNNOA_API_KEY, LUNNOA_PROJECT_ID
2498
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.
2499
2603
  `;
2500
2604
  async function bindClient(args) {
2501
2605
  const fileConfig = await loadOptionalConfig();
@@ -2606,6 +2710,22 @@ async function deployAgent(args) {
2606
2710
  const result = await client.agents.upsertBySlug(projectId, payload);
2607
2711
  console.log(`Upserted agent ${result.id} (${payload.slug})`);
2608
2712
  }
2713
+ async function deployEntityType(args) {
2714
+ if (!args.path) {
2715
+ throw new Error(
2716
+ "Missing definition path. Example: entity-types deploy ./entity-type.ts"
2717
+ );
2718
+ }
2719
+ const { client } = await bindClient(args);
2720
+ const mod = await loadDefinitionModule(args.path);
2721
+ const def = pickEntityTypeDefinition(mod);
2722
+ const payload = entityTypeDefinitionToUpsert(def);
2723
+ console.log(`Deploying entity type slug="${payload.slug}" ...`);
2724
+ const result = await client.entityTypes.upsertBySlug(payload);
2725
+ console.log(
2726
+ `Upserted entity type ${result.id} (${payload.slug})` + (typeof result.schemaRevision === "number" ? ` schemaRevision=${result.schemaRevision}` : "")
2727
+ );
2728
+ }
2609
2729
  async function main() {
2610
2730
  const args = parseArgs(process.argv.slice(2));
2611
2731
  if (!args.command || args.command === "--help" || args.command === "help") {
@@ -2624,6 +2744,10 @@ async function main() {
2624
2744
  await deployAgent(args);
2625
2745
  return;
2626
2746
  }
2747
+ if (args.command === "entity-types" && args.subcommand === "deploy") {
2748
+ await deployEntityType(args);
2749
+ return;
2750
+ }
2627
2751
  console.error(`Unknown command: ${args.command} ${args.subcommand ?? ""}
2628
2752
  `);
2629
2753
  console.log(USAGE);
package/dist/index.cjs CHANGED
@@ -533,7 +533,8 @@ var DEFINE_KINDS = {
533
533
  aiConnection: "aiConnection",
534
534
  action: "action",
535
535
  workflow: "workflow",
536
- agent: "agent"
536
+ agent: "agent",
537
+ entityType: "entityType"
537
538
  };
538
539
  function defineConnection(config = {}) {
539
540
  return { kind: DEFINE_KINDS.connection, ...config };
@@ -568,6 +569,19 @@ function defineAgent(config) {
568
569
  }
569
570
  return { kind: DEFINE_KINDS.agent, ...config };
570
571
  }
572
+ function defineEntityType(config) {
573
+ if (!config.slug?.trim()) {
574
+ throw new Error("defineEntityType requires slug");
575
+ }
576
+ if (!config.attributeSchema || typeof config.attributeSchema !== "object" || Array.isArray(config.attributeSchema)) {
577
+ throw new Error("defineEntityType requires attributeSchema object");
578
+ }
579
+ return {
580
+ kind: DEFINE_KINDS.entityType,
581
+ allowUnknownAttributes: false,
582
+ ...config
583
+ };
584
+ }
571
585
  function resolveActionAppId(action) {
572
586
  if (action.appId?.trim()) {
573
587
  return action.appId.trim();
@@ -1045,6 +1059,21 @@ var EntitiesResource = class {
1045
1059
  };
1046
1060
 
1047
1061
  // src/resources/entity-types.ts
1062
+ function entityTypeDefinitionToUpsert(def) {
1063
+ return {
1064
+ slug: def.slug,
1065
+ name: def.name,
1066
+ namePlural: def.namePlural,
1067
+ description: def.description,
1068
+ icon: def.icon,
1069
+ color: def.color,
1070
+ attributeSchema: def.attributeSchema,
1071
+ stateSchema: def.stateSchema,
1072
+ isEnabled: def.isEnabled,
1073
+ allowUnknownAttributes: def.allowUnknownAttributes ?? false,
1074
+ managedByCode: true
1075
+ };
1076
+ }
1048
1077
  var EntityTypesResource = class {
1049
1078
  constructor(http) {
1050
1079
  this.http = http;
@@ -1075,6 +1104,19 @@ var EntityTypesResource = class {
1075
1104
  query: { expansion: serializeCsv(options.expansion) }
1076
1105
  });
1077
1106
  }
1107
+ /**
1108
+ * Creates or updates an entity type by workspace-scoped slug.
1109
+ * Prefer the CLI (`npx @lunnoa/client entity-types deploy`) for happy-path deploy.
1110
+ */
1111
+ upsertBySlug(definition) {
1112
+ const payload = "kind" in definition && definition.kind === "entityType" ? entityTypeDefinitionToUpsert(definition) : definition;
1113
+ return this.http.post("/api/object-types/upsert", {
1114
+ body: payload,
1115
+ query: {
1116
+ expansion: "attributeSchema,stateSchema,description"
1117
+ }
1118
+ });
1119
+ }
1078
1120
  };
1079
1121
 
1080
1122
  // src/resources/approvals.ts
@@ -1535,12 +1577,42 @@ var QueueItemsResource = class {
1535
1577
  `/api/queues/${queueId}/items/${itemId}`
1536
1578
  );
1537
1579
  }
1538
- /** Adds an item to a queue. */
1580
+ /** Adds an item to a queue. Pass `objectId` to link the item to an entity. */
1539
1581
  create(queueId, data) {
1540
1582
  return this.http.post(`/api/queues/${queueId}/items`, {
1541
1583
  body: data
1542
1584
  });
1543
1585
  }
1586
+ /**
1587
+ * Lists queue items linked to an Object across all queues in the workspace.
1588
+ * Use for Object detail "Work" panels.
1589
+ */
1590
+ listByObject(objectId, options = {}) {
1591
+ return this.http.get(
1592
+ `/api/objects/${objectId}/queue-items`,
1593
+ {
1594
+ query: {
1595
+ status: options.status,
1596
+ limit: options.limit,
1597
+ offset: options.offset,
1598
+ expansion: serializeCsv(options.expansion)
1599
+ }
1600
+ }
1601
+ );
1602
+ }
1603
+ /** Async-iterates every queue item linked to an Object. */
1604
+ iterateByObject(objectId, options = {}) {
1605
+ return iterateOffset(async (offset) => {
1606
+ const result = await this.listByObject(objectId, {
1607
+ ...options,
1608
+ offset
1609
+ });
1610
+ return {
1611
+ items: result.items ?? [],
1612
+ pagination: result.pagination
1613
+ };
1614
+ });
1615
+ }
1544
1616
  /** Adds many items to a queue in one call. */
1545
1617
  bulkCreate(queueId, data) {
1546
1618
  return this.http.post(
@@ -2128,7 +2200,9 @@ exports.defineAction = defineAction;
2128
2200
  exports.defineAgent = defineAgent;
2129
2201
  exports.defineAiConnection = defineAiConnection;
2130
2202
  exports.defineConnection = defineConnection;
2203
+ exports.defineEntityType = defineEntityType;
2131
2204
  exports.defineWorkflow = defineWorkflow;
2205
+ exports.entityTypeDefinitionToUpsert = entityTypeDefinitionToUpsert;
2132
2206
  exports.isBrowserEnvironment = isBrowserEnvironment;
2133
2207
  exports.isLoginRequires2FA = isLoginRequires2FA;
2134
2208
  exports.iterateOffset = iterateOffset;