@seclai/cli 1.4.0 → 1.6.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/CHANGELOG.md +168 -0
- package/README.md +154 -6
- package/dist/cli.js +1088 -509
- package/package.json +7 -3
- package/dist/cli.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { Command } from "commander";
|
|
4
|
+
import { Command as Command4 } from "commander";
|
|
5
5
|
import { realpathSync } from "fs";
|
|
6
6
|
import { fileURLToPath, pathToFileURL } from "url";
|
|
7
7
|
|
|
8
8
|
// src/helpers.ts
|
|
9
|
+
import { InvalidArgumentError } from "commander";
|
|
9
10
|
import { readFile } from "fs/promises";
|
|
10
11
|
import { readFileSync } from "fs";
|
|
11
12
|
import process2 from "process";
|
|
@@ -74,10 +75,27 @@ function getCliVersion() {
|
|
|
74
75
|
}
|
|
75
76
|
function createClient(opts) {
|
|
76
77
|
const seclaiOpts = {};
|
|
78
|
+
const identityFlags = [
|
|
79
|
+
["apiKey", "--api-key", "Pass a key, or omit the flag to use SECLAI_API_KEY or SSO."],
|
|
80
|
+
["profile", "--profile", "Pass a profile name, or omit the flag to use the default profile."],
|
|
81
|
+
["accountId", "--account-id", "Pass an account ID, or omit the flag to use the default org."],
|
|
82
|
+
["configDir", "--config-dir", "Pass a directory, or omit the flag to use ~/.seclai."]
|
|
83
|
+
];
|
|
84
|
+
for (const [key, flag, hint] of identityFlags) {
|
|
85
|
+
const value = opts[key];
|
|
86
|
+
if (typeof value === "string" && value.trim().length === 0) {
|
|
87
|
+
throw new Error(`${flag} was given an empty value. ${hint}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
77
90
|
if (opts.apiKey !== void 0) seclaiOpts.apiKey = opts.apiKey;
|
|
78
91
|
if (opts.profile !== void 0) seclaiOpts.profile = opts.profile;
|
|
79
92
|
if (opts.configDir !== void 0) seclaiOpts.configDir = opts.configDir;
|
|
80
93
|
if (opts.accountId !== void 0) seclaiOpts.accountId = opts.accountId;
|
|
94
|
+
const envVersion = process2.env.SECLAI_API_VERSION;
|
|
95
|
+
const blank = (v) => v === void 0 || v.trim().length === 0;
|
|
96
|
+
const version = blank(opts.apiVersion) ? opts.apiVersion !== void 0 ? void 0 : blank(envVersion) ? void 0 : envVersion : opts.apiVersion;
|
|
97
|
+
if (version !== void 0) seclaiOpts.apiVersion = version;
|
|
98
|
+
if (opts.allowUnknownApiVersion) seclaiOpts.allowUnknownApiVersion = true;
|
|
81
99
|
const envUrl = process2.env.SECLAI_API_URL;
|
|
82
100
|
seclaiOpts.baseUrl = envUrl && envUrl.length > 0 ? envUrl : "https://api.seclai.com";
|
|
83
101
|
return new Seclai(seclaiOpts);
|
|
@@ -87,6 +105,11 @@ function printJson(rt, value) {
|
|
|
87
105
|
rt.writeOut(`${JSON.stringify(value, null, indent)}
|
|
88
106
|
`);
|
|
89
107
|
}
|
|
108
|
+
function warnDeprecated(rt, message, fate = "rejected") {
|
|
109
|
+
const suffix = fate === "kept" ? "" : fate === "removed" ? " This will be removed in a future release." : " This will be rejected in a future release.";
|
|
110
|
+
rt.writeErr(`warning: ${message}${suffix}
|
|
111
|
+
`);
|
|
112
|
+
}
|
|
90
113
|
function printError(rt, err) {
|
|
91
114
|
if (err instanceof SeclaiAPIValidationError) {
|
|
92
115
|
rt.writeErr(`${err.name}: ${err.message}
|
|
@@ -114,8 +137,13 @@ function printError(rt, err) {
|
|
|
114
137
|
if (err instanceof SeclaiConfigurationError) {
|
|
115
138
|
rt.writeErr(`${err.name}: ${err.message}
|
|
116
139
|
`);
|
|
117
|
-
|
|
140
|
+
if (/api version/i.test(err.message)) {
|
|
141
|
+
rt.writeErr(`hint: Pass --allow-unknown-api-version to send it anyway.
|
|
142
|
+
`);
|
|
143
|
+
} else {
|
|
144
|
+
rt.writeErr(`hint: Set the SECLAI_API_KEY environment variable or pass --api-key.
|
|
118
145
|
`);
|
|
146
|
+
}
|
|
119
147
|
return;
|
|
120
148
|
}
|
|
121
149
|
if (err instanceof Error) {
|
|
@@ -134,6 +162,37 @@ async function run(rt, main) {
|
|
|
134
162
|
rt.setExitCode(1);
|
|
135
163
|
}
|
|
136
164
|
}
|
|
165
|
+
function parseNumber(value) {
|
|
166
|
+
if (!/^\d+$/.test(value.trim())) {
|
|
167
|
+
throw new InvalidArgumentError("Expected a non-negative whole number.");
|
|
168
|
+
}
|
|
169
|
+
return Number(value.trim());
|
|
170
|
+
}
|
|
171
|
+
function withLimitOption(cmd, description = "Page size.") {
|
|
172
|
+
return cmd.option("--limit <n>", description, parseNumber);
|
|
173
|
+
}
|
|
174
|
+
function withOffsetListOptions(cmd) {
|
|
175
|
+
return withLimitOption(cmd).option("--offset <n>", "Number of items to skip.", parseNumber);
|
|
176
|
+
}
|
|
177
|
+
function toPagedEnvelope(res, ...legacyKeys) {
|
|
178
|
+
if (Array.isArray(res)) return { data: res };
|
|
179
|
+
if (res === null || typeof res !== "object") return res;
|
|
180
|
+
const obj = res;
|
|
181
|
+
if (Array.isArray(obj.data)) return obj;
|
|
182
|
+
for (const key of legacyKeys) {
|
|
183
|
+
if (Array.isArray(obj[key])) {
|
|
184
|
+
const { [key]: rows, ...rest } = obj;
|
|
185
|
+
return { data: rows, ...rest };
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return res;
|
|
189
|
+
}
|
|
190
|
+
function offsetListOpts(opts) {
|
|
191
|
+
const o = {};
|
|
192
|
+
if (opts.limit !== void 0) o.limit = opts.limit;
|
|
193
|
+
if (opts.offset !== void 0) o.offset = opts.offset;
|
|
194
|
+
return o;
|
|
195
|
+
}
|
|
137
196
|
function withJsonInputOptions(cmd) {
|
|
138
197
|
return cmd.option("--json <json>", "Inline JSON body. Use '-' to read from stdin.").option("--json-file <path>", "Path to JSON file. Use '-' to read from stdin.");
|
|
139
198
|
}
|
|
@@ -176,7 +235,7 @@ async function readAiInput(rt, opts) {
|
|
|
176
235
|
// src/commands/agents.ts
|
|
177
236
|
function register(program, rt) {
|
|
178
237
|
const agents = program.command("agents").description("Manage agents, runs, definitions, export, and AI assistance.");
|
|
179
|
-
agents.command("list").description("List agents.").option("--page <n>", "Page number.",
|
|
238
|
+
agents.command("list").description("List agents.").option("--page <n>", "Page number.", parseNumber).option("--limit <n>", "Page size.", parseNumber).action(async (opts) => {
|
|
180
239
|
await run(rt, async () => {
|
|
181
240
|
const client = createClient(program.opts());
|
|
182
241
|
printJson(rt, await client.listAgents(listOpts(opts)));
|
|
@@ -209,7 +268,40 @@ function register(program, rt) {
|
|
|
209
268
|
printJson(rt, { ok: true });
|
|
210
269
|
});
|
|
211
270
|
});
|
|
212
|
-
agents.command("
|
|
271
|
+
agents.command("disable").description("Pause an agent across every trigger path (API, schedule, email, sub-agent calls).").argument("<agentId>", "Agent ID.").action(async (agentId) => {
|
|
272
|
+
await run(rt, async () => {
|
|
273
|
+
const client = createClient(program.opts());
|
|
274
|
+
printJson(rt, await client.disableAgent(agentId));
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
agents.command("enable").description("Resume a paused agent.").argument("<agentId>", "Agent ID.").action(async (agentId) => {
|
|
278
|
+
await run(rt, async () => {
|
|
279
|
+
const client = createClient(program.opts());
|
|
280
|
+
printJson(rt, await client.enableAgent(agentId));
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
agents.command("callers").description("List the live agents that call this agent via a call_agent step.").argument("<agentId>", "Agent ID.").action(async (agentId) => {
|
|
284
|
+
await run(rt, async () => {
|
|
285
|
+
const client = createClient(program.opts());
|
|
286
|
+
printJson(rt, await client.getAgentCallers(agentId));
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
const triggers = agents.command("triggers").description("Agent trigger configuration.");
|
|
290
|
+
triggers.command("email-config").description("Set the alias, sender allowlist, and inbound-handling flags on an EMAIL_RECEIVED trigger.").argument("<agentId>", "Agent ID.").argument("<triggerId>", "Trigger ID.").option("--json <json>", "Config body JSON. Use '-' for stdin.").option("--json-file <path>", "Config body JSON file. Use '-' for stdin.").action(async (agentId, triggerId, opts) => {
|
|
291
|
+
await run(rt, async () => {
|
|
292
|
+
const client = createClient(program.opts());
|
|
293
|
+
const body = await readJsonInput(rt, { json: opts.json, jsonFile: opts.jsonFile });
|
|
294
|
+
printJson(
|
|
295
|
+
rt,
|
|
296
|
+
await client.setEmailTriggerConfig(
|
|
297
|
+
agentId,
|
|
298
|
+
triggerId,
|
|
299
|
+
body
|
|
300
|
+
)
|
|
301
|
+
);
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
agents.command("run").description("Run an agent. Use --stream/--events/--poll for different modes.").argument("<agentId>", "Agent ID.").option("--json <json>", "Inline JSON body. Use '-' for stdin.").option("--json-file <path>", "JSON file path. Use '-' for stdin.").option("--stream", "Stream and print final result when done.").option("--events", "Stream SSE events as newline-delimited JSON.").option("--event-filter <types>", "Comma-separated event types to show (with --events).").option("--output <mode>", "Output mode: 'full' prints entire event, 'data' prints only the data field, 'status' prints a one-line summary.", "full").option("--poll", "Poll until completion instead of streaming.").option("--poll-interval-ms <n>", "Poll interval in ms (with --poll).", parseNumber).option("--timeout-ms <n>", "Client-side timeout in ms.", parseNumber).option("--include-step-outputs", "Include step outputs (with --poll).").action(async (agentId, opts) => {
|
|
213
305
|
await run(rt, async () => {
|
|
214
306
|
const client = createClient(program.opts());
|
|
215
307
|
const body = await readJsonInput(rt, { json: opts.json, jsonFile: opts.jsonFile });
|
|
@@ -257,7 +349,7 @@ function register(program, rt) {
|
|
|
257
349
|
});
|
|
258
350
|
});
|
|
259
351
|
const runs = agents.command("runs").description("Manage agent runs.");
|
|
260
|
-
runs.command("list").description("List runs for an agent.").argument("<agentId>", "Agent ID.").option("--page <n>", "Page number.",
|
|
352
|
+
runs.command("list").description("List runs for an agent.").argument("<agentId>", "Agent ID.").option("--page <n>", "Page number.", parseNumber).option("--limit <n>", "Page size.", parseNumber).option("--status <status>", "Filter by run status (e.g. queued, running, completed, failed, cancelled).").action(async (agentId, opts) => {
|
|
261
353
|
await run(rt, async () => {
|
|
262
354
|
const client = createClient(program.opts());
|
|
263
355
|
const o = listOpts(opts);
|
|
@@ -274,10 +366,17 @@ function register(program, rt) {
|
|
|
274
366
|
);
|
|
275
367
|
});
|
|
276
368
|
});
|
|
277
|
-
runs.command("delete").description("
|
|
369
|
+
runs.command("delete").description("Deprecated alias for 'runs cancel'. The API has no delete-a-run operation.").argument("<runId>", "Run ID.").action(async (runId) => {
|
|
278
370
|
await run(rt, async () => {
|
|
371
|
+
warnDeprecated(
|
|
372
|
+
rt,
|
|
373
|
+
`'agents runs delete' is deprecated and cancels the run rather than deleting it \u2014 the API has no delete-a-run operation. Use 'agents runs cancel', which also prints the cancelled run instead of {"ok": true}.`,
|
|
374
|
+
// Kept, not scheduled for removal — the whole point of the alias is
|
|
375
|
+
// that existing scripts keep working.
|
|
376
|
+
"kept"
|
|
377
|
+
);
|
|
279
378
|
const client = createClient(program.opts());
|
|
280
|
-
await client.
|
|
379
|
+
await client.cancelAgentRun(runId);
|
|
281
380
|
printJson(rt, { ok: true });
|
|
282
381
|
});
|
|
283
382
|
});
|
|
@@ -398,10 +497,20 @@ function register(program, rt) {
|
|
|
398
497
|
printJson(rt, await client.generateStepConfig(agentId, body));
|
|
399
498
|
});
|
|
400
499
|
});
|
|
401
|
-
|
|
500
|
+
withOffsetListOptions(
|
|
501
|
+
ai.command("history").description("Get agent AI conversation history for one step type.").argument("<agentId>", "Agent ID.").requiredOption(
|
|
502
|
+
"--step-type <type>",
|
|
503
|
+
"Step type to read history for (e.g. llm). Required by the API."
|
|
504
|
+
).option("--step-id <id>", "Restrict to a single step.")
|
|
505
|
+
).action(async (agentId, opts) => {
|
|
402
506
|
await run(rt, async () => {
|
|
403
507
|
const client = createClient(program.opts());
|
|
404
|
-
|
|
508
|
+
const o = {
|
|
509
|
+
...offsetListOpts(opts),
|
|
510
|
+
stepType: opts.stepType
|
|
511
|
+
};
|
|
512
|
+
if (opts.stepId !== void 0) o.stepId = opts.stepId;
|
|
513
|
+
printJson(rt, await client.getAgentAiConversationHistory(agentId, o));
|
|
405
514
|
});
|
|
406
515
|
});
|
|
407
516
|
ai.command("mark").description("Mark an AI suggestion (accept/reject).").argument("<agentId>", "Agent ID.").argument("<conversationId>", "Conversation ID.").option("--json <json>", "Mark body JSON.").option("--json-file <path>", "Mark body JSON file.").action(async (agentId, conversationId, opts) => {
|
|
@@ -412,10 +521,14 @@ function register(program, rt) {
|
|
|
412
521
|
printJson(rt, { ok: true });
|
|
413
522
|
});
|
|
414
523
|
});
|
|
415
|
-
runs.command("eval-results").description("List evaluation results for a run.").argument("<agentId>", "Agent ID.").argument("<runId>", "Run ID.").option("--page <n>", "Page number.",
|
|
524
|
+
runs.command("eval-results").description("List evaluation results for a run.").argument("<agentId>", "Agent ID.").argument("<runId>", "Run ID.").option("--page <n>", "Page number.", parseNumber).option("--limit <n>", "Page size.", parseNumber).option(
|
|
525
|
+
"--paged",
|
|
526
|
+
"Wrap the results in {data: [...]}, the shape this endpoint moves to from --api-version 2026-07-27."
|
|
527
|
+
).action(async (agentId, runId, opts) => {
|
|
416
528
|
await run(rt, async () => {
|
|
417
529
|
const client = createClient(program.opts());
|
|
418
|
-
|
|
530
|
+
const res = await client.listRunEvaluationResults(agentId, runId, listOpts(opts));
|
|
531
|
+
printJson(rt, opts.paged ? toPagedEnvelope(res) : res);
|
|
419
532
|
});
|
|
420
533
|
});
|
|
421
534
|
}
|
|
@@ -423,7 +536,7 @@ function register(program, rt) {
|
|
|
423
536
|
// src/commands/sources.ts
|
|
424
537
|
function register2(program, rt) {
|
|
425
538
|
const sources = program.command("sources").alias("source").description("Manage content sources.");
|
|
426
|
-
sources.command("list").description("List sources.").option("--page <n>", "Page number.",
|
|
539
|
+
sources.command("list").description("List sources.").option("--page <n>", "Page number.", parseNumber).option("--limit <n>", "Page size.", parseNumber).option("--sort <field>", "Sort field.").option("--order <asc|desc>", "Sort direction.").option("--account-id <id>", "Filter by account ID.").action(async (opts) => {
|
|
427
540
|
await run(rt, async () => {
|
|
428
541
|
const globalOpts = program.opts();
|
|
429
542
|
const client = createClient(globalOpts);
|
|
@@ -476,7 +589,7 @@ function register2(program, rt) {
|
|
|
476
589
|
});
|
|
477
590
|
});
|
|
478
591
|
const exports_ = sources.command("exports").description("Manage source exports.");
|
|
479
|
-
exports_.command("list").description("List exports for a source.").argument("<sourceId>", "Source ID.").option("--page <n>", "Page number.",
|
|
592
|
+
exports_.command("list").description("List exports for a source.").argument("<sourceId>", "Source ID.").option("--page <n>", "Page number.", parseNumber).option("--limit <n>", "Page size.", parseNumber).action(async (sourceId, opts) => {
|
|
480
593
|
await run(rt, async () => {
|
|
481
594
|
const client = createClient(program.opts());
|
|
482
595
|
printJson(rt, await client.listSourceExports(sourceId, listOpts(opts)));
|
|
@@ -547,7 +660,7 @@ function register2(program, rt) {
|
|
|
547
660
|
// src/commands/contents.ts
|
|
548
661
|
function register3(program, rt) {
|
|
549
662
|
const contents = program.command("contents").description("Manage indexed content and embeddings.");
|
|
550
|
-
contents.command("get").description("Get content version details.").argument("<contentVersionId>", "Content version ID.").option("--start <n>", "Text start offset (0-based).",
|
|
663
|
+
contents.command("get").description("Get content version details.").argument("<contentVersionId>", "Content version ID.").option("--start <n>", "Text start offset (0-based).", parseNumber).option("--end <n>", "Text end offset (exclusive).", parseNumber).action(async (contentVersionId, opts) => {
|
|
551
664
|
await run(rt, async () => {
|
|
552
665
|
const client = createClient(program.opts());
|
|
553
666
|
const o = {};
|
|
@@ -578,7 +691,7 @@ function register3(program, rt) {
|
|
|
578
691
|
printJson(rt, await client.replaceContentWithInlineText(contentVersionId, body));
|
|
579
692
|
});
|
|
580
693
|
});
|
|
581
|
-
contents.command("embeddings").description("List embeddings for a content version.").argument("<contentVersionId>", "Content version ID.").option("--page <n>", "Page number.",
|
|
694
|
+
contents.command("embeddings").description("List embeddings for a content version.").argument("<contentVersionId>", "Content version ID.").option("--page <n>", "Page number.", parseNumber).option("--limit <n>", "Page size.", parseNumber).action(async (contentVersionId, opts) => {
|
|
582
695
|
await run(rt, async () => {
|
|
583
696
|
const client = createClient(program.opts());
|
|
584
697
|
printJson(rt, await client.listContentEmbeddings(contentVersionId, listOpts(opts)));
|
|
@@ -589,7 +702,7 @@ function register3(program, rt) {
|
|
|
589
702
|
// src/commands/kb.ts
|
|
590
703
|
function register4(program, rt) {
|
|
591
704
|
const kb = program.command("kb").description("Manage knowledge bases.");
|
|
592
|
-
kb.command("list").description("List knowledge bases.").option("--page <n>", "Page number.",
|
|
705
|
+
kb.command("list").description("List knowledge bases.").option("--page <n>", "Page number.", parseNumber).option("--limit <n>", "Page size.", parseNumber).option("--sort <field>", "Sort field.").option("--order <asc|desc>", "Sort direction.").action(async (opts) => {
|
|
593
706
|
await run(rt, async () => {
|
|
594
707
|
const client = createClient(program.opts());
|
|
595
708
|
printJson(rt, await client.listKnowledgeBases(listOpts(opts)));
|
|
@@ -627,7 +740,7 @@ function register4(program, rt) {
|
|
|
627
740
|
// src/commands/memory.ts
|
|
628
741
|
function register5(program, rt) {
|
|
629
742
|
const memory = program.command("memory").description("Manage memory banks.");
|
|
630
|
-
memory.command("list").description("List memory banks.").option("--page <n>", "Page number.",
|
|
743
|
+
memory.command("list").description("List memory banks.").option("--page <n>", "Page number.", parseNumber).option("--limit <n>", "Page size.", parseNumber).option("--sort <field>", "Sort field.").option("--order <asc|desc>", "Sort direction.").action(async (opts) => {
|
|
631
744
|
await run(rt, async () => {
|
|
632
745
|
const client = createClient(program.opts());
|
|
633
746
|
printJson(rt, await client.listMemoryBanks(listOpts(opts)));
|
|
@@ -735,10 +848,16 @@ function register5(program, rt) {
|
|
|
735
848
|
function register6(program, rt) {
|
|
736
849
|
const evals = program.command("evals").description("Manage evaluations.");
|
|
737
850
|
const criteria = evals.command("criteria").description("Evaluation criteria.");
|
|
738
|
-
criteria.command("list").description("List evaluation criteria for an agent.").argument("<agentId>", "Agent ID.").option("--page <n>", "Page number.",
|
|
851
|
+
criteria.command("list").description("List evaluation criteria for an agent.").argument("<agentId>", "Agent ID.").option("--page <n>", "Page number.", parseNumber).option("--limit <n>", "Page size.", parseNumber).option(
|
|
852
|
+
"--paged",
|
|
853
|
+
"Wrap the results in {data: [...]} instead of returning a bare array. The pagination block is included once the API sends one, from --api-version 2026-07-27."
|
|
854
|
+
).action(async (agentId, opts) => {
|
|
739
855
|
await run(rt, async () => {
|
|
740
856
|
const client = createClient(program.opts());
|
|
741
|
-
printJson(
|
|
857
|
+
printJson(
|
|
858
|
+
rt,
|
|
859
|
+
opts.paged ? await client.listEvaluationCriteriaPage(agentId, listOpts(opts)) : await client.listEvaluationCriteria(agentId, listOpts(opts))
|
|
860
|
+
);
|
|
742
861
|
});
|
|
743
862
|
});
|
|
744
863
|
criteria.command("create").description("Create evaluation criteria.").argument("<agentId>", "Agent ID.").option("--json <json>", "Criteria body JSON.").option("--json-file <path>", "Criteria body JSON file.").action(async (agentId, opts) => {
|
|
@@ -775,7 +894,7 @@ function register6(program, rt) {
|
|
|
775
894
|
});
|
|
776
895
|
});
|
|
777
896
|
const results = evals.command("results").description("Evaluation results.");
|
|
778
|
-
results.command("list").description("List results for criteria.").argument("<criteriaId>", "Criteria ID.").option("--page <n>", "Page number.",
|
|
897
|
+
results.command("list").description("List results for criteria.").argument("<criteriaId>", "Criteria ID.").option("--page <n>", "Page number.", parseNumber).option("--limit <n>", "Page size.", parseNumber).action(async (criteriaId, opts) => {
|
|
779
898
|
await run(rt, async () => {
|
|
780
899
|
const client = createClient(program.opts());
|
|
781
900
|
printJson(rt, await client.listEvaluationResults(criteriaId, listOpts(opts)));
|
|
@@ -788,7 +907,7 @@ function register6(program, rt) {
|
|
|
788
907
|
printJson(rt, await client.createEvaluationResult(criteriaId, body));
|
|
789
908
|
});
|
|
790
909
|
});
|
|
791
|
-
evals.command("compatible-runs").description("List runs compatible with criteria.").argument("<criteriaId>", "Criteria ID.").option("--page <n>", "Page number.",
|
|
910
|
+
evals.command("compatible-runs").description("List runs compatible with criteria.").argument("<criteriaId>", "Criteria ID.").option("--page <n>", "Page number.", parseNumber).option("--limit <n>", "Page size.", parseNumber).action(async (criteriaId, opts) => {
|
|
792
911
|
await run(rt, async () => {
|
|
793
912
|
const client = createClient(program.opts());
|
|
794
913
|
printJson(rt, await client.listCompatibleRuns(criteriaId, listOpts(opts)));
|
|
@@ -801,13 +920,13 @@ function register6(program, rt) {
|
|
|
801
920
|
printJson(rt, await client.testDraftEvaluation(agentId, body));
|
|
802
921
|
});
|
|
803
922
|
});
|
|
804
|
-
evals.command("agent-results").description("List all evaluation results for an agent.").argument("<agentId>", "Agent ID.").option("--page <n>", "Page number.",
|
|
923
|
+
evals.command("agent-results").description("List all evaluation results for an agent.").argument("<agentId>", "Agent ID.").option("--page <n>", "Page number.", parseNumber).option("--limit <n>", "Page size.", parseNumber).action(async (agentId, opts) => {
|
|
805
924
|
await run(rt, async () => {
|
|
806
925
|
const client = createClient(program.opts());
|
|
807
926
|
printJson(rt, await client.listAgentEvaluationResults(agentId, listOpts(opts)));
|
|
808
927
|
});
|
|
809
928
|
});
|
|
810
|
-
evals.command("agent-runs").description("List evaluation run summaries for an agent.").argument("<agentId>", "Agent ID.").option("--page <n>", "Page number.",
|
|
929
|
+
evals.command("agent-runs").description("List evaluation run summaries for an agent.").argument("<agentId>", "Agent ID.").option("--page <n>", "Page number.", parseNumber).option("--limit <n>", "Page size.", parseNumber).action(async (agentId, opts) => {
|
|
811
930
|
await run(rt, async () => {
|
|
812
931
|
const client = createClient(program.opts());
|
|
813
932
|
printJson(rt, await client.listEvaluationRuns(agentId, listOpts(opts)));
|
|
@@ -824,7 +943,7 @@ function register6(program, rt) {
|
|
|
824
943
|
// src/commands/solutions.ts
|
|
825
944
|
function register7(program, rt) {
|
|
826
945
|
const solutions = program.command("solutions").description("Manage solutions.");
|
|
827
|
-
solutions.command("list").description("List solutions.").option("--page <n>", "Page number.",
|
|
946
|
+
solutions.command("list").description("List solutions.").option("--page <n>", "Page number.", parseNumber).option("--limit <n>", "Page size.", parseNumber).option("--sort <field>", "Sort field.").option("--order <asc|desc>", "Sort direction.").action(async (opts) => {
|
|
828
947
|
await run(rt, async () => {
|
|
829
948
|
const client = createClient(program.opts());
|
|
830
949
|
printJson(rt, await client.listSolutions(listOpts(opts)));
|
|
@@ -1002,12 +1121,11 @@ function register8(program, rt) {
|
|
|
1002
1121
|
// src/commands/alerts.ts
|
|
1003
1122
|
function register9(program, rt) {
|
|
1004
1123
|
const alerts = program.command("alerts").description("Manage alerts and alert configurations.");
|
|
1005
|
-
alerts.command("list").description("List alerts.").option("--page <n>", "Page number.",
|
|
1124
|
+
alerts.command("list").description("List alerts.").option("--page <n>", "Page number.", parseNumber).option("--limit <n>", "Page size.", parseNumber).option("--status <status>", "Filter by status.").action(async (opts) => {
|
|
1006
1125
|
await run(rt, async () => {
|
|
1007
1126
|
const client = createClient(program.opts());
|
|
1008
1127
|
const o = listOpts(opts);
|
|
1009
1128
|
if (opts.status) o.status = opts.status;
|
|
1010
|
-
if (opts.severity) o.severity = opts.severity;
|
|
1011
1129
|
printJson(rt, await client.listAlerts(o));
|
|
1012
1130
|
});
|
|
1013
1131
|
});
|
|
@@ -1044,10 +1162,14 @@ function register9(program, rt) {
|
|
|
1044
1162
|
});
|
|
1045
1163
|
});
|
|
1046
1164
|
const configs = alerts.command("configs").description("Alert configurations.");
|
|
1047
|
-
configs.command("list").description("List alert configurations.").option("--page <n>", "Page number.",
|
|
1165
|
+
configs.command("list").description("List alert configurations.").option("--page <n>", "Page number.", parseNumber).option("--limit <n>", "Page size.", parseNumber).option(
|
|
1166
|
+
"--paged",
|
|
1167
|
+
"Wrap the results in {data: [...]}, the shape this endpoint moves to from --api-version 2026-07-27."
|
|
1168
|
+
).action(async (opts) => {
|
|
1048
1169
|
await run(rt, async () => {
|
|
1049
1170
|
const client = createClient(program.opts());
|
|
1050
|
-
|
|
1171
|
+
const res = await client.listAlertConfigs(listOpts(opts));
|
|
1172
|
+
printJson(rt, opts.paged ? toPagedEnvelope(res, "configs") : res);
|
|
1051
1173
|
});
|
|
1052
1174
|
});
|
|
1053
1175
|
configs.command("create").description("Create an alert configuration.").option("--json <json>", "Config body JSON.").option("--json-file <path>", "Config body JSON file.").action(async (opts) => {
|
|
@@ -1093,19 +1215,220 @@ function register9(program, rt) {
|
|
|
1093
1215
|
});
|
|
1094
1216
|
}
|
|
1095
1217
|
|
|
1096
|
-
// src/commands/
|
|
1218
|
+
// src/commands/email.ts
|
|
1219
|
+
import { Argument, Option } from "commander";
|
|
1097
1220
|
function register10(program, rt) {
|
|
1221
|
+
const email = program.command("email").description("Agent email: sending domains, inbound blocklist, inbound health, and opt-outs.");
|
|
1222
|
+
const domains = email.command("domains").description("Agent-email sending domains.");
|
|
1223
|
+
domains.command("list").description("List the account's email domains and the plan limits for adding more.").action(async () => {
|
|
1224
|
+
await run(rt, async () => {
|
|
1225
|
+
const client = createClient(program.opts());
|
|
1226
|
+
printJson(rt, await client.listEmailDomains());
|
|
1227
|
+
});
|
|
1228
|
+
});
|
|
1229
|
+
domains.command("add").description("Add and provision a new agent-email domain. Returns the DNS records to publish.").addOption(
|
|
1230
|
+
new Option("--kind <kind>", "'vanity' (a subdomain of seclai.com) or 'custom' (your own domain).").choices(["vanity", "custom"]).makeOptionMandatory()
|
|
1231
|
+
).requiredOption("--value <domain>", "The domain to add.").option("--delegated", "The domain's DNS is delegated to Seclai, so records are published automatically.").action(async (opts) => {
|
|
1232
|
+
await run(rt, async () => {
|
|
1233
|
+
const client = createClient(program.opts());
|
|
1234
|
+
const body = {
|
|
1235
|
+
kind: opts.kind,
|
|
1236
|
+
value: opts.value
|
|
1237
|
+
};
|
|
1238
|
+
if (opts.delegated) body.delegated = true;
|
|
1239
|
+
printJson(rt, await client.addEmailDomain(body));
|
|
1240
|
+
});
|
|
1241
|
+
});
|
|
1242
|
+
domains.command("remove").description("Remove a domain and tear down its sending identity and inbound routing.").argument("<domainId>", "Domain ID.").action(async (domainId) => {
|
|
1243
|
+
await run(rt, async () => {
|
|
1244
|
+
const client = createClient(program.opts());
|
|
1245
|
+
printJson(rt, await client.removeEmailDomain(domainId));
|
|
1246
|
+
});
|
|
1247
|
+
});
|
|
1248
|
+
domains.command("verify").description("Run a verification check on a domain immediately.").argument("<domainId>", "Domain ID.").action(async (domainId) => {
|
|
1249
|
+
await run(rt, async () => {
|
|
1250
|
+
const client = createClient(program.opts());
|
|
1251
|
+
printJson(rt, await client.verifyEmailDomain(domainId));
|
|
1252
|
+
});
|
|
1253
|
+
});
|
|
1254
|
+
domains.command("set-primary").description("Promote a verified domain to the account's primary sending domain.").argument("<domainId>", "Domain ID.").action(async (domainId) => {
|
|
1255
|
+
await run(rt, async () => {
|
|
1256
|
+
const client = createClient(program.opts());
|
|
1257
|
+
printJson(rt, await client.setPrimaryEmailDomain(domainId));
|
|
1258
|
+
});
|
|
1259
|
+
});
|
|
1260
|
+
domains.command("use-shared").description("Revert to the shared agent.seclai.com sending and inbound domain.").action(async () => {
|
|
1261
|
+
await run(rt, async () => {
|
|
1262
|
+
const client = createClient(program.opts());
|
|
1263
|
+
await client.useSharedEmailDomain();
|
|
1264
|
+
printJson(rt, { ok: true });
|
|
1265
|
+
});
|
|
1266
|
+
});
|
|
1267
|
+
domains.command("test-email").description("Send a test message from a verified domain to the account owner.").argument("<domainId>", "Domain ID.").action(async (domainId) => {
|
|
1268
|
+
await run(rt, async () => {
|
|
1269
|
+
const client = createClient(program.opts());
|
|
1270
|
+
printJson(rt, await client.sendEmailDomainTestEmail(domainId));
|
|
1271
|
+
});
|
|
1272
|
+
});
|
|
1273
|
+
domains.command("dmarc").description("Get the DMARC aggregate-report summary for a domain.").argument("<domainId>", "Domain ID.").option("--days <n>", "Reporting window in days.", parseNumber).option("--top-sources <n>", "How many sending sources to include.", parseNumber).action(async (domainId, opts) => {
|
|
1274
|
+
await run(rt, async () => {
|
|
1275
|
+
const client = createClient(program.opts());
|
|
1276
|
+
const o = {};
|
|
1277
|
+
if (opts.days !== void 0) o.days = opts.days;
|
|
1278
|
+
if (opts.topSources !== void 0) o.topSources = opts.topSources;
|
|
1279
|
+
printJson(rt, await client.getDmarcSummary(domainId, o));
|
|
1280
|
+
});
|
|
1281
|
+
});
|
|
1282
|
+
const blocked = email.command("blocked").description("Inbound email sender blocklist.");
|
|
1283
|
+
withOffsetListOptions(
|
|
1284
|
+
blocked.command("list").description("List blocked inbound senders (newest first) and the account's auto-block mode.")
|
|
1285
|
+
).action(async (opts) => {
|
|
1286
|
+
await run(rt, async () => {
|
|
1287
|
+
const client = createClient(program.opts());
|
|
1288
|
+
printJson(rt, await client.listBlockedEmailSenders(offsetListOpts(opts)));
|
|
1289
|
+
});
|
|
1290
|
+
});
|
|
1291
|
+
blocked.command("add").description("Block an inbound sender address or domain.").requiredOption("--sender-email <email>", "Sender address, or the domain when --match-type is 'domain'.").addOption(
|
|
1292
|
+
new Option("--match-type <type>", "'address' or 'domain'.").choices(["address", "domain"]).default("address")
|
|
1293
|
+
).option("--note <text>", "Why the sender was blocked.").action(async (opts) => {
|
|
1294
|
+
await run(rt, async () => {
|
|
1295
|
+
const client = createClient(program.opts());
|
|
1296
|
+
const body = {
|
|
1297
|
+
sender_email: opts.senderEmail,
|
|
1298
|
+
match_type: opts.matchType
|
|
1299
|
+
};
|
|
1300
|
+
if (opts.note !== void 0) body.note = opts.note;
|
|
1301
|
+
printJson(rt, await client.blockEmailSender(body));
|
|
1302
|
+
});
|
|
1303
|
+
});
|
|
1304
|
+
blocked.command("remove").description("Unblock a sender.").argument("<blockedId>", "Blocklist entry ID.").action(async (blockedId) => {
|
|
1305
|
+
await run(rt, async () => {
|
|
1306
|
+
const client = createClient(program.opts());
|
|
1307
|
+
await client.unblockEmailSender(blockedId);
|
|
1308
|
+
printJson(rt, { ok: true });
|
|
1309
|
+
});
|
|
1310
|
+
});
|
|
1311
|
+
blocked.command("auto-block-mode").description("Set whether a governance BLOCK on an authenticated sender auto-adds them to the blocklist.").addArgument(
|
|
1312
|
+
// A closed set the API will only ever 422 on. Failing the parse names the
|
|
1313
|
+
// accepted values; the server names nothing.
|
|
1314
|
+
new Argument("<mode>", "How governance BLOCK verdicts feed the blocklist.").choices([
|
|
1315
|
+
"disabled",
|
|
1316
|
+
"input",
|
|
1317
|
+
"input_and_output"
|
|
1318
|
+
])
|
|
1319
|
+
).action(async (mode) => {
|
|
1320
|
+
await run(rt, async () => {
|
|
1321
|
+
const client = createClient(program.opts());
|
|
1322
|
+
printJson(rt, await client.setAutoBlockMode({ mode }));
|
|
1323
|
+
});
|
|
1324
|
+
});
|
|
1325
|
+
const inbound = email.command("inbound").description("Inbound email health and queue control.");
|
|
1326
|
+
inbound.command("status").description("Get inbound-email quota usage, pause state, and queued-run counts.").action(async () => {
|
|
1327
|
+
await run(rt, async () => {
|
|
1328
|
+
const client = createClient(program.opts());
|
|
1329
|
+
printJson(rt, await client.getInboundEmailStatus());
|
|
1330
|
+
});
|
|
1331
|
+
});
|
|
1332
|
+
inbound.command("rejections").description("List recently rejected inbound emails and why they were rejected.").option("--agent-id <id>", "Restrict to one agent.").option("--limit <n>", "Maximum rejections to return.", parseNumber).action(async (opts) => {
|
|
1333
|
+
await run(rt, async () => {
|
|
1334
|
+
const client = createClient(program.opts());
|
|
1335
|
+
const o = {};
|
|
1336
|
+
if (opts.agentId !== void 0) o.agentId = opts.agentId;
|
|
1337
|
+
if (opts.limit !== void 0) o.limit = opts.limit;
|
|
1338
|
+
printJson(rt, await client.listInboundEmailRejections(o));
|
|
1339
|
+
});
|
|
1340
|
+
});
|
|
1341
|
+
inbound.command("cancel-queued").description("Fail all of the account's queued (over-quota) inbound-email runs at once.").action(async () => {
|
|
1342
|
+
await run(rt, async () => {
|
|
1343
|
+
const client = createClient(program.opts());
|
|
1344
|
+
printJson(rt, await client.cancelQueuedEmailRuns());
|
|
1345
|
+
});
|
|
1346
|
+
});
|
|
1347
|
+
inbound.command("resume").description("Manually lift the account-wide inbound-email pause.").action(async () => {
|
|
1348
|
+
await run(rt, async () => {
|
|
1349
|
+
const client = createClient(program.opts());
|
|
1350
|
+
printJson(rt, await client.resumeInboundEmail());
|
|
1351
|
+
});
|
|
1352
|
+
});
|
|
1353
|
+
const optouts = email.command("optouts").description("Recipients who opted out of agent email.");
|
|
1354
|
+
withOffsetListOptions(
|
|
1355
|
+
optouts.command("list").description("List agent-email opt-outs.").option("--agent-id <id>", "Restrict to one agent.")
|
|
1356
|
+
).action(async (opts) => {
|
|
1357
|
+
await run(rt, async () => {
|
|
1358
|
+
const client = createClient(program.opts());
|
|
1359
|
+
const o = offsetListOpts(opts);
|
|
1360
|
+
if (opts.agentId !== void 0) o.agentId = opts.agentId;
|
|
1361
|
+
printJson(rt, await client.listAgentEmailOptOuts(o));
|
|
1362
|
+
});
|
|
1363
|
+
});
|
|
1364
|
+
optouts.command("remove").description("Remove an opt-out so the recipient can receive agent email again.").argument("<optoutId>", "Opt-out ID.").action(async (optoutId) => {
|
|
1365
|
+
await run(rt, async () => {
|
|
1366
|
+
const client = createClient(program.opts());
|
|
1367
|
+
await client.removeAgentEmailOptOut(optoutId);
|
|
1368
|
+
printJson(rt, { ok: true });
|
|
1369
|
+
});
|
|
1370
|
+
});
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
// src/commands/account.ts
|
|
1374
|
+
import { SeclaiApiVersion } from "@seclai/sdk";
|
|
1375
|
+
function register11(program, rt) {
|
|
1376
|
+
program.command("me").description("Show the authenticated user's account ID and organization memberships.").action(async () => {
|
|
1377
|
+
await run(rt, async () => {
|
|
1378
|
+
const client = createClient(program.opts());
|
|
1379
|
+
printJson(rt, await client.getMe());
|
|
1380
|
+
});
|
|
1381
|
+
});
|
|
1382
|
+
const version = program.command("api-version").description("Read or pin the account's dated API version.");
|
|
1383
|
+
version.command("get").description(
|
|
1384
|
+
"Show the version a request resolves to. Reflects --api-version when passed, otherwise the account pin, otherwise the default."
|
|
1385
|
+
).action(async () => {
|
|
1386
|
+
await run(rt, async () => {
|
|
1387
|
+
const client = createClient(program.opts());
|
|
1388
|
+
printJson(rt, await client.getApiVersion());
|
|
1389
|
+
});
|
|
1390
|
+
});
|
|
1391
|
+
version.command("set").description("Pin the account to a dated API version. Affects every client, not just this CLI.").argument("<date>", "API version as YYYY-MM-DD.").action(async (date) => {
|
|
1392
|
+
await run(rt, async () => {
|
|
1393
|
+
const known = Object.values(SeclaiApiVersion);
|
|
1394
|
+
if (!known.includes(date) && !program.opts().allowUnknownApiVersion) {
|
|
1395
|
+
throw new Error(
|
|
1396
|
+
`Unknown API version "${date}". This release knows ${[...new Set(known)].sort().join(", ")}. Pass --allow-unknown-api-version to pin it anyway.`
|
|
1397
|
+
);
|
|
1398
|
+
}
|
|
1399
|
+
const client = createClient(program.opts());
|
|
1400
|
+
printJson(rt, await client.updateApiVersion(date));
|
|
1401
|
+
});
|
|
1402
|
+
});
|
|
1403
|
+
version.command("clear").description("Remove the account's version pin, reverting to the default version.").action(async () => {
|
|
1404
|
+
await run(rt, async () => {
|
|
1405
|
+
const client = createClient(program.opts());
|
|
1406
|
+
printJson(rt, await client.updateApiVersion(null));
|
|
1407
|
+
});
|
|
1408
|
+
});
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
// src/commands/models.ts
|
|
1412
|
+
function register12(program, rt) {
|
|
1098
1413
|
const models = program.command("models").description("Models, model alerts, recommendations, and playground experiments.");
|
|
1099
|
-
models.command("list").description("List models grouped by provider.").option("--provider <provider>", "Filter by provider name.").option("--supports-tool-use", "Only models that support tool use.").option("--supports-thinking", "Only models that support thinking.").action(async (opts) => {
|
|
1414
|
+
models.command("list").description("List models grouped by provider.").option("--provider <provider>", "Filter by provider name.").option("--supports-tool-use", "Only models that support tool use.").option("--supports-thinking", "Only models that support thinking.").option("--supports-input-media <media>", "Only models accepting this input modality (e.g. image, audio).").option("--supports-output-media <media>", "Only models producing this output modality (e.g. image, video).").action(async (opts) => {
|
|
1100
1415
|
await run(rt, async () => {
|
|
1101
1416
|
const client = createClient(program.opts());
|
|
1102
1417
|
const o = {};
|
|
1103
1418
|
if (opts.provider !== void 0) o.provider = opts.provider;
|
|
1104
1419
|
if (opts.supportsToolUse !== void 0) o.supportsToolUse = opts.supportsToolUse;
|
|
1105
1420
|
if (opts.supportsThinking !== void 0) o.supportsThinking = opts.supportsThinking;
|
|
1421
|
+
if (opts.supportsInputMedia !== void 0) o.supportsInputMedia = opts.supportsInputMedia;
|
|
1422
|
+
if (opts.supportsOutputMedia !== void 0) o.supportsOutputMedia = opts.supportsOutputMedia;
|
|
1106
1423
|
printJson(rt, await client.listModels(o));
|
|
1107
1424
|
});
|
|
1108
1425
|
});
|
|
1426
|
+
models.command("tiers").description("Show each media-generation modality and tier with its model and cost.").action(async () => {
|
|
1427
|
+
await run(rt, async () => {
|
|
1428
|
+
const client = createClient(program.opts());
|
|
1429
|
+
printJson(rt, await client.getGenerationTiers());
|
|
1430
|
+
});
|
|
1431
|
+
});
|
|
1109
1432
|
models.command("get").description("Get full details for a specific model.").argument("<modelId>", "Model ID.").action(async (modelId) => {
|
|
1110
1433
|
await run(rt, async () => {
|
|
1111
1434
|
const client = createClient(program.opts());
|
|
@@ -1113,10 +1436,14 @@ function register10(program, rt) {
|
|
|
1113
1436
|
});
|
|
1114
1437
|
});
|
|
1115
1438
|
const alerts = models.command("alerts").description("Model alerts.");
|
|
1116
|
-
alerts.command("list").description("List model alerts.").option("--page <n>", "Page number.",
|
|
1439
|
+
alerts.command("list").description("List model alerts.").option("--page <n>", "Page number.", parseNumber).option("--limit <n>", "Page size.", parseNumber).option(
|
|
1440
|
+
"--paged",
|
|
1441
|
+
"Wrap the results in {data: [...]}, the shape this endpoint moves to from --api-version 2026-07-27."
|
|
1442
|
+
).action(async (opts) => {
|
|
1117
1443
|
await run(rt, async () => {
|
|
1118
1444
|
const client = createClient(program.opts());
|
|
1119
|
-
|
|
1445
|
+
const res = await client.listModelAlerts(listOpts(opts));
|
|
1446
|
+
printJson(rt, opts.paged ? toPagedEnvelope(res, "alerts") : res);
|
|
1120
1447
|
});
|
|
1121
1448
|
});
|
|
1122
1449
|
alerts.command("mark-read").description("Mark a model alert as read.").argument("<alertId>", "Alert ID.").action(async (alertId) => {
|
|
@@ -1146,15 +1473,15 @@ function register10(program, rt) {
|
|
|
1146
1473
|
});
|
|
1147
1474
|
});
|
|
1148
1475
|
const experiments = models.command("experiments").description("Model playground experiments.");
|
|
1149
|
-
|
|
1476
|
+
withOffsetListOptions(
|
|
1477
|
+
experiments.command("list").description("List model playground experiments.").option("--days <n>", "Filter to last N days.", parseNumber).option("--start-date <date>", "Start date (ISO 8601).").option("--end-date <date>", "End date (ISO 8601).")
|
|
1478
|
+
).action(async (opts) => {
|
|
1150
1479
|
await run(rt, async () => {
|
|
1151
1480
|
const client = createClient(program.opts());
|
|
1152
|
-
const o =
|
|
1481
|
+
const o = offsetListOpts(opts);
|
|
1153
1482
|
if (opts.days !== void 0) o.days = opts.days;
|
|
1154
1483
|
if (opts.startDate !== void 0) o.startDate = opts.startDate;
|
|
1155
1484
|
if (opts.endDate !== void 0) o.endDate = opts.endDate;
|
|
1156
|
-
if (opts.limit !== void 0) o.limit = opts.limit;
|
|
1157
|
-
if (opts.offset !== void 0) o.offset = opts.offset;
|
|
1158
1485
|
printJson(rt, await client.listExperiments(o));
|
|
1159
1486
|
});
|
|
1160
1487
|
});
|
|
@@ -1187,20 +1514,31 @@ function register10(program, rt) {
|
|
|
1187
1514
|
}
|
|
1188
1515
|
|
|
1189
1516
|
// src/commands/search.ts
|
|
1190
|
-
|
|
1191
|
-
|
|
1517
|
+
import { Option as Option2 } from "commander";
|
|
1518
|
+
function register13(program, rt) {
|
|
1519
|
+
program.command("search").description("Search across Seclai resources.").requiredOption("--query <text>", "Search query text.").option("--limit <n>", "Max results.", parseNumber).option("--entity-type <type>", "Filter by entity type (e.g. agent, source, knowledge_base, memory_bank).").action(async (opts) => {
|
|
1192
1520
|
await run(rt, async () => {
|
|
1193
1521
|
const client = createClient(program.opts());
|
|
1194
1522
|
const o = { query: opts.query };
|
|
1195
1523
|
if (opts.limit !== void 0) o.limit = opts.limit;
|
|
1196
|
-
if (opts.entityType) o.entityType = opts.entityType;
|
|
1524
|
+
if (opts.entityType !== void 0) o.entityType = opts.entityType;
|
|
1197
1525
|
printJson(rt, await client.search(o));
|
|
1198
1526
|
});
|
|
1199
1527
|
});
|
|
1528
|
+
const docs = program.command("docs").description("Seclai documentation.");
|
|
1529
|
+
docs.command("search").description("Search the Seclai documentation.").requiredOption("--query <text>", "Search query text.").addOption(new Option2("--mode <mode>", "Search mode.").choices(["keyword", "semantic"])).option("--limit <n>", "Max results.", parseNumber).action(async (opts) => {
|
|
1530
|
+
await run(rt, async () => {
|
|
1531
|
+
const client = createClient(program.opts());
|
|
1532
|
+
const o = { query: opts.query };
|
|
1533
|
+
if (opts.mode !== void 0) o.mode = opts.mode;
|
|
1534
|
+
if (opts.limit !== void 0) o.limit = opts.limit;
|
|
1535
|
+
printJson(rt, await client.searchDocs(o));
|
|
1536
|
+
});
|
|
1537
|
+
});
|
|
1200
1538
|
}
|
|
1201
1539
|
|
|
1202
1540
|
// src/commands/ai.ts
|
|
1203
|
-
function
|
|
1541
|
+
function register14(program, rt) {
|
|
1204
1542
|
const ai = program.command("ai").description("Top-level AI assistant.");
|
|
1205
1543
|
ai.command("feedback").description("Submit AI feedback.").option("--json <json>", "Feedback body JSON.").option("--json-file <path>", "Feedback body JSON file.").action(async (opts) => {
|
|
1206
1544
|
await run(rt, async () => {
|
|
@@ -1278,73 +1616,138 @@ function register12(program, rt) {
|
|
|
1278
1616
|
import { existsSync, statSync } from "fs";
|
|
1279
1617
|
import { mkdir, writeFile } from "fs/promises";
|
|
1280
1618
|
import { dirname, join } from "path";
|
|
1281
|
-
var
|
|
1619
|
+
var SKILL_FILES = [
|
|
1620
|
+
{ name: "SKILL.md", content: `---
|
|
1282
1621
|
name: seclai-cli
|
|
1283
1622
|
description: >-
|
|
1284
1623
|
Manage Seclai agents, knowledge bases, sources, memory banks, evaluations,
|
|
1285
|
-
solutions, governance, alerts, and
|
|
1286
|
-
the Seclai platform or when the user mentions Seclai CLI commands.
|
|
1624
|
+
solutions, governance, alerts, agent email, and models via the CLI. Use when
|
|
1625
|
+
working with the Seclai platform or when the user mentions Seclai CLI commands.
|
|
1287
1626
|
---
|
|
1288
1627
|
|
|
1289
1628
|
# Seclai CLI
|
|
1290
1629
|
|
|
1291
|
-
The Seclai CLI (\`seclai\` / \`npx @seclai/cli\`) manages agents, knowledge bases,
|
|
1630
|
+
The Seclai CLI (\`seclai\` / \`npx @seclai/cli\`) manages agents, knowledge bases,
|
|
1631
|
+
sources, memory banks, evaluations, solutions, governance, alerts, agent email,
|
|
1632
|
+
and models from the terminal.
|
|
1292
1633
|
|
|
1293
|
-
|
|
1634
|
+
Every command writes JSON to stdout. Pipe into \`jq\` for filtering. Errors go to
|
|
1635
|
+
stderr and set a non-zero exit code, so \`set -e\` scripts fail as expected.
|
|
1636
|
+
|
|
1637
|
+
**Find the commands for a task in the map below, then read that reference file.**
|
|
1638
|
+
Only this page is loaded up front; the references are read on demand.
|
|
1294
1639
|
|
|
1295
1640
|
## Quick start
|
|
1296
1641
|
|
|
1297
1642
|
\`\`\`bash
|
|
1298
|
-
# authenticate
|
|
1299
1643
|
export SECLAI_API_KEY="sk-..."
|
|
1300
1644
|
|
|
1301
|
-
# create an agent
|
|
1302
1645
|
seclai agents create --json '{"name":"My Agent","description":"QA chatbot"}'
|
|
1303
|
-
|
|
1304
|
-
# configure steps via AI assistant
|
|
1305
1646
|
seclai agents ai gen-steps <agentId> --user-input "Build a QA chatbot that uses a knowledge base"
|
|
1306
|
-
|
|
1307
|
-
# accept the generated plan
|
|
1308
1647
|
seclai agents ai mark <agentId> <conversationId> --json '{"accepted":true}'
|
|
1309
|
-
|
|
1310
|
-
# run the agent
|
|
1311
1648
|
seclai agents run <agentId> --json '{"input":"How do I reset my password?"}' --stream
|
|
1312
|
-
|
|
1313
|
-
# list runs
|
|
1314
1649
|
seclai agents runs list <agentId>
|
|
1315
1650
|
\`\`\`
|
|
1316
1651
|
|
|
1652
|
+
## Command map
|
|
1653
|
+
|
|
1654
|
+
| Group | What it covers | Reference |
|
|
1655
|
+
| --- | --- | --- |
|
|
1656
|
+
| \`agents\` | Agents, runs, definitions, export/import, input uploads, triggers, agent AI | [references/agents.md](references/agents.md) |
|
|
1657
|
+
| \`sources\` \`contents\` \`kb\` \`memory\` | Sources and uploads, exports, embedding migration, indexed content, knowledge bases, memory banks | [references/knowledge.md](references/knowledge.md) |
|
|
1658
|
+
| \`evals\` | Evaluation criteria, results, runs, agent-level summaries | [references/evaluations.md](references/evaluations.md) |
|
|
1659
|
+
| \`solutions\` \`governance\` | Solutions, resource links, conversations, solution and governance AI | [references/solutions.md](references/solutions.md) |
|
|
1660
|
+
| \`alerts\` | Alerts, alert configurations, organization preferences | [references/alerts.md](references/alerts.md) |
|
|
1661
|
+
| \`email\` | Agent email: sending domains, inbound blocklist, inbound health, opt-outs | [references/email.md](references/email.md) |
|
|
1662
|
+
| \`models\` | Model catalog, generation tiers, model alerts, recommendations, playground experiments | [references/models.md](references/models.md) |
|
|
1663
|
+
| \`auth\` \`configure\` \`api-version\` \`mcp\` \`skills\` \`completion\` | Authentication, profiles, API version pinning, editor integration | [references/setup.md](references/setup.md) |
|
|
1664
|
+
| \`ai\` | Top-level AI assistant for knowledge bases, sources, solutions and memory | [references/ai-assistant.md](references/ai-assistant.md) |
|
|
1665
|
+
| \`search\` \`docs\` \`me\` | Search across resources, search the documentation, show the authenticated account | below, under [Search and account](#search-and-account) |
|
|
1666
|
+
|
|
1667
|
+
Cross-cutting topics: [streaming and event modes](references/streaming.md),
|
|
1668
|
+
[file uploads](references/uploads.md).
|
|
1669
|
+
|
|
1317
1670
|
## Authentication
|
|
1318
1671
|
|
|
1319
|
-
|
|
1320
|
-
|
|
1672
|
+
Two modes:
|
|
1673
|
+
|
|
1674
|
+
1. **API key** \u2014 set \`SECLAI_API_KEY\`, or pass \`--api-key <key>\`.
|
|
1675
|
+
2. **SSO** \u2014 \`seclai auth login\` for browser-based OAuth2/PKCE. Tokens are cached
|
|
1676
|
+
locally and refreshed automatically.
|
|
1677
|
+
|
|
1678
|
+
Override the API host with \`SECLAI_API_URL\` (default \`https://api.seclai.com\`).
|
|
1321
1679
|
|
|
1322
1680
|
## Global options
|
|
1323
1681
|
|
|
1324
1682
|
\`\`\`bash
|
|
1325
|
-
--api-key <key>
|
|
1326
|
-
--
|
|
1327
|
-
-
|
|
1683
|
+
--api-key <key> # or set SECLAI_API_KEY
|
|
1684
|
+
--profile <name> # SSO profile (or SECLAI_PROFILE, default 'default')
|
|
1685
|
+
--account-id <id> # multi-org targeting (X-Account-Id header)
|
|
1686
|
+
--config-dir <path> # or SECLAI_CONFIG_DIR, default ~/.seclai
|
|
1687
|
+
--api-version <date> # or SECLAI_API_VERSION; see below
|
|
1688
|
+
--allow-unknown-api-version # send a version this CLI was not built against
|
|
1689
|
+
--compact # single-line JSON
|
|
1690
|
+
-V, --version
|
|
1691
|
+
\`\`\`
|
|
1692
|
+
|
|
1693
|
+
## API versions
|
|
1694
|
+
|
|
1695
|
+
The API is versioned by date, and a version can change a response's shape \u2014 a
|
|
1696
|
+
bare array becoming \`{data, pagination}\`, for instance. **The CLI sends no
|
|
1697
|
+
version header by default**, so upgrading it never changes what a command
|
|
1698
|
+
prints. Opt in per invocation, or pin the account:
|
|
1699
|
+
|
|
1700
|
+
\`\`\`bash
|
|
1701
|
+
seclai api-version get # what does a request resolve to?
|
|
1702
|
+
seclai --api-version 2026-07-27 alerts list # this invocation only
|
|
1703
|
+
seclai api-version set 2026-07-27 # every client on the account
|
|
1704
|
+
seclai api-version clear
|
|
1328
1705
|
\`\`\`
|
|
1329
1706
|
|
|
1707
|
+
An \`--api-version\` this CLI was not built against is rejected, because a newer
|
|
1708
|
+
version can reshape a response the CLI would then misread. Pass
|
|
1709
|
+
\`--allow-unknown-api-version\` to send it anyway. \`api-version set\` takes a
|
|
1710
|
+
\`YYYY-MM-DD\` date and rejects anything else, because the pin applies to every
|
|
1711
|
+
client on the account.
|
|
1712
|
+
|
|
1713
|
+
\`--api-key\`, \`--profile\`, \`--account-id\` and \`--config-dir\` reject an empty
|
|
1714
|
+
value. A shell expanding an unset variable passes \`""\`, which the SDK's
|
|
1715
|
+
credential chain discards, so each would silently resolve elsewhere \u2014 a
|
|
1716
|
+
different identity, another account's cached tokens, or the default org. Guard
|
|
1717
|
+
the flag rather than the value: \`seclai \${KEY:+--api-key "$KEY"} agents list\`.
|
|
1718
|
+
|
|
1719
|
+
An empty \`--api-version\` is accepted with a warning, since it costs only the
|
|
1720
|
+
version header; a future release will reject it too.
|
|
1721
|
+
|
|
1330
1722
|
## Common patterns
|
|
1331
1723
|
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1724
|
+
**JSON input.** Most create/update commands take \`--json '{"key":"value"}'\` or
|
|
1725
|
+
\`--json-file path.json\`. Use \`-\` as the value to read from stdin.
|
|
1726
|
+
|
|
1727
|
+
**AI shorthand.** AI generation commands accept \`--user-input <text>\` in place of
|
|
1728
|
+
\`--json '{"user_input":"<text>"}'\`.
|
|
1729
|
+
|
|
1730
|
+
**Pagination.** List commands take \`--page <n>\` and \`--limit <n>\`; some add
|
|
1731
|
+
\`--sort <field>\` and \`--order asc|desc\`. A few endpoints paginate by offset
|
|
1732
|
+
instead and take \`--limit\` / \`--offset\`.
|
|
1335
1733
|
|
|
1336
|
-
|
|
1337
|
-
|
|
1734
|
+
**Uploads.** Upload commands take \`--file <path>\`, plus optional \`--title\`,
|
|
1735
|
+
\`--metadata '{"k":"v"}'\`, \`--metadata-file\`, \`--file-name\` and \`--mime-type\`.
|
|
1338
1736
|
|
|
1339
|
-
|
|
1340
|
-
List commands support \`--page <n>\` and \`--limit <n>\`. Some also support \`--sort <field>\` and \`--order asc|desc\`.
|
|
1737
|
+
## Search and account
|
|
1341
1738
|
|
|
1342
|
-
|
|
1343
|
-
|
|
1739
|
+
\`\`\`bash
|
|
1740
|
+
seclai search --query "deployment guide" [--limit N] [--entity-type <type>]
|
|
1741
|
+
seclai docs search --query "memory banks" [--mode keyword|semantic] [--limit N]
|
|
1742
|
+
seclai me # account ID and organization memberships
|
|
1743
|
+
\`\`\`
|
|
1744
|
+
` },
|
|
1745
|
+
{ name: "references/agents.md", content: `# Agents
|
|
1344
1746
|
|
|
1345
|
-
|
|
1747
|
+
Agents, their runs, definitions, export/import, input uploads, triggers, and the
|
|
1748
|
+
agent AI assistant.
|
|
1346
1749
|
|
|
1347
|
-
|
|
1750
|
+
## CRUD and lifecycle
|
|
1348
1751
|
|
|
1349
1752
|
\`\`\`bash
|
|
1350
1753
|
seclai agents list [--page N] [--limit N]
|
|
@@ -1352,100 +1755,382 @@ seclai agents create --json '{"name":"My Agent","description":"..."}'
|
|
|
1352
1755
|
seclai agents get <agentId>
|
|
1353
1756
|
seclai agents update <agentId> --json '{"name":"Renamed"}'
|
|
1354
1757
|
seclai agents delete <agentId>
|
|
1758
|
+
|
|
1759
|
+
# pause across every trigger path (API, schedule, email, sub-agent calls)
|
|
1760
|
+
seclai agents disable <agentId>
|
|
1761
|
+
seclai agents enable <agentId>
|
|
1762
|
+
|
|
1763
|
+
# which live agents call this one via a call_agent step?
|
|
1764
|
+
seclai agents callers <agentId>
|
|
1765
|
+
\`\`\`
|
|
1766
|
+
|
|
1767
|
+
## Triggers
|
|
1768
|
+
|
|
1769
|
+
\`\`\`bash
|
|
1770
|
+
# alias, sender allowlist and inbound-handling flags for an EMAIL_RECEIVED trigger
|
|
1771
|
+
seclai agents triggers email-config <agentId> <triggerId> --json '{"alias":"support"}'
|
|
1355
1772
|
\`\`\`
|
|
1356
1773
|
|
|
1357
|
-
|
|
1774
|
+
## Running agents
|
|
1775
|
+
|
|
1776
|
+
Four modes: basic, streaming, NDJSON events, and polling. See
|
|
1777
|
+
[streaming.md](streaming.md) for event shapes and filtering.
|
|
1358
1778
|
|
|
1359
1779
|
\`\`\`bash
|
|
1360
1780
|
# simple run \u2014 returns the final result
|
|
1361
1781
|
seclai agents run <agentId> --json '{"input":"Hello"}'
|
|
1362
1782
|
|
|
1363
|
-
# stream \u2014 wait for completion via SSE, print final result
|
|
1783
|
+
# stream \u2014 wait for completion via SSE, print the final result
|
|
1364
1784
|
seclai agents run <agentId> --json '{"input":"Hello"}' --stream [--timeout-ms 60000]
|
|
1365
1785
|
|
|
1366
|
-
# events \u2014
|
|
1367
|
-
# --output: full (entire event), data (event data only), status (
|
|
1368
|
-
# --event-filter: comma-separated event types
|
|
1786
|
+
# events \u2014 every SSE event as an NDJSON line
|
|
1787
|
+
# --output: full (entire event), data (event data only), status (one-line summary)
|
|
1788
|
+
# --event-filter: comma-separated event types, e.g. "status,data"
|
|
1369
1789
|
seclai agents run <agentId> --json '{"input":"Hello"}' --events [--output full|data|status] [--event-filter "status,data"]
|
|
1370
1790
|
|
|
1371
|
-
# poll \u2014 poll
|
|
1791
|
+
# poll \u2014 submit, then poll until complete
|
|
1372
1792
|
seclai agents run <agentId> --json '{"input":"Hello"}' --poll [--poll-interval-ms 2000] [--include-step-outputs]
|
|
1373
1793
|
\`\`\`
|
|
1374
1794
|
|
|
1375
|
-
|
|
1795
|
+
## Runs
|
|
1376
1796
|
|
|
1377
1797
|
\`\`\`bash
|
|
1378
1798
|
seclai agents runs list <agentId> [--page N] [--limit N] [--status <status>]
|
|
1379
1799
|
seclai agents runs get <runId> [--include-step-outputs]
|
|
1380
|
-
seclai agents runs delete <runId>
|
|
1381
1800
|
seclai agents runs cancel <runId>
|
|
1801
|
+
seclai agents runs delete <runId> # deprecated alias for \`runs cancel\`; the API has no delete-a-run operation
|
|
1382
1802
|
seclai agents runs search --json '{"query":"..."}'
|
|
1383
1803
|
seclai agents runs eval-results <agentId> <runId> [--page N] [--limit N]
|
|
1804
|
+
|
|
1805
|
+
# Download a file emitted by a run step. attachmentId is the URL-safe-base64
|
|
1806
|
+
# storage_key from run output manifests or webhooks.
|
|
1807
|
+
seclai agents runs download-attachment <runId> <attachmentId> [--download-name <name>] [--output <path>]
|
|
1384
1808
|
\`\`\`
|
|
1385
1809
|
|
|
1386
|
-
|
|
1810
|
+
Without \`--output\`, raw bytes go to stdout \u2014 redirect to a file rather than
|
|
1811
|
+
letting them hit the terminal.
|
|
1812
|
+
|
|
1813
|
+
## Definitions
|
|
1387
1814
|
|
|
1388
1815
|
\`\`\`bash
|
|
1389
1816
|
seclai agents def get <agentId>
|
|
1390
|
-
seclai agents def update <agentId> --json '{"steps":[{"step_type":"llm","config":{
|
|
1817
|
+
seclai agents def update <agentId> --json '{"steps":[{"step_type":"llm","config":{}}]}'
|
|
1391
1818
|
\`\`\`
|
|
1392
1819
|
|
|
1393
|
-
|
|
1820
|
+
## Export and import
|
|
1394
1821
|
|
|
1395
1822
|
\`\`\`bash
|
|
1823
|
+
# portable JSON snapshot of an agent definition
|
|
1824
|
+
seclai agents export <agentId> [--no-download]
|
|
1825
|
+
|
|
1826
|
+
# Validate an agent_definition payload before importing \u2014 no writes.
|
|
1827
|
+
# Reports counts and any unresolved_refs (knowledge bases, memory banks, source
|
|
1828
|
+
# connections or sub-agents that do not exist in this account).
|
|
1829
|
+
seclai agents export <agentId> \\
|
|
1830
|
+
| jq '{agent_definition: .}' \\
|
|
1831
|
+
| seclai agents preview-import --json-file -
|
|
1832
|
+
|
|
1833
|
+
# Import via \`agents create\` (or \`agents update\`) with agent_definition set to
|
|
1834
|
+
# the export payload, and entity_remap mapping unresolved source UUIDs to target
|
|
1835
|
+
# UUIDs taken from preview-import's unresolved_refs[*].alternatives.
|
|
1836
|
+
seclai agents create --json '{"name":"Imported","trigger_type":"dynamic_input","agent_definition":{},"entity_remap":{}}'
|
|
1837
|
+
\`\`\`
|
|
1838
|
+
|
|
1839
|
+
## Input uploads
|
|
1840
|
+
|
|
1841
|
+
\`\`\`bash
|
|
1842
|
+
# What files (if any) does this agent expect? requires_uploads reports whether it
|
|
1843
|
+
# accepts files; the agent block lists the names, indexes and patterns a run-time
|
|
1844
|
+
# batch must satisfy. Call this before staging uploads.
|
|
1845
|
+
seclai agents attachment-references <agentId>
|
|
1846
|
+
|
|
1396
1847
|
seclai agents upload-input <agentId> --file ./input.pdf [--file-name name] [--mime-type type]
|
|
1397
1848
|
seclai agents input-status <agentId> <uploadId>
|
|
1398
1849
|
\`\`\`
|
|
1399
1850
|
|
|
1400
|
-
|
|
1851
|
+
## Agent AI assistant
|
|
1401
1852
|
|
|
1402
1853
|
\`\`\`bash
|
|
1403
1854
|
seclai agents ai gen-steps <agentId> --user-input "Build a QA chatbot"
|
|
1404
1855
|
seclai agents ai step-config <agentId> --json '{"step_type":"llm","user_input":"Configure the LLM step"}'
|
|
1405
|
-
|
|
1856
|
+
|
|
1857
|
+
# --step-type is required; the API rejects the request without it
|
|
1858
|
+
seclai agents ai history <agentId> --step-type llm [--step-id <id>] [--limit N] [--offset N]
|
|
1859
|
+
|
|
1406
1860
|
seclai agents ai mark <agentId> <conversationId> --json '{"accepted":true}'
|
|
1407
1861
|
\`\`\`
|
|
1408
1862
|
|
|
1409
|
-
|
|
1863
|
+
## Example: knowledge-base-backed agent
|
|
1410
1864
|
|
|
1411
1865
|
\`\`\`bash
|
|
1412
|
-
seclai
|
|
1413
|
-
seclai
|
|
1414
|
-
seclai
|
|
1415
|
-
seclai
|
|
1416
|
-
seclai
|
|
1866
|
+
seclai kb create --json '{"name":"Support KB","description":"Customer support articles"}'
|
|
1867
|
+
seclai agents create --json '{"name":"Support Bot","description":"Answers customer questions"}'
|
|
1868
|
+
seclai agents ai gen-steps <agentId> --user-input "Build a QA chatbot that searches the Support KB"
|
|
1869
|
+
seclai agents ai mark <agentId> <conversationId> --json '{"accepted":true}'
|
|
1870
|
+
seclai agents run <agentId> --json '{"input":"How do I reset my password?"}' --stream
|
|
1417
1871
|
\`\`\`
|
|
1418
1872
|
|
|
1419
|
-
|
|
1873
|
+
## Example: memory-powered agent
|
|
1420
1874
|
|
|
1421
1875
|
\`\`\`bash
|
|
1422
|
-
seclai
|
|
1423
|
-
seclai
|
|
1876
|
+
seclai memory create --json '{"name":"User Preferences","type":"general"}'
|
|
1877
|
+
seclai agents create --json '{"name":"Personal Assistant","description":"Remembers user preferences"}'
|
|
1878
|
+
seclai agents ai gen-steps <agentId> --user-input "Build a chat agent that remembers user preferences. Use general memory bank <memoryBankId>"
|
|
1879
|
+
seclai agents ai mark <agentId> <conversationId> --json '{"accepted":true}'
|
|
1424
1880
|
\`\`\`
|
|
1881
|
+
` },
|
|
1882
|
+
{ name: "references/ai-assistant.md", content: `# Top-level AI assistant
|
|
1425
1883
|
|
|
1426
|
-
|
|
1884
|
+
\`seclai ai\` creates resources from a natural-language description, without
|
|
1885
|
+
starting from a solution or an agent. The domain-scoped assistants \u2014
|
|
1886
|
+
\`agents ai\`, \`memory ai\`, \`solutions ai\`, \`governance ai\` \u2014 live with their
|
|
1887
|
+
resources.
|
|
1427
1888
|
|
|
1428
1889
|
\`\`\`bash
|
|
1429
|
-
seclai
|
|
1430
|
-
seclai
|
|
1431
|
-
seclai
|
|
1432
|
-
seclai
|
|
1433
|
-
|
|
1434
|
-
seclai
|
|
1435
|
-
seclai
|
|
1890
|
+
seclai ai kb --user-input "Create a support knowledge base"
|
|
1891
|
+
seclai ai source --user-input "Create a documentation source"
|
|
1892
|
+
seclai ai solution --user-input "Build a customer support solution"
|
|
1893
|
+
seclai ai memory --user-input "Create a conversation memory bank"
|
|
1894
|
+
|
|
1895
|
+
seclai ai memory-history
|
|
1896
|
+
seclai ai accept <conversationId> --json '{"accepted":true}'
|
|
1897
|
+
seclai ai decline <conversationId>
|
|
1898
|
+
seclai ai memory-accept <conversationId> --json '{"accepted":true}'
|
|
1899
|
+
|
|
1900
|
+
seclai ai feedback --json '{"feedback":"The response was helpful"}'
|
|
1436
1901
|
\`\`\`
|
|
1437
1902
|
|
|
1438
|
-
|
|
1903
|
+
## The generate-then-accept cycle
|
|
1904
|
+
|
|
1905
|
+
Every assistant command returns a *proposal* with a conversation ID. Nothing is
|
|
1906
|
+
created until you accept it:
|
|
1439
1907
|
|
|
1440
1908
|
\`\`\`bash
|
|
1441
|
-
seclai
|
|
1442
|
-
|
|
1443
|
-
seclai
|
|
1909
|
+
seclai ai kb --user-input "Create a support knowledge base"
|
|
1910
|
+
# read the proposal, note the conversation id
|
|
1911
|
+
seclai ai accept <conversationId> --json '{"accepted":true}'
|
|
1444
1912
|
\`\`\`
|
|
1445
1913
|
|
|
1446
|
-
|
|
1914
|
+
Memory-bank proposals have their own accept command (\`ai memory-accept\`) and
|
|
1915
|
+
their own history (\`ai memory-history\`); everything else uses \`ai accept\` /
|
|
1916
|
+
\`ai decline\`.
|
|
1917
|
+
` },
|
|
1918
|
+
{ name: "references/alerts.md", content: `# Alerts
|
|
1447
1919
|
|
|
1448
|
-
|
|
1920
|
+
Account alerts, the configurations that raise them, and per-organization
|
|
1921
|
+
delivery preferences.
|
|
1922
|
+
|
|
1923
|
+
Model-catalog alerts are separate \u2014 see [models.md](models.md).
|
|
1924
|
+
|
|
1925
|
+
## Alerts
|
|
1926
|
+
|
|
1927
|
+
\`\`\`bash
|
|
1928
|
+
seclai alerts list [--page N] [--limit N] [--status <status>]
|
|
1929
|
+
seclai alerts get <alertId>
|
|
1930
|
+
seclai alerts status <alertId> --json '{"status":"resolved"}'
|
|
1931
|
+
seclai alerts comment <alertId> --json '{"comment":"Fixed the issue"}'
|
|
1932
|
+
seclai alerts subscribe <alertId>
|
|
1933
|
+
seclai alerts unsubscribe <alertId>
|
|
1934
|
+
\`\`\`
|
|
1935
|
+
|
|
1936
|
+
\`GET /alerts\` declares no severity filter, so there is no \`--severity\` \u2014 it
|
|
1937
|
+
never filtered anything, and returned unfiltered rows that looked filtered.
|
|
1938
|
+
Filter client-side instead:
|
|
1939
|
+
|
|
1940
|
+
\`\`\`bash
|
|
1941
|
+
seclai alerts list | jq '[.data[] | select(.severity == "high")]'
|
|
1942
|
+
\`\`\`
|
|
1943
|
+
|
|
1944
|
+
## Alert configurations
|
|
1945
|
+
|
|
1946
|
+
\`\`\`bash
|
|
1947
|
+
seclai alerts configs list [--page N] [--limit N]
|
|
1948
|
+
seclai alerts configs create --json '{"name":"Latency Alert","description":"...","threshold":5000}'
|
|
1949
|
+
seclai alerts configs get <configId>
|
|
1950
|
+
seclai alerts configs update <configId> --json '{"threshold":3000}'
|
|
1951
|
+
seclai alerts configs delete <configId>
|
|
1952
|
+
\`\`\`
|
|
1953
|
+
|
|
1954
|
+
## Organization preferences
|
|
1955
|
+
|
|
1956
|
+
\`\`\`bash
|
|
1957
|
+
seclai alerts prefs list
|
|
1958
|
+
seclai alerts prefs update <organizationId> <alertType> --json '{"enabled":true}'
|
|
1959
|
+
\`\`\`
|
|
1960
|
+
|
|
1961
|
+
Preferences are per organization and per alert type, so \`update\` takes both.
|
|
1962
|
+
` },
|
|
1963
|
+
{ name: "references/email.md", content: `# Agent email
|
|
1964
|
+
|
|
1965
|
+
The domains agents send from, the inbound blocklist, inbound health, and
|
|
1966
|
+
recipient opt-outs.
|
|
1967
|
+
|
|
1968
|
+
Per-agent inbound configuration (alias, sender allowlist) lives on the trigger \u2014
|
|
1969
|
+
see \`agents triggers email-config\` in [agents.md](agents.md).
|
|
1970
|
+
|
|
1971
|
+
## Sending domains
|
|
1972
|
+
|
|
1973
|
+
\`\`\`bash
|
|
1974
|
+
seclai email domains list
|
|
1975
|
+
seclai email domains add --kind custom --value mail.example.com [--delegated]
|
|
1976
|
+
seclai email domains verify <domainId> # run a DNS check now
|
|
1977
|
+
seclai email domains set-primary <domainId>
|
|
1978
|
+
seclai email domains test-email <domainId> # send a test to the account owner
|
|
1979
|
+
seclai email domains dmarc <domainId> [--days N] [--top-sources N]
|
|
1980
|
+
seclai email domains remove <domainId>
|
|
1981
|
+
seclai email domains use-shared # revert to agent.seclai.com
|
|
1982
|
+
\`\`\`
|
|
1983
|
+
|
|
1984
|
+
\`--kind\` is \`vanity\` (a subdomain of seclai.com) or \`custom\` (your own domain).
|
|
1985
|
+
\`add\` returns the DNS records to publish; pass \`--delegated\` when the domain's
|
|
1986
|
+
DNS is delegated to Seclai so those records are published for you. A domain must
|
|
1987
|
+
verify before \`set-primary\` will accept it.
|
|
1988
|
+
|
|
1989
|
+
## Inbound sender blocklist
|
|
1990
|
+
|
|
1991
|
+
\`\`\`bash
|
|
1992
|
+
seclai email blocked list [--limit N] [--offset N]
|
|
1993
|
+
seclai email blocked add --sender-email spam@example.com [--note "phishing"]
|
|
1994
|
+
seclai email blocked add --sender-email example.com --match-type domain
|
|
1995
|
+
seclai email blocked remove <blockedId>
|
|
1996
|
+
seclai email blocked auto-block-mode disabled|input|input_and_output
|
|
1997
|
+
\`\`\`
|
|
1998
|
+
|
|
1999
|
+
\`--match-type\` is \`address\` (the default) or \`domain\`. \`auto-block-mode\` controls
|
|
2000
|
+
whether a governance BLOCK on an authenticated sender adds them to the blocklist
|
|
2001
|
+
automatically.
|
|
2002
|
+
|
|
2003
|
+
## Inbound health
|
|
2004
|
+
|
|
2005
|
+
\`\`\`bash
|
|
2006
|
+
seclai email inbound status # quota usage, pause state, queued run counts
|
|
2007
|
+
seclai email inbound rejections [--agent-id <id>] [--limit N]
|
|
2008
|
+
seclai email inbound cancel-queued # fail every over-quota parked run at once
|
|
2009
|
+
seclai email inbound resume # lift the account-wide pause
|
|
2010
|
+
\`\`\`
|
|
2011
|
+
|
|
2012
|
+
When inbound email exceeds quota, runs park in a QUEUED state and the account
|
|
2013
|
+
pauses. \`status\` shows both; \`cancel-queued\` clears the backlog and \`resume\`
|
|
2014
|
+
lifts the pause. Check \`rejections\` first \u2014 it reports why messages were turned
|
|
2015
|
+
away, which is usually the more useful answer.
|
|
2016
|
+
|
|
2017
|
+
## Recipient opt-outs
|
|
2018
|
+
|
|
2019
|
+
\`\`\`bash
|
|
2020
|
+
seclai email optouts list [--agent-id <id>] [--limit N] [--offset N]
|
|
2021
|
+
seclai email optouts remove <optoutId>
|
|
2022
|
+
\`\`\`
|
|
2023
|
+
|
|
2024
|
+
Removing an opt-out lets that recipient receive agent email again.
|
|
2025
|
+
` },
|
|
2026
|
+
{ name: "references/evaluations.md", content: `# Evaluations Workflow
|
|
2027
|
+
|
|
2028
|
+
## Step 1: Create evaluation criteria for an agent
|
|
2029
|
+
\`\`\`bash
|
|
2030
|
+
seclai evals criteria create <agentId> --json '{"name":"Answer Accuracy","description":"Does the answer correctly address the question?","eval_type":"llm_judge"}'
|
|
2031
|
+
\`\`\`
|
|
2032
|
+
|
|
2033
|
+
## Step 2: Find runs to evaluate
|
|
2034
|
+
\`\`\`bash
|
|
2035
|
+
# list all runs for an agent
|
|
2036
|
+
seclai agents runs list <agentId> --limit 10
|
|
2037
|
+
|
|
2038
|
+
# or find runs compatible with specific criteria
|
|
2039
|
+
seclai evals compatible-runs <criteriaId> --limit 10
|
|
2040
|
+
\`\`\`
|
|
2041
|
+
|
|
2042
|
+
## Step 3: Test criteria before committing
|
|
2043
|
+
\`\`\`bash
|
|
2044
|
+
seclai evals test-draft <agentId> --json '{"criteria":{"name":"Answer Accuracy","eval_type":"llm_judge","description":"..."},"run_id":"<runId>"}'
|
|
2045
|
+
\`\`\`
|
|
2046
|
+
|
|
2047
|
+
## Step 4: Create evaluation results
|
|
2048
|
+
\`\`\`bash
|
|
2049
|
+
seclai evals results create <criteriaId> --json '{"run_id":"<runId>","score":0.95}'
|
|
2050
|
+
\`\`\`
|
|
2051
|
+
|
|
2052
|
+
## Step 5: Review summaries
|
|
2053
|
+
\`\`\`bash
|
|
2054
|
+
seclai evals criteria summary <criteriaId>
|
|
2055
|
+
seclai evals agent-results <agentId>
|
|
2056
|
+
seclai evals agent-runs <agentId> --limit 20
|
|
2057
|
+
seclai evals non-manual-summary <agentId>
|
|
2058
|
+
\`\`\`
|
|
2059
|
+
|
|
2060
|
+
## Managing criteria
|
|
2061
|
+
\`\`\`bash
|
|
2062
|
+
seclai evals criteria list <agentId> [--page N] [--limit N] [--paged]
|
|
2063
|
+
seclai evals criteria get <criteriaId>
|
|
2064
|
+
seclai evals criteria update <criteriaId> --json '{"name":"Updated Name"}'
|
|
2065
|
+
seclai evals criteria delete <criteriaId>
|
|
2066
|
+
\`\`\`
|
|
2067
|
+
|
|
2068
|
+
\`--paged\` wraps the results in \`{"data": [...]}\` instead of returning a bare
|
|
2069
|
+
array, so \`.data\` is a stable path to read whatever \`--api-version\` is in effect.
|
|
2070
|
+
Nothing is invented: the \`pagination\` block appears only once the API sends one,
|
|
2071
|
+
from \`--api-version 2026-07-27\`. Move scripts to \`.data\` first, then opt in to
|
|
2072
|
+
get \`.pagination\`.
|
|
2073
|
+
|
|
2074
|
+
## Viewing results
|
|
2075
|
+
\`\`\`bash
|
|
2076
|
+
seclai evals results list <criteriaId> [--page N] [--limit N]
|
|
2077
|
+
seclai evals compatible-runs <criteriaId> [--page N] [--limit N]
|
|
2078
|
+
seclai evals agent-results <agentId> [--page N] [--limit N]
|
|
2079
|
+
seclai evals agent-runs <agentId> [--page N] [--limit N]
|
|
2080
|
+
\`\`\`
|
|
2081
|
+
` },
|
|
2082
|
+
{ name: "references/knowledge.md", content: `# Sources, content, knowledge bases and memory banks
|
|
2083
|
+
|
|
2084
|
+
The ingestion side of Seclai: where documents come from, how they are indexed,
|
|
2085
|
+
and the stores agents read from.
|
|
2086
|
+
|
|
2087
|
+
For upload mechanics \u2014 MIME types, size limits, metadata \u2014 see
|
|
2088
|
+
[uploads.md](uploads.md).
|
|
2089
|
+
|
|
2090
|
+
## Sources
|
|
2091
|
+
|
|
2092
|
+
\`\`\`bash
|
|
2093
|
+
seclai sources list [--page N] [--limit N] [--sort field] [--order asc|desc] [--account-id id]
|
|
2094
|
+
seclai sources create --json '{"name":"Docs","description":"Product documentation"}'
|
|
2095
|
+
seclai sources get <sourceId>
|
|
2096
|
+
seclai sources update <sourceId> --json '{"name":"Updated Docs"}'
|
|
2097
|
+
seclai sources delete <sourceId>
|
|
2098
|
+
\`\`\`
|
|
2099
|
+
|
|
2100
|
+
\`source\` is accepted as an alias for \`sources\`.
|
|
2101
|
+
|
|
2102
|
+
## Source uploads
|
|
2103
|
+
|
|
2104
|
+
\`\`\`bash
|
|
2105
|
+
seclai sources upload <sourceId> --file ./doc.pdf [--title "My Doc"] [--metadata '{"category":"docs"}'] [--file-name name] [--mime-type type]
|
|
2106
|
+
seclai sources upload-text <sourceId> --json '{"text":"Article content here...","title":"My Article"}'
|
|
2107
|
+
\`\`\`
|
|
2108
|
+
|
|
2109
|
+
## Source exports
|
|
2110
|
+
|
|
2111
|
+
\`\`\`bash
|
|
2112
|
+
seclai sources exports list <sourceId> [--page N] [--limit N]
|
|
2113
|
+
seclai sources exports create <sourceId> --json '{"format":"jsonl"}'
|
|
2114
|
+
seclai sources exports get <sourceId> <exportId>
|
|
2115
|
+
seclai sources exports cancel <sourceId> <exportId>
|
|
2116
|
+
seclai sources exports delete <sourceId> <exportId>
|
|
2117
|
+
seclai sources exports download <sourceId> <exportId>
|
|
2118
|
+
seclai sources exports estimate <sourceId> --json '{"format":"jsonl"}'
|
|
2119
|
+
\`\`\`
|
|
2120
|
+
|
|
2121
|
+
\`estimate\` reports the size and cost before you commit to \`create\`.
|
|
2122
|
+
|
|
2123
|
+
## Embedding migration
|
|
2124
|
+
|
|
2125
|
+
\`\`\`bash
|
|
2126
|
+
seclai sources migration get <sourceId>
|
|
2127
|
+
seclai sources migration start <sourceId> --json '{"target_model":"text-embedding-3-large"}'
|
|
2128
|
+
seclai sources migration cancel <sourceId>
|
|
2129
|
+
\`\`\`
|
|
2130
|
+
|
|
2131
|
+
## Contents (indexed content)
|
|
2132
|
+
|
|
2133
|
+
\`\`\`bash
|
|
1449
2134
|
seclai contents get <contentVersionId> [--start N] [--end N]
|
|
1450
2135
|
seclai contents delete <contentVersionId>
|
|
1451
2136
|
seclai contents upload <contentVersionId> --file ./updated.pdf [--title "Title"] [--file-name name] [--mime-type type]
|
|
@@ -1453,7 +2138,10 @@ seclai contents replace-text <contentVersionId> --json '{"text":"Replacement tex
|
|
|
1453
2138
|
seclai contents embeddings <contentVersionId> [--page N] [--limit N]
|
|
1454
2139
|
\`\`\`
|
|
1455
2140
|
|
|
1456
|
-
|
|
2141
|
+
\`--start\` / \`--end\` on \`contents get\` slice the returned text by character
|
|
2142
|
+
offset, which is how you inspect a long document without pulling all of it.
|
|
2143
|
+
|
|
2144
|
+
## Knowledge bases
|
|
1457
2145
|
|
|
1458
2146
|
\`\`\`bash
|
|
1459
2147
|
seclai kb list [--page N] [--limit N] [--sort field] [--order asc|desc]
|
|
@@ -1463,7 +2151,7 @@ seclai kb update <kbId> --json '{"name":"Updated KB"}'
|
|
|
1463
2151
|
seclai kb delete <kbId>
|
|
1464
2152
|
\`\`\`
|
|
1465
2153
|
|
|
1466
|
-
|
|
2154
|
+
## Memory banks
|
|
1467
2155
|
|
|
1468
2156
|
\`\`\`bash
|
|
1469
2157
|
seclai memory list [--page N] [--limit N] [--sort field] [--order asc|desc]
|
|
@@ -1474,11 +2162,11 @@ seclai memory update <memoryBankId> --json '{"name":"Renamed"}'
|
|
|
1474
2162
|
seclai memory delete <memoryBankId>
|
|
1475
2163
|
\`\`\`
|
|
1476
2164
|
|
|
1477
|
-
###
|
|
2165
|
+
### Utilities
|
|
1478
2166
|
|
|
1479
2167
|
\`\`\`bash
|
|
1480
2168
|
seclai memory stats <memoryBankId>
|
|
1481
|
-
seclai memory agents <memoryBankId>
|
|
2169
|
+
seclai memory agents <memoryBankId> # agents using this bank
|
|
1482
2170
|
seclai memory compact <memoryBankId>
|
|
1483
2171
|
seclai memory delete-source <memoryBankId>
|
|
1484
2172
|
seclai memory templates
|
|
@@ -1486,6 +2174,9 @@ seclai memory test-compaction <memoryBankId> --json '{"prompt":"Summarize the co
|
|
|
1486
2174
|
seclai memory test-compaction-standalone --json '{"prompt":"Summarize the conversation"}'
|
|
1487
2175
|
\`\`\`
|
|
1488
2176
|
|
|
2177
|
+
Both \`test-compaction\` commands are dry runs \u2014 they show what compaction would
|
|
2178
|
+
produce without writing to the bank.
|
|
2179
|
+
|
|
1489
2180
|
### Memory bank AI
|
|
1490
2181
|
|
|
1491
2182
|
\`\`\`bash
|
|
@@ -1494,180 +2185,190 @@ seclai memory ai last
|
|
|
1494
2185
|
seclai memory ai accept <conversationId> --json '{"accepted":true}'
|
|
1495
2186
|
\`\`\`
|
|
1496
2187
|
|
|
1497
|
-
|
|
2188
|
+
## Example: create a source and upload content
|
|
1498
2189
|
|
|
1499
2190
|
\`\`\`bash
|
|
1500
|
-
seclai
|
|
1501
|
-
|
|
1502
|
-
seclai
|
|
1503
|
-
seclai
|
|
1504
|
-
seclai evals criteria delete <criteriaId>
|
|
1505
|
-
seclai evals criteria summary <criteriaId>
|
|
2191
|
+
seclai sources create --json '{"name":"Product Docs","description":"Product documentation source"}'
|
|
2192
|
+
# note the id from the output
|
|
2193
|
+
seclai sources upload <sourceId> --file ./docs.pdf --title "Product Manual" --metadata '{"version":"2.0"}'
|
|
2194
|
+
seclai sources get <sourceId>
|
|
1506
2195
|
\`\`\`
|
|
2196
|
+
` },
|
|
2197
|
+
{ name: "references/models.md", content: `# Models
|
|
1507
2198
|
|
|
1508
|
-
|
|
2199
|
+
The model catalog, media-generation tiers, model-catalog alerts, recommendations
|
|
2200
|
+
and the playground.
|
|
1509
2201
|
|
|
1510
|
-
|
|
1511
|
-
seclai evals results list <criteriaId> [--page N] [--limit N]
|
|
1512
|
-
seclai evals results create <criteriaId> --json '{"run_id":"...","score":0.9}'
|
|
1513
|
-
seclai evals compatible-runs <criteriaId> [--page N] [--limit N]
|
|
1514
|
-
seclai evals test-draft <agentId> --json '{"criteria":{"name":"Test","eval_type":"llm_judge"},"run_id":"..."}'
|
|
1515
|
-
seclai evals agent-results <agentId> [--page N] [--limit N]
|
|
1516
|
-
seclai evals agent-runs <agentId> [--page N] [--limit N]
|
|
1517
|
-
seclai evals non-manual-summary <agentId>
|
|
1518
|
-
\`\`\`
|
|
1519
|
-
|
|
1520
|
-
### Solutions
|
|
2202
|
+
## Catalog
|
|
1521
2203
|
|
|
1522
2204
|
\`\`\`bash
|
|
1523
|
-
seclai
|
|
1524
|
-
seclai
|
|
1525
|
-
seclai
|
|
1526
|
-
|
|
1527
|
-
|
|
2205
|
+
seclai models list [--provider <name>] [--supports-tool-use] [--supports-thinking]
|
|
2206
|
+
seclai models list [--supports-input-media <media>] [--supports-output-media <media>]
|
|
2207
|
+
seclai models get <modelId>
|
|
2208
|
+
|
|
2209
|
+
# each media-generation modality and tier, with its model and cost
|
|
2210
|
+
seclai models tiers
|
|
1528
2211
|
\`\`\`
|
|
1529
2212
|
|
|
1530
|
-
|
|
2213
|
+
The capability flags compose, so \`--supports-tool-use --supports-thinking\`
|
|
2214
|
+
returns only models with both. \`--supports-input-media\` / \`--supports-output-media\`
|
|
2215
|
+
take a modality such as \`image\`, \`audio\` or \`video\`.
|
|
2216
|
+
|
|
2217
|
+
## Model alerts
|
|
1531
2218
|
|
|
1532
2219
|
\`\`\`bash
|
|
1533
|
-
|
|
1534
|
-
seclai
|
|
1535
|
-
seclai
|
|
2220
|
+
seclai models alerts list [--page N] [--limit N]
|
|
2221
|
+
seclai models alerts mark-read <alertId>
|
|
2222
|
+
seclai models alerts mark-all-read
|
|
2223
|
+
seclai models alerts unread-count
|
|
1536
2224
|
\`\`\`
|
|
1537
2225
|
|
|
1538
|
-
|
|
2226
|
+
These are catalog alerts \u2014 deprecations, price changes, new models \u2014 not the
|
|
2227
|
+
account alerts in [alerts.md](alerts.md).
|
|
1539
2228
|
|
|
1540
|
-
|
|
1541
|
-
seclai solutions convos list <solutionId>
|
|
1542
|
-
seclai solutions convos add <solutionId> --json '{"message":"How should I structure this?"}'
|
|
1543
|
-
seclai solutions convos mark <solutionId> <conversationId> --json '{"accepted":true}'
|
|
2229
|
+
## Recommendations
|
|
1544
2230
|
|
|
1545
|
-
|
|
1546
|
-
seclai
|
|
1547
|
-
seclai solutions ai source <solutionId> --user-input "Create a file source for PDFs"
|
|
1548
|
-
seclai solutions ai accept <solutionId> <conversationId> --json '{"accepted":true}'
|
|
1549
|
-
seclai solutions ai decline <solutionId> <conversationId>
|
|
2231
|
+
\`\`\`bash
|
|
2232
|
+
seclai models recommendations <modelId>
|
|
1550
2233
|
\`\`\`
|
|
1551
2234
|
|
|
1552
|
-
|
|
2235
|
+
Suggests replacements for a model, which is how you act on a deprecation alert.
|
|
2236
|
+
|
|
2237
|
+
## Playground experiments
|
|
1553
2238
|
|
|
1554
2239
|
\`\`\`bash
|
|
1555
|
-
seclai
|
|
1556
|
-
seclai
|
|
1557
|
-
seclai
|
|
1558
|
-
seclai
|
|
1559
|
-
seclai
|
|
1560
|
-
seclai alerts unsubscribe <alertId>
|
|
2240
|
+
seclai models experiments list [--days N] [--start-date <date>] [--end-date <date>] [--limit N] [--offset N]
|
|
2241
|
+
seclai models experiments create --json '{"model_ids":["gpt-4o"],"prompt":"Compare responses"}'
|
|
2242
|
+
seclai models experiments get <experimentId>
|
|
2243
|
+
seclai models experiments cancel <experimentId>
|
|
2244
|
+
seclai models experiments delete <experimentId> # soft-delete, preserves audit history
|
|
1561
2245
|
\`\`\`
|
|
1562
2246
|
|
|
1563
|
-
|
|
2247
|
+
\`create\` takes several \`model_ids\` and runs the same prompt against each, which
|
|
2248
|
+
is the point \u2014 side-by-side comparison. \`cancel\` stops a running experiment;
|
|
2249
|
+
\`delete\` soft-deletes a finished one.
|
|
2250
|
+
` },
|
|
2251
|
+
{ name: "references/setup.md", content: `# Setup: authentication, profiles, API version, editor integration
|
|
2252
|
+
|
|
2253
|
+
## SSO authentication
|
|
1564
2254
|
|
|
1565
2255
|
\`\`\`bash
|
|
1566
|
-
seclai
|
|
1567
|
-
seclai
|
|
1568
|
-
seclai
|
|
1569
|
-
seclai
|
|
1570
|
-
seclai alerts configs delete <configId>
|
|
2256
|
+
seclai auth login [--port <port>] [--no-browser] # OAuth2 + PKCE in the browser
|
|
2257
|
+
seclai auth status # active profile's auth state
|
|
2258
|
+
seclai auth refresh # refresh the token manually
|
|
2259
|
+
seclai auth logout # clear cached tokens
|
|
1571
2260
|
\`\`\`
|
|
1572
2261
|
|
|
1573
|
-
|
|
2262
|
+
Tokens are cached under the config directory and refreshed automatically, so
|
|
2263
|
+
\`auth refresh\` is only needed to force it. An API key in \`SECLAI_API_KEY\` takes a
|
|
2264
|
+
different path entirely and needs none of this.
|
|
2265
|
+
|
|
2266
|
+
## Profiles
|
|
1574
2267
|
|
|
1575
2268
|
\`\`\`bash
|
|
1576
|
-
seclai
|
|
1577
|
-
seclai
|
|
2269
|
+
seclai configure sso [--profile-name <name>] # interactive: domain, client ID, region, account ID
|
|
2270
|
+
seclai configure list # every configured profile
|
|
1578
2271
|
\`\`\`
|
|
1579
2272
|
|
|
1580
|
-
|
|
2273
|
+
Profiles live in \`~/.seclai/config\` (override with \`--config-dir\` or
|
|
2274
|
+
\`SECLAI_CONFIG_DIR\`). Select one per invocation with \`--profile <name>\`, or set
|
|
2275
|
+
\`SECLAI_PROFILE\`.
|
|
2276
|
+
|
|
2277
|
+
## API version
|
|
1581
2278
|
|
|
1582
2279
|
\`\`\`bash
|
|
1583
|
-
seclai
|
|
1584
|
-
seclai
|
|
1585
|
-
seclai
|
|
1586
|
-
seclai governance ai decline <conversationId>
|
|
2280
|
+
seclai api-version get # what version does a request resolve to?
|
|
2281
|
+
seclai api-version set <date> # pin the account \u2014 affects every client
|
|
2282
|
+
seclai api-version clear # remove the pin
|
|
1587
2283
|
\`\`\`
|
|
1588
2284
|
|
|
1589
|
-
|
|
2285
|
+
\`set\` and \`clear\` change the account, not just this CLI. To affect only your own
|
|
2286
|
+
invocation, use the \`--api-version\` global option instead.
|
|
2287
|
+
|
|
2288
|
+
## MCP server
|
|
1590
2289
|
|
|
1591
2290
|
\`\`\`bash
|
|
1592
|
-
|
|
1593
|
-
seclai
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
seclai
|
|
2291
|
+
# write Seclai MCP server config into AI coding tool config files
|
|
2292
|
+
seclai mcp configure --key <apiKey> [--target claude-code|cursor|claude-desktop|windsurf|all] [--dir .]
|
|
2293
|
+
|
|
2294
|
+
# print the config JSON for manual setup
|
|
2295
|
+
seclai mcp show [--key <apiKey>]
|
|
1597
2296
|
\`\`\`
|
|
1598
2297
|
|
|
1599
|
-
|
|
2298
|
+
## Skill files
|
|
1600
2299
|
|
|
1601
2300
|
\`\`\`bash
|
|
1602
|
-
|
|
2301
|
+
# install these skill files into AI coding tool directories
|
|
2302
|
+
seclai skills install [--tool copilot|claude|cursor|windsurf|codex|kiro|cline|roo|gemini|antigravity|all] [--dir .]
|
|
1603
2303
|
\`\`\`
|
|
1604
2304
|
|
|
1605
|
-
|
|
2305
|
+
With no \`--tool\`, the target is detected from the directory structure.
|
|
2306
|
+
|
|
2307
|
+
## Shell completion
|
|
1606
2308
|
|
|
1607
2309
|
\`\`\`bash
|
|
1608
|
-
seclai
|
|
1609
|
-
seclai
|
|
1610
|
-
seclai
|
|
1611
|
-
seclai ai solution --user-input "Build a customer support solution"
|
|
1612
|
-
seclai ai memory --user-input "Create a conversation memory bank"
|
|
1613
|
-
seclai ai memory-history
|
|
1614
|
-
seclai ai accept <conversationId> --json '{"accepted":true}'
|
|
1615
|
-
seclai ai decline <conversationId>
|
|
1616
|
-
seclai ai memory-accept <conversationId> --json '{"accepted":true}'
|
|
2310
|
+
seclai completion bash # eval "$(seclai completion bash)" in ~/.bashrc
|
|
2311
|
+
seclai completion zsh # eval "$(seclai completion zsh)" in ~/.zshrc
|
|
2312
|
+
seclai completion fish # seclai completion fish > ~/.config/fish/completions/seclai.fish
|
|
1617
2313
|
\`\`\`
|
|
2314
|
+
` },
|
|
2315
|
+
{ name: "references/solutions.md", content: `# Solutions and governance
|
|
1618
2316
|
|
|
1619
|
-
|
|
2317
|
+
Solutions group agents, knowledge bases and sources into one deliverable.
|
|
2318
|
+
Governance defines the policies applied to agent input and output.
|
|
2319
|
+
|
|
2320
|
+
## Solutions
|
|
1620
2321
|
|
|
1621
2322
|
\`\`\`bash
|
|
1622
|
-
|
|
1623
|
-
seclai
|
|
2323
|
+
seclai solutions list [--page N] [--limit N] [--sort field] [--order asc|desc]
|
|
2324
|
+
seclai solutions create --json '{"name":"Customer Support Solution"}'
|
|
2325
|
+
seclai solutions get <solutionId>
|
|
2326
|
+
seclai solutions update <solutionId> --json '{"name":"Updated"}'
|
|
2327
|
+
seclai solutions delete <solutionId>
|
|
1624
2328
|
\`\`\`
|
|
1625
2329
|
|
|
1626
|
-
|
|
2330
|
+
## Linking resources
|
|
1627
2331
|
|
|
1628
2332
|
\`\`\`bash
|
|
1629
|
-
#
|
|
1630
|
-
seclai
|
|
1631
|
-
|
|
1632
|
-
# show the MCP config JSON snippet
|
|
1633
|
-
seclai mcp show [--key <apiKey>]
|
|
2333
|
+
# each flag takes a JSON array of IDs
|
|
2334
|
+
seclai solutions link <solutionId> --agents '["agentId1"]' --kb '["kbId1"]' --sources '["sourceId1"]'
|
|
2335
|
+
seclai solutions unlink <solutionId> --agents '["agentId1"]'
|
|
1634
2336
|
\`\`\`
|
|
1635
2337
|
|
|
1636
|
-
##
|
|
2338
|
+
## Conversations
|
|
1637
2339
|
|
|
1638
2340
|
\`\`\`bash
|
|
1639
|
-
seclai
|
|
1640
|
-
|
|
1641
|
-
seclai
|
|
1642
|
-
seclai sources get <sourceId>
|
|
2341
|
+
seclai solutions convos list <solutionId>
|
|
2342
|
+
seclai solutions convos add <solutionId> --json '{"message":"How should I structure this?"}'
|
|
2343
|
+
seclai solutions convos mark <solutionId> <conversationId> --json '{"accepted":true}'
|
|
1643
2344
|
\`\`\`
|
|
1644
2345
|
|
|
1645
|
-
##
|
|
2346
|
+
## Solution AI
|
|
1646
2347
|
|
|
1647
2348
|
\`\`\`bash
|
|
1648
|
-
seclai
|
|
1649
|
-
seclai
|
|
1650
|
-
seclai
|
|
1651
|
-
seclai
|
|
1652
|
-
seclai
|
|
2349
|
+
seclai solutions ai generate <solutionId> --user-input "Add an FAQ source"
|
|
2350
|
+
seclai solutions ai kb <solutionId> --user-input "Create a knowledge base for docs"
|
|
2351
|
+
seclai solutions ai source <solutionId> --user-input "Create a file source for PDFs"
|
|
2352
|
+
seclai solutions ai accept <solutionId> <conversationId> --json '{"accepted":true}'
|
|
2353
|
+
seclai solutions ai decline <solutionId> <conversationId>
|
|
1653
2354
|
\`\`\`
|
|
1654
2355
|
|
|
1655
|
-
|
|
2356
|
+
\`ai kb\` and \`ai source\` create the resource and link it to the solution in one
|
|
2357
|
+
step, which is why they live here rather than under \`kb\` or \`sources\`.
|
|
2358
|
+
|
|
2359
|
+
## Governance AI
|
|
1656
2360
|
|
|
1657
2361
|
\`\`\`bash
|
|
1658
|
-
|
|
1659
|
-
seclai
|
|
1660
|
-
|
|
1661
|
-
seclai
|
|
1662
|
-
# test the criteria against a run without persisting
|
|
1663
|
-
seclai evals test-draft <agentId> --json '{"criteria":{"name":"Answer Accuracy","eval_type":"llm_judge"},"run_id":"<runId>"}'
|
|
1664
|
-
# create a persisted result
|
|
1665
|
-
seclai evals results create <criteriaId> --json '{"run_id":"<runId>","score":0.95}'
|
|
1666
|
-
# view summary
|
|
1667
|
-
seclai evals criteria summary <criteriaId>
|
|
2362
|
+
seclai governance ai generate --user-input "Create a content safety policy"
|
|
2363
|
+
seclai governance ai list
|
|
2364
|
+
seclai governance ai accept <conversationId>
|
|
2365
|
+
seclai governance ai decline <conversationId>
|
|
1668
2366
|
\`\`\`
|
|
1669
2367
|
|
|
1670
|
-
|
|
2368
|
+
A generated policy is a proposal until accepted \u2014 \`generate\` alone changes
|
|
2369
|
+
nothing.
|
|
2370
|
+
|
|
2371
|
+
## Example: solution with linked resources
|
|
1671
2372
|
|
|
1672
2373
|
\`\`\`bash
|
|
1673
2374
|
seclai solutions create --json '{"name":"Customer Support"}'
|
|
@@ -1675,30 +2376,15 @@ seclai solutions link <solutionId> --agents '["<agentId>"]' --kb '["<kbId>"]' --
|
|
|
1675
2376
|
seclai solutions get <solutionId>
|
|
1676
2377
|
\`\`\`
|
|
1677
2378
|
|
|
1678
|
-
## Example:
|
|
1679
|
-
|
|
1680
|
-
\`\`\`bash
|
|
1681
|
-
seclai memory create --json '{"name":"User Preferences","type":"general"}'
|
|
1682
|
-
seclai agents create --json '{"name":"Personal Assistant","description":"Remembers user preferences"}'
|
|
1683
|
-
seclai agents ai gen-steps <agentId> --user-input "Build a chat agent that remembers user preferences. Use general memory bank <memoryBankId>"
|
|
1684
|
-
seclai agents ai mark <agentId> <conversationId> --json '{"accepted":true}'
|
|
1685
|
-
\`\`\`
|
|
1686
|
-
|
|
1687
|
-
## Example: Governance policy setup
|
|
2379
|
+
## Example: governance policy setup
|
|
1688
2380
|
|
|
1689
2381
|
\`\`\`bash
|
|
1690
2382
|
seclai governance ai generate --user-input "Create a content safety policy that blocks harmful outputs"
|
|
1691
2383
|
seclai governance ai list
|
|
1692
2384
|
seclai governance ai accept <conversationId>
|
|
1693
2385
|
\`\`\`
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
* **Streaming & event modes** [references/streaming.md](references/streaming.md)
|
|
1698
|
-
* **File uploads & content management** [references/uploads.md](references/uploads.md)
|
|
1699
|
-
* **Evaluations workflow** [references/evaluations.md](references/evaluations.md)
|
|
1700
|
-
`;
|
|
1701
|
-
var STREAMING_REF = `# Streaming Agent Runs
|
|
2386
|
+
` },
|
|
2387
|
+
{ name: "references/streaming.md", content: `# Streaming Agent Runs
|
|
1702
2388
|
|
|
1703
2389
|
## Modes
|
|
1704
2390
|
|
|
@@ -1750,8 +2436,8 @@ seclai agents run <agentId> --json '{"input":"Hello"}'
|
|
|
1750
2436
|
# check later:
|
|
1751
2437
|
seclai agents runs get <runId>
|
|
1752
2438
|
\`\`\`
|
|
1753
|
-
|
|
1754
|
-
|
|
2439
|
+
` },
|
|
2440
|
+
{ name: "references/uploads.md", content: `# File Uploads & Content Management
|
|
1755
2441
|
|
|
1756
2442
|
## Upload to a source
|
|
1757
2443
|
\`\`\`bash
|
|
@@ -1767,11 +2453,21 @@ seclai sources upload-text <sourceId> --json '{"text":"Article content here...",
|
|
|
1767
2453
|
|
|
1768
2454
|
## Upload input for agent runs
|
|
1769
2455
|
\`\`\`bash
|
|
2456
|
+
# Check what files (if any) the agent expects before uploading. requires_uploads
|
|
2457
|
+
# reports whether the agent accepts files; the agent block lists the exact names /
|
|
2458
|
+
# indexes / patterns a run-time batch must satisfy.
|
|
2459
|
+
seclai agents attachment-references <agentId>
|
|
1770
2460
|
seclai agents upload-input <agentId> --file ./input.pdf
|
|
1771
2461
|
seclai agents upload-input <agentId> --file ./data.csv --file-name "report.csv" --mime-type "text/csv"
|
|
1772
2462
|
seclai agents input-status <agentId> <uploadId>
|
|
1773
2463
|
\`\`\`
|
|
1774
2464
|
|
|
2465
|
+
## Download an attachment emitted by a run
|
|
2466
|
+
\`\`\`bash
|
|
2467
|
+
# attachmentId is the URL-safe-base64 storage_key from run output manifests / webhooks.
|
|
2468
|
+
seclai agents runs download-attachment <runId> <attachmentId> --output ./out.pdf
|
|
2469
|
+
\`\`\`
|
|
2470
|
+
|
|
1775
2471
|
## Replace content
|
|
1776
2472
|
\`\`\`bash
|
|
1777
2473
|
# replace with file
|
|
@@ -1792,61 +2488,10 @@ seclai contents get <contentVersionId> --start 0 --end 1000
|
|
|
1792
2488
|
# view embeddings
|
|
1793
2489
|
seclai contents embeddings <contentVersionId> [--page N] [--limit N]
|
|
1794
2490
|
\`\`\`
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
## Step 1: Create evaluation criteria for an agent
|
|
1799
|
-
\`\`\`bash
|
|
1800
|
-
seclai evals criteria create <agentId> --json '{"name":"Answer Accuracy","description":"Does the answer correctly address the question?","eval_type":"llm_judge"}'
|
|
1801
|
-
\`\`\`
|
|
1802
|
-
|
|
1803
|
-
## Step 2: Find runs to evaluate
|
|
1804
|
-
\`\`\`bash
|
|
1805
|
-
# list all runs for an agent
|
|
1806
|
-
seclai agents runs list <agentId> --limit 10
|
|
1807
|
-
|
|
1808
|
-
# or find runs compatible with specific criteria
|
|
1809
|
-
seclai evals compatible-runs <criteriaId> --limit 10
|
|
1810
|
-
\`\`\`
|
|
1811
|
-
|
|
1812
|
-
## Step 3: Test criteria before committing
|
|
1813
|
-
\`\`\`bash
|
|
1814
|
-
seclai evals test-draft <agentId> --json '{"criteria":{"name":"Answer Accuracy","eval_type":"llm_judge","description":"..."},"run_id":"<runId>"}'
|
|
1815
|
-
\`\`\`
|
|
1816
|
-
|
|
1817
|
-
## Step 4: Create evaluation results
|
|
1818
|
-
\`\`\`bash
|
|
1819
|
-
seclai evals results create <criteriaId> --json '{"run_id":"<runId>","score":0.95}'
|
|
1820
|
-
\`\`\`
|
|
1821
|
-
|
|
1822
|
-
## Step 5: Review summaries
|
|
1823
|
-
\`\`\`bash
|
|
1824
|
-
seclai evals criteria summary <criteriaId>
|
|
1825
|
-
seclai evals agent-results <agentId>
|
|
1826
|
-
seclai evals agent-runs <agentId> --limit 20
|
|
1827
|
-
seclai evals non-manual-summary <agentId>
|
|
1828
|
-
\`\`\`
|
|
1829
|
-
|
|
1830
|
-
## Managing criteria
|
|
1831
|
-
\`\`\`bash
|
|
1832
|
-
seclai evals criteria list <agentId>
|
|
1833
|
-
seclai evals criteria get <criteriaId>
|
|
1834
|
-
seclai evals criteria update <criteriaId> --json '{"name":"Updated Name"}'
|
|
1835
|
-
seclai evals criteria delete <criteriaId>
|
|
1836
|
-
\`\`\`
|
|
1837
|
-
|
|
1838
|
-
## Viewing results
|
|
1839
|
-
\`\`\`bash
|
|
1840
|
-
seclai evals results list <criteriaId> [--page N] [--limit N]
|
|
1841
|
-
\`\`\`
|
|
1842
|
-
`;
|
|
2491
|
+
` }
|
|
2492
|
+
];
|
|
1843
2493
|
function getToolConfig(tool, destDir) {
|
|
1844
|
-
const skillFiles =
|
|
1845
|
-
{ name: "SKILL.md", content: SKILL_MD },
|
|
1846
|
-
{ name: "references/streaming.md", content: STREAMING_REF },
|
|
1847
|
-
{ name: "references/uploads.md", content: UPLOADS_REF },
|
|
1848
|
-
{ name: "references/evaluations.md", content: EVALUATIONS_REF }
|
|
1849
|
-
];
|
|
2494
|
+
const skillFiles = SKILL_FILES;
|
|
1850
2495
|
switch (tool) {
|
|
1851
2496
|
case "copilot":
|
|
1852
2497
|
return { dir: join(destDir, ".github", "copilot", "seclai-cli"), files: skillFiles };
|
|
@@ -1888,7 +2533,7 @@ function detectTools(destDir) {
|
|
|
1888
2533
|
if (existsSync(join(destDir, ".antigravity"))) detected.push("antigravity");
|
|
1889
2534
|
return detected;
|
|
1890
2535
|
}
|
|
1891
|
-
function
|
|
2536
|
+
function register15(program, rt) {
|
|
1892
2537
|
const skills = program.command("skills").description("Install Seclai CLI skill files for AI coding tools.");
|
|
1893
2538
|
skills.command("install").description(
|
|
1894
2539
|
"Write Seclai CLI skill/instruction files into the current workspace.\n\nDetected tools: copilot, claude, cursor, windsurf, codex, kiro, cline, roo, gemini, antigravity.\nUse --tool to target a specific tool, or 'all' for all supported tools."
|
|
@@ -1989,7 +2634,7 @@ function detectTargets(destDir) {
|
|
|
1989
2634
|
return false;
|
|
1990
2635
|
});
|
|
1991
2636
|
}
|
|
1992
|
-
function
|
|
2637
|
+
function register16(program, rt) {
|
|
1993
2638
|
const mcp = program.command("mcp").description("Configure the Seclai MCP server for AI coding tools.");
|
|
1994
2639
|
mcp.command("configure").description(
|
|
1995
2640
|
"Add the Seclai MCP server to AI coding tool config files.\n\nTargets: claude-code, cursor, claude-desktop, windsurf.\nUse --target to pick a specific tool, or 'all' for all known targets."
|
|
@@ -2045,237 +2690,156 @@ function register14(program, rt) {
|
|
|
2045
2690
|
}
|
|
2046
2691
|
|
|
2047
2692
|
// src/commands/completion.ts
|
|
2048
|
-
|
|
2693
|
+
function collectTree(program) {
|
|
2694
|
+
const tree = /* @__PURE__ */ new Map();
|
|
2695
|
+
const walk = (cmd, prefix) => {
|
|
2696
|
+
const children = [];
|
|
2697
|
+
for (const sub of cmd.commands) {
|
|
2698
|
+
const name = sub.name();
|
|
2699
|
+
if (name === "help") continue;
|
|
2700
|
+
children.push({ name, description: sub.description().replace(/\s+/g, " ").trim() });
|
|
2701
|
+
walk(sub, [...prefix, name]);
|
|
2702
|
+
}
|
|
2703
|
+
if (children.length > 0) tree.set(prefix.join(" "), children);
|
|
2704
|
+
};
|
|
2705
|
+
walk(program, []);
|
|
2706
|
+
return tree;
|
|
2707
|
+
}
|
|
2708
|
+
function globalOptions(program) {
|
|
2709
|
+
return program.options.filter((o) => o.long && o.long !== "--help").map((o) => ({
|
|
2710
|
+
flag: o.long,
|
|
2711
|
+
description: o.description.replace(/\s+/g, " ").trim()
|
|
2712
|
+
}));
|
|
2713
|
+
}
|
|
2714
|
+
var names = (children) => children.map((c) => c.name).join(" ");
|
|
2715
|
+
function renderBash(tree, options) {
|
|
2716
|
+
const cases = [...tree.entries()].map(([prefix, children]) => ` ${JSON.stringify(prefix)}) __seclai_reply "${names(children)}" ;;`).join("\n");
|
|
2717
|
+
return `#!/usr/bin/env bash
|
|
2049
2718
|
# seclai bash completion \u2014 add to ~/.bashrc:
|
|
2050
2719
|
# eval "$(seclai completion bash)"
|
|
2720
|
+
#
|
|
2721
|
+
# Generated from the command tree by \`seclai completion bash\`. Do not edit.
|
|
2722
|
+
|
|
2723
|
+
__seclai_reply() {
|
|
2724
|
+
COMPREPLY=( $(compgen -W "$1" -- "$__seclai_cur") )
|
|
2725
|
+
}
|
|
2051
2726
|
|
|
2052
2727
|
_seclai_completions() {
|
|
2053
|
-
local
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
#
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
*) COMPREPLY=( $(compgen -W "list create get update delete upload upload-text exports migration" -- "$cur") ); return ;;
|
|
2073
|
-
esac ;;
|
|
2074
|
-
contents) COMPREPLY=( $(compgen -W "get delete upload replace replace-text embeddings" -- "$cur") ); return ;;
|
|
2075
|
-
kb) COMPREPLY=( $(compgen -W "list create get update delete" -- "$cur") ); return ;;
|
|
2076
|
-
memory)
|
|
2077
|
-
case "\${COMP_WORDS[2]}" in
|
|
2078
|
-
ai) COMPREPLY=( $(compgen -W "generate last accept" -- "$cur") ); return ;;
|
|
2079
|
-
*) COMPREPLY=( $(compgen -W "list create get update delete stats agents compact delete-source templates test-compaction test-compaction-standalone ai" -- "$cur") ); return ;;
|
|
2080
|
-
esac ;;
|
|
2081
|
-
evals)
|
|
2082
|
-
case "\${COMP_WORDS[2]}" in
|
|
2083
|
-
criteria) COMPREPLY=( $(compgen -W "list create get update delete summary" -- "$cur") ); return ;;
|
|
2084
|
-
results) COMPREPLY=( $(compgen -W "list create" -- "$cur") ); return ;;
|
|
2085
|
-
*) COMPREPLY=( $(compgen -W "criteria results compatible-runs test-draft agent-results agent-runs non-manual-summary" -- "$cur") ); return ;;
|
|
2086
|
-
esac ;;
|
|
2087
|
-
solutions)
|
|
2088
|
-
case "\${COMP_WORDS[2]}" in
|
|
2089
|
-
convos) COMPREPLY=( $(compgen -W "list add mark" -- "$cur") ); return ;;
|
|
2090
|
-
ai) COMPREPLY=( $(compgen -W "generate kb source accept decline" -- "$cur") ); return ;;
|
|
2091
|
-
*) COMPREPLY=( $(compgen -W "list create get update delete link unlink convos ai" -- "$cur") ); return ;;
|
|
2092
|
-
esac ;;
|
|
2093
|
-
governance)
|
|
2094
|
-
case "\${COMP_WORDS[2]}" in
|
|
2095
|
-
ai) COMPREPLY=( $(compgen -W "generate list accept decline" -- "$cur") ); return ;;
|
|
2096
|
-
*) COMPREPLY=( $(compgen -W "ai" -- "$cur") ); return ;;
|
|
2097
|
-
esac ;;
|
|
2098
|
-
alerts)
|
|
2099
|
-
case "\${COMP_WORDS[2]}" in
|
|
2100
|
-
configs) COMPREPLY=( $(compgen -W "list create get update delete" -- "$cur") ); return ;;
|
|
2101
|
-
prefs) COMPREPLY=( $(compgen -W "list update" -- "$cur") ); return ;;
|
|
2102
|
-
*) COMPREPLY=( $(compgen -W "list get status comment subscribe unsubscribe configs prefs" -- "$cur") ); return ;;
|
|
2103
|
-
esac ;;
|
|
2104
|
-
models)
|
|
2105
|
-
case "\${COMP_WORDS[2]}" in
|
|
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 ;;
|
|
2109
|
-
esac ;;
|
|
2110
|
-
ai) COMPREPLY=( $(compgen -W "feedback kb source solution memory memory-history accept decline memory-accept" -- "$cur") ); return ;;
|
|
2111
|
-
skills) COMPREPLY=( $(compgen -W "install" -- "$cur") ); return ;;
|
|
2112
|
-
mcp) COMPREPLY=( $(compgen -W "configure show" -- "$cur") ); return ;;
|
|
2113
|
-
completion) COMPREPLY=( $(compgen -W "bash zsh fish" -- "$cur") ); return ;;
|
|
2728
|
+
local __seclai_cur prefix i
|
|
2729
|
+
__seclai_cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
2730
|
+
|
|
2731
|
+
# Everything typed so far except the program name and the word being
|
|
2732
|
+
# completed, with flags dropped, joined by spaces.
|
|
2733
|
+
prefix=""
|
|
2734
|
+
for ((i = 1; i < COMP_CWORD; i++)); do
|
|
2735
|
+
case "\${COMP_WORDS[i]}" in
|
|
2736
|
+
-*) continue ;;
|
|
2737
|
+
esac
|
|
2738
|
+
if [ -z "$prefix" ]; then
|
|
2739
|
+
prefix="\${COMP_WORDS[i]}"
|
|
2740
|
+
else
|
|
2741
|
+
prefix="$prefix \${COMP_WORDS[i]}"
|
|
2742
|
+
fi
|
|
2743
|
+
done
|
|
2744
|
+
|
|
2745
|
+
case "$__seclai_cur" in
|
|
2746
|
+
-*) __seclai_reply "${options.map((o) => o.flag).join(" ")}"; return ;;
|
|
2114
2747
|
esac
|
|
2115
2748
|
|
|
2116
|
-
|
|
2749
|
+
case "$prefix" in
|
|
2750
|
+
${cases}
|
|
2751
|
+
*) COMPREPLY=() ;;
|
|
2752
|
+
esac
|
|
2117
2753
|
}
|
|
2118
2754
|
|
|
2119
2755
|
complete -F _seclai_completions seclai
|
|
2120
2756
|
`;
|
|
2121
|
-
|
|
2757
|
+
}
|
|
2758
|
+
function renderZsh(tree, options) {
|
|
2759
|
+
const cases = [...tree.entries()].map(([prefix, children]) => {
|
|
2760
|
+
const described = children.map((c) => `'${c.name}:${c.description.replace(/['`$]/g, "")}'`).join(" ");
|
|
2761
|
+
return ` ${JSON.stringify(prefix)}) sub=(${described}) ;;`;
|
|
2762
|
+
}).join("\n");
|
|
2763
|
+
const opts = options.map((o) => `'${o.flag}[${o.description.replace(/['\]`$]/g, "")}]'`).join(" ");
|
|
2764
|
+
return `#compdef seclai
|
|
2122
2765
|
# seclai zsh completion \u2014 add to ~/.zshrc:
|
|
2123
2766
|
# eval "$(seclai completion zsh)"
|
|
2767
|
+
#
|
|
2768
|
+
# Generated from the command tree by \`seclai completion zsh\`. Do not edit.
|
|
2124
2769
|
|
|
2125
2770
|
_seclai() {
|
|
2126
|
-
local
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
'
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
'help:Display help for command'
|
|
2144
|
-
)
|
|
2145
|
-
|
|
2146
|
-
_arguments -C \\
|
|
2147
|
-
'--api-key[Seclai API key]:key' \\
|
|
2148
|
-
'--compact[Output compact JSON]' \\
|
|
2149
|
-
'-V[Output version]' \\
|
|
2150
|
-
'-h[Display help]' \\
|
|
2151
|
-
'1:command:->cmd' \\
|
|
2152
|
-
'*::arg:->args'
|
|
2153
|
-
|
|
2154
|
-
case $state in
|
|
2155
|
-
cmd) _describe 'command' commands ;;
|
|
2156
|
-
args)
|
|
2157
|
-
case \${words[1]} in
|
|
2158
|
-
agents)
|
|
2159
|
-
local -a sub=(list create get update delete run runs def export preview-import upload-input input-status attachment-references ai)
|
|
2160
|
-
_describe 'subcommand' sub ;;
|
|
2161
|
-
sources|source)
|
|
2162
|
-
local -a sub=(list create get update delete upload upload-text exports migration)
|
|
2163
|
-
_describe 'subcommand' sub ;;
|
|
2164
|
-
contents)
|
|
2165
|
-
local -a sub=(get delete upload replace replace-text embeddings)
|
|
2166
|
-
_describe 'subcommand' sub ;;
|
|
2167
|
-
kb)
|
|
2168
|
-
local -a sub=(list create get update delete)
|
|
2169
|
-
_describe 'subcommand' sub ;;
|
|
2170
|
-
memory)
|
|
2171
|
-
local -a sub=(list create get update delete stats agents compact delete-source templates test-compaction test-compaction-standalone ai)
|
|
2172
|
-
_describe 'subcommand' sub ;;
|
|
2173
|
-
evals)
|
|
2174
|
-
local -a sub=(criteria results compatible-runs test-draft agent-results agent-runs non-manual-summary)
|
|
2175
|
-
_describe 'subcommand' sub ;;
|
|
2176
|
-
solutions)
|
|
2177
|
-
local -a sub=(list create get update delete link unlink convos ai)
|
|
2178
|
-
_describe 'subcommand' sub ;;
|
|
2179
|
-
governance)
|
|
2180
|
-
local -a sub=(ai)
|
|
2181
|
-
_describe 'subcommand' sub ;;
|
|
2182
|
-
alerts)
|
|
2183
|
-
local -a sub=(list get status comment subscribe unsubscribe configs prefs)
|
|
2184
|
-
_describe 'subcommand' sub ;;
|
|
2185
|
-
models)
|
|
2186
|
-
local -a sub=(alerts recommendations experiments)
|
|
2187
|
-
_describe 'subcommand' sub ;;
|
|
2188
|
-
ai)
|
|
2189
|
-
local -a sub=(feedback kb source solution memory memory-history accept decline memory-accept)
|
|
2190
|
-
_describe 'subcommand' sub ;;
|
|
2191
|
-
skills)
|
|
2192
|
-
local -a sub=(install)
|
|
2193
|
-
_describe 'subcommand' sub ;;
|
|
2194
|
-
mcp)
|
|
2195
|
-
local -a sub=(configure show)
|
|
2196
|
-
_describe 'subcommand' sub ;;
|
|
2197
|
-
completion)
|
|
2198
|
-
local -a sub=(bash zsh fish)
|
|
2199
|
-
_describe 'shell' sub ;;
|
|
2200
|
-
esac ;;
|
|
2771
|
+
local prefix cur
|
|
2772
|
+
local -a sub opts
|
|
2773
|
+
cur="\${words[CURRENT]}"
|
|
2774
|
+
|
|
2775
|
+
opts=(${opts})
|
|
2776
|
+
if [[ "$cur" == -* ]]; then
|
|
2777
|
+
_describe 'option' opts
|
|
2778
|
+
return
|
|
2779
|
+
fi
|
|
2780
|
+
|
|
2781
|
+
# Words typed so far, minus the program name, the word being completed, and
|
|
2782
|
+
# any flags.
|
|
2783
|
+
prefix="\${(j: :)\${(M)words[2,CURRENT-1]:#[^-]*}}"
|
|
2784
|
+
|
|
2785
|
+
case "$prefix" in
|
|
2786
|
+
${cases}
|
|
2787
|
+
*) return ;;
|
|
2201
2788
|
esac
|
|
2789
|
+
|
|
2790
|
+
_describe 'command' sub
|
|
2202
2791
|
}
|
|
2203
2792
|
|
|
2204
|
-
_seclai
|
|
2793
|
+
compdef _seclai seclai
|
|
2205
2794
|
`;
|
|
2206
|
-
|
|
2795
|
+
}
|
|
2796
|
+
function renderFish(tree, options) {
|
|
2797
|
+
const lines = [...tree.entries()].map(
|
|
2798
|
+
([prefix, children]) => children.map(
|
|
2799
|
+
(c) => `complete -c seclai -f -n "__seclai_at '${prefix}'" -a "${c.name}" -d "${c.description.replace(/["$`]/g, "")}"`
|
|
2800
|
+
).join("\n")
|
|
2801
|
+
).join("\n");
|
|
2802
|
+
const opts = options.map(
|
|
2803
|
+
(o) => `complete -c seclai -l ${o.flag.replace(/^--/, "")} -d "${o.description.replace(/["$`]/g, "")}"`
|
|
2804
|
+
).join("\n");
|
|
2805
|
+
return `# seclai fish completion \u2014 save to your completions directory:
|
|
2207
2806
|
# seclai completion fish > ~/.config/fish/completions/seclai.fish
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
# agents
|
|
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"
|
|
2230
|
-
|
|
2231
|
-
# sources
|
|
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"
|
|
2233
|
-
|
|
2234
|
-
# contents
|
|
2235
|
-
complete -c seclai -n "__fish_seen_subcommand_from contents; and not __fish_seen_subcommand_from get delete upload replace replace-text embeddings" -f -a "get delete upload replace replace-text embeddings"
|
|
2236
|
-
|
|
2237
|
-
# kb
|
|
2238
|
-
complete -c seclai -n "__fish_seen_subcommand_from kb; and not __fish_seen_subcommand_from list create get update delete" -f -a "list create get update delete"
|
|
2239
|
-
|
|
2240
|
-
# memory
|
|
2241
|
-
complete -c seclai -n "__fish_seen_subcommand_from memory; and not __fish_seen_subcommand_from list create get update delete stats agents compact delete-source templates test-compaction test-compaction-standalone ai" -f -a "list create get update delete stats agents compact delete-source templates test-compaction test-compaction-standalone ai"
|
|
2242
|
-
|
|
2243
|
-
# evals
|
|
2244
|
-
complete -c seclai -n "__fish_seen_subcommand_from evals; and not __fish_seen_subcommand_from criteria results compatible-runs test-draft agent-results agent-runs non-manual-summary" -f -a "criteria results compatible-runs test-draft agent-results agent-runs non-manual-summary"
|
|
2245
|
-
|
|
2246
|
-
# solutions
|
|
2247
|
-
complete -c seclai -n "__fish_seen_subcommand_from solutions; and not __fish_seen_subcommand_from list create get update delete link unlink convos ai" -f -a "list create get update delete link unlink convos ai"
|
|
2248
|
-
|
|
2249
|
-
# governance
|
|
2250
|
-
complete -c seclai -n "__fish_seen_subcommand_from governance; and not __fish_seen_subcommand_from ai" -f -a "ai"
|
|
2251
|
-
|
|
2252
|
-
# alerts
|
|
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"
|
|
2254
|
-
|
|
2255
|
-
# models
|
|
2256
|
-
complete -c seclai -n "__fish_seen_subcommand_from models; and not __fish_seen_subcommand_from alerts recommendations experiments" -f -a "alerts recommendations experiments"
|
|
2257
|
-
|
|
2258
|
-
# ai
|
|
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"
|
|
2260
|
-
|
|
2261
|
-
# skills
|
|
2262
|
-
complete -c seclai -n "__fish_seen_subcommand_from skills; and not __fish_seen_subcommand_from install" -f -a "install"
|
|
2263
|
-
|
|
2264
|
-
# mcp
|
|
2265
|
-
complete -c seclai -n "__fish_seen_subcommand_from mcp; and not __fish_seen_subcommand_from configure show" -f -a "configure show"
|
|
2266
|
-
|
|
2267
|
-
# completion
|
|
2268
|
-
complete -c seclai -n "__fish_seen_subcommand_from completion; and not __fish_seen_subcommand_from bash zsh fish" -f -a "bash zsh fish"
|
|
2807
|
+
#
|
|
2808
|
+
# Generated from the command tree by \`seclai completion fish\`. Do not edit.
|
|
2809
|
+
|
|
2810
|
+
function __seclai_prefix
|
|
2811
|
+
set -l toks (commandline -opc)
|
|
2812
|
+
set -e toks[1]
|
|
2813
|
+
set -l out
|
|
2814
|
+
for t in $toks
|
|
2815
|
+
if not string match -q -- '-*' $t
|
|
2816
|
+
set -a out $t
|
|
2817
|
+
end
|
|
2818
|
+
end
|
|
2819
|
+
string join ' ' $out
|
|
2820
|
+
end
|
|
2821
|
+
|
|
2822
|
+
function __seclai_at
|
|
2823
|
+
test (__seclai_prefix) = "$argv[1]"
|
|
2824
|
+
end
|
|
2825
|
+
|
|
2826
|
+
${lines}
|
|
2269
2827
|
|
|
2270
2828
|
# Global options
|
|
2271
|
-
|
|
2272
|
-
complete -c seclai -l compact -d "Output compact JSON"
|
|
2273
|
-
complete -c seclai -s V -l version -d "Output version"
|
|
2829
|
+
${opts}
|
|
2274
2830
|
`;
|
|
2275
|
-
|
|
2276
|
-
function
|
|
2277
|
-
const
|
|
2278
|
-
|
|
2831
|
+
}
|
|
2832
|
+
function renderCompletion(shell, program) {
|
|
2833
|
+
const tree = collectTree(program);
|
|
2834
|
+
const options = globalOptions(program);
|
|
2835
|
+
if (shell === "bash") return renderBash(tree, options);
|
|
2836
|
+
if (shell === "zsh") return renderZsh(tree, options);
|
|
2837
|
+
if (shell === "fish") return renderFish(tree, options);
|
|
2838
|
+
return void 0;
|
|
2839
|
+
}
|
|
2840
|
+
function register17(program, rt) {
|
|
2841
|
+
program.command("completion").description("Generate shell completion scripts.").argument("<shell>", "Shell type: bash, zsh, or fish.").action((shell) => {
|
|
2842
|
+
const script = renderCompletion(shell, program);
|
|
2279
2843
|
if (!script) {
|
|
2280
2844
|
rt.writeErr(`Unknown shell "${shell}". Use: bash, zsh, or fish.
|
|
2281
2845
|
`);
|
|
@@ -2447,7 +3011,7 @@ async function loadProfile(rt, opts) {
|
|
|
2447
3011
|
const profile = await loadSsoProfile(configDir, profileName);
|
|
2448
3012
|
return { profile, profileName, configDir };
|
|
2449
3013
|
}
|
|
2450
|
-
function
|
|
3014
|
+
function register18(program, rt) {
|
|
2451
3015
|
const group = program.command("auth").description("SSO authentication (login/logout/status/refresh).");
|
|
2452
3016
|
group.command("login").description("Authenticate via SSO using Authorization Code + PKCE flow.").option("--port <port>", "Local callback port", String(DEFAULT_CALLBACK_PORT)).option("--no-browser", "Print the URL instead of opening a browser").action(async (opts) => {
|
|
2453
3017
|
await run(rt, async () => {
|
|
@@ -2621,7 +3185,7 @@ function resolveConfigDir(opts) {
|
|
|
2621
3185
|
const home = process4.env.HOME ?? process4.env.USERPROFILE ?? "";
|
|
2622
3186
|
return join4(home, ".seclai");
|
|
2623
3187
|
}
|
|
2624
|
-
function
|
|
3188
|
+
function register19(program, rt) {
|
|
2625
3189
|
const group = program.command("configure").description("Configure CLI profiles and settings.");
|
|
2626
3190
|
group.command("sso").description("Configure an SSO profile with optional overrides. Defaults to production Seclai SSO.").option("--profile-name <name>", "Profile name to configure (default: from --profile flag)").action(async (opts) => {
|
|
2627
3191
|
await run(rt, async () => {
|
|
@@ -2735,7 +3299,7 @@ function escapeRegExp(s) {
|
|
|
2735
3299
|
|
|
2736
3300
|
// src/cli.ts
|
|
2737
3301
|
function createProgram(rt = defaultRuntime()) {
|
|
2738
|
-
const program = new
|
|
3302
|
+
const program = new Command4();
|
|
2739
3303
|
const cliVersion = getCliVersion();
|
|
2740
3304
|
program.name("seclai").description(
|
|
2741
3305
|
`Seclai Command Line Interface (v${cliVersion})
|
|
@@ -2755,6 +3319,12 @@ All commands return JSON to stdout, making it easy to pipe into jq or other tool
|
|
|
2755
3319
|
).option(
|
|
2756
3320
|
"--config-dir <path>",
|
|
2757
3321
|
"Config directory (defaults to SECLAI_CONFIG_DIR, then ~/.seclai)."
|
|
3322
|
+
).option(
|
|
3323
|
+
"--api-version <date>",
|
|
3324
|
+
"Opt into dated API changes released on or before this YYYY-MM-DD (defaults to SECLAI_API_VERSION; omitted means the account default)."
|
|
3325
|
+
).option(
|
|
3326
|
+
"--allow-unknown-api-version",
|
|
3327
|
+
"Send an --api-version this CLI was not built against instead of rejecting it."
|
|
2758
3328
|
).option(
|
|
2759
3329
|
"--compact",
|
|
2760
3330
|
"Output compact JSON (no indentation)."
|
|
@@ -2767,6 +3337,7 @@ Environment:
|
|
|
2767
3337
|
SECLAI_API_URL Override API base URL (default: https://api.seclai.com)
|
|
2768
3338
|
SECLAI_PROFILE Default SSO profile (alternative to --profile)
|
|
2769
3339
|
SECLAI_CONFIG_DIR Config directory (alternative to --config-dir)
|
|
3340
|
+
SECLAI_API_VERSION Dated API version (alternative to --api-version)
|
|
2770
3341
|
|
|
2771
3342
|
Examples:
|
|
2772
3343
|
seclai agents list
|
|
@@ -2786,6 +3357,13 @@ Examples:
|
|
|
2786
3357
|
program.exitOverride();
|
|
2787
3358
|
program.hook("preAction", (thisCommand) => {
|
|
2788
3359
|
const globalOpts = thisCommand.opts();
|
|
3360
|
+
if (typeof globalOpts.apiVersion === "string" && globalOpts.apiVersion.trim().length === 0) {
|
|
3361
|
+
warnDeprecated(
|
|
3362
|
+
rt,
|
|
3363
|
+
"--api-version was given an empty value and is being ignored. Pass a YYYY-MM-DD date, or omit the flag to use the account default.",
|
|
3364
|
+
"rejected"
|
|
3365
|
+
);
|
|
3366
|
+
}
|
|
2789
3367
|
rt.compact = Boolean(globalOpts.compact);
|
|
2790
3368
|
});
|
|
2791
3369
|
register(program, rt);
|
|
@@ -2805,6 +3383,8 @@ Examples:
|
|
|
2805
3383
|
register15(program, rt);
|
|
2806
3384
|
register16(program, rt);
|
|
2807
3385
|
register17(program, rt);
|
|
3386
|
+
register18(program, rt);
|
|
3387
|
+
register19(program, rt);
|
|
2808
3388
|
return program;
|
|
2809
3389
|
}
|
|
2810
3390
|
async function runCli(argv, rt = defaultRuntime()) {
|
|
@@ -2851,4 +3431,3 @@ export {
|
|
|
2851
3431
|
createProgram,
|
|
2852
3432
|
runCli
|
|
2853
3433
|
};
|
|
2854
|
-
//# sourceMappingURL=cli.js.map
|