@wirecat/cli-core 0.547.0 → 0.549.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 +228 -0
- package/dist/calendar/cli/answers.d.ts +7 -2
- package/dist/calendar/cli/answers.js +12 -4
- package/dist/calendar/cli/index.d.ts +10 -1
- package/dist/calendar/cli/index.js +18 -11
- package/dist/calendar/cli/search-all.d.ts +17 -0
- package/dist/calendar/cli/search-all.js +72 -0
- package/dist/calendar/feeds.d.ts +19 -0
- package/dist/calendar/feeds.js +52 -9
- package/dist/calendar/source.d.ts +10 -2
- package/dist/calendar/sync.d.ts +2 -0
- package/dist/calendar/sync.js +10 -6
- package/dist/calendar/testing/memory.js +39 -0
- package/dist/cli/answer-entity.d.ts +4 -0
- package/dist/cli/answer-entity.js +5 -0
- package/dist/cli/bot/context.d.ts +2 -0
- package/dist/cli/compact.d.ts +41 -0
- package/dist/cli/compact.js +313 -0
- package/dist/cli/context.d.ts +11 -2
- package/dist/cli/context.js +15 -6
- package/dist/cli/envelope.d.ts +5 -1
- package/dist/cli/envelope.js +15 -4
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +2 -0
- package/dist/cli/messenger/search-command.d.ts +10 -0
- package/dist/cli/messenger/search-command.js +19 -13
- package/dist/cli/messenger/store-maintenance-command.d.ts +1 -1
- package/dist/cli/program.d.ts +3 -0
- package/dist/cli/program.js +7 -1
- package/dist/domain/providers.d.ts +3 -0
- package/dist/domain/providers.js +20 -4
- package/dist/domain/ref-arguments.js +3 -7
- package/dist/domain/ref-summaries.d.ts +56 -0
- package/dist/domain/ref-summaries.js +63 -0
- package/dist/entities/index.d.ts +11 -0
- package/dist/entities/relationship.d.ts +2 -0
- package/dist/entities/relationship.js +2 -0
- package/dist/entities/task-results.d.ts +10 -0
- package/dist/entities/task-results.js +1 -0
- package/dist/errors/reasons.js +1 -0
- package/dist/meetings/event-store.d.ts +10 -0
- package/dist/meetings/testing/event-store-contract.js +36 -0
- package/dist/output-detail.js +9 -5
- package/dist/output.d.ts +5 -1
- package/dist/output.js +29 -4
- package/dist/services/proposals/kinds/relationship-update.d.ts +4 -0
- package/dist/services/proposals/kinds/relationship-update.js +53 -0
- package/dist/services/proposals/registry.js +2 -0
- package/dist/store/decisions.d.ts +3 -1
- package/dist/store/decisions.js +7 -1
- package/dist/store/index.d.ts +2 -1
- package/dist/store/sqlite/accounts.d.ts +8 -0
- package/dist/store/sqlite/accounts.js +2 -2
- package/dist/store/sqlite/calendar-forget.d.ts +17 -0
- package/dist/store/sqlite/calendar-forget.js +68 -0
- package/dist/store/sqlite/calendar.js +8 -0
- package/dist/store/sqlite/evidence-sources.d.ts +2 -2
- package/dist/store/sqlite/evidence-sources.js +41 -77
- package/dist/store/sqlite/knowledge.d.ts +17 -23
- package/dist/store/sqlite/knowledge.js +13 -13
- package/dist/store/sqlite/manifest.js +2 -0
- package/dist/store/sqlite/migrations.generated.js +12 -0
- package/dist/store/sqlite/notes.d.ts +5 -0
- package/dist/store/sqlite/notes.js +6 -2
- package/dist/store/sqlite/open-store.js +2 -0
- package/dist/store/sqlite/proposed-actions.d.ts +1 -1
- package/dist/store/sqlite/proposed-actions.js +10 -2
- package/dist/store/sqlite/ref-summaries.d.ts +17 -0
- package/dist/store/sqlite/ref-summaries.js +378 -0
- package/dist/store/sqlite/relation-proposals.d.ts +40 -0
- package/dist/store/sqlite/relation-proposals.js +88 -1
- package/dist/store/sqlite/schema.d.ts +16 -0
- package/dist/store/sqlite/schema.js +1 -0
- package/dist/store/sqlite/suggested-links.d.ts +10 -0
- package/dist/store/sqlite/suggested-links.js +68 -0
- package/dist/store/store.d.ts +3 -0
- package/dist/store/store.js +5 -0
- package/package.json +3 -3
- package/parity.json +4 -0
package/dist/cli/context.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type KeyringStore, type Renderer, type RenderFormat, type Streams } from "@wirecat/cli-base";
|
|
2
|
-
import type
|
|
3
|
-
import type
|
|
2
|
+
import { type ErrorObject } from "@wirecat/cli-base/standards";
|
|
3
|
+
import { type Command } from "commander";
|
|
4
4
|
import { type Deadline, type RunBudget } from "../background/budget.js";
|
|
5
5
|
import type { MessageEvent } from "../domain/models.js";
|
|
6
6
|
import type { AppIdentity } from "../ports/app.js";
|
|
@@ -13,6 +13,8 @@ import { type Closeable } from "./deadline.js";
|
|
|
13
13
|
import { type GlobalFlags, type ResolveOptions, type Settings } from "./settings.js";
|
|
14
14
|
/** What every CLI's commands write to, when it is not the real terminal. A CLI adds its own fields. */
|
|
15
15
|
export interface BaseEnvironment {
|
|
16
|
+
/** The app's own compact rows (`ProgramDefinition.rows`), for `renderAnswer`. */
|
|
17
|
+
rows?: import("./compact.js").RowTable;
|
|
16
18
|
discoveryAvailability?: (command: import("./command-availability.js").AvailabilityCommand) => import("./command-availability.js").CommandAvailability | undefined;
|
|
17
19
|
streams?: Streams;
|
|
18
20
|
/** Whether a person is looking. Defaults to whether stdout is a terminal. */
|
|
@@ -121,12 +123,16 @@ export declare const environmentOf: <E extends BaseEnvironment = BaseEnvironment
|
|
|
121
123
|
* output goes and a test that captures it sees it. `readSecret(prompt, { ...secretInputOf(this), echo: true })`.
|
|
122
124
|
*/
|
|
123
125
|
export declare const secretInputOf: (command: Command) => Pick<SecretInput, "input" | "output">;
|
|
126
|
+
/** Whether the run asked for one line per item (WC-OUT-24). */
|
|
127
|
+
export declare const compactOf: (command: Command) => boolean;
|
|
124
128
|
/** For the commands that print but never need settings or a connection. */
|
|
125
129
|
export declare const outputFor: (command: Command) => {
|
|
126
130
|
format: "json" | "jsonl" | "pretty";
|
|
127
131
|
color: boolean;
|
|
128
132
|
streams: Streams;
|
|
129
133
|
renderer: Renderer;
|
|
134
|
+
compact?: boolean | undefined;
|
|
135
|
+
full?: boolean | undefined;
|
|
130
136
|
};
|
|
131
137
|
export interface BaseContext {
|
|
132
138
|
settings: Settings;
|
|
@@ -135,6 +141,9 @@ export interface BaseContext {
|
|
|
135
141
|
/** Whether the human view may use colour — for views that print their own text. */
|
|
136
142
|
color: boolean;
|
|
137
143
|
streams: Streams;
|
|
144
|
+
/** One line per item; `renderAnswer` draws it, `full` keeps each text whole. */
|
|
145
|
+
compact?: boolean;
|
|
146
|
+
full?: boolean;
|
|
138
147
|
env: NodeJS.ProcessEnv;
|
|
139
148
|
keyring?: KeyringStore;
|
|
140
149
|
/**
|
package/dist/cli/context.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { CliError } from "@wirecat/cli-base";
|
|
2
2
|
import { metaOf } from "@wirecat/cli-base/commands";
|
|
3
|
+
import { COMPACT_FLAG } from "@wirecat/cli-base/standards";
|
|
4
|
+
import { Option } from "commander";
|
|
3
5
|
import { runBudget } from "../background/budget.js";
|
|
4
6
|
import { resolveOutput } from "../output.js";
|
|
5
7
|
import { recorded } from "../runs/recording.js";
|
|
@@ -43,22 +45,27 @@ export const secretInputOf = (command) => {
|
|
|
43
45
|
const { stdin, stderr } = environmentOf(command);
|
|
44
46
|
return { ...(stdin ? { input: stdin } : {}), ...(stderr ? { output: stderr } : {}) };
|
|
45
47
|
};
|
|
48
|
+
const detailOf = ({ brief, full }) => {
|
|
49
|
+
if (brief && full)
|
|
50
|
+
throw new CliError("validation_error", "--brief and --full ask for opposite things — pick one");
|
|
51
|
+
return brief ? { hitDetail: "brief" } : full ? { hitDetail: "full" } : {};
|
|
52
|
+
};
|
|
53
|
+
const COMPACT_KEY = new Option(COMPACT_FLAG).attributeName();
|
|
54
|
+
/** Whether the run asked for one line per item (WC-OUT-24). */
|
|
55
|
+
export const compactOf = (command) => command.optsWithGlobals()[COMPACT_KEY] === true;
|
|
46
56
|
/** For the commands that print but never need settings or a connection. */
|
|
47
57
|
export const outputFor = (command) => {
|
|
48
58
|
const { streams, tty, env, agent } = environmentOf(command);
|
|
49
59
|
return resolveOutput({
|
|
50
60
|
...command.optsWithGlobals(),
|
|
61
|
+
...detailOf(command.optsWithGlobals()),
|
|
62
|
+
compact: compactOf(command),
|
|
51
63
|
...(agent ? { capture: agent.result } : {}),
|
|
52
64
|
...(streams ? { streams } : {}),
|
|
53
65
|
...(env ? { env } : {}),
|
|
54
66
|
...(tty === undefined ? {} : { tty }),
|
|
55
67
|
});
|
|
56
68
|
};
|
|
57
|
-
const detailOf = ({ brief, full }) => {
|
|
58
|
-
if (brief && full)
|
|
59
|
-
throw new CliError("validation_error", "--brief and --full ask for opposite things — pick one");
|
|
60
|
-
return brief ? { hitDetail: "brief" } : full ? { hitDetail: "full" } : {};
|
|
61
|
-
};
|
|
62
69
|
/** Everything a command needs that no messenger decides, resolved once. */
|
|
63
70
|
export const baseContext = (command, resolveSettings) => {
|
|
64
71
|
const environment = environmentOf(command);
|
|
@@ -66,9 +73,10 @@ export const baseContext = (command, resolveSettings) => {
|
|
|
66
73
|
const flags = command.optsWithGlobals();
|
|
67
74
|
const overrides = environment.agent?.overrides ?? [];
|
|
68
75
|
const settings = resolveSettings(overrides.length > 0 ? { ...flags, permission: [...overrides, ...(flags.permission ?? [])] } : flags, { env });
|
|
69
|
-
const { renderer, format, color, streams } = resolveOutput({
|
|
76
|
+
const { renderer, format, color, streams, compact, full } = resolveOutput({
|
|
70
77
|
...settings,
|
|
71
78
|
env,
|
|
79
|
+
compact: compactOf(command),
|
|
72
80
|
...detailOf(command.optsWithGlobals()),
|
|
73
81
|
...(command.optsWithGlobals().fields
|
|
74
82
|
? { fields: command.optsWithGlobals().fields }
|
|
@@ -85,6 +93,7 @@ export const baseContext = (command, resolveSettings) => {
|
|
|
85
93
|
format,
|
|
86
94
|
color,
|
|
87
95
|
streams,
|
|
96
|
+
...(compact ? { compact, full: full === true } : {}),
|
|
88
97
|
env,
|
|
89
98
|
...(environment.keyring ? { keyring: environment.keyring } : {}),
|
|
90
99
|
// The deadline inside the record, so a timeout finishes the run as failed rather than leaving it running.
|
package/dist/cli/envelope.d.ts
CHANGED
|
@@ -193,6 +193,10 @@ export interface AnswerOutput {
|
|
|
193
193
|
renderer: Renderer;
|
|
194
194
|
format: RenderFormat;
|
|
195
195
|
streams: Streams;
|
|
196
|
+
/** One line per item from the same answer (WC-OUT-24); an agent's run keeps JSON. */
|
|
197
|
+
compact?: boolean;
|
|
198
|
+
/** With `compact`: each text whole, not cut. */
|
|
199
|
+
full?: boolean;
|
|
196
200
|
}
|
|
197
201
|
/** An answer as JSON-lines records, for a writer that streams objects: one per item, then the end record. */
|
|
198
202
|
export declare const jsonlRecords: <T>(body: Body<T>) => object[];
|
|
@@ -201,7 +205,7 @@ export declare const jsonlRecords: <T>(body: Body<T>) => object[];
|
|
|
201
205
|
* the items — `view` draws them — and each next step this CLI has, as a line on stderr. A partial answer the
|
|
202
206
|
* command does not declare `partial` exits with its first issue's code, after printing what was read.
|
|
203
207
|
*/
|
|
204
|
-
export declare const renderAnswer: <T>(command: Command, { renderer, format, streams }: AnswerOutput, body: Body<T>, view?: (body: Body<T>) => string) => void;
|
|
208
|
+
export declare const renderAnswer: <T>(command: Command, { renderer, format, streams, compact, full }: AnswerOutput, body: Body<T>, view?: (body: Body<T>) => string) => void;
|
|
205
209
|
/**
|
|
206
210
|
* A document that is not an answer about the owner's data — the command registry, a `--dry-run` preview of what a
|
|
207
211
|
* command would do — printed whole, with no envelope.
|
package/dist/cli/envelope.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { exitCodeOfAnswer } from "@wirecat/cli-base";
|
|
2
2
|
import { metaOf } from "@wirecat/cli-base/commands";
|
|
3
|
-
import { itemEnvelopeSchema, jsonlOf, listEnvelopeSchema, MAX_NEXT_STEPS, } from "@wirecat/cli-base/standards";
|
|
3
|
+
import { COMPACT_FLAG, itemEnvelopeSchema, jsonlOf, listEnvelopeSchema, MAX_NEXT_STEPS, } from "@wirecat/cli-base/standards";
|
|
4
4
|
import * as v from "valibot";
|
|
5
5
|
import { currentRun } from "../runs/scope.js";
|
|
6
|
+
import { compactText, entityRows, rowShapeOf, rowsOfEntity, TEXT_CHARS } from "./compact.js";
|
|
6
7
|
import { environmentOf, setExitCode } from "./context.js";
|
|
7
8
|
import { withoutProviderRefs } from "./provider-refs.js";
|
|
8
9
|
import { nextStep, rootOf, runnable, stepLine, withLines, withSafety } from "./steps.js";
|
|
@@ -128,6 +129,8 @@ export const sealed = (value) => {
|
|
|
128
129
|
const { meta, ...rest } = withoutProviderRefs(value);
|
|
129
130
|
return { ...rest, meta: metaNow(meta) };
|
|
130
131
|
};
|
|
132
|
+
/** The steps a compact answer prints, each rerunning in the compact form the caller chose. */
|
|
133
|
+
const compactSteps = (root, steps) => steps.map((step) => step.kind === "command" ? { ...step, line: `${step.line ?? stepLine(root, step)} ${COMPACT_FLAG}` } : step);
|
|
131
134
|
/** An answer as JSON lines: one line per item, then the end line. One thing is a list of one that has no more. */
|
|
132
135
|
const linesOf = (body) => jsonlOf("items" in body ? body : { items: [body.item], page: { mode: "page", limit: 1, has_more: false }, meta: body.meta });
|
|
133
136
|
/** An answer as JSON-lines records, for a writer that streams objects: one per item, then the end record. */
|
|
@@ -139,7 +142,7 @@ export const jsonlRecords = (body) => linesOf(sealed(body))
|
|
|
139
142
|
* the items — `view` draws them — and each next step this CLI has, as a line on stderr. A partial answer the
|
|
140
143
|
* command does not declare `partial` exits with its first issue's code, after printing what was read.
|
|
141
144
|
*/
|
|
142
|
-
export const renderAnswer = (command, { renderer, format, streams }, body, view) => {
|
|
145
|
+
export const renderAnswer = (command, { renderer, format, streams, compact, full }, body, view) => {
|
|
143
146
|
if (!isBody(body))
|
|
144
147
|
refuse(body);
|
|
145
148
|
const root = rootOf(command);
|
|
@@ -149,7 +152,15 @@ export const renderAnswer = (command, { renderer, format, streams }, body, view)
|
|
|
149
152
|
if (next.length === 0)
|
|
150
153
|
delete kept.next;
|
|
151
154
|
const whole = sealed(kept);
|
|
152
|
-
|
|
155
|
+
const environment = environmentOf(command);
|
|
156
|
+
const drawn = compact && !environment.agent;
|
|
157
|
+
if (drawn) {
|
|
158
|
+
const lined = whole.next ? { ...whole, next: compactSteps(root, whole.next) } : whole;
|
|
159
|
+
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
160
|
+
const { rows } = environment;
|
|
161
|
+
streams.data(compactText(lined, rowShapeOf(command, rows && entityRows(rows)), { timezone, textChars: full ? 0 : TEXT_CHARS }, rowsOfEntity(rows)));
|
|
162
|
+
}
|
|
163
|
+
else if (format === "json")
|
|
153
164
|
renderer.result(whole);
|
|
154
165
|
else if (format === "jsonl")
|
|
155
166
|
for (const line of linesOf(whole).split("\n"))
|
|
@@ -161,7 +172,7 @@ export const renderAnswer = (command, { renderer, format, streams }, body, view)
|
|
|
161
172
|
const code = exitCodeOfAnswer(whole.meta, metaOf(command).partial === true);
|
|
162
173
|
if (code !== 0)
|
|
163
174
|
setExitCode(command, code);
|
|
164
|
-
if (format === "json")
|
|
175
|
+
if (drawn || format === "json")
|
|
165
176
|
return;
|
|
166
177
|
for (const step of next)
|
|
167
178
|
renderer.note(step.kind === "command" ? `→ ${step.why}: ${stepLine(root, step)}` : `→ ${step.why}`);
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { AI_ENTRIES, AI_SETTING_KEYS, type AISettings, resolveAISettings } from "../analysis/settings.js";
|
|
2
2
|
export { parseDuration } from "../domain/duration.js";
|
|
3
|
+
export { openOf, PROFILE_PLACEHOLDER, type ProfilesFor, type RefOpen, type RefSummary, refLabel, } from "../domain/ref-summaries.js";
|
|
3
4
|
export { botTemplate } from "../entities/list-results.js";
|
|
4
5
|
export { syncProgress } from "../entities/store-records.js";
|
|
5
6
|
export { isCliFailure, isCommanderFailure } from "../errors/failures.js";
|
|
@@ -58,6 +59,7 @@ export type { CommandAvailability, DiscoveredCommand } from "./command-availabil
|
|
|
58
59
|
export { commandContract, type JsonSchema, SCHEMA_DIALECT } from "./command-contract.js";
|
|
59
60
|
export { type CommandRunOptions, type CommandRuns, commandRuns, type ProviderAnswer, type ProviderCall, providerEvent, providerRequest, type RunSettings, recordRuns, runSettings, type SavedRunSettings, } from "./command-runs.js";
|
|
60
61
|
export { CONTRACT } from "./commands-command.js";
|
|
62
|
+
export { type CompactOptions, compactText, entityRows, GENERIC_ROW, type RowColumn, type RowShape, type RowsResolver, type RowTable, type RowValue, rowShapeOf, rowsOfEntity, SHARED_ROWS, } from "./compact.js";
|
|
61
63
|
export { refuseUnknownKey } from "./config-command.js";
|
|
62
64
|
export { type ConfigIssue, type ConfigIssueKind, checkConfigText, issueMessage, readConfigFile, readConfigText, settingPathOf, withoutSetting, } from "./config-issues.js";
|
|
63
65
|
export { loadSnakeCaseConfig } from "./config-keys.js";
|
package/dist/cli/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { AI_ENTRIES, AI_SETTING_KEYS, resolveAISettings } from "../analysis/settings.js";
|
|
2
2
|
export { parseDuration } from "../domain/duration.js";
|
|
3
|
+
export { openOf, PROFILE_PLACEHOLDER, refLabel, } from "../domain/ref-summaries.js";
|
|
3
4
|
export { botTemplate } from "../entities/list-results.js";
|
|
4
5
|
export { syncProgress } from "../entities/store-records.js";
|
|
5
6
|
export { isCliFailure, isCommanderFailure } from "../errors/failures.js";
|
|
@@ -50,6 +51,7 @@ export { PressLog, UpdatesCursor } from "./bot/updates.js";
|
|
|
50
51
|
export { commandContract, SCHEMA_DIALECT } from "./command-contract.js";
|
|
51
52
|
export { commandRuns, providerEvent, providerRequest, recordRuns, runSettings, } from "./command-runs.js";
|
|
52
53
|
export { CONTRACT } from "./commands-command.js";
|
|
54
|
+
export { compactText, entityRows, GENERIC_ROW, rowShapeOf, rowsOfEntity, SHARED_ROWS, } from "./compact.js";
|
|
53
55
|
export { refuseUnknownKey } from "./config-command.js";
|
|
54
56
|
export { checkConfigText, issueMessage, readConfigFile, readConfigText, settingPathOf, withoutSetting, } from "./config-issues.js";
|
|
55
57
|
export { loadSnakeCaseConfig } from "./config-keys.js";
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
+
import type { SearchAllIncludingMeetingsFound } from "../../services/search-all-meetings.js";
|
|
2
3
|
import { type Messenger } from "./context.js";
|
|
4
|
+
import { type SourceSearched } from "./search-answers.js";
|
|
5
|
+
export declare const searchAllOnlyOf: (value: string) => SourceSearched[];
|
|
6
|
+
/** One `search all` hit as a person reads it. */
|
|
7
|
+
export declare const searchAllLine: (item: SearchAllIncludingMeetingsFound["items"][number]) => string;
|
|
8
|
+
/**
|
|
9
|
+
* `search all`'s scope, paging and matching options, the same in every CLI that mounts it (WC-ACCT-8): a messenger's
|
|
10
|
+
* and a calendar's.
|
|
11
|
+
*/
|
|
12
|
+
export declare const searchAllOptions: (command: Command) => Command;
|
|
3
13
|
/**
|
|
4
14
|
* `search <resource>`: every search of the tool, one leaf per resource (STANDARD.md, "Search
|
|
5
15
|
* hierarchy"). `topics` only where the CLI has forum topics.
|
|
@@ -15,7 +15,7 @@ import { messagesSearchCommand } from "./messages-search-command.js";
|
|
|
15
15
|
import { allAnswer, meaningNote, notesAnswer, offsetOf, SOURCE_SEARCHED, shownOf, } from "./search-answers.js";
|
|
16
16
|
import { backendOptions, backendRequest, noteServer } from "./search-backend-options.js";
|
|
17
17
|
import { topicsSearchCommand } from "./topics-command.js";
|
|
18
|
-
const
|
|
18
|
+
export const searchAllOnlyOf = (value) => {
|
|
19
19
|
const chosen = value.split(",").map((one) => one.trim());
|
|
20
20
|
if (chosen.includes("mail"))
|
|
21
21
|
throw new CliError("validation_error", "mail is searched by the separate memo CLI: install @wirecat/cli-memo, then `memo search mail …`");
|
|
@@ -24,7 +24,8 @@ const onlyOf = (value) => {
|
|
|
24
24
|
throw new CliError("validation_error", `--only takes ${SOURCE_SEARCHED.join(", ")}, separated by commas; meetings when --source names a meeting account`);
|
|
25
25
|
return [...new Set(chosen)];
|
|
26
26
|
};
|
|
27
|
-
|
|
27
|
+
/** One `search all` hit as a person reads it. */
|
|
28
|
+
export const searchAllLine = (item) => item.kind === "meeting"
|
|
28
29
|
? `meeting · ${item.provider} · ${singleLine(item.title ?? "")} ${item.ref}\n ${item.timestamp ?? "time unknown"} ${singleLine(item.text).slice(0, 200)}\n`
|
|
29
30
|
: `${item.kind} · ${item.provider} · ${singleLine(item.title ?? "")} ${item.ref}${item.url ? `\n ${singleLine(item.url)}` : ""}\n ${item.timestamp}${item.from?.name ? ` ${singleLine(item.from.name)}` : ""} ${singleLine(item.text)}\n`;
|
|
30
31
|
const maxMeetingsOf = (value) => {
|
|
@@ -34,6 +35,19 @@ const maxMeetingsOf = (value) => {
|
|
|
34
35
|
throw new CliError("validation_error", "--max-meetings takes a whole number from 1, or all");
|
|
35
36
|
return Number(value);
|
|
36
37
|
};
|
|
38
|
+
/**
|
|
39
|
+
* `search all`'s scope, paging and matching options, the same in every CLI that mounts it (WC-ACCT-8): a messenger's
|
|
40
|
+
* and a calendar's.
|
|
41
|
+
*/
|
|
42
|
+
export const searchAllOptions = (command) => command
|
|
43
|
+
.option("--only <resources>", "only these resources, separated by commas: messages, notes, events; meetings when --source names a meeting account", searchAllOnlyOf)
|
|
44
|
+
.option("--source <accounts>", "only these accounts, separated by commas: provider:account as a hit shows it, a provider, a kind or " +
|
|
45
|
+
"group:<name>; each resource reads the ones of its kind, and meetings only when it names one")
|
|
46
|
+
.option("--max-meetings <n>", "how many of the meeting account's meetings to look through, newest first, or all for every one (default 100)", maxMeetingsOf)
|
|
47
|
+
.option("--limit <n>", "how many", positiveCount("--limit"))
|
|
48
|
+
.option("--page <n>", "which page, starting at 1", positiveCount("--page"))
|
|
49
|
+
.option("--exact", "bare words and quotes match their exact form only, as exact:word does")
|
|
50
|
+
.option("--timezone <zone>", "the IANA timezone for calendar date boundaries");
|
|
37
51
|
const allCommand = (messenger) => {
|
|
38
52
|
const command = annotate(new Command("all"), { effects: REMOTE_READ, partial: true })
|
|
39
53
|
.summary("search stored messages, notes, calendar events and selected meeting transcripts")
|
|
@@ -48,16 +62,8 @@ const allCommand = (messenger) => {
|
|
|
48
62
|
.argument("<query...>", 'strict Lucene query: words, "phrases", AND/OR/NOT, field groups and date ranges')
|
|
49
63
|
.addHelpText("after", SAVE_HELP)
|
|
50
64
|
.addHelpText("after", `transcript: narrows a message search to voice transcripts, content: to the text of files, filename: to their names — \`${messenger.app.command} search all 'content:invoice'\`.
|
|
51
|
-
--json: the list is items; each result's reference is ref, with its kind.`)
|
|
52
|
-
|
|
53
|
-
.option("--source <accounts>", "only these accounts, separated by commas: provider:account as a hit shows it, a provider, a kind or " +
|
|
54
|
-
"group:<name>; each resource reads the ones of its kind, and meetings only when it names one")
|
|
55
|
-
.option("--max-meetings <n>", "how many of the meeting account's meetings to look through, newest first, or all for every one (default 100)", maxMeetingsOf)
|
|
56
|
-
.option("--limit <n>", "how many", positiveCount("--limit"))
|
|
57
|
-
.option("--page <n>", "which page, starting at 1", positiveCount("--page"))
|
|
58
|
-
.option("--exact", "bare words and quotes match their exact form only, as exact:word does")
|
|
59
|
-
.option("--timezone <zone>", "the IANA timezone for calendar date boundaries");
|
|
60
|
-
return backendOptions(annotate(command, { output: { entity: "hit" } }), messenger).action(async function (words) {
|
|
65
|
+
--json: the list is items; each result's reference is ref, with its kind.`);
|
|
66
|
+
return backendOptions(annotate(searchAllOptions(command), { output: { entity: "hit" } }), messenger).action(async function (words) {
|
|
61
67
|
const context = messengerContext(this, messenger);
|
|
62
68
|
const { only, source, maxMeetings, limit: typed, page, exact, timezone, } = this.opts();
|
|
63
69
|
const limit = typed ?? context.settings.limit;
|
|
@@ -99,7 +105,7 @@ const allCommand = (messenger) => {
|
|
|
99
105
|
context.renderer.note(found.server && !found.server.skipped
|
|
100
106
|
? "nothing found in the local store or on the messenger's server"
|
|
101
107
|
: "nothing found in what the local store holds");
|
|
102
|
-
return found.items.map(
|
|
108
|
+
return found.items.map(searchAllLine).join("");
|
|
103
109
|
});
|
|
104
110
|
});
|
|
105
111
|
};
|
|
@@ -3,9 +3,9 @@ import type { CacheDatabase } from "../../store/driver.js";
|
|
|
3
3
|
import type { Messenger } from "./context.js";
|
|
4
4
|
/** `doctor`'s short summary of the store. Must answer for a broken file, so an error is a field. */
|
|
5
5
|
export declare const storeSummary: (env: NodeJS.ProcessEnv) => Promise<{
|
|
6
|
-
error?: undefined;
|
|
7
6
|
path: string;
|
|
8
7
|
exists: boolean;
|
|
8
|
+
error?: undefined;
|
|
9
9
|
} | {
|
|
10
10
|
path: string;
|
|
11
11
|
exists: boolean;
|
package/dist/cli/program.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { type AppIdentity } from "../ports/app.js";
|
|
3
|
+
import { type RowTable } from "./compact.js";
|
|
3
4
|
import { type BaseEnvironment } from "./context.js";
|
|
4
5
|
import { type FirstScreen } from "./help/first-screen.js";
|
|
5
6
|
import type { CommandPermissions } from "./permission-keys.js";
|
|
@@ -16,6 +17,8 @@ export interface ProgramDefinition {
|
|
|
16
17
|
resolveSettings: (flags?: GlobalFlags, options?: ResolveOptions) => Pick<Settings, "profile" | "keepFailedRuns" | "keepRunsForDays" | "skillHint"> & Partial<Pick<Settings, "recordInput" | "keepSuccessfulRunsForDays" | "permissions" | "permissionSources" | "configPath" | "savedPermissions" | "sources">>;
|
|
17
18
|
};
|
|
18
19
|
configure?: (program: Command) => void;
|
|
20
|
+
/** The app's own rows for `--compact`, by entity name; a shared entity's name is refused. */
|
|
21
|
+
rows?: RowTable;
|
|
19
22
|
/** The tool's pitch and "what you can do" lines on `--help` and a bare run. */
|
|
20
23
|
firstScreen?: FirstScreen;
|
|
21
24
|
prepare?: (program: Command, environment: RunOptions) => void | Promise<void>;
|
package/dist/cli/program.js
CHANGED
|
@@ -2,6 +2,7 @@ import { join } from "node:path";
|
|
|
2
2
|
import { CliError, errorObjectOf, errorRecordOf, exitCodeFor, exitCodeOfError, processStreams, renderError, resolvePaths, } from "@wirecat/cli-base";
|
|
3
3
|
import { metaOf, refuseJoinedPath } from "@wirecat/cli-base/commands";
|
|
4
4
|
import { skillHint } from "@wirecat/cli-base/skill";
|
|
5
|
+
import { COMPACT_FLAG } from "@wirecat/cli-base/standards";
|
|
5
6
|
import { Command, Help } from "commander";
|
|
6
7
|
import { relayCommand } from "../background/relay.js";
|
|
7
8
|
import { failingNotice, scheduledRuns } from "../background/schedules.js";
|
|
@@ -17,6 +18,7 @@ import { liftOptionsAfterDashes } from "./after-dashes.js";
|
|
|
17
18
|
import { logParsedArgv, logVersion } from "./argv-log.js";
|
|
18
19
|
import { commandAvailability } from "./command-availability.js";
|
|
19
20
|
import { commandPathOf } from "./command-contract.js";
|
|
21
|
+
import { entityRows } from "./compact.js";
|
|
20
22
|
import { outputFor, provide } from "./context.js";
|
|
21
23
|
import { refuseByCommandPermissions, refuseWriteBeforeConnecting } from "./early-refusal.js";
|
|
22
24
|
import { changesAuthorityAt, changesAuthorityNow, isLogin, isStoreChange, judgedEffects, judgedTargets, storeChangeRefusal, } from "./effects.js";
|
|
@@ -73,6 +75,7 @@ export const createProgram = (definition, { out, err } = {}) => {
|
|
|
73
75
|
.option("--max-output-bytes <bytes>", "maximum machine output bytes (default: 4194304; 0 disables)")
|
|
74
76
|
.option("--brief", `${found} as reference, title, time, ${messenger ? "sender" : "author"} and excerpt only`)
|
|
75
77
|
.option("--full", `${found} with their whole text and every provider field`)
|
|
78
|
+
.option(COMPACT_FLAG, "one line per item for an agent: reference first, text last, paging and next steps as # lines")
|
|
76
79
|
.option("--dry-run", "preview parsed arguments and permissions before running the action")
|
|
77
80
|
.option("--yes", "go ahead without the question an ask level puts before a write")
|
|
78
81
|
.option("--record", "keep this run even where the config says record false; record_input false leaves the typed words out")
|
|
@@ -82,6 +85,8 @@ export const createProgram = (definition, { out, err } = {}) => {
|
|
|
82
85
|
program.addCommand(command);
|
|
83
86
|
configure?.(program);
|
|
84
87
|
if (!contractsChecked.has(definition)) {
|
|
88
|
+
if (definition.rows)
|
|
89
|
+
entityRows(definition.rows);
|
|
85
90
|
assertSharedContracts(program);
|
|
86
91
|
contractsChecked.add(definition);
|
|
87
92
|
}
|
|
@@ -136,7 +141,7 @@ export const run = async (argv, definition, options = {}) => {
|
|
|
136
141
|
useAsProcessRun(outer);
|
|
137
142
|
}
|
|
138
143
|
};
|
|
139
|
-
const isMachine = (words) => words.some((word) => word === "--json" || word.startsWith("--json=") || word === "--jsonl");
|
|
144
|
+
const isMachine = (words) => words.some((word) => word === "--json" || word.startsWith("--json=") || word === "--jsonl" || word === COMPACT_FLAG);
|
|
140
145
|
const runOnce = async (argv, definition, options) => {
|
|
141
146
|
const streams = options.streams ?? processStreams;
|
|
142
147
|
if (!options.streams)
|
|
@@ -218,6 +223,7 @@ const runOnce = async (argv, definition, options) => {
|
|
|
218
223
|
commandSignal: control.signal,
|
|
219
224
|
trackCloseable: control.trackCloseable,
|
|
220
225
|
app: definition.app,
|
|
226
|
+
...(definition.rows ? { rows: definition.rows } : {}),
|
|
221
227
|
platform: options.platform ?? process.platform,
|
|
222
228
|
exit,
|
|
223
229
|
runCommand: (words, given) => run(words, definition, given),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Provider } from "./models.js";
|
|
2
|
+
import { type RefKindName } from "./references.js";
|
|
2
3
|
/** What a search reads; the same names `--only`, `searched` and `skipped` use. */
|
|
3
4
|
export declare const KINDS: readonly ["messages", "mail", "notes", "documents", "meetings", "events"];
|
|
4
5
|
export type Kind = (typeof KINDS)[number];
|
|
@@ -8,6 +9,8 @@ export interface ProviderEntry {
|
|
|
8
9
|
aliases: readonly string[];
|
|
9
10
|
/** The CLI that wrote the entry (`zm`, `drive`). One CLI may write several providers, so it is not an alias. */
|
|
10
11
|
command?: string;
|
|
12
|
+
/** The read command, after `<command> <profile>`, that opens a ref of each kind: `{ chat: "messages list" }`. */
|
|
13
|
+
opens?: Partial<Record<RefKindName, string>>;
|
|
11
14
|
}
|
|
12
15
|
/** The seed: what cli-core knows before any CLI has written its entry into the store. */
|
|
13
16
|
export declare const PROVIDERS: readonly ProviderEntry[];
|
package/dist/domain/providers.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { CliError } from "@wirecat/cli-base";
|
|
2
|
+
import { REF_KINDS } from "./references.js";
|
|
2
3
|
/** What a search reads; the same names `--only`, `searched` and `skipped` use. */
|
|
3
4
|
export const KINDS = ["messages", "mail", "notes", "documents", "meetings", "events"];
|
|
5
|
+
const MESSENGER_OPENS = { chat: "messages list", message: "messages show", contact: "contacts show" };
|
|
4
6
|
/** The seed: what cli-core knows before any CLI has written its entry into the store. */
|
|
5
7
|
export const PROVIDERS = [
|
|
6
|
-
{ name: "telegram", kind: "messages", aliases: ["tg"], command: "tg" },
|
|
8
|
+
{ name: "telegram", kind: "messages", aliases: ["tg"], command: "tg", opens: MESSENGER_OPENS },
|
|
7
9
|
{ name: "telegram-bot", kind: "messages", aliases: ["tg-bot"] },
|
|
8
|
-
{ name: "max", kind: "messages", aliases: [], command: "max" },
|
|
10
|
+
{ name: "max", kind: "messages", aliases: [], command: "max", opens: MESSENGER_OPENS },
|
|
9
11
|
{ name: "max-bot", kind: "messages", aliases: [] },
|
|
10
|
-
{ name: "whatsapp", kind: "messages", aliases: ["wa"], command: "wa" },
|
|
12
|
+
{ name: "whatsapp", kind: "messages", aliases: ["wa"], command: "wa", opens: MESSENGER_OPENS },
|
|
11
13
|
{ name: "whatsapp-bot", kind: "messages", aliases: ["wa-bot"] },
|
|
12
14
|
{ name: "email", kind: "mail", aliases: [] },
|
|
13
15
|
{ name: "notes", kind: "notes", aliases: [] },
|
|
@@ -58,11 +60,23 @@ const invalid = (message) => {
|
|
|
58
60
|
const wordOf = (value, what) => typeof value === "string" && WORD.test(value)
|
|
59
61
|
? value
|
|
60
62
|
: invalid(`${what} is lowercase letters, digits, - and _ — not ${JSON.stringify(value)}`);
|
|
63
|
+
const OPEN_KINDS = new Set(REF_KINDS.map(({ type }) => type).filter((type) => type !== "entity"));
|
|
64
|
+
const COMMAND_WORDS = /^[a-z0-9][a-z0-9_-]*( [a-z0-9][a-z0-9_-]*)*$/;
|
|
65
|
+
/** A kind this build does not know is dropped, not refused: a newer CLI may open kinds added after this release. */
|
|
66
|
+
const opensOf = (value, named) => {
|
|
67
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
68
|
+
return invalid(`provider ${named}'s opens maps a kind of reference to a command`);
|
|
69
|
+
const known = Object.entries(value).filter(([kind]) => OPEN_KINDS.has(kind));
|
|
70
|
+
for (const [kind, command] of known)
|
|
71
|
+
if (typeof command !== "string" || !COMMAND_WORDS.test(command))
|
|
72
|
+
invalid(`provider ${named} opens a ${kind} with command words, not ${JSON.stringify(command)}`);
|
|
73
|
+
return Object.fromEntries(known);
|
|
74
|
+
};
|
|
61
75
|
/** An entry as a CLI or the store gives it, checked: anything else is a `validation_error`. */
|
|
62
76
|
export const providerEntryOf = (value) => {
|
|
63
77
|
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
64
78
|
return invalid("a provider entry is an object");
|
|
65
|
-
const { name, kind, aliases = [], command } = value;
|
|
79
|
+
const { name, kind, aliases = [], command, opens } = value;
|
|
66
80
|
const named = wordOf(name, "a provider's name");
|
|
67
81
|
if (typeof kind !== "string" || !KINDS.includes(kind))
|
|
68
82
|
invalid(`provider ${named}'s kind is one of ${KINDS.join(", ")} — not ${JSON.stringify(kind)}`);
|
|
@@ -73,6 +87,7 @@ export const providerEntryOf = (value) => {
|
|
|
73
87
|
kind: kind,
|
|
74
88
|
aliases: [...new Set(aliases.map((alias) => wordOf(alias, `an alias of ${named}`)))],
|
|
75
89
|
...(command === undefined ? {} : { command: wordOf(command, `the command of ${named}`) }),
|
|
90
|
+
...(opens === undefined ? {} : { opens: opensOf(opens, named) }),
|
|
76
91
|
};
|
|
77
92
|
};
|
|
78
93
|
const describe = ({ name, kind, command }) => `${name} (${kind}${command ? `, from ${command}` : ""})`;
|
|
@@ -87,6 +102,7 @@ const joined = (known, entry) => {
|
|
|
87
102
|
...same,
|
|
88
103
|
aliases: [...new Set([...same.aliases, ...entry.aliases])],
|
|
89
104
|
...(entry.command ? { command: entry.command } : {}),
|
|
105
|
+
...(entry.opens ? { opens: { ...same.opens, ...entry.opens } } : {}),
|
|
90
106
|
}
|
|
91
107
|
: entry;
|
|
92
108
|
const others = known.filter((one) => one !== same);
|
|
@@ -5,18 +5,14 @@ const TAKES = {
|
|
|
5
5
|
chat: "a chat: chat:<provider>/<account>/<chat>, its id or its title",
|
|
6
6
|
message: "a message: msg:<provider>/<account>/<chat>/<message>, or a chat and the message's id",
|
|
7
7
|
};
|
|
8
|
-
const OPENS = {
|
|
9
|
-
chat: "messages list",
|
|
10
|
-
message: "messages show",
|
|
11
|
-
contact: "contacts show",
|
|
12
|
-
};
|
|
13
8
|
const article = (word) => (/^[aeiou]/.test(word) ? "an" : "a");
|
|
14
9
|
const wrongKind = (text, kind, takes) => new CliError("validation_error", `${text} names ${article(kind)} ${kind}; this takes ${TAKES[takes]}`, {
|
|
15
10
|
reason: "ref.wrong-kind",
|
|
16
11
|
});
|
|
17
12
|
const wrongProvider = (text, provider, kind) => {
|
|
18
|
-
const
|
|
19
|
-
const
|
|
13
|
+
const entry = providerNamed(provider);
|
|
14
|
+
const command = entry?.command;
|
|
15
|
+
const opens = kind === "entity" ? undefined : entry?.opens?.[kind];
|
|
20
16
|
const how = command && opens ? `open it with ${command} <profile> ${opens} ${text}` : "open it with its own CLI";
|
|
21
17
|
const label = providerLabel(provider);
|
|
22
18
|
return new CliError("validation_error", `${text} is ${article(label.toLowerCase())} ${label} ${kind}; ${how}`, {
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { RefKindName } from "./references.js";
|
|
2
|
+
export type RefState = "available" | "deleted" | "unavailable";
|
|
3
|
+
/** A message or chat of the `email` provider is mail, so it reads as `email` and `email_thread`. */
|
|
4
|
+
export type RefSummaryKind = RefKindName | "email" | "email_thread" | "unknown";
|
|
5
|
+
/** What a reference names, read from the store in one batch so a page of refs costs one query per kind. */
|
|
6
|
+
export interface RefSummary {
|
|
7
|
+
/** Canonical spelling; the map key is the text the caller passed. */
|
|
8
|
+
ref: string;
|
|
9
|
+
kind: RefSummaryKind;
|
|
10
|
+
/** One line, at most 120 characters, never a message body. `null` when the store has no title. */
|
|
11
|
+
title: string | null;
|
|
12
|
+
/** What holds it, one line: a message's chat title, a task's project, a document's provider label. */
|
|
13
|
+
in: string | null;
|
|
14
|
+
/** ISO 8601 UTC. */
|
|
15
|
+
at: string | null;
|
|
16
|
+
provider: string | null;
|
|
17
|
+
/** The external id of the account that holds it, for `ProfilesFor`; `null` when it belongs to no account. */
|
|
18
|
+
account: string | null;
|
|
19
|
+
/** The CLI that opens it (`max`, `tg`, `drive`); `null` when no CLI has registered the provider. */
|
|
20
|
+
tool: string | null;
|
|
21
|
+
state: RefState;
|
|
22
|
+
/** Only with `{ excerpt: true }`: at most 280 characters of the body. */
|
|
23
|
+
excerpt?: string | null;
|
|
24
|
+
}
|
|
25
|
+
export interface RefsStore {
|
|
26
|
+
/** One query per kind present. Never throws for a bad ref: it comes back `unknown`, `unavailable`. */
|
|
27
|
+
summaries(refs: readonly string[], options?: {
|
|
28
|
+
excerpt?: boolean;
|
|
29
|
+
}): Promise<Map<string, RefSummary>>;
|
|
30
|
+
}
|
|
31
|
+
/** The caller's answer to "which profile opens this": one signed in, or the choices and why there is not one. */
|
|
32
|
+
export type ProfilesFor = (provider: string, account: string | null) => {
|
|
33
|
+
profile: string;
|
|
34
|
+
} | {
|
|
35
|
+
why: string;
|
|
36
|
+
profiles: string[];
|
|
37
|
+
};
|
|
38
|
+
export interface RefOpen {
|
|
39
|
+
/** Ready to run; contains `<profile>` when `profiles` is not exactly one. `null` when no CLI opens it. */
|
|
40
|
+
argv: string[] | null;
|
|
41
|
+
profiles: string[];
|
|
42
|
+
why: string | null;
|
|
43
|
+
}
|
|
44
|
+
export declare const PROFILE_PLACEHOLDER = "<profile>";
|
|
45
|
+
/** A title from someone else's text, as one line: a newline in it would break every row it is printed in. */
|
|
46
|
+
export declare const oneLine: (text: string) => string;
|
|
47
|
+
/**
|
|
48
|
+
* The command that opens a ref. A registered provider opens its own kinds; anything else opens with the caller's
|
|
49
|
+
* `local` words (memo's `notes show`). The profile is never picked: with none or several, `argv` keeps `<profile>`.
|
|
50
|
+
*/
|
|
51
|
+
export declare const openOf: (summary: RefSummary, ports: {
|
|
52
|
+
profilesFor: ProfilesFor;
|
|
53
|
+
local?: Partial<Record<RefKindName, string[]>>;
|
|
54
|
+
}) => RefOpen;
|
|
55
|
+
/** `Chat «Сайт + автоматизация» (MAX)`: the one label every review row, status line and `show` prints. */
|
|
56
|
+
export declare const refLabel: (summary: RefSummary) => string;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { singleLine } from "@wirecat/cli-base";
|
|
2
|
+
import { providerNamed } from "./providers.js";
|
|
3
|
+
export const PROFILE_PLACEHOLDER = "<profile>";
|
|
4
|
+
/** A title from someone else's text, as one line: a newline in it would break every row it is printed in. */
|
|
5
|
+
export const oneLine = (text) => singleLine(text.replace(/\s+/g, " ").trim());
|
|
6
|
+
/** Mail is a message and a chat to the provider registry: `opens` is keyed by the reference kind. */
|
|
7
|
+
const openKind = (kind) => kind === "email" ? "message" : kind === "email_thread" ? "chat" : kind === "unknown" ? undefined : kind;
|
|
8
|
+
/**
|
|
9
|
+
* The command that opens a ref. A registered provider opens its own kinds; anything else opens with the caller's
|
|
10
|
+
* `local` words (memo's `notes show`). The profile is never picked: with none or several, `argv` keeps `<profile>`.
|
|
11
|
+
*/
|
|
12
|
+
export const openOf = (summary, ports) => {
|
|
13
|
+
const kind = openKind(summary.kind);
|
|
14
|
+
if (kind === undefined)
|
|
15
|
+
return { argv: null, profiles: [], why: `${summary.ref} is not a reference` };
|
|
16
|
+
const entry = summary.provider === null ? undefined : providerNamed(summary.provider);
|
|
17
|
+
const words = entry?.command === undefined ? undefined : entry.opens?.[kind];
|
|
18
|
+
if (entry?.command !== undefined && words !== undefined && summary.provider !== null) {
|
|
19
|
+
const command = [...words.split(" "), summary.ref];
|
|
20
|
+
const asked = ports.profilesFor(summary.provider, summary.account);
|
|
21
|
+
return "profile" in asked
|
|
22
|
+
? { argv: [entry.command, asked.profile, ...command], profiles: [asked.profile], why: null }
|
|
23
|
+
: { argv: [entry.command, PROFILE_PLACEHOLDER, ...command], profiles: asked.profiles, why: asked.why };
|
|
24
|
+
}
|
|
25
|
+
const local = ports.local?.[kind];
|
|
26
|
+
if (local !== undefined)
|
|
27
|
+
return { argv: [...local, summary.ref], profiles: [], why: null };
|
|
28
|
+
return {
|
|
29
|
+
argv: null,
|
|
30
|
+
profiles: [],
|
|
31
|
+
why: summary.provider === null
|
|
32
|
+
? `nothing here opens a ${kind}`
|
|
33
|
+
: `no CLI has registered ${summary.provider} in this store yet; run its CLI once`,
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
const WORDS = {
|
|
37
|
+
message: "Message",
|
|
38
|
+
chat: "Chat",
|
|
39
|
+
contact: "Contact",
|
|
40
|
+
folder: "Folder",
|
|
41
|
+
meeting: "Meeting",
|
|
42
|
+
event: "Event",
|
|
43
|
+
note: "Note",
|
|
44
|
+
document: "Document",
|
|
45
|
+
person: "Person",
|
|
46
|
+
organization: "Organization",
|
|
47
|
+
project: "Project",
|
|
48
|
+
task: "Task",
|
|
49
|
+
memory: "Memory",
|
|
50
|
+
fact: "Fact",
|
|
51
|
+
bot: "Bot",
|
|
52
|
+
email: "Email",
|
|
53
|
+
email_thread: "Email thread",
|
|
54
|
+
unknown: "Unknown",
|
|
55
|
+
};
|
|
56
|
+
/** `Chat «Сайт + автоматизация» (MAX)`: the one label every review row, status line and `show` prints. */
|
|
57
|
+
export const refLabel = (summary) => {
|
|
58
|
+
const named = summary.title === null ? summary.ref : `«${oneLine(summary.title)}»`;
|
|
59
|
+
const notes = [summary.in === null ? null : oneLine(summary.in), summary.state === "available" ? null : summary.state]
|
|
60
|
+
.filter((one) => one !== null)
|
|
61
|
+
.join(", ");
|
|
62
|
+
return `${WORDS[summary.kind]} ${named}${notes === "" ? "" : ` (${notes})`}`;
|
|
63
|
+
};
|