@seclai/cli 1.4.0 → 1.5.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 +161 -0
- package/README.md +150 -5
- package/dist/cli.js +988 -308
- package/dist/cli.js.map +1 -1
- package/package.json +5 -3
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 Command3 } 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";
|
|
@@ -78,6 +79,10 @@ function createClient(opts) {
|
|
|
78
79
|
if (opts.profile !== void 0) seclaiOpts.profile = opts.profile;
|
|
79
80
|
if (opts.configDir !== void 0) seclaiOpts.configDir = opts.configDir;
|
|
80
81
|
if (opts.accountId !== void 0) seclaiOpts.accountId = opts.accountId;
|
|
82
|
+
const envVersion = process2.env.SECLAI_API_VERSION;
|
|
83
|
+
const version = opts.apiVersion ?? (envVersion && envVersion.length > 0 ? envVersion : void 0);
|
|
84
|
+
if (version !== void 0) seclaiOpts.apiVersion = version;
|
|
85
|
+
if (opts.allowUnknownApiVersion) seclaiOpts.allowUnknownApiVersion = true;
|
|
81
86
|
const envUrl = process2.env.SECLAI_API_URL;
|
|
82
87
|
seclaiOpts.baseUrl = envUrl && envUrl.length > 0 ? envUrl : "https://api.seclai.com";
|
|
83
88
|
return new Seclai(seclaiOpts);
|
|
@@ -87,6 +92,10 @@ function printJson(rt, value) {
|
|
|
87
92
|
rt.writeOut(`${JSON.stringify(value, null, indent)}
|
|
88
93
|
`);
|
|
89
94
|
}
|
|
95
|
+
function warnDeprecated(rt, message) {
|
|
96
|
+
rt.writeErr(`warning: ${message} This will be rejected in a future release.
|
|
97
|
+
`);
|
|
98
|
+
}
|
|
90
99
|
function printError(rt, err) {
|
|
91
100
|
if (err instanceof SeclaiAPIValidationError) {
|
|
92
101
|
rt.writeErr(`${err.name}: ${err.message}
|
|
@@ -134,6 +143,25 @@ async function run(rt, main) {
|
|
|
134
143
|
rt.setExitCode(1);
|
|
135
144
|
}
|
|
136
145
|
}
|
|
146
|
+
function parseNumber(value) {
|
|
147
|
+
const parsed = value.trim() === "" ? Number.NaN : Number(value);
|
|
148
|
+
if (!Number.isFinite(parsed)) {
|
|
149
|
+
throw new InvalidArgumentError("Expected a number.");
|
|
150
|
+
}
|
|
151
|
+
return parsed;
|
|
152
|
+
}
|
|
153
|
+
function withLimitOption(cmd) {
|
|
154
|
+
return cmd.option("--limit <n>", "Page size.", parseNumber);
|
|
155
|
+
}
|
|
156
|
+
function withOffsetListOptions(cmd) {
|
|
157
|
+
return withLimitOption(cmd).option("--offset <n>", "Number of items to skip.", parseNumber);
|
|
158
|
+
}
|
|
159
|
+
function offsetListOpts(opts) {
|
|
160
|
+
const o = {};
|
|
161
|
+
if (opts.limit !== void 0) o.limit = opts.limit;
|
|
162
|
+
if (opts.offset !== void 0) o.offset = opts.offset;
|
|
163
|
+
return o;
|
|
164
|
+
}
|
|
137
165
|
function withJsonInputOptions(cmd) {
|
|
138
166
|
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
167
|
}
|
|
@@ -176,7 +204,7 @@ async function readAiInput(rt, opts) {
|
|
|
176
204
|
// src/commands/agents.ts
|
|
177
205
|
function register(program, rt) {
|
|
178
206
|
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.",
|
|
207
|
+
agents.command("list").description("List agents.").option("--page <n>", "Page number.", parseNumber).option("--limit <n>", "Page size.", parseNumber).action(async (opts) => {
|
|
180
208
|
await run(rt, async () => {
|
|
181
209
|
const client = createClient(program.opts());
|
|
182
210
|
printJson(rt, await client.listAgents(listOpts(opts)));
|
|
@@ -209,7 +237,40 @@ function register(program, rt) {
|
|
|
209
237
|
printJson(rt, { ok: true });
|
|
210
238
|
});
|
|
211
239
|
});
|
|
212
|
-
agents.command("
|
|
240
|
+
agents.command("disable").description("Pause an agent across every trigger path (API, schedule, email, sub-agent calls).").argument("<agentId>", "Agent ID.").action(async (agentId) => {
|
|
241
|
+
await run(rt, async () => {
|
|
242
|
+
const client = createClient(program.opts());
|
|
243
|
+
printJson(rt, await client.disableAgent(agentId));
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
agents.command("enable").description("Resume a paused agent.").argument("<agentId>", "Agent ID.").action(async (agentId) => {
|
|
247
|
+
await run(rt, async () => {
|
|
248
|
+
const client = createClient(program.opts());
|
|
249
|
+
printJson(rt, await client.enableAgent(agentId));
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
agents.command("callers").description("List the live agents that call this agent via a call_agent step.").argument("<agentId>", "Agent ID.").action(async (agentId) => {
|
|
253
|
+
await run(rt, async () => {
|
|
254
|
+
const client = createClient(program.opts());
|
|
255
|
+
printJson(rt, await client.getAgentCallers(agentId));
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
const triggers = agents.command("triggers").description("Agent trigger configuration.");
|
|
259
|
+
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) => {
|
|
260
|
+
await run(rt, async () => {
|
|
261
|
+
const client = createClient(program.opts());
|
|
262
|
+
const body = await readJsonInput(rt, { json: opts.json, jsonFile: opts.jsonFile });
|
|
263
|
+
printJson(
|
|
264
|
+
rt,
|
|
265
|
+
await client.setEmailTriggerConfig(
|
|
266
|
+
agentId,
|
|
267
|
+
triggerId,
|
|
268
|
+
body
|
|
269
|
+
)
|
|
270
|
+
);
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
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
274
|
await run(rt, async () => {
|
|
214
275
|
const client = createClient(program.opts());
|
|
215
276
|
const body = await readJsonInput(rt, { json: opts.json, jsonFile: opts.jsonFile });
|
|
@@ -257,7 +318,7 @@ function register(program, rt) {
|
|
|
257
318
|
});
|
|
258
319
|
});
|
|
259
320
|
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.",
|
|
321
|
+
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
322
|
await run(rt, async () => {
|
|
262
323
|
const client = createClient(program.opts());
|
|
263
324
|
const o = listOpts(opts);
|
|
@@ -274,10 +335,14 @@ function register(program, rt) {
|
|
|
274
335
|
);
|
|
275
336
|
});
|
|
276
337
|
});
|
|
277
|
-
runs.command("delete").description("
|
|
338
|
+
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
339
|
await run(rt, async () => {
|
|
340
|
+
warnDeprecated(
|
|
341
|
+
rt,
|
|
342
|
+
`'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}.`
|
|
343
|
+
);
|
|
279
344
|
const client = createClient(program.opts());
|
|
280
|
-
await client.
|
|
345
|
+
await client.cancelAgentRun(runId);
|
|
281
346
|
printJson(rt, { ok: true });
|
|
282
347
|
});
|
|
283
348
|
});
|
|
@@ -398,10 +463,20 @@ function register(program, rt) {
|
|
|
398
463
|
printJson(rt, await client.generateStepConfig(agentId, body));
|
|
399
464
|
});
|
|
400
465
|
});
|
|
401
|
-
|
|
466
|
+
withOffsetListOptions(
|
|
467
|
+
ai.command("history").description("Get agent AI conversation history for one step type.").argument("<agentId>", "Agent ID.").requiredOption(
|
|
468
|
+
"--step-type <type>",
|
|
469
|
+
"Step type to read history for (e.g. llm). Required by the API."
|
|
470
|
+
).option("--step-id <id>", "Restrict to a single step.")
|
|
471
|
+
).action(async (agentId, opts) => {
|
|
402
472
|
await run(rt, async () => {
|
|
403
473
|
const client = createClient(program.opts());
|
|
404
|
-
|
|
474
|
+
const o = {
|
|
475
|
+
...offsetListOpts(opts),
|
|
476
|
+
stepType: opts.stepType
|
|
477
|
+
};
|
|
478
|
+
if (opts.stepId !== void 0) o.stepId = opts.stepId;
|
|
479
|
+
printJson(rt, await client.getAgentAiConversationHistory(agentId, o));
|
|
405
480
|
});
|
|
406
481
|
});
|
|
407
482
|
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,7 +487,7 @@ function register(program, rt) {
|
|
|
412
487
|
printJson(rt, { ok: true });
|
|
413
488
|
});
|
|
414
489
|
});
|
|
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.",
|
|
490
|
+
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).action(async (agentId, runId, opts) => {
|
|
416
491
|
await run(rt, async () => {
|
|
417
492
|
const client = createClient(program.opts());
|
|
418
493
|
printJson(rt, await client.listRunEvaluationResults(agentId, runId, listOpts(opts)));
|
|
@@ -735,10 +810,16 @@ function register5(program, rt) {
|
|
|
735
810
|
function register6(program, rt) {
|
|
736
811
|
const evals = program.command("evals").description("Manage evaluations.");
|
|
737
812
|
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.",
|
|
813
|
+
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(
|
|
814
|
+
"--paged",
|
|
815
|
+
"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."
|
|
816
|
+
).action(async (agentId, opts) => {
|
|
739
817
|
await run(rt, async () => {
|
|
740
818
|
const client = createClient(program.opts());
|
|
741
|
-
printJson(
|
|
819
|
+
printJson(
|
|
820
|
+
rt,
|
|
821
|
+
opts.paged ? await client.listEvaluationCriteriaPage(agentId, listOpts(opts)) : await client.listEvaluationCriteria(agentId, listOpts(opts))
|
|
822
|
+
);
|
|
742
823
|
});
|
|
743
824
|
});
|
|
744
825
|
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 +856,7 @@ function register6(program, rt) {
|
|
|
775
856
|
});
|
|
776
857
|
});
|
|
777
858
|
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.",
|
|
859
|
+
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
860
|
await run(rt, async () => {
|
|
780
861
|
const client = createClient(program.opts());
|
|
781
862
|
printJson(rt, await client.listEvaluationResults(criteriaId, listOpts(opts)));
|
|
@@ -788,7 +869,7 @@ function register6(program, rt) {
|
|
|
788
869
|
printJson(rt, await client.createEvaluationResult(criteriaId, body));
|
|
789
870
|
});
|
|
790
871
|
});
|
|
791
|
-
evals.command("compatible-runs").description("List runs compatible with criteria.").argument("<criteriaId>", "Criteria ID.").option("--page <n>", "Page number.",
|
|
872
|
+
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
873
|
await run(rt, async () => {
|
|
793
874
|
const client = createClient(program.opts());
|
|
794
875
|
printJson(rt, await client.listCompatibleRuns(criteriaId, listOpts(opts)));
|
|
@@ -801,13 +882,13 @@ function register6(program, rt) {
|
|
|
801
882
|
printJson(rt, await client.testDraftEvaluation(agentId, body));
|
|
802
883
|
});
|
|
803
884
|
});
|
|
804
|
-
evals.command("agent-results").description("List all evaluation results for an agent.").argument("<agentId>", "Agent ID.").option("--page <n>", "Page number.",
|
|
885
|
+
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
886
|
await run(rt, async () => {
|
|
806
887
|
const client = createClient(program.opts());
|
|
807
888
|
printJson(rt, await client.listAgentEvaluationResults(agentId, listOpts(opts)));
|
|
808
889
|
});
|
|
809
890
|
});
|
|
810
|
-
evals.command("agent-runs").description("List evaluation run summaries for an agent.").argument("<agentId>", "Agent ID.").option("--page <n>", "Page number.",
|
|
891
|
+
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
892
|
await run(rt, async () => {
|
|
812
893
|
const client = createClient(program.opts());
|
|
813
894
|
printJson(rt, await client.listEvaluationRuns(agentId, listOpts(opts)));
|
|
@@ -1002,12 +1083,20 @@ function register8(program, rt) {
|
|
|
1002
1083
|
// src/commands/alerts.ts
|
|
1003
1084
|
function register9(program, rt) {
|
|
1004
1085
|
const alerts = program.command("alerts").description("Manage alerts and alert configurations.");
|
|
1005
|
-
alerts.command("list").description("List alerts.").option("--page <n>", "Page number.",
|
|
1086
|
+
alerts.command("list").description("List alerts.").option("--page <n>", "Page number.", parseNumber).option("--limit <n>", "Page size.", parseNumber).option("--status <status>", "Filter by status.").option(
|
|
1087
|
+
"--severity <severity>",
|
|
1088
|
+
"Deprecated and ignored \u2014 GET /alerts declares no severity filter. Filter with jq instead."
|
|
1089
|
+
).action(async (opts) => {
|
|
1006
1090
|
await run(rt, async () => {
|
|
1007
1091
|
const client = createClient(program.opts());
|
|
1008
1092
|
const o = listOpts(opts);
|
|
1009
1093
|
if (opts.status) o.status = opts.status;
|
|
1010
|
-
if (opts.severity
|
|
1094
|
+
if (opts.severity !== void 0) {
|
|
1095
|
+
warnDeprecated(
|
|
1096
|
+
rt,
|
|
1097
|
+
`'alerts list --severity' is ignored \u2014 the API has no severity filter, so it never filtered anything. Filter client-side, e.g. | jq '[.data[] | select(.severity == "high")]'.`
|
|
1098
|
+
);
|
|
1099
|
+
}
|
|
1011
1100
|
printJson(rt, await client.listAlerts(o));
|
|
1012
1101
|
});
|
|
1013
1102
|
});
|
|
@@ -1044,7 +1133,7 @@ function register9(program, rt) {
|
|
|
1044
1133
|
});
|
|
1045
1134
|
});
|
|
1046
1135
|
const configs = alerts.command("configs").description("Alert configurations.");
|
|
1047
|
-
configs.command("list").description("List alert configurations.").option("--page <n>", "Page number.",
|
|
1136
|
+
configs.command("list").description("List alert configurations.").option("--page <n>", "Page number.", parseNumber).option("--limit <n>", "Page size.", parseNumber).action(async (opts) => {
|
|
1048
1137
|
await run(rt, async () => {
|
|
1049
1138
|
const client = createClient(program.opts());
|
|
1050
1139
|
printJson(rt, await client.listAlertConfigs(listOpts(opts)));
|
|
@@ -1093,19 +1182,203 @@ function register9(program, rt) {
|
|
|
1093
1182
|
});
|
|
1094
1183
|
}
|
|
1095
1184
|
|
|
1096
|
-
// src/commands/
|
|
1185
|
+
// src/commands/email.ts
|
|
1097
1186
|
function register10(program, rt) {
|
|
1187
|
+
const email = program.command("email").description("Agent email: sending domains, inbound blocklist, inbound health, and opt-outs.");
|
|
1188
|
+
const domains = email.command("domains").description("Agent-email sending domains.");
|
|
1189
|
+
domains.command("list").description("List the account's email domains and the plan limits for adding more.").action(async () => {
|
|
1190
|
+
await run(rt, async () => {
|
|
1191
|
+
const client = createClient(program.opts());
|
|
1192
|
+
printJson(rt, await client.listEmailDomains());
|
|
1193
|
+
});
|
|
1194
|
+
});
|
|
1195
|
+
domains.command("add").description("Add and provision a new agent-email domain. Returns the DNS records to publish.").requiredOption("--kind <kind>", "'vanity' (a subdomain of seclai.com) or 'custom' (your own domain).").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) => {
|
|
1196
|
+
await run(rt, async () => {
|
|
1197
|
+
const client = createClient(program.opts());
|
|
1198
|
+
const body = {
|
|
1199
|
+
kind: opts.kind,
|
|
1200
|
+
value: opts.value
|
|
1201
|
+
};
|
|
1202
|
+
if (opts.delegated) body.delegated = true;
|
|
1203
|
+
printJson(rt, await client.addEmailDomain(body));
|
|
1204
|
+
});
|
|
1205
|
+
});
|
|
1206
|
+
domains.command("remove").description("Remove a domain and tear down its sending identity and inbound routing.").argument("<domainId>", "Domain ID.").action(async (domainId) => {
|
|
1207
|
+
await run(rt, async () => {
|
|
1208
|
+
const client = createClient(program.opts());
|
|
1209
|
+
printJson(rt, await client.removeEmailDomain(domainId));
|
|
1210
|
+
});
|
|
1211
|
+
});
|
|
1212
|
+
domains.command("verify").description("Run a verification check on a domain immediately.").argument("<domainId>", "Domain ID.").action(async (domainId) => {
|
|
1213
|
+
await run(rt, async () => {
|
|
1214
|
+
const client = createClient(program.opts());
|
|
1215
|
+
printJson(rt, await client.verifyEmailDomain(domainId));
|
|
1216
|
+
});
|
|
1217
|
+
});
|
|
1218
|
+
domains.command("set-primary").description("Promote a verified domain to the account's primary sending domain.").argument("<domainId>", "Domain ID.").action(async (domainId) => {
|
|
1219
|
+
await run(rt, async () => {
|
|
1220
|
+
const client = createClient(program.opts());
|
|
1221
|
+
printJson(rt, await client.setPrimaryEmailDomain(domainId));
|
|
1222
|
+
});
|
|
1223
|
+
});
|
|
1224
|
+
domains.command("use-shared").description("Revert to the shared agent.seclai.com sending and inbound domain.").action(async () => {
|
|
1225
|
+
await run(rt, async () => {
|
|
1226
|
+
const client = createClient(program.opts());
|
|
1227
|
+
await client.useSharedEmailDomain();
|
|
1228
|
+
printJson(rt, { ok: true });
|
|
1229
|
+
});
|
|
1230
|
+
});
|
|
1231
|
+
domains.command("test-email").description("Send a test message from a verified domain to the account owner.").argument("<domainId>", "Domain ID.").action(async (domainId) => {
|
|
1232
|
+
await run(rt, async () => {
|
|
1233
|
+
const client = createClient(program.opts());
|
|
1234
|
+
printJson(rt, await client.sendEmailDomainTestEmail(domainId));
|
|
1235
|
+
});
|
|
1236
|
+
});
|
|
1237
|
+
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) => {
|
|
1238
|
+
await run(rt, async () => {
|
|
1239
|
+
const client = createClient(program.opts());
|
|
1240
|
+
const o = {};
|
|
1241
|
+
if (opts.days !== void 0) o.days = opts.days;
|
|
1242
|
+
if (opts.topSources !== void 0) o.topSources = opts.topSources;
|
|
1243
|
+
printJson(rt, await client.getDmarcSummary(domainId, o));
|
|
1244
|
+
});
|
|
1245
|
+
});
|
|
1246
|
+
const blocked = email.command("blocked").description("Inbound email sender blocklist.");
|
|
1247
|
+
withOffsetListOptions(
|
|
1248
|
+
blocked.command("list").description("List blocked inbound senders (newest first) and the account's auto-block mode.")
|
|
1249
|
+
).action(async (opts) => {
|
|
1250
|
+
await run(rt, async () => {
|
|
1251
|
+
const client = createClient(program.opts());
|
|
1252
|
+
printJson(rt, await client.listBlockedEmailSenders(offsetListOpts(opts)));
|
|
1253
|
+
});
|
|
1254
|
+
});
|
|
1255
|
+
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'.").option("--match-type <type>", "'address' or 'domain'.", "address").option("--note <text>", "Why the sender was blocked.").action(async (opts) => {
|
|
1256
|
+
await run(rt, async () => {
|
|
1257
|
+
const client = createClient(program.opts());
|
|
1258
|
+
const body = {
|
|
1259
|
+
sender_email: opts.senderEmail,
|
|
1260
|
+
match_type: opts.matchType
|
|
1261
|
+
};
|
|
1262
|
+
if (opts.note !== void 0) body.note = opts.note;
|
|
1263
|
+
printJson(rt, await client.blockEmailSender(body));
|
|
1264
|
+
});
|
|
1265
|
+
});
|
|
1266
|
+
blocked.command("remove").description("Unblock a sender.").argument("<blockedId>", "Blocklist entry ID.").action(async (blockedId) => {
|
|
1267
|
+
await run(rt, async () => {
|
|
1268
|
+
const client = createClient(program.opts());
|
|
1269
|
+
await client.unblockEmailSender(blockedId);
|
|
1270
|
+
printJson(rt, { ok: true });
|
|
1271
|
+
});
|
|
1272
|
+
});
|
|
1273
|
+
blocked.command("auto-block-mode").description("Set whether a governance BLOCK on an authenticated sender auto-adds them to the blocklist.").argument("<mode>", "One of 'disabled', 'input', or 'input_and_output'.").action(async (mode) => {
|
|
1274
|
+
await run(rt, async () => {
|
|
1275
|
+
const client = createClient(program.opts());
|
|
1276
|
+
printJson(rt, await client.setAutoBlockMode({ mode }));
|
|
1277
|
+
});
|
|
1278
|
+
});
|
|
1279
|
+
const inbound = email.command("inbound").description("Inbound email health and queue control.");
|
|
1280
|
+
inbound.command("status").description("Get inbound-email quota usage, pause state, and queued-run counts.").action(async () => {
|
|
1281
|
+
await run(rt, async () => {
|
|
1282
|
+
const client = createClient(program.opts());
|
|
1283
|
+
printJson(rt, await client.getInboundEmailStatus());
|
|
1284
|
+
});
|
|
1285
|
+
});
|
|
1286
|
+
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) => {
|
|
1287
|
+
await run(rt, async () => {
|
|
1288
|
+
const client = createClient(program.opts());
|
|
1289
|
+
const o = {};
|
|
1290
|
+
if (opts.agentId !== void 0) o.agentId = opts.agentId;
|
|
1291
|
+
if (opts.limit !== void 0) o.limit = opts.limit;
|
|
1292
|
+
printJson(rt, await client.listInboundEmailRejections(o));
|
|
1293
|
+
});
|
|
1294
|
+
});
|
|
1295
|
+
inbound.command("cancel-queued").description("Fail all of the account's queued (over-quota) inbound-email runs at once.").action(async () => {
|
|
1296
|
+
await run(rt, async () => {
|
|
1297
|
+
const client = createClient(program.opts());
|
|
1298
|
+
printJson(rt, await client.cancelQueuedEmailRuns());
|
|
1299
|
+
});
|
|
1300
|
+
});
|
|
1301
|
+
inbound.command("resume").description("Manually lift the account-wide inbound-email pause.").action(async () => {
|
|
1302
|
+
await run(rt, async () => {
|
|
1303
|
+
const client = createClient(program.opts());
|
|
1304
|
+
printJson(rt, await client.resumeInboundEmail());
|
|
1305
|
+
});
|
|
1306
|
+
});
|
|
1307
|
+
const optouts = email.command("optouts").description("Recipients who opted out of agent email.");
|
|
1308
|
+
withOffsetListOptions(
|
|
1309
|
+
optouts.command("list").description("List agent-email opt-outs.").option("--agent-id <id>", "Restrict to one agent.")
|
|
1310
|
+
).action(async (opts) => {
|
|
1311
|
+
await run(rt, async () => {
|
|
1312
|
+
const client = createClient(program.opts());
|
|
1313
|
+
const o = offsetListOpts(opts);
|
|
1314
|
+
if (opts.agentId !== void 0) o.agentId = opts.agentId;
|
|
1315
|
+
printJson(rt, await client.listAgentEmailOptOuts(o));
|
|
1316
|
+
});
|
|
1317
|
+
});
|
|
1318
|
+
optouts.command("remove").description("Remove an opt-out so the recipient can receive agent email again.").argument("<optoutId>", "Opt-out ID.").action(async (optoutId) => {
|
|
1319
|
+
await run(rt, async () => {
|
|
1320
|
+
const client = createClient(program.opts());
|
|
1321
|
+
await client.removeAgentEmailOptOut(optoutId);
|
|
1322
|
+
printJson(rt, { ok: true });
|
|
1323
|
+
});
|
|
1324
|
+
});
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
// src/commands/account.ts
|
|
1328
|
+
function register11(program, rt) {
|
|
1329
|
+
program.command("me").description("Show the authenticated user's account ID and organization memberships.").action(async () => {
|
|
1330
|
+
await run(rt, async () => {
|
|
1331
|
+
const client = createClient(program.opts());
|
|
1332
|
+
printJson(rt, await client.getMe());
|
|
1333
|
+
});
|
|
1334
|
+
});
|
|
1335
|
+
const version = program.command("api-version").description("Read or pin the account's dated API version.");
|
|
1336
|
+
version.command("get").description(
|
|
1337
|
+
"Show the version a request resolves to. Reflects --api-version when passed, otherwise the account pin, otherwise the default."
|
|
1338
|
+
).action(async () => {
|
|
1339
|
+
await run(rt, async () => {
|
|
1340
|
+
const client = createClient(program.opts());
|
|
1341
|
+
printJson(rt, await client.getApiVersion());
|
|
1342
|
+
});
|
|
1343
|
+
});
|
|
1344
|
+
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) => {
|
|
1345
|
+
await run(rt, async () => {
|
|
1346
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) {
|
|
1347
|
+
throw new Error(`Expected an API version as YYYY-MM-DD, got "${date}".`);
|
|
1348
|
+
}
|
|
1349
|
+
const client = createClient(program.opts());
|
|
1350
|
+
printJson(rt, await client.updateApiVersion(date));
|
|
1351
|
+
});
|
|
1352
|
+
});
|
|
1353
|
+
version.command("clear").description("Remove the account's version pin, reverting to the default version.").action(async () => {
|
|
1354
|
+
await run(rt, async () => {
|
|
1355
|
+
const client = createClient(program.opts());
|
|
1356
|
+
printJson(rt, await client.updateApiVersion(null));
|
|
1357
|
+
});
|
|
1358
|
+
});
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
// src/commands/models.ts
|
|
1362
|
+
function register12(program, rt) {
|
|
1098
1363
|
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) => {
|
|
1364
|
+
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
1365
|
await run(rt, async () => {
|
|
1101
1366
|
const client = createClient(program.opts());
|
|
1102
1367
|
const o = {};
|
|
1103
1368
|
if (opts.provider !== void 0) o.provider = opts.provider;
|
|
1104
1369
|
if (opts.supportsToolUse !== void 0) o.supportsToolUse = opts.supportsToolUse;
|
|
1105
1370
|
if (opts.supportsThinking !== void 0) o.supportsThinking = opts.supportsThinking;
|
|
1371
|
+
if (opts.supportsInputMedia !== void 0) o.supportsInputMedia = opts.supportsInputMedia;
|
|
1372
|
+
if (opts.supportsOutputMedia !== void 0) o.supportsOutputMedia = opts.supportsOutputMedia;
|
|
1106
1373
|
printJson(rt, await client.listModels(o));
|
|
1107
1374
|
});
|
|
1108
1375
|
});
|
|
1376
|
+
models.command("tiers").description("Show each media-generation modality and tier with its model and cost.").action(async () => {
|
|
1377
|
+
await run(rt, async () => {
|
|
1378
|
+
const client = createClient(program.opts());
|
|
1379
|
+
printJson(rt, await client.getGenerationTiers());
|
|
1380
|
+
});
|
|
1381
|
+
});
|
|
1109
1382
|
models.command("get").description("Get full details for a specific model.").argument("<modelId>", "Model ID.").action(async (modelId) => {
|
|
1110
1383
|
await run(rt, async () => {
|
|
1111
1384
|
const client = createClient(program.opts());
|
|
@@ -1113,7 +1386,7 @@ function register10(program, rt) {
|
|
|
1113
1386
|
});
|
|
1114
1387
|
});
|
|
1115
1388
|
const alerts = models.command("alerts").description("Model alerts.");
|
|
1116
|
-
alerts.command("list").description("List model alerts.").option("--page <n>", "Page number.",
|
|
1389
|
+
alerts.command("list").description("List model alerts.").option("--page <n>", "Page number.", parseNumber).option("--limit <n>", "Page size.", parseNumber).action(async (opts) => {
|
|
1117
1390
|
await run(rt, async () => {
|
|
1118
1391
|
const client = createClient(program.opts());
|
|
1119
1392
|
printJson(rt, await client.listModelAlerts(listOpts(opts)));
|
|
@@ -1146,15 +1419,15 @@ function register10(program, rt) {
|
|
|
1146
1419
|
});
|
|
1147
1420
|
});
|
|
1148
1421
|
const experiments = models.command("experiments").description("Model playground experiments.");
|
|
1149
|
-
|
|
1422
|
+
withOffsetListOptions(
|
|
1423
|
+
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).")
|
|
1424
|
+
).action(async (opts) => {
|
|
1150
1425
|
await run(rt, async () => {
|
|
1151
1426
|
const client = createClient(program.opts());
|
|
1152
|
-
const o =
|
|
1427
|
+
const o = offsetListOpts(opts);
|
|
1153
1428
|
if (opts.days !== void 0) o.days = opts.days;
|
|
1154
1429
|
if (opts.startDate !== void 0) o.startDate = opts.startDate;
|
|
1155
1430
|
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
1431
|
printJson(rt, await client.listExperiments(o));
|
|
1159
1432
|
});
|
|
1160
1433
|
});
|
|
@@ -1187,8 +1460,9 @@ function register10(program, rt) {
|
|
|
1187
1460
|
}
|
|
1188
1461
|
|
|
1189
1462
|
// src/commands/search.ts
|
|
1190
|
-
|
|
1191
|
-
|
|
1463
|
+
import { Option } from "commander";
|
|
1464
|
+
function register13(program, rt) {
|
|
1465
|
+
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
1466
|
await run(rt, async () => {
|
|
1193
1467
|
const client = createClient(program.opts());
|
|
1194
1468
|
const o = { query: opts.query };
|
|
@@ -1197,10 +1471,20 @@ function register11(program, rt) {
|
|
|
1197
1471
|
printJson(rt, await client.search(o));
|
|
1198
1472
|
});
|
|
1199
1473
|
});
|
|
1474
|
+
const docs = program.command("docs").description("Seclai documentation.");
|
|
1475
|
+
docs.command("search").description("Search the Seclai documentation.").requiredOption("--query <text>", "Search query text.").addOption(new Option("--mode <mode>", "Search mode.").choices(["keyword", "semantic"])).option("--limit <n>", "Max results.", parseNumber).action(async (opts) => {
|
|
1476
|
+
await run(rt, async () => {
|
|
1477
|
+
const client = createClient(program.opts());
|
|
1478
|
+
const o = { query: opts.query };
|
|
1479
|
+
if (opts.mode !== void 0) o.mode = opts.mode;
|
|
1480
|
+
if (opts.limit !== void 0) o.limit = opts.limit;
|
|
1481
|
+
printJson(rt, await client.searchDocs(o));
|
|
1482
|
+
});
|
|
1483
|
+
});
|
|
1200
1484
|
}
|
|
1201
1485
|
|
|
1202
1486
|
// src/commands/ai.ts
|
|
1203
|
-
function
|
|
1487
|
+
function register14(program, rt) {
|
|
1204
1488
|
const ai = program.command("ai").description("Top-level AI assistant.");
|
|
1205
1489
|
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
1490
|
await run(rt, async () => {
|
|
@@ -1278,73 +1562,137 @@ function register12(program, rt) {
|
|
|
1278
1562
|
import { existsSync, statSync } from "fs";
|
|
1279
1563
|
import { mkdir, writeFile } from "fs/promises";
|
|
1280
1564
|
import { dirname, join } from "path";
|
|
1281
|
-
var
|
|
1565
|
+
var SKILL_FILES = [
|
|
1566
|
+
{ name: "SKILL.md", content: `---
|
|
1282
1567
|
name: seclai-cli
|
|
1283
1568
|
description: >-
|
|
1284
1569
|
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.
|
|
1570
|
+
solutions, governance, alerts, agent email, and models via the CLI. Use when
|
|
1571
|
+
working with the Seclai platform or when the user mentions Seclai CLI commands.
|
|
1287
1572
|
---
|
|
1288
1573
|
|
|
1289
1574
|
# Seclai CLI
|
|
1290
1575
|
|
|
1291
|
-
The Seclai CLI (\`seclai\` / \`npx @seclai/cli\`) manages agents, knowledge bases,
|
|
1576
|
+
The Seclai CLI (\`seclai\` / \`npx @seclai/cli\`) manages agents, knowledge bases,
|
|
1577
|
+
sources, memory banks, evaluations, solutions, governance, alerts, agent email,
|
|
1578
|
+
and models from the terminal.
|
|
1292
1579
|
|
|
1293
|
-
|
|
1580
|
+
Every command writes JSON to stdout. Pipe into \`jq\` for filtering. Errors go to
|
|
1581
|
+
stderr and set a non-zero exit code, so \`set -e\` scripts fail as expected.
|
|
1582
|
+
|
|
1583
|
+
**Find the commands for a task in the map below, then read that reference file.**
|
|
1584
|
+
Only this page is loaded up front; the references are read on demand.
|
|
1294
1585
|
|
|
1295
1586
|
## Quick start
|
|
1296
1587
|
|
|
1297
1588
|
\`\`\`bash
|
|
1298
|
-
# authenticate
|
|
1299
1589
|
export SECLAI_API_KEY="sk-..."
|
|
1300
1590
|
|
|
1301
|
-
# create an agent
|
|
1302
1591
|
seclai agents create --json '{"name":"My Agent","description":"QA chatbot"}'
|
|
1303
|
-
|
|
1304
|
-
# configure steps via AI assistant
|
|
1305
1592
|
seclai agents ai gen-steps <agentId> --user-input "Build a QA chatbot that uses a knowledge base"
|
|
1306
|
-
|
|
1307
|
-
# accept the generated plan
|
|
1308
1593
|
seclai agents ai mark <agentId> <conversationId> --json '{"accepted":true}'
|
|
1309
|
-
|
|
1310
|
-
# run the agent
|
|
1311
1594
|
seclai agents run <agentId> --json '{"input":"How do I reset my password?"}' --stream
|
|
1312
|
-
|
|
1313
|
-
# list runs
|
|
1314
1595
|
seclai agents runs list <agentId>
|
|
1315
1596
|
\`\`\`
|
|
1316
1597
|
|
|
1598
|
+
## Command map
|
|
1599
|
+
|
|
1600
|
+
| Group | What it covers | Reference |
|
|
1601
|
+
| --- | --- | --- |
|
|
1602
|
+
| \`agents\` | Agents, runs, definitions, export/import, input uploads, triggers, agent AI | [references/agents.md](references/agents.md) |
|
|
1603
|
+
| \`sources\` \`contents\` \`kb\` \`memory\` | Sources and uploads, exports, embedding migration, indexed content, knowledge bases, memory banks | [references/knowledge.md](references/knowledge.md) |
|
|
1604
|
+
| \`evals\` | Evaluation criteria, results, runs, agent-level summaries | [references/evaluations.md](references/evaluations.md) |
|
|
1605
|
+
| \`solutions\` \`governance\` | Solutions, resource links, conversations, solution and governance AI | [references/solutions.md](references/solutions.md) |
|
|
1606
|
+
| \`alerts\` | Alerts, alert configurations, organization preferences | [references/alerts.md](references/alerts.md) |
|
|
1607
|
+
| \`email\` | Agent email: sending domains, inbound blocklist, inbound health, opt-outs | [references/email.md](references/email.md) |
|
|
1608
|
+
| \`models\` | Model catalog, generation tiers, model alerts, recommendations, playground experiments | [references/models.md](references/models.md) |
|
|
1609
|
+
| \`auth\` \`configure\` \`api-version\` \`mcp\` \`skills\` \`completion\` | Authentication, profiles, API version pinning, editor integration | [references/setup.md](references/setup.md) |
|
|
1610
|
+
| \`ai\` | Top-level AI assistant for knowledge bases, sources, solutions and memory | [references/ai-assistant.md](references/ai-assistant.md) |
|
|
1611
|
+
|
|
1612
|
+
Cross-cutting topics: [streaming and event modes](references/streaming.md),
|
|
1613
|
+
[file uploads](references/uploads.md).
|
|
1614
|
+
|
|
1317
1615
|
## Authentication
|
|
1318
1616
|
|
|
1319
|
-
|
|
1320
|
-
|
|
1617
|
+
Two modes:
|
|
1618
|
+
|
|
1619
|
+
1. **API key** \u2014 set \`SECLAI_API_KEY\`, or pass \`--api-key <key>\`.
|
|
1620
|
+
2. **SSO** \u2014 \`seclai auth login\` for browser-based OAuth2/PKCE. Tokens are cached
|
|
1621
|
+
locally and refreshed automatically.
|
|
1622
|
+
|
|
1623
|
+
Override the API host with \`SECLAI_API_URL\` (default \`https://api.seclai.com\`).
|
|
1321
1624
|
|
|
1322
1625
|
## Global options
|
|
1323
1626
|
|
|
1324
1627
|
\`\`\`bash
|
|
1325
|
-
--api-key <key>
|
|
1326
|
-
--
|
|
1327
|
-
-
|
|
1628
|
+
--api-key <key> # or set SECLAI_API_KEY
|
|
1629
|
+
--profile <name> # SSO profile (or SECLAI_PROFILE, default 'default')
|
|
1630
|
+
--account-id <id> # multi-org targeting (X-Account-Id header)
|
|
1631
|
+
--config-dir <path> # or SECLAI_CONFIG_DIR, default ~/.seclai
|
|
1632
|
+
--api-version <date> # or SECLAI_API_VERSION; see below
|
|
1633
|
+
--allow-unknown-api-version # send a version this CLI was not built against
|
|
1634
|
+
--compact # single-line JSON
|
|
1635
|
+
-V, --version
|
|
1636
|
+
\`\`\`
|
|
1637
|
+
|
|
1638
|
+
## API versions
|
|
1639
|
+
|
|
1640
|
+
The API is versioned by date, and a version can change a response's shape \u2014 a
|
|
1641
|
+
bare array becoming \`{data, pagination}\`, for instance. **The CLI sends no
|
|
1642
|
+
version header by default**, so upgrading it never changes what a command
|
|
1643
|
+
prints. Opt in per invocation, or pin the account:
|
|
1644
|
+
|
|
1645
|
+
\`\`\`bash
|
|
1646
|
+
seclai api-version get # what does a request resolve to?
|
|
1647
|
+
seclai --api-version 2026-07-27 alerts list # this invocation only
|
|
1648
|
+
seclai api-version set 2026-07-27 # every client on the account
|
|
1649
|
+
seclai api-version clear
|
|
1328
1650
|
\`\`\`
|
|
1329
1651
|
|
|
1652
|
+
An \`--api-version\` this CLI was not built against is rejected, because a newer
|
|
1653
|
+
version can reshape a response the CLI would then misread. Pass
|
|
1654
|
+
\`--allow-unknown-api-version\` to send it anyway. \`api-version set\` takes a
|
|
1655
|
+
\`YYYY-MM-DD\` date and rejects anything else, because the pin applies to every
|
|
1656
|
+
client on the account.
|
|
1657
|
+
|
|
1658
|
+
\`--api-key\`, \`--profile\`, \`--account-id\` and \`--config-dir\` reject an empty
|
|
1659
|
+
value. A shell expanding an unset variable passes \`""\`, which the SDK's
|
|
1660
|
+
credential chain discards, so each would silently resolve elsewhere \u2014 a
|
|
1661
|
+
different identity, another account's cached tokens, or the default org. Guard
|
|
1662
|
+
the flag rather than the value: \`seclai \${KEY:+--api-key "$KEY"} agents list\`.
|
|
1663
|
+
|
|
1664
|
+
An empty \`--api-version\` is accepted with a warning, since it costs only the
|
|
1665
|
+
version header; a future release will reject it too.
|
|
1666
|
+
|
|
1330
1667
|
## Common patterns
|
|
1331
1668
|
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1669
|
+
**JSON input.** Most create/update commands take \`--json '{"key":"value"}'\` or
|
|
1670
|
+
\`--json-file path.json\`. Use \`-\` as the value to read from stdin.
|
|
1671
|
+
|
|
1672
|
+
**AI shorthand.** AI generation commands accept \`--user-input <text>\` in place of
|
|
1673
|
+
\`--json '{"user_input":"<text>"}'\`.
|
|
1674
|
+
|
|
1675
|
+
**Pagination.** List commands take \`--page <n>\` and \`--limit <n>\`; some add
|
|
1676
|
+
\`--sort <field>\` and \`--order asc|desc\`. A few endpoints paginate by offset
|
|
1677
|
+
instead and take \`--limit\` / \`--offset\`.
|
|
1335
1678
|
|
|
1336
|
-
|
|
1337
|
-
|
|
1679
|
+
**Uploads.** Upload commands take \`--file <path>\`, plus optional \`--title\`,
|
|
1680
|
+
\`--metadata '{"k":"v"}'\`, \`--metadata-file\`, \`--file-name\` and \`--mime-type\`.
|
|
1338
1681
|
|
|
1339
|
-
|
|
1340
|
-
List commands support \`--page <n>\` and \`--limit <n>\`. Some also support \`--sort <field>\` and \`--order asc|desc\`.
|
|
1682
|
+
## Search and account
|
|
1341
1683
|
|
|
1342
|
-
|
|
1343
|
-
|
|
1684
|
+
\`\`\`bash
|
|
1685
|
+
seclai search --query "deployment guide" [--limit N] [--entity-type <type>]
|
|
1686
|
+
seclai docs search --query "memory banks" [--mode keyword|semantic] [--limit N]
|
|
1687
|
+
seclai me # account ID and organization memberships
|
|
1688
|
+
\`\`\`
|
|
1689
|
+
` },
|
|
1690
|
+
{ name: "references/agents.md", content: `# Agents
|
|
1344
1691
|
|
|
1345
|
-
|
|
1692
|
+
Agents, their runs, definitions, export/import, input uploads, triggers, and the
|
|
1693
|
+
agent AI assistant.
|
|
1346
1694
|
|
|
1347
|
-
|
|
1695
|
+
## CRUD and lifecycle
|
|
1348
1696
|
|
|
1349
1697
|
\`\`\`bash
|
|
1350
1698
|
seclai agents list [--page N] [--limit N]
|
|
@@ -1352,353 +1700,636 @@ seclai agents create --json '{"name":"My Agent","description":"..."}'
|
|
|
1352
1700
|
seclai agents get <agentId>
|
|
1353
1701
|
seclai agents update <agentId> --json '{"name":"Renamed"}'
|
|
1354
1702
|
seclai agents delete <agentId>
|
|
1703
|
+
|
|
1704
|
+
# pause across every trigger path (API, schedule, email, sub-agent calls)
|
|
1705
|
+
seclai agents disable <agentId>
|
|
1706
|
+
seclai agents enable <agentId>
|
|
1707
|
+
|
|
1708
|
+
# which live agents call this one via a call_agent step?
|
|
1709
|
+
seclai agents callers <agentId>
|
|
1710
|
+
\`\`\`
|
|
1711
|
+
|
|
1712
|
+
## Triggers
|
|
1713
|
+
|
|
1714
|
+
\`\`\`bash
|
|
1715
|
+
# alias, sender allowlist and inbound-handling flags for an EMAIL_RECEIVED trigger
|
|
1716
|
+
seclai agents triggers email-config <agentId> <triggerId> --json '{"alias":"support"}'
|
|
1355
1717
|
\`\`\`
|
|
1356
1718
|
|
|
1357
|
-
|
|
1719
|
+
## Running agents
|
|
1720
|
+
|
|
1721
|
+
Four modes: basic, streaming, NDJSON events, and polling. See
|
|
1722
|
+
[streaming.md](streaming.md) for event shapes and filtering.
|
|
1358
1723
|
|
|
1359
1724
|
\`\`\`bash
|
|
1360
1725
|
# simple run \u2014 returns the final result
|
|
1361
1726
|
seclai agents run <agentId> --json '{"input":"Hello"}'
|
|
1362
1727
|
|
|
1363
|
-
# stream \u2014 wait for completion via SSE, print final result
|
|
1728
|
+
# stream \u2014 wait for completion via SSE, print the final result
|
|
1364
1729
|
seclai agents run <agentId> --json '{"input":"Hello"}' --stream [--timeout-ms 60000]
|
|
1365
1730
|
|
|
1366
|
-
# events \u2014
|
|
1367
|
-
# --output: full (entire event), data (event data only), status (
|
|
1368
|
-
# --event-filter: comma-separated event types
|
|
1731
|
+
# events \u2014 every SSE event as an NDJSON line
|
|
1732
|
+
# --output: full (entire event), data (event data only), status (one-line summary)
|
|
1733
|
+
# --event-filter: comma-separated event types, e.g. "status,data"
|
|
1369
1734
|
seclai agents run <agentId> --json '{"input":"Hello"}' --events [--output full|data|status] [--event-filter "status,data"]
|
|
1370
1735
|
|
|
1371
|
-
# poll \u2014 poll
|
|
1736
|
+
# poll \u2014 submit, then poll until complete
|
|
1372
1737
|
seclai agents run <agentId> --json '{"input":"Hello"}' --poll [--poll-interval-ms 2000] [--include-step-outputs]
|
|
1373
1738
|
\`\`\`
|
|
1374
1739
|
|
|
1375
|
-
|
|
1740
|
+
## Runs
|
|
1376
1741
|
|
|
1377
1742
|
\`\`\`bash
|
|
1378
1743
|
seclai agents runs list <agentId> [--page N] [--limit N] [--status <status>]
|
|
1379
1744
|
seclai agents runs get <runId> [--include-step-outputs]
|
|
1380
|
-
seclai agents runs delete <runId>
|
|
1381
1745
|
seclai agents runs cancel <runId>
|
|
1746
|
+
seclai agents runs delete <runId> # deprecated alias for \`runs cancel\`; the API has no delete-a-run operation
|
|
1382
1747
|
seclai agents runs search --json '{"query":"..."}'
|
|
1383
1748
|
seclai agents runs eval-results <agentId> <runId> [--page N] [--limit N]
|
|
1749
|
+
|
|
1750
|
+
# Download a file emitted by a run step. attachmentId is the URL-safe-base64
|
|
1751
|
+
# storage_key from run output manifests or webhooks.
|
|
1752
|
+
seclai agents runs download-attachment <runId> <attachmentId> [--download-name <name>] [--output <path>]
|
|
1384
1753
|
\`\`\`
|
|
1385
1754
|
|
|
1386
|
-
|
|
1755
|
+
Without \`--output\`, raw bytes go to stdout \u2014 redirect to a file rather than
|
|
1756
|
+
letting them hit the terminal.
|
|
1757
|
+
|
|
1758
|
+
## Definitions
|
|
1387
1759
|
|
|
1388
1760
|
\`\`\`bash
|
|
1389
1761
|
seclai agents def get <agentId>
|
|
1390
|
-
seclai agents def update <agentId> --json '{"steps":[{"step_type":"llm","config":{
|
|
1762
|
+
seclai agents def update <agentId> --json '{"steps":[{"step_type":"llm","config":{}}]}'
|
|
1391
1763
|
\`\`\`
|
|
1392
1764
|
|
|
1393
|
-
|
|
1765
|
+
## Export and import
|
|
1394
1766
|
|
|
1395
1767
|
\`\`\`bash
|
|
1768
|
+
# portable JSON snapshot of an agent definition
|
|
1769
|
+
seclai agents export <agentId> [--no-download]
|
|
1770
|
+
|
|
1771
|
+
# Validate an agent_definition payload before importing \u2014 no writes.
|
|
1772
|
+
# Reports counts and any unresolved_refs (knowledge bases, memory banks, source
|
|
1773
|
+
# connections or sub-agents that do not exist in this account).
|
|
1774
|
+
seclai agents export <agentId> \\
|
|
1775
|
+
| jq '{agent_definition: .}' \\
|
|
1776
|
+
| seclai agents preview-import --json-file -
|
|
1777
|
+
|
|
1778
|
+
# Import via \`agents create\` (or \`agents update\`) with agent_definition set to
|
|
1779
|
+
# the export payload, and entity_remap mapping unresolved source UUIDs to target
|
|
1780
|
+
# UUIDs taken from preview-import's unresolved_refs[*].alternatives.
|
|
1781
|
+
seclai agents create --json '{"name":"Imported","trigger_type":"dynamic_input","agent_definition":{},"entity_remap":{}}'
|
|
1782
|
+
\`\`\`
|
|
1783
|
+
|
|
1784
|
+
## Input uploads
|
|
1785
|
+
|
|
1786
|
+
\`\`\`bash
|
|
1787
|
+
# What files (if any) does this agent expect? requires_uploads reports whether it
|
|
1788
|
+
# accepts files; the agent block lists the names, indexes and patterns a run-time
|
|
1789
|
+
# batch must satisfy. Call this before staging uploads.
|
|
1790
|
+
seclai agents attachment-references <agentId>
|
|
1791
|
+
|
|
1396
1792
|
seclai agents upload-input <agentId> --file ./input.pdf [--file-name name] [--mime-type type]
|
|
1397
1793
|
seclai agents input-status <agentId> <uploadId>
|
|
1398
1794
|
\`\`\`
|
|
1399
1795
|
|
|
1400
|
-
|
|
1796
|
+
## Agent AI assistant
|
|
1401
1797
|
|
|
1402
1798
|
\`\`\`bash
|
|
1403
1799
|
seclai agents ai gen-steps <agentId> --user-input "Build a QA chatbot"
|
|
1404
1800
|
seclai agents ai step-config <agentId> --json '{"step_type":"llm","user_input":"Configure the LLM step"}'
|
|
1405
|
-
|
|
1801
|
+
|
|
1802
|
+
# --step-type is required; the API rejects the request without it
|
|
1803
|
+
seclai agents ai history <agentId> --step-type llm [--step-id <id>] [--limit N] [--offset N]
|
|
1804
|
+
|
|
1406
1805
|
seclai agents ai mark <agentId> <conversationId> --json '{"accepted":true}'
|
|
1407
1806
|
\`\`\`
|
|
1408
1807
|
|
|
1409
|
-
|
|
1808
|
+
## Example: knowledge-base-backed agent
|
|
1410
1809
|
|
|
1411
1810
|
\`\`\`bash
|
|
1412
|
-
seclai
|
|
1413
|
-
seclai
|
|
1414
|
-
seclai
|
|
1415
|
-
seclai
|
|
1416
|
-
seclai
|
|
1811
|
+
seclai kb create --json '{"name":"Support KB","description":"Customer support articles"}'
|
|
1812
|
+
seclai agents create --json '{"name":"Support Bot","description":"Answers customer questions"}'
|
|
1813
|
+
seclai agents ai gen-steps <agentId> --user-input "Build a QA chatbot that searches the Support KB"
|
|
1814
|
+
seclai agents ai mark <agentId> <conversationId> --json '{"accepted":true}'
|
|
1815
|
+
seclai agents run <agentId> --json '{"input":"How do I reset my password?"}' --stream
|
|
1417
1816
|
\`\`\`
|
|
1418
1817
|
|
|
1419
|
-
|
|
1818
|
+
## Example: memory-powered agent
|
|
1420
1819
|
|
|
1421
1820
|
\`\`\`bash
|
|
1422
|
-
seclai
|
|
1423
|
-
seclai
|
|
1821
|
+
seclai memory create --json '{"name":"User Preferences","type":"general"}'
|
|
1822
|
+
seclai agents create --json '{"name":"Personal Assistant","description":"Remembers user preferences"}'
|
|
1823
|
+
seclai agents ai gen-steps <agentId> --user-input "Build a chat agent that remembers user preferences. Use general memory bank <memoryBankId>"
|
|
1824
|
+
seclai agents ai mark <agentId> <conversationId> --json '{"accepted":true}'
|
|
1424
1825
|
\`\`\`
|
|
1826
|
+
` },
|
|
1827
|
+
{ name: "references/ai-assistant.md", content: `# Top-level AI assistant
|
|
1425
1828
|
|
|
1426
|
-
|
|
1829
|
+
\`seclai ai\` creates resources from a natural-language description, without
|
|
1830
|
+
starting from a solution or an agent. The domain-scoped assistants \u2014
|
|
1831
|
+
\`agents ai\`, \`memory ai\`, \`solutions ai\`, \`governance ai\` \u2014 live with their
|
|
1832
|
+
resources.
|
|
1427
1833
|
|
|
1428
1834
|
\`\`\`bash
|
|
1429
|
-
seclai
|
|
1430
|
-
seclai
|
|
1431
|
-
seclai
|
|
1432
|
-
seclai
|
|
1433
|
-
seclai sources exports delete <sourceId> <exportId>
|
|
1434
|
-
seclai sources exports download <sourceId> <exportId>
|
|
1435
|
-
seclai sources exports estimate <sourceId> --json '{"format":"jsonl"}'
|
|
1436
|
-
\`\`\`
|
|
1835
|
+
seclai ai kb --user-input "Create a support knowledge base"
|
|
1836
|
+
seclai ai source --user-input "Create a documentation source"
|
|
1837
|
+
seclai ai solution --user-input "Build a customer support solution"
|
|
1838
|
+
seclai ai memory --user-input "Create a conversation memory bank"
|
|
1437
1839
|
|
|
1438
|
-
|
|
1840
|
+
seclai ai memory-history
|
|
1841
|
+
seclai ai accept <conversationId> --json '{"accepted":true}'
|
|
1842
|
+
seclai ai decline <conversationId>
|
|
1843
|
+
seclai ai memory-accept <conversationId> --json '{"accepted":true}'
|
|
1439
1844
|
|
|
1440
|
-
|
|
1441
|
-
seclai sources migration get <sourceId>
|
|
1442
|
-
seclai sources migration start <sourceId> --json '{"target_model":"text-embedding-3-large"}'
|
|
1443
|
-
seclai sources migration cancel <sourceId>
|
|
1845
|
+
seclai ai feedback --json '{"feedback":"The response was helpful"}'
|
|
1444
1846
|
\`\`\`
|
|
1445
1847
|
|
|
1446
|
-
|
|
1848
|
+
## The generate-then-accept cycle
|
|
1849
|
+
|
|
1850
|
+
Every assistant command returns a *proposal* with a conversation ID. Nothing is
|
|
1851
|
+
created until you accept it:
|
|
1447
1852
|
|
|
1448
1853
|
\`\`\`bash
|
|
1449
|
-
seclai
|
|
1450
|
-
|
|
1451
|
-
seclai
|
|
1452
|
-
seclai contents replace-text <contentVersionId> --json '{"text":"Replacement text","title":"Updated"}'
|
|
1453
|
-
seclai contents embeddings <contentVersionId> [--page N] [--limit N]
|
|
1854
|
+
seclai ai kb --user-input "Create a support knowledge base"
|
|
1855
|
+
# read the proposal, note the conversation id
|
|
1856
|
+
seclai ai accept <conversationId> --json '{"accepted":true}'
|
|
1454
1857
|
\`\`\`
|
|
1455
1858
|
|
|
1456
|
-
|
|
1859
|
+
Memory-bank proposals have their own accept command (\`ai memory-accept\`) and
|
|
1860
|
+
their own history (\`ai memory-history\`); everything else uses \`ai accept\` /
|
|
1861
|
+
\`ai decline\`.
|
|
1862
|
+
` },
|
|
1863
|
+
{ name: "references/alerts.md", content: `# Alerts
|
|
1864
|
+
|
|
1865
|
+
Account alerts, the configurations that raise them, and per-organization
|
|
1866
|
+
delivery preferences.
|
|
1867
|
+
|
|
1868
|
+
Model-catalog alerts are separate \u2014 see [models.md](models.md).
|
|
1869
|
+
|
|
1870
|
+
## Alerts
|
|
1457
1871
|
|
|
1458
1872
|
\`\`\`bash
|
|
1459
|
-
seclai
|
|
1460
|
-
seclai
|
|
1461
|
-
seclai
|
|
1462
|
-
seclai
|
|
1463
|
-
seclai
|
|
1873
|
+
seclai alerts list [--page N] [--limit N] [--status <status>]
|
|
1874
|
+
seclai alerts get <alertId>
|
|
1875
|
+
seclai alerts status <alertId> --json '{"status":"resolved"}'
|
|
1876
|
+
seclai alerts comment <alertId> --json '{"comment":"Fixed the issue"}'
|
|
1877
|
+
seclai alerts subscribe <alertId>
|
|
1878
|
+
seclai alerts unsubscribe <alertId>
|
|
1464
1879
|
\`\`\`
|
|
1465
1880
|
|
|
1466
|
-
|
|
1881
|
+
\`GET /alerts\` declares no severity filter. \`--severity\` still parses, but it is
|
|
1882
|
+
ignored with a warning and will be removed \u2014 it never filtered anything. Filter
|
|
1883
|
+
client-side instead:
|
|
1467
1884
|
|
|
1468
1885
|
\`\`\`bash
|
|
1469
|
-
seclai
|
|
1470
|
-
# type: "conversation" (chat history) or "general" (structured facts)
|
|
1471
|
-
seclai memory create --json '{"name":"Chat Memory","type":"conversation"}'
|
|
1472
|
-
seclai memory get <memoryBankId>
|
|
1473
|
-
seclai memory update <memoryBankId> --json '{"name":"Renamed"}'
|
|
1474
|
-
seclai memory delete <memoryBankId>
|
|
1886
|
+
seclai alerts list | jq '[.data[] | select(.severity == "high")]'
|
|
1475
1887
|
\`\`\`
|
|
1476
1888
|
|
|
1477
|
-
|
|
1889
|
+
## Alert configurations
|
|
1478
1890
|
|
|
1479
1891
|
\`\`\`bash
|
|
1480
|
-
seclai
|
|
1481
|
-
seclai
|
|
1482
|
-
seclai
|
|
1483
|
-
seclai
|
|
1484
|
-
seclai
|
|
1485
|
-
seclai memory test-compaction <memoryBankId> --json '{"prompt":"Summarize the conversation"}'
|
|
1486
|
-
seclai memory test-compaction-standalone --json '{"prompt":"Summarize the conversation"}'
|
|
1892
|
+
seclai alerts configs list [--page N] [--limit N]
|
|
1893
|
+
seclai alerts configs create --json '{"name":"Latency Alert","description":"...","threshold":5000}'
|
|
1894
|
+
seclai alerts configs get <configId>
|
|
1895
|
+
seclai alerts configs update <configId> --json '{"threshold":3000}'
|
|
1896
|
+
seclai alerts configs delete <configId>
|
|
1487
1897
|
\`\`\`
|
|
1488
1898
|
|
|
1489
|
-
|
|
1899
|
+
## Organization preferences
|
|
1490
1900
|
|
|
1491
1901
|
\`\`\`bash
|
|
1492
|
-
seclai
|
|
1493
|
-
seclai
|
|
1494
|
-
|
|
1902
|
+
seclai alerts prefs list
|
|
1903
|
+
seclai alerts prefs update <organizationId> <alertType> --json '{"enabled":true}'
|
|
1904
|
+
\`\`\`
|
|
1905
|
+
|
|
1906
|
+
Preferences are per organization and per alert type, so \`update\` takes both.
|
|
1907
|
+
` },
|
|
1908
|
+
{ name: "references/email.md", content: `# Agent email
|
|
1909
|
+
|
|
1910
|
+
The domains agents send from, the inbound blocklist, inbound health, and
|
|
1911
|
+
recipient opt-outs.
|
|
1912
|
+
|
|
1913
|
+
Per-agent inbound configuration (alias, sender allowlist) lives on the trigger \u2014
|
|
1914
|
+
see \`agents triggers email-config\` in [agents.md](agents.md).
|
|
1915
|
+
|
|
1916
|
+
## Sending domains
|
|
1917
|
+
|
|
1918
|
+
\`\`\`bash
|
|
1919
|
+
seclai email domains list
|
|
1920
|
+
seclai email domains add --kind custom --value mail.example.com [--delegated]
|
|
1921
|
+
seclai email domains verify <domainId> # run a DNS check now
|
|
1922
|
+
seclai email domains set-primary <domainId>
|
|
1923
|
+
seclai email domains test-email <domainId> # send a test to the account owner
|
|
1924
|
+
seclai email domains dmarc <domainId> [--days N] [--top-sources N]
|
|
1925
|
+
seclai email domains remove <domainId>
|
|
1926
|
+
seclai email domains use-shared # revert to agent.seclai.com
|
|
1927
|
+
\`\`\`
|
|
1928
|
+
|
|
1929
|
+
\`--kind\` is \`vanity\` (a subdomain of seclai.com) or \`custom\` (your own domain).
|
|
1930
|
+
\`add\` returns the DNS records to publish; pass \`--delegated\` when the domain's
|
|
1931
|
+
DNS is delegated to Seclai so those records are published for you. A domain must
|
|
1932
|
+
verify before \`set-primary\` will accept it.
|
|
1933
|
+
|
|
1934
|
+
## Inbound sender blocklist
|
|
1935
|
+
|
|
1936
|
+
\`\`\`bash
|
|
1937
|
+
seclai email blocked list [--limit N] [--offset N]
|
|
1938
|
+
seclai email blocked add --sender-email spam@example.com [--note "phishing"]
|
|
1939
|
+
seclai email blocked add --sender-email example.com --match-type domain
|
|
1940
|
+
seclai email blocked remove <blockedId>
|
|
1941
|
+
seclai email blocked auto-block-mode disabled|input|input_and_output
|
|
1495
1942
|
\`\`\`
|
|
1496
1943
|
|
|
1497
|
-
|
|
1944
|
+
\`--match-type\` is \`address\` (the default) or \`domain\`. \`auto-block-mode\` controls
|
|
1945
|
+
whether a governance BLOCK on an authenticated sender adds them to the blocklist
|
|
1946
|
+
automatically.
|
|
1498
1947
|
|
|
1948
|
+
## Inbound health
|
|
1949
|
+
|
|
1950
|
+
\`\`\`bash
|
|
1951
|
+
seclai email inbound status # quota usage, pause state, queued run counts
|
|
1952
|
+
seclai email inbound rejections [--agent-id <id>] [--limit N]
|
|
1953
|
+
seclai email inbound cancel-queued # fail every over-quota parked run at once
|
|
1954
|
+
seclai email inbound resume # lift the account-wide pause
|
|
1955
|
+
\`\`\`
|
|
1956
|
+
|
|
1957
|
+
When inbound email exceeds quota, runs park in a QUEUED state and the account
|
|
1958
|
+
pauses. \`status\` shows both; \`cancel-queued\` clears the backlog and \`resume\`
|
|
1959
|
+
lifts the pause. Check \`rejections\` first \u2014 it reports why messages were turned
|
|
1960
|
+
away, which is usually the more useful answer.
|
|
1961
|
+
|
|
1962
|
+
## Recipient opt-outs
|
|
1963
|
+
|
|
1964
|
+
\`\`\`bash
|
|
1965
|
+
seclai email optouts list [--agent-id <id>] [--limit N] [--offset N]
|
|
1966
|
+
seclai email optouts remove <optoutId>
|
|
1967
|
+
\`\`\`
|
|
1968
|
+
|
|
1969
|
+
Removing an opt-out lets that recipient receive agent email again.
|
|
1970
|
+
` },
|
|
1971
|
+
{ name: "references/evaluations.md", content: `# Evaluations Workflow
|
|
1972
|
+
|
|
1973
|
+
## Step 1: Create evaluation criteria for an agent
|
|
1974
|
+
\`\`\`bash
|
|
1975
|
+
seclai evals criteria create <agentId> --json '{"name":"Answer Accuracy","description":"Does the answer correctly address the question?","eval_type":"llm_judge"}'
|
|
1976
|
+
\`\`\`
|
|
1977
|
+
|
|
1978
|
+
## Step 2: Find runs to evaluate
|
|
1979
|
+
\`\`\`bash
|
|
1980
|
+
# list all runs for an agent
|
|
1981
|
+
seclai agents runs list <agentId> --limit 10
|
|
1982
|
+
|
|
1983
|
+
# or find runs compatible with specific criteria
|
|
1984
|
+
seclai evals compatible-runs <criteriaId> --limit 10
|
|
1985
|
+
\`\`\`
|
|
1986
|
+
|
|
1987
|
+
## Step 3: Test criteria before committing
|
|
1988
|
+
\`\`\`bash
|
|
1989
|
+
seclai evals test-draft <agentId> --json '{"criteria":{"name":"Answer Accuracy","eval_type":"llm_judge","description":"..."},"run_id":"<runId>"}'
|
|
1990
|
+
\`\`\`
|
|
1991
|
+
|
|
1992
|
+
## Step 4: Create evaluation results
|
|
1993
|
+
\`\`\`bash
|
|
1994
|
+
seclai evals results create <criteriaId> --json '{"run_id":"<runId>","score":0.95}'
|
|
1995
|
+
\`\`\`
|
|
1996
|
+
|
|
1997
|
+
## Step 5: Review summaries
|
|
1499
1998
|
\`\`\`bash
|
|
1500
|
-
seclai evals criteria
|
|
1501
|
-
seclai evals
|
|
1999
|
+
seclai evals criteria summary <criteriaId>
|
|
2000
|
+
seclai evals agent-results <agentId>
|
|
2001
|
+
seclai evals agent-runs <agentId> --limit 20
|
|
2002
|
+
seclai evals non-manual-summary <agentId>
|
|
2003
|
+
\`\`\`
|
|
2004
|
+
|
|
2005
|
+
## Managing criteria
|
|
2006
|
+
\`\`\`bash
|
|
2007
|
+
seclai evals criteria list <agentId> [--page N] [--limit N] [--paged]
|
|
1502
2008
|
seclai evals criteria get <criteriaId>
|
|
1503
|
-
seclai evals criteria update <criteriaId> --json '{"name":"Updated
|
|
2009
|
+
seclai evals criteria update <criteriaId> --json '{"name":"Updated Name"}'
|
|
1504
2010
|
seclai evals criteria delete <criteriaId>
|
|
1505
|
-
seclai evals criteria summary <criteriaId>
|
|
1506
2011
|
\`\`\`
|
|
1507
2012
|
|
|
1508
|
-
|
|
2013
|
+
\`--paged\` wraps the results in \`{"data": [...]}\` instead of returning a bare
|
|
2014
|
+
array, so \`.data\` is a stable path to read whatever \`--api-version\` is in effect.
|
|
2015
|
+
Nothing is invented: the \`pagination\` block appears only once the API sends one,
|
|
2016
|
+
from \`--api-version 2026-07-27\`. Move scripts to \`.data\` first, then opt in to
|
|
2017
|
+
get \`.pagination\`.
|
|
1509
2018
|
|
|
2019
|
+
## Viewing results
|
|
1510
2020
|
\`\`\`bash
|
|
1511
2021
|
seclai evals results list <criteriaId> [--page N] [--limit N]
|
|
1512
|
-
seclai evals results create <criteriaId> --json '{"run_id":"...","score":0.9}'
|
|
1513
2022
|
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
2023
|
seclai evals agent-results <agentId> [--page N] [--limit N]
|
|
1516
2024
|
seclai evals agent-runs <agentId> [--page N] [--limit N]
|
|
1517
|
-
seclai evals non-manual-summary <agentId>
|
|
1518
2025
|
\`\`\`
|
|
2026
|
+
` },
|
|
2027
|
+
{ name: "references/knowledge.md", content: `# Sources, content, knowledge bases and memory banks
|
|
1519
2028
|
|
|
1520
|
-
|
|
2029
|
+
The ingestion side of Seclai: where documents come from, how they are indexed,
|
|
2030
|
+
and the stores agents read from.
|
|
2031
|
+
|
|
2032
|
+
For upload mechanics \u2014 MIME types, size limits, metadata \u2014 see
|
|
2033
|
+
[uploads.md](uploads.md).
|
|
2034
|
+
|
|
2035
|
+
## Sources
|
|
1521
2036
|
|
|
1522
2037
|
\`\`\`bash
|
|
1523
|
-
seclai
|
|
1524
|
-
seclai
|
|
1525
|
-
seclai
|
|
1526
|
-
seclai
|
|
1527
|
-
seclai
|
|
2038
|
+
seclai sources list [--page N] [--limit N] [--sort field] [--order asc|desc] [--account-id id]
|
|
2039
|
+
seclai sources create --json '{"name":"Docs","description":"Product documentation"}'
|
|
2040
|
+
seclai sources get <sourceId>
|
|
2041
|
+
seclai sources update <sourceId> --json '{"name":"Updated Docs"}'
|
|
2042
|
+
seclai sources delete <sourceId>
|
|
1528
2043
|
\`\`\`
|
|
1529
2044
|
|
|
1530
|
-
|
|
2045
|
+
\`source\` is accepted as an alias for \`sources\`.
|
|
2046
|
+
|
|
2047
|
+
## Source uploads
|
|
1531
2048
|
|
|
1532
2049
|
\`\`\`bash
|
|
1533
|
-
|
|
1534
|
-
seclai
|
|
1535
|
-
seclai solutions unlink <solutionId> --agents '["agentId1"]'
|
|
2050
|
+
seclai sources upload <sourceId> --file ./doc.pdf [--title "My Doc"] [--metadata '{"category":"docs"}'] [--file-name name] [--mime-type type]
|
|
2051
|
+
seclai sources upload-text <sourceId> --json '{"text":"Article content here...","title":"My Article"}'
|
|
1536
2052
|
\`\`\`
|
|
1537
2053
|
|
|
1538
|
-
|
|
2054
|
+
## Source exports
|
|
1539
2055
|
|
|
1540
2056
|
\`\`\`bash
|
|
1541
|
-
seclai
|
|
1542
|
-
seclai
|
|
1543
|
-
seclai
|
|
2057
|
+
seclai sources exports list <sourceId> [--page N] [--limit N]
|
|
2058
|
+
seclai sources exports create <sourceId> --json '{"format":"jsonl"}'
|
|
2059
|
+
seclai sources exports get <sourceId> <exportId>
|
|
2060
|
+
seclai sources exports cancel <sourceId> <exportId>
|
|
2061
|
+
seclai sources exports delete <sourceId> <exportId>
|
|
2062
|
+
seclai sources exports download <sourceId> <exportId>
|
|
2063
|
+
seclai sources exports estimate <sourceId> --json '{"format":"jsonl"}'
|
|
2064
|
+
\`\`\`
|
|
1544
2065
|
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
2066
|
+
\`estimate\` reports the size and cost before you commit to \`create\`.
|
|
2067
|
+
|
|
2068
|
+
## Embedding migration
|
|
2069
|
+
|
|
2070
|
+
\`\`\`bash
|
|
2071
|
+
seclai sources migration get <sourceId>
|
|
2072
|
+
seclai sources migration start <sourceId> --json '{"target_model":"text-embedding-3-large"}'
|
|
2073
|
+
seclai sources migration cancel <sourceId>
|
|
1550
2074
|
\`\`\`
|
|
1551
2075
|
|
|
1552
|
-
|
|
2076
|
+
## Contents (indexed content)
|
|
1553
2077
|
|
|
1554
2078
|
\`\`\`bash
|
|
1555
|
-
seclai
|
|
1556
|
-
seclai
|
|
1557
|
-
seclai
|
|
1558
|
-
seclai
|
|
1559
|
-
seclai
|
|
1560
|
-
seclai alerts unsubscribe <alertId>
|
|
2079
|
+
seclai contents get <contentVersionId> [--start N] [--end N]
|
|
2080
|
+
seclai contents delete <contentVersionId>
|
|
2081
|
+
seclai contents upload <contentVersionId> --file ./updated.pdf [--title "Title"] [--file-name name] [--mime-type type]
|
|
2082
|
+
seclai contents replace-text <contentVersionId> --json '{"text":"Replacement text","title":"Updated"}'
|
|
2083
|
+
seclai contents embeddings <contentVersionId> [--page N] [--limit N]
|
|
1561
2084
|
\`\`\`
|
|
1562
2085
|
|
|
1563
|
-
|
|
2086
|
+
\`--start\` / \`--end\` on \`contents get\` slice the returned text by character
|
|
2087
|
+
offset, which is how you inspect a long document without pulling all of it.
|
|
2088
|
+
|
|
2089
|
+
## Knowledge bases
|
|
1564
2090
|
|
|
1565
2091
|
\`\`\`bash
|
|
1566
|
-
seclai
|
|
1567
|
-
seclai
|
|
1568
|
-
seclai
|
|
1569
|
-
seclai
|
|
1570
|
-
seclai
|
|
2092
|
+
seclai kb list [--page N] [--limit N] [--sort field] [--order asc|desc]
|
|
2093
|
+
seclai kb create --json '{"name":"Support KB","description":"Customer support articles"}'
|
|
2094
|
+
seclai kb get <kbId>
|
|
2095
|
+
seclai kb update <kbId> --json '{"name":"Updated KB"}'
|
|
2096
|
+
seclai kb delete <kbId>
|
|
1571
2097
|
\`\`\`
|
|
1572
2098
|
|
|
1573
|
-
|
|
2099
|
+
## Memory banks
|
|
1574
2100
|
|
|
1575
2101
|
\`\`\`bash
|
|
1576
|
-
seclai
|
|
1577
|
-
|
|
2102
|
+
seclai memory list [--page N] [--limit N] [--sort field] [--order asc|desc]
|
|
2103
|
+
# type: "conversation" (chat history) or "general" (structured facts)
|
|
2104
|
+
seclai memory create --json '{"name":"Chat Memory","type":"conversation"}'
|
|
2105
|
+
seclai memory get <memoryBankId>
|
|
2106
|
+
seclai memory update <memoryBankId> --json '{"name":"Renamed"}'
|
|
2107
|
+
seclai memory delete <memoryBankId>
|
|
1578
2108
|
\`\`\`
|
|
1579
2109
|
|
|
1580
|
-
###
|
|
2110
|
+
### Utilities
|
|
1581
2111
|
|
|
1582
2112
|
\`\`\`bash
|
|
1583
|
-
seclai
|
|
1584
|
-
seclai
|
|
1585
|
-
seclai
|
|
1586
|
-
seclai
|
|
2113
|
+
seclai memory stats <memoryBankId>
|
|
2114
|
+
seclai memory agents <memoryBankId> # agents using this bank
|
|
2115
|
+
seclai memory compact <memoryBankId>
|
|
2116
|
+
seclai memory delete-source <memoryBankId>
|
|
2117
|
+
seclai memory templates
|
|
2118
|
+
seclai memory test-compaction <memoryBankId> --json '{"prompt":"Summarize the conversation"}'
|
|
2119
|
+
seclai memory test-compaction-standalone --json '{"prompt":"Summarize the conversation"}'
|
|
1587
2120
|
\`\`\`
|
|
1588
2121
|
|
|
1589
|
-
|
|
2122
|
+
Both \`test-compaction\` commands are dry runs \u2014 they show what compaction would
|
|
2123
|
+
produce without writing to the bank.
|
|
2124
|
+
|
|
2125
|
+
### Memory bank AI
|
|
2126
|
+
|
|
2127
|
+
\`\`\`bash
|
|
2128
|
+
seclai memory ai generate --user-input "Configure compaction for chat memory"
|
|
2129
|
+
seclai memory ai last
|
|
2130
|
+
seclai memory ai accept <conversationId> --json '{"accepted":true}'
|
|
2131
|
+
\`\`\`
|
|
2132
|
+
|
|
2133
|
+
## Example: create a source and upload content
|
|
2134
|
+
|
|
2135
|
+
\`\`\`bash
|
|
2136
|
+
seclai sources create --json '{"name":"Product Docs","description":"Product documentation source"}'
|
|
2137
|
+
# note the id from the output
|
|
2138
|
+
seclai sources upload <sourceId> --file ./docs.pdf --title "Product Manual" --metadata '{"version":"2.0"}'
|
|
2139
|
+
seclai sources get <sourceId>
|
|
2140
|
+
\`\`\`
|
|
2141
|
+
` },
|
|
2142
|
+
{ name: "references/models.md", content: `# Models
|
|
2143
|
+
|
|
2144
|
+
The model catalog, media-generation tiers, model-catalog alerts, recommendations
|
|
2145
|
+
and the playground.
|
|
2146
|
+
|
|
2147
|
+
## Catalog
|
|
2148
|
+
|
|
2149
|
+
\`\`\`bash
|
|
2150
|
+
seclai models list [--provider <name>] [--supports-tool-use] [--supports-thinking]
|
|
2151
|
+
seclai models list [--supports-input-media <media>] [--supports-output-media <media>]
|
|
2152
|
+
seclai models get <modelId>
|
|
2153
|
+
|
|
2154
|
+
# each media-generation modality and tier, with its model and cost
|
|
2155
|
+
seclai models tiers
|
|
2156
|
+
\`\`\`
|
|
2157
|
+
|
|
2158
|
+
The capability flags compose, so \`--supports-tool-use --supports-thinking\`
|
|
2159
|
+
returns only models with both. \`--supports-input-media\` / \`--supports-output-media\`
|
|
2160
|
+
take a modality such as \`image\`, \`audio\` or \`video\`.
|
|
2161
|
+
|
|
2162
|
+
## Model alerts
|
|
1590
2163
|
|
|
1591
2164
|
\`\`\`bash
|
|
1592
2165
|
seclai models alerts list [--page N] [--limit N]
|
|
1593
2166
|
seclai models alerts mark-read <alertId>
|
|
1594
2167
|
seclai models alerts mark-all-read
|
|
1595
2168
|
seclai models alerts unread-count
|
|
2169
|
+
\`\`\`
|
|
2170
|
+
|
|
2171
|
+
These are catalog alerts \u2014 deprecations, price changes, new models \u2014 not the
|
|
2172
|
+
account alerts in [alerts.md](alerts.md).
|
|
2173
|
+
|
|
2174
|
+
## Recommendations
|
|
2175
|
+
|
|
2176
|
+
\`\`\`bash
|
|
1596
2177
|
seclai models recommendations <modelId>
|
|
1597
2178
|
\`\`\`
|
|
1598
2179
|
|
|
1599
|
-
|
|
2180
|
+
Suggests replacements for a model, which is how you act on a deprecation alert.
|
|
2181
|
+
|
|
2182
|
+
## Playground experiments
|
|
1600
2183
|
|
|
1601
2184
|
\`\`\`bash
|
|
1602
|
-
seclai
|
|
2185
|
+
seclai models experiments list [--days N] [--start-date <date>] [--end-date <date>] [--limit N] [--offset N]
|
|
2186
|
+
seclai models experiments create --json '{"model_ids":["gpt-4o"],"prompt":"Compare responses"}'
|
|
2187
|
+
seclai models experiments get <experimentId>
|
|
2188
|
+
seclai models experiments cancel <experimentId>
|
|
2189
|
+
seclai models experiments delete <experimentId> # soft-delete, preserves audit history
|
|
1603
2190
|
\`\`\`
|
|
1604
2191
|
|
|
1605
|
-
|
|
2192
|
+
\`create\` takes several \`model_ids\` and runs the same prompt against each, which
|
|
2193
|
+
is the point \u2014 side-by-side comparison. \`cancel\` stops a running experiment;
|
|
2194
|
+
\`delete\` soft-deletes a finished one.
|
|
2195
|
+
` },
|
|
2196
|
+
{ name: "references/setup.md", content: `# Setup: authentication, profiles, API version, editor integration
|
|
2197
|
+
|
|
2198
|
+
## SSO authentication
|
|
1606
2199
|
|
|
1607
2200
|
\`\`\`bash
|
|
1608
|
-
seclai
|
|
1609
|
-
seclai
|
|
1610
|
-
seclai
|
|
1611
|
-
seclai
|
|
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}'
|
|
2201
|
+
seclai auth login [--port <port>] [--no-browser] # OAuth2 + PKCE in the browser
|
|
2202
|
+
seclai auth status # active profile's auth state
|
|
2203
|
+
seclai auth refresh # refresh the token manually
|
|
2204
|
+
seclai auth logout # clear cached tokens
|
|
1617
2205
|
\`\`\`
|
|
1618
2206
|
|
|
1619
|
-
|
|
2207
|
+
Tokens are cached under the config directory and refreshed automatically, so
|
|
2208
|
+
\`auth refresh\` is only needed to force it. An API key in \`SECLAI_API_KEY\` takes a
|
|
2209
|
+
different path entirely and needs none of this.
|
|
2210
|
+
|
|
2211
|
+
## Profiles
|
|
1620
2212
|
|
|
1621
2213
|
\`\`\`bash
|
|
1622
|
-
|
|
1623
|
-
seclai
|
|
2214
|
+
seclai configure sso [--profile-name <name>] # interactive: domain, client ID, region, account ID
|
|
2215
|
+
seclai configure list # every configured profile
|
|
1624
2216
|
\`\`\`
|
|
1625
2217
|
|
|
1626
|
-
|
|
2218
|
+
Profiles live in \`~/.seclai/config\` (override with \`--config-dir\` or
|
|
2219
|
+
\`SECLAI_CONFIG_DIR\`). Select one per invocation with \`--profile <name>\`, or set
|
|
2220
|
+
\`SECLAI_PROFILE\`.
|
|
2221
|
+
|
|
2222
|
+
## API version
|
|
1627
2223
|
|
|
1628
2224
|
\`\`\`bash
|
|
1629
|
-
|
|
2225
|
+
seclai api-version get # what version does a request resolve to?
|
|
2226
|
+
seclai api-version set <date> # pin the account \u2014 affects every client
|
|
2227
|
+
seclai api-version clear # remove the pin
|
|
2228
|
+
\`\`\`
|
|
2229
|
+
|
|
2230
|
+
\`set\` and \`clear\` change the account, not just this CLI. To affect only your own
|
|
2231
|
+
invocation, use the \`--api-version\` global option instead.
|
|
2232
|
+
|
|
2233
|
+
## MCP server
|
|
2234
|
+
|
|
2235
|
+
\`\`\`bash
|
|
2236
|
+
# write Seclai MCP server config into AI coding tool config files
|
|
1630
2237
|
seclai mcp configure --key <apiKey> [--target claude-code|cursor|claude-desktop|windsurf|all] [--dir .]
|
|
1631
2238
|
|
|
1632
|
-
#
|
|
2239
|
+
# print the config JSON for manual setup
|
|
1633
2240
|
seclai mcp show [--key <apiKey>]
|
|
1634
2241
|
\`\`\`
|
|
1635
2242
|
|
|
1636
|
-
##
|
|
2243
|
+
## Skill files
|
|
1637
2244
|
|
|
1638
2245
|
\`\`\`bash
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
seclai sources upload <sourceId> --file ./docs.pdf --title "Product Manual" --metadata '{"version":"2.0"}'
|
|
1642
|
-
seclai sources get <sourceId>
|
|
2246
|
+
# install these skill files into AI coding tool directories
|
|
2247
|
+
seclai skills install [--tool copilot|claude|cursor|windsurf|codex|kiro|cline|roo|gemini|antigravity|all] [--dir .]
|
|
1643
2248
|
\`\`\`
|
|
1644
2249
|
|
|
1645
|
-
|
|
2250
|
+
With no \`--tool\`, the target is detected from the directory structure.
|
|
2251
|
+
|
|
2252
|
+
## Shell completion
|
|
1646
2253
|
|
|
1647
2254
|
\`\`\`bash
|
|
1648
|
-
seclai
|
|
1649
|
-
seclai
|
|
1650
|
-
seclai
|
|
1651
|
-
seclai agents ai mark <agentId> <conversationId> --json '{"accepted":true}'
|
|
1652
|
-
seclai agents run <agentId> --json '{"input":"How do I reset my password?"}' --stream
|
|
2255
|
+
seclai completion bash # eval "$(seclai completion bash)" in ~/.bashrc
|
|
2256
|
+
seclai completion zsh # eval "$(seclai completion zsh)" in ~/.zshrc
|
|
2257
|
+
seclai completion fish # seclai completion fish > ~/.config/fish/completions/seclai.fish
|
|
1653
2258
|
\`\`\`
|
|
2259
|
+
` },
|
|
2260
|
+
{ name: "references/solutions.md", content: `# Solutions and governance
|
|
1654
2261
|
|
|
1655
|
-
|
|
2262
|
+
Solutions group agents, knowledge bases and sources into one deliverable.
|
|
2263
|
+
Governance defines the policies applied to agent input and output.
|
|
2264
|
+
|
|
2265
|
+
## Solutions
|
|
1656
2266
|
|
|
1657
2267
|
\`\`\`bash
|
|
1658
|
-
|
|
1659
|
-
seclai
|
|
1660
|
-
|
|
1661
|
-
seclai
|
|
1662
|
-
|
|
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>
|
|
2268
|
+
seclai solutions list [--page N] [--limit N] [--sort field] [--order asc|desc]
|
|
2269
|
+
seclai solutions create --json '{"name":"Customer Support Solution"}'
|
|
2270
|
+
seclai solutions get <solutionId>
|
|
2271
|
+
seclai solutions update <solutionId> --json '{"name":"Updated"}'
|
|
2272
|
+
seclai solutions delete <solutionId>
|
|
1668
2273
|
\`\`\`
|
|
1669
2274
|
|
|
1670
|
-
##
|
|
2275
|
+
## Linking resources
|
|
1671
2276
|
|
|
1672
2277
|
\`\`\`bash
|
|
1673
|
-
|
|
1674
|
-
seclai solutions link <solutionId> --agents '["
|
|
1675
|
-
seclai solutions
|
|
2278
|
+
# each flag takes a JSON array of IDs
|
|
2279
|
+
seclai solutions link <solutionId> --agents '["agentId1"]' --kb '["kbId1"]' --sources '["sourceId1"]'
|
|
2280
|
+
seclai solutions unlink <solutionId> --agents '["agentId1"]'
|
|
1676
2281
|
\`\`\`
|
|
1677
2282
|
|
|
1678
|
-
##
|
|
2283
|
+
## Conversations
|
|
1679
2284
|
|
|
1680
2285
|
\`\`\`bash
|
|
1681
|
-
seclai
|
|
1682
|
-
seclai
|
|
1683
|
-
seclai
|
|
1684
|
-
seclai agents ai mark <agentId> <conversationId> --json '{"accepted":true}'
|
|
2286
|
+
seclai solutions convos list <solutionId>
|
|
2287
|
+
seclai solutions convos add <solutionId> --json '{"message":"How should I structure this?"}'
|
|
2288
|
+
seclai solutions convos mark <solutionId> <conversationId> --json '{"accepted":true}'
|
|
1685
2289
|
\`\`\`
|
|
1686
2290
|
|
|
1687
|
-
##
|
|
2291
|
+
## Solution AI
|
|
1688
2292
|
|
|
1689
2293
|
\`\`\`bash
|
|
1690
|
-
seclai
|
|
2294
|
+
seclai solutions ai generate <solutionId> --user-input "Add an FAQ source"
|
|
2295
|
+
seclai solutions ai kb <solutionId> --user-input "Create a knowledge base for docs"
|
|
2296
|
+
seclai solutions ai source <solutionId> --user-input "Create a file source for PDFs"
|
|
2297
|
+
seclai solutions ai accept <solutionId> <conversationId> --json '{"accepted":true}'
|
|
2298
|
+
seclai solutions ai decline <solutionId> <conversationId>
|
|
2299
|
+
\`\`\`
|
|
2300
|
+
|
|
2301
|
+
\`ai kb\` and \`ai source\` create the resource and link it to the solution in one
|
|
2302
|
+
step, which is why they live here rather than under \`kb\` or \`sources\`.
|
|
2303
|
+
|
|
2304
|
+
## Governance AI
|
|
2305
|
+
|
|
2306
|
+
\`\`\`bash
|
|
2307
|
+
seclai governance ai generate --user-input "Create a content safety policy"
|
|
1691
2308
|
seclai governance ai list
|
|
1692
2309
|
seclai governance ai accept <conversationId>
|
|
2310
|
+
seclai governance ai decline <conversationId>
|
|
1693
2311
|
\`\`\`
|
|
1694
2312
|
|
|
1695
|
-
|
|
2313
|
+
A generated policy is a proposal until accepted \u2014 \`generate\` alone changes
|
|
2314
|
+
nothing.
|
|
1696
2315
|
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
2316
|
+
## Example: solution with linked resources
|
|
2317
|
+
|
|
2318
|
+
\`\`\`bash
|
|
2319
|
+
seclai solutions create --json '{"name":"Customer Support"}'
|
|
2320
|
+
seclai solutions link <solutionId> --agents '["<agentId>"]' --kb '["<kbId>"]' --sources '["<sourceId>"]'
|
|
2321
|
+
seclai solutions get <solutionId>
|
|
2322
|
+
\`\`\`
|
|
2323
|
+
|
|
2324
|
+
## Example: governance policy setup
|
|
2325
|
+
|
|
2326
|
+
\`\`\`bash
|
|
2327
|
+
seclai governance ai generate --user-input "Create a content safety policy that blocks harmful outputs"
|
|
2328
|
+
seclai governance ai list
|
|
2329
|
+
seclai governance ai accept <conversationId>
|
|
2330
|
+
\`\`\`
|
|
2331
|
+
` },
|
|
2332
|
+
{ name: "references/streaming.md", content: `# Streaming Agent Runs
|
|
1702
2333
|
|
|
1703
2334
|
## Modes
|
|
1704
2335
|
|
|
@@ -1750,8 +2381,8 @@ seclai agents run <agentId> --json '{"input":"Hello"}'
|
|
|
1750
2381
|
# check later:
|
|
1751
2382
|
seclai agents runs get <runId>
|
|
1752
2383
|
\`\`\`
|
|
1753
|
-
|
|
1754
|
-
|
|
2384
|
+
` },
|
|
2385
|
+
{ name: "references/uploads.md", content: `# File Uploads & Content Management
|
|
1755
2386
|
|
|
1756
2387
|
## Upload to a source
|
|
1757
2388
|
\`\`\`bash
|
|
@@ -1767,11 +2398,21 @@ seclai sources upload-text <sourceId> --json '{"text":"Article content here...",
|
|
|
1767
2398
|
|
|
1768
2399
|
## Upload input for agent runs
|
|
1769
2400
|
\`\`\`bash
|
|
2401
|
+
# Check what files (if any) the agent expects before uploading. requires_uploads
|
|
2402
|
+
# reports whether the agent accepts files; the agent block lists the exact names /
|
|
2403
|
+
# indexes / patterns a run-time batch must satisfy.
|
|
2404
|
+
seclai agents attachment-references <agentId>
|
|
1770
2405
|
seclai agents upload-input <agentId> --file ./input.pdf
|
|
1771
2406
|
seclai agents upload-input <agentId> --file ./data.csv --file-name "report.csv" --mime-type "text/csv"
|
|
1772
2407
|
seclai agents input-status <agentId> <uploadId>
|
|
1773
2408
|
\`\`\`
|
|
1774
2409
|
|
|
2410
|
+
## Download an attachment emitted by a run
|
|
2411
|
+
\`\`\`bash
|
|
2412
|
+
# attachmentId is the URL-safe-base64 storage_key from run output manifests / webhooks.
|
|
2413
|
+
seclai agents runs download-attachment <runId> <attachmentId> --output ./out.pdf
|
|
2414
|
+
\`\`\`
|
|
2415
|
+
|
|
1775
2416
|
## Replace content
|
|
1776
2417
|
\`\`\`bash
|
|
1777
2418
|
# replace with file
|
|
@@ -1792,61 +2433,10 @@ seclai contents get <contentVersionId> --start 0 --end 1000
|
|
|
1792
2433
|
# view embeddings
|
|
1793
2434
|
seclai contents embeddings <contentVersionId> [--page N] [--limit N]
|
|
1794
2435
|
\`\`\`
|
|
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
|
-
`;
|
|
2436
|
+
` }
|
|
2437
|
+
];
|
|
1843
2438
|
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
|
-
];
|
|
2439
|
+
const skillFiles = SKILL_FILES;
|
|
1850
2440
|
switch (tool) {
|
|
1851
2441
|
case "copilot":
|
|
1852
2442
|
return { dir: join(destDir, ".github", "copilot", "seclai-cli"), files: skillFiles };
|
|
@@ -1888,7 +2478,7 @@ function detectTools(destDir) {
|
|
|
1888
2478
|
if (existsSync(join(destDir, ".antigravity"))) detected.push("antigravity");
|
|
1889
2479
|
return detected;
|
|
1890
2480
|
}
|
|
1891
|
-
function
|
|
2481
|
+
function register15(program, rt) {
|
|
1892
2482
|
const skills = program.command("skills").description("Install Seclai CLI skill files for AI coding tools.");
|
|
1893
2483
|
skills.command("install").description(
|
|
1894
2484
|
"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 +2579,7 @@ function detectTargets(destDir) {
|
|
|
1989
2579
|
return false;
|
|
1990
2580
|
});
|
|
1991
2581
|
}
|
|
1992
|
-
function
|
|
2582
|
+
function register16(program, rt) {
|
|
1993
2583
|
const mcp = program.command("mcp").description("Configure the Seclai MCP server for AI coding tools.");
|
|
1994
2584
|
mcp.command("configure").description(
|
|
1995
2585
|
"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."
|
|
@@ -2055,15 +2645,16 @@ _seclai_completions() {
|
|
|
2055
2645
|
prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
2056
2646
|
|
|
2057
2647
|
# Top-level commands
|
|
2058
|
-
commands="agents sources contents kb memory evals solutions governance alerts models search ai skills mcp completion help"
|
|
2648
|
+
commands="agents sources contents kb memory evals solutions governance alerts email models search docs me api-version ai skills mcp completion auth configure help"
|
|
2059
2649
|
|
|
2060
2650
|
case "\${COMP_WORDS[1]}" in
|
|
2061
2651
|
agents)
|
|
2062
2652
|
case "\${COMP_WORDS[2]}" in
|
|
2063
|
-
runs)
|
|
2064
|
-
def)
|
|
2065
|
-
ai)
|
|
2066
|
-
|
|
2653
|
+
runs) COMPREPLY=( $(compgen -W "list get delete cancel search eval-results download-attachment" -- "$cur") ); return ;;
|
|
2654
|
+
def) COMPREPLY=( $(compgen -W "get update" -- "$cur") ); return ;;
|
|
2655
|
+
ai) COMPREPLY=( $(compgen -W "gen-steps step-config history mark" -- "$cur") ); return ;;
|
|
2656
|
+
triggers) COMPREPLY=( $(compgen -W "email-config" -- "$cur") ); return ;;
|
|
2657
|
+
*) COMPREPLY=( $(compgen -W "list create get update delete disable enable callers triggers run runs def export preview-import upload-input input-status attachment-references ai" -- "$cur") ); return ;;
|
|
2067
2658
|
esac ;;
|
|
2068
2659
|
sources|source)
|
|
2069
2660
|
case "\${COMP_WORDS[2]}" in
|
|
@@ -2101,12 +2692,24 @@ _seclai_completions() {
|
|
|
2101
2692
|
prefs) COMPREPLY=( $(compgen -W "list update" -- "$cur") ); return ;;
|
|
2102
2693
|
*) COMPREPLY=( $(compgen -W "list get status comment subscribe unsubscribe configs prefs" -- "$cur") ); return ;;
|
|
2103
2694
|
esac ;;
|
|
2695
|
+
email)
|
|
2696
|
+
case "\${COMP_WORDS[2]}" in
|
|
2697
|
+
domains) COMPREPLY=( $(compgen -W "list add remove verify set-primary use-shared test-email dmarc" -- "$cur") ); return ;;
|
|
2698
|
+
blocked) COMPREPLY=( $(compgen -W "list add remove auto-block-mode" -- "$cur") ); return ;;
|
|
2699
|
+
inbound) COMPREPLY=( $(compgen -W "status rejections cancel-queued resume" -- "$cur") ); return ;;
|
|
2700
|
+
optouts) COMPREPLY=( $(compgen -W "list remove" -- "$cur") ); return ;;
|
|
2701
|
+
*) COMPREPLY=( $(compgen -W "domains blocked inbound optouts" -- "$cur") ); return ;;
|
|
2702
|
+
esac ;;
|
|
2104
2703
|
models)
|
|
2105
2704
|
case "\${COMP_WORDS[2]}" in
|
|
2106
2705
|
alerts) COMPREPLY=( $(compgen -W "list mark-read mark-all-read unread-count" -- "$cur") ); return ;;
|
|
2107
2706
|
experiments) COMPREPLY=( $(compgen -W "list create get cancel delete" -- "$cur") ); return ;;
|
|
2108
|
-
*) COMPREPLY=( $(compgen -W "alerts recommendations experiments" -- "$cur") ); return ;;
|
|
2707
|
+
*) COMPREPLY=( $(compgen -W "list get tiers alerts recommendations experiments" -- "$cur") ); return ;;
|
|
2109
2708
|
esac ;;
|
|
2709
|
+
docs) COMPREPLY=( $(compgen -W "search" -- "$cur") ); return ;;
|
|
2710
|
+
api-version) COMPREPLY=( $(compgen -W "get set clear" -- "$cur") ); return ;;
|
|
2711
|
+
auth) COMPREPLY=( $(compgen -W "login logout status refresh" -- "$cur") ); return ;;
|
|
2712
|
+
configure) COMPREPLY=( $(compgen -W "sso list" -- "$cur") ); return ;;
|
|
2110
2713
|
ai) COMPREPLY=( $(compgen -W "feedback kb source solution memory memory-history accept decline memory-accept" -- "$cur") ); return ;;
|
|
2111
2714
|
skills) COMPREPLY=( $(compgen -W "install" -- "$cur") ); return ;;
|
|
2112
2715
|
mcp) COMPREPLY=( $(compgen -W "configure show" -- "$cur") ); return ;;
|
|
@@ -2134,17 +2737,28 @@ _seclai() {
|
|
|
2134
2737
|
'solutions:Manage solutions'
|
|
2135
2738
|
'governance:Governance AI assistant'
|
|
2136
2739
|
'alerts:Manage alerts and alert configurations'
|
|
2137
|
-
'
|
|
2740
|
+
'email:Agent email domains, blocklist, inbound health, opt-outs'
|
|
2741
|
+
'models:Models, model alerts, recommendations, experiments'
|
|
2138
2742
|
'search:Search across Seclai resources'
|
|
2743
|
+
'docs:Search the Seclai documentation'
|
|
2744
|
+
'me:Show the authenticated user and organizations'
|
|
2745
|
+
'api-version:Read or pin the dated API version'
|
|
2139
2746
|
'ai:Top-level AI assistant'
|
|
2140
2747
|
'skills:Install skill files for AI coding tools'
|
|
2141
2748
|
'mcp:Configure the Seclai MCP server'
|
|
2142
2749
|
'completion:Generate shell completion scripts'
|
|
2750
|
+
'auth:SSO authentication'
|
|
2751
|
+
'configure:Manage SSO profiles'
|
|
2143
2752
|
'help:Display help for command'
|
|
2144
2753
|
)
|
|
2145
2754
|
|
|
2146
2755
|
_arguments -C \\
|
|
2147
2756
|
'--api-key[Seclai API key]:key' \\
|
|
2757
|
+
'--profile[SSO profile name]:name' \\
|
|
2758
|
+
'--account-id[Account ID (X-Account-Id header)]:id' \\
|
|
2759
|
+
'--config-dir[Config directory]:path' \\
|
|
2760
|
+
'--api-version[Dated API version (YYYY-MM-DD)]:date' \\
|
|
2761
|
+
'--allow-unknown-api-version[Permit an unrecognized --api-version]' \\
|
|
2148
2762
|
'--compact[Output compact JSON]' \\
|
|
2149
2763
|
'-V[Output version]' \\
|
|
2150
2764
|
'-h[Display help]' \\
|
|
@@ -2156,7 +2770,7 @@ _seclai() {
|
|
|
2156
2770
|
args)
|
|
2157
2771
|
case \${words[1]} in
|
|
2158
2772
|
agents)
|
|
2159
|
-
local -a sub=(list create get update delete run runs def export preview-import upload-input input-status attachment-references ai)
|
|
2773
|
+
local -a sub=(list create get update delete disable enable callers triggers run runs def export preview-import upload-input input-status attachment-references ai)
|
|
2160
2774
|
_describe 'subcommand' sub ;;
|
|
2161
2775
|
sources|source)
|
|
2162
2776
|
local -a sub=(list create get update delete upload upload-text exports migration)
|
|
@@ -2182,8 +2796,23 @@ _seclai() {
|
|
|
2182
2796
|
alerts)
|
|
2183
2797
|
local -a sub=(list get status comment subscribe unsubscribe configs prefs)
|
|
2184
2798
|
_describe 'subcommand' sub ;;
|
|
2799
|
+
email)
|
|
2800
|
+
local -a sub=(domains blocked inbound optouts)
|
|
2801
|
+
_describe 'subcommand' sub ;;
|
|
2185
2802
|
models)
|
|
2186
|
-
local -a sub=(alerts recommendations experiments)
|
|
2803
|
+
local -a sub=(list get tiers alerts recommendations experiments)
|
|
2804
|
+
_describe 'subcommand' sub ;;
|
|
2805
|
+
docs)
|
|
2806
|
+
local -a sub=(search)
|
|
2807
|
+
_describe 'subcommand' sub ;;
|
|
2808
|
+
api-version)
|
|
2809
|
+
local -a sub=(get set clear)
|
|
2810
|
+
_describe 'subcommand' sub ;;
|
|
2811
|
+
auth)
|
|
2812
|
+
local -a sub=(login logout status refresh)
|
|
2813
|
+
_describe 'subcommand' sub ;;
|
|
2814
|
+
configure)
|
|
2815
|
+
local -a sub=(sso list)
|
|
2187
2816
|
_describe 'subcommand' sub ;;
|
|
2188
2817
|
ai)
|
|
2189
2818
|
local -a sub=(feedback kb source solution memory memory-history accept decline memory-accept)
|
|
@@ -2206,7 +2835,7 @@ _seclai "$@"
|
|
|
2206
2835
|
var FISH = `# seclai fish completion \u2014 save to ~/.config/fish/completions/seclai.fish
|
|
2207
2836
|
# seclai completion fish > ~/.config/fish/completions/seclai.fish
|
|
2208
2837
|
|
|
2209
|
-
set -l top agents sources contents kb memory evals solutions governance alerts models search ai skills mcp completion help
|
|
2838
|
+
set -l top agents sources contents kb memory evals solutions governance alerts email models search docs me api-version ai skills mcp completion auth configure help
|
|
2210
2839
|
|
|
2211
2840
|
# Top-level
|
|
2212
2841
|
complete -c seclai -n "not __fish_seen_subcommand_from $top" -f -a "agents" -d "Manage agents"
|
|
@@ -2218,15 +2847,21 @@ complete -c seclai -n "not __fish_seen_subcommand_from $top" -f -a "evals" -d "E
|
|
|
2218
2847
|
complete -c seclai -n "not __fish_seen_subcommand_from $top" -f -a "solutions" -d "Solutions"
|
|
2219
2848
|
complete -c seclai -n "not __fish_seen_subcommand_from $top" -f -a "governance" -d "Governance AI"
|
|
2220
2849
|
complete -c seclai -n "not __fish_seen_subcommand_from $top" -f -a "alerts" -d "Alerts"
|
|
2221
|
-
complete -c seclai -n "not __fish_seen_subcommand_from $top" -f -a "
|
|
2850
|
+
complete -c seclai -n "not __fish_seen_subcommand_from $top" -f -a "email" -d "Agent email"
|
|
2851
|
+
complete -c seclai -n "not __fish_seen_subcommand_from $top" -f -a "models" -d "Models and model alerts"
|
|
2222
2852
|
complete -c seclai -n "not __fish_seen_subcommand_from $top" -f -a "search" -d "Search resources"
|
|
2853
|
+
complete -c seclai -n "not __fish_seen_subcommand_from $top" -f -a "docs" -d "Search documentation"
|
|
2854
|
+
complete -c seclai -n "not __fish_seen_subcommand_from $top" -f -a "me" -d "Authenticated user"
|
|
2855
|
+
complete -c seclai -n "not __fish_seen_subcommand_from $top" -f -a "api-version" -d "Dated API version"
|
|
2223
2856
|
complete -c seclai -n "not __fish_seen_subcommand_from $top" -f -a "ai" -d "AI assistant"
|
|
2224
2857
|
complete -c seclai -n "not __fish_seen_subcommand_from $top" -f -a "skills" -d "Skill files"
|
|
2225
2858
|
complete -c seclai -n "not __fish_seen_subcommand_from $top" -f -a "mcp" -d "MCP server config"
|
|
2226
2859
|
complete -c seclai -n "not __fish_seen_subcommand_from $top" -f -a "completion" -d "Shell completions"
|
|
2860
|
+
complete -c seclai -n "not __fish_seen_subcommand_from $top" -f -a "auth" -d "SSO authentication"
|
|
2861
|
+
complete -c seclai -n "not __fish_seen_subcommand_from $top" -f -a "configure" -d "Manage SSO profiles"
|
|
2227
2862
|
|
|
2228
2863
|
# 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"
|
|
2864
|
+
complete -c seclai -n "__fish_seen_subcommand_from agents; and not __fish_seen_subcommand_from list create get update delete disable enable callers triggers run runs def export preview-import upload-input input-status attachment-references ai" -f -a "list create get update delete disable enable callers triggers run runs def export preview-import upload-input input-status attachment-references ai"
|
|
2230
2865
|
|
|
2231
2866
|
# sources
|
|
2232
2867
|
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"
|
|
@@ -2252,8 +2887,23 @@ complete -c seclai -n "__fish_seen_subcommand_from governance; and not __fish_se
|
|
|
2252
2887
|
# alerts
|
|
2253
2888
|
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
2889
|
|
|
2890
|
+
# email
|
|
2891
|
+
complete -c seclai -n "__fish_seen_subcommand_from email; and not __fish_seen_subcommand_from domains blocked inbound optouts" -f -a "domains blocked inbound optouts"
|
|
2892
|
+
|
|
2255
2893
|
# 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"
|
|
2894
|
+
complete -c seclai -n "__fish_seen_subcommand_from models; and not __fish_seen_subcommand_from list get tiers alerts recommendations experiments" -f -a "list get tiers alerts recommendations experiments"
|
|
2895
|
+
|
|
2896
|
+
# docs
|
|
2897
|
+
complete -c seclai -n "__fish_seen_subcommand_from docs; and not __fish_seen_subcommand_from search" -f -a "search"
|
|
2898
|
+
|
|
2899
|
+
# api-version
|
|
2900
|
+
complete -c seclai -n "__fish_seen_subcommand_from api-version; and not __fish_seen_subcommand_from get set clear" -f -a "get set clear"
|
|
2901
|
+
|
|
2902
|
+
# auth
|
|
2903
|
+
complete -c seclai -n "__fish_seen_subcommand_from auth; and not __fish_seen_subcommand_from login logout status refresh" -f -a "login logout status refresh"
|
|
2904
|
+
|
|
2905
|
+
# configure
|
|
2906
|
+
complete -c seclai -n "__fish_seen_subcommand_from configure; and not __fish_seen_subcommand_from sso list" -f -a "sso list"
|
|
2257
2907
|
|
|
2258
2908
|
# ai
|
|
2259
2909
|
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"
|
|
@@ -2269,11 +2919,16 @@ complete -c seclai -n "__fish_seen_subcommand_from completion; and not __fish_se
|
|
|
2269
2919
|
|
|
2270
2920
|
# Global options
|
|
2271
2921
|
complete -c seclai -l api-key -d "Seclai API key"
|
|
2922
|
+
complete -c seclai -l profile -d "SSO profile name"
|
|
2923
|
+
complete -c seclai -l account-id -d "Account ID (X-Account-Id header)"
|
|
2924
|
+
complete -c seclai -l config-dir -d "Config directory"
|
|
2925
|
+
complete -c seclai -l api-version -d "Dated API version (YYYY-MM-DD)"
|
|
2926
|
+
complete -c seclai -l allow-unknown-api-version -d "Permit an unrecognized --api-version"
|
|
2272
2927
|
complete -c seclai -l compact -d "Output compact JSON"
|
|
2273
2928
|
complete -c seclai -s V -l version -d "Output version"
|
|
2274
2929
|
`;
|
|
2275
2930
|
var SCRIPTS = { bash: BASH, zsh: ZSH, fish: FISH };
|
|
2276
|
-
function
|
|
2931
|
+
function register17(program, rt) {
|
|
2277
2932
|
const completion = program.command("completion").description("Generate shell completion scripts.").argument("<shell>", "Shell type: bash, zsh, or fish.").action(async (shell) => {
|
|
2278
2933
|
const script = SCRIPTS[shell];
|
|
2279
2934
|
if (!script) {
|
|
@@ -2447,7 +3102,7 @@ async function loadProfile(rt, opts) {
|
|
|
2447
3102
|
const profile = await loadSsoProfile(configDir, profileName);
|
|
2448
3103
|
return { profile, profileName, configDir };
|
|
2449
3104
|
}
|
|
2450
|
-
function
|
|
3105
|
+
function register18(program, rt) {
|
|
2451
3106
|
const group = program.command("auth").description("SSO authentication (login/logout/status/refresh).");
|
|
2452
3107
|
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
3108
|
await run(rt, async () => {
|
|
@@ -2621,7 +3276,7 @@ function resolveConfigDir(opts) {
|
|
|
2621
3276
|
const home = process4.env.HOME ?? process4.env.USERPROFILE ?? "";
|
|
2622
3277
|
return join4(home, ".seclai");
|
|
2623
3278
|
}
|
|
2624
|
-
function
|
|
3279
|
+
function register19(program, rt) {
|
|
2625
3280
|
const group = program.command("configure").description("Configure CLI profiles and settings.");
|
|
2626
3281
|
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
3282
|
await run(rt, async () => {
|
|
@@ -2734,8 +3389,15 @@ function escapeRegExp(s) {
|
|
|
2734
3389
|
}
|
|
2735
3390
|
|
|
2736
3391
|
// src/cli.ts
|
|
3392
|
+
var VALUED_GLOBAL_OPTIONS = [
|
|
3393
|
+
["apiKey", "--api-key", "reject", "Pass a key, or omit the flag to use SECLAI_API_KEY or SSO."],
|
|
3394
|
+
["profile", "--profile", "reject", "Pass a profile name, or omit the flag to use the default profile."],
|
|
3395
|
+
["accountId", "--account-id", "reject", "Pass an account ID, or omit the flag to use the default org."],
|
|
3396
|
+
["configDir", "--config-dir", "reject", "Pass a directory, or omit the flag to use ~/.seclai."],
|
|
3397
|
+
["apiVersion", "--api-version", "warn", "Pass a YYYY-MM-DD date, or omit the flag to use the account default."]
|
|
3398
|
+
];
|
|
2737
3399
|
function createProgram(rt = defaultRuntime()) {
|
|
2738
|
-
const program = new
|
|
3400
|
+
const program = new Command3();
|
|
2739
3401
|
const cliVersion = getCliVersion();
|
|
2740
3402
|
program.name("seclai").description(
|
|
2741
3403
|
`Seclai Command Line Interface (v${cliVersion})
|
|
@@ -2755,6 +3417,12 @@ All commands return JSON to stdout, making it easy to pipe into jq or other tool
|
|
|
2755
3417
|
).option(
|
|
2756
3418
|
"--config-dir <path>",
|
|
2757
3419
|
"Config directory (defaults to SECLAI_CONFIG_DIR, then ~/.seclai)."
|
|
3420
|
+
).option(
|
|
3421
|
+
"--api-version <date>",
|
|
3422
|
+
"Opt into dated API changes released on or before this YYYY-MM-DD (defaults to SECLAI_API_VERSION; omitted means the account default)."
|
|
3423
|
+
).option(
|
|
3424
|
+
"--allow-unknown-api-version",
|
|
3425
|
+
"Send an --api-version this CLI was not built against instead of rejecting it."
|
|
2758
3426
|
).option(
|
|
2759
3427
|
"--compact",
|
|
2760
3428
|
"Output compact JSON (no indentation)."
|
|
@@ -2767,6 +3435,7 @@ Environment:
|
|
|
2767
3435
|
SECLAI_API_URL Override API base URL (default: https://api.seclai.com)
|
|
2768
3436
|
SECLAI_PROFILE Default SSO profile (alternative to --profile)
|
|
2769
3437
|
SECLAI_CONFIG_DIR Config directory (alternative to --config-dir)
|
|
3438
|
+
SECLAI_API_VERSION Dated API version (alternative to --api-version)
|
|
2770
3439
|
|
|
2771
3440
|
Examples:
|
|
2772
3441
|
seclai agents list
|
|
@@ -2786,6 +3455,15 @@ Examples:
|
|
|
2786
3455
|
program.exitOverride();
|
|
2787
3456
|
program.hook("preAction", (thisCommand) => {
|
|
2788
3457
|
const globalOpts = thisCommand.opts();
|
|
3458
|
+
for (const [key, flag, policy, hint] of VALUED_GLOBAL_OPTIONS) {
|
|
3459
|
+
const value = globalOpts[key];
|
|
3460
|
+
if (typeof value !== "string" || value.length > 0) continue;
|
|
3461
|
+
if (policy === "reject") {
|
|
3462
|
+
throw new Error(`${flag} was given an empty value. ${hint}`);
|
|
3463
|
+
}
|
|
3464
|
+
delete globalOpts[key];
|
|
3465
|
+
warnDeprecated(rt, `${flag} was given an empty value and is being ignored. ${hint}`);
|
|
3466
|
+
}
|
|
2789
3467
|
rt.compact = Boolean(globalOpts.compact);
|
|
2790
3468
|
});
|
|
2791
3469
|
register(program, rt);
|
|
@@ -2805,6 +3483,8 @@ Examples:
|
|
|
2805
3483
|
register15(program, rt);
|
|
2806
3484
|
register16(program, rt);
|
|
2807
3485
|
register17(program, rt);
|
|
3486
|
+
register18(program, rt);
|
|
3487
|
+
register19(program, rt);
|
|
2808
3488
|
return program;
|
|
2809
3489
|
}
|
|
2810
3490
|
async function runCli(argv, rt = defaultRuntime()) {
|