@wirecat/cli-core 0.423.0 → 0.425.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 +51 -0
- package/dist/cli/author.d.ts +5 -6
- package/dist/cli/author.js +9 -16
- package/dist/cli/config-issues.d.ts +20 -2
- package/dist/cli/config-issues.js +111 -17
- package/dist/cli/context.d.ts +3 -1
- package/dist/cli/context.js +20 -7
- package/dist/cli/dry-run-guard.d.ts +2 -0
- package/dist/cli/dry-run-guard.js +68 -0
- package/dist/cli/entry.js +14 -9
- package/dist/cli/index.d.ts +4 -4
- package/dist/cli/index.js +4 -3
- package/dist/cli/list-query.d.ts +2 -0
- package/dist/cli/list-query.js +5 -2
- package/dist/cli/messenger/store-maintenance-command.js +15 -5
- package/dist/cli/preview.d.ts +44 -5
- package/dist/cli/preview.js +26 -8
- package/dist/cli/program.d.ts +1 -1
- package/dist/cli/program.js +36 -23
- package/dist/cli/runs/search.d.ts +1 -1
- package/dist/cli/runs/search.js +2 -0
- package/dist/cli/settings.d.ts +13 -0
- package/dist/cli/settings.js +36 -16
- package/dist/cli/shared-command-exports.js +3 -1
- package/dist/entities/index.d.ts +151 -3
- package/dist/entities/recorded-run.d.ts +87 -3
- package/dist/entities/recorded-run.js +16 -1
- package/dist/entities/store-maintenance.d.ts +64 -0
- package/dist/entities/store-maintenance.js +24 -1
- package/dist/mcp/agent.js +3 -4
- package/dist/mcp/bot/server.js +1 -1
- package/dist/mcp/meeting-preparation.js +2 -2
- package/dist/mcp/meeting-project-candidates.js +2 -2
- package/dist/mcp/meeting-series-inventory.js +2 -2
- package/dist/mcp/server.js +1 -1
- package/dist/mcp/surface.d.ts +3 -1
- package/dist/mcp/surface.js +20 -9
- package/dist/mcp/text.d.ts +1 -1
- package/dist/mcp/text.js +1 -1
- package/dist/mcp/tool.d.ts +1 -1
- package/dist/mcp/tool.js +6 -2
- package/dist/meetings/mcp/agent-text.d.ts +2 -0
- package/dist/meetings/mcp/agent-text.js +2 -0
- package/dist/meetings/mcp/index.d.ts +1 -1
- package/dist/meetings/mcp/index.js +3 -3
- package/dist/meetings/mcp/passage.js +2 -2
- package/dist/meetings/mcp/project-evidence.js +2 -2
- package/dist/meetings/mcp/search.js +2 -2
- package/dist/runs/error-detail.d.ts +42 -0
- package/dist/runs/error-detail.js +131 -0
- package/dist/runs/events.d.ts +4 -0
- package/dist/runs/events.js +2 -0
- package/dist/runs/log-attributes.js +2 -0
- package/dist/runs/recording.d.ts +7 -3
- package/dist/runs/recording.js +22 -21
- package/dist/runs/run-context.d.ts +37 -0
- package/dist/runs/run-context.js +148 -0
- package/dist/runs/run.d.ts +22 -5
- package/dist/runs/run.js +2 -1
- package/dist/store/exports.d.ts +2 -0
- package/dist/store/exports.js +2 -0
- package/dist/store/sqlite/store-contents.d.ts +47 -0
- package/dist/store/sqlite/store-contents.js +104 -0
- package/package.json +1 -1
|
@@ -11,7 +11,7 @@ import { MIGRATIONS, migrate } from "../../store/migrations.js";
|
|
|
11
11
|
import { openCache } from "../../store/open.js";
|
|
12
12
|
import { storePath } from "../../store/path.js";
|
|
13
13
|
import { deleteCopy, repairStore } from "../../store/repair.js";
|
|
14
|
-
import { drainNoteIndex, fillSearchIndex, fillStems, involvementStoreOver, noteIndexState, openStore, orphanPointers, pendingNormalization, resetAttachmentWords, resetNoteIndex, resetSearchIndex, resetStems, searchIndexState, stemmerCache, stemsState, } from "../../store/store.js";
|
|
14
|
+
import { drainNoteIndex, fillSearchIndex, fillStems, involvementStoreOver, noteIndexState, openSqlite, openStore, orphanPointers, pendingNormalization, resetAttachmentWords, resetNoteIndex, resetSearchIndex, resetStems, rowCounts, searchIndexState, stemmerCache, stemsState, storeContents, } from "../../store/store.js";
|
|
15
15
|
import { environmentOf, outputFor } from "../context.js";
|
|
16
16
|
import { destroysNothing, LOCAL_READ, LOCAL_WRITE } from "../effects.js";
|
|
17
17
|
import { itemOf, renderAnswer, snakeKeys } from "../envelope.js";
|
|
@@ -49,6 +49,16 @@ const reading = async (path, read) => {
|
|
|
49
49
|
database.close();
|
|
50
50
|
}
|
|
51
51
|
};
|
|
52
|
+
/** For a command that only reads: the connection refuses writes, and opening sets no journal mode. */
|
|
53
|
+
const readingOnly = async (path, read) => {
|
|
54
|
+
const { database } = await openSqlite(path, { readOnly: true });
|
|
55
|
+
try {
|
|
56
|
+
return read(database);
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
database.close();
|
|
60
|
+
}
|
|
61
|
+
};
|
|
52
62
|
/** `doctor`'s short summary of the store. Must answer for a broken file, so an error is a field. */
|
|
53
63
|
export const storeSummary = async (env) => {
|
|
54
64
|
const path = storePath(env);
|
|
@@ -154,25 +164,25 @@ const copiesCommand = () => new Command("copies").description("the tables `store
|
|
|
154
164
|
}));
|
|
155
165
|
const infoCommand = () => annotate(new Command("info"), { output: { entity: "store_info" }, effects: LOCAL_READ })
|
|
156
166
|
.summary("the local archive's format, size and contents")
|
|
157
|
-
.description("the store file: where it is, its size, its schema and how many rows it holds; changes nothing")
|
|
167
|
+
.description("the store file: where it is, its size, its schema and how many rows it holds, by provider, chat kind, account, year (UTC) and attachment kind, over every account; changes nothing")
|
|
158
168
|
.action(async function () {
|
|
159
169
|
const path = storePath(environmentOf(this).env ?? process.env);
|
|
160
170
|
if (!existsSync(path)) {
|
|
161
171
|
result(this, { path, exists: false });
|
|
162
172
|
return;
|
|
163
173
|
}
|
|
164
|
-
const read =
|
|
174
|
+
const read = readingOnly(path, (database) => {
|
|
165
175
|
const { version, minCompatible, writable } = schemaOf(database);
|
|
166
|
-
const tables = ["accounts", "chats", "messages", "attachments", "identities"];
|
|
167
176
|
return {
|
|
168
177
|
path,
|
|
169
178
|
exists: true,
|
|
170
179
|
bytes: { file: bytesOf(path), wal: bytesOf(`${path}-wal`) },
|
|
171
180
|
schema: { version, minCompatible, speaks: SPEAKS, writable },
|
|
172
|
-
rows: version > 0 ?
|
|
181
|
+
rows: version > 0 ? rowCounts(database) : {},
|
|
173
182
|
pendingNormalization: pendingIfKnown(database),
|
|
174
183
|
wordIndex: searchIndexState(database) ?? null,
|
|
175
184
|
stemIndex: stemsState(database) ?? null,
|
|
185
|
+
...(version > 0 ? storeContents(database) : {}),
|
|
176
186
|
};
|
|
177
187
|
});
|
|
178
188
|
result(this, await read.catch((error) => ({ path, exists: true, opens: false, error: messageOf(error) })));
|
package/dist/cli/preview.d.ts
CHANGED
|
@@ -3,13 +3,51 @@ import { type PermissionKey } from "../sends/permissions.js";
|
|
|
3
3
|
import type { Settings } from "./settings.js";
|
|
4
4
|
export declare class PreviewComplete extends Error {
|
|
5
5
|
}
|
|
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
|
+
export declare const preview: (command: Command, settings: Pick<Settings, "permissions" | "permissionSources" | "configPath" | "savedPermissions" | "profile">, tool: string, keyOf?: (path: readonly string[]) => PermissionKey | null | undefined, messenger?: boolean) => {
|
|
7
7
|
preview: boolean;
|
|
8
8
|
path: string[];
|
|
9
|
-
permission
|
|
10
|
-
key:
|
|
11
|
-
level:
|
|
9
|
+
permission?: {
|
|
10
|
+
key: PermissionKey | null;
|
|
11
|
+
level: string;
|
|
12
|
+
} | undefined;
|
|
13
|
+
validation: {
|
|
14
|
+
arguments: string;
|
|
15
|
+
permissions: string;
|
|
16
|
+
targets: string;
|
|
17
|
+
businessRules: string;
|
|
18
|
+
};
|
|
19
|
+
effects: {
|
|
20
|
+
actionInvoked: boolean;
|
|
21
|
+
writeReserved: boolean;
|
|
22
|
+
messengerContacted?: boolean | undefined;
|
|
23
|
+
serviceContacted: boolean;
|
|
24
|
+
configurationRead: boolean;
|
|
12
25
|
};
|
|
26
|
+
arguments: ({
|
|
27
|
+
name: string;
|
|
28
|
+
reference: string;
|
|
29
|
+
} | {
|
|
30
|
+
name: string;
|
|
31
|
+
supplied: boolean;
|
|
32
|
+
bytes: number | undefined;
|
|
33
|
+
})[];
|
|
34
|
+
options: string[];
|
|
35
|
+
trust: string;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* `messenger_contacted` stays for the messengers, whose callers may read it; `service_contacted` says the same
|
|
39
|
+
* for every tool. Without `permission` nothing was checked: a host that is not `run` has no settings to check.
|
|
40
|
+
*/
|
|
41
|
+
export declare const previewBody: (command: Command, permission: {
|
|
42
|
+
key: PermissionKey | null;
|
|
43
|
+
level: string;
|
|
44
|
+
} | undefined, messenger: boolean) => {
|
|
45
|
+
preview: boolean;
|
|
46
|
+
path: string[];
|
|
47
|
+
permission?: {
|
|
48
|
+
key: PermissionKey | null;
|
|
49
|
+
level: string;
|
|
50
|
+
} | undefined;
|
|
13
51
|
validation: {
|
|
14
52
|
arguments: string;
|
|
15
53
|
permissions: string;
|
|
@@ -19,7 +57,8 @@ export declare const preview: (command: Command, settings: Pick<Settings, "permi
|
|
|
19
57
|
effects: {
|
|
20
58
|
actionInvoked: boolean;
|
|
21
59
|
writeReserved: boolean;
|
|
22
|
-
messengerContacted
|
|
60
|
+
messengerContacted?: boolean | undefined;
|
|
61
|
+
serviceContacted: boolean;
|
|
23
62
|
configurationRead: boolean;
|
|
24
63
|
};
|
|
25
64
|
arguments: ({
|
package/dist/cli/preview.js
CHANGED
|
@@ -6,11 +6,11 @@ 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, settings, tool, keyOf) => {
|
|
9
|
+
export const preview = (command, settings, tool, keyOf, messenger = true) => {
|
|
10
10
|
const path = commandPathOf(command);
|
|
11
11
|
const { permissions } = settings;
|
|
12
12
|
refuseUnknownPermissionKeys(command, settings, tool, keyOf && ((one) => keyOf(commandPathOf(one))));
|
|
13
|
-
const key = keyForCommand(path);
|
|
13
|
+
const key = keyOf?.(path) ?? keyForCommand(path);
|
|
14
14
|
const level = key ? levelFor(permissions, key).level : "allow";
|
|
15
15
|
for (const source of readKeysForCommand(path)) {
|
|
16
16
|
if (levelFor(permissions, source).level === "deny")
|
|
@@ -21,18 +21,36 @@ export const preview = (command, settings, tool, keyOf) => {
|
|
|
21
21
|
(key !== null && key !== undefined && WRITE_KEYS.includes(key));
|
|
22
22
|
if (level === "deny" || (writes && level === "readonly"))
|
|
23
23
|
throw new CliError("permission_error", `this profile does not permit ${path.join(" ")}`, { permission: key });
|
|
24
|
-
|
|
24
|
+
return previewBody(command, { key: key ?? null, level }, messenger);
|
|
25
|
+
};
|
|
26
|
+
const TARGETS = new Set(["chat", "message", "person", "profile"]);
|
|
27
|
+
/**
|
|
28
|
+
* `messenger_contacted` stays for the messengers, whose callers may read it; `service_contacted` says the same
|
|
29
|
+
* for every tool. Without `permission` nothing was checked: a host that is not `run` has no settings to check.
|
|
30
|
+
*/
|
|
31
|
+
export const previewBody = (command, permission, messenger) => {
|
|
25
32
|
return {
|
|
26
33
|
preview: true,
|
|
27
|
-
path,
|
|
28
|
-
permission
|
|
29
|
-
validation: {
|
|
30
|
-
|
|
34
|
+
path: commandPathOf(command),
|
|
35
|
+
...(permission ? { permission } : {}),
|
|
36
|
+
validation: {
|
|
37
|
+
arguments: "parsed",
|
|
38
|
+
permissions: permission ? "checked" : "not-checked",
|
|
39
|
+
targets: "unresolved",
|
|
40
|
+
businessRules: "not-executed",
|
|
41
|
+
},
|
|
42
|
+
effects: {
|
|
43
|
+
actionInvoked: false,
|
|
44
|
+
writeReserved: false,
|
|
45
|
+
...(messenger ? { messengerContacted: false } : {}),
|
|
46
|
+
serviceContacted: false,
|
|
47
|
+
configurationRead: permission !== undefined,
|
|
48
|
+
},
|
|
31
49
|
arguments: command.registeredArguments.map((argument, at) => {
|
|
32
50
|
const value = command.processedArgs[at];
|
|
33
51
|
return {
|
|
34
52
|
name: argument.name(),
|
|
35
|
-
...(
|
|
53
|
+
...(TARGETS.has(argument.name()) && typeof value === "string"
|
|
36
54
|
? { reference: value }
|
|
37
55
|
: { supplied: value !== undefined, bytes: typeof value === "string" ? Buffer.byteLength(value) : undefined }),
|
|
38
56
|
};
|
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" | "permissionSources" | "configPath" | "savedPermissions">>;
|
|
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
|
@@ -10,6 +10,7 @@ import { isCliFailure, isCommanderFailure } from "../errors/failures.js";
|
|
|
10
10
|
import { failureContext } from "../errors/reasons.js";
|
|
11
11
|
import { envName, readOnlyLockOf, readOnlyRefusal } from "../ports/app.js";
|
|
12
12
|
import { recorded, wasSettled } from "../runs/recording.js";
|
|
13
|
+
import { runContextOf } from "../runs/run-context.js";
|
|
13
14
|
import { processRun, useAsProcessRun } from "../runs/scope.js";
|
|
14
15
|
import { AUTHORITY_KEY, BOT_API_KEY, isBotApi, keyForCommand } from "../sends/permissions.js";
|
|
15
16
|
import { logParsedArgv, logVersion } from "./argv-log.js";
|
|
@@ -28,7 +29,7 @@ import { checkJson, liftJson } from "./json-fields.js";
|
|
|
28
29
|
import { PreviewComplete, preview } from "./preview.js";
|
|
29
30
|
import { commandWords, liftProfile, PROFILE_FREE, sampleCommand, valuedOptions } from "./profile.js";
|
|
30
31
|
import { absolutePaths, relayTarget } from "./relay-route.js";
|
|
31
|
-
import { settingsFor } from "./settings.js";
|
|
32
|
+
import { NoProfileHere, namedProfile, settingsFor, } from "./settings.js";
|
|
32
33
|
import { assertSharedContracts } from "./shared-contracts.js";
|
|
33
34
|
import { commandAt, lineOf, withLines } from "./steps.js";
|
|
34
35
|
/**
|
|
@@ -191,6 +192,7 @@ const runOnce = async (argv, definition, options) => {
|
|
|
191
192
|
const exit = {};
|
|
192
193
|
const environment = {
|
|
193
194
|
...options,
|
|
195
|
+
argv,
|
|
194
196
|
streams: control.streams,
|
|
195
197
|
signal: options.signal ? AbortSignal.any([control.signal, options.signal]) : control.signal,
|
|
196
198
|
commandSignal: control.signal,
|
|
@@ -267,16 +269,18 @@ const runOnce = async (argv, definition, options) => {
|
|
|
267
269
|
const path = commandPathOf(action);
|
|
268
270
|
const lock = readOnlyLockOf(definition.app, commandEnv);
|
|
269
271
|
const input = options.stdin ?? process.stdin;
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
272
|
+
// The first-run questions write the shared config; a dry run writes nothing.
|
|
273
|
+
if (!flags.dryRun)
|
|
274
|
+
await setupAtStart({
|
|
275
|
+
command,
|
|
276
|
+
env: commandEnv,
|
|
277
|
+
contentLanguages: definition.app.contentLanguages,
|
|
278
|
+
path,
|
|
279
|
+
headless,
|
|
280
|
+
readOnlyLock: lock,
|
|
281
|
+
input,
|
|
282
|
+
terminal: !options.streams && input.isTTY === true && process.stderr.isTTY === true,
|
|
283
|
+
});
|
|
280
284
|
if (lock && isLogin(action) && !flags.dryRun)
|
|
281
285
|
throw readOnlyRefusal(lock, "log in or out");
|
|
282
286
|
if (lock && isStoreChange(action) && !flags.dryRun)
|
|
@@ -385,7 +389,7 @@ const runOnce = async (argv, definition, options) => {
|
|
|
385
389
|
configPath: resolved.configPath ?? "",
|
|
386
390
|
profile: resolved.profile,
|
|
387
391
|
...(resolved.savedPermissions ? { savedPermissions: resolved.savedPermissions } : {}),
|
|
388
|
-
}, command, definition.configuration?.permissionKey);
|
|
392
|
+
}, command, definition.configuration?.permissionKey, definition.app.messenger !== false);
|
|
389
393
|
renderAnswer(action, outputFor(action), itemOf(snakeKeys(previewed)));
|
|
390
394
|
throw new PreviewComplete();
|
|
391
395
|
}
|
|
@@ -412,7 +416,7 @@ const runOnce = async (argv, definition, options) => {
|
|
|
412
416
|
refuseJoinedPath(program, words.filter((word) => !word.startsWith("-")), profile !== undefined && !/\s/.test(profile) ? [profile] : []);
|
|
413
417
|
}
|
|
414
418
|
catch (failure) {
|
|
415
|
-
const settled = await settleFailure(failure, { definition, program, rest: lifted, profile, options });
|
|
419
|
+
const settled = await settleFailure(failure, { definition, program, rest: lifted, argv, profile, options });
|
|
416
420
|
return report(streams, reporting, failed(definition, program, lifted, failure, settled, debug));
|
|
417
421
|
}
|
|
418
422
|
if (profile !== undefined)
|
|
@@ -427,7 +431,7 @@ const runOnce = async (argv, definition, options) => {
|
|
|
427
431
|
const message = `"${profile}" is not a command, so it was read as a profile name — and no command followed it. ` +
|
|
428
432
|
`Run \`${command} --help\` for the commands, or \`${command} ${profile} account show\` if "${profile}" is your profile.`;
|
|
429
433
|
const failure = new CliError("validation_error", message, { param: "profile" });
|
|
430
|
-
const settled = await settleFailure(failure, { definition, program, rest, profile, options });
|
|
434
|
+
const settled = await settleFailure(failure, { definition, program, rest, argv, profile, options });
|
|
431
435
|
return report(streams, reporting, failed(definition, program, rest, failure, settled, debug));
|
|
432
436
|
}
|
|
433
437
|
try {
|
|
@@ -472,10 +476,10 @@ const runOnce = async (argv, definition, options) => {
|
|
|
472
476
|
message += ` — "${profile}" is not a command, so it was read as a profile name — which left "${rest[0]}" to be one.`;
|
|
473
477
|
}
|
|
474
478
|
const failure = new CliError("validation_error", message);
|
|
475
|
-
const settled = await settleFailure(failure, { definition, program, rest, profile, options });
|
|
479
|
+
const settled = await settleFailure(failure, { definition, program, rest, argv, profile, options });
|
|
476
480
|
return report(streams, reporting, failed(definition, program, rest, failure, settled, debug));
|
|
477
481
|
}
|
|
478
|
-
const settled = await settleFailure(error, { definition, program, rest, profile, options });
|
|
482
|
+
const settled = await settleFailure(error, { definition, program, rest, argv, profile, options });
|
|
479
483
|
const exit = report(streams, reporting, failed(definition, program, rest, error, settled, debug));
|
|
480
484
|
return signalExit ?? exit;
|
|
481
485
|
}
|
|
@@ -562,9 +566,9 @@ const settleFailure = async (failure, state) => {
|
|
|
562
566
|
/**
|
|
563
567
|
* **Every failure is kept as a run** — a usage error, a configuration that will not load, a command
|
|
564
568
|
* that never opens a run — unless recording was turned off. One its own run already kept is skipped.
|
|
565
|
-
*
|
|
569
|
+
* Its arguments are kept masked (`maskArgv`): a value that can be a message never is.
|
|
566
570
|
*/
|
|
567
|
-
const keepFailure = async (failure, { definition, program, rest, profile, options }) => {
|
|
571
|
+
const keepFailure = async (failure, { definition, program, rest, argv, profile, options }) => {
|
|
568
572
|
if (wasSettled(failure))
|
|
569
573
|
return;
|
|
570
574
|
const env = options.env ?? process.env;
|
|
@@ -577,10 +581,18 @@ const keepFailure = async (failure, { definition, program, rest, profile, option
|
|
|
577
581
|
catch {
|
|
578
582
|
// A configuration that will not load is a failure worth keeping too; the flags are all there is to go on.
|
|
579
583
|
}
|
|
584
|
+
const command = `${options.agent ? "mcp " : ""}${path || definition.app.command}`;
|
|
580
585
|
await recorded({
|
|
581
586
|
app: definition.app,
|
|
582
|
-
command
|
|
583
|
-
|
|
587
|
+
command,
|
|
588
|
+
context: runContextOf({
|
|
589
|
+
bin: definition.app.command,
|
|
590
|
+
command: path ? command : options.agent ? "mcp" : "",
|
|
591
|
+
program,
|
|
592
|
+
argv,
|
|
593
|
+
env,
|
|
594
|
+
}),
|
|
595
|
+
profile: settings ? namedProfile(settings) : profile,
|
|
584
596
|
record: false,
|
|
585
597
|
keepFailed: settings?.keepFailedRuns ?? !rest.includes("--no-record"),
|
|
586
598
|
trace: false,
|
|
@@ -609,10 +621,11 @@ const commandPath = (program, rest) => {
|
|
|
609
621
|
return words.join(" ");
|
|
610
622
|
};
|
|
611
623
|
const failed = ({ app }, program, rest, error, settled, debug) => {
|
|
612
|
-
const failure = settled.settlementFailed && isCliFailure(error)
|
|
613
|
-
? new CliError(error.code, error.message, { ...error.details, settlement_failed: true })
|
|
614
|
-
: error;
|
|
615
624
|
const path = commandPath(program, program.args.length > 0 ? program.args : rest);
|
|
625
|
+
const told = error instanceof NoProfileHere && !commandAt(program, "setup") ? error.withoutSetup(path) : error;
|
|
626
|
+
const failure = settled.settlementFailed && isCliFailure(told)
|
|
627
|
+
? new CliError(told.code, told.message, { ...told.details, settlement_failed: true })
|
|
628
|
+
: told;
|
|
616
629
|
const effects = judgedEffects(path ? metaOf(commandAt(program, path) ?? program).effects : undefined);
|
|
617
630
|
const object = errorObjectOf(failure, {
|
|
618
631
|
...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;
|
package/dist/cli/runs/search.js
CHANGED
package/dist/cli/settings.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CliError } from "@wirecat/cli-base";
|
|
1
2
|
import * as v from "valibot";
|
|
2
3
|
import { type AISettings } from "../analysis/settings.js";
|
|
3
4
|
import { type AppIdentity } from "../ports/app.js";
|
|
@@ -161,6 +162,18 @@ export interface ResolveOptions {
|
|
|
161
162
|
}
|
|
162
163
|
/** Where `profile` came from when none was named and the command needs none. */
|
|
163
164
|
export declare const NONE_NAMED = "none named";
|
|
165
|
+
/** The profile a run is for: none when nothing named one, even though `Settings.profile` holds a placeholder. */
|
|
166
|
+
export declare const namedProfile: (settings: Pick<Settings, "profile"> & Partial<Pick<Settings, "sources">>) => string | undefined;
|
|
167
|
+
/**
|
|
168
|
+
* No profile named and none on this computer. The settings cannot see the program, so the program swaps the
|
|
169
|
+
* `setup` this suggests for `withoutSetup` when it has no such command.
|
|
170
|
+
*/
|
|
171
|
+
export declare class NoProfileHere extends CliError {
|
|
172
|
+
private readonly app;
|
|
173
|
+
constructor(app: AppIdentity);
|
|
174
|
+
/** `words`: the command that was typed, without its arguments, so the suggestion runs as written. */
|
|
175
|
+
withoutSetup(words: string): CliError;
|
|
176
|
+
}
|
|
164
177
|
/** The first candidate that is set, and which layer it came from. */
|
|
165
178
|
export declare const first: <T>(candidates: [Source, T | undefined][], fallback: T) => {
|
|
166
179
|
value: T;
|
package/dist/cli/settings.js
CHANGED
|
@@ -7,7 +7,7 @@ import { envName, readOnlyLockOf } from "../ports/app.js";
|
|
|
7
7
|
import { assertOverridesTighten, CEILING_KEY, LEVELS, layerPermissions, permissionOverrides, RESOURCES, } from "../sends/permissions.js";
|
|
8
8
|
import { storePath } from "../store/path.js";
|
|
9
9
|
import { sharedConfigPath } from "../store/shared-config.js";
|
|
10
|
-
import { issueMessage, parseConfigText, publicIssue, savedPermissionKeys, schemaIssues,
|
|
10
|
+
import { checkConfigText, issueMessage, parseConfigText, publicIssue, savedPermissionKeys, schemaIssues, } from "./config-issues.js";
|
|
11
11
|
import { DEFAULT_CONFIG, ensureDefaultConfig } from "./default-config.js";
|
|
12
12
|
import { profilesHere } from "./messenger/accounts.js";
|
|
13
13
|
import { usableProfileName } from "./profile.js";
|
|
@@ -81,6 +81,26 @@ const SHARED_DEFAULTS_ONLY = {
|
|
|
81
81
|
export const NONE_NAMED = "none named";
|
|
82
82
|
/** A name for a command that speaks for no account; never chosen for one that does. */
|
|
83
83
|
const NO_PROFILE = "default";
|
|
84
|
+
/** The profile a run is for: none when nothing named one, even though `Settings.profile` holds a placeholder. */
|
|
85
|
+
export const namedProfile = (settings) => settings.sources?.profile === NONE_NAMED ? undefined : settings.profile;
|
|
86
|
+
/**
|
|
87
|
+
* No profile named and none on this computer. The settings cannot see the program, so the program swaps the
|
|
88
|
+
* `setup` this suggests for `withoutSetup` when it has no such command.
|
|
89
|
+
*/
|
|
90
|
+
export class NoProfileHere extends CliError {
|
|
91
|
+
app;
|
|
92
|
+
constructor(app) {
|
|
93
|
+
super("validation_error", `name a profile: there is none on this computer yet — create one with \`${app.command} personal setup\` ` +
|
|
94
|
+
"(any name in place of personal)", { profiles: [] });
|
|
95
|
+
this.app = app;
|
|
96
|
+
}
|
|
97
|
+
/** `words`: the command that was typed, without its arguments, so the suggestion runs as written. */
|
|
98
|
+
withoutSetup(words) {
|
|
99
|
+
const command = words ? `${this.app.command} personal ${words}` : `${this.app.command} personal …`;
|
|
100
|
+
return new CliError("validation_error", `name a profile: there is none on this computer yet — any name works, such as \`${command}\` ` +
|
|
101
|
+
`(or set ${envName(this.app, "PROFILE")})`, { profiles: [] });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
84
104
|
/** The first candidate that is set, and which layer it came from. */
|
|
85
105
|
export const first = (candidates, fallback) => {
|
|
86
106
|
for (const [from, value] of candidates)
|
|
@@ -137,14 +157,15 @@ export const settingsFor = (app, extension = {}) => {
|
|
|
137
157
|
};
|
|
138
158
|
/** The file with every problem found; what loads is the file without the settings they name. */
|
|
139
159
|
const loadConfig = (path) => {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
160
|
+
let text;
|
|
161
|
+
try {
|
|
162
|
+
text = readFileSync(path, "utf8");
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return { config: { profiles: {} }, issues: [] };
|
|
166
|
+
}
|
|
167
|
+
const { config, issues } = checkConfigText(text, { file: path, tool: app.command, schema: readSchema });
|
|
168
|
+
return { config: (config ?? { profiles: {} }), issues };
|
|
148
169
|
};
|
|
149
170
|
/**
|
|
150
171
|
* A lock pins a process to one profile: the owner sets it where an agent runs, and a first word
|
|
@@ -170,10 +191,9 @@ export const settingsFor = (app, extension = {}) => {
|
|
|
170
191
|
if (!needed)
|
|
171
192
|
return { value: NO_PROFILE, from: NONE_NAMED };
|
|
172
193
|
const here = profilesOf(config, env);
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
: `name a profile: \`${app.command} <profile> …\` — profiles here: ${here.join(", ")} (or set ${PROFILE})`, { profiles: here });
|
|
194
|
+
if (here.length === 0)
|
|
195
|
+
throw new NoProfileHere(app);
|
|
196
|
+
throw new CliError("validation_error", `name a profile: \`${app.command} <profile> …\` — profiles here: ${here.join(", ")} (or set ${PROFILE})`, { profiles: here });
|
|
177
197
|
};
|
|
178
198
|
const profilesOf = (config, env) => profilesHere(app, [...namedProfiles(config), ...(extension.profiles?.(env) ?? [])], env);
|
|
179
199
|
const resolveSettings = (flags = {}, options = {}) => {
|
|
@@ -188,7 +208,7 @@ export const settingsFor = (app, extension = {}) => {
|
|
|
188
208
|
}
|
|
189
209
|
const loaded = loadConfig(configPath);
|
|
190
210
|
if (loaded.issues.length > 0 && !options.withIssues)
|
|
191
|
-
throw new CliError("configuration_error", issueMessage(loaded.issues
|
|
211
|
+
throw new CliError("configuration_error", issueMessage(loaded.issues));
|
|
192
212
|
const { config } = loaded;
|
|
193
213
|
const lock = given(env[PROFILE_LOCK]);
|
|
194
214
|
const named = first([
|
|
@@ -311,7 +331,7 @@ export const settingsFor = (app, extension = {}) => {
|
|
|
311
331
|
},
|
|
312
332
|
configured,
|
|
313
333
|
shared,
|
|
314
|
-
configIssues: loaded.issues
|
|
334
|
+
configIssues: loaded.issues,
|
|
315
335
|
savedPermissions: savedPermissionKeys(config),
|
|
316
336
|
};
|
|
317
337
|
// The file was checked by the schema; a flag was not, and `--limit abc` is `NaN` by the time it
|
|
@@ -332,7 +352,7 @@ export const settingsFor = (app, extension = {}) => {
|
|
|
332
352
|
const configuredProfiles = (options = {}) => {
|
|
333
353
|
const { config, issues } = loadConfig(configPathFor(options));
|
|
334
354
|
if (issues.length > 0 && !options.withIssues)
|
|
335
|
-
throw new CliError("configuration_error", issueMessage(issues
|
|
355
|
+
throw new CliError("configuration_error", issueMessage(issues));
|
|
336
356
|
return namedProfiles(config);
|
|
337
357
|
};
|
|
338
358
|
/**
|
|
@@ -5,6 +5,7 @@ import { botMcpCommand as botMcpCommandDefinition } from "./bot/mcp.js";
|
|
|
5
5
|
import { botMessagesCommand as botMessagesCommandDefinition } from "./bot/messages.js";
|
|
6
6
|
import { commandsCommand as commandsCommandDefinition } from "./commands-command.js";
|
|
7
7
|
import { configCommand as configCommandDefinition } from "./config-command.js";
|
|
8
|
+
import { honoursDryRun } from "./dry-run-guard.js";
|
|
8
9
|
import { guideCommand as guideCommandDefinition } from "./guide.js";
|
|
9
10
|
import { addMeetingBatchCommand as addMeetingBatchCommandDefinition } from "./meeting-batch.js";
|
|
10
11
|
import { addMeetingPreparationCommand as addMeetingPreparationCommandDefinition } from "./meeting-preparation-command.js";
|
|
@@ -48,9 +49,10 @@ import { tasksCommand as tasksCommandDefinition, tasksStatsCommand as tasksStats
|
|
|
48
49
|
import { topicsCommand as topicsCommandDefinition } from "./messenger/topics-command.js";
|
|
49
50
|
import { watchCommand as watchCommandDefinition } from "./messenger/watch-command.js";
|
|
50
51
|
import { runsCommand as runsCommandDefinition } from "./runs/command.js";
|
|
51
|
-
import { sharedCommand } from "./shared-contracts.js";
|
|
52
|
+
import { sharedCommand as protectedCommand } from "./shared-contracts.js";
|
|
52
53
|
import { skillCommand as skillCommandDefinition } from "./skill-command.js";
|
|
53
54
|
import { upgradeCommand as upgradeCommandDefinition } from "./upgrade-command.js";
|
|
55
|
+
const sharedCommand = (factory) => honoursDryRun(protectedCommand(factory));
|
|
54
56
|
export const botAdminsCommand = sharedCommand(botAdminsCommandDefinition);
|
|
55
57
|
export const botMembersCommand = sharedCommand(botMembersCommandDefinition);
|
|
56
58
|
export const generatedApiCommand = sharedCommand(generatedApiCommandDefinition);
|