@2kw/ai 6.2.2 → 6.3.0-dev.106
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 +3 -0
- package/dist/agent-config/schema.d.ts +6 -3
- package/dist/agent-config/schema.js +13 -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 +37 -17
- package/dist/commands/agents.js +33 -6
- package/dist/commands/ai.js +3 -9
- package/dist/commands/auth.js +6 -1
- package/dist/commands/billing.js +5 -3
- package/dist/commands/config.d.ts +1 -1
- package/dist/commands/config.js +16 -2
- package/dist/commands/connectors.d.ts +10 -0
- package/dist/commands/connectors.js +97 -0
- 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 +1 -1
- package/dist/commands/knowledge-documents.js +24 -32
- 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/relays.d.ts +3 -0
- package/dist/commands/relays.js +135 -0
- package/dist/commands/schemas.js +15 -6
- package/dist/commands/settings.d.ts +13 -0
- package/dist/commands/settings.js +80 -0
- package/dist/commands/skill-versions.js +27 -1
- package/dist/commands/skills.js +31 -1
- package/dist/commands/tracing.js +5 -12
- package/dist/index.js +8 -0
- package/dist/lib/agent-decide.d.ts +3 -2
- package/dist/lib/agent-decide.js +5 -2
- package/dist/lib/agent-run.d.ts +49 -3
- package/dist/lib/agent-run.js +152 -9
- package/dist/lib/approval-prompt.js +31 -1
- package/dist/lib/client.d.ts +8 -0
- package/dist/lib/client.js +20 -1
- package/dist/lib/config.d.ts +16 -0
- package/dist/lib/config.js +40 -1
- package/dist/lib/errors.d.ts +6 -0
- package/dist/lib/errors.js +9 -0
- 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
|
@@ -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,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")
|
|
@@ -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
|
});
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { getClient, runAction } from "../lib/client.js";
|
|
3
|
+
import { isJsonOutput } from "../lib/config.js";
|
|
4
|
+
import { formatDetail, formatPage, formatSuccess } from "../lib/output.js";
|
|
5
|
+
import { addPaginationOptions, paginationParams } from "../lib/pagination.js";
|
|
6
|
+
/**
|
|
7
|
+
* Prints an enrolment response in text mode. The relay record comes first when the response
|
|
8
|
+
* carries one: `create` is the only place the new relay's id is ever shown, and every other
|
|
9
|
+
* subcommand takes that id as its argument.
|
|
10
|
+
*/
|
|
11
|
+
function printEnrolment(data, command) {
|
|
12
|
+
if (isJsonOutput(command)) {
|
|
13
|
+
console.log(JSON.stringify(data, null, 2));
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (data.relay) {
|
|
17
|
+
formatDetail(data.relay, command);
|
|
18
|
+
console.log("");
|
|
19
|
+
}
|
|
20
|
+
console.log(data.enrolmentExpiresAt
|
|
21
|
+
? `Enrolment token (shown once, valid until ${data.enrolmentExpiresAt}):`
|
|
22
|
+
: "Enrolment token (shown once):");
|
|
23
|
+
console.log(` ${data.enrolmentToken ?? ""}`);
|
|
24
|
+
console.log("");
|
|
25
|
+
console.log("Run the relay inside your network:");
|
|
26
|
+
console.log(data.dockerRun ?? "");
|
|
27
|
+
}
|
|
28
|
+
export function makeRelaysCommand() {
|
|
29
|
+
const cmd = new Command("relays").description("Manage MCP relays that reach MCP servers on private networks");
|
|
30
|
+
const list = new Command("list").description("List MCP relays");
|
|
31
|
+
addPaginationOptions(list);
|
|
32
|
+
list.action(async (opts, command) => {
|
|
33
|
+
await runAction(command, async () => {
|
|
34
|
+
const client = getClient(command);
|
|
35
|
+
const { data } = await client.GET("/v1/mcp-relays", {
|
|
36
|
+
params: { query: { ...paginationParams(opts) } },
|
|
37
|
+
});
|
|
38
|
+
formatPage(data, command, [
|
|
39
|
+
"id",
|
|
40
|
+
"name",
|
|
41
|
+
"status",
|
|
42
|
+
"online",
|
|
43
|
+
"relayVersion",
|
|
44
|
+
"inventoryReportedAt",
|
|
45
|
+
]);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
cmd.addCommand(list);
|
|
49
|
+
cmd
|
|
50
|
+
.command("get")
|
|
51
|
+
.description("Show a relay with its inventory")
|
|
52
|
+
.argument("<id>", "Relay ID")
|
|
53
|
+
.action(async (id, _opts, command) => {
|
|
54
|
+
await runAction(command, async () => {
|
|
55
|
+
const client = getClient(command);
|
|
56
|
+
const { data } = await client.GET("/v1/mcp-relays/{id}", {
|
|
57
|
+
params: { path: { id } },
|
|
58
|
+
});
|
|
59
|
+
formatDetail(data, command);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
cmd
|
|
63
|
+
.command("create")
|
|
64
|
+
.description("Create a relay; prints a single-use enrolment token and a docker run snippet")
|
|
65
|
+
.requiredOption("--name <name>", 'Display name, e.g. "Office Berlin"')
|
|
66
|
+
.action(async (opts, command) => {
|
|
67
|
+
await runAction(command, async () => {
|
|
68
|
+
const client = getClient(command);
|
|
69
|
+
const { data } = await client.POST("/v1/mcp-relays", {
|
|
70
|
+
body: { name: opts.name },
|
|
71
|
+
});
|
|
72
|
+
printEnrolment(data ?? {}, command);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
cmd
|
|
76
|
+
.command("rename")
|
|
77
|
+
.description("Rename a relay")
|
|
78
|
+
.argument("<id>", "Relay ID")
|
|
79
|
+
.requiredOption("--name <name>", "New name")
|
|
80
|
+
.action(async (id, opts, command) => {
|
|
81
|
+
await runAction(command, async () => {
|
|
82
|
+
const client = getClient(command);
|
|
83
|
+
const { data } = await client.PATCH("/v1/mcp-relays/{id}", {
|
|
84
|
+
params: { path: { id } },
|
|
85
|
+
body: { name: opts.name },
|
|
86
|
+
});
|
|
87
|
+
formatDetail(data, command);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
for (const [name, disabled, description] of [
|
|
91
|
+
["disable", true, "Disable a relay: it gets no new work and its enrolment token is voided"],
|
|
92
|
+
["enable", false, "Enable a disabled relay"],
|
|
93
|
+
]) {
|
|
94
|
+
cmd
|
|
95
|
+
.command(name)
|
|
96
|
+
.description(description)
|
|
97
|
+
.argument("<id>", "Relay ID")
|
|
98
|
+
.action(async (id, _opts, command) => {
|
|
99
|
+
await runAction(command, async () => {
|
|
100
|
+
const client = getClient(command);
|
|
101
|
+
const { data } = await client.PATCH("/v1/mcp-relays/{id}", {
|
|
102
|
+
params: { path: { id } },
|
|
103
|
+
body: { disabled },
|
|
104
|
+
});
|
|
105
|
+
formatDetail(data, command);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
cmd
|
|
110
|
+
.command("re-enrol")
|
|
111
|
+
.description("Issue a new enrolment token; redeeming it replaces the relay's key")
|
|
112
|
+
.argument("<id>", "Relay ID")
|
|
113
|
+
.action(async (id, _opts, command) => {
|
|
114
|
+
await runAction(command, async () => {
|
|
115
|
+
const client = getClient(command);
|
|
116
|
+
const { data } = await client.POST("/v1/mcp-relays/{id}/enrolment-token", {
|
|
117
|
+
params: { path: { id } },
|
|
118
|
+
});
|
|
119
|
+
printEnrolment(data ?? {}, command);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
cmd
|
|
123
|
+
.command("delete")
|
|
124
|
+
.description("Revoke a relay for good")
|
|
125
|
+
.argument("<id>", "Relay ID")
|
|
126
|
+
.action(async (id, _opts, command) => {
|
|
127
|
+
await runAction(command, async () => {
|
|
128
|
+
const client = getClient(command);
|
|
129
|
+
await client.DELETE("/v1/mcp-relays/{id}", { params: { path: { id } } });
|
|
130
|
+
formatSuccess(`Relay ${id} revoked.`, command);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
return cmd;
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=relays.js.map
|
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
|
});
|