@2kw/ai 6.0.0 → 6.1.0-dev.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/dist/commands/agent-labels.d.ts +3 -0
- package/dist/commands/agent-labels.js +72 -0
- package/dist/commands/agent-versions.d.ts +3 -0
- package/dist/commands/agent-versions.js +103 -0
- package/dist/commands/agents.d.ts +3 -0
- package/dist/commands/agents.js +154 -0
- package/dist/commands/ai.d.ts +54 -0
- package/dist/commands/ai.js +194 -2
- package/dist/commands/billing.js +34 -0
- package/dist/commands/conversations.d.ts +3 -0
- package/dist/commands/conversations.js +81 -0
- package/dist/commands/dataset-versions.js +44 -1
- package/dist/commands/datasets.js +23 -1
- package/dist/commands/experiments.js +209 -0
- package/dist/commands/extractions.js +14 -1
- package/dist/commands/files.d.ts +3 -0
- package/dist/commands/files.js +143 -0
- package/dist/commands/installations.d.ts +3 -0
- package/dist/commands/installations.js +177 -0
- package/dist/commands/knowledge-documents.d.ts +3 -0
- package/dist/commands/knowledge-documents.js +155 -0
- package/dist/commands/knowledge.d.ts +3 -0
- package/dist/commands/knowledge.js +197 -0
- package/dist/commands/prompt-versions.js +24 -1
- package/dist/commands/providers.js +38 -0
- package/dist/commands/queues.d.ts +11 -0
- package/dist/commands/queues.js +244 -0
- package/dist/commands/schema-versions.js +24 -1
- package/dist/commands/scores.js +43 -5
- package/dist/commands/tracing.js +40 -0
- package/dist/index.js +12 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -99,7 +99,7 @@ kubectl-style contexts switch between organizations and environments:
|
|
|
99
99
|
| `prompts` | Versioned prompt management, labels, compilation, testing |
|
|
100
100
|
| `extractions` | Create extractions (text, file, images), list, re-run, estimate tokens |
|
|
101
101
|
| `convert` | Convert PDF, DOCX, XLSX, images, and 20+ formats to Markdown/text/HTML/JSON |
|
|
102
|
-
| `ai` | Chat completions and model listing via the OpenAI-compatible gateway |
|
|
102
|
+
| `ai` | Chat completions, Responses (agent or direct model, streaming), and model listing via the OpenAI-compatible gateway |
|
|
103
103
|
| `transcribe` | Transcribe audio (FLAC, MP3, MP4, OGG, WAV, WebM) |
|
|
104
104
|
| `datasets` | Build datasets and dataset versions for evaluation |
|
|
105
105
|
| `experiments` | Run experiments against datasets |
|
|
@@ -109,6 +109,11 @@ kubectl-style contexts switch between organizations and environments:
|
|
|
109
109
|
| `providers` | Manage BYOK AI providers |
|
|
110
110
|
| `analytics` | Usage analytics: spend, quality, providers, errors |
|
|
111
111
|
| `billing` | Check subscription tier and usage limits |
|
|
112
|
+
| `agents` | Manage agents, versions, labels, approvals, and tool catalogs |
|
|
113
|
+
| `conversations` | Create and manage conversations and their items |
|
|
114
|
+
| `knowledge` | Manage knowledge bases, documents, search, and citations |
|
|
115
|
+
| `files` | Upload, download, and manage files |
|
|
116
|
+
| `installations` | Manage surface installations, keys, origins, and pairing |
|
|
112
117
|
| `docs` | Browse API documentation from the terminal |
|
|
113
118
|
|
|
114
119
|
### Global Options
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { getClient, runAction } from "../lib/client.js";
|
|
3
|
+
import { formatList, formatDetail, formatSuccess } from "../lib/output.js";
|
|
4
|
+
export function makeAgentLabelsCommand() {
|
|
5
|
+
const cmd = new Command("labels").description("Manage agent labels");
|
|
6
|
+
cmd
|
|
7
|
+
.command("list")
|
|
8
|
+
.description("List labels for an agent")
|
|
9
|
+
.requiredOption("--agent <id>", "Agent ID")
|
|
10
|
+
.action(async (opts, command) => {
|
|
11
|
+
await runAction(command, async () => {
|
|
12
|
+
const client = getClient(command);
|
|
13
|
+
const { data } = await client.GET("/v1/agents/{agentId}/labels", {
|
|
14
|
+
params: { path: { agentId: opts.agent } },
|
|
15
|
+
});
|
|
16
|
+
formatList(data, command, [
|
|
17
|
+
"name",
|
|
18
|
+
"agentVersionId",
|
|
19
|
+
"versionNumber",
|
|
20
|
+
"createdAt",
|
|
21
|
+
]);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
cmd
|
|
25
|
+
.command("create")
|
|
26
|
+
.description("Create a label for an agent version")
|
|
27
|
+
.requiredOption("--agent <id>", "Agent ID")
|
|
28
|
+
.requiredOption("-n, --name <name>", "Label name")
|
|
29
|
+
.requiredOption("--version-id <id>", "Agent version ID to label")
|
|
30
|
+
.action(async (opts, command) => {
|
|
31
|
+
await runAction(command, async () => {
|
|
32
|
+
const client = getClient(command);
|
|
33
|
+
const { data } = await client.POST("/v1/agents/{agentId}/labels", {
|
|
34
|
+
params: { path: { agentId: opts.agent } },
|
|
35
|
+
body: { name: opts.name, agentVersionId: opts.versionId },
|
|
36
|
+
});
|
|
37
|
+
formatDetail(data, command);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
cmd
|
|
41
|
+
.command("update")
|
|
42
|
+
.description("Repoint a label to a different agent version")
|
|
43
|
+
.argument("<labelName>", "Label name")
|
|
44
|
+
.requiredOption("--agent <id>", "Agent ID")
|
|
45
|
+
.requiredOption("--version-id <id>", "New agent version ID")
|
|
46
|
+
.action(async (labelName, opts, command) => {
|
|
47
|
+
await runAction(command, async () => {
|
|
48
|
+
const client = getClient(command);
|
|
49
|
+
const { data } = await client.PUT("/v1/agents/{agentId}/labels/{labelName}", {
|
|
50
|
+
params: { path: { agentId: opts.agent, labelName } },
|
|
51
|
+
body: { agentVersionId: opts.versionId },
|
|
52
|
+
});
|
|
53
|
+
formatDetail(data, command);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
cmd
|
|
57
|
+
.command("delete")
|
|
58
|
+
.description("Delete an agent label")
|
|
59
|
+
.argument("<labelName>", "Label name")
|
|
60
|
+
.requiredOption("--agent <id>", "Agent ID")
|
|
61
|
+
.action(async (labelName, opts, command) => {
|
|
62
|
+
await runAction(command, async () => {
|
|
63
|
+
const client = getClient(command);
|
|
64
|
+
await client.DELETE("/v1/agents/{agentId}/labels/{labelName}", {
|
|
65
|
+
params: { path: { agentId: opts.agent, labelName } },
|
|
66
|
+
});
|
|
67
|
+
formatSuccess(`Label "${labelName}" deleted.`, command);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
return cmd;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=agent-labels.js.map
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { getClient, runAction } from "../lib/client.js";
|
|
3
|
+
import { formatPage, formatDetail, formatSuccess } from "../lib/output.js";
|
|
4
|
+
import { addPaginationOptions, paginationParams } from "../lib/pagination.js";
|
|
5
|
+
export function makeAgentVersionsCommand() {
|
|
6
|
+
const cmd = new Command("versions").description("Manage agent versions");
|
|
7
|
+
const list = new Command("list").description("List versions of an agent");
|
|
8
|
+
addPaginationOptions(list);
|
|
9
|
+
list.requiredOption("--agent <id>", "Agent ID");
|
|
10
|
+
list.action(async (opts, command) => {
|
|
11
|
+
await runAction(command, async () => {
|
|
12
|
+
const client = getClient(command);
|
|
13
|
+
const { data } = await client.GET("/v1/agents/{agentId}/versions", {
|
|
14
|
+
params: {
|
|
15
|
+
path: { agentId: opts.agent },
|
|
16
|
+
query: paginationParams(opts),
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
formatPage(data, command, ["id", "versionNumber", "model", "changeDescription", "createdAt"]);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
cmd.addCommand(list);
|
|
23
|
+
cmd
|
|
24
|
+
.command("get")
|
|
25
|
+
.description("Get a specific agent version")
|
|
26
|
+
.argument("<versionId>", "Version ID")
|
|
27
|
+
.requiredOption("--agent <id>", "Agent ID")
|
|
28
|
+
.action(async (versionId, opts, command) => {
|
|
29
|
+
await runAction(command, async () => {
|
|
30
|
+
const client = getClient(command);
|
|
31
|
+
const { data } = await client.GET("/v1/agents/{agentId}/versions/{versionId}", { params: { path: { agentId: opts.agent, versionId } } });
|
|
32
|
+
formatDetail(data, command);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
cmd
|
|
36
|
+
.command("latest")
|
|
37
|
+
.description("Get the latest version of an agent")
|
|
38
|
+
.requiredOption("--agent <id>", "Agent ID")
|
|
39
|
+
.action(async (opts, command) => {
|
|
40
|
+
await runAction(command, async () => {
|
|
41
|
+
const client = getClient(command);
|
|
42
|
+
const { data } = await client.GET("/v1/agents/{agentId}/versions/latest", { params: { path: { agentId: opts.agent } } });
|
|
43
|
+
formatDetail(data, command);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
cmd
|
|
47
|
+
.command("create")
|
|
48
|
+
.description("Create a new agent version")
|
|
49
|
+
.requiredOption("--agent <id>", "Agent ID")
|
|
50
|
+
.requiredOption("-m, --model <model>", "Model (provider/model)")
|
|
51
|
+
.option("-i, --instructions <text>", "System instructions")
|
|
52
|
+
.option("--options <json>", "Model options as JSON")
|
|
53
|
+
.option("--tools <json>", "Tool definitions as JSON")
|
|
54
|
+
.option("--skills <json>", "Skill definitions as JSON")
|
|
55
|
+
.option("--hitl-policy <json>", "Human-in-the-loop policy as JSON")
|
|
56
|
+
.option("-c, --change-description <text>", "Change description")
|
|
57
|
+
.action(async (opts, command) => {
|
|
58
|
+
await runAction(command, async () => {
|
|
59
|
+
const client = getClient(command);
|
|
60
|
+
const { data } = await client.POST("/v1/agents/{agentId}/versions", {
|
|
61
|
+
params: { path: { agentId: opts.agent } },
|
|
62
|
+
body: {
|
|
63
|
+
model: opts.model,
|
|
64
|
+
instructions: opts.instructions,
|
|
65
|
+
changeDescription: opts.changeDescription,
|
|
66
|
+
options: opts.options ? JSON.parse(opts.options) : undefined,
|
|
67
|
+
tools: opts.tools ? JSON.parse(opts.tools) : undefined,
|
|
68
|
+
skills: opts.skills ? JSON.parse(opts.skills) : undefined,
|
|
69
|
+
hitlPolicy: opts.hitlPolicy ? JSON.parse(opts.hitlPolicy) : undefined,
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
formatDetail(data, command);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
cmd
|
|
76
|
+
.command("activate")
|
|
77
|
+
.description("Re-activate a historical agent version")
|
|
78
|
+
.argument("<versionId>", "Version ID")
|
|
79
|
+
.requiredOption("--agent <id>", "Agent ID")
|
|
80
|
+
.action(async (versionId, opts, command) => {
|
|
81
|
+
await runAction(command, async () => {
|
|
82
|
+
const client = getClient(command);
|
|
83
|
+
const { data } = await client.PUT("/v1/agents/{agentId}/versions/{versionId}/activate", { params: { path: { agentId: opts.agent, versionId } } });
|
|
84
|
+
formatDetail(data, command);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
cmd
|
|
88
|
+
.command("delete")
|
|
89
|
+
.description("Deactivate (soft-delete) an agent version")
|
|
90
|
+
.argument("<versionId>", "Version ID")
|
|
91
|
+
.requiredOption("--agent <id>", "Agent ID")
|
|
92
|
+
.action(async (versionId, opts, command) => {
|
|
93
|
+
await runAction(command, async () => {
|
|
94
|
+
const client = getClient(command);
|
|
95
|
+
await client.DELETE("/v1/agents/{agentId}/versions/{versionId}", {
|
|
96
|
+
params: { path: { agentId: opts.agent, versionId } },
|
|
97
|
+
});
|
|
98
|
+
formatSuccess(`Agent version ${versionId} deactivated.`, command);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
return cmd;
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=agent-versions.js.map
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { getClient, runAction } from "../lib/client.js";
|
|
3
|
+
import { formatPage, formatDetail, formatSuccess } from "../lib/output.js";
|
|
4
|
+
import { addPaginationOptions, paginationParams } from "../lib/pagination.js";
|
|
5
|
+
import { makeAgentVersionsCommand } from "./agent-versions.js";
|
|
6
|
+
import { makeAgentLabelsCommand } from "./agent-labels.js";
|
|
7
|
+
export function makeAgentsCommand() {
|
|
8
|
+
const cmd = new Command("agents").description("Manage agents");
|
|
9
|
+
const list = new Command("list").description("List agents");
|
|
10
|
+
addPaginationOptions(list);
|
|
11
|
+
list.option("-s, --search <term>", "Filter by name");
|
|
12
|
+
list.action(async (opts, command) => {
|
|
13
|
+
await runAction(command, async () => {
|
|
14
|
+
const client = getClient(command);
|
|
15
|
+
const { data } = await client.GET("/v1/agents", {
|
|
16
|
+
params: { query: { search: opts.search, ...paginationParams(opts) } },
|
|
17
|
+
});
|
|
18
|
+
formatPage(data, command, ["id", "name", "model", "latestVersionId", "createdAt"]);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
cmd.addCommand(list);
|
|
22
|
+
cmd
|
|
23
|
+
.command("get")
|
|
24
|
+
.description("Get an agent by ID")
|
|
25
|
+
.argument("<id>", "Agent ID")
|
|
26
|
+
.action(async (id, _opts, command) => {
|
|
27
|
+
await runAction(command, async () => {
|
|
28
|
+
const client = getClient(command);
|
|
29
|
+
const { data } = await client.GET("/v1/agents/{id}", {
|
|
30
|
+
params: { path: { id } },
|
|
31
|
+
});
|
|
32
|
+
formatDetail(data, command);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
cmd
|
|
36
|
+
.command("create")
|
|
37
|
+
.description("Create a new agent")
|
|
38
|
+
.requiredOption("-n, --name <name>", "Agent name")
|
|
39
|
+
.requiredOption("-m, --model <model>", "Model (provider/model)")
|
|
40
|
+
.option("-d, --description <text>", "Agent description")
|
|
41
|
+
.option("-i, --instructions <text>", "System instructions")
|
|
42
|
+
.option("--options <json>", "Model options as JSON")
|
|
43
|
+
.option("--tools <json>", "Tool definitions as JSON")
|
|
44
|
+
.option("--skills <json>", "Skill definitions as JSON")
|
|
45
|
+
.option("--hitl-policy <json>", "Human-in-the-loop policy as JSON")
|
|
46
|
+
.action(async (opts, command) => {
|
|
47
|
+
await runAction(command, async () => {
|
|
48
|
+
const client = getClient(command);
|
|
49
|
+
const { data } = await client.POST("/v1/agents", {
|
|
50
|
+
body: {
|
|
51
|
+
name: opts.name,
|
|
52
|
+
model: opts.model,
|
|
53
|
+
description: opts.description,
|
|
54
|
+
instructions: opts.instructions,
|
|
55
|
+
options: opts.options ? JSON.parse(opts.options) : undefined,
|
|
56
|
+
tools: opts.tools ? JSON.parse(opts.tools) : undefined,
|
|
57
|
+
skills: opts.skills ? JSON.parse(opts.skills) : undefined,
|
|
58
|
+
hitlPolicy: opts.hitlPolicy ? JSON.parse(opts.hitlPolicy) : undefined,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
formatDetail(data, command);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
cmd
|
|
65
|
+
.command("update")
|
|
66
|
+
.description("Update an agent's metadata and configuration")
|
|
67
|
+
.argument("<id>", "Agent ID")
|
|
68
|
+
.option("-n, --name <name>", "New name")
|
|
69
|
+
.option("-m, --model <model>", "New model (provider/model)")
|
|
70
|
+
.option("-d, --description <text>", "New description")
|
|
71
|
+
.option("-i, --instructions <text>", "New system instructions")
|
|
72
|
+
.option("--options <json>", "New model options as JSON")
|
|
73
|
+
.option("--tools <json>", "New tool definitions as JSON")
|
|
74
|
+
.option("--skills <json>", "New skill definitions as JSON")
|
|
75
|
+
.option("--hitl-policy <json>", "New human-in-the-loop policy as JSON")
|
|
76
|
+
.action(async (id, opts, command) => {
|
|
77
|
+
await runAction(command, async () => {
|
|
78
|
+
const client = getClient(command);
|
|
79
|
+
const body = {};
|
|
80
|
+
if (opts.name)
|
|
81
|
+
body.name = opts.name;
|
|
82
|
+
if (opts.model)
|
|
83
|
+
body.model = opts.model;
|
|
84
|
+
if (opts.description !== undefined)
|
|
85
|
+
body.description = opts.description;
|
|
86
|
+
if (opts.instructions !== undefined)
|
|
87
|
+
body.instructions = opts.instructions;
|
|
88
|
+
if (opts.options)
|
|
89
|
+
body.options = JSON.parse(opts.options);
|
|
90
|
+
if (opts.tools)
|
|
91
|
+
body.tools = JSON.parse(opts.tools);
|
|
92
|
+
if (opts.skills)
|
|
93
|
+
body.skills = JSON.parse(opts.skills);
|
|
94
|
+
if (opts.hitlPolicy)
|
|
95
|
+
body.hitlPolicy = JSON.parse(opts.hitlPolicy);
|
|
96
|
+
const { data } = await client.PUT("/v1/agents/{id}", {
|
|
97
|
+
params: { path: { id } },
|
|
98
|
+
body: body,
|
|
99
|
+
});
|
|
100
|
+
formatDetail(data, command);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
cmd
|
|
104
|
+
.command("delete")
|
|
105
|
+
.description("Delete an agent and cascade-delete its versions and labels")
|
|
106
|
+
.argument("<id>", "Agent ID")
|
|
107
|
+
.action(async (id, _opts, command) => {
|
|
108
|
+
await runAction(command, async () => {
|
|
109
|
+
const client = getClient(command);
|
|
110
|
+
await client.DELETE("/v1/agents/{id}", {
|
|
111
|
+
params: { path: { id } },
|
|
112
|
+
});
|
|
113
|
+
formatSuccess(`Agent ${id} deleted.`, command);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
const approvals = new Command("approvals").description("List tool approvals raised by an agent");
|
|
117
|
+
addPaginationOptions(approvals);
|
|
118
|
+
approvals.requiredOption("--agent <id>", "Agent ID");
|
|
119
|
+
approvals.option("--status <status>", "Filter by status (pending, approved, rejected, cancelled)");
|
|
120
|
+
approvals.action(async (opts, command) => {
|
|
121
|
+
await runAction(command, async () => {
|
|
122
|
+
const client = getClient(command);
|
|
123
|
+
const { data } = await client.GET("/v1/agents/{agentId}/approvals", {
|
|
124
|
+
params: {
|
|
125
|
+
path: { agentId: opts.agent },
|
|
126
|
+
query: { status: opts.status, ...paginationParams(opts) },
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
formatPage(data, command, ["id", "tool", "policyClass", "status", "createdAt"]);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
cmd.addCommand(approvals);
|
|
133
|
+
const toolCatalogs = new Command("tool-catalogs").description("List tool-catalog sync history for an agent");
|
|
134
|
+
addPaginationOptions(toolCatalogs);
|
|
135
|
+
toolCatalogs.requiredOption("--agent <id>", "Agent ID");
|
|
136
|
+
toolCatalogs.option("--installation <id>", "Narrow to one installation");
|
|
137
|
+
toolCatalogs.action(async (opts, command) => {
|
|
138
|
+
await runAction(command, async () => {
|
|
139
|
+
const client = getClient(command);
|
|
140
|
+
const { data } = await client.GET("/v1/agents/{agentId}/tool-catalogs", {
|
|
141
|
+
params: {
|
|
142
|
+
path: { agentId: opts.agent },
|
|
143
|
+
query: { installationId: opts.installation, ...paginationParams(opts) },
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
formatPage(data, command, ["id", "installationId", "active", "toolCount", "syncedAt"]);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
cmd.addCommand(toolCatalogs);
|
|
150
|
+
cmd.addCommand(makeAgentVersionsCommand());
|
|
151
|
+
cmd.addCommand(makeAgentLabelsCommand());
|
|
152
|
+
return cmd;
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=agents.js.map
|
package/dist/commands/ai.d.ts
CHANGED
|
@@ -1,3 +1,57 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
+
/**
|
|
3
|
+
* Reads a single text input from stdin, or `undefined` when stdin is a TTY
|
|
4
|
+
* (nothing was piped in) or empty. Mirrors the `-` stdin convention used by
|
|
5
|
+
* `bb convert file`, but always reads stdin implicitly (there is no `-`
|
|
6
|
+
* sentinel here since `respond` takes a plain input string).
|
|
7
|
+
*/
|
|
8
|
+
export declare function readStdinInput(): string | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* Resolves the `model` field for `POST /v1/responses` from the CLI's
|
|
11
|
+
* `--agent` / `--model` options, per {@code ModelReferenceResolver}'s
|
|
12
|
+
* grammar: a stored agent is addressed as `agent/{ref}` (an `--agent`
|
|
13
|
+
* value of `ref@label` naturally composes into `agent/ref@label`), a
|
|
14
|
+
* direct gateway call is a plain `provider/model` string.
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveModel(opts: {
|
|
17
|
+
agent?: string;
|
|
18
|
+
model?: string;
|
|
19
|
+
}): string;
|
|
20
|
+
interface ResponseContentPart {
|
|
21
|
+
type?: string;
|
|
22
|
+
text?: string;
|
|
23
|
+
}
|
|
24
|
+
interface ResponseOutputItem {
|
|
25
|
+
type?: string;
|
|
26
|
+
role?: string;
|
|
27
|
+
content?: ResponseContentPart[];
|
|
28
|
+
}
|
|
29
|
+
interface ResponseUsage {
|
|
30
|
+
input_tokens?: number;
|
|
31
|
+
output_tokens?: number;
|
|
32
|
+
total_tokens?: number;
|
|
33
|
+
}
|
|
34
|
+
interface ResponseResult {
|
|
35
|
+
id?: string;
|
|
36
|
+
status?: string;
|
|
37
|
+
model?: string;
|
|
38
|
+
output?: ResponseOutputItem[];
|
|
39
|
+
usage?: ResponseUsage;
|
|
40
|
+
conversation?: {
|
|
41
|
+
id?: string;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Extracts the assistant's text from a Responses API result: the
|
|
46
|
+
* `output_text` content parts of every `message` output item, in order.
|
|
47
|
+
*/
|
|
48
|
+
export declare function extractResponseText(result: ResponseResult | undefined): string;
|
|
49
|
+
/**
|
|
50
|
+
* Extracts the text delta from one line of an SSE stream, or `undefined`
|
|
51
|
+
* when the line carries nothing to print (not a `data:` line, `[DONE]`, an
|
|
52
|
+
* event with no textual delta, or unparsable JSON).
|
|
53
|
+
*/
|
|
54
|
+
export declare function extractSseDelta(line: string): string | undefined;
|
|
2
55
|
export declare function makeAiCommand(): Command;
|
|
56
|
+
export {};
|
|
3
57
|
//# sourceMappingURL=ai.d.ts.map
|
package/dist/commands/ai.js
CHANGED
|
@@ -1,8 +1,162 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { getClient, resolveAuthHeader, runAction } from "../lib/client.js";
|
|
4
|
+
import { resolveConfig, isJsonOutput } from "../lib/config.js";
|
|
5
|
+
import { BackboneApiError } from "../lib/errors.js";
|
|
4
6
|
import { formatList, withSpinner } from "../lib/output.js";
|
|
5
7
|
import chalk from "chalk";
|
|
8
|
+
/**
|
|
9
|
+
* Reads a single text input from stdin, or `undefined` when stdin is a TTY
|
|
10
|
+
* (nothing was piped in) or empty. Mirrors the `-` stdin convention used by
|
|
11
|
+
* `bb convert file`, but always reads stdin implicitly (there is no `-`
|
|
12
|
+
* sentinel here since `respond` takes a plain input string).
|
|
13
|
+
*/
|
|
14
|
+
export function readStdinInput() {
|
|
15
|
+
if (process.stdin.isTTY)
|
|
16
|
+
return undefined;
|
|
17
|
+
try {
|
|
18
|
+
const text = readFileSync(0, "utf-8").trim();
|
|
19
|
+
return text.length > 0 ? text : undefined;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Resolves the `model` field for `POST /v1/responses` from the CLI's
|
|
27
|
+
* `--agent` / `--model` options, per {@code ModelReferenceResolver}'s
|
|
28
|
+
* grammar: a stored agent is addressed as `agent/{ref}` (an `--agent`
|
|
29
|
+
* value of `ref@label` naturally composes into `agent/ref@label`), a
|
|
30
|
+
* direct gateway call is a plain `provider/model` string.
|
|
31
|
+
*/
|
|
32
|
+
export function resolveModel(opts) {
|
|
33
|
+
if (opts.agent && opts.model) {
|
|
34
|
+
throw new Error("Use either --agent or --model, not both.");
|
|
35
|
+
}
|
|
36
|
+
if (opts.agent)
|
|
37
|
+
return `agent/${opts.agent}`;
|
|
38
|
+
if (opts.model)
|
|
39
|
+
return opts.model;
|
|
40
|
+
throw new Error("Either --agent or --model is required.");
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Extracts the assistant's text from a Responses API result: the
|
|
44
|
+
* `output_text` content parts of every `message` output item, in order.
|
|
45
|
+
*/
|
|
46
|
+
export function extractResponseText(result) {
|
|
47
|
+
const parts = [];
|
|
48
|
+
for (const item of result?.output ?? []) {
|
|
49
|
+
if (item.type !== "message")
|
|
50
|
+
continue;
|
|
51
|
+
for (const part of item.content ?? []) {
|
|
52
|
+
if (part.type === "output_text" && part.text) {
|
|
53
|
+
parts.push(part.text);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return parts.join("\n");
|
|
58
|
+
}
|
|
59
|
+
function printResponseResult(result, command) {
|
|
60
|
+
if (isJsonOutput(command)) {
|
|
61
|
+
console.log(JSON.stringify(result, null, 2));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const text = extractResponseText(result);
|
|
65
|
+
if (text) {
|
|
66
|
+
console.log(text);
|
|
67
|
+
}
|
|
68
|
+
if (result?.status && result.status !== "completed") {
|
|
69
|
+
console.log(chalk.yellow(`\n[status: ${result.status}]`));
|
|
70
|
+
}
|
|
71
|
+
if (result?.usage) {
|
|
72
|
+
const usage = result.usage;
|
|
73
|
+
console.log(chalk.dim(`\n[${result.model} | ${usage.input_tokens} in / ${usage.output_tokens} out / ${usage.total_tokens} total tokens]`));
|
|
74
|
+
}
|
|
75
|
+
if (result?.conversation?.id) {
|
|
76
|
+
console.log(chalk.dim(`[conversation: ${result.conversation.id}]`));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Extracts the text delta from one line of an SSE stream, or `undefined`
|
|
81
|
+
* when the line carries nothing to print (not a `data:` line, `[DONE]`, an
|
|
82
|
+
* event with no textual delta, or unparsable JSON).
|
|
83
|
+
*/
|
|
84
|
+
export function extractSseDelta(line) {
|
|
85
|
+
const trimmed = line.trim();
|
|
86
|
+
if (!trimmed.startsWith("data:"))
|
|
87
|
+
return undefined;
|
|
88
|
+
const payload = trimmed.slice(5).trim();
|
|
89
|
+
if (!payload || payload === "[DONE]")
|
|
90
|
+
return undefined;
|
|
91
|
+
try {
|
|
92
|
+
const event = JSON.parse(payload);
|
|
93
|
+
const delta = event.delta;
|
|
94
|
+
return typeof delta === "string" ? delta : undefined;
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async function parseErrorResponse(res) {
|
|
101
|
+
let body = {};
|
|
102
|
+
try {
|
|
103
|
+
body = await res.json();
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// Fall through to the defaults below.
|
|
107
|
+
}
|
|
108
|
+
return new BackboneApiError({
|
|
109
|
+
status: body.status ?? res.status,
|
|
110
|
+
error: body.error ?? res.statusText,
|
|
111
|
+
message: body.message ?? `HTTP ${res.status}: ${res.statusText}`,
|
|
112
|
+
timestamp: body.timestamp ?? new Date().toISOString(),
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Streams `POST /v1/responses` over SSE, printing text deltas as they
|
|
117
|
+
* arrive. Bypasses the typed openapi-fetch client (like the multipart raw
|
|
118
|
+
* `fetch` calls in convert.ts/transcribe.ts) because the response body must
|
|
119
|
+
* be read incrementally rather than parsed whole.
|
|
120
|
+
*/
|
|
121
|
+
async function streamResponse(command, body) {
|
|
122
|
+
const config = resolveConfig(command);
|
|
123
|
+
const authHeader = await resolveAuthHeader(config);
|
|
124
|
+
const baseUrl = config.baseUrl.replace(/\/+$/, "");
|
|
125
|
+
const res = await withSpinner("Connecting...", () => fetch(`${baseUrl}/v1/responses`, {
|
|
126
|
+
method: "POST",
|
|
127
|
+
headers: {
|
|
128
|
+
Authorization: authHeader,
|
|
129
|
+
"Content-Type": "application/json",
|
|
130
|
+
Accept: "text/event-stream",
|
|
131
|
+
},
|
|
132
|
+
body: JSON.stringify(body),
|
|
133
|
+
}));
|
|
134
|
+
if (!res.ok || !res.body) {
|
|
135
|
+
throw await parseErrorResponse(res);
|
|
136
|
+
}
|
|
137
|
+
const reader = res.body.getReader();
|
|
138
|
+
const decoder = new TextDecoder();
|
|
139
|
+
let buffer = "";
|
|
140
|
+
let wroteAny = false;
|
|
141
|
+
for (;;) {
|
|
142
|
+
const { done, value } = await reader.read();
|
|
143
|
+
if (done)
|
|
144
|
+
break;
|
|
145
|
+
buffer += decoder.decode(value, { stream: true });
|
|
146
|
+
const lines = buffer.split("\n");
|
|
147
|
+
buffer = lines.pop() ?? "";
|
|
148
|
+
for (const line of lines) {
|
|
149
|
+
const delta = extractSseDelta(line);
|
|
150
|
+
if (delta) {
|
|
151
|
+
process.stdout.write(delta);
|
|
152
|
+
wroteAny = true;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (wroteAny) {
|
|
157
|
+
process.stdout.write("\n");
|
|
158
|
+
}
|
|
159
|
+
}
|
|
6
160
|
export function makeAiCommand() {
|
|
7
161
|
const cmd = new Command("ai").description("AI gateway operations");
|
|
8
162
|
cmd
|
|
@@ -50,6 +204,44 @@ export function makeAiCommand() {
|
|
|
50
204
|
}
|
|
51
205
|
});
|
|
52
206
|
});
|
|
207
|
+
cmd
|
|
208
|
+
.command("respond")
|
|
209
|
+
.description("Send a request to the OpenAI-compatible Responses endpoint")
|
|
210
|
+
.argument("[input]", "Input text (reads stdin if omitted)")
|
|
211
|
+
.option("--agent <id>", "Invoke a stored agent by id or name (mutually exclusive with --model)")
|
|
212
|
+
.option("--model <id>", "Model in provider/model format (mutually exclusive with --agent)")
|
|
213
|
+
.option("--conversation <id>", "Conversation ID this response belongs to")
|
|
214
|
+
.option("--stream", "Stream the response as it is generated")
|
|
215
|
+
.action(async (input, opts, command) => {
|
|
216
|
+
await runAction(command, async () => {
|
|
217
|
+
const model = resolveModel(opts);
|
|
218
|
+
const inputText = input ?? readStdinInput();
|
|
219
|
+
if (!inputText) {
|
|
220
|
+
throw new Error("Input is required as an argument or via stdin.");
|
|
221
|
+
}
|
|
222
|
+
const body = {
|
|
223
|
+
model,
|
|
224
|
+
input: inputText,
|
|
225
|
+
stream: !!opts.stream,
|
|
226
|
+
};
|
|
227
|
+
if (opts.conversation)
|
|
228
|
+
body.conversation = opts.conversation;
|
|
229
|
+
if (opts.stream) {
|
|
230
|
+
await streamResponse(command, body);
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
const client = getClient(command);
|
|
234
|
+
const { data } = await withSpinner("Generating response...", () =>
|
|
235
|
+
// The wire `input` field also accepts a bare string (shorthand for
|
|
236
|
+
// a single user message; see ResponseItemInputDeserializer), which
|
|
237
|
+
// the generated type does not model — hence the cast, same as the
|
|
238
|
+
// untyped convert.ts source-conversion calls.
|
|
239
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
240
|
+
client.POST("/v1/responses", { body }));
|
|
241
|
+
printResponseResult(data, command);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
});
|
|
53
245
|
cmd
|
|
54
246
|
.command("models")
|
|
55
247
|
.description("List available AI models")
|
package/dist/commands/billing.js
CHANGED
|
@@ -97,6 +97,40 @@ export function makeBillingCommand() {
|
|
|
97
97
|
}
|
|
98
98
|
});
|
|
99
99
|
});
|
|
100
|
+
cmd
|
|
101
|
+
.command("has-available")
|
|
102
|
+
.description("Check whether a metered resource has enough available capacity")
|
|
103
|
+
.argument("<type>", "extraction-credits, conversion-credits, or gateway-tokens")
|
|
104
|
+
.option("--required <n>", "Amount required (default: 1)")
|
|
105
|
+
.action(async (type, opts, command) => {
|
|
106
|
+
await runAction(command, async () => {
|
|
107
|
+
const client = getClient(command);
|
|
108
|
+
const typeMap = {
|
|
109
|
+
"extraction-credits": "EXTRACTION_CREDITS",
|
|
110
|
+
"conversion-credits": "CONVERSION_CREDITS",
|
|
111
|
+
"gateway-tokens": "GATEWAY_TOKENS",
|
|
112
|
+
};
|
|
113
|
+
const apiType = typeMap[type] ?? type.toUpperCase();
|
|
114
|
+
const { data } = await client.GET("/v1/billing/has-available", {
|
|
115
|
+
params: {
|
|
116
|
+
query: {
|
|
117
|
+
type: apiType,
|
|
118
|
+
required: opts.required !== undefined ? Number(opts.required) : undefined,
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
const result = data?.result;
|
|
123
|
+
if (isJsonOutput(command)) {
|
|
124
|
+
console.log(JSON.stringify({ type: apiType, hasAvailable: result }));
|
|
125
|
+
}
|
|
126
|
+
else if (result) {
|
|
127
|
+
console.log(chalk.green(`${apiType} has available capacity.`));
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
console.log(chalk.yellow(`${apiType} does NOT have available capacity.`));
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
});
|
|
100
134
|
return cmd;
|
|
101
135
|
}
|
|
102
136
|
//# sourceMappingURL=billing.js.map
|