@soat/cli 0.6.5 → 0.6.10

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/bin/soat CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import('../dist/esm/index.js').then(({ runCli }) => {
2
+ import('../dist/index.mjs').then(({ runCli }) => {
3
3
  return runCli(process.argv);
4
4
  });
@@ -1,65 +1,20 @@
1
1
  /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
- var __defProp = Object.defineProperty;
3
- var __name = (target, value) => __defProp(target, "name", {
4
- value,
5
- configurable: true
6
- });
7
-
8
- // src/index.ts
9
- import { createHmac, timingSafeEqual } from "crypto";
10
- import { createServer } from "http";
11
- import * as nodePath from "path";
12
- import { fileURLToPath } from "url";
2
+ import { createHmac, timingSafeEqual } from "node:crypto";
3
+ import { createServer } from "node:http";
4
+ import * as path from "node:path";
5
+ import { fileURLToPath } from "node:url";
13
6
  import * as sdk from "@soat/sdk";
7
+ import { createClient, createConfig } from "@soat/sdk";
14
8
  import { program } from "commander";
9
+ import * as fs from "node:fs";
10
+ import yaml from "js-yaml";
11
+ import * as os from "node:os";
15
12
 
16
- // package.json
17
- var package_default = {
18
- name: "@soat/cli",
19
- version: "0.6.5",
20
- type: "module",
21
- scripts: {
22
- generate: "tsx scripts/generate.ts",
23
- lint: "eslint src tests",
24
- test: "jest --config tests/unit/jest.config.ts --coverage=false",
25
- typecheck: "tsc --noEmit && tsc --noEmit -p tests/tsconfig.json",
26
- build: "pnpm generate && tsup"
27
- },
28
- dependencies: {
29
- "@inquirer/input": "^5.0.12",
30
- "@inquirer/password": "^5.0.12",
31
- "@soat/sdk": "workspace:*",
32
- "@ttoss/logger": "^0.8.10",
33
- commander: "^14.0.3",
34
- "js-yaml": "^4.1.1"
35
- },
36
- devDependencies: {
37
- "@ttoss/config": "^1.37.10",
38
- "@ttoss/test-utils": "^4.2.10",
39
- "@types/jest": "^30.0.0",
40
- "@types/js-yaml": "^4.0.9",
41
- "@types/node": "^24",
42
- jest: "^30.3.0",
43
- tsup: "^8.5.1",
44
- tsx: "^4.21.0"
45
- },
46
- files: ["dist", "bin"],
47
- repository: {
48
- type: "git",
49
- url: "https://github.com/ttoss/soat"
50
- },
51
- bin: {
52
- soat: "./bin/soat"
53
- },
54
- publishConfig: {
55
- access: "public",
56
- provenance: true
57
- }
58
- };
13
+ //#region package.json
14
+ var version = "0.6.10";
59
15
 
60
- // src/cli-wrappers/wrappers/formations.ts
61
- import * as fs from "fs";
62
- import yaml from "js-yaml";
16
+ //#endregion
17
+ //#region src/cli-wrappers/wrappers/formations.ts
63
18
  var FORMATION_COMMANDS = ["validate-formation", "plan-formation", "create-formation", "update-formation"];
64
19
  var TEMPLATE_PATH_FLAG = "template-path";
65
20
  var TEMPLATE_FILE_FLAG = "template-file";
@@ -67,7 +22,7 @@ var ENV_FILE_FLAG = "env-file";
67
22
  var PARAMETER_FLAG = "parameter";
68
23
  var TEMPLATE_FIELD = "template";
69
24
  var PARAMETERS_FIELD = "parameters";
70
- var parseEnvFile = /* @__PURE__ */__name(args => {
25
+ var parseEnvFile = args => {
71
26
  const {
72
27
  envPath
73
28
  } = args;
@@ -81,19 +36,17 @@ var parseEnvFile = /* @__PURE__ */__name(args => {
81
36
  for (const rawLine of content.split(/\r?\n/)) {
82
37
  const line = rawLine.trim();
83
38
  if (!line || line.startsWith("#")) continue;
84
- const withoutExport = line.startsWith("export ") ? line.slice("export ".length).trim() : line;
39
+ const withoutExport = line.startsWith("export ") ? line.slice(7).trim() : line;
85
40
  const eqIdx = withoutExport.indexOf("=");
86
41
  if (eqIdx <= 0) continue;
87
42
  const key = withoutExport.slice(0, eqIdx).trim();
88
43
  let value = withoutExport.slice(eqIdx + 1).trim();
89
- if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
90
- value = value.slice(1, -1);
91
- }
44
+ if (value.startsWith("\"") && value.endsWith("\"") || value.startsWith("'") && value.endsWith("'")) value = value.slice(1, -1);
92
45
  vars[key] = value;
93
46
  }
94
47
  return vars;
95
- }, "parseEnvFile");
96
- var readTemplateFromPath = /* @__PURE__ */__name(args => {
48
+ };
49
+ var readTemplateFromPath = args => {
97
50
  const {
98
51
  templatePath
99
52
  } = args;
@@ -104,9 +57,7 @@ var readTemplateFromPath = /* @__PURE__ */__name(args => {
104
57
  throw new Error(`Unable to read template file: ${templatePath}`);
105
58
  }
106
59
  const trimmed = content.trim();
107
- if (!trimmed) {
108
- throw new Error(`Template file is empty: ${templatePath}`);
109
- }
60
+ if (!trimmed) throw new Error(`Template file is empty: ${templatePath}`);
110
61
  try {
111
62
  return JSON.parse(trimmed);
112
63
  } catch {}
@@ -115,8 +66,8 @@ var readTemplateFromPath = /* @__PURE__ */__name(args => {
115
66
  } catch {
116
67
  throw new Error(`Template file must contain valid JSON or YAML: ${templatePath}`);
117
68
  }
118
- }, "readTemplateFromPath");
119
- var resolveEnvRef = /* @__PURE__ */__name(args => {
69
+ };
70
+ var resolveEnvRef = args => {
120
71
  const {
121
72
  value,
122
73
  env
@@ -124,57 +75,43 @@ var resolveEnvRef = /* @__PURE__ */__name(args => {
124
75
  const atRef = /^@([A-Za-z_][A-Za-z0-9_]*)$/.exec(value);
125
76
  if (atRef) {
126
77
  const resolved = env[atRef[1]];
127
- if (resolved === void 0) {
128
- throw new Error(`Missing environment variable: ${atRef[1]}`);
129
- }
78
+ if (resolved === void 0) throw new Error(`Missing environment variable: ${atRef[1]}`);
130
79
  return resolved;
131
80
  }
132
81
  const simple = /^\$([A-Za-z_][A-Za-z0-9_]*)$/.exec(value);
133
82
  if (simple) {
134
83
  const resolved = env[simple[1]];
135
- if (resolved === void 0) {
136
- throw new Error(`Missing environment variable: ${simple[1]}`);
137
- }
84
+ if (resolved === void 0) throw new Error(`Missing environment variable: ${simple[1]}`);
138
85
  return resolved;
139
86
  }
140
87
  const bracketed = /^\$\{([A-Za-z_][A-Za-z0-9_]*)\}$/.exec(value);
141
88
  if (bracketed) {
142
89
  const resolved = env[bracketed[1]];
143
- if (resolved === void 0) {
144
- throw new Error(`Missing environment variable: ${bracketed[1]}`);
145
- }
90
+ if (resolved === void 0) throw new Error(`Missing environment variable: ${bracketed[1]}`);
146
91
  return resolved;
147
92
  }
148
93
  return value;
149
- }, "resolveEnvRef");
150
- var resolveParameterPair = /* @__PURE__ */__name(args => {
94
+ };
95
+ var resolveParameterPair = args => {
151
96
  const {
152
97
  pair,
153
98
  env
154
99
  } = args;
155
100
  const eqIdx = pair.indexOf("=");
156
- if (eqIdx === 0) {
157
- throw new Error(`Invalid --${PARAMETER_FLAG} value "${pair}". Parameter key cannot be empty.`);
158
- }
101
+ if (eqIdx === 0) throw new Error(`Invalid --${PARAMETER_FLAG} value "${pair}". Parameter key cannot be empty.`);
159
102
  if (eqIdx === -1) {
160
- const key2 = pair.trim();
161
- if (!key2) {
162
- throw new Error(`Invalid --${PARAMETER_FLAG} value "${pair}". Parameter key cannot be empty.`);
163
- }
164
- const resolved = env[key2];
165
- if (resolved === void 0) {
166
- throw new Error(`Missing environment variable: ${key2}`);
167
- }
103
+ const key = pair.trim();
104
+ if (!key) throw new Error(`Invalid --${PARAMETER_FLAG} value "${pair}". Parameter key cannot be empty.`);
105
+ const resolved = env[key];
106
+ if (resolved === void 0) throw new Error(`Missing environment variable: ${key}`);
168
107
  return {
169
- key: key2,
108
+ key,
170
109
  value: resolved
171
110
  };
172
111
  }
173
112
  const key = pair.slice(0, eqIdx).trim();
174
113
  const rawValue = pair.slice(eqIdx + 1);
175
- if (!key) {
176
- throw new Error(`Invalid --${PARAMETER_FLAG} value "${pair}". Parameter key cannot be empty.`);
177
- }
114
+ if (!key) throw new Error(`Invalid --${PARAMETER_FLAG} value "${pair}". Parameter key cannot be empty.`);
178
115
  return {
179
116
  key,
180
117
  value: resolveEnvRef({
@@ -182,7 +119,7 @@ var resolveParameterPair = /* @__PURE__ */__name(args => {
182
119
  env
183
120
  })
184
121
  };
185
- }, "resolveParameterPair");
122
+ };
186
123
  var formationsWrapper = {
187
124
  id: "formations-wrapper",
188
125
  commands: FORMATION_COMMANDS,
@@ -202,8 +139,7 @@ var formationsWrapper = {
202
139
  required: false,
203
140
  type: "string"
204
141
  }],
205
- // eslint-disable-next-line complexity
206
- apply: /* @__PURE__ */__name(({
142
+ apply: ({
207
143
  context
208
144
  }) => {
209
145
  const forcedBody = {};
@@ -221,31 +157,21 @@ var formationsWrapper = {
221
157
  const parametersInline = flags.single[PARAMETERS_FIELD];
222
158
  const parameterValues = flags.repeated[PARAMETER_FLAG] ?? [];
223
159
  const envFile = flags.single[ENV_FILE_FLAG];
224
- if (templatePath && templateFile) {
225
- throw new Error(`Use either --${TEMPLATE_PATH_FLAG} or --${TEMPLATE_FILE_FLAG}, not both.`);
226
- }
160
+ if (templatePath && templateFile) throw new Error(`Use either --${TEMPLATE_PATH_FLAG} or --${TEMPLATE_FILE_FLAG}, not both.`);
227
161
  const effectiveTemplatePath = templatePath ?? templateFile;
228
- if (templateInline && effectiveTemplatePath) {
229
- throw new Error(`Use either --${TEMPLATE_FIELD} or --${TEMPLATE_PATH_FLAG}, not both.`);
230
- }
231
- if (parametersInline && parameterValues.length > 0) {
232
- throw new Error(`Use either --${PARAMETERS_FIELD} or repeatable --${PARAMETER_FLAG}, not both.`);
233
- }
162
+ if (templateInline && effectiveTemplatePath) throw new Error(`Use either --${TEMPLATE_FIELD} or --${TEMPLATE_PATH_FLAG}, not both.`);
163
+ if (parametersInline && parameterValues.length > 0) throw new Error(`Use either --${PARAMETERS_FIELD} or repeatable --${PARAMETER_FLAG}, not both.`);
234
164
  let envFileVars = {};
235
- if (envFile) {
236
- envFileVars = parseEnvFile({
237
- envPath: envFile
238
- });
239
- }
165
+ if (envFile) envFileVars = parseEnvFile({
166
+ envPath: envFile
167
+ });
240
168
  const mergedEnv = {
241
169
  ...envFileVars,
242
170
  ...process.env
243
171
  };
244
- if (effectiveTemplatePath) {
245
- forcedBody[TEMPLATE_FIELD] = readTemplateFromPath({
246
- templatePath: effectiveTemplatePath
247
- });
248
- }
172
+ if (effectiveTemplatePath) forcedBody[TEMPLATE_FIELD] = readTemplateFromPath({
173
+ templatePath: effectiveTemplatePath
174
+ });
249
175
  if (parameterValues.length > 0) {
250
176
  const resolvedParameters = {};
251
177
  for (const pair of parameterValues) {
@@ -269,11 +195,12 @@ var formationsWrapper = {
269
195
  flags,
270
196
  forcedBody
271
197
  };
272
- }, "apply")
198
+ }
273
199
  };
274
200
 
275
- // src/cli-wrappers/flagParser.ts
276
- var parseUnknownWithRepeats = /* @__PURE__ */__name(args => {
201
+ //#endregion
202
+ //#region src/cli-wrappers/flagParser.ts
203
+ var parseUnknownWithRepeats = args => {
277
204
  const {
278
205
  cliArgs
279
206
  } = args;
@@ -286,40 +213,35 @@ var parseUnknownWithRepeats = /* @__PURE__ */__name(args => {
286
213
  const hasInlineValue = inlineSplitIdx > 2;
287
214
  const key = hasInlineValue ? arg.slice(2, inlineSplitIdx) : arg.slice(2);
288
215
  let value;
289
- if (hasInlineValue) {
290
- value = arg.slice(inlineSplitIdx + 1);
291
- } else {
216
+ if (hasInlineValue) value = arg.slice(inlineSplitIdx + 1);else {
292
217
  const next = cliArgs[i + 1];
293
218
  if (next !== void 0 && !next.startsWith("--")) {
294
219
  value = next;
295
220
  i++;
296
- } else {
297
- value = "true";
298
- }
221
+ } else value = "true";
299
222
  }
300
223
  single[key] = value;
301
- if (!repeated[key]) {
302
- repeated[key] = [];
303
- }
224
+ if (!repeated[key]) repeated[key] = [];
304
225
  repeated[key].push(value);
305
226
  }
306
227
  return {
307
228
  single,
308
229
  repeated
309
230
  };
310
- }, "parseUnknownWithRepeats");
231
+ };
311
232
 
312
- // src/cli-wrappers/index.ts
233
+ //#endregion
234
+ //#region src/cli-wrappers/index.ts
313
235
  var WRAPPERS = [formationsWrapper];
314
- var resolveWrapperForCommand = /* @__PURE__ */__name(args => {
236
+ var resolveWrapperForCommand = args => {
315
237
  const {
316
238
  commandName
317
239
  } = args;
318
240
  return WRAPPERS.find(wrapper => {
319
241
  return wrapper.commands.includes(commandName);
320
242
  });
321
- }, "resolveWrapperForCommand");
322
- var applyWrapperForCommand = /* @__PURE__ */__name(args => {
243
+ };
244
+ var applyWrapperForCommand = args => {
323
245
  const {
324
246
  commandName,
325
247
  route,
@@ -328,12 +250,10 @@ var applyWrapperForCommand = /* @__PURE__ */__name(args => {
328
250
  const wrapper = resolveWrapperForCommand({
329
251
  commandName
330
252
  });
331
- if (!wrapper) {
332
- return {
333
- flags: parsedFlags,
334
- forcedBody: {}
335
- };
336
- }
253
+ if (!wrapper) return {
254
+ flags: parsedFlags,
255
+ forcedBody: {}
256
+ };
337
257
  return wrapper.apply({
338
258
  context: {
339
259
  commandName,
@@ -341,67 +261,57 @@ var applyWrapperForCommand = /* @__PURE__ */__name(args => {
341
261
  parsedFlags
342
262
  }
343
263
  });
344
- }, "applyWrapperForCommand");
345
- var getWrapperHelpFlags = /* @__PURE__ */__name(commandName => {
346
- const wrapper = WRAPPERS.find(w => {
264
+ };
265
+ var getWrapperHelpFlags = commandName => {
266
+ return WRAPPERS.find(w => {
347
267
  return w.commands.includes(commandName);
348
- });
349
- return wrapper?.helpFlags ?? [];
350
- }, "getWrapperHelpFlags");
268
+ })?.helpFlags ?? [];
269
+ };
351
270
 
352
- // src/config.ts
353
- import * as fs2 from "fs";
354
- import * as os from "os";
355
- import * as path from "path";
356
- import { createClient, createConfig } from "@soat/sdk";
271
+ //#endregion
272
+ //#region src/config.ts
357
273
  var CONFIG_FILE = path.join(os.homedir(), ".soat", "config.json");
358
- var readConfig = /* @__PURE__ */__name(() => {
274
+ var readConfig = () => {
359
275
  try {
360
- return JSON.parse(fs2.readFileSync(CONFIG_FILE, "utf8"));
276
+ return JSON.parse(fs.readFileSync(CONFIG_FILE, "utf8"));
361
277
  } catch {
362
278
  return {};
363
279
  }
364
- }, "readConfig");
365
- var writeProfile = /* @__PURE__ */__name((name, profile) => {
280
+ };
281
+ var writeProfile = (name, profile) => {
366
282
  const config = readConfig();
367
283
  config[name] = profile;
368
- fs2.mkdirSync(path.dirname(CONFIG_FILE), {
284
+ fs.mkdirSync(path.dirname(CONFIG_FILE), {
369
285
  recursive: true
370
286
  });
371
- fs2.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
372
- }, "writeProfile");
373
- var resolveClient = /* @__PURE__ */__name(profileName => {
287
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
288
+ };
289
+ var resolveClient = profileName => {
374
290
  const envBaseUrl = process.env["SOAT_BASE_URL"];
375
291
  const envToken = process.env["SOAT_TOKEN"];
376
- if (envBaseUrl && envToken) {
377
- return createClient(createConfig({
378
- baseUrl: envBaseUrl,
379
- headers: {
380
- Authorization: `Bearer ${envToken}`
381
- }
382
- }));
383
- }
292
+ if (envBaseUrl && envToken) return createClient(createConfig({
293
+ baseUrl: envBaseUrl,
294
+ headers: {
295
+ Authorization: `Bearer ${envToken}`
296
+ }
297
+ }));
384
298
  const name = profileName ?? process.env["SOAT_PROFILE"] ?? "default";
385
- const config = readConfig();
386
- const profile = config[name];
387
- if (profile) {
388
- return createClient(createConfig({
389
- baseUrl: envBaseUrl ?? profile.baseUrl,
390
- headers: {
391
- Authorization: `Bearer ${profile.token}`
392
- }
393
- }));
394
- }
395
- if (envBaseUrl) {
396
- return createClient(createConfig({
397
- baseUrl: envBaseUrl
398
- }));
399
- }
299
+ const profile = readConfig()[name];
300
+ if (profile) return createClient(createConfig({
301
+ baseUrl: envBaseUrl ?? profile.baseUrl,
302
+ headers: {
303
+ Authorization: `Bearer ${profile.token}`
304
+ }
305
+ }));
306
+ if (envBaseUrl) return createClient(createConfig({
307
+ baseUrl: envBaseUrl
308
+ }));
400
309
  console.error(`Profile "${name}" not found. Run: soat configure${name !== "default" ? ` --profile ${name}` : ""}`);
401
310
  process.exit(1);
402
- }, "resolveClient");
311
+ };
403
312
 
404
- // src/generated/routes.ts
313
+ //#endregion
314
+ //#region src/generated/routes.ts
405
315
  var routes = {
406
316
  "list-actors": {
407
317
  serviceClass: "Actors",
@@ -457,7 +367,7 @@ var routes = {
457
367
  "in": "body"
458
368
  }, {
459
369
  "name": "external_id",
460
- "description": "Optional external identifier (e.g. WhatsApp phone number). If provided and an actor with this externalId already exists in the project, the existing actor is returned (idempotent \u2014 200 OK).",
370
+ "description": "Optional external identifier (e.g. WhatsApp phone number). If provided and an actor with this externalId already exists in the project, the existing actor is returned (idempotent 200 OK).",
461
371
  "required": false,
462
372
  "type": "string",
463
373
  "in": "body"
@@ -655,9 +565,9 @@ var routes = {
655
565
  "in": "body"
656
566
  }, {
657
567
  "name": "tool_choice",
658
- "description": "",
568
+ "description": "Tool choice strategy. Accepts a string (`\"auto\"`, `\"required\"`) or an object (`{ \"type\": \"tool\", \"name\": \"my_tool\" }`).",
659
569
  "required": false,
660
- "type": "object",
570
+ "type": "string",
661
571
  "in": "body"
662
572
  }, {
663
573
  "name": "stop_conditions",
@@ -775,9 +685,9 @@ var routes = {
775
685
  "in": "body"
776
686
  }, {
777
687
  "name": "tool_choice",
778
- "description": "",
688
+ "description": "Tool choice strategy. Accepts a string (`\"auto\"`, `\"required\"`) or an object (`{ \"type\": \"tool\", \"name\": \"my_tool\" }`).",
779
689
  "required": false,
780
- "type": "object",
690
+ "type": "string",
781
691
  "in": "body"
782
692
  }, {
783
693
  "name": "stop_conditions",
@@ -1339,7 +1249,7 @@ var routes = {
1339
1249
  "create-chat-completion": {
1340
1250
  serviceClass: "Chats",
1341
1251
  operationId: "createChatCompletion",
1342
- description: "OpenAI Chat Completions-compatible endpoint. Resolves the AI provider from `ai_provider_id`, decrypts its secret, and calls the appropriate Vercel AI SDK provider. `ai_provider_id` is required \u2014 there is no server-side model fallback.",
1252
+ description: "OpenAI Chat Completions-compatible endpoint. Resolves the AI provider from `ai_provider_id`, decrypts its secret, and calls the appropriate Vercel AI SDK provider. `ai_provider_id` is required there is no server-side model fallback.",
1343
1253
  moduleDocsUrl: "https://soat.ttoss.dev/docs/modules/chats",
1344
1254
  pathParams: [],
1345
1255
  queryParams: [],
@@ -1624,7 +1534,7 @@ var routes = {
1624
1534
  "in": "body"
1625
1535
  }, {
1626
1536
  "name": "stream",
1627
- "description": "If true, stream tokens via SSE. NOT IMPLEMENTED in v1 \u2014 returns 501.",
1537
+ "description": "If true, stream tokens via SSE. NOT IMPLEMENTED in v1 returns 501.",
1628
1538
  "required": false,
1629
1539
  "type": "boolean",
1630
1540
  "in": "body"
@@ -2356,7 +2266,7 @@ var routes = {
2356
2266
  "in": "body"
2357
2267
  }, {
2358
2268
  "name": "min_score",
2359
- "description": "Minimum similarity score (0\u20131). Results with lower scores are excluded. Only applies when `query` is provided.",
2269
+ "description": "Minimum similarity score (0–1). Results with lower scores are excluded. Only applies when `query` is provided.",
2360
2270
  "required": false,
2361
2271
  "type": "number",
2362
2272
  "in": "body"
@@ -2407,7 +2317,7 @@ var routes = {
2407
2317
  "in": "query"
2408
2318
  }, {
2409
2319
  "name": "tags",
2410
- "description": "Filter memories by tag patterns. Supports glob syntax (`*` matches any substring, `?` matches any single character). Multiple values are ORed \u2014 a memory is returned if any of its tags match any of the provided patterns. Omit to return all memories.\n",
2320
+ "description": "Filter memories by tag patterns. Supports glob syntax (`*` matches any substring, `?` matches any single character). Multiple values are ORed a memory is returned if any of its tags match any of the provided patterns. Omit to return all memories.\n",
2411
2321
  "required": false,
2412
2322
  "type": "array",
2413
2323
  "in": "query"
@@ -3248,6 +3158,12 @@ var routes = {
3248
3158
  "required": false,
3249
3159
  "type": "integer",
3250
3160
  "in": "body"
3161
+ }, {
3162
+ "name": "message_delay_seconds",
3163
+ "description": "Number of seconds to wait after the last user message before sending to the LLM. Acts as a debounce: each new message resets the timer. null or absent means no delay (immediate processing).\n",
3164
+ "required": false,
3165
+ "type": "integer",
3166
+ "in": "body"
3251
3167
  }]
3252
3168
  },
3253
3169
  "get-agent-session": {
@@ -3314,6 +3230,12 @@ var routes = {
3314
3230
  "required": false,
3315
3231
  "type": "object",
3316
3232
  "in": "body"
3233
+ }, {
3234
+ "name": "message_delay_seconds",
3235
+ "description": "Number of seconds to wait after the last user message before sending to the LLM. Acts as a debounce: each new message resets the timer. Set to null to disable the delay.\n",
3236
+ "required": false,
3237
+ "type": "integer",
3238
+ "in": "body"
3317
3239
  }]
3318
3240
  },
3319
3241
  "delete-agent-session": {
@@ -4227,30 +4149,36 @@ var routes = {
4227
4149
  }
4228
4150
  };
4229
4151
 
4230
- // src/index.ts
4231
- var toCanonical = /* @__PURE__ */__name(s => {
4152
+ //#endregion
4153
+ //#region src/index.ts
4154
+ /**
4155
+ * Normalize kebab-case, snake_case, or camelCase to camelCase for param matching.
4156
+ * e.g. agent-id → agentId, actor_id → actorId, agentId → agentId
4157
+ */
4158
+ var toCanonical = s => {
4232
4159
  return s.replace(/[-_]([a-z0-9])/g, (_, c) => {
4233
4160
  return c.toUpperCase();
4234
4161
  });
4235
- }, "toCanonical");
4236
- var kebabToSnake = /* @__PURE__ */__name(s => {
4162
+ };
4163
+ /** Convert kebab-case to snake_case for body/query keys (e.g. project-id → project_id). */
4164
+ var kebabToSnake = s => {
4237
4165
  return s.replace(/-/g, "_");
4238
- }, "kebabToSnake");
4239
- var parseFlagValue = /* @__PURE__ */__name(value => {
4166
+ };
4167
+ var parseFlagValue = value => {
4240
4168
  const trimmed = value.trim();
4241
- if (trimmed.startsWith("{") || trimmed.startsWith("[") || trimmed === "true" || trimmed === "false" || trimmed === "null" || /^-?\d+(\.\d+)?$/.test(trimmed)) {
4242
- try {
4243
- return JSON.parse(trimmed);
4244
- } catch {
4245
- return value;
4246
- }
4169
+ if (trimmed.startsWith("{") || trimmed.startsWith("[") || trimmed === "true" || trimmed === "false" || trimmed === "null" || /^-?\d+(\.\d+)?$/.test(trimmed)) try {
4170
+ return JSON.parse(trimmed);
4171
+ } catch {
4172
+ return value;
4247
4173
  }
4248
4174
  return value;
4249
- }, "parseFlagValue");
4250
- var normalizeSymbol = /* @__PURE__ */__name(name => {
4175
+ };
4176
+ /** Normalize symbol names to compare exports across acronym casing differences. */
4177
+ var normalizeSymbol = name => {
4251
4178
  return name.toLowerCase().replace(/[^a-z0-9]/g, "");
4252
- }, "normalizeSymbol");
4253
- var resolveServiceClass = /* @__PURE__ */__name(serviceClassName => {
4179
+ };
4180
+ /** Resolve SDK service exports even when generated names differ by acronym casing. */
4181
+ var resolveServiceClass = serviceClassName => {
4254
4182
  const sdkExports = sdk;
4255
4183
  const exactMatch = sdkExports[serviceClassName];
4256
4184
  if (exactMatch) return exactMatch;
@@ -4258,12 +4186,9 @@ var resolveServiceClass = /* @__PURE__ */__name(serviceClassName => {
4258
4186
  const fuzzyMatches = Object.entries(sdkExports).filter(([exportName, value]) => {
4259
4187
  return Boolean(value) && normalizeSymbol(exportName) === normalizedTarget;
4260
4188
  });
4261
- if (fuzzyMatches.length === 1) {
4262
- return fuzzyMatches[0][1];
4263
- }
4264
- return void 0;
4265
- }, "resolveServiceClass");
4266
- program.name("soat").description("SOAT CLI").version(package_default.version).option("-p, --profile <name>", "config profile to use");
4189
+ if (fuzzyMatches.length === 1) return fuzzyMatches[0][1];
4190
+ };
4191
+ program.name("soat").description("SOAT CLI").version(version).option("-p, --profile <name>", "config profile to use");
4267
4192
  program.addHelpText("after", "\nTip: Run `soat <command> --help` to see command-specific flags and docs.");
4268
4193
  program.command("configure").description("Save credentials to a named profile (~/.soat/config.json)").option("-p, --profile <name>", "profile name", "default").action(async opts => {
4269
4194
  const [{
@@ -4287,33 +4212,29 @@ program.command("list-commands").description("List all available API commands").
4287
4212
  const pad = Math.max(...Object.keys(routes).map(k => {
4288
4213
  return k.length;
4289
4214
  }));
4290
- for (const [cmd, r] of Object.entries(routes).sort()) {
4291
- console.log(` ${cmd.padEnd(pad)} ${r.description}`);
4292
- }
4215
+ for (const [cmd, r] of Object.entries(routes).sort()) console.log(` ${cmd.padEnd(pad)} ${r.description}`);
4293
4216
  });
4294
- var matchesFilter = /* @__PURE__ */__name((eventType, filter) => {
4217
+ var matchesFilter = (eventType, filter) => {
4295
4218
  const patterns = filter.split(",").map(part => {
4296
4219
  return part.trim();
4297
4220
  }).filter(Boolean);
4298
4221
  if (patterns.length === 0) return true;
4299
4222
  return patterns.some(pattern => {
4300
4223
  if (pattern === "*") return true;
4301
- if (pattern.endsWith("*")) {
4302
- return eventType.startsWith(pattern.slice(0, -1));
4303
- }
4224
+ if (pattern.endsWith("*")) return eventType.startsWith(pattern.slice(0, -1));
4304
4225
  return eventType === pattern;
4305
4226
  });
4306
- }, "matchesFilter");
4307
- var verifySignature = /* @__PURE__ */__name((secret, payload, signatureHeader) => {
4227
+ };
4228
+ var verifySignature = (secret, payload, signatureHeader) => {
4308
4229
  const expected = createHmac("sha256", secret).update(payload).digest("hex");
4309
4230
  const expectedBuffer = Buffer.from(expected, "utf8");
4310
4231
  const actualBuffer = Buffer.from(signatureHeader, "utf8");
4311
4232
  if (expectedBuffer.length !== actualBuffer.length) return false;
4312
4233
  return timingSafeEqual(expectedBuffer, actualBuffer);
4313
- }, "verifySignature");
4234
+ };
4314
4235
  program.command("listen").description("Start a local webhook listener for testing deliveries").option("--port <number>", "port to listen on", "8787").option("--path <path>", "request path to accept", "/webhook").option("--secret <secret>", "verify X-Soat-Signature with this webhook secret").option("--filter <pattern>", "filter event type(s), supports prefix wildcard and comma separation (e.g. sessions.generation.*,files.*)").option("--json", "print one JSON object per line").action(opts => {
4315
4236
  const port = Number(opts.port);
4316
- const path2 = opts.path;
4237
+ const path = opts.path;
4317
4238
  const secret = opts.secret;
4318
4239
  const filter = opts.filter;
4319
4240
  const asJson = Boolean(opts.json);
@@ -4322,7 +4243,7 @@ program.command("listen").description("Start a local webhook listener for testin
4322
4243
  process.exit(1);
4323
4244
  }
4324
4245
  const server = createServer((req, res) => {
4325
- if (req.method !== "POST" || req.url !== path2) {
4246
+ if (req.method !== "POST" || req.url !== path) {
4326
4247
  res.writeHead(404, {
4327
4248
  "Content-Type": "application/json"
4328
4249
  });
@@ -4355,9 +4276,7 @@ program.command("listen").description("Start a local webhook listener for testin
4355
4276
  parsedPayload = JSON.parse(rawBody);
4356
4277
  } catch {}
4357
4278
  let isSignatureValid = null;
4358
- if (secret) {
4359
- isSignatureValid = verifySignature(secret, rawBody, signature);
4360
- }
4279
+ if (secret) isSignatureValid = verifySignature(secret, rawBody, signature);
4361
4280
  const record = {
4362
4281
  timestamp: (/* @__PURE__ */new Date()).toISOString(),
4363
4282
  event_type: eventType,
@@ -4366,15 +4285,11 @@ program.command("listen").description("Start a local webhook listener for testin
4366
4285
  signature_valid: isSignatureValid,
4367
4286
  payload: parsedPayload
4368
4287
  };
4369
- if (asJson) {
4370
- console.log(JSON.stringify(record));
4371
- } else {
4288
+ if (asJson) console.log(JSON.stringify(record));else {
4372
4289
  console.log("--- webhook received ---");
4373
4290
  console.log("event_type:", eventType);
4374
4291
  console.log("delivery_id:", deliveryId);
4375
- if (secret) {
4376
- console.log("signature_valid:", isSignatureValid);
4377
- }
4292
+ if (secret) console.log("signature_valid:", isSignatureValid);
4378
4293
  console.log("payload:", JSON.stringify(parsedPayload, null, 2));
4379
4294
  }
4380
4295
  const responseStatus = secret && isSignatureValid === false ? 401 : 200;
@@ -4390,13 +4305,9 @@ program.command("listen").description("Start a local webhook listener for testin
4390
4305
  });
4391
4306
  });
4392
4307
  server.listen(port, () => {
4393
- console.log(`Listening for SOAT webhooks on http://localhost:${port}${path2}`);
4394
- if (filter) {
4395
- console.log(`Filter: ${filter}`);
4396
- }
4397
- if (secret) {
4398
- console.log("Signature verification: enabled");
4399
- }
4308
+ console.log(`Listening for SOAT webhooks on http://localhost:${port}${path}`);
4309
+ if (filter) console.log(`Filter: ${filter}`);
4310
+ if (secret) console.log("Signature verification: enabled");
4400
4311
  });
4401
4312
  process.on("SIGINT", () => {
4402
4313
  server.close(() => {
@@ -4425,13 +4336,9 @@ program.argument("[command]", "API command in kebab-case (e.g. list-actors)").ar
4425
4336
  in: "wrapper"
4426
4337
  };
4427
4338
  })];
4428
- console.log(`
4429
- Usage: soat ${commandName} [flags]
4430
- `);
4431
- console.log(` ${route.description}
4432
- `);
4433
- console.log(` Module docs: ${route.moduleDocsUrl}
4434
- `);
4339
+ console.log(`\nUsage: soat ${commandName} [flags]\n`);
4340
+ console.log(` ${route.description}\n`);
4341
+ console.log(` Module docs: ${route.moduleDocsUrl}\n`);
4435
4342
  if (allFlags.length > 0) {
4436
4343
  console.log("Flags:");
4437
4344
  for (const f of allFlags) {
@@ -4442,13 +4349,12 @@ Usage: soat ${commandName} [flags]
4442
4349
  }
4443
4350
  process.exit(0);
4444
4351
  }
4445
- const parsedFlags = parseUnknownWithRepeats({
4446
- cliArgs: rawArgs
4447
- });
4448
4352
  const wrapped = applyWrapperForCommand({
4449
4353
  commandName,
4450
4354
  route,
4451
- parsedFlags
4355
+ parsedFlags: parseUnknownWithRepeats({
4356
+ cliArgs: rawArgs
4357
+ })
4452
4358
  });
4453
4359
  const flags = wrapped.flags.single;
4454
4360
  const pathArgs = {};
@@ -4464,16 +4370,9 @@ Usage: soat ${commandName} [flags]
4464
4370
  const queryParam = route.queryParams.find(p => {
4465
4371
  return toCanonical(p) === canonical;
4466
4372
  });
4467
- if (pathParam) {
4468
- pathArgs[pathParam] = parsedValue;
4469
- } else if (queryParam) {
4470
- queryArgs[queryParam] = parsedValue;
4471
- } else {
4472
- bodyArgs[kebabToSnake(flagKey)] = parsedValue;
4473
- }
4373
+ if (pathParam) pathArgs[pathParam] = parsedValue;else if (queryParam) queryArgs[queryParam] = parsedValue;else bodyArgs[kebabToSnake(flagKey)] = parsedValue;
4474
4374
  }
4475
- const profileOpt = flags["profile"] ?? program.opts().profile;
4476
- const client = resolveClient(profileOpt);
4375
+ const client = resolveClient(flags["profile"] ?? program.opts().profile);
4477
4376
  const serviceClass = resolveServiceClass(route.serviceClass);
4478
4377
  if (!serviceClass) {
4479
4378
  console.error(`SDK class "${route.serviceClass}" not found.`);
@@ -4487,9 +4386,7 @@ Usage: soat ${commandName} [flags]
4487
4386
  const callOpts = {
4488
4387
  client
4489
4388
  };
4490
- if (Object.keys(wrapped.forcedBody).length) {
4491
- Object.assign(bodyArgs, wrapped.forcedBody);
4492
- }
4389
+ if (Object.keys(wrapped.forcedBody).length) Object.assign(bodyArgs, wrapped.forcedBody);
4493
4390
  if (Object.keys(pathArgs).length) callOpts["path"] = pathArgs;
4494
4391
  if (Object.keys(queryArgs).length) callOpts["query"] = queryArgs;
4495
4392
  if (Object.keys(bodyArgs).length) callOpts["body"] = bodyArgs;
@@ -4515,7 +4412,7 @@ Usage: soat ${commandName} [flags]
4515
4412
  }
4516
4413
  console.log(JSON.stringify(result.data, null, 2));
4517
4414
  });
4518
- var runCli = /* @__PURE__ */__name(async args => {
4415
+ var runCli = async args => {
4519
4416
  const previousArgv = process.argv;
4520
4417
  process.argv = args;
4521
4418
  try {
@@ -4523,12 +4420,11 @@ var runCli = /* @__PURE__ */__name(async args => {
4523
4420
  } finally {
4524
4421
  process.argv = previousArgv;
4525
4422
  }
4526
- }, "runCli");
4527
- var isMainModule = (() => {
4423
+ };
4424
+ if ((() => {
4528
4425
  if (!process.argv[1]) return false;
4529
- return nodePath.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
4530
- })();
4531
- if (isMainModule) {
4532
- void runCli(process.argv);
4533
- }
4426
+ return path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
4427
+ })()) runCli(process.argv);
4428
+
4429
+ //#endregion
4534
4430
  export { runCli };
package/package.json CHANGED
@@ -1,24 +1,24 @@
1
1
  {
2
2
  "name": "@soat/cli",
3
- "version": "0.6.5",
3
+ "version": "0.6.10",
4
4
  "type": "module",
5
5
  "dependencies": {
6
- "@inquirer/input": "^5.0.12",
7
- "@inquirer/password": "^5.0.12",
8
- "@ttoss/logger": "^0.8.10",
9
- "commander": "^14.0.3",
10
- "js-yaml": "^4.1.1",
11
- "@soat/sdk": "0.6.5"
6
+ "@inquirer/input": "^5.1.2",
7
+ "@inquirer/password": "^5.1.1",
8
+ "@ttoss/logger": "^0.8.16",
9
+ "commander": "^15.0.0",
10
+ "js-yaml": "^4.2.0",
11
+ "@soat/sdk": "0.6.10"
12
12
  },
13
13
  "devDependencies": {
14
- "@ttoss/config": "^1.37.10",
15
- "@ttoss/test-utils": "^4.2.10",
14
+ "@ttoss/config": "^1.37.15",
15
+ "@ttoss/test-utils": "^4.2.15",
16
16
  "@types/jest": "^30.0.0",
17
17
  "@types/js-yaml": "^4.0.9",
18
18
  "@types/node": "^24",
19
- "jest": "^30.3.0",
20
- "tsup": "^8.5.1",
21
- "tsx": "^4.21.0"
19
+ "jest": "^30.4.2",
20
+ "tsdown": "^0.22.1",
21
+ "tsx": "^4.22.4"
22
22
  },
23
23
  "files": [
24
24
  "dist",
@@ -40,6 +40,6 @@
40
40
  "lint": "eslint src tests",
41
41
  "test": "jest --config tests/unit/jest.config.ts --coverage=false",
42
42
  "typecheck": "tsc --noEmit && tsc --noEmit -p tests/tsconfig.json",
43
- "build": "pnpm generate && tsup"
43
+ "build": "pnpm generate && tsdown"
44
44
  }
45
45
  }