@lunnoa/client 1.0.0 → 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/README.md CHANGED
@@ -266,7 +266,13 @@ Re-run codegen whenever entity types, workflows, or agents change on the deploym
266
266
 
267
267
  ## Define, run, and CLI deploy
268
268
 
269
- Author local definitions with `defineWorkflow` / `defineAgent`, run linear chains with `lunnoa.runs.start`, and upsert into a project with the CLI (prefer CLI over calling upsert from app code):
269
+ Author local definitions with `defineWorkflow` / `defineAgent` / `defineEntityType`, run linear chains with `lunnoa.runs.start`, and upsert with the CLI (prefer CLI over calling upsert from app code). Entity types are workspace-scoped (no `--project-id`); instance writes reject unknown attribute keys unless `allowUnknownAttributes` is true — use the reserved `extensions` object for flexible bags:
270
+
271
+ ```bash
272
+ npx @lunnoa/client entity-types deploy ./human.ts --url https://lunnoa.example --api-key lna_...
273
+ ```
274
+
275
+ Workflows and agents:
270
276
 
271
277
  ```bash
272
278
  npx @lunnoa/client workflows deploy ./workflows/sync-orders.ts \
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
@@ -2021,6 +2050,14 @@ var LunnoaClient = class {
2021
2050
  };
2022
2051
 
2023
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
+ }
2024
2061
  function toCamelCase(input) {
2025
2062
  const parts = input.replace(/[^a-zA-Z0-9]+/g, " ").trim().split(/\s+/);
2026
2063
  if (parts.length === 0 || parts[0] === "") {
@@ -2140,6 +2177,9 @@ function isFieldRequired(field) {
2140
2177
  }
2141
2178
  function generateDeploymentModule(definitions) {
2142
2179
  const chunks = [];
2180
+ const schemaFingerprint = computeDeploymentSchemaFingerprint(
2181
+ definitions.entityTypes
2182
+ );
2143
2183
  chunks.push(
2144
2184
  `/* eslint-disable */
2145
2185
  /**
@@ -2148,8 +2188,23 @@ function generateDeploymentModule(definitions) {
2148
2188
  * Generated by \`npx @lunnoa/client codegen\` \u2014 do not edit by hand.
2149
2189
  * Re-run codegen whenever entity types, workflows, or agents change on
2150
2190
  * the deployment.
2191
+ *
2192
+ * Schema fingerprint: ${schemaFingerprint}
2151
2193
  */
2152
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;
2153
2208
  `
2154
2209
  );
2155
2210
  chunks.push(GENERATED_RUNTIME_HELPERS);
@@ -2438,6 +2493,21 @@ function pickAgentDefinition(mod) {
2438
2493
  "No defineAgent() export found. Export the definition as default or `agent`."
2439
2494
  );
2440
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
+ }
2441
2511
  async function loadOptionalConfig() {
2442
2512
  const candidates = [
2443
2513
  "lunnoa.config.ts",
@@ -2470,7 +2540,7 @@ async function loadOptionalConfig() {
2470
2540
  function parseArgs(argv) {
2471
2541
  const args = { command: argv[0], out: "./src/lunnoa" };
2472
2542
  let i = 1;
2473
- if (args.command === "workflows" || args.command === "agents") {
2543
+ if (args.command === "workflows" || args.command === "agents" || args.command === "entity-types") {
2474
2544
  args.subcommand = argv[1];
2475
2545
  i = 2;
2476
2546
  if (argv[2] && !argv[2].startsWith("--")) {
@@ -2516,16 +2586,20 @@ var USAGE = `Usage:
2516
2586
  npx @lunnoa/client codegen --url <deployment-url> --api-key <lna_key> [--out <dir>]
2517
2587
  npx @lunnoa/client workflows deploy <path> --url <url> --api-key <key> --project-id <uuid>
2518
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>
2519
2590
 
2520
2591
  Options:
2521
2592
  --url Base URL of the Lunnoa Automate deployment
2522
2593
  --api-key Machine API key (lna_...)
2523
2594
  --access-token User JWT as an alternative to --api-key
2524
- --project-id Project UUID (required for deploy)
2595
+ --project-id Project UUID (required for workflow/agent deploy)
2525
2596
  --out Output directory for lunnoa.generated.ts (codegen; default: ./src/lunnoa)
2526
2597
 
2527
2598
  Environment: LUNNOA_URL, LUNNOA_API_KEY, LUNNOA_PROJECT_ID
2528
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.
2529
2603
  `;
2530
2604
  async function bindClient(args) {
2531
2605
  const fileConfig = await loadOptionalConfig();
@@ -2636,6 +2710,22 @@ async function deployAgent(args) {
2636
2710
  const result = await client.agents.upsertBySlug(projectId, payload);
2637
2711
  console.log(`Upserted agent ${result.id} (${payload.slug})`);
2638
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
+ }
2639
2729
  async function main() {
2640
2730
  const args = parseArgs(process.argv.slice(2));
2641
2731
  if (!args.command || args.command === "--help" || args.command === "help") {
@@ -2654,6 +2744,10 @@ async function main() {
2654
2744
  await deployAgent(args);
2655
2745
  return;
2656
2746
  }
2747
+ if (args.command === "entity-types" && args.subcommand === "deploy") {
2748
+ await deployEntityType(args);
2749
+ return;
2750
+ }
2657
2751
  console.error(`Unknown command: ${args.command} ${args.subcommand ?? ""}
2658
2752
  `);
2659
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
@@ -2158,7 +2200,9 @@ exports.defineAction = defineAction;
2158
2200
  exports.defineAgent = defineAgent;
2159
2201
  exports.defineAiConnection = defineAiConnection;
2160
2202
  exports.defineConnection = defineConnection;
2203
+ exports.defineEntityType = defineEntityType;
2161
2204
  exports.defineWorkflow = defineWorkflow;
2205
+ exports.entityTypeDefinitionToUpsert = entityTypeDefinitionToUpsert;
2162
2206
  exports.isBrowserEnvironment = isBrowserEnvironment;
2163
2207
  exports.isLoginRequires2FA = isLoginRequires2FA;
2164
2208
  exports.iterateOffset = iterateOffset;