@imbrace/cli 0.4.0 → 0.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/dist/commands/ai-agent/create.d.ts +1 -0
- package/dist/commands/ai-agent/create.js +5 -0
- package/dist/commands/document-ai/create.d.ts +18 -0
- package/dist/commands/document-ai/create.js +76 -0
- package/dist/commands/document-ai/delete.d.ts +13 -0
- package/dist/commands/document-ai/delete.js +41 -0
- package/dist/commands/document-ai/get.d.ts +12 -0
- package/dist/commands/document-ai/get.js +45 -0
- package/dist/commands/document-ai/list.d.ts +11 -0
- package/dist/commands/document-ai/list.js +42 -0
- package/dist/commands/document-ai/process.d.ts +21 -0
- package/dist/commands/document-ai/process.js +58 -0
- package/dist/commands/document-ai/suggest-schema.d.ts +12 -0
- package/dist/commands/document-ai/suggest-schema.js +37 -0
- package/dist/commands/document-ai/update.d.ts +20 -0
- package/dist/commands/document-ai/update.js +76 -0
- package/dist/commands/guardrail/create.d.ts +19 -0
- package/dist/commands/guardrail/create.js +69 -0
- package/dist/commands/guardrail/delete.d.ts +13 -0
- package/dist/commands/guardrail/delete.js +41 -0
- package/dist/commands/guardrail/get.d.ts +12 -0
- package/dist/commands/guardrail/get.js +42 -0
- package/dist/commands/guardrail/list.d.ts +9 -0
- package/dist/commands/guardrail/list.js +40 -0
- package/dist/commands/guardrail/update.d.ts +20 -0
- package/dist/commands/guardrail/update.js +49 -0
- package/dist/commands/orchestrator/create.d.ts +18 -0
- package/dist/commands/orchestrator/create.js +68 -0
- package/dist/commands/orchestrator/delete.d.ts +13 -0
- package/dist/commands/orchestrator/delete.js +41 -0
- package/dist/commands/orchestrator/get.d.ts +12 -0
- package/dist/commands/orchestrator/get.js +52 -0
- package/dist/commands/orchestrator/list.d.ts +9 -0
- package/dist/commands/orchestrator/list.js +41 -0
- package/dist/lib/ai-agent.d.ts +8 -0
- package/dist/lib/ai-agent.js +63 -6
- package/llms.txt +55 -0
- package/package.json +11 -2
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Args, Flags } from "@oclif/core";
|
|
2
|
+
import { BaseCommand } from "../../base-command.js";
|
|
3
|
+
import { confirm, input } from "@inquirer/prompts";
|
|
4
|
+
import { getClient } from "../../lib/client.js";
|
|
5
|
+
export default class GuardrailDelete extends BaseCommand {
|
|
6
|
+
static description = "Delete a Guardrail. Any AI agent attached via --guardrail-id will lose its safety rules.";
|
|
7
|
+
static examples = [
|
|
8
|
+
"imbrace guardrail delete <id> --yes --json",
|
|
9
|
+
];
|
|
10
|
+
static args = {
|
|
11
|
+
id: Args.string({ description: "Guardrail ID" }),
|
|
12
|
+
};
|
|
13
|
+
static flags = {
|
|
14
|
+
yes: Flags.boolean({ char: "y", description: "Skip confirmation" }),
|
|
15
|
+
json: Flags.boolean({ description: "Output as JSON" }),
|
|
16
|
+
};
|
|
17
|
+
async run() {
|
|
18
|
+
const { args, flags } = await this.parse(GuardrailDelete);
|
|
19
|
+
const id = args.id ?? (flags.json ? this.error("ID is required") : await input({ message: "Guardrail ID:" }));
|
|
20
|
+
if (!flags.yes) {
|
|
21
|
+
const ok = await confirm({ message: `Delete guardrail ${id}?`, default: false });
|
|
22
|
+
if (!ok) {
|
|
23
|
+
this.log("Cancelled.");
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const client = getClient();
|
|
29
|
+
await client.ai.deleteGuardrail(id);
|
|
30
|
+
const message = "Guardrail deleted";
|
|
31
|
+
if (flags.json) {
|
|
32
|
+
this.log(JSON.stringify({ ok: true, message }, null, 2));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
this.log(`\n✅ ${message}\n`);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
this.error(`Failed: ${error.message}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BaseCommand } from "../../base-command.js";
|
|
2
|
+
export default class GuardrailGet extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static args: {
|
|
6
|
+
id: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
static flags: {
|
|
9
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
};
|
|
11
|
+
run(): Promise<void>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Args, Flags } from "@oclif/core";
|
|
2
|
+
import { BaseCommand } from "../../base-command.js";
|
|
3
|
+
import { input } from "@inquirer/prompts";
|
|
4
|
+
import { getClient } from "../../lib/client.js";
|
|
5
|
+
export default class GuardrailGet extends BaseCommand {
|
|
6
|
+
static description = "Get details of a single guardrail";
|
|
7
|
+
static examples = [
|
|
8
|
+
"imbrace guardrail get <guardrailId>",
|
|
9
|
+
"imbrace guardrail get <guardrailId> --json",
|
|
10
|
+
];
|
|
11
|
+
static args = {
|
|
12
|
+
id: Args.string({ description: "Guardrail ID" }),
|
|
13
|
+
};
|
|
14
|
+
static flags = {
|
|
15
|
+
json: Flags.boolean({ description: "Output as JSON" }),
|
|
16
|
+
};
|
|
17
|
+
async run() {
|
|
18
|
+
const { args, flags } = await this.parse(GuardrailGet);
|
|
19
|
+
const id = args.id ?? (flags.json ? this.error("ID is required") : await input({ message: "Guardrail ID:" }));
|
|
20
|
+
try {
|
|
21
|
+
const client = getClient();
|
|
22
|
+
const data = await client.ai.getGuardrail(id);
|
|
23
|
+
if (flags.json) {
|
|
24
|
+
this.log(JSON.stringify({ ok: true, data }, null, 2));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
this.log(`\n ID: ${data.guardrails_config_id || data._id || ""}`);
|
|
28
|
+
this.log(` Name: ${data.name || ""}`);
|
|
29
|
+
this.log(` Model: ${data.model || ""}`);
|
|
30
|
+
this.log(` Provider ID: ${data.guardrail_provider_id || ""}`);
|
|
31
|
+
this.log(` Description: ${(data.description || "").slice(0, 80)}`);
|
|
32
|
+
if (data.unsafe_categories)
|
|
33
|
+
this.log(` Unsafe categories: ${(data.unsafe_categories || []).join(", ") || "(none)"}`);
|
|
34
|
+
if (data.competitor_keywords)
|
|
35
|
+
this.log(` Competitors: ${(data.competitor_keywords || []).join(", ") || "(none)"}`);
|
|
36
|
+
this.log("");
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
this.error(`Failed: ${error.message}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { BaseCommand } from "../../base-command.js";
|
|
2
|
+
export default class GuardrailList extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
};
|
|
8
|
+
run(): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Flags } from "@oclif/core";
|
|
2
|
+
import { BaseCommand } from "../../base-command.js";
|
|
3
|
+
import { getClient } from "../../lib/client.js";
|
|
4
|
+
export default class GuardrailList extends BaseCommand {
|
|
5
|
+
static description = "List Guardrails (safety rules + compliance constraints attached to AI agents via --guardrail-id)";
|
|
6
|
+
static examples = [
|
|
7
|
+
"imbrace guardrail list",
|
|
8
|
+
"imbrace guardrail list --json",
|
|
9
|
+
];
|
|
10
|
+
static flags = {
|
|
11
|
+
json: Flags.boolean({ description: "Output as JSON" }),
|
|
12
|
+
};
|
|
13
|
+
async run() {
|
|
14
|
+
const { flags } = await this.parse(GuardrailList);
|
|
15
|
+
try {
|
|
16
|
+
const client = getClient();
|
|
17
|
+
const res = await client.ai.listGuardrails();
|
|
18
|
+
// Backend returns a raw array, not { data, total }.
|
|
19
|
+
const data = Array.isArray(res) ? res : (res?.data ?? []);
|
|
20
|
+
if (flags.json) {
|
|
21
|
+
this.log(JSON.stringify({ ok: true, count: data.length, data }, null, 2));
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
this.log(`\n Found ${data.length} guardrail(s):\n`);
|
|
25
|
+
this.log(" ID NAME MODEL");
|
|
26
|
+
this.log(" ──────────────────────────────────────────────────────────────────────────────");
|
|
27
|
+
for (const g of data) {
|
|
28
|
+
// Backend's id field is `guardrails_config_id`, not `_id`.
|
|
29
|
+
const id = (g.guardrails_config_id || g._id || "").padEnd(38);
|
|
30
|
+
const name = (g.name || "").padEnd(28);
|
|
31
|
+
const model = g.model || "";
|
|
32
|
+
this.log(` ${id} ${name} ${model}`);
|
|
33
|
+
}
|
|
34
|
+
this.log("");
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
this.error(`Failed: ${error.message}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { BaseCommand } from "../../base-command.js";
|
|
2
|
+
export default class GuardrailUpdate extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static args: {
|
|
6
|
+
id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
static flags: {
|
|
9
|
+
name: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
model: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
instructions: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
"guardrail-provider-id": import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
"unsafe-categories": import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
"custom-unsafe-patterns": import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
"competitor-keywords": import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
18
|
+
};
|
|
19
|
+
run(): Promise<void>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Args, Flags } from "@oclif/core";
|
|
2
|
+
import { BaseCommand } from "../../base-command.js";
|
|
3
|
+
import { getClient } from "../../lib/client.js";
|
|
4
|
+
export default class GuardrailUpdate extends BaseCommand {
|
|
5
|
+
static description = "Update a Guardrail (PUT — must pass name, model, instructions; other fields optional)";
|
|
6
|
+
static examples = [
|
|
7
|
+
'imbrace guardrail update <id> --name "X" --model gpt-4o --instructions "..." --json',
|
|
8
|
+
];
|
|
9
|
+
static args = {
|
|
10
|
+
id: Args.string({ description: "Guardrail ID", required: true }),
|
|
11
|
+
};
|
|
12
|
+
static flags = {
|
|
13
|
+
name: Flags.string({ char: "n", description: "Name (required by SDK PUT)", required: true }),
|
|
14
|
+
model: Flags.string({ description: "LLM model (required by SDK PUT)", required: true }),
|
|
15
|
+
instructions: Flags.string({ char: "i", description: "Rules (required by SDK PUT)", required: true }),
|
|
16
|
+
"guardrail-provider-id": Flags.string({ description: "Guardrail provider UUID" }),
|
|
17
|
+
description: Flags.string({ char: "d", description: "Human-readable description" }),
|
|
18
|
+
"unsafe-categories": Flags.string({ description: "Comma-separated unsafe categories" }),
|
|
19
|
+
"custom-unsafe-patterns": Flags.string({ description: "Comma-separated custom regex patterns" }),
|
|
20
|
+
"competitor-keywords": Flags.string({ description: "Comma-separated competitor names" }),
|
|
21
|
+
json: Flags.boolean({ description: "Output as JSON" }),
|
|
22
|
+
};
|
|
23
|
+
async run() {
|
|
24
|
+
const { args, flags } = await this.parse(GuardrailUpdate);
|
|
25
|
+
const split = (s) => s.split(",").map((x) => x.trim()).filter(Boolean);
|
|
26
|
+
try {
|
|
27
|
+
const client = getClient();
|
|
28
|
+
const data = await client.ai.updateGuardrail(args.id, {
|
|
29
|
+
name: flags.name,
|
|
30
|
+
model: flags.model,
|
|
31
|
+
instructions: flags.instructions,
|
|
32
|
+
...(flags["guardrail-provider-id"] && { guardrail_provider_id: flags["guardrail-provider-id"] }),
|
|
33
|
+
...(flags.description && { description: flags.description }),
|
|
34
|
+
...(flags["unsafe-categories"] && { unsafe_categories: split(flags["unsafe-categories"]) }),
|
|
35
|
+
...(flags["custom-unsafe-patterns"] && { custom_unsafe_patterns: split(flags["custom-unsafe-patterns"]) }),
|
|
36
|
+
...(flags["competitor-keywords"] && { competitor_keywords: split(flags["competitor-keywords"]) }),
|
|
37
|
+
});
|
|
38
|
+
const message = "Guardrail updated";
|
|
39
|
+
if (flags.json) {
|
|
40
|
+
this.log(JSON.stringify({ ok: true, message, data }, null, 2));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
this.log(`\n✅ ${message}\n`);
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
this.error(`Failed: ${error.message}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { BaseCommand } from "../../base-command.js";
|
|
2
|
+
export default class OrchestratorCreate extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
instructions: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
model: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
"provider-id": import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
"sub-agents": import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
"team-leads": import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
temperature: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
|
+
"id-only": import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
|
+
};
|
|
17
|
+
run(): Promise<void>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Flags } from "@oclif/core";
|
|
2
|
+
import { BaseCommand } from "../../base-command.js";
|
|
3
|
+
import { input } from "@inquirer/prompts";
|
|
4
|
+
import { createAgent, patchOrchestratorFields } from "../../lib/ai-agent.js";
|
|
5
|
+
export default class OrchestratorCreate extends BaseCommand {
|
|
6
|
+
static description = "Create an Orchestrator agent (coordinates multiple sub-agents toward a shared goal)";
|
|
7
|
+
static examples = [
|
|
8
|
+
'imbrace orchestrator create --name "Sales Orchestrator" --instructions "Route requests to the right sub-agent" --sub-agents uc_xxx,uc_yyy --json',
|
|
9
|
+
'imbrace orchestrator create --name "Support Lead" --team-leads uc_zzz --id-only',
|
|
10
|
+
];
|
|
11
|
+
static flags = {
|
|
12
|
+
name: Flags.string({ char: "n", description: "Orchestrator name" }),
|
|
13
|
+
description: Flags.string({ char: "d", description: "Description" }),
|
|
14
|
+
instructions: Flags.string({ char: "i", description: "Routing / coordination instructions" }),
|
|
15
|
+
model: Flags.string({ description: "LLM model (e.g. gpt-4o)" }),
|
|
16
|
+
"provider-id": Flags.string({ description: "LLM provider ID. Default 'system'." }),
|
|
17
|
+
"sub-agents": Flags.string({ description: "Comma-separated sub-agent IDs (the agents this orchestrator delegates to)" }),
|
|
18
|
+
"team-leads": Flags.string({ description: "Comma-separated team-lead IDs" }),
|
|
19
|
+
temperature: Flags.string({ description: "Model temperature 0.0-2.0 (default: 0.1)" }),
|
|
20
|
+
json: Flags.boolean({ description: "Output as JSON" }),
|
|
21
|
+
"id-only": Flags.boolean({ description: "Print only the new orchestrator ID" }),
|
|
22
|
+
};
|
|
23
|
+
async run() {
|
|
24
|
+
const { flags } = await this.parse(OrchestratorCreate);
|
|
25
|
+
const nonInteractive = flags.json || flags["id-only"];
|
|
26
|
+
const name = flags.name ?? (nonInteractive ? this.error("--name is required with --json or --id-only") : await input({ message: "Orchestrator name:" }));
|
|
27
|
+
const instructions = flags.instructions ?? (nonInteractive ? undefined : await input({ message: "Instructions (optional):", default: "" }));
|
|
28
|
+
const body = {
|
|
29
|
+
name,
|
|
30
|
+
...(instructions && { instructions }),
|
|
31
|
+
...(flags.description && { description: flags.description }),
|
|
32
|
+
...(flags.model && { model: flags.model }),
|
|
33
|
+
...(flags["provider-id"] && { provider_id: flags["provider-id"] }),
|
|
34
|
+
...(flags.temperature && { temperature: parseFloat(flags.temperature) }),
|
|
35
|
+
is_orchestrator: true,
|
|
36
|
+
sub_agents: flags["sub-agents"] ? flags["sub-agents"].split(",").map(s => s.trim()).filter(Boolean) : [],
|
|
37
|
+
team_leads: flags["team-leads"] ? flags["team-leads"].split(",").map(s => s.trim()).filter(Boolean) : [],
|
|
38
|
+
};
|
|
39
|
+
try {
|
|
40
|
+
const data = await createAgent(body);
|
|
41
|
+
const useCaseId = data?._id ?? "";
|
|
42
|
+
// createUseCase ignores sub_agents/team_leads on the assistant block.
|
|
43
|
+
// Patch them in via chatAi.updateAiAgent after the agent record exists.
|
|
44
|
+
if (useCaseId && (body.sub_agents.length || body.team_leads.length)) {
|
|
45
|
+
await patchOrchestratorFields(useCaseId, {
|
|
46
|
+
sub_agents: body.sub_agents,
|
|
47
|
+
team_leads: body.team_leads,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
const message = `Orchestrator "${name}" created`;
|
|
51
|
+
if (flags["id-only"]) {
|
|
52
|
+
this.log(data?._id ?? "");
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (flags.json) {
|
|
56
|
+
this.log(JSON.stringify({ ok: true, message, data }, null, 2));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
this.log(`\n✅ ${message}`);
|
|
60
|
+
if (data?._id)
|
|
61
|
+
this.log(` ID: ${data._id}`);
|
|
62
|
+
this.log("");
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
this.error(`Failed: ${error.message}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BaseCommand } from "../../base-command.js";
|
|
2
|
+
export default class OrchestratorDelete extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static args: {
|
|
6
|
+
id: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
static flags: {
|
|
9
|
+
yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
};
|
|
12
|
+
run(): Promise<void>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Args, Flags } from "@oclif/core";
|
|
2
|
+
import { BaseCommand } from "../../base-command.js";
|
|
3
|
+
import { confirm, input } from "@inquirer/prompts";
|
|
4
|
+
import { getClient } from "../../lib/client.js";
|
|
5
|
+
export default class OrchestratorDelete extends BaseCommand {
|
|
6
|
+
static description = "Delete an Orchestrator agent";
|
|
7
|
+
static examples = [
|
|
8
|
+
"imbrace orchestrator delete <id> --yes --json",
|
|
9
|
+
];
|
|
10
|
+
static args = {
|
|
11
|
+
id: Args.string({ description: "Orchestrator ID" }),
|
|
12
|
+
};
|
|
13
|
+
static flags = {
|
|
14
|
+
yes: Flags.boolean({ char: "y", description: "Skip confirmation" }),
|
|
15
|
+
json: Flags.boolean({ description: "Output as JSON" }),
|
|
16
|
+
};
|
|
17
|
+
async run() {
|
|
18
|
+
const { args, flags } = await this.parse(OrchestratorDelete);
|
|
19
|
+
const id = args.id ?? (flags.json ? this.error("ID is required") : await input({ message: "Orchestrator ID:" }));
|
|
20
|
+
if (!flags.yes) {
|
|
21
|
+
const ok = await confirm({ message: `Delete orchestrator ${id}?`, default: false });
|
|
22
|
+
if (!ok) {
|
|
23
|
+
this.log("Cancelled.");
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const client = getClient();
|
|
29
|
+
await client.agent.delete(id);
|
|
30
|
+
const message = "Orchestrator deleted";
|
|
31
|
+
if (flags.json) {
|
|
32
|
+
this.log(JSON.stringify({ ok: true, message }, null, 2));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
this.log(`\n✅ ${message}\n`);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
this.error(`Failed: ${error.message}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BaseCommand } from "../../base-command.js";
|
|
2
|
+
export default class OrchestratorGet extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static args: {
|
|
6
|
+
id: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
static flags: {
|
|
9
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
};
|
|
11
|
+
run(): Promise<void>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Args, Flags } from "@oclif/core";
|
|
2
|
+
import { BaseCommand } from "../../base-command.js";
|
|
3
|
+
import { input } from "@inquirer/prompts";
|
|
4
|
+
import { getClient } from "../../lib/client.js";
|
|
5
|
+
import { gatewayFetch } from "../../lib/gateway.js";
|
|
6
|
+
export default class OrchestratorGet extends BaseCommand {
|
|
7
|
+
static description = "Get details of an Orchestrator agent (sub_agents, team_leads, instructions)";
|
|
8
|
+
static examples = [
|
|
9
|
+
"imbrace orchestrator get <id>",
|
|
10
|
+
"imbrace orchestrator get <id> --json",
|
|
11
|
+
];
|
|
12
|
+
static args = {
|
|
13
|
+
id: Args.string({ description: "Orchestrator ID" }),
|
|
14
|
+
};
|
|
15
|
+
static flags = {
|
|
16
|
+
json: Flags.boolean({ description: "Output as JSON" }),
|
|
17
|
+
};
|
|
18
|
+
async run() {
|
|
19
|
+
const { args, flags } = await this.parse(OrchestratorGet);
|
|
20
|
+
const id = args.id ?? (flags.json ? this.error("ID is required") : await input({ message: "Orchestrator ID:" }));
|
|
21
|
+
try {
|
|
22
|
+
const client = getClient();
|
|
23
|
+
const tpl = (await client.agent.get(id))?.data ?? {};
|
|
24
|
+
// sub_agents / team_leads live on the assistant, not the use case.
|
|
25
|
+
// Fetch the assistant directly to get the canonical state.
|
|
26
|
+
const assistant = tpl.assistant_id
|
|
27
|
+
? await gatewayFetch(`/v3/ai/assistants/${tpl.assistant_id}`).catch(() => null)
|
|
28
|
+
: null;
|
|
29
|
+
if (flags.json) {
|
|
30
|
+
this.log(JSON.stringify({ ok: true, data: { ...tpl, assistant } }, null, 2));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const agentType = assistant?.agent_type ?? tpl.agent_type;
|
|
34
|
+
this.log(`\n ID: ${tpl._id || tpl.id || ""}`);
|
|
35
|
+
this.log(` Title: ${tpl.title || tpl.name || ""}`);
|
|
36
|
+
this.log(` Assistant ID: ${tpl.assistant_id || ""}`);
|
|
37
|
+
this.log(` Agent type: ${agentType || ""}`);
|
|
38
|
+
this.log(` Is orchestrator: ${agentType === "team_lead"}`);
|
|
39
|
+
const subs = assistant?.sub_agents || [];
|
|
40
|
+
const leads = assistant?.team_leads || [];
|
|
41
|
+
// Backend returns either a string id or an enriched object {assistant_id, name}.
|
|
42
|
+
const fmt = (a) => typeof a === "string" ? a : (a?.name || a?.assistant_id || JSON.stringify(a));
|
|
43
|
+
this.log(` Sub-agents (${subs.length}): ${subs.map(fmt).join(", ") || "(none)"}`);
|
|
44
|
+
this.log(` Team-leads (${leads.length}): ${leads.map(fmt).join(", ") || "(none)"}`);
|
|
45
|
+
this.log(` Description: ${(tpl.short_description || "").slice(0, 80)}`);
|
|
46
|
+
this.log("");
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
this.error(`Failed: ${error.message}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { BaseCommand } from "../../base-command.js";
|
|
2
|
+
export default class OrchestratorList extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
};
|
|
8
|
+
run(): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Flags } from "@oclif/core";
|
|
2
|
+
import { BaseCommand } from "../../base-command.js";
|
|
3
|
+
import { getClient } from "../../lib/client.js";
|
|
4
|
+
export default class OrchestratorList extends BaseCommand {
|
|
5
|
+
static description = "List Orchestrator agents (filters AI Agents where is_orchestrator=true)";
|
|
6
|
+
static examples = [
|
|
7
|
+
"imbrace orchestrator list",
|
|
8
|
+
"imbrace orchestrator list --json",
|
|
9
|
+
];
|
|
10
|
+
static flags = {
|
|
11
|
+
json: Flags.boolean({ description: "Output as JSON" }),
|
|
12
|
+
};
|
|
13
|
+
async run() {
|
|
14
|
+
const { flags } = await this.parse(OrchestratorList);
|
|
15
|
+
try {
|
|
16
|
+
const client = getClient();
|
|
17
|
+
const res = await client.agent.list();
|
|
18
|
+
const all = (res?.data ?? res ?? []);
|
|
19
|
+
// Webapp identifies orchestrators via agent_type === "team_lead"
|
|
20
|
+
// (see new-frontend/.../useAIAssistantFormHook.tsx). Filter client-side.
|
|
21
|
+
const data = all.filter((a) => a.agent_type === "team_lead" ||
|
|
22
|
+
a.assistant?.agent_type === "team_lead");
|
|
23
|
+
if (flags.json) {
|
|
24
|
+
this.log(JSON.stringify({ ok: true, count: data.length, data }, null, 2));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
this.log(`\n Found ${data.length} orchestrator(s):\n`);
|
|
28
|
+
this.log(" ID TITLE");
|
|
29
|
+
this.log(" ────────────────────────────────────────────────────────────");
|
|
30
|
+
for (const a of data) {
|
|
31
|
+
const id = (a._id || a.id || "").padEnd(38);
|
|
32
|
+
const title = a.title || a.name || "";
|
|
33
|
+
this.log(` ${id} ${title}`);
|
|
34
|
+
}
|
|
35
|
+
this.log(`\n Use 'imbrace orchestrator get <id>' to see sub_agents.\n`);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
this.error(`Failed: ${error.message}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
package/dist/lib/ai-agent.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export interface CreateAgentInput {
|
|
|
6
6
|
model?: string;
|
|
7
7
|
provider_id?: string;
|
|
8
8
|
mode?: string;
|
|
9
|
+
agent_type?: string;
|
|
9
10
|
channel?: string;
|
|
10
11
|
category?: string[] | string;
|
|
11
12
|
personality_role?: string;
|
|
@@ -24,9 +25,16 @@ export interface CreateAgentInput {
|
|
|
24
25
|
default_folder_id?: string;
|
|
25
26
|
board_ids?: string[];
|
|
26
27
|
file_ids?: string[];
|
|
28
|
+
is_orchestrator?: boolean;
|
|
29
|
+
sub_agents?: string[];
|
|
30
|
+
team_leads?: string[];
|
|
27
31
|
}
|
|
28
32
|
export declare function createAgent(body: CreateAgentInput): Promise<any>;
|
|
29
33
|
export declare function updateAgent(id: string, body: Record<string, any>): Promise<any>;
|
|
34
|
+
export declare function patchOrchestratorFields(useCaseId: string, patch: {
|
|
35
|
+
sub_agents?: string[];
|
|
36
|
+
team_leads?: string[];
|
|
37
|
+
}): Promise<void>;
|
|
30
38
|
export declare function listProviders(): Promise<{
|
|
31
39
|
id: any;
|
|
32
40
|
_id: any;
|
package/dist/lib/ai-agent.js
CHANGED
|
@@ -6,11 +6,18 @@ export async function createAgent(body) {
|
|
|
6
6
|
const client = getClient();
|
|
7
7
|
const slug = body.name.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
8
8
|
const workflowName = `${slug}_v${Date.now()}`;
|
|
9
|
+
// Orchestrator detection: webapp encodes orchestrator-ness via
|
|
10
|
+
// `agent_type: "team_lead"`, not via a separate `is_orchestrator` field.
|
|
11
|
+
// (See new-frontend/src/pages/AIAssistantManagement/useAIAssistantFormHook.tsx:
|
|
12
|
+
// `is_orchestrator: responseData.agent_type === agentTypeValue.teamLead`.)
|
|
13
|
+
const agentType = body.is_orchestrator || body.agent_type === "team_lead"
|
|
14
|
+
? "team_lead"
|
|
15
|
+
: (body.agent_type || "agent");
|
|
9
16
|
const data = await client.agent.createUseCase({
|
|
10
17
|
usecase: {
|
|
11
18
|
title: body.name,
|
|
12
19
|
short_description: body.description || "",
|
|
13
|
-
agent_type:
|
|
20
|
+
agent_type: agentType,
|
|
14
21
|
demo_url: "https://chat-widgetv2.imbrace.co",
|
|
15
22
|
supported_channels: [{ title: "channel_", icon: "" }],
|
|
16
23
|
},
|
|
@@ -22,7 +29,7 @@ export async function createAgent(body) {
|
|
|
22
29
|
credential_name: `${body.mode || "standard"} | ${body.name}`,
|
|
23
30
|
provider_id: body.provider_id || "system",
|
|
24
31
|
model_id: body.model || "Default",
|
|
25
|
-
agent_type:
|
|
32
|
+
agent_type: agentType,
|
|
26
33
|
mode: body.mode || "standard",
|
|
27
34
|
version: 2,
|
|
28
35
|
channel: body.channel || "",
|
|
@@ -44,8 +51,9 @@ export async function createAgent(body) {
|
|
|
44
51
|
board_ids: body.board_ids || [],
|
|
45
52
|
file_ids: body.file_ids || [],
|
|
46
53
|
workflow_function_call: [],
|
|
47
|
-
sub_agents: [],
|
|
48
|
-
team_leads: [],
|
|
54
|
+
sub_agents: body.sub_agents || [],
|
|
55
|
+
team_leads: body.team_leads || [],
|
|
56
|
+
is_orchestrator: body.is_orchestrator ?? false,
|
|
49
57
|
metadata: {
|
|
50
58
|
other_requirements: [],
|
|
51
59
|
channel_id: "",
|
|
@@ -76,7 +84,7 @@ export async function updateAgent(id, body) {
|
|
|
76
84
|
typeof body.show_thinking_process === "boolean" || body.mode ||
|
|
77
85
|
ASSISTANT_FIELDS.some((f) => body[f] !== undefined));
|
|
78
86
|
if (hasAssistantUpdate) {
|
|
79
|
-
// chatAi.
|
|
87
|
+
// chatAi.updateAiAgent uses PUT (full replace) — fetch+merge so unchanged
|
|
80
88
|
// fields aren't reset to null.
|
|
81
89
|
const currentAssistant = await gatewayFetch(`/v3/ai/assistants/${assistantId}`);
|
|
82
90
|
const aUpdate = {
|
|
@@ -101,7 +109,7 @@ export async function updateAgent(id, body) {
|
|
|
101
109
|
if (body[f] !== undefined)
|
|
102
110
|
aUpdate[f] = body[f];
|
|
103
111
|
}
|
|
104
|
-
results.assistant = await client.chatAi.
|
|
112
|
+
results.assistant = await client.chatAi.updateAiAgent(assistantId, aUpdate);
|
|
105
113
|
}
|
|
106
114
|
if (body.name || body.description) {
|
|
107
115
|
const tUpdate = {};
|
|
@@ -116,6 +124,55 @@ export async function updateAgent(id, body) {
|
|
|
116
124
|
}
|
|
117
125
|
return results;
|
|
118
126
|
}
|
|
127
|
+
// Resolve a list of agent identifiers (mix of UseCase IDs `uc_xxx` and bare
|
|
128
|
+
// assistant UUIDs) into their assistant_id form. The orchestrator backend
|
|
129
|
+
// stores `sub_agents` as assistant IDs, NOT use-case IDs.
|
|
130
|
+
async function resolveAssistantIds(ids) {
|
|
131
|
+
const client = getClient();
|
|
132
|
+
const out = [];
|
|
133
|
+
for (const id of ids) {
|
|
134
|
+
if (!id)
|
|
135
|
+
continue;
|
|
136
|
+
if (id.startsWith("uc_")) {
|
|
137
|
+
try {
|
|
138
|
+
const tpl = (await client.agent.get(id))?.data ?? {};
|
|
139
|
+
if (tpl.assistant_id)
|
|
140
|
+
out.push(tpl.assistant_id);
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// Unknown UseCase — skip with no error so partial input still works.
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
out.push(id);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return out;
|
|
151
|
+
}
|
|
152
|
+
// For orchestrators: createUseCase ignores `sub_agents` / `team_leads` /
|
|
153
|
+
// `agent_type=team_lead` fields on the assistant block. We work around by
|
|
154
|
+
// following up with a chatAi.updateAiAgent PUT once the use case + assistant
|
|
155
|
+
// exist. Backend stores them as assistant IDs (UUIDs), so we resolve any
|
|
156
|
+
// `uc_*` use-case IDs into assistant IDs first.
|
|
157
|
+
export async function patchOrchestratorFields(useCaseId, patch) {
|
|
158
|
+
const client = getClient();
|
|
159
|
+
const tpl = (await client.agent.get(useCaseId))?.data ?? {};
|
|
160
|
+
const assistantId = tpl.assistant_id;
|
|
161
|
+
if (!assistantId)
|
|
162
|
+
return;
|
|
163
|
+
const subAgentAssistantIds = patch.sub_agents
|
|
164
|
+
? await resolveAssistantIds(patch.sub_agents)
|
|
165
|
+
: undefined;
|
|
166
|
+
const teamLeadAssistantIds = patch.team_leads
|
|
167
|
+
? await resolveAssistantIds(patch.team_leads)
|
|
168
|
+
: undefined;
|
|
169
|
+
const current = await gatewayFetch(`/v3/ai/assistants/${assistantId}`);
|
|
170
|
+
await client.chatAi.updateAiAgent(assistantId, {
|
|
171
|
+
...current,
|
|
172
|
+
...(subAgentAssistantIds && { sub_agents: subAgentAssistantIds }),
|
|
173
|
+
...(teamLeadAssistantIds && { team_leads: teamLeadAssistantIds }),
|
|
174
|
+
});
|
|
175
|
+
}
|
|
119
176
|
export async function listProviders() {
|
|
120
177
|
const client = getClient();
|
|
121
178
|
const [r, systemModels] = await Promise.all([
|