@wirecat/cli-core 0.422.0 → 0.424.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 +29 -0
- package/dist/analysis/settings.d.ts +0 -6
- package/dist/analysis/settings.js +2 -5
- package/dist/cli/analysis-choice.d.ts +0 -1
- package/dist/cli/analysis-choice.js +9 -22
- package/dist/cli/bot/context.js +1 -1
- package/dist/cli/config-command.js +33 -25
- package/dist/cli/config-issues.d.ts +52 -0
- package/dist/cli/config-issues.js +187 -0
- package/dist/cli/config-keys.js +4 -28
- package/dist/cli/context.d.ts +1 -1
- package/dist/cli/context.js +3 -1
- package/dist/cli/embedding-choice.d.ts +1 -2
- package/dist/cli/embedding-choice.js +2 -4
- package/dist/cli/index.d.ts +2 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/messenger/context.js +1 -1
- package/dist/cli/messenger/conversations-command.js +5 -9
- package/dist/cli/messenger/doctor-command.js +38 -4
- package/dist/cli/messenger/inbox.js +2 -6
- package/dist/cli/messenger/private-people-command.js +1 -2
- package/dist/cli/messenger/review.js +2 -5
- package/dist/cli/permission-keys.d.ts +9 -2
- package/dist/cli/permission-keys.js +21 -6
- package/dist/cli/preview.d.ts +3 -2
- package/dist/cli/preview.js +4 -3
- package/dist/cli/program.d.ts +1 -1
- package/dist/cli/program.js +14 -9
- package/dist/cli/runs/search.d.ts +1 -1
- package/dist/cli/settings.d.ts +34 -117
- package/dist/cli/settings.js +130 -118
- package/dist/cli/shared-command-exports.d.ts +4 -232
- package/dist/entities/index.d.ts +3 -3
- package/dist/entities/recorded-run.d.ts +3 -3
- package/dist/entities/recorded-run.js +1 -1
- package/dist/mcp/tool.js +3 -1
- package/dist/meetings/mcp/index.d.ts +15 -15
- package/dist/models/settings.d.ts +1 -7
- package/dist/models/settings.js +1 -18
- package/dist/runs/recording.d.ts +2 -1
- package/dist/runs/recording.js +2 -2
- package/dist/runs/run.d.ts +3 -2
- package/dist/runs/run.js +1 -1
- package/dist/search/account-list.js +8 -7
- package/package.json +1 -1
|
@@ -6,10 +6,8 @@ import { isLocal, remoteModel } from "../embeddings/remote.js";
|
|
|
6
6
|
import { readOnlyLockOf, readOnlyRefusal } from "../ports/app.js";
|
|
7
7
|
import { typedCommand } from "../runs/scope.js";
|
|
8
8
|
/** A local model id, or a remote model with its key from `models keys set`. */
|
|
9
|
-
export const embeddingChoice = ({ model,
|
|
10
|
-
|
|
11
|
-
throw new CliError("validation_error", "give --embedding-provider or its legacy --provider spelling, not both");
|
|
12
|
-
provider = embeddingProvider ?? provider;
|
|
9
|
+
export const embeddingChoice = ({ model, embeddingProvider, baseUrl, dims, concurrency }, app, settings, env, { needKey = true, keyring } = {}) => {
|
|
10
|
+
let provider = embeddingProvider;
|
|
13
11
|
const flagsChooseProvider = (provider !== undefined && provider !== (settings.embeddingProvider ?? "local")) ||
|
|
14
12
|
(baseUrl !== undefined && baseUrl.replace(/\/+$/, "") !== settings.embeddingBaseUrl?.replace(/\/+$/, ""));
|
|
15
13
|
provider ??= flagsChooseProvider ? "openai" : (settings.embeddingProvider ?? "local");
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -52,6 +52,7 @@ export type { CommandAvailability, DiscoveredCommand } from "./command-availabil
|
|
|
52
52
|
export { commandContract, type JsonSchema, SCHEMA_DIALECT } from "./command-contract.js";
|
|
53
53
|
export { CONTRACT } from "./commands-command.js";
|
|
54
54
|
export { refuseUnknownKey } from "./config-command.js";
|
|
55
|
+
export type { ConfigIssue, ConfigIssueKind } from "./config-issues.js";
|
|
55
56
|
export { loadSnakeCaseConfig } from "./config-keys.js";
|
|
56
57
|
export { type AgentRun, type BaseContext, type BaseEnvironment, baseContext, environmentOf, outputFor, provide, secretInputOf, } from "./context.js";
|
|
57
58
|
export { addContractInfo, type ContractEvidenceOptions, contractEvidence } from "./contract-evidence.js";
|
|
@@ -102,7 +103,7 @@ export { knownBeside, knownPermissionKeys, type PermissionKeyOf, unknownPermissi
|
|
|
102
103
|
export { asFirstWord, commandWords, liftProfile, refuseCommandName, rootOf, usableProfileName } from "./profile.js";
|
|
103
104
|
export { createProgram, type ProgramDefinition, type ProgramOptions, type RunOptions, run } from "./program.js";
|
|
104
105
|
export { fieldsOf, projectFields } from "./result-fields.js";
|
|
105
|
-
export { type Config, type Configuration, count, first, flag, fromFile, type GlobalFlags, NONE_NAMED, type ProfileKind, plain, type ResolveOptions, type Settings, type SettingsExtension, type SettingsExtensionContext, type Source, settingsFor, } from "./settings.js";
|
|
106
|
+
export { type Config, type Configuration, count, first, flag, fromFile, type GlobalFlags, NONE_NAMED, namedProfile, type ProfileKind, plain, type ResolveOptions, type Settings, type SettingsExtension, type SettingsExtensionContext, type Source, settingsFor, } from "./settings.js";
|
|
106
107
|
export { type PermissionAnswer, readSetupAnswers, type SetupAnswers, setupPermissionCeiling } from "./setup-answers.js";
|
|
107
108
|
export { AGENTS, type Agent, agentFor, screenFor } from "./setup-screen.js";
|
|
108
109
|
export { accountCommand, addMeetingBatchCommand, addMeetingPreparationCommand, addMeetingProjectCandidatesCommand, addMeetingSeriesInventoryCommand, attachmentsCommand, botAdminsCommand, botCommand, botMcpCommand, botMembersCommand, botMessagesCommand, callsCommand, chatsCommand, commandsCommand, completeCommand, configCommand, contactsCommand, conversationsCommand, deleteCommand, doctorCommand, editCommand, floodCommand, forwardCommand, generatedApiCommand, guideCommand, imagesCommand, inboxCommand, markReadCommand, mcpCommand, mentionsCommand, messagesCommand, metadataCommand, modelCatalogueCommand, modelsCommand, pinCommand, pollsCommand, reactionsCommand, recipientsCommand, repliesCommand, reviewCommand, runsCommand, scheduleCommand, searchCommand, searchesCommand, sendCommand, sendsCommand, serveCommand, serverCommand, skillCommand, starCommands, statsCommand, stickersCommand, storeCommand, tagsCommand, tasksCommand, tasksStatsCommand, topicsCommand, unpinCommand, upgradeCommand, watchCommand, } from "./shared-command-exports.js";
|
package/dist/cli/index.js
CHANGED
|
@@ -93,7 +93,7 @@ export { knownBeside, knownPermissionKeys, unknownPermissionKeys } from "./permi
|
|
|
93
93
|
export { asFirstWord, commandWords, liftProfile, refuseCommandName, rootOf, usableProfileName } from "./profile.js";
|
|
94
94
|
export { createProgram, run } from "./program.js";
|
|
95
95
|
export { fieldsOf, projectFields } from "./result-fields.js";
|
|
96
|
-
export { count, first, flag, fromFile, NONE_NAMED, plain, settingsFor, } from "./settings.js";
|
|
96
|
+
export { count, first, flag, fromFile, NONE_NAMED, namedProfile, plain, settingsFor, } from "./settings.js";
|
|
97
97
|
export { readSetupAnswers, setupPermissionCeiling } from "./setup-answers.js";
|
|
98
98
|
export { AGENTS, agentFor, screenFor } from "./setup-screen.js";
|
|
99
99
|
export { accountCommand, addMeetingBatchCommand, addMeetingPreparationCommand, addMeetingProjectCandidatesCommand, addMeetingSeriesInventoryCommand, attachmentsCommand, botAdminsCommand, botCommand, botMcpCommand, botMembersCommand, botMessagesCommand, callsCommand, chatsCommand, commandsCommand, completeCommand, configCommand, contactsCommand, conversationsCommand, deleteCommand, doctorCommand, editCommand, floodCommand, forwardCommand, generatedApiCommand, guideCommand, imagesCommand, inboxCommand, markReadCommand, mcpCommand, mentionsCommand, messagesCommand, metadataCommand, modelCatalogueCommand, modelsCommand, pinCommand, pollsCommand, reactionsCommand, recipientsCommand, repliesCommand, reviewCommand, runsCommand, scheduleCommand, searchCommand, searchesCommand, sendCommand, sendsCommand, serveCommand, serverCommand, skillCommand, starCommands, statsCommand, stickersCommand, storeCommand, tagsCommand, tasksCommand, tasksStatsCommand, topicsCommand, unpinCommand, upgradeCommand, watchCommand, } from "./shared-command-exports.js";
|
|
@@ -355,7 +355,7 @@ export const messengerContext = (command, messenger, resolving = {}) => {
|
|
|
355
355
|
const { profile } = base.settings;
|
|
356
356
|
refuseDenied(command, base.settings);
|
|
357
357
|
refuseLacking(messenger, pathOf(command));
|
|
358
|
-
refuseUnknownPermissionKeys(command, base.settings
|
|
358
|
+
refuseUnknownPermissionKeys(command, base.settings, app.command, messenger.permissionKey);
|
|
359
359
|
// Read when warning, not now: serve swaps the renderer's methods for log records after this.
|
|
360
360
|
const warn = (text, warning) => base.renderer.warn(text, warning);
|
|
361
361
|
const accountFor = () => {
|
|
@@ -37,8 +37,8 @@ const conversationBuildCommand = (messenger) => boundOptions(reachesOtherService
|
|
|
37
37
|
.option("--chat <chat>", messenger.chatArgument)
|
|
38
38
|
.option("--analyze", "link batches using the configured analysis provider; requires --chat and remembers consent for this chat/provider")
|
|
39
39
|
.addOption(new Option("--analysis-provider <provider>", "analysis: agent, openai or anthropic"))
|
|
40
|
-
.option("--model <model>", "analysis model; overrides
|
|
41
|
-
.option("--base-url <url>", "analysis API endpoint; overrides
|
|
40
|
+
.option("--model <model>", "analysis model; overrides models.analysis.model")
|
|
41
|
+
.option("--base-url <url>", "analysis API endpoint; overrides models.analysis.base_url")
|
|
42
42
|
.option("--size <n>", "analysis answer messages per batch, 10–200; default 50", positiveCount("--size"))
|
|
43
43
|
.option("--max-tokens <n>", "analysis input/output reservation cap per run; default 100000", positiveCount("--max-tokens")), { chunks: false }).action(async function () {
|
|
44
44
|
const options = this.optsWithGlobals();
|
|
@@ -93,7 +93,6 @@ const conversationBuildCommand = (messenger) => boundOptions(reachesOtherService
|
|
|
93
93
|
return;
|
|
94
94
|
}
|
|
95
95
|
if (options.analysisProvider !== undefined ||
|
|
96
|
-
options.provider !== undefined ||
|
|
97
96
|
options.model !== undefined ||
|
|
98
97
|
options.baseUrl !== undefined ||
|
|
99
98
|
options.size !== undefined ||
|
|
@@ -133,10 +132,7 @@ const embeddingClearCommand = (messenger) => withModelOptions(destroysNothing(an
|
|
|
133
132
|
const chat = chatOf(options);
|
|
134
133
|
const context = messengerContext(this, messenger);
|
|
135
134
|
refuseLocalWrite(context, messenger.app.command, EMBED_KEY);
|
|
136
|
-
const given = options.model !== undefined ||
|
|
137
|
-
options.embeddingProvider !== undefined ||
|
|
138
|
-
options.provider !== undefined ||
|
|
139
|
-
options.baseUrl !== undefined;
|
|
135
|
+
const given = options.model !== undefined || options.embeddingProvider !== undefined || options.baseUrl !== undefined;
|
|
140
136
|
const model = given ? choiceOf(options, messenger, context, { needKey: false }) : undefined;
|
|
141
137
|
const cleared = await context.withServices((services) => services.embeddings.clear(chat, model));
|
|
142
138
|
renderAnswer(this, context, itemOf(cleared), () => `${cleared.cleared} vectors dropped\n`);
|
|
@@ -379,14 +375,14 @@ export const conversationsCommand = (messenger) => {
|
|
|
379
375
|
.option("--chat <chat>", "revoke only this chat's consents; defaults to every chat")
|
|
380
376
|
.addOption(new Option("--consent-provider <identity>", "exact provider identity from consents list; defaults to every provider"))
|
|
381
377
|
.action(async function () {
|
|
382
|
-
const { chat,
|
|
378
|
+
const { chat, consentProvider } = this.opts();
|
|
383
379
|
const context = messengerContext(this, messenger);
|
|
384
380
|
refuseLocalWrite(context, messenger.app.command, LINKS_KEY);
|
|
385
381
|
await context.withStore(async (store, account) => {
|
|
386
382
|
const chatId = chat === undefined
|
|
387
383
|
? undefined
|
|
388
384
|
: (await servicesFor(storedDeps(messenger, store, account, context.guard)).conversations.batchStatus(chat, BATCH_SIZE.default)).chat;
|
|
389
|
-
await analysisConsents(store, account).revoke(chatId, consentProvider
|
|
385
|
+
await analysisConsents(store, account).revoke(chatId, consentProvider);
|
|
390
386
|
});
|
|
391
387
|
renderAnswer(this, context, itemOf({ revoked: true }));
|
|
392
388
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
1
2
|
import { dirname, resolve } from "node:path";
|
|
2
|
-
import { writeSecurely } from "@wirecat/cli-base";
|
|
3
|
+
import { configFilePath, resolvePaths, writeSecurely } from "@wirecat/cli-base";
|
|
3
4
|
import { annotate } from "@wirecat/cli-base/commands";
|
|
4
5
|
import { Command } from "commander";
|
|
5
6
|
import { engineStates } from "../../attachments/extract.js";
|
|
@@ -11,6 +12,8 @@ import { storePath } from "../../store/path.js";
|
|
|
11
12
|
import { baseContext, environmentOf, outputFor } from "../context.js";
|
|
12
13
|
import { destroysNothing, LOCAL_WRITE, NO_EFFECTS, REMOTE_READ } from "../effects.js";
|
|
13
14
|
import { itemOf, renderAnswer, snakeKeys } from "../envelope.js";
|
|
15
|
+
import { unknownPermissionIssues } from "../permission-keys.js";
|
|
16
|
+
import { NONE_NAMED } from "../settings.js";
|
|
14
17
|
import { lineOf, nextStep } from "../steps.js";
|
|
15
18
|
import { recalledAccount } from "./accounts.js";
|
|
16
19
|
import { messengerContext } from "./context.js";
|
|
@@ -126,14 +129,45 @@ const diagnose = async (command, messenger) => {
|
|
|
126
129
|
platform: process.platform,
|
|
127
130
|
arch: process.arch,
|
|
128
131
|
};
|
|
132
|
+
// Every problem in the file is reported, not refused: doctor is what a person runs when nothing else will.
|
|
129
133
|
let context;
|
|
130
134
|
try {
|
|
131
|
-
context = baseContext(command, messenger.resolveSettings);
|
|
135
|
+
context = baseContext(command, (flags, options) => messenger.resolveSettings(flags, { ...options, needsProfile: false, readOnly: true, withIssues: true }));
|
|
132
136
|
}
|
|
133
137
|
catch (error) {
|
|
134
|
-
|
|
138
|
+
// Not the file: a profile lock, a flag or an environment variable the settings refuse.
|
|
139
|
+
const path = configFilePath(resolvePaths({ appName: app.appName, prefix: app.envPrefix, env }).config);
|
|
140
|
+
return {
|
|
141
|
+
report: {
|
|
142
|
+
cli,
|
|
143
|
+
config: { path, found: existsSync(path), issues: [] },
|
|
144
|
+
settings: { error: messageOf(error) },
|
|
145
|
+
store: snakeKeys(await storeSummary(env)),
|
|
146
|
+
runs: { directory: runsDirFor(app, env), kept: listRuns(runsDirFor(app, env)).length },
|
|
147
|
+
},
|
|
148
|
+
};
|
|
135
149
|
}
|
|
136
150
|
const { settings, renderer } = context;
|
|
151
|
+
const config = {
|
|
152
|
+
path: settings.configPath,
|
|
153
|
+
found: settings.configFound,
|
|
154
|
+
issues: [
|
|
155
|
+
...(settings.configIssues ?? []),
|
|
156
|
+
...unknownPermissionIssues(command, settings, app.command, messenger.permissionKey),
|
|
157
|
+
],
|
|
158
|
+
};
|
|
159
|
+
if (settings.sources.profile === NONE_NAMED)
|
|
160
|
+
return {
|
|
161
|
+
renderer,
|
|
162
|
+
report: {
|
|
163
|
+
cli,
|
|
164
|
+
profile: null,
|
|
165
|
+
config,
|
|
166
|
+
store: snakeKeys(await storeSummary(env)),
|
|
167
|
+
readers: engineStates(),
|
|
168
|
+
runs: { directory: runsDirFor(app, env), kept: listRuns(runsDirFor(app, env)).length },
|
|
169
|
+
},
|
|
170
|
+
};
|
|
137
171
|
const { profile } = settings;
|
|
138
172
|
const account = recalledAccount(app, provider, profile, env);
|
|
139
173
|
const store = storePath(env);
|
|
@@ -151,7 +185,7 @@ const diagnose = async (command, messenger) => {
|
|
|
151
185
|
report: {
|
|
152
186
|
cli,
|
|
153
187
|
profile,
|
|
154
|
-
config
|
|
188
|
+
config,
|
|
155
189
|
account: { remembered: account?.account ?? null },
|
|
156
190
|
// A session file on disk says nothing about whether the messenger still accepts it.
|
|
157
191
|
login: { state: "not checked" },
|
|
@@ -2,7 +2,6 @@ import { CliError } from "@wirecat/cli-base";
|
|
|
2
2
|
import { annotate } from "@wirecat/cli-base/commands";
|
|
3
3
|
import { Command } from "commander";
|
|
4
4
|
import { renderMessages } from "../../render/messages.js";
|
|
5
|
-
import { kindsOf } from "../../services/inbox.js";
|
|
6
5
|
import { modelWith } from "../../speech/hearing.js";
|
|
7
6
|
import { REMOTE_READ } from "../effects.js";
|
|
8
7
|
import { renderAnswer } from "../envelope.js";
|
|
@@ -44,15 +43,12 @@ export const inboxCommand = (messenger) => annotate(new Command("inbox"), {
|
|
|
44
43
|
.option(...MODEL_OPTION)
|
|
45
44
|
.addHelpText("after", "To mark a chat read, use `chats mark-read <chat>`. The other side sees it.")
|
|
46
45
|
.action(async function (query) {
|
|
47
|
-
const { new: fresh, all,
|
|
46
|
+
const { new: fresh, all, transcribe, model, } = this.opts();
|
|
48
47
|
const hearWith = modelWith(transcribe, model);
|
|
49
48
|
const context = messengerContext(this, messenger);
|
|
50
49
|
const { app } = messenger;
|
|
51
50
|
const { settings, renderer, format, env } = context;
|
|
52
|
-
const { since, meta, kinds
|
|
53
|
-
if (kind !== undefined && queriedKinds !== undefined)
|
|
54
|
-
throw new CliError("validation_error", "give kind: or its legacy --kind spelling, not both");
|
|
55
|
-
const kinds = queriedKinds ?? (kind === undefined ? undefined : kindsOf(kind));
|
|
51
|
+
const { since, meta, kinds } = catchUpQuery(query, INBOX_QUERY, "inbox");
|
|
56
52
|
if (fresh && since !== undefined)
|
|
57
53
|
throw new CliError("validation_error", "--new keeps its own point — not with a date in the query");
|
|
58
54
|
const saved = fresh && since === undefined
|
|
@@ -65,8 +65,7 @@ export const privatePeopleCommands = (messenger) => {
|
|
|
65
65
|
command.action(async function (person, id) {
|
|
66
66
|
const context = messengerContext(this, messenger);
|
|
67
67
|
refuseLocalWrite(context, messenger.app.command, `contacts.notes.${name}`);
|
|
68
|
-
const { textFile
|
|
69
|
-
const file = textFile ?? legacyFile;
|
|
68
|
+
const { textFile: file, revision } = this.opts();
|
|
70
69
|
const text = file === undefined || file === "-" ? await readAll(context.stdin) : await readFile(file, "utf8");
|
|
71
70
|
const version = Number(revision);
|
|
72
71
|
if (name === "edit" && (!Number.isSafeInteger(version) || version < 1))
|
|
@@ -3,7 +3,7 @@ import { annotate } from "@wirecat/cli-base/commands";
|
|
|
3
3
|
import { Command } from "commander";
|
|
4
4
|
import { parseDuration } from "../../domain/duration.js";
|
|
5
5
|
import { renderMessages } from "../../render/messages.js";
|
|
6
|
-
import {
|
|
6
|
+
import { REVIEW_DAYS, reviewStart, UNANSWERED_HOURS } from "../../services/inbox.js";
|
|
7
7
|
import { modelWith } from "../../speech/hearing.js";
|
|
8
8
|
import { REMOTE_READ } from "../effects.js";
|
|
9
9
|
import { renderAnswer } from "../envelope.js";
|
|
@@ -45,9 +45,7 @@ export const reviewCommand = (messenger) => annotate(new Command("review"), {
|
|
|
45
45
|
const hearWith = modelWith(options.transcribe, options.model);
|
|
46
46
|
const context = messengerContext(this, messenger);
|
|
47
47
|
const { settings, renderer, format, env } = context;
|
|
48
|
-
const { since: from, chat, meta, kinds
|
|
49
|
-
if (options.kind !== undefined && queriedKinds !== undefined)
|
|
50
|
-
throw new CliError("validation_error", "give kind: or its legacy --kind spelling, not both");
|
|
48
|
+
const { since: from, chat, meta, kinds } = catchUpQuery(query, REVIEW_QUERY, "review");
|
|
51
49
|
if (options.new && (from !== undefined || options.unanswered !== undefined)) {
|
|
52
50
|
// An open question stays open: moving past it would drop it from the next review.
|
|
53
51
|
throw new CliError("validation_error", "--new keeps its own point — not with a date or --unanswered");
|
|
@@ -62,7 +60,6 @@ export const reviewCommand = (messenger) => annotate(new Command("review"), {
|
|
|
62
60
|
: undefined;
|
|
63
61
|
const since = from ?? saved?.first ?? reviewStart();
|
|
64
62
|
const hours = options.unanswered === undefined ? undefined : unansweredHours(options.unanswered);
|
|
65
|
-
const kinds = queriedKinds ?? (options.kind === undefined ? undefined : kindsOf(options.kind));
|
|
66
63
|
let hearing;
|
|
67
64
|
const transcribe = options.transcribe === true;
|
|
68
65
|
const { found, reader } = await context.withServices(async (services, connect) => {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Command } from "commander";
|
|
2
2
|
import { type PermissionKey } from "../sends/permissions.js";
|
|
3
|
+
import { type ConfigIssue } from "./config-issues.js";
|
|
4
|
+
import type { Settings } from "./settings.js";
|
|
3
5
|
export type PermissionKeyOf = (command: Command) => PermissionKey | null | undefined;
|
|
4
6
|
/**
|
|
5
7
|
* The keys `permissions` may name: every command of the program, every write key, and each of their
|
|
@@ -9,5 +11,10 @@ export declare const knownPermissionKeys: (command: Command, keyOf?: PermissionK
|
|
|
9
11
|
/** The known keys one level under the unknown key's parent, to name in the refusal. */
|
|
10
12
|
export declare const knownBeside: (key: PermissionKey, known: ReadonlySet<PermissionKey>) => PermissionKey[];
|
|
11
13
|
export declare const unknownPermissionKeys: (levels: Readonly<Record<PermissionKey, unknown>>, known: ReadonlySet<PermissionKey>) => PermissionKey[];
|
|
12
|
-
/**
|
|
13
|
-
export declare const
|
|
14
|
+
/** Every unknown permission key saved in the file, in every section and profile, with the unset that removes it. */
|
|
15
|
+
export declare const unknownPermissionIssues: (command: Command, settings: Pick<Settings, "configPath" | "savedPermissions" | "permissions" | "permissionSources" | "profile">, tool: string, keyOf?: PermissionKeyOf) => ConfigIssue[];
|
|
16
|
+
/**
|
|
17
|
+
* A saved key no command checks would do nothing, so the command refuses to run and names the unset that removes
|
|
18
|
+
* it. A `--permission` key is checked too: it is no file's, so the flag is the fix.
|
|
19
|
+
*/
|
|
20
|
+
export declare const refuseUnknownPermissionKeys: (command: Command, settings: Pick<Settings, "configPath" | "savedPermissions" | "permissions" | "permissionSources" | "profile">, tool: string, keyOf?: PermissionKeyOf) => void;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CliError } from "@wirecat/cli-base";
|
|
2
2
|
import { CEILING_KEY, keyForCommand, WRITE_KEYS } from "../sends/permissions.js";
|
|
3
|
+
import { issueMessage, permissionIssues, publicIssue } from "./config-issues.js";
|
|
3
4
|
const pathOf = (command) => {
|
|
4
5
|
const words = [];
|
|
5
6
|
for (let at = command; at?.parent; at = at.parent)
|
|
@@ -42,10 +43,24 @@ export const knownBeside = (key, known) => {
|
|
|
42
43
|
.sort();
|
|
43
44
|
};
|
|
44
45
|
export const unknownPermissionKeys = (levels, known) => Object.keys(levels).filter((key) => key !== CEILING_KEY && !known.has(key));
|
|
45
|
-
/**
|
|
46
|
-
export const
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
/** Every unknown permission key saved in the file, in every section and profile, with the unset that removes it. */
|
|
47
|
+
export const unknownPermissionIssues = (command, settings, tool, keyOf) => {
|
|
48
|
+
const saved = settings.savedPermissions ??
|
|
49
|
+
Object.keys(settings.permissions)
|
|
50
|
+
.filter((key) => key !== CEILING_KEY && settings.permissionSources[key] !== "flag")
|
|
51
|
+
.map((key) => ({ key, at: ["profiles", settings.profile, "permissions", key] }));
|
|
52
|
+
return permissionIssues(saved, knownPermissionKeys(command, keyOf), { file: settings.configPath, tool }).map(publicIssue);
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* A saved key no command checks would do nothing, so the command refuses to run and names the unset that removes
|
|
56
|
+
* it. A `--permission` key is checked too: it is no file's, so the flag is the fix.
|
|
57
|
+
*/
|
|
58
|
+
export const refuseUnknownPermissionKeys = (command, settings, tool, keyOf) => {
|
|
59
|
+
const issues = unknownPermissionIssues(command, settings, tool, keyOf);
|
|
60
|
+
if (issues.length > 0)
|
|
61
|
+
throw new CliError("configuration_error", issueMessage(issues), { permission: issues[0]?.key });
|
|
62
|
+
const flagged = Object.keys(settings.permissions).filter((key) => settings.permissionSources[key] === "flag");
|
|
63
|
+
const [key] = unknownPermissionKeys(Object.fromEntries(flagged.map((one) => [one, true])), knownPermissionKeys(command, keyOf));
|
|
64
|
+
if (key !== undefined)
|
|
65
|
+
throw new CliError("validation_error", `--permission ${key} names no command`, { permission: key });
|
|
51
66
|
};
|
package/dist/cli/preview.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { Command } from "commander";
|
|
2
|
-
import { type
|
|
2
|
+
import { type PermissionKey } from "../sends/permissions.js";
|
|
3
|
+
import type { Settings } from "./settings.js";
|
|
3
4
|
export declare class PreviewComplete extends Error {
|
|
4
5
|
}
|
|
5
|
-
export declare const preview: (command: Command,
|
|
6
|
+
export declare const preview: (command: Command, settings: Pick<Settings, "permissions" | "permissionSources" | "configPath" | "savedPermissions" | "profile">, tool: string, keyOf?: (path: readonly string[]) => PermissionKey | null | undefined) => {
|
|
6
7
|
preview: boolean;
|
|
7
8
|
path: string[];
|
|
8
9
|
permission: {
|
package/dist/cli/preview.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { CliError } from "@wirecat/cli-base";
|
|
2
2
|
import { metaOf } from "@wirecat/cli-base/commands";
|
|
3
3
|
import { writes as writesAnything } from "@wirecat/cli-base/standards";
|
|
4
|
-
import { keyForCommand, levelFor, readKeysForCommand, WRITE_KEYS
|
|
4
|
+
import { keyForCommand, levelFor, readKeysForCommand, WRITE_KEYS } from "../sends/permissions.js";
|
|
5
5
|
import { commandPathOf } from "./command-contract.js";
|
|
6
6
|
import { refuseUnknownPermissionKeys } from "./permission-keys.js";
|
|
7
7
|
export class PreviewComplete extends Error {
|
|
8
8
|
}
|
|
9
|
-
export const preview = (command,
|
|
9
|
+
export const preview = (command, settings, tool, keyOf) => {
|
|
10
10
|
const path = commandPathOf(command);
|
|
11
|
-
|
|
11
|
+
const { permissions } = settings;
|
|
12
|
+
refuseUnknownPermissionKeys(command, settings, tool, keyOf && ((one) => keyOf(commandPathOf(one))));
|
|
12
13
|
const key = keyForCommand(path);
|
|
13
14
|
const level = key ? levelFor(permissions, key).level : "allow";
|
|
14
15
|
for (const source of readKeysForCommand(path)) {
|
package/dist/cli/program.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export interface ProgramDefinition {
|
|
|
10
10
|
/** The CLI's own settings, for keeping a failure that happened before its command could; plain ones without. */
|
|
11
11
|
configuration?: {
|
|
12
12
|
permissionKey?: (path: readonly string[]) => string | null | undefined;
|
|
13
|
-
resolveSettings: (flags?: GlobalFlags, options?: ResolveOptions) => Pick<Settings, "profile" | "keepFailedRuns" | "keepRunsForDays" | "skillHint"> & Partial<Pick<Settings, "permissions">>;
|
|
13
|
+
resolveSettings: (flags?: GlobalFlags, options?: ResolveOptions) => Pick<Settings, "profile" | "keepFailedRuns" | "keepRunsForDays" | "skillHint"> & Partial<Pick<Settings, "permissions" | "permissionSources" | "configPath" | "savedPermissions" | "sources">>;
|
|
14
14
|
};
|
|
15
15
|
configure?: (program: Command) => void;
|
|
16
16
|
/** The tool's pitch and "what you can do" lines on `--help` and a bare run. */
|
package/dist/cli/program.js
CHANGED
|
@@ -26,9 +26,9 @@ import { showInputFields } from "./input-fields.js";
|
|
|
26
26
|
import { MAX_BUFFERED_INPUT, provideInputPolicy } from "./input-policy.js";
|
|
27
27
|
import { checkJson, liftJson } from "./json-fields.js";
|
|
28
28
|
import { PreviewComplete, preview } from "./preview.js";
|
|
29
|
-
import {
|
|
29
|
+
import { commandWords, liftProfile, PROFILE_FREE, sampleCommand, valuedOptions } from "./profile.js";
|
|
30
30
|
import { absolutePaths, relayTarget } from "./relay-route.js";
|
|
31
|
-
import { settingsFor } from "./settings.js";
|
|
31
|
+
import { NoProfileHere, namedProfile, settingsFor, } from "./settings.js";
|
|
32
32
|
import { assertSharedContracts } from "./shared-contracts.js";
|
|
33
33
|
import { commandAt, lineOf, withLines } from "./steps.js";
|
|
34
34
|
/**
|
|
@@ -379,9 +379,13 @@ const runOnce = async (argv, definition, options) => {
|
|
|
379
379
|
// No permission governs housekeeping like `skill install`, so no profile's settings change its preview.
|
|
380
380
|
needsProfile: keyForCommand(path) !== null,
|
|
381
381
|
});
|
|
382
|
-
const
|
|
383
|
-
|
|
384
|
-
|
|
382
|
+
const previewed = preview(action, {
|
|
383
|
+
permissions: resolved.permissions ?? {},
|
|
384
|
+
permissionSources: resolved.permissionSources ?? {},
|
|
385
|
+
configPath: resolved.configPath ?? "",
|
|
386
|
+
profile: resolved.profile,
|
|
387
|
+
...(resolved.savedPermissions ? { savedPermissions: resolved.savedPermissions } : {}),
|
|
388
|
+
}, command, definition.configuration?.permissionKey);
|
|
385
389
|
renderAnswer(action, outputFor(action), itemOf(snakeKeys(previewed)));
|
|
386
390
|
throw new PreviewComplete();
|
|
387
391
|
}
|
|
@@ -576,7 +580,7 @@ const keepFailure = async (failure, { definition, program, rest, profile, option
|
|
|
576
580
|
await recorded({
|
|
577
581
|
app: definition.app,
|
|
578
582
|
command: `${options.agent ? "mcp " : ""}${path || definition.app.command}`,
|
|
579
|
-
profile: settings
|
|
583
|
+
profile: settings ? namedProfile(settings) : profile,
|
|
580
584
|
record: false,
|
|
581
585
|
keepFailed: settings?.keepFailedRuns ?? !rest.includes("--no-record"),
|
|
582
586
|
trace: false,
|
|
@@ -605,10 +609,11 @@ const commandPath = (program, rest) => {
|
|
|
605
609
|
return words.join(" ");
|
|
606
610
|
};
|
|
607
611
|
const failed = ({ app }, program, rest, error, settled, debug) => {
|
|
608
|
-
const failure = settled.settlementFailed && isCliFailure(error)
|
|
609
|
-
? new CliError(error.code, error.message, { ...error.details, settlement_failed: true })
|
|
610
|
-
: error;
|
|
611
612
|
const path = commandPath(program, program.args.length > 0 ? program.args : rest);
|
|
613
|
+
const told = error instanceof NoProfileHere && !commandAt(program, "setup") ? error.withoutSetup(path) : error;
|
|
614
|
+
const failure = settled.settlementFailed && isCliFailure(told)
|
|
615
|
+
? new CliError(told.code, told.message, { ...told.details, settlement_failed: true })
|
|
616
|
+
: told;
|
|
612
617
|
const effects = judgedEffects(path ? metaOf(commandAt(program, path) ?? program).effects : undefined);
|
|
613
618
|
const object = errorObjectOf(failure, {
|
|
614
619
|
...failureContext(app.errors),
|
|
@@ -14,7 +14,7 @@ export declare const searchRuns: (dir: string, { query, terms, status, errorCode
|
|
|
14
14
|
items: {
|
|
15
15
|
runId: string;
|
|
16
16
|
command: string;
|
|
17
|
-
profile: string;
|
|
17
|
+
profile: string | undefined;
|
|
18
18
|
status: "running" | import("../../runs/run.js").RunStatus;
|
|
19
19
|
startedAt: string;
|
|
20
20
|
completedAt: string | undefined;
|