@seclai/cli 1.2.2 → 1.4.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
@@ -42,7 +42,7 @@ npx add-mcp https://api.seclai.com/mcp --header "X-API-Key: $SECLAI_API_KEY" --n
42
42
 
43
43
  ## Documentation
44
44
 
45
- Command reference (latest): https://seclai.github.io/seclai-cli/1.2.2/
45
+ Command reference (latest): https://seclai.github.io/seclai-cli/1.4.0/
46
46
 
47
47
  ## Authentication
48
48
 
@@ -162,6 +162,9 @@ seclai agents runs delete <runId>
162
162
  seclai agents runs cancel <runId>
163
163
  seclai agents runs search [--page N] [--limit N] [--json '...']
164
164
  seclai agents runs eval-results <agentId> <runId> [--page N] [--limit N]
165
+ # Download a file attachment emitted by a run step. attachmentId is the
166
+ # URL-safe-base64 storage_key from run output manifests / webhooks.
167
+ seclai agents runs download-attachment <runId> <attachmentId> [--download-name <name>] [--output <path>]
165
168
  ```
166
169
 
167
170
  #### Agent Definition
@@ -171,16 +174,27 @@ seclai agents def get <agentId>
171
174
  seclai agents def update <agentId> --json '{"steps":[...]}'
172
175
  ```
173
176
 
174
- #### Agent Export
177
+ #### Agent Export / Import
175
178
 
176
179
  ```bash
177
180
  seclai agents export <agentId>
178
181
  seclai agents export <agentId> --no-download
182
+
183
+ # Validate an agent_definition payload without creating an agent.
184
+ # Reports counts and any unresolved_refs you'll need to map with entity_remap
185
+ # when calling `agents create` or `agents update`.
186
+ # The body shape is `{ "agent_definition": <export payload> }`.
187
+ seclai agents preview-import --json-file ./preview-body.json
188
+ seclai agents export <agentId> \
189
+ | jq '{agent_definition: .}' \
190
+ | seclai agents preview-import --json-file -
179
191
  ```
180
192
 
181
193
  #### Agent Input Upload
182
194
 
183
195
  ```bash
196
+ # Discover which files (if any) the agent expects before staging uploads.
197
+ seclai agents attachment-references <agentId>
184
198
  seclai agents upload-input <agentId> --file ./data.csv [--file-name data.csv] [--mime-type text/csv]
185
199
  seclai agents input-status <agentId> <uploadId>
186
200
  ```
@@ -403,6 +417,16 @@ seclai models alerts unread-count
403
417
  seclai models recommendations <modelId>
404
418
  ```
405
419
 
420
+ #### Model Playground Experiments
421
+
422
+ ```bash
423
+ seclai models experiments list [--days N] [--start-date <date>] [--end-date <date>] [--limit N] [--offset N]
424
+ seclai models experiments create --json '{"model_ids":["gpt-4o"],"prompt":"Compare responses"}'
425
+ seclai models experiments get <experimentId>
426
+ seclai models experiments cancel <experimentId>
427
+ seclai models experiments delete <experimentId> # soft-delete, preserves audit history
428
+ ```
429
+
406
430
  ### Search
407
431
 
408
432
  ```bash
package/dist/cli.js CHANGED
@@ -21,6 +21,9 @@ function defaultRuntime() {
21
21
  writeOut: (text) => {
22
22
  process2.stdout.write(text);
23
23
  },
24
+ writeOutBytes: (bytes) => {
25
+ process2.stdout.write(bytes);
26
+ },
24
27
  writeErr: (text) => {
25
28
  process2.stderr.write(text);
26
29
  },
@@ -291,6 +294,37 @@ function register(program, rt) {
291
294
  printJson(rt, await client.searchAgentRuns(body));
292
295
  });
293
296
  });
297
+ runs.command("download-attachment").description(
298
+ "Download a file attachment emitted by a step in an agent run. The attachmentId is the URL-safe-base64 storage_key from run output manifests / webhooks."
299
+ ).argument("<runId>", "Run ID.").argument("<attachmentId>", "Attachment ID (storage_key).").option("--download-name <name>", "Filename hint for the download disposition.").option("--output <path>", "Write the attachment bytes to this file. If omitted, raw bytes are written to stdout.").action(async (runId, attachmentId, opts) => {
300
+ await run(rt, async () => {
301
+ const client = createClient(program.opts());
302
+ const res = await client.downloadAgentRunAttachment(
303
+ runId,
304
+ attachmentId,
305
+ opts.downloadName ? { downloadName: opts.downloadName } : {}
306
+ );
307
+ if (opts.output) {
308
+ const { createWriteStream } = await import("fs");
309
+ const { stat } = await import("fs/promises");
310
+ if (res.body) {
311
+ const { Readable } = await import("stream");
312
+ const { pipeline } = await import("stream/promises");
313
+ await pipeline(
314
+ Readable.fromWeb(res.body),
315
+ createWriteStream(opts.output)
316
+ );
317
+ } else {
318
+ const { writeFile: writeFile5 } = await import("fs/promises");
319
+ await writeFile5(opts.output, Buffer.from(await res.arrayBuffer()));
320
+ }
321
+ const { size } = await stat(opts.output);
322
+ printJson(rt, { saved: opts.output, bytes: size });
323
+ } else {
324
+ rt.writeOutBytes(new Uint8Array(await res.arrayBuffer()));
325
+ }
326
+ });
327
+ });
294
328
  const def = agents.command("def").description("Agent definition (step workflow).");
295
329
  def.command("get").description("Get agent definition.").argument("<agentId>", "Agent ID.").action(async (agentId) => {
296
330
  await run(rt, async () => {
@@ -311,6 +345,15 @@ function register(program, rt) {
311
345
  printJson(rt, await client.exportAgent(agentId, opts.download));
312
346
  });
313
347
  });
348
+ agents.command("preview-import").description(
349
+ "Validate an agent_definition payload without creating any agent. Reports step/schedule/alert/criteria/policy counts and any unresolved_refs (workflow refs to KBs, memory banks, source connections, or sub-agents that don't exist in this account)."
350
+ ).option("--json <json>", "Inline JSON body ({ agent_definition: ... }). Use '-' for stdin.").option("--json-file <path>", "JSON file path. Use '-' for stdin.").action(async (opts) => {
351
+ await run(rt, async () => {
352
+ const client = createClient(program.opts());
353
+ const body = await readJsonInput(rt, { json: opts.json, jsonFile: opts.jsonFile });
354
+ printJson(rt, await client.previewImportAgent(body));
355
+ });
356
+ });
314
357
  agents.command("upload-input").description("Upload a file as agent input.").argument("<agentId>", "Agent ID.").requiredOption("--file <path>", "File to upload.").option("--file-name <name>", "Override filename.").option("--mime-type <type>", "MIME type.").action(async (agentId, opts) => {
315
358
  await run(rt, async () => {
316
359
  const client = createClient(program.opts());
@@ -328,6 +371,14 @@ function register(program, rt) {
328
371
  printJson(rt, await client.getAgentInputUploadStatus(agentId, uploadId));
329
372
  });
330
373
  });
374
+ agents.command("attachment-references").description(
375
+ "Show which files (if any) an agent's templates expect on a run. Call before staging uploads: requires_uploads reports whether the agent accepts files, and the agent block lists the exact names / indexes / patterns a run-time batch must satisfy."
376
+ ).argument("<agentId>", "Agent ID.").action(async (agentId) => {
377
+ await run(rt, async () => {
378
+ const client = createClient(program.opts());
379
+ printJson(rt, await client.getAgentAttachmentReferences(agentId));
380
+ });
381
+ });
331
382
  const ai = agents.command("ai").description("Agent AI assistant.");
332
383
  withAiInputOptions(
333
384
  ai.command("gen-steps").description("Generate agent steps via AI.").argument("<agentId>", "Agent ID.")
@@ -1126,6 +1177,13 @@ function register10(program, rt) {
1126
1177
  printJson(rt, await client.cancelExperiment(experimentId));
1127
1178
  });
1128
1179
  });
1180
+ experiments.command("delete").description("Soft-delete a model playground experiment (preserves audit history).").argument("<experimentId>", "Experiment ID.").action(async (experimentId) => {
1181
+ await run(rt, async () => {
1182
+ const client = createClient(program.opts());
1183
+ await client.deleteExperiment(experimentId);
1184
+ printJson(rt, { ok: true });
1185
+ });
1186
+ });
1129
1187
  }
1130
1188
 
1131
1189
  // src/commands/search.ts
@@ -2002,10 +2060,10 @@ _seclai_completions() {
2002
2060
  case "\${COMP_WORDS[1]}" in
2003
2061
  agents)
2004
2062
  case "\${COMP_WORDS[2]}" in
2005
- runs) COMPREPLY=( $(compgen -W "list get delete cancel search eval-results" -- "$cur") ); return ;;
2063
+ runs) COMPREPLY=( $(compgen -W "list get delete cancel search eval-results download-attachment" -- "$cur") ); return ;;
2006
2064
  def) COMPREPLY=( $(compgen -W "get update" -- "$cur") ); return ;;
2007
2065
  ai) COMPREPLY=( $(compgen -W "gen-steps step-config history mark" -- "$cur") ); return ;;
2008
- *) COMPREPLY=( $(compgen -W "list create get update delete run runs def export upload-input input-status ai" -- "$cur") ); return ;;
2066
+ *) COMPREPLY=( $(compgen -W "list create get update delete run runs def export preview-import upload-input input-status attachment-references ai" -- "$cur") ); return ;;
2009
2067
  esac ;;
2010
2068
  sources|source)
2011
2069
  case "\${COMP_WORDS[2]}" in
@@ -2045,8 +2103,9 @@ _seclai_completions() {
2045
2103
  esac ;;
2046
2104
  models)
2047
2105
  case "\${COMP_WORDS[2]}" in
2048
- alerts) COMPREPLY=( $(compgen -W "list mark-read mark-all-read unread-count" -- "$cur") ); return ;;
2049
- *) COMPREPLY=( $(compgen -W "alerts recommendations" -- "$cur") ); return ;;
2106
+ alerts) COMPREPLY=( $(compgen -W "list mark-read mark-all-read unread-count" -- "$cur") ); return ;;
2107
+ experiments) COMPREPLY=( $(compgen -W "list create get cancel delete" -- "$cur") ); return ;;
2108
+ *) COMPREPLY=( $(compgen -W "alerts recommendations experiments" -- "$cur") ); return ;;
2050
2109
  esac ;;
2051
2110
  ai) COMPREPLY=( $(compgen -W "feedback kb source solution memory memory-history accept decline memory-accept" -- "$cur") ); return ;;
2052
2111
  skills) COMPREPLY=( $(compgen -W "install" -- "$cur") ); return ;;
@@ -2097,7 +2156,7 @@ _seclai() {
2097
2156
  args)
2098
2157
  case \${words[1]} in
2099
2158
  agents)
2100
- local -a sub=(list create get update delete run runs def export upload-input input-status ai)
2159
+ local -a sub=(list create get update delete run runs def export preview-import upload-input input-status attachment-references ai)
2101
2160
  _describe 'subcommand' sub ;;
2102
2161
  sources|source)
2103
2162
  local -a sub=(list create get update delete upload upload-text exports migration)
@@ -2124,7 +2183,7 @@ _seclai() {
2124
2183
  local -a sub=(list get status comment subscribe unsubscribe configs prefs)
2125
2184
  _describe 'subcommand' sub ;;
2126
2185
  models)
2127
- local -a sub=(alerts recommendations)
2186
+ local -a sub=(alerts recommendations experiments)
2128
2187
  _describe 'subcommand' sub ;;
2129
2188
  ai)
2130
2189
  local -a sub=(feedback kb source solution memory memory-history accept decline memory-accept)
@@ -2167,7 +2226,7 @@ complete -c seclai -n "not __fish_seen_subcommand_from $top" -f -a "mcp" -d "MCP
2167
2226
  complete -c seclai -n "not __fish_seen_subcommand_from $top" -f -a "completion" -d "Shell completions"
2168
2227
 
2169
2228
  # agents
2170
- complete -c seclai -n "__fish_seen_subcommand_from agents; and not __fish_seen_subcommand_from list create get update delete run runs def export upload-input input-status ai" -f -a "list create get update delete run runs def export upload-input input-status ai"
2229
+ complete -c seclai -n "__fish_seen_subcommand_from agents; and not __fish_seen_subcommand_from list create get update delete run runs def export preview-import upload-input input-status attachment-references ai" -f -a "list create get update delete run runs def export preview-import upload-input input-status attachment-references ai"
2171
2230
 
2172
2231
  # sources
2173
2232
  complete -c seclai -n "__fish_seen_subcommand_from sources; and not __fish_seen_subcommand_from list create get update delete upload upload-text exports migration" -f -a "list create get update delete upload upload-text exports migration"
@@ -2194,7 +2253,7 @@ complete -c seclai -n "__fish_seen_subcommand_from governance; and not __fish_se
2194
2253
  complete -c seclai -n "__fish_seen_subcommand_from alerts; and not __fish_seen_subcommand_from list get status comment subscribe unsubscribe configs prefs" -f -a "list get status comment subscribe unsubscribe configs prefs"
2195
2254
 
2196
2255
  # models
2197
- complete -c seclai -n "__fish_seen_subcommand_from models; and not __fish_seen_subcommand_from alerts recommendations" -f -a "alerts recommendations"
2256
+ complete -c seclai -n "__fish_seen_subcommand_from models; and not __fish_seen_subcommand_from alerts recommendations experiments" -f -a "alerts recommendations experiments"
2198
2257
 
2199
2258
  # ai
2200
2259
  complete -c seclai -n "__fish_seen_subcommand_from ai; and not __fish_seen_subcommand_from feedback kb source solution memory memory-history accept decline memory-accept" -f -a "feedback kb source solution memory memory-history accept decline memory-accept"