@holon-run/uxc-daemon-client 0.12.8 → 0.13.3

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/index.cjs CHANGED
@@ -31,7 +31,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
33
  DaemonRpcError: () => DaemonRpcError,
34
- UxcDaemonClient: () => UxcDaemonClient
34
+ UxcDaemonClient: () => UxcDaemonClient,
35
+ generateTypeScriptClient: () => generateTypeScriptClient
35
36
  });
36
37
  module.exports = __toCommonJS(index_exports);
37
38
  var import_node_child_process = require("child_process");
@@ -82,7 +83,38 @@ var UxcDaemonClient = class {
82
83
  options: normalizeOptions(args.options)
83
84
  });
84
85
  }
86
+ async codegenSchema(args) {
87
+ const response = await this.request("runtime.invoke", {
88
+ request_id: requestId("codegen"),
89
+ endpoint: args.endpoint,
90
+ action: "codegen_schema",
91
+ operation_id: null,
92
+ args: null,
93
+ options: normalizeOptions(args.options)
94
+ });
95
+ if (response.kind !== "codegen_host_schema") {
96
+ throw new Error(
97
+ `Unexpected codegen response kind '${response.kind}' (expected codegen_host_schema)`
98
+ );
99
+ }
100
+ assertCodegenHostSchema(response.data);
101
+ return response.data;
102
+ }
103
+ async generateTypeScriptClient(args) {
104
+ const schema = await this.codegenSchema({
105
+ endpoint: args.endpoint,
106
+ options: args.options
107
+ });
108
+ return generateTypeScriptClient(schema, args.emitter);
109
+ }
85
110
  async subscribeStart(args) {
111
+ const mode = args.mode ?? (args.pollConfig ? "poll" : "stream");
112
+ if (mode === "poll" && !args.pollConfig) {
113
+ throw new Error("pollConfig is required when mode is 'poll'");
114
+ }
115
+ if (mode !== "poll" && args.pollConfig) {
116
+ throw new Error("pollConfig is only valid when mode is 'poll'");
117
+ }
86
118
  return this.request("subscription.start", {
87
119
  request_id: requestId("subscribe"),
88
120
  endpoint: args.endpoint,
@@ -94,8 +126,8 @@ var UxcDaemonClient = class {
94
126
  transport_hint: args.transportHint ?? null,
95
127
  subprotocols: [],
96
128
  initial_text_frames: [],
97
- mode: args.mode ?? "stream",
98
- poll_config: null,
129
+ mode,
130
+ poll_config: args.pollConfig ?? null,
99
131
  ephemeral: args.ephemeral ?? (args.sink ?? "memory:") === "memory:",
100
132
  options: normalizeOptions(args.options)
101
133
  });
@@ -229,6 +261,361 @@ var UxcDaemonClient = class {
229
261
  return response;
230
262
  }
231
263
  };
264
+ function generateTypeScriptClient(schema, options = {}) {
265
+ const packageImport = options.packageImport ?? "@holon-run/uxc-daemon-client";
266
+ const className = sanitizeTypeName(options.className ?? defaultClassName(schema.host.id));
267
+ const methodNames = /* @__PURE__ */ new Set();
268
+ const typeNames = /* @__PURE__ */ new Set();
269
+ const operationBlocks = [];
270
+ const typeBlocks = [];
271
+ for (const operation of schema.operations) {
272
+ if (!operation.execute || operation.help_only || operation.subscribable) {
273
+ continue;
274
+ }
275
+ const methodName = uniqueName(
276
+ sanitizeMethodName(defaultMethodName(operation.id)),
277
+ methodNames
278
+ );
279
+ const typeName = uniqueName(
280
+ sanitizeTypeName(`${upperFirst(methodName)}Input`),
281
+ typeNames
282
+ );
283
+ const selectedInputSchema = selectOperationInputSchema(operation.input_schema);
284
+ const inputType = selectedInputSchema ? renderTsTypeFromSchema(selectedInputSchema, 0) : "Record<string, unknown>";
285
+ const inputRequired = selectedInputSchema ? hasRequiredInput(selectedInputSchema) : false;
286
+ typeBlocks.push(`export type ${typeName} = ${inputType};`);
287
+ operationBlocks.push(
288
+ [
289
+ ` async ${methodName}(`,
290
+ inputRequired ? ` input: ${typeName},` : ` input?: ${typeName},`,
291
+ ` options: RuntimeInvokeOptions = {},`,
292
+ ` ): Promise<RuntimeResult<unknown>> {`,
293
+ ` return this.client.call({`,
294
+ ` endpoint: this.endpoint,`,
295
+ ` operation: ${JSON.stringify(operation.id)},`,
296
+ ` payload: toRuntimePayload(input),`,
297
+ ` options: { ...this.defaultOptions, ...options },`,
298
+ ` }) as Promise<RuntimeResult<unknown>>;`,
299
+ ` }`
300
+ ].join("\n")
301
+ );
302
+ }
303
+ const lines = [
304
+ `import { UxcDaemonClient, type RuntimeInvokeOptions, type RuntimeResult } from ${JSON.stringify(packageImport)};`,
305
+ "",
306
+ ...typeBlocks,
307
+ "",
308
+ `export interface ${className}Options {`,
309
+ " client?: UxcDaemonClient;",
310
+ " endpoint?: string;",
311
+ " defaultOptions?: RuntimeInvokeOptions;",
312
+ "}",
313
+ "",
314
+ `export class ${className} {`,
315
+ " readonly client: UxcDaemonClient;",
316
+ " readonly endpoint: string;",
317
+ " readonly defaultOptions: RuntimeInvokeOptions;",
318
+ "",
319
+ ` constructor(options: ${className}Options = {}) {`,
320
+ " this.client = options.client ?? new UxcDaemonClient();",
321
+ ` this.endpoint = options.endpoint ?? ${JSON.stringify(schema.host.endpoint)};`,
322
+ " this.defaultOptions = options.defaultOptions ?? {};",
323
+ " }",
324
+ "",
325
+ ...operationBlocks,
326
+ "}",
327
+ "",
328
+ "function toRuntimePayload(input: unknown): Record<string, unknown> | undefined {",
329
+ " if (input == null) {",
330
+ " return undefined;",
331
+ " }",
332
+ ' if (typeof input === "object" && !Array.isArray(input)) {',
333
+ " return input as Record<string, unknown>;",
334
+ " }",
335
+ " return { body: input };",
336
+ "}"
337
+ ];
338
+ if (options.includeSchemaJson) {
339
+ lines.push(
340
+ "",
341
+ `export const GENERATED_SCHEMA = ${JSON.stringify(schema, null, 2)} as const;`
342
+ );
343
+ }
344
+ return `${lines.join("\n")}
345
+ `;
346
+ }
347
+ function defaultClassName(hostId) {
348
+ return `${sanitizeTypeName(hostId)}Client`;
349
+ }
350
+ function defaultMethodName(operationId) {
351
+ const openApiMatch = operationId.match(/^([a-z]+):\/(.*)$/i);
352
+ if (openApiMatch) {
353
+ const verb = openApiMatch[1].toLowerCase();
354
+ const path = openApiMatch[2].replace(/\{([^}]+)\}/g, " by $1 ").replace(/[/:_-]+/g, " ");
355
+ return `${verb} ${path}`;
356
+ }
357
+ return operationId.replace(/[/:._-]+/g, " ");
358
+ }
359
+ function sanitizeMethodName(raw) {
360
+ const tokens = raw.replace(/[^a-zA-Z0-9]+/g, " ").trim().split(/\s+/).filter(Boolean);
361
+ if (tokens.length === 0) {
362
+ return "invoke";
363
+ }
364
+ const [first, ...rest] = tokens;
365
+ const normalized = [first.toLowerCase(), ...rest.map((token) => upperFirst(token.toLowerCase()))].join(
366
+ ""
367
+ );
368
+ return /^[a-zA-Z_$]/.test(normalized) ? normalized : `op${upperFirst(normalized)}`;
369
+ }
370
+ function sanitizeTypeName(raw) {
371
+ if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(raw)) {
372
+ return raw;
373
+ }
374
+ const tokens = raw.replace(/[^a-zA-Z0-9]+/g, " ").trim().split(/\s+/).filter(Boolean).map((token) => upperFirst(token));
375
+ const candidate = tokens.join("") || "Generated";
376
+ return /^[A-Za-z_$]/.test(candidate) ? candidate : `T${candidate}`;
377
+ }
378
+ function upperFirst(input) {
379
+ if (input.length === 0) {
380
+ return input;
381
+ }
382
+ return `${input[0].toUpperCase()}${input.slice(1)}`;
383
+ }
384
+ function uniqueName(name, used) {
385
+ let candidate = name;
386
+ let idx = 2;
387
+ while (used.has(candidate)) {
388
+ candidate = `${name}${idx}`;
389
+ idx += 1;
390
+ }
391
+ used.add(candidate);
392
+ return candidate;
393
+ }
394
+ function hasRequiredInput(schema) {
395
+ if (!schema || typeof schema !== "object") {
396
+ return false;
397
+ }
398
+ const required = schema.required;
399
+ return Array.isArray(required) && required.length > 0;
400
+ }
401
+ function selectOperationInputSchema(inputSchema) {
402
+ if (!inputSchema || typeof inputSchema !== "object") {
403
+ return void 0;
404
+ }
405
+ const obj = inputSchema;
406
+ if (obj.kind === "grpc_message" && typeof obj.schema === "object") {
407
+ return obj.schema;
408
+ }
409
+ if (obj.kind === "openrpc_method" && Array.isArray(obj.params)) {
410
+ const properties = {};
411
+ const required = [];
412
+ for (const raw of obj.params) {
413
+ if (!raw || typeof raw !== "object") {
414
+ continue;
415
+ }
416
+ const param = raw;
417
+ const name = typeof param.name === "string" ? param.name : void 0;
418
+ if (!name) {
419
+ continue;
420
+ }
421
+ const schema = typeof param.schema === "object" ? param.schema : {};
422
+ properties[name] = schema;
423
+ if (param.required === true) {
424
+ required.push(name);
425
+ }
426
+ }
427
+ return { type: "object", properties, required };
428
+ }
429
+ if (obj.kind === "openapi_request_body" && obj.content && typeof obj.content === "object") {
430
+ const content = obj.content;
431
+ const prioritized = [
432
+ "application/json",
433
+ "application/x-www-form-urlencoded",
434
+ "multipart/form-data"
435
+ ];
436
+ for (const mime of prioritized) {
437
+ const entry = content[mime];
438
+ if (entry && typeof entry === "object" && entry.schema) {
439
+ return entry.schema;
440
+ }
441
+ }
442
+ for (const entry of Object.values(content)) {
443
+ if (entry && typeof entry === "object" && entry.schema) {
444
+ return entry.schema;
445
+ }
446
+ }
447
+ }
448
+ if (typeof obj.schema === "object" && obj.type == null) {
449
+ return obj.schema;
450
+ }
451
+ return inputSchema;
452
+ }
453
+ function renderTsTypeFromSchema(schema, depth) {
454
+ if (!schema || typeof schema !== "object") {
455
+ return "Record<string, unknown>";
456
+ }
457
+ const obj = schema;
458
+ const typeInfo = resolveSchemaType(obj.type);
459
+ const enumValues = asPrimitiveArray(obj.enum);
460
+ if (enumValues && enumValues.length > 0) {
461
+ return withNullable(
462
+ enumValues.map((value) => JSON.stringify(value)).join(" | "),
463
+ typeInfo.nullable
464
+ );
465
+ }
466
+ if (Array.isArray(obj.oneOf) && obj.oneOf.length > 0) {
467
+ return withNullable(
468
+ obj.oneOf.map((item) => renderTsTypeFromSchema(item, depth + 1)).join(" | "),
469
+ typeInfo.nullable
470
+ );
471
+ }
472
+ if (Array.isArray(obj.anyOf) && obj.anyOf.length > 0) {
473
+ return withNullable(
474
+ obj.anyOf.map((item) => renderTsTypeFromSchema(item, depth + 1)).join(" | "),
475
+ typeInfo.nullable
476
+ );
477
+ }
478
+ const schemaType = typeInfo.base;
479
+ const openApiNullable = obj.nullable === true;
480
+ switch (schemaType) {
481
+ case "string":
482
+ return withNullable("string", typeInfo.nullable || openApiNullable);
483
+ case "integer":
484
+ case "number":
485
+ return withNullable("number", typeInfo.nullable || openApiNullable);
486
+ case "boolean":
487
+ return withNullable("boolean", typeInfo.nullable || openApiNullable);
488
+ case "null":
489
+ return "null";
490
+ case "array": {
491
+ const itemType = renderTsTypeFromSchema(obj.items, depth + 1);
492
+ return withNullable(`Array<${itemType}>`, typeInfo.nullable || openApiNullable);
493
+ }
494
+ case "object": {
495
+ const properties = obj.properties;
496
+ if (!properties || typeof properties !== "object") {
497
+ return withNullable("Record<string, unknown>", typeInfo.nullable || openApiNullable);
498
+ }
499
+ if (depth > 4) {
500
+ return withNullable("Record<string, unknown>", typeInfo.nullable || openApiNullable);
501
+ }
502
+ const required = new Set(
503
+ Array.isArray(obj.required) ? obj.required.filter((item) => typeof item === "string") : []
504
+ );
505
+ const fields = Object.entries(properties).map(([name, value]) => {
506
+ const optional = required.has(name) ? "" : "?";
507
+ const key = safePropertyName(name);
508
+ const valueType = renderTsTypeFromSchema(value, depth + 1);
509
+ return `${key}${optional}: ${valueType}`;
510
+ });
511
+ if (fields.length === 0) {
512
+ return withNullable("Record<string, unknown>", typeInfo.nullable || openApiNullable);
513
+ }
514
+ return withNullable(`{ ${fields.join("; ")} }`, typeInfo.nullable || openApiNullable);
515
+ }
516
+ default:
517
+ return withNullable("unknown", typeInfo.nullable || openApiNullable);
518
+ }
519
+ }
520
+ function resolveSchemaType(typeValue) {
521
+ if (typeof typeValue === "string") {
522
+ return {
523
+ base: typeValue,
524
+ nullable: typeValue === "null"
525
+ };
526
+ }
527
+ if (Array.isArray(typeValue)) {
528
+ const nullable = typeValue.some((entry) => entry === "null");
529
+ const nonNull = typeValue.find(
530
+ (entry) => typeof entry === "string" && entry !== "null"
531
+ );
532
+ return {
533
+ base: nonNull,
534
+ nullable
535
+ };
536
+ }
537
+ return {
538
+ base: void 0,
539
+ nullable: false
540
+ };
541
+ }
542
+ function asPrimitiveArray(value) {
543
+ if (!Array.isArray(value)) {
544
+ return void 0;
545
+ }
546
+ const allPrimitive = value.every(
547
+ (item) => item == null || typeof item === "string" || typeof item === "number" || typeof item === "boolean"
548
+ );
549
+ return allPrimitive ? value : void 0;
550
+ }
551
+ function safePropertyName(name) {
552
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
553
+ }
554
+ function withNullable(type, nullable) {
555
+ if (!nullable || type.includes("null")) {
556
+ return type;
557
+ }
558
+ return `${type} | null`;
559
+ }
560
+ function assertCodegenHostSchema(value) {
561
+ const obj = asRecord(value, "codegen schema");
562
+ assertString(obj.version, "codegen.version");
563
+ assertNumber(obj.generated_at_unix, "codegen.generated_at_unix");
564
+ const host = asRecord(obj.host, "codegen.host");
565
+ assertString(host.id, "codegen.host.id");
566
+ assertString(host.endpoint, "codegen.host.endpoint");
567
+ assertString(host.protocol, "codegen.host.protocol");
568
+ const runtime = asRecord(obj.runtime, "codegen.runtime");
569
+ if (!("invoke_options_schema" in runtime)) {
570
+ throw new Error("Invalid codegen schema: runtime.invoke_options_schema is required");
571
+ }
572
+ if (!("result_meta_schema" in runtime)) {
573
+ throw new Error("Invalid codegen schema: runtime.result_meta_schema is required");
574
+ }
575
+ if (!("artifact_meta_schema" in runtime)) {
576
+ throw new Error("Invalid codegen schema: runtime.artifact_meta_schema is required");
577
+ }
578
+ if (!("lifecycle_contract" in runtime)) {
579
+ throw new Error("Invalid codegen schema: runtime.lifecycle_contract is required");
580
+ }
581
+ if (!("artifact_contract" in runtime)) {
582
+ throw new Error("Invalid codegen schema: runtime.artifact_contract is required");
583
+ }
584
+ if (!Array.isArray(obj.operations)) {
585
+ throw new Error("Invalid codegen schema: operations must be an array");
586
+ }
587
+ for (const [index, operationValue] of obj.operations.entries()) {
588
+ const operation = asRecord(operationValue, `codegen.operations[${index}]`);
589
+ assertString(operation.id, `codegen.operations[${index}].id`);
590
+ assertString(operation.display_name, `codegen.operations[${index}].display_name`);
591
+ assertString(operation.kind, `codegen.operations[${index}].kind`);
592
+ assertString(operation.result_kind, `codegen.operations[${index}].result_kind`);
593
+ assertBoolean(operation.execute, `codegen.operations[${index}].execute`);
594
+ assertBoolean(operation.help_only, `codegen.operations[${index}].help_only`);
595
+ assertBoolean(operation.subscribable, `codegen.operations[${index}].subscribable`);
596
+ }
597
+ }
598
+ function asRecord(value, label) {
599
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
600
+ throw new Error(`Invalid ${label}: expected object`);
601
+ }
602
+ return value;
603
+ }
604
+ function assertString(value, label) {
605
+ if (typeof value !== "string" || value.length === 0) {
606
+ throw new Error(`Invalid ${label}: expected non-empty string`);
607
+ }
608
+ }
609
+ function assertNumber(value, label) {
610
+ if (typeof value !== "number" || !Number.isFinite(value)) {
611
+ throw new Error(`Invalid ${label}: expected number`);
612
+ }
613
+ }
614
+ function assertBoolean(value, label) {
615
+ if (typeof value !== "boolean") {
616
+ throw new Error(`Invalid ${label}: expected boolean`);
617
+ }
618
+ }
232
619
  function normalizeOptions(options) {
233
620
  return {
234
621
  inject_env: [],
@@ -279,5 +666,6 @@ function tryParseFrame(buffer) {
279
666
  // Annotate the CommonJS export names for ESM import in node:
280
667
  0 && (module.exports = {
281
668
  DaemonRpcError,
282
- UxcDaemonClient
669
+ UxcDaemonClient,
670
+ generateTypeScriptClient
283
671
  });
package/dist/index.d.cts CHANGED
@@ -6,9 +6,13 @@ interface RuntimeInvokeOptions {
6
6
  refresh_schema?: boolean;
7
7
  schema_url?: string;
8
8
  link_name?: string;
9
+ link_skill?: string;
10
+ link_skill_doc?: string;
11
+ link_skill_path?: string;
9
12
  schema_mapping_file?: string;
10
13
  daemon_exclusive?: string[];
11
14
  daemon_idle_ttl?: number;
15
+ request_headers?: Record<string, string>;
12
16
  }
13
17
  interface RuntimeInvokeResponse {
14
18
  protocol: string;
@@ -17,8 +21,59 @@ interface RuntimeInvokeResponse {
17
21
  operation?: string | null;
18
22
  data: unknown;
19
23
  duration_ms?: number | null;
20
- meta: Record<string, unknown>;
24
+ meta: RuntimeMeta;
21
25
  }
26
+ interface RuntimeMeta {
27
+ schema_involved?: boolean | null;
28
+ cache_source?: string | null;
29
+ cache_age_ms?: number | null;
30
+ cache_stale?: boolean | null;
31
+ cache_fallback?: boolean | null;
32
+ daemon_session_reused?: boolean | null;
33
+ response_status_code?: number | null;
34
+ response_headers?: Record<string, string> | null;
35
+ artifact_truncated?: boolean | null;
36
+ artifact_kind?: string | null;
37
+ artifact_bytes?: number | null;
38
+ artifact_path?: string | null;
39
+ artifact_ref?: string | null;
40
+ artifact_sha256?: string | null;
41
+ [key: string]: unknown;
42
+ }
43
+ interface CodegenHostSchemaV1 {
44
+ version: "v1" | string;
45
+ generated_at_unix: number;
46
+ host: {
47
+ id: string;
48
+ endpoint: string;
49
+ protocol: string;
50
+ link_name?: string | null;
51
+ };
52
+ runtime: {
53
+ invoke_options_schema: unknown;
54
+ result_meta_schema: unknown;
55
+ artifact_meta_schema: unknown;
56
+ lifecycle_contract: unknown;
57
+ artifact_contract: unknown;
58
+ };
59
+ operations: CodegenOperationV1[];
60
+ }
61
+ interface CodegenOperationV1 {
62
+ id: string;
63
+ display_name: string;
64
+ description?: string | null;
65
+ kind: string;
66
+ input_schema?: unknown;
67
+ output_schema?: unknown;
68
+ result_kind: string;
69
+ execute: boolean;
70
+ help_only: boolean;
71
+ subscribable: boolean;
72
+ }
73
+ type RuntimeResult<TData = unknown, TMeta = Record<string, unknown>> = Omit<RuntimeInvokeResponse, "data" | "meta"> & {
74
+ data: TData;
75
+ meta: TMeta;
76
+ };
22
77
  interface SubscriptionEventEnvelope {
23
78
  version: string;
24
79
  job_id: string;
@@ -92,18 +147,48 @@ interface UxcDaemonClientOptions {
92
147
  requestTimeoutMs?: number;
93
148
  env?: NodeJS.ProcessEnv;
94
149
  }
150
+ interface GenerateTypeScriptClientOptions {
151
+ className?: string;
152
+ packageImport?: string;
153
+ includeSchemaJson?: boolean;
154
+ }
95
155
  interface SubscribeStartArgs {
96
156
  endpoint: string;
97
157
  resourceUri?: string;
98
158
  operationId?: string;
99
159
  args?: Record<string, unknown>;
100
160
  mode?: "stream" | "poll";
161
+ pollConfig?: PollSubscriptionConfig;
101
162
  options?: RuntimeInvokeOptions;
102
163
  sink?: `file:${string}` | "memory:";
103
164
  ephemeral?: boolean;
104
165
  readResource?: boolean;
105
166
  transportHint?: "websocket" | "discord_gateway" | "slack_socket_mode" | "feishu_long_connection";
106
167
  }
168
+ interface PollSubscriptionConfig {
169
+ interval_secs: number;
170
+ extract_items_pointer: string;
171
+ missing_extract_items_pointer_as_empty?: boolean;
172
+ request_cursor_arg?: string;
173
+ response_cursor_pointer?: string;
174
+ cursor_from_item_pointer?: string;
175
+ cursor_transform?: "increment";
176
+ checkpoint_strategy: {
177
+ type: "cursor_only";
178
+ } | {
179
+ type: "item_key";
180
+ item_key_pointer: string;
181
+ seen_window?: number;
182
+ } | {
183
+ type: "watermark";
184
+ item_watermark_pointer: string;
185
+ item_tiebreaker_pointer?: string;
186
+ seen_window?: number;
187
+ } | {
188
+ type: "content_hash";
189
+ seen_window?: number;
190
+ };
191
+ }
107
192
  declare class DaemonRpcError extends Error {
108
193
  readonly code: number;
109
194
  readonly method: string;
@@ -123,9 +208,18 @@ declare class UxcDaemonClient {
123
208
  call(args: {
124
209
  endpoint: string;
125
210
  operation: string;
126
- payload?: Record<string, unknown>;
211
+ payload?: Record<string, unknown> | undefined;
127
212
  options?: RuntimeInvokeOptions;
128
213
  }): Promise<RuntimeInvokeResponse>;
214
+ codegenSchema(args: {
215
+ endpoint: string;
216
+ options?: RuntimeInvokeOptions;
217
+ }): Promise<CodegenHostSchemaV1>;
218
+ generateTypeScriptClient(args: {
219
+ endpoint: string;
220
+ options?: RuntimeInvokeOptions;
221
+ emitter?: GenerateTypeScriptClientOptions;
222
+ }): Promise<string>;
129
223
  subscribeStart(args: SubscribeStartArgs): Promise<SubscribeStartResponse>;
130
224
  subscribeList(): Promise<SubscriptionJobView[]>;
131
225
  subscribeStatus(jobId: string): Promise<SubscriptionJobView>;
@@ -146,5 +240,6 @@ declare class UxcDaemonClient {
146
240
  private ensureDaemon;
147
241
  private requestOnce;
148
242
  }
243
+ declare function generateTypeScriptClient(schema: CodegenHostSchemaV1, options?: GenerateTypeScriptClientOptions): string;
149
244
 
150
- export { DaemonRpcError, type DaemonStatus, type RuntimeInvokeOptions, type RuntimeInvokeResponse, type SubscribeStartArgs, type SubscribeStartResponse, type SubscribeStopResponse, type SubscriptionEventEnvelope, type SubscriptionEventsResponse, type SubscriptionJobView, UxcDaemonClient, type UxcDaemonClientOptions };
245
+ export { type CodegenHostSchemaV1, type CodegenOperationV1, DaemonRpcError, type DaemonStatus, type GenerateTypeScriptClientOptions, type PollSubscriptionConfig, type RuntimeInvokeOptions, type RuntimeInvokeResponse, type RuntimeMeta, type RuntimeResult, type SubscribeStartArgs, type SubscribeStartResponse, type SubscribeStopResponse, type SubscriptionEventEnvelope, type SubscriptionEventsResponse, type SubscriptionJobView, UxcDaemonClient, type UxcDaemonClientOptions, generateTypeScriptClient };
package/dist/index.d.ts CHANGED
@@ -6,9 +6,13 @@ interface RuntimeInvokeOptions {
6
6
  refresh_schema?: boolean;
7
7
  schema_url?: string;
8
8
  link_name?: string;
9
+ link_skill?: string;
10
+ link_skill_doc?: string;
11
+ link_skill_path?: string;
9
12
  schema_mapping_file?: string;
10
13
  daemon_exclusive?: string[];
11
14
  daemon_idle_ttl?: number;
15
+ request_headers?: Record<string, string>;
12
16
  }
13
17
  interface RuntimeInvokeResponse {
14
18
  protocol: string;
@@ -17,8 +21,59 @@ interface RuntimeInvokeResponse {
17
21
  operation?: string | null;
18
22
  data: unknown;
19
23
  duration_ms?: number | null;
20
- meta: Record<string, unknown>;
24
+ meta: RuntimeMeta;
21
25
  }
26
+ interface RuntimeMeta {
27
+ schema_involved?: boolean | null;
28
+ cache_source?: string | null;
29
+ cache_age_ms?: number | null;
30
+ cache_stale?: boolean | null;
31
+ cache_fallback?: boolean | null;
32
+ daemon_session_reused?: boolean | null;
33
+ response_status_code?: number | null;
34
+ response_headers?: Record<string, string> | null;
35
+ artifact_truncated?: boolean | null;
36
+ artifact_kind?: string | null;
37
+ artifact_bytes?: number | null;
38
+ artifact_path?: string | null;
39
+ artifact_ref?: string | null;
40
+ artifact_sha256?: string | null;
41
+ [key: string]: unknown;
42
+ }
43
+ interface CodegenHostSchemaV1 {
44
+ version: "v1" | string;
45
+ generated_at_unix: number;
46
+ host: {
47
+ id: string;
48
+ endpoint: string;
49
+ protocol: string;
50
+ link_name?: string | null;
51
+ };
52
+ runtime: {
53
+ invoke_options_schema: unknown;
54
+ result_meta_schema: unknown;
55
+ artifact_meta_schema: unknown;
56
+ lifecycle_contract: unknown;
57
+ artifact_contract: unknown;
58
+ };
59
+ operations: CodegenOperationV1[];
60
+ }
61
+ interface CodegenOperationV1 {
62
+ id: string;
63
+ display_name: string;
64
+ description?: string | null;
65
+ kind: string;
66
+ input_schema?: unknown;
67
+ output_schema?: unknown;
68
+ result_kind: string;
69
+ execute: boolean;
70
+ help_only: boolean;
71
+ subscribable: boolean;
72
+ }
73
+ type RuntimeResult<TData = unknown, TMeta = Record<string, unknown>> = Omit<RuntimeInvokeResponse, "data" | "meta"> & {
74
+ data: TData;
75
+ meta: TMeta;
76
+ };
22
77
  interface SubscriptionEventEnvelope {
23
78
  version: string;
24
79
  job_id: string;
@@ -92,18 +147,48 @@ interface UxcDaemonClientOptions {
92
147
  requestTimeoutMs?: number;
93
148
  env?: NodeJS.ProcessEnv;
94
149
  }
150
+ interface GenerateTypeScriptClientOptions {
151
+ className?: string;
152
+ packageImport?: string;
153
+ includeSchemaJson?: boolean;
154
+ }
95
155
  interface SubscribeStartArgs {
96
156
  endpoint: string;
97
157
  resourceUri?: string;
98
158
  operationId?: string;
99
159
  args?: Record<string, unknown>;
100
160
  mode?: "stream" | "poll";
161
+ pollConfig?: PollSubscriptionConfig;
101
162
  options?: RuntimeInvokeOptions;
102
163
  sink?: `file:${string}` | "memory:";
103
164
  ephemeral?: boolean;
104
165
  readResource?: boolean;
105
166
  transportHint?: "websocket" | "discord_gateway" | "slack_socket_mode" | "feishu_long_connection";
106
167
  }
168
+ interface PollSubscriptionConfig {
169
+ interval_secs: number;
170
+ extract_items_pointer: string;
171
+ missing_extract_items_pointer_as_empty?: boolean;
172
+ request_cursor_arg?: string;
173
+ response_cursor_pointer?: string;
174
+ cursor_from_item_pointer?: string;
175
+ cursor_transform?: "increment";
176
+ checkpoint_strategy: {
177
+ type: "cursor_only";
178
+ } | {
179
+ type: "item_key";
180
+ item_key_pointer: string;
181
+ seen_window?: number;
182
+ } | {
183
+ type: "watermark";
184
+ item_watermark_pointer: string;
185
+ item_tiebreaker_pointer?: string;
186
+ seen_window?: number;
187
+ } | {
188
+ type: "content_hash";
189
+ seen_window?: number;
190
+ };
191
+ }
107
192
  declare class DaemonRpcError extends Error {
108
193
  readonly code: number;
109
194
  readonly method: string;
@@ -123,9 +208,18 @@ declare class UxcDaemonClient {
123
208
  call(args: {
124
209
  endpoint: string;
125
210
  operation: string;
126
- payload?: Record<string, unknown>;
211
+ payload?: Record<string, unknown> | undefined;
127
212
  options?: RuntimeInvokeOptions;
128
213
  }): Promise<RuntimeInvokeResponse>;
214
+ codegenSchema(args: {
215
+ endpoint: string;
216
+ options?: RuntimeInvokeOptions;
217
+ }): Promise<CodegenHostSchemaV1>;
218
+ generateTypeScriptClient(args: {
219
+ endpoint: string;
220
+ options?: RuntimeInvokeOptions;
221
+ emitter?: GenerateTypeScriptClientOptions;
222
+ }): Promise<string>;
129
223
  subscribeStart(args: SubscribeStartArgs): Promise<SubscribeStartResponse>;
130
224
  subscribeList(): Promise<SubscriptionJobView[]>;
131
225
  subscribeStatus(jobId: string): Promise<SubscriptionJobView>;
@@ -146,5 +240,6 @@ declare class UxcDaemonClient {
146
240
  private ensureDaemon;
147
241
  private requestOnce;
148
242
  }
243
+ declare function generateTypeScriptClient(schema: CodegenHostSchemaV1, options?: GenerateTypeScriptClientOptions): string;
149
244
 
150
- export { DaemonRpcError, type DaemonStatus, type RuntimeInvokeOptions, type RuntimeInvokeResponse, type SubscribeStartArgs, type SubscribeStartResponse, type SubscribeStopResponse, type SubscriptionEventEnvelope, type SubscriptionEventsResponse, type SubscriptionJobView, UxcDaemonClient, type UxcDaemonClientOptions };
245
+ export { type CodegenHostSchemaV1, type CodegenOperationV1, DaemonRpcError, type DaemonStatus, type GenerateTypeScriptClientOptions, type PollSubscriptionConfig, type RuntimeInvokeOptions, type RuntimeInvokeResponse, type RuntimeMeta, type RuntimeResult, type SubscribeStartArgs, type SubscribeStartResponse, type SubscribeStopResponse, type SubscriptionEventEnvelope, type SubscriptionEventsResponse, type SubscriptionJobView, UxcDaemonClient, type UxcDaemonClientOptions, generateTypeScriptClient };
package/dist/index.js CHANGED
@@ -47,7 +47,38 @@ var UxcDaemonClient = class {
47
47
  options: normalizeOptions(args.options)
48
48
  });
49
49
  }
50
+ async codegenSchema(args) {
51
+ const response = await this.request("runtime.invoke", {
52
+ request_id: requestId("codegen"),
53
+ endpoint: args.endpoint,
54
+ action: "codegen_schema",
55
+ operation_id: null,
56
+ args: null,
57
+ options: normalizeOptions(args.options)
58
+ });
59
+ if (response.kind !== "codegen_host_schema") {
60
+ throw new Error(
61
+ `Unexpected codegen response kind '${response.kind}' (expected codegen_host_schema)`
62
+ );
63
+ }
64
+ assertCodegenHostSchema(response.data);
65
+ return response.data;
66
+ }
67
+ async generateTypeScriptClient(args) {
68
+ const schema = await this.codegenSchema({
69
+ endpoint: args.endpoint,
70
+ options: args.options
71
+ });
72
+ return generateTypeScriptClient(schema, args.emitter);
73
+ }
50
74
  async subscribeStart(args) {
75
+ const mode = args.mode ?? (args.pollConfig ? "poll" : "stream");
76
+ if (mode === "poll" && !args.pollConfig) {
77
+ throw new Error("pollConfig is required when mode is 'poll'");
78
+ }
79
+ if (mode !== "poll" && args.pollConfig) {
80
+ throw new Error("pollConfig is only valid when mode is 'poll'");
81
+ }
51
82
  return this.request("subscription.start", {
52
83
  request_id: requestId("subscribe"),
53
84
  endpoint: args.endpoint,
@@ -59,8 +90,8 @@ var UxcDaemonClient = class {
59
90
  transport_hint: args.transportHint ?? null,
60
91
  subprotocols: [],
61
92
  initial_text_frames: [],
62
- mode: args.mode ?? "stream",
63
- poll_config: null,
93
+ mode,
94
+ poll_config: args.pollConfig ?? null,
64
95
  ephemeral: args.ephemeral ?? (args.sink ?? "memory:") === "memory:",
65
96
  options: normalizeOptions(args.options)
66
97
  });
@@ -194,6 +225,361 @@ var UxcDaemonClient = class {
194
225
  return response;
195
226
  }
196
227
  };
228
+ function generateTypeScriptClient(schema, options = {}) {
229
+ const packageImport = options.packageImport ?? "@holon-run/uxc-daemon-client";
230
+ const className = sanitizeTypeName(options.className ?? defaultClassName(schema.host.id));
231
+ const methodNames = /* @__PURE__ */ new Set();
232
+ const typeNames = /* @__PURE__ */ new Set();
233
+ const operationBlocks = [];
234
+ const typeBlocks = [];
235
+ for (const operation of schema.operations) {
236
+ if (!operation.execute || operation.help_only || operation.subscribable) {
237
+ continue;
238
+ }
239
+ const methodName = uniqueName(
240
+ sanitizeMethodName(defaultMethodName(operation.id)),
241
+ methodNames
242
+ );
243
+ const typeName = uniqueName(
244
+ sanitizeTypeName(`${upperFirst(methodName)}Input`),
245
+ typeNames
246
+ );
247
+ const selectedInputSchema = selectOperationInputSchema(operation.input_schema);
248
+ const inputType = selectedInputSchema ? renderTsTypeFromSchema(selectedInputSchema, 0) : "Record<string, unknown>";
249
+ const inputRequired = selectedInputSchema ? hasRequiredInput(selectedInputSchema) : false;
250
+ typeBlocks.push(`export type ${typeName} = ${inputType};`);
251
+ operationBlocks.push(
252
+ [
253
+ ` async ${methodName}(`,
254
+ inputRequired ? ` input: ${typeName},` : ` input?: ${typeName},`,
255
+ ` options: RuntimeInvokeOptions = {},`,
256
+ ` ): Promise<RuntimeResult<unknown>> {`,
257
+ ` return this.client.call({`,
258
+ ` endpoint: this.endpoint,`,
259
+ ` operation: ${JSON.stringify(operation.id)},`,
260
+ ` payload: toRuntimePayload(input),`,
261
+ ` options: { ...this.defaultOptions, ...options },`,
262
+ ` }) as Promise<RuntimeResult<unknown>>;`,
263
+ ` }`
264
+ ].join("\n")
265
+ );
266
+ }
267
+ const lines = [
268
+ `import { UxcDaemonClient, type RuntimeInvokeOptions, type RuntimeResult } from ${JSON.stringify(packageImport)};`,
269
+ "",
270
+ ...typeBlocks,
271
+ "",
272
+ `export interface ${className}Options {`,
273
+ " client?: UxcDaemonClient;",
274
+ " endpoint?: string;",
275
+ " defaultOptions?: RuntimeInvokeOptions;",
276
+ "}",
277
+ "",
278
+ `export class ${className} {`,
279
+ " readonly client: UxcDaemonClient;",
280
+ " readonly endpoint: string;",
281
+ " readonly defaultOptions: RuntimeInvokeOptions;",
282
+ "",
283
+ ` constructor(options: ${className}Options = {}) {`,
284
+ " this.client = options.client ?? new UxcDaemonClient();",
285
+ ` this.endpoint = options.endpoint ?? ${JSON.stringify(schema.host.endpoint)};`,
286
+ " this.defaultOptions = options.defaultOptions ?? {};",
287
+ " }",
288
+ "",
289
+ ...operationBlocks,
290
+ "}",
291
+ "",
292
+ "function toRuntimePayload(input: unknown): Record<string, unknown> | undefined {",
293
+ " if (input == null) {",
294
+ " return undefined;",
295
+ " }",
296
+ ' if (typeof input === "object" && !Array.isArray(input)) {',
297
+ " return input as Record<string, unknown>;",
298
+ " }",
299
+ " return { body: input };",
300
+ "}"
301
+ ];
302
+ if (options.includeSchemaJson) {
303
+ lines.push(
304
+ "",
305
+ `export const GENERATED_SCHEMA = ${JSON.stringify(schema, null, 2)} as const;`
306
+ );
307
+ }
308
+ return `${lines.join("\n")}
309
+ `;
310
+ }
311
+ function defaultClassName(hostId) {
312
+ return `${sanitizeTypeName(hostId)}Client`;
313
+ }
314
+ function defaultMethodName(operationId) {
315
+ const openApiMatch = operationId.match(/^([a-z]+):\/(.*)$/i);
316
+ if (openApiMatch) {
317
+ const verb = openApiMatch[1].toLowerCase();
318
+ const path = openApiMatch[2].replace(/\{([^}]+)\}/g, " by $1 ").replace(/[/:_-]+/g, " ");
319
+ return `${verb} ${path}`;
320
+ }
321
+ return operationId.replace(/[/:._-]+/g, " ");
322
+ }
323
+ function sanitizeMethodName(raw) {
324
+ const tokens = raw.replace(/[^a-zA-Z0-9]+/g, " ").trim().split(/\s+/).filter(Boolean);
325
+ if (tokens.length === 0) {
326
+ return "invoke";
327
+ }
328
+ const [first, ...rest] = tokens;
329
+ const normalized = [first.toLowerCase(), ...rest.map((token) => upperFirst(token.toLowerCase()))].join(
330
+ ""
331
+ );
332
+ return /^[a-zA-Z_$]/.test(normalized) ? normalized : `op${upperFirst(normalized)}`;
333
+ }
334
+ function sanitizeTypeName(raw) {
335
+ if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(raw)) {
336
+ return raw;
337
+ }
338
+ const tokens = raw.replace(/[^a-zA-Z0-9]+/g, " ").trim().split(/\s+/).filter(Boolean).map((token) => upperFirst(token));
339
+ const candidate = tokens.join("") || "Generated";
340
+ return /^[A-Za-z_$]/.test(candidate) ? candidate : `T${candidate}`;
341
+ }
342
+ function upperFirst(input) {
343
+ if (input.length === 0) {
344
+ return input;
345
+ }
346
+ return `${input[0].toUpperCase()}${input.slice(1)}`;
347
+ }
348
+ function uniqueName(name, used) {
349
+ let candidate = name;
350
+ let idx = 2;
351
+ while (used.has(candidate)) {
352
+ candidate = `${name}${idx}`;
353
+ idx += 1;
354
+ }
355
+ used.add(candidate);
356
+ return candidate;
357
+ }
358
+ function hasRequiredInput(schema) {
359
+ if (!schema || typeof schema !== "object") {
360
+ return false;
361
+ }
362
+ const required = schema.required;
363
+ return Array.isArray(required) && required.length > 0;
364
+ }
365
+ function selectOperationInputSchema(inputSchema) {
366
+ if (!inputSchema || typeof inputSchema !== "object") {
367
+ return void 0;
368
+ }
369
+ const obj = inputSchema;
370
+ if (obj.kind === "grpc_message" && typeof obj.schema === "object") {
371
+ return obj.schema;
372
+ }
373
+ if (obj.kind === "openrpc_method" && Array.isArray(obj.params)) {
374
+ const properties = {};
375
+ const required = [];
376
+ for (const raw of obj.params) {
377
+ if (!raw || typeof raw !== "object") {
378
+ continue;
379
+ }
380
+ const param = raw;
381
+ const name = typeof param.name === "string" ? param.name : void 0;
382
+ if (!name) {
383
+ continue;
384
+ }
385
+ const schema = typeof param.schema === "object" ? param.schema : {};
386
+ properties[name] = schema;
387
+ if (param.required === true) {
388
+ required.push(name);
389
+ }
390
+ }
391
+ return { type: "object", properties, required };
392
+ }
393
+ if (obj.kind === "openapi_request_body" && obj.content && typeof obj.content === "object") {
394
+ const content = obj.content;
395
+ const prioritized = [
396
+ "application/json",
397
+ "application/x-www-form-urlencoded",
398
+ "multipart/form-data"
399
+ ];
400
+ for (const mime of prioritized) {
401
+ const entry = content[mime];
402
+ if (entry && typeof entry === "object" && entry.schema) {
403
+ return entry.schema;
404
+ }
405
+ }
406
+ for (const entry of Object.values(content)) {
407
+ if (entry && typeof entry === "object" && entry.schema) {
408
+ return entry.schema;
409
+ }
410
+ }
411
+ }
412
+ if (typeof obj.schema === "object" && obj.type == null) {
413
+ return obj.schema;
414
+ }
415
+ return inputSchema;
416
+ }
417
+ function renderTsTypeFromSchema(schema, depth) {
418
+ if (!schema || typeof schema !== "object") {
419
+ return "Record<string, unknown>";
420
+ }
421
+ const obj = schema;
422
+ const typeInfo = resolveSchemaType(obj.type);
423
+ const enumValues = asPrimitiveArray(obj.enum);
424
+ if (enumValues && enumValues.length > 0) {
425
+ return withNullable(
426
+ enumValues.map((value) => JSON.stringify(value)).join(" | "),
427
+ typeInfo.nullable
428
+ );
429
+ }
430
+ if (Array.isArray(obj.oneOf) && obj.oneOf.length > 0) {
431
+ return withNullable(
432
+ obj.oneOf.map((item) => renderTsTypeFromSchema(item, depth + 1)).join(" | "),
433
+ typeInfo.nullable
434
+ );
435
+ }
436
+ if (Array.isArray(obj.anyOf) && obj.anyOf.length > 0) {
437
+ return withNullable(
438
+ obj.anyOf.map((item) => renderTsTypeFromSchema(item, depth + 1)).join(" | "),
439
+ typeInfo.nullable
440
+ );
441
+ }
442
+ const schemaType = typeInfo.base;
443
+ const openApiNullable = obj.nullable === true;
444
+ switch (schemaType) {
445
+ case "string":
446
+ return withNullable("string", typeInfo.nullable || openApiNullable);
447
+ case "integer":
448
+ case "number":
449
+ return withNullable("number", typeInfo.nullable || openApiNullable);
450
+ case "boolean":
451
+ return withNullable("boolean", typeInfo.nullable || openApiNullable);
452
+ case "null":
453
+ return "null";
454
+ case "array": {
455
+ const itemType = renderTsTypeFromSchema(obj.items, depth + 1);
456
+ return withNullable(`Array<${itemType}>`, typeInfo.nullable || openApiNullable);
457
+ }
458
+ case "object": {
459
+ const properties = obj.properties;
460
+ if (!properties || typeof properties !== "object") {
461
+ return withNullable("Record<string, unknown>", typeInfo.nullable || openApiNullable);
462
+ }
463
+ if (depth > 4) {
464
+ return withNullable("Record<string, unknown>", typeInfo.nullable || openApiNullable);
465
+ }
466
+ const required = new Set(
467
+ Array.isArray(obj.required) ? obj.required.filter((item) => typeof item === "string") : []
468
+ );
469
+ const fields = Object.entries(properties).map(([name, value]) => {
470
+ const optional = required.has(name) ? "" : "?";
471
+ const key = safePropertyName(name);
472
+ const valueType = renderTsTypeFromSchema(value, depth + 1);
473
+ return `${key}${optional}: ${valueType}`;
474
+ });
475
+ if (fields.length === 0) {
476
+ return withNullable("Record<string, unknown>", typeInfo.nullable || openApiNullable);
477
+ }
478
+ return withNullable(`{ ${fields.join("; ")} }`, typeInfo.nullable || openApiNullable);
479
+ }
480
+ default:
481
+ return withNullable("unknown", typeInfo.nullable || openApiNullable);
482
+ }
483
+ }
484
+ function resolveSchemaType(typeValue) {
485
+ if (typeof typeValue === "string") {
486
+ return {
487
+ base: typeValue,
488
+ nullable: typeValue === "null"
489
+ };
490
+ }
491
+ if (Array.isArray(typeValue)) {
492
+ const nullable = typeValue.some((entry) => entry === "null");
493
+ const nonNull = typeValue.find(
494
+ (entry) => typeof entry === "string" && entry !== "null"
495
+ );
496
+ return {
497
+ base: nonNull,
498
+ nullable
499
+ };
500
+ }
501
+ return {
502
+ base: void 0,
503
+ nullable: false
504
+ };
505
+ }
506
+ function asPrimitiveArray(value) {
507
+ if (!Array.isArray(value)) {
508
+ return void 0;
509
+ }
510
+ const allPrimitive = value.every(
511
+ (item) => item == null || typeof item === "string" || typeof item === "number" || typeof item === "boolean"
512
+ );
513
+ return allPrimitive ? value : void 0;
514
+ }
515
+ function safePropertyName(name) {
516
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
517
+ }
518
+ function withNullable(type, nullable) {
519
+ if (!nullable || type.includes("null")) {
520
+ return type;
521
+ }
522
+ return `${type} | null`;
523
+ }
524
+ function assertCodegenHostSchema(value) {
525
+ const obj = asRecord(value, "codegen schema");
526
+ assertString(obj.version, "codegen.version");
527
+ assertNumber(obj.generated_at_unix, "codegen.generated_at_unix");
528
+ const host = asRecord(obj.host, "codegen.host");
529
+ assertString(host.id, "codegen.host.id");
530
+ assertString(host.endpoint, "codegen.host.endpoint");
531
+ assertString(host.protocol, "codegen.host.protocol");
532
+ const runtime = asRecord(obj.runtime, "codegen.runtime");
533
+ if (!("invoke_options_schema" in runtime)) {
534
+ throw new Error("Invalid codegen schema: runtime.invoke_options_schema is required");
535
+ }
536
+ if (!("result_meta_schema" in runtime)) {
537
+ throw new Error("Invalid codegen schema: runtime.result_meta_schema is required");
538
+ }
539
+ if (!("artifact_meta_schema" in runtime)) {
540
+ throw new Error("Invalid codegen schema: runtime.artifact_meta_schema is required");
541
+ }
542
+ if (!("lifecycle_contract" in runtime)) {
543
+ throw new Error("Invalid codegen schema: runtime.lifecycle_contract is required");
544
+ }
545
+ if (!("artifact_contract" in runtime)) {
546
+ throw new Error("Invalid codegen schema: runtime.artifact_contract is required");
547
+ }
548
+ if (!Array.isArray(obj.operations)) {
549
+ throw new Error("Invalid codegen schema: operations must be an array");
550
+ }
551
+ for (const [index, operationValue] of obj.operations.entries()) {
552
+ const operation = asRecord(operationValue, `codegen.operations[${index}]`);
553
+ assertString(operation.id, `codegen.operations[${index}].id`);
554
+ assertString(operation.display_name, `codegen.operations[${index}].display_name`);
555
+ assertString(operation.kind, `codegen.operations[${index}].kind`);
556
+ assertString(operation.result_kind, `codegen.operations[${index}].result_kind`);
557
+ assertBoolean(operation.execute, `codegen.operations[${index}].execute`);
558
+ assertBoolean(operation.help_only, `codegen.operations[${index}].help_only`);
559
+ assertBoolean(operation.subscribable, `codegen.operations[${index}].subscribable`);
560
+ }
561
+ }
562
+ function asRecord(value, label) {
563
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
564
+ throw new Error(`Invalid ${label}: expected object`);
565
+ }
566
+ return value;
567
+ }
568
+ function assertString(value, label) {
569
+ if (typeof value !== "string" || value.length === 0) {
570
+ throw new Error(`Invalid ${label}: expected non-empty string`);
571
+ }
572
+ }
573
+ function assertNumber(value, label) {
574
+ if (typeof value !== "number" || !Number.isFinite(value)) {
575
+ throw new Error(`Invalid ${label}: expected number`);
576
+ }
577
+ }
578
+ function assertBoolean(value, label) {
579
+ if (typeof value !== "boolean") {
580
+ throw new Error(`Invalid ${label}: expected boolean`);
581
+ }
582
+ }
197
583
  function normalizeOptions(options) {
198
584
  return {
199
585
  inject_env: [],
@@ -243,5 +629,6 @@ function tryParseFrame(buffer) {
243
629
  }
244
630
  export {
245
631
  DaemonRpcError,
246
- UxcDaemonClient
632
+ UxcDaemonClient,
633
+ generateTypeScriptClient
247
634
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@holon-run/uxc-daemon-client",
3
- "version": "0.12.8",
3
+ "version": "0.13.3",
4
4
  "description": "Thin Node.js client for UXC daemon-backed operations",
5
5
  "license": "MIT",
6
6
  "repository": {