@wirecat/cli-core 0.409.0 → 0.411.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +47 -0
- package/dist/cli/bot/command.js +1 -0
- package/dist/cli/bot/port.d.ts +1 -1
- package/dist/cli/bot/setup.js +11 -5
- package/dist/cli/commands-command.js +18 -12
- package/dist/cli/contract-evidence.d.ts +26 -0
- package/dist/cli/contract-evidence.js +58 -0
- package/dist/cli/contract-templates.d.ts +4 -0
- package/dist/cli/contract-templates.js +83 -0
- package/dist/cli/discovery-descriptions.d.ts +4 -0
- package/dist/cli/discovery-descriptions.js +4 -0
- package/dist/cli/index.d.ts +22 -55
- package/dist/cli/index.js +19 -55
- package/dist/cli/messenger/chat-answers.d.ts +1 -1
- package/dist/cli/messenger/chat-answers.js +1 -1
- package/dist/cli/messenger/chat-lists.d.ts +1 -1
- package/dist/cli/messenger/chats-command.js +3 -0
- package/dist/cli/messenger/contacts-command.js +23 -15
- package/dist/cli/messenger/guard-commands.d.ts +5 -1
- package/dist/cli/messenger/guard-commands.js +5 -3
- package/dist/cli/messenger/messages-press-command.js +4 -1
- package/dist/cli/messenger/models-command.js +3 -11
- package/dist/cli/messenger/records-command.js +1 -1
- package/dist/cli/messenger/report.d.ts +21 -6
- package/dist/cli/messenger/report.js +20 -5
- package/dist/cli/messenger/tasks-command.js +14 -4
- package/dist/cli/model-catalogue-command.d.ts +15 -0
- package/dist/cli/model-catalogue-command.js +110 -0
- package/dist/cli/program.js +2 -0
- package/dist/cli/runs/search-command.js +2 -3
- package/dist/cli/runs/search-query.d.ts +1 -1
- package/dist/cli/runs/search-query.js +18 -2
- package/dist/cli/runs/search.d.ts +2 -1
- package/dist/cli/runs/search.js +4 -3
- package/dist/cli/shared-command-exports.d.ts +644 -0
- package/dist/cli/shared-command-exports.js +114 -0
- package/dist/cli/shared-contracts.d.ts +1 -0
- package/dist/cli/shared-contracts.js +1 -0
- package/dist/domain/attachments.js +1 -0
- package/dist/entities/chat.d.ts +3 -3
- package/dist/entities/chat.js +2 -2
- package/dist/entities/contact-context.d.ts +8 -8
- package/dist/entities/contact.d.ts +4 -4
- package/dist/entities/index.d.ts +11 -12
- package/dist/entities/list-results.d.ts +1 -1
- package/dist/entities/list-results.js +1 -1
- package/dist/entities/send-attempt.d.ts +0 -1
- package/dist/entities/send-attempt.js +0 -1
- package/dist/errors/reasons.d.ts +1 -1
- package/dist/errors/reasons.js +7 -0
- package/dist/meetings/cli/index.d.ts +1 -1
- package/dist/meetings/cli/index.js +25 -9
- package/dist/meetings/cli/passage.d.ts +0 -1
- package/dist/meetings/cli/passage.js +5 -3
- package/dist/meetings/cli/project-evidence.js +3 -2
- package/dist/meetings/cli/search.d.ts +2 -2
- package/dist/meetings/cli/search.js +14 -12
- package/dist/meetings/event-query.d.ts +1 -0
- package/dist/meetings/event-query.js +15 -0
- package/dist/meetings/index.d.ts +1 -0
- package/dist/meetings/index.js +1 -0
- package/dist/meetings/store.d.ts +2 -0
- package/dist/meetings/testing/memory.js +5 -0
- package/dist/services/tasks.d.ts +2 -0
- package/dist/services/tasks.js +6 -4
- package/dist/store/sqlite/events.d.ts +2 -1
- package/dist/store/sqlite/events.js +16 -1
- package/dist/store/sqlite/meetings.js +3 -0
- package/package.json +3 -3
- package/parity.json +61 -76
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { CliError } from "@wirecat/cli-base";
|
|
3
|
+
import { annotate } from "@wirecat/cli-base/commands";
|
|
4
|
+
import { defineQuery } from "@wirecat/cli-base/query";
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
import { isTextModelInstalled, textModelsDirectory } from "../embeddings/embed.js";
|
|
7
|
+
import { DEFAULT_TEXT_MODEL, TEXT_MODELS } from "../embeddings/models.js";
|
|
8
|
+
import { downloadTextModel } from "../services/model-downloads.js";
|
|
9
|
+
import { install, installedBytes, isInstalled, modelsDirectory } from "../speech/install.js";
|
|
10
|
+
import { DEFAULT_ORDER, orderedModels, speechModel } from "../speech/models.js";
|
|
11
|
+
import { FETCH, LOCAL_READ, NO_EFFECTS } from "./effects.js";
|
|
12
|
+
import { itemOf, listOf } from "./envelope.js";
|
|
13
|
+
import { listQuery, nativeTerms, termField, typedQuery, withQuery } from "./list-query.js";
|
|
14
|
+
export const MODEL_CATALOGUE_QUERY = defineQuery({
|
|
15
|
+
entity: "model",
|
|
16
|
+
backend: "native",
|
|
17
|
+
fields: [
|
|
18
|
+
termField("kind", "enum", "kind:audio", { values: ["audio", "text"] }),
|
|
19
|
+
termField("id", "kw", "id:e5-small"),
|
|
20
|
+
termField("downloaded", "bool", "downloaded:true"),
|
|
21
|
+
],
|
|
22
|
+
});
|
|
23
|
+
/** Provider composition supplies I/O and supported kinds; parsing, receipts and download policy stay shared. */
|
|
24
|
+
export const modelCatalogueCommand = (app, options) => {
|
|
25
|
+
if (options.kinds.length === 0)
|
|
26
|
+
throw new CliError("configuration_error", "declare at least one supported model kind");
|
|
27
|
+
const audio = (id, env) => {
|
|
28
|
+
const model = speechModel(id);
|
|
29
|
+
const directory = modelsDirectory(env);
|
|
30
|
+
return {
|
|
31
|
+
id,
|
|
32
|
+
kind: "audio",
|
|
33
|
+
title: model.title,
|
|
34
|
+
languages: model.languages,
|
|
35
|
+
size_bytes: installedBytes(model),
|
|
36
|
+
downloaded: isInstalled(model, directory),
|
|
37
|
+
default: id === (options.defaultAudio?.() ?? DEFAULT_ORDER[0]),
|
|
38
|
+
path: join(directory, id),
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
const text = (id, env) => {
|
|
42
|
+
const model = TEXT_MODELS.find((one) => one.id === id);
|
|
43
|
+
if (!model)
|
|
44
|
+
throw new CliError("validation_error", `unknown text model ${id}`);
|
|
45
|
+
const directory = textModelsDirectory(env);
|
|
46
|
+
return {
|
|
47
|
+
id,
|
|
48
|
+
kind: "text",
|
|
49
|
+
title: model.title,
|
|
50
|
+
languages: model.languages,
|
|
51
|
+
licence: model.licence,
|
|
52
|
+
size_bytes: model.files.reduce((sum, file) => sum + file.bytes, 0),
|
|
53
|
+
downloaded: isTextModelInstalled(model, directory),
|
|
54
|
+
default: id === DEFAULT_TEXT_MODEL,
|
|
55
|
+
path: join(directory, id),
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
const models = annotate(new Command("models"), { effects: NO_EFFECTS }).description("models that run on this machine");
|
|
59
|
+
withQuery(annotate(models.command("list"), {
|
|
60
|
+
effects: LOCAL_READ,
|
|
61
|
+
output: { entity: "model" },
|
|
62
|
+
examples: [`${app.command} models list`, `${app.command} models list kind:${options.kinds[0]}`],
|
|
63
|
+
}), MODEL_CATALOGUE_QUERY)
|
|
64
|
+
.description("speech and embedding models, their download status and the default of each kind")
|
|
65
|
+
.action(function (words) {
|
|
66
|
+
const terms = nativeTerms(listQuery(typedQuery(words), MODEL_CATALOGUE_QUERY, "models list"), "models list");
|
|
67
|
+
const env = options.env();
|
|
68
|
+
const items = [
|
|
69
|
+
...(options.kinds.includes("audio") ? orderedModels().map((model) => audio(model.id, env)) : []),
|
|
70
|
+
...(options.kinds.includes("text") ? TEXT_MODELS.map((model) => text(model.id, env)) : []),
|
|
71
|
+
].filter((item) => (terms.get("kind") === undefined || item.kind === terms.get("kind")?.value) &&
|
|
72
|
+
(terms.get("id") === undefined || item.id === terms.get("id")?.value) &&
|
|
73
|
+
(terms.get("downloaded") === undefined || item.downloaded === (terms.get("downloaded")?.value === "true")));
|
|
74
|
+
options.output(listOf("models list", { items, limit: Math.max(1, items.length), has_more: false }), this);
|
|
75
|
+
});
|
|
76
|
+
annotate(models.command("download"), {
|
|
77
|
+
effects: FETCH,
|
|
78
|
+
output: { entity: "model" },
|
|
79
|
+
examples: [
|
|
80
|
+
`${app.command} models download ${options.kinds.includes("text") ? DEFAULT_TEXT_MODEL : DEFAULT_ORDER[0]}`,
|
|
81
|
+
],
|
|
82
|
+
})
|
|
83
|
+
.description("download and verify one speech or embedding model")
|
|
84
|
+
.argument("<model>", "a model id from models list")
|
|
85
|
+
.option("--offline", "refuse a download that needs the network")
|
|
86
|
+
.option("--accept-terms", "accept the model's licence terms, for a model that has its own")
|
|
87
|
+
.action(async function (id, given) {
|
|
88
|
+
if (options.offline?.() || this.optsWithGlobals().offline)
|
|
89
|
+
throw new CliError("permission_error", "downloading a model needs the network; --offline was requested", {
|
|
90
|
+
reason: "model.offline",
|
|
91
|
+
});
|
|
92
|
+
const env = options.env();
|
|
93
|
+
if (TEXT_MODELS.some((model) => model.id === id)) {
|
|
94
|
+
if (!options.kinds.includes("text"))
|
|
95
|
+
throw new CliError("validation_error", "this tool does not use text models");
|
|
96
|
+
await downloadTextModel(id, { env, acceptTerms: given.acceptTerms === true, progress: options.diagnostic });
|
|
97
|
+
options.output(itemOf(text(id, env)), this);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
if (!options.kinds.includes("audio"))
|
|
101
|
+
throw new CliError("validation_error", "this tool does not use audio models");
|
|
102
|
+
await install(speechModel(id), modelsDirectory(env), { verifyExisting: true, progress: options.diagnostic });
|
|
103
|
+
options.output(itemOf(audio(id, env)), this);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
if (options.jsonOption)
|
|
107
|
+
for (const command of models.commands)
|
|
108
|
+
command.option("--json", "print JSON");
|
|
109
|
+
return models;
|
|
110
|
+
};
|
package/dist/cli/program.js
CHANGED
|
@@ -30,6 +30,7 @@ import { PreviewComplete, preview } from "./preview.js";
|
|
|
30
30
|
import { commandWords, liftProfile, PROFILE_FREE, valuedOptions } from "./profile.js";
|
|
31
31
|
import { absolutePaths, relayTarget } from "./relay-route.js";
|
|
32
32
|
import { settingsFor } from "./settings.js";
|
|
33
|
+
import { assertSharedContracts } from "./shared-contracts.js";
|
|
33
34
|
import { commandAt, lineOf, withLines } from "./steps.js";
|
|
34
35
|
/**
|
|
35
36
|
* The command tree, built fresh on each call. Commander is global state by default —
|
|
@@ -66,6 +67,7 @@ export const createProgram = ({ app, commands, configure, firstScreen }, { out,
|
|
|
66
67
|
for (const command of commands())
|
|
67
68
|
program.addCommand(command);
|
|
68
69
|
configure?.(program);
|
|
70
|
+
assertSharedContracts(program);
|
|
69
71
|
installHelp(program, { pitch: app.description, ...firstScreen });
|
|
70
72
|
// Commander builds a usage line from the command tree, where the profile is not a word.
|
|
71
73
|
for (const top of program.commands) {
|
|
@@ -12,7 +12,6 @@ import { RUN_SEARCH_QUERY, runSearchFilter } from "./search-query.js";
|
|
|
12
12
|
export const runsSearchCommand = (app) => annotate(withQuery(new Command("runs")
|
|
13
13
|
.summary("search recorded diagnostic metadata and events")
|
|
14
14
|
.description("search recorded diagnostic metadata and events; literal words and field:value terms joined by AND; no message contents")
|
|
15
|
-
.option("--since-time <time>", "runs starting on or after an ISO time or 2h / 1d ago")
|
|
16
15
|
.option("--limit <n>", "matches per page, at most 100", positiveCount("--limit"), 20)
|
|
17
16
|
.option("--page <n>", "result page", positiveCount("--page"), 1), RUN_SEARCH_QUERY), {
|
|
18
17
|
effects: LOCAL_READ,
|
|
@@ -22,6 +21,6 @@ export const runsSearchCommand = (app) => annotate(withQuery(new Command("runs")
|
|
|
22
21
|
const filter = runSearchFilter(words);
|
|
23
22
|
const options = this.opts();
|
|
24
23
|
const dir = runsDirFor(app, environmentOf(this).env ?? process.env);
|
|
25
|
-
const found = searchRuns(dir, { ...filter, ...options
|
|
26
|
-
renderAnswer(this, outputFor(this), runsFound(found, { query: typedQuery(words)
|
|
24
|
+
const found = searchRuns(dir, { ...filter, ...options });
|
|
25
|
+
renderAnswer(this, outputFor(this), runsFound(found, { query: typedQuery(words) }));
|
|
27
26
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { type QueryWords } from "../list-query.js";
|
|
2
2
|
import type { RunSearch } from "./search.js";
|
|
3
3
|
export declare const RUN_SEARCH_QUERY: import("@wirecat/cli-base/query").QueryRegistry;
|
|
4
|
-
export declare const runSearchFilter: (words: QueryWords) => Pick<RunSearch, "terms" | "status" | "errorCode" | "operation" | "profile">;
|
|
4
|
+
export declare const runSearchFilter: (words: QueryWords) => Pick<RunSearch, "terms" | "status" | "errorCode" | "operation" | "profile" | "since" | "until">;
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import { defineQuery, parseLucene, queryError, validateQuery } from "@wirecat/cli-base/query";
|
|
2
|
-
import { LIST_QUERY_GUIDE, termField, typedQuery } from "../list-query.js";
|
|
1
|
+
import { defineQuery, parseLucene, queryError, validateQuery, } from "@wirecat/cli-base/query";
|
|
2
|
+
import { LIST_QUERY_GUIDE, nativeField, termField, timeBounds, typedQuery } from "../list-query.js";
|
|
3
3
|
export const RUN_SEARCH_QUERY = defineQuery({
|
|
4
4
|
entity: "run",
|
|
5
5
|
backend: "native",
|
|
6
6
|
fields: [
|
|
7
|
+
nativeField({
|
|
8
|
+
name: "started_at",
|
|
9
|
+
type: "time",
|
|
10
|
+
operators: ["term", "phrase", "range"],
|
|
11
|
+
example: "started_at>=7d",
|
|
12
|
+
}),
|
|
7
13
|
termField("text", "kw", "text:timeout"),
|
|
8
14
|
termField("status", "enum", "status:failed", { values: ["success", "failed", "partial", "running"] }),
|
|
9
15
|
termField("error_code", "kw", "error_code:rate_limited"),
|
|
@@ -20,6 +26,7 @@ export const runSearchFilter = (words) => {
|
|
|
20
26
|
validateQuery(ast, RUN_SEARCH_QUERY, context);
|
|
21
27
|
const terms = [];
|
|
22
28
|
const fields = new Map();
|
|
29
|
+
let started;
|
|
23
30
|
const add = (node) => {
|
|
24
31
|
if (node.kind === "boolean") {
|
|
25
32
|
for (const clause of node.clauses) {
|
|
@@ -29,6 +36,12 @@ export const runSearchFilter = (words) => {
|
|
|
29
36
|
}
|
|
30
37
|
return;
|
|
31
38
|
}
|
|
39
|
+
if (node.field === "started_at") {
|
|
40
|
+
if (started)
|
|
41
|
+
queryError("query.unsupported-placement", node.span, "started_at only once here; use a range", context);
|
|
42
|
+
started = node;
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
32
45
|
if (node.operator !== "term" && node.operator !== "phrase")
|
|
33
46
|
queryError("query.unsupported-placement", node.span, "run search takes literal words and field:value terms", context);
|
|
34
47
|
if (node.field === "text")
|
|
@@ -40,7 +53,10 @@ export const runSearchFilter = (words) => {
|
|
|
40
53
|
}
|
|
41
54
|
};
|
|
42
55
|
add(ast.root);
|
|
56
|
+
const window = timeBounds(started);
|
|
43
57
|
return {
|
|
58
|
+
...(window.since === undefined ? {} : { since: new Date(window.since).toISOString() }),
|
|
59
|
+
...(window.until === undefined ? {} : { until: new Date(window.until).toISOString() }),
|
|
44
60
|
...(terms.length ? { terms } : {}),
|
|
45
61
|
...(fields.has("status") ? { status: fields.get("status")?.toLowerCase() } : {}),
|
|
46
62
|
...(fields.has("error_code") ? { errorCode: fields.get("error_code") } : {}),
|
|
@@ -6,10 +6,11 @@ export interface RunSearch {
|
|
|
6
6
|
operation?: string;
|
|
7
7
|
profile?: string;
|
|
8
8
|
since?: string;
|
|
9
|
+
until?: string;
|
|
9
10
|
limit?: number;
|
|
10
11
|
page?: number;
|
|
11
12
|
}
|
|
12
|
-
export declare const searchRuns: (dir: string, { query, terms, status, errorCode, operation, profile, since, limit, page }?: RunSearch) => {
|
|
13
|
+
export declare const searchRuns: (dir: string, { query, terms, status, errorCode, operation, profile, since, until, limit, page }?: RunSearch) => {
|
|
13
14
|
items: {
|
|
14
15
|
runId: string;
|
|
15
16
|
command: string;
|
package/dist/cli/runs/search.js
CHANGED
|
@@ -32,10 +32,10 @@ const safeEvent = (event) => Object.fromEntries(FIELDS.flatMap((name) => {
|
|
|
32
32
|
}
|
|
33
33
|
return typeof value === "string" || typeof value === "number" ? [[name, value]] : [];
|
|
34
34
|
}));
|
|
35
|
-
export const searchRuns = (dir, { query = "", terms = [], status, errorCode, operation, profile, since, limit = 20, page = 1 } = {}) => {
|
|
35
|
+
export const searchRuns = (dir, { query = "", terms = [], status, errorCode, operation, profile, since, until, limit = 20, page = 1 } = {}) => {
|
|
36
36
|
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100 || !Number.isSafeInteger(page) || page < 1)
|
|
37
37
|
throw new CliError("validation_error", "run search uses a limit from 1 to 100 and a positive page");
|
|
38
|
-
const window = dateWindow(
|
|
38
|
+
const window = dateWindow({ sinceTime: since, untilTime: until });
|
|
39
39
|
const from = window.since;
|
|
40
40
|
if (from !== undefined && !Number.isFinite(from))
|
|
41
41
|
throw new CliError("validation_error", "--since-time must be a valid date or ISO timestamp");
|
|
@@ -45,7 +45,8 @@ export const searchRuns = (dir, { query = "", terms = [], status, errorCode, ope
|
|
|
45
45
|
for (const run of listRuns(dir, from === undefined ? {} : { since: from })) {
|
|
46
46
|
if ((profile !== undefined && run.profile !== profile) ||
|
|
47
47
|
(status !== undefined && run.status !== status) ||
|
|
48
|
-
(from !== undefined && Date.parse(run.startedAt) < from)
|
|
48
|
+
(from !== undefined && Date.parse(run.startedAt) < from) ||
|
|
49
|
+
(window.until !== undefined && Date.parse(run.startedAt) > window.until))
|
|
49
50
|
continue;
|
|
50
51
|
const metadataMatch = (!errorCode ||
|
|
51
52
|
run.errorCode === errorCode ||
|