@2kw/ai 6.3.0-dev.14 → 6.3.0-dev.144
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 +4 -1
- package/dist/agent-config/schema.d.ts +6 -3
- package/dist/agent-config/schema.js +19 -4
- package/dist/agent-config/template.js +1 -1
- package/dist/commands/agent-apply.js +1 -1
- package/dist/commands/agent-policy.d.ts +2 -1
- package/dist/commands/agent-policy.js +9 -2
- package/dist/commands/agent-run.d.ts +5 -0
- package/dist/commands/agent-run.js +47 -21
- package/dist/commands/agents.js +18 -5
- package/dist/commands/ai.js +5 -11
- package/dist/commands/auth.js +6 -1
- package/dist/commands/billing.js +107 -3
- package/dist/commands/config.d.ts +1 -1
- package/dist/commands/config.js +16 -2
- package/dist/commands/conversations.js +16 -0
- package/dist/commands/datasets.js +17 -15
- package/dist/commands/experiments.js +36 -26
- package/dist/commands/files.js +10 -28
- package/dist/commands/installations.js +46 -1
- package/dist/commands/knowledge-documents.js +24 -32
- package/dist/commands/knowledge.js +5 -0
- package/dist/commands/memory.d.ts +10 -0
- package/dist/commands/memory.js +132 -0
- package/dist/commands/prompts.js +9 -8
- package/dist/commands/schemas.js +15 -6
- package/dist/commands/skill-versions.js +27 -1
- package/dist/commands/skills.js +31 -1
- package/dist/commands/tracing.js +12 -17
- package/dist/index.js +2 -0
- package/dist/lib/agent-decide.d.ts +29 -2
- package/dist/lib/agent-decide.js +100 -3
- package/dist/lib/agent-run.d.ts +50 -3
- package/dist/lib/agent-run.js +168 -16
- package/dist/lib/approval-prompt.js +31 -1
- package/dist/lib/client.d.ts +8 -0
- package/dist/lib/client.js +18 -1
- package/dist/lib/config.d.ts +16 -0
- package/dist/lib/config.js +26 -1
- package/dist/lib/connect-pause.d.ts +71 -0
- package/dist/lib/connect-pause.js +147 -0
- package/dist/lib/errors.js +3 -1
- package/dist/lib/overlay.d.ts +10 -0
- package/dist/lib/overlay.js +20 -0
- package/dist/lib/skills-apply-preview.d.ts +19 -0
- package/dist/lib/skills-apply-preview.js +94 -0
- package/dist/lib/tracing-settings.d.ts +26 -0
- package/dist/lib/tracing-settings.js +25 -0
- package/package.json +1 -1
|
@@ -98,6 +98,22 @@ export function makeConversationsCommand() {
|
|
|
98
98
|
formatSuccess(`Conversation ${conversationId} deleted.`, command);
|
|
99
99
|
});
|
|
100
100
|
});
|
|
101
|
+
cmd
|
|
102
|
+
.command("cancel")
|
|
103
|
+
.description("Ask a running turn of a conversation to stop")
|
|
104
|
+
.argument("<conversationId>", "Conversation ID")
|
|
105
|
+
.requiredOption("--turn <turnId>", "The turn id the client sent as the Backbone-Turn-Id header")
|
|
106
|
+
.action(async (conversationId, opts, command) => {
|
|
107
|
+
await runAction(command, async () => {
|
|
108
|
+
const client = getClient(command);
|
|
109
|
+
await client.POST("/v1/conversations/{conversationId}/cancel", {
|
|
110
|
+
params: { path: { conversationId } },
|
|
111
|
+
body: { turn_id: opts.turn },
|
|
112
|
+
});
|
|
113
|
+
// Accepted, not performed: the turn's own response says whether it stopped.
|
|
114
|
+
formatSuccess(`Stop requested for turn ${opts.turn} of conversation ${conversationId}.`, command);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
101
117
|
const items = new Command("items").description("List a conversation's items");
|
|
102
118
|
items
|
|
103
119
|
.command("list")
|
|
@@ -3,6 +3,7 @@ import { getClient, runAction } from "../lib/client.js";
|
|
|
3
3
|
import { resolveLatestVersionId } from "../lib/datasets.js";
|
|
4
4
|
import { formatPage, formatDetail, formatList, formatSuccess } from "../lib/output.js";
|
|
5
5
|
import { addPaginationOptions, paginationParams } from "../lib/pagination.js";
|
|
6
|
+
import { assertNameNotBlank, overlay } from "../lib/overlay.js";
|
|
6
7
|
import { makeDatasetVersionsCommand } from "./dataset-versions.js";
|
|
7
8
|
export function makeDatasetsCommand() {
|
|
8
9
|
const cmd = new Command("datasets").description("Manage datasets");
|
|
@@ -77,24 +78,25 @@ export function makeDatasetsCommand() {
|
|
|
77
78
|
.option("--metadata <json>", "Metadata (JSON)")
|
|
78
79
|
.action(async (id, opts, command) => {
|
|
79
80
|
await runAction(command, async () => {
|
|
81
|
+
assertNameNotBlank(opts.name);
|
|
82
|
+
const changes = {
|
|
83
|
+
name: opts.name,
|
|
84
|
+
description: opts.description,
|
|
85
|
+
type: opts.type,
|
|
86
|
+
inputSchema: opts.inputSchema ? JSON.parse(opts.inputSchema) : undefined,
|
|
87
|
+
expectedOutputSchema: opts.expectedOutputSchema ? JSON.parse(opts.expectedOutputSchema) : undefined,
|
|
88
|
+
metadata: opts.metadata ? JSON.parse(opts.metadata) : undefined,
|
|
89
|
+
};
|
|
80
90
|
const client = getClient(command);
|
|
81
|
-
//
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if (
|
|
86
|
-
|
|
87
|
-
if (opts.type !== undefined)
|
|
88
|
-
body.type = opts.type;
|
|
89
|
-
if (opts.inputSchema)
|
|
90
|
-
body.inputSchema = JSON.parse(opts.inputSchema);
|
|
91
|
-
if (opts.expectedOutputSchema)
|
|
92
|
-
body.expectedOutputSchema = JSON.parse(opts.expectedOutputSchema);
|
|
93
|
-
if (opts.metadata)
|
|
94
|
-
body.metadata = JSON.parse(opts.metadata);
|
|
91
|
+
// PUT /v1/datasets/{id} replaces the whole dataset, so an omitted field would be stored as null.
|
|
92
|
+
const { data: current } = await client.GET("/v1/datasets/{id}", {
|
|
93
|
+
params: { path: { id } },
|
|
94
|
+
});
|
|
95
|
+
if (!current)
|
|
96
|
+
throw new Error(`Dataset ${id} could not be read.`);
|
|
95
97
|
const { data } = await client.PUT("/v1/datasets/{id}", {
|
|
96
98
|
params: { path: { id } },
|
|
97
|
-
body:
|
|
99
|
+
body: overlay(current, changes),
|
|
98
100
|
});
|
|
99
101
|
formatDetail(data, command);
|
|
100
102
|
});
|
|
@@ -5,6 +5,7 @@ import Table from "cli-table3";
|
|
|
5
5
|
import { getClient, runAction } from "../lib/client.js";
|
|
6
6
|
import { formatPage, formatDetail, formatList, formatSuccess, withSpinner } from "../lib/output.js";
|
|
7
7
|
import { addPaginationOptions, paginationParams } from "../lib/pagination.js";
|
|
8
|
+
import { assertNameNotBlank, overlay } from "../lib/overlay.js";
|
|
8
9
|
import { isJsonOutput } from "../lib/config.js";
|
|
9
10
|
const TERMINAL_STATUSES = new Set(["COMPLETED", "FAILED"]);
|
|
10
11
|
/** Summarize a single cell of the comparison matrix (a variant's result for one item). */
|
|
@@ -92,21 +93,24 @@ export function makeExperimentsCommand() {
|
|
|
92
93
|
.option("--metadata <json>", "Metadata (JSON)")
|
|
93
94
|
.action(async (id, opts, command) => {
|
|
94
95
|
await runAction(command, async () => {
|
|
96
|
+
assertNameNotBlank(opts.name);
|
|
97
|
+
const changes = {
|
|
98
|
+
name: opts.name,
|
|
99
|
+
description: opts.description,
|
|
100
|
+
type: opts.type,
|
|
101
|
+
datasetVersionId: opts.datasetVersion,
|
|
102
|
+
metadata: opts.metadata ? JSON.parse(opts.metadata) : undefined,
|
|
103
|
+
};
|
|
95
104
|
const client = getClient(command);
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
body.type = opts.type;
|
|
103
|
-
if (opts.datasetVersion !== undefined)
|
|
104
|
-
body.datasetVersionId = opts.datasetVersion;
|
|
105
|
-
if (opts.metadata)
|
|
106
|
-
body.metadata = JSON.parse(opts.metadata);
|
|
105
|
+
// PUT /v1/experiments/{id} replaces the whole experiment, so an omitted field would be stored as null.
|
|
106
|
+
const { data: current } = await client.GET("/v1/experiments/{id}", {
|
|
107
|
+
params: { path: { id } },
|
|
108
|
+
});
|
|
109
|
+
if (!current)
|
|
110
|
+
throw new Error(`Experiment ${id} could not be read.`);
|
|
107
111
|
const { data } = await client.PUT("/v1/experiments/{id}", {
|
|
108
112
|
params: { path: { id } },
|
|
109
|
-
body:
|
|
113
|
+
body: overlay(current, changes),
|
|
110
114
|
});
|
|
111
115
|
formatDetail(data, command);
|
|
112
116
|
});
|
|
@@ -131,7 +135,7 @@ export function makeExperimentsCommand() {
|
|
|
131
135
|
.description("Add a variant to an experiment")
|
|
132
136
|
.requiredOption("--experiment <id>", "Experiment ID")
|
|
133
137
|
.requiredOption("-n, --name <name>", "Variant name")
|
|
134
|
-
.requiredOption("--task-type <type>", "Task type")
|
|
138
|
+
.requiredOption("--task-type <type>", "Task type: extraction or agent")
|
|
135
139
|
.requiredOption("--config <json>", "Variant configuration (JSON)")
|
|
136
140
|
.option("-d, --description <text>", "Variant description")
|
|
137
141
|
.option("--sort-order <n>", "Sort order", parseInt)
|
|
@@ -179,27 +183,33 @@ export function makeExperimentsCommand() {
|
|
|
179
183
|
.argument("<variantId>", "Variant ID")
|
|
180
184
|
.requiredOption("--experiment <id>", "Experiment ID")
|
|
181
185
|
.option("-n, --name <name>", "Variant name")
|
|
182
|
-
.option("--task-type <type>", "Task type")
|
|
186
|
+
.option("--task-type <type>", "Task type; must equal the variant's current one (it cannot change)")
|
|
183
187
|
.option("--config <json>", "Variant configuration (JSON)")
|
|
184
188
|
.option("-d, --description <text>", "Variant description")
|
|
185
189
|
.option("--sort-order <n>", "Sort order", parseInt)
|
|
186
190
|
.action(async (variantId, opts, command) => {
|
|
187
191
|
await runAction(command, async () => {
|
|
192
|
+
assertNameNotBlank(opts.name);
|
|
193
|
+
const changes = {
|
|
194
|
+
name: opts.name,
|
|
195
|
+
taskType: opts.taskType,
|
|
196
|
+
configuration: opts.config ? JSON.parse(opts.config) : undefined,
|
|
197
|
+
description: opts.description,
|
|
198
|
+
sortOrder: opts.sortOrder,
|
|
199
|
+
};
|
|
188
200
|
const client = getClient(command);
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
if (
|
|
197
|
-
|
|
198
|
-
if (opts.sortOrder !== undefined)
|
|
199
|
-
body.sortOrder = opts.sortOrder;
|
|
201
|
+
// The variant PUT writes name, description and configuration as sent, so an omitted one would
|
|
202
|
+
// be cleared. There is no single-variant GET: read it from the list. The read's version goes
|
|
203
|
+
// back with the body, so a change made since is refused with 409.
|
|
204
|
+
const { data: variants } = await client.GET("/v1/experiments/{id}/variants", {
|
|
205
|
+
params: { path: { id: opts.experiment } },
|
|
206
|
+
});
|
|
207
|
+
const current = variants?.find((v) => v.id === variantId);
|
|
208
|
+
if (!current)
|
|
209
|
+
throw new Error(`Variant ${variantId} is not a variant of experiment ${opts.experiment}.`);
|
|
200
210
|
const { data } = await client.PUT("/v1/experiments/{id}/variants/{variantId}", {
|
|
201
211
|
params: { path: { id: opts.experiment, variantId } },
|
|
202
|
-
body:
|
|
212
|
+
body: overlay(current, changes),
|
|
203
213
|
});
|
|
204
214
|
formatDetail(data, command);
|
|
205
215
|
});
|
package/dist/commands/files.js
CHANGED
|
@@ -8,40 +8,22 @@ import { formatPage, formatDetail, formatSuccess, withSpinner } from "../lib/out
|
|
|
8
8
|
import { addPaginationOptions, paginationParams } from "../lib/pagination.js";
|
|
9
9
|
import { fileToBlob } from "../lib/multipart.js";
|
|
10
10
|
/**
|
|
11
|
-
* Upload a single file with purpose=agent_input|knowledge.
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* Upload a single file with purpose=agent_input|knowledge. The generated type
|
|
12
|
+
* describes the "file" part as a binary string, which FormData cannot match,
|
|
13
|
+
* so the placeholder body satisfies the type and bodySerializer sends the
|
|
14
|
+
* real part.
|
|
15
15
|
*/
|
|
16
16
|
async function uploadFile(command, filePath, purpose) {
|
|
17
|
-
const config = resolveConfig(command);
|
|
18
|
-
const authHeader = await resolveAuthHeader(config);
|
|
19
17
|
const { blob, filename } = fileToBlob(filePath);
|
|
20
18
|
const formData = new FormData();
|
|
21
19
|
formData.append("file", blob, filename);
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
body: formData,
|
|
20
|
+
const client = getClient(command);
|
|
21
|
+
const { data } = await client.POST("/v1/files", {
|
|
22
|
+
params: { query: { purpose } },
|
|
23
|
+
body: { file: "" },
|
|
24
|
+
bodySerializer: () => formData,
|
|
28
25
|
});
|
|
29
|
-
|
|
30
|
-
let body;
|
|
31
|
-
try {
|
|
32
|
-
body = await res.json();
|
|
33
|
-
}
|
|
34
|
-
catch {
|
|
35
|
-
body = {
|
|
36
|
-
error: res.statusText,
|
|
37
|
-
message: `HTTP ${res.status}: ${res.statusText}`,
|
|
38
|
-
status: res.status,
|
|
39
|
-
timestamp: new Date().toISOString(),
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
throw new BackboneApiError(body);
|
|
43
|
-
}
|
|
44
|
-
return (await res.json());
|
|
26
|
+
return data;
|
|
45
27
|
}
|
|
46
28
|
/** Stream a file's bytes from /v1/files/{fileId}/content via a raw fetch. */
|
|
47
29
|
async function downloadFile(command, fileId) {
|
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { getClient, runAction } from "../lib/client.js";
|
|
3
|
+
import { CliUsageError } from "../lib/errors.js";
|
|
3
4
|
import { formatPage, formatDetail, formatList, formatSuccess } from "../lib/output.js";
|
|
4
5
|
import { addPaginationOptions, paginationParams } from "../lib/pagination.js";
|
|
6
|
+
const MIN_MONTHLY_PERCENT = 0;
|
|
7
|
+
const MAX_MONTHLY_PERCENT = 1000;
|
|
8
|
+
/**
|
|
9
|
+
* Validated inside the action rather than as commander's argument parser, so the refusal
|
|
10
|
+
* travels through `runAction` and exits 2 like every other usage error (see `parsePort` in
|
|
11
|
+
* connectors.ts).
|
|
12
|
+
*/
|
|
13
|
+
function parseMonthlyPercent(value) {
|
|
14
|
+
const percent = /^\d+$/.test(value) ? Number(value) : NaN;
|
|
15
|
+
if (!Number.isInteger(percent) || percent < MIN_MONTHLY_PERCENT || percent > MAX_MONTHLY_PERCENT) {
|
|
16
|
+
throw new CliUsageError(`percent must be a whole number between ${MIN_MONTHLY_PERCENT} and ${MAX_MONTHLY_PERCENT}, not '${value}'`);
|
|
17
|
+
}
|
|
18
|
+
return percent;
|
|
19
|
+
}
|
|
5
20
|
export function makeInstallationsCommand() {
|
|
6
21
|
const cmd = new Command("installations").description("Manage surface installations");
|
|
7
22
|
const list = new Command("list").description("List installations");
|
|
@@ -73,7 +88,7 @@ export function makeInstallationsCommand() {
|
|
|
73
88
|
body.status = opts.status;
|
|
74
89
|
const { data } = await client.PUT("/v1/installations/{id}", {
|
|
75
90
|
params: { path: { id } },
|
|
76
|
-
body
|
|
91
|
+
body,
|
|
77
92
|
});
|
|
78
93
|
formatDetail(data, command);
|
|
79
94
|
});
|
|
@@ -172,6 +187,36 @@ export function makeInstallationsCommand() {
|
|
|
172
187
|
});
|
|
173
188
|
});
|
|
174
189
|
cmd.addCommand(keys);
|
|
190
|
+
const budget = new Command("budget").description("Manage an installation's monthly budget: a share of the organisation's pool");
|
|
191
|
+
budget
|
|
192
|
+
.command("get")
|
|
193
|
+
.description("Read an installation's current budget: percent, period, and usage")
|
|
194
|
+
.argument("<id>", "Installation ID")
|
|
195
|
+
.action(async (id, _opts, command) => {
|
|
196
|
+
await runAction(command, async () => {
|
|
197
|
+
const client = getClient(command);
|
|
198
|
+
const { data } = await client.GET("/v1/installations/{id}/budget", {
|
|
199
|
+
params: { path: { id } },
|
|
200
|
+
});
|
|
201
|
+
formatDetail(data, command);
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
budget
|
|
205
|
+
.command("set")
|
|
206
|
+
.description("Set or change an installation's monthly budget share, 0-1000 percent")
|
|
207
|
+
.argument("<id>", "Installation ID")
|
|
208
|
+
.argument("<percent>", "Monthly percent of the organisation's pool, 0-1000")
|
|
209
|
+
.action(async (id, percent, _opts, command) => {
|
|
210
|
+
await runAction(command, async () => {
|
|
211
|
+
const client = getClient(command);
|
|
212
|
+
const { data } = await client.PUT("/v1/installations/{id}/budget", {
|
|
213
|
+
params: { path: { id } },
|
|
214
|
+
body: { monthlyPercent: parseMonthlyPercent(percent) },
|
|
215
|
+
});
|
|
216
|
+
formatDetail(data, command);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
cmd.addCommand(budget);
|
|
175
220
|
return cmd;
|
|
176
221
|
}
|
|
177
222
|
//# sourceMappingURL=installations.js.map
|
|
@@ -1,49 +1,29 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import chalk from "chalk";
|
|
3
|
-
import { getClient,
|
|
4
|
-
import {
|
|
5
|
-
import { BackboneApiError } from "../lib/errors.js";
|
|
3
|
+
import { getClient, runAction } from "../lib/client.js";
|
|
4
|
+
import { isJsonOutput } from "../lib/config.js";
|
|
6
5
|
import { formatPage, formatDetail, formatSuccess, withSpinner } from "../lib/output.js";
|
|
7
6
|
import { addPaginationOptions, paginationParams } from "../lib/pagination.js";
|
|
8
7
|
import { fileToBlob } from "../lib/multipart.js";
|
|
9
8
|
/**
|
|
10
|
-
* Upload one or more local files to a knowledge base
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* multipart/form-data body with repeated "files" parts.
|
|
9
|
+
* Upload one or more local files to a knowledge base as repeated "files"
|
|
10
|
+
* parts of a multipart/form-data body. The generated type describes each part
|
|
11
|
+
* as a binary string, which FormData cannot match, so the placeholder body
|
|
12
|
+
* satisfies the type and bodySerializer sends the real parts.
|
|
15
13
|
*/
|
|
16
14
|
async function uploadDocuments(command, knowledgeBaseId, paths) {
|
|
17
|
-
const config = resolveConfig(command);
|
|
18
|
-
const authHeader = await resolveAuthHeader(config);
|
|
19
15
|
const formData = new FormData();
|
|
20
16
|
for (const p of paths) {
|
|
21
17
|
const { blob, filename } = fileToBlob(p);
|
|
22
18
|
formData.append("files", blob, filename);
|
|
23
19
|
}
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
body: formData,
|
|
20
|
+
const client = getClient(command);
|
|
21
|
+
const { data } = await client.POST("/v1/knowledge-bases/{knowledgeBaseId}/documents", {
|
|
22
|
+
params: { path: { knowledgeBaseId } },
|
|
23
|
+
body: { files: [] },
|
|
24
|
+
bodySerializer: () => formData,
|
|
30
25
|
});
|
|
31
|
-
|
|
32
|
-
let body;
|
|
33
|
-
try {
|
|
34
|
-
body = await res.json();
|
|
35
|
-
}
|
|
36
|
-
catch {
|
|
37
|
-
body = {
|
|
38
|
-
error: res.statusText,
|
|
39
|
-
message: `HTTP ${res.status}: ${res.statusText}`,
|
|
40
|
-
status: res.status,
|
|
41
|
-
timestamp: new Date().toISOString(),
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
throw new BackboneApiError(body);
|
|
45
|
-
}
|
|
46
|
-
return (await res.json());
|
|
26
|
+
return data ?? [];
|
|
47
27
|
}
|
|
48
28
|
function printUploadResults(results, command) {
|
|
49
29
|
if (isJsonOutput(command)) {
|
|
@@ -119,6 +99,18 @@ export function makeKnowledgeDocumentsCommand() {
|
|
|
119
99
|
formatDetail(data, command);
|
|
120
100
|
});
|
|
121
101
|
});
|
|
102
|
+
cmd
|
|
103
|
+
.command("retry")
|
|
104
|
+
.description("Re-ingest a document whose newest revision failed, from its stored file")
|
|
105
|
+
.argument("<documentId>", "Document ID")
|
|
106
|
+
.requiredOption("--kb <id>", "Knowledge base ID")
|
|
107
|
+
.action(async (documentId, opts, command) => {
|
|
108
|
+
await runAction(command, async () => {
|
|
109
|
+
const client = getClient(command);
|
|
110
|
+
const { data } = await client.POST("/v1/knowledge-bases/{knowledgeBaseId}/documents/{documentId}/retry", { params: { path: { knowledgeBaseId: opts.kb, documentId } } });
|
|
111
|
+
formatDetail(data, command);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
122
114
|
cmd
|
|
123
115
|
.command("delete")
|
|
124
116
|
.description("Delete a document and take every revision out of retrieval")
|
|
@@ -47,6 +47,7 @@ export function makeKnowledgeCommand() {
|
|
|
47
47
|
.option("--parent-chunk-size <n>", "Parent chunk size (hierarchical chunking)", parseInt)
|
|
48
48
|
.option("--reranker-provider <id>", "Reranker provider ID")
|
|
49
49
|
.option("--hybrid-search <bool>", "Enable hybrid search (true/false)")
|
|
50
|
+
.option("--text-search-language <name>", "Text-search language for the lexical leg (default simple)")
|
|
50
51
|
.action(async (opts, command) => {
|
|
51
52
|
await runAction(command, async () => {
|
|
52
53
|
const client = getClient(command);
|
|
@@ -64,6 +65,7 @@ export function makeKnowledgeCommand() {
|
|
|
64
65
|
parentChunkSize: opts.parentChunkSize,
|
|
65
66
|
rerankerProviderId: opts.rerankerProvider,
|
|
66
67
|
hybridSearchEnabled: opts.hybridSearch !== undefined ? opts.hybridSearch === "true" : undefined,
|
|
68
|
+
textSearchLanguage: opts.textSearchLanguage,
|
|
67
69
|
},
|
|
68
70
|
});
|
|
69
71
|
formatDetail(data, command);
|
|
@@ -83,6 +85,7 @@ export function makeKnowledgeCommand() {
|
|
|
83
85
|
.option("--parent-chunk-size <n>", "New parent chunk size", parseInt)
|
|
84
86
|
.option("--reranker-provider <id>", "New reranker provider ID")
|
|
85
87
|
.option("--hybrid-search <bool>", "Enable hybrid search (true/false)")
|
|
88
|
+
.option("--text-search-language <name>", "Change the text-search language (reindexes in the background); omit to leave it untouched")
|
|
86
89
|
.action(async (id, opts, command) => {
|
|
87
90
|
await runAction(command, async () => {
|
|
88
91
|
const client = getClient(command);
|
|
@@ -107,6 +110,8 @@ export function makeKnowledgeCommand() {
|
|
|
107
110
|
body.rerankerProviderId = opts.rerankerProvider;
|
|
108
111
|
if (opts.hybridSearch !== undefined)
|
|
109
112
|
body.hybridSearchEnabled = opts.hybridSearch === "true";
|
|
113
|
+
if (opts.textSearchLanguage)
|
|
114
|
+
body.textSearchLanguage = opts.textSearchLanguage;
|
|
110
115
|
const { data } = await client.PATCH("/v1/knowledge-bases/{id}", {
|
|
111
116
|
params: { path: { id } },
|
|
112
117
|
body: body,
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
/** The If-Match value for a version a GET returned: the quoted ETag the API expects. */
|
|
3
|
+
export declare function ifMatchHeader(version: string): string;
|
|
4
|
+
/**
|
|
5
|
+
* Agent memory (agent memory spec §7.1, §7.4). `ls`, `cat`, `put`, `rm` and `forget` act on
|
|
6
|
+
* the caller's own memory; `members` and `erase` are for ADMIN and OWNER and never show
|
|
7
|
+
* paths or content. Content is lossless: `cat` prints it raw, `put` sends the file as is.
|
|
8
|
+
*/
|
|
9
|
+
export declare function makeMemoryCommand(): Command;
|
|
10
|
+
//# sourceMappingURL=memory.d.ts.map
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { getClient, runAction } from "../lib/client.js";
|
|
4
|
+
import { isJsonOutput } from "../lib/config.js";
|
|
5
|
+
import { CliUsageError } from "../lib/errors.js";
|
|
6
|
+
import { formatDetail, formatList, formatSuccess } from "../lib/output.js";
|
|
7
|
+
/** The If-Match value for a version a GET returned: the quoted ETag the API expects. */
|
|
8
|
+
export function ifMatchHeader(version) {
|
|
9
|
+
if (!/^\d{1,18}$/.test(version)) {
|
|
10
|
+
throw new CliUsageError(`--if-match takes the file's version, a whole number: ${version}`);
|
|
11
|
+
}
|
|
12
|
+
return `"${version}"`;
|
|
13
|
+
}
|
|
14
|
+
function requireYes(opts, what) {
|
|
15
|
+
if (!opts.yes) {
|
|
16
|
+
throw new CliUsageError(`${what} cannot be undone. Pass --yes to confirm.`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Agent memory (agent memory spec §7.1, §7.4). `ls`, `cat`, `put`, `rm` and `forget` act on
|
|
21
|
+
* the caller's own memory; `members` and `erase` are for ADMIN and OWNER and never show
|
|
22
|
+
* paths or content. Content is lossless: `cat` prints it raw, `put` sends the file as is.
|
|
23
|
+
*/
|
|
24
|
+
export function makeMemoryCommand() {
|
|
25
|
+
const cmd = new Command("memory").description("Your agent memory files; member totals and erasure for admins");
|
|
26
|
+
cmd
|
|
27
|
+
.command("ls")
|
|
28
|
+
.description("List your memory files")
|
|
29
|
+
.action(async (_opts, command) => {
|
|
30
|
+
await runAction(command, async () => {
|
|
31
|
+
const client = getClient(command);
|
|
32
|
+
const { data } = await client.GET("/v1/memories/me/files");
|
|
33
|
+
formatList(data, command, [
|
|
34
|
+
"path",
|
|
35
|
+
"sizeBytes",
|
|
36
|
+
"version",
|
|
37
|
+
"updatedAt",
|
|
38
|
+
"lastWrittenByAgentId",
|
|
39
|
+
]);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
cmd
|
|
43
|
+
.command("cat <path>")
|
|
44
|
+
.description("Print one of your memory files, raw (--json for the file object)")
|
|
45
|
+
.action(async (path, _opts, command) => {
|
|
46
|
+
await runAction(command, async () => {
|
|
47
|
+
const client = getClient(command);
|
|
48
|
+
const { data } = await client.GET("/v1/memories/me/file", {
|
|
49
|
+
params: { query: { path } },
|
|
50
|
+
});
|
|
51
|
+
if (isJsonOutput(command)) {
|
|
52
|
+
formatDetail(data, command);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
process.stdout.write(data.content);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
cmd
|
|
60
|
+
.command("put <path>")
|
|
61
|
+
.description("Create or replace one of your memory files from a local UTF-8 file")
|
|
62
|
+
.requiredOption("--file <file>", "Local file whose content is stored")
|
|
63
|
+
.option("--if-match <version>", "Only replace the file if it still has this version")
|
|
64
|
+
.action(async (path, opts, command) => {
|
|
65
|
+
await runAction(command, async () => {
|
|
66
|
+
// An empty --if-match (e.g. an unset shell variable) must be refused, never dropped
|
|
67
|
+
// into an unconditional overwrite.
|
|
68
|
+
const headers = opts.ifMatch !== undefined ? { "If-Match": ifMatchHeader(opts.ifMatch) } : undefined;
|
|
69
|
+
const content = readFileSync(opts.file, "utf8");
|
|
70
|
+
const client = getClient(command);
|
|
71
|
+
const { data } = await client.PUT("/v1/memories/me/file", {
|
|
72
|
+
params: { query: { path } },
|
|
73
|
+
body: { content },
|
|
74
|
+
headers,
|
|
75
|
+
});
|
|
76
|
+
formatDetail(data, command);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
cmd
|
|
80
|
+
.command("rm <path>")
|
|
81
|
+
.description("Delete one of your memory files, or a directory with everything below it")
|
|
82
|
+
.action(async (path, _opts, command) => {
|
|
83
|
+
await runAction(command, async () => {
|
|
84
|
+
const client = getClient(command);
|
|
85
|
+
await client.DELETE("/v1/memories/me/file", { params: { query: { path } } });
|
|
86
|
+
formatSuccess(`Deleted ${path}`, command);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
cmd
|
|
90
|
+
.command("forget")
|
|
91
|
+
.description("Delete your whole memory (cannot be undone)")
|
|
92
|
+
.option("--yes", "Confirm")
|
|
93
|
+
.action(async (opts, command) => {
|
|
94
|
+
await runAction(command, async () => {
|
|
95
|
+
requireYes(opts, "Forgetting your whole memory");
|
|
96
|
+
const client = getClient(command);
|
|
97
|
+
await client.DELETE("/v1/memories/me");
|
|
98
|
+
formatSuccess("Forgot your whole memory", command);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
cmd
|
|
102
|
+
.command("members")
|
|
103
|
+
.description("Memory totals per member, without paths or content (ADMIN+)")
|
|
104
|
+
.action(async (_opts, command) => {
|
|
105
|
+
await runAction(command, async () => {
|
|
106
|
+
const client = getClient(command);
|
|
107
|
+
const { data } = await client.GET("/v1/memories/users");
|
|
108
|
+
formatList(data, command, [
|
|
109
|
+
"userId",
|
|
110
|
+
"fileCount",
|
|
111
|
+
"totalBytes",
|
|
112
|
+
"lastUpdatedAt",
|
|
113
|
+
]);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
cmd
|
|
117
|
+
.command("erase <userId>")
|
|
118
|
+
.description("Erase a member's whole memory (ADMIN+, cannot be undone)")
|
|
119
|
+
.option("--yes", "Confirm")
|
|
120
|
+
.action(async (userId, opts, command) => {
|
|
121
|
+
await runAction(command, async () => {
|
|
122
|
+
requireYes(opts, "Erasing a member's memory");
|
|
123
|
+
const client = getClient(command);
|
|
124
|
+
await client.DELETE("/v1/memories/users/{userId}", {
|
|
125
|
+
params: { path: { userId } },
|
|
126
|
+
});
|
|
127
|
+
formatSuccess(`Erased the memory of ${userId}`, command);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
return cmd;
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=memory.js.map
|
package/dist/commands/prompts.js
CHANGED
|
@@ -2,6 +2,7 @@ import { Command } from "commander";
|
|
|
2
2
|
import { getClient, runAction } from "../lib/client.js";
|
|
3
3
|
import { formatPage, formatDetail, formatSuccess } from "../lib/output.js";
|
|
4
4
|
import { addPaginationOptions, paginationParams } from "../lib/pagination.js";
|
|
5
|
+
import { assertNameNotBlank, overlay } from "../lib/overlay.js";
|
|
5
6
|
import { makePromptVersionsCommand } from "./prompt-versions.js";
|
|
6
7
|
import { makePromptLabelsCommand } from "./prompt-labels.js";
|
|
7
8
|
export function makePromptsCommand() {
|
|
@@ -59,17 +60,17 @@ export function makePromptsCommand() {
|
|
|
59
60
|
.option("--type <type>", "New type")
|
|
60
61
|
.action(async (id, opts, command) => {
|
|
61
62
|
await runAction(command, async () => {
|
|
63
|
+
assertNameNotBlank(opts.name);
|
|
62
64
|
const client = getClient(command);
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
body.type = opts.type;
|
|
65
|
+
// PUT /v1/prompts/{id} replaces the whole prompt, so an omitted field would be stored as null.
|
|
66
|
+
const { data: current } = await client.GET("/v1/prompts/{id}", {
|
|
67
|
+
params: { path: { id } },
|
|
68
|
+
});
|
|
69
|
+
if (!current)
|
|
70
|
+
throw new Error(`Prompt ${id} could not be read.`);
|
|
70
71
|
const { data } = await client.PUT("/v1/prompts/{id}", {
|
|
71
72
|
params: { path: { id } },
|
|
72
|
-
body:
|
|
73
|
+
body: overlay(current, { name: opts.name, description: opts.description, type: opts.type }),
|
|
73
74
|
});
|
|
74
75
|
formatDetail(data, command);
|
|
75
76
|
});
|
package/dist/commands/schemas.js
CHANGED
|
@@ -58,15 +58,24 @@ export function makeSchemasCommand() {
|
|
|
58
58
|
.option("-d, --description <text>", "New description")
|
|
59
59
|
.action(async (id, opts, command) => {
|
|
60
60
|
await runAction(command, async () => {
|
|
61
|
+
let name = opts.name;
|
|
62
|
+
let description = opts.description;
|
|
63
|
+
if (name !== undefined && !name.trim())
|
|
64
|
+
throw new Error("--name must not be empty.");
|
|
61
65
|
const client = getClient(command);
|
|
62
|
-
|
|
63
|
-
if (
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
// PUT /v1/schemas/{id} replaces the whole schema, so an omitted field would be stored as null.
|
|
67
|
+
if (name === undefined || description === undefined) {
|
|
68
|
+
const { data: current } = await client.GET("/v1/schemas/{id}", {
|
|
69
|
+
params: { path: { id } },
|
|
70
|
+
});
|
|
71
|
+
if (!current)
|
|
72
|
+
throw new Error(`Schema ${id} could not be read.`);
|
|
73
|
+
name ??= current.name;
|
|
74
|
+
description ??= current.description;
|
|
75
|
+
}
|
|
67
76
|
const { data } = await client.PUT("/v1/schemas/{id}", {
|
|
68
77
|
params: { path: { id } },
|
|
69
|
-
body:
|
|
78
|
+
body: { name, description },
|
|
70
79
|
});
|
|
71
80
|
formatDetail(data, command);
|
|
72
81
|
});
|