@wirecat/cli-core 0.550.0 → 0.552.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 +235 -0
- package/README.md +1 -1
- package/THIRD_PARTY_NOTICES +41 -0
- package/dist/background/schedules.d.ts +12 -1
- package/dist/background/schedules.js +65 -24
- package/dist/background/units.d.ts +16 -9
- package/dist/background/units.js +26 -11
- package/dist/calendar/ics.d.ts +1 -1
- package/dist/calendar/ics.js +5 -2
- package/dist/calendar/windows-zones.generated.d.ts +1 -0
- package/dist/calendar/windows-zones.generated.js +146 -0
- package/dist/cli/compact.js +2 -0
- package/dist/cli/context.d.ts +2 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +3 -0
- package/dist/cli/meeting-findings-input.js +4 -3
- package/dist/cli/meeting-preparation-answer.d.ts +3 -0
- package/dist/cli/meeting-preparation-projects.d.ts +3 -0
- package/dist/cli/meeting-preparation-read.d.ts +3 -0
- package/dist/cli/messenger/backfill-command.js +28 -1
- package/dist/cli/messenger/context.js +1 -0
- package/dist/cli/messenger/history-gaps.js +4 -4
- package/dist/cli/messenger/mcp-command.js +1 -0
- package/dist/cli/messenger/messages-delete-command.js +7 -8
- package/dist/cli/messenger/projects-command.js +47 -2
- package/dist/cli/messenger/search-answers.js +8 -1
- package/dist/cli/messenger/serve-catch-up.d.ts +43 -0
- package/dist/cli/messenger/serve-catch-up.js +90 -0
- package/dist/cli/messenger/serve-command.js +54 -1
- package/dist/cli/messenger/server-command.d.ts +4 -3
- package/dist/cli/messenger/server-command.js +30 -12
- package/dist/cli/payment-query.d.ts +8 -0
- package/dist/cli/payment-query.js +27 -0
- package/dist/cli/program.js +9 -4
- package/dist/cli/project-status-rows.d.ts +8 -0
- package/dist/cli/project-status-rows.js +102 -0
- package/dist/cli/project-status.d.ts +132 -0
- package/dist/cli/project-status.js +248 -0
- package/dist/cli/schedule/schedule-command.js +5 -1
- package/dist/cli/steps.js +4 -1
- package/dist/domain/knowledge.d.ts +1 -1
- package/dist/domain/knowledge.js +1 -1
- package/dist/domain/status-words.d.ts +3 -0
- package/dist/domain/status-words.js +44 -0
- package/dist/entities/fact.d.ts +12 -1
- package/dist/entities/fact.js +13 -0
- package/dist/entities/hit.d.ts +5 -0
- package/dist/entities/hit.js +5 -0
- package/dist/entities/index.d.ts +45 -2
- package/dist/entities/listener.d.ts +6 -0
- package/dist/entities/listener.js +1 -0
- package/dist/entities/meeting-findings.d.ts +1 -1
- package/dist/entities/meeting-preparation.d.ts +3 -0
- package/dist/entities/message-transfer.d.ts +16 -0
- package/dist/entities/message-transfer.js +6 -0
- package/dist/errors/reasons.js +1 -0
- package/dist/mcp/app-server.js +1 -0
- package/dist/mcp/http/oauth.d.ts +2 -1
- package/dist/mcp/http/oauth.js +2 -2
- package/dist/mcp/http/serve.d.ts +2 -0
- package/dist/mcp/http/serve.js +2 -1
- package/dist/mcp/meeting-preparation.d.ts +3 -0
- package/dist/mcp/server.js +1 -0
- package/dist/mcp/surface.js +1 -1
- package/dist/meetings/answers.d.ts +5 -0
- package/dist/meetings/answers.js +8 -0
- package/dist/meetings/cli/index.js +14 -0
- package/dist/meetings/findings-rules.d.ts +14 -3
- package/dist/meetings/findings-rules.js +16 -2
- package/dist/meetings/index.d.ts +5 -3
- package/dist/meetings/index.js +3 -1
- package/dist/meetings/ingest.js +2 -1
- package/dist/meetings/language.d.ts +5 -0
- package/dist/meetings/language.js +57 -0
- package/dist/meetings/mcp/search.d.ts +3 -0
- package/dist/meetings/meeting-context.d.ts +2 -3
- package/dist/meetings/meeting-context.js +3 -4
- package/dist/meetings/payment.d.ts +27 -0
- package/dist/meetings/payment.js +81 -0
- package/dist/meetings/project-evidence-answer.d.ts +3 -0
- package/dist/meetings/result-items.d.ts +9 -0
- package/dist/meetings/result-items.js +7 -0
- package/dist/meetings/store.d.ts +22 -0
- package/dist/meetings/testing/memory.js +3 -0
- package/dist/meetings/transcription.d.ts +2 -0
- package/dist/meetings/transcription.js +5 -1
- package/dist/search/lucene/types.d.ts +7 -0
- package/dist/search/lucene/types.js +11 -1
- package/dist/services/archive.d.ts +13 -0
- package/dist/services/archive.js +56 -5
- package/dist/services/deps.d.ts +3 -0
- package/dist/services/history-limits.d.ts +2 -0
- package/dist/services/index.d.ts +4 -1
- package/dist/services/index.js +4 -1
- package/dist/services/job-query.d.ts +5 -2
- package/dist/services/job-query.js +5 -2
- package/dist/services/link-scorers/alias-hits.d.ts +7 -0
- package/dist/services/link-scorers/alias-hits.js +54 -0
- package/dist/services/link-scorers/chat-senders.d.ts +6 -0
- package/dist/services/link-scorers/chat-senders.js +56 -0
- package/dist/services/link-scorers/chat-title.d.ts +6 -0
- package/dist/services/link-scorers/chat-title.js +44 -0
- package/dist/services/link-scorers/email-domain.d.ts +6 -0
- package/dist/services/link-scorers/email-domain.js +64 -0
- package/dist/services/link-scorers/index.d.ts +9 -0
- package/dist/services/link-scorers/index.js +8 -0
- package/dist/services/link-scorers/mail-thread.d.ts +3 -0
- package/dist/services/link-scorers/mail-thread.js +74 -0
- package/dist/services/link-scorers/member-chats.d.ts +3 -0
- package/dist/services/link-scorers/member-chats.js +40 -0
- package/dist/services/link-scorers/member-dialog.d.ts +3 -0
- package/dist/services/link-scorers/member-dialog.js +34 -0
- package/dist/services/link-scorers/scan.d.ts +41 -0
- package/dist/services/link-scorers/scan.js +244 -0
- package/dist/services/link-scorers/shared.d.ts +26 -0
- package/dist/services/link-scorers/shared.js +48 -0
- package/dist/services/link-scorers/types.d.ts +77 -0
- package/dist/services/link-scorers/types.js +1 -0
- package/dist/services/messages-archive-search.js +3 -2
- package/dist/services/messages-combined.js +2 -2
- package/dist/services/messages-discovery.js +2 -2
- package/dist/services/notes-search.js +4 -5
- package/dist/services/project-status/index.d.ts +71 -0
- package/dist/services/project-status/index.js +196 -0
- package/dist/services/project-status/items.d.ts +10 -0
- package/dist/services/project-status/items.js +23 -0
- package/dist/services/project-status/signals/amounts.d.ts +20 -0
- package/dist/services/project-status/signals/amounts.js +91 -0
- package/dist/services/project-status/signals/children.d.ts +13 -0
- package/dist/services/project-status/signals/children.js +28 -0
- package/dist/services/project-status/signals/coverage.d.ts +13 -0
- package/dist/services/project-status/signals/coverage.js +30 -0
- package/dist/services/project-status/signals/index.d.ts +3 -0
- package/dist/services/project-status/signals/index.js +21 -0
- package/dist/services/project-status/signals/last-message.d.ts +4 -0
- package/dist/services/project-status/signals/last-message.js +22 -0
- package/dist/services/project-status/signals/payments.d.ts +9 -0
- package/dist/services/project-status/signals/payments.js +24 -0
- package/dist/services/project-status/signals/promises.d.ts +17 -0
- package/dist/services/project-status/signals/promises.js +109 -0
- package/dist/services/project-status/signals/questions.d.ts +8 -0
- package/dist/services/project-status/signals/questions.js +38 -0
- package/dist/services/project-status/signals/reply-next.d.ts +19 -0
- package/dist/services/project-status/signals/reply-next.js +75 -0
- package/dist/services/project-status/signals/topics.d.ts +10 -0
- package/dist/services/project-status/signals/topics.js +50 -0
- package/dist/services/project-status/types.d.ts +84 -0
- package/dist/services/project-status/types.js +1 -0
- package/dist/services/proposals/index.d.ts +1 -0
- package/dist/services/proposals/index.js +1 -0
- package/dist/services/proposals/kinds/findings.d.ts +3 -0
- package/dist/services/proposals/kinds/findings.js +8 -1
- package/dist/services/search-all-meetings.d.ts +3 -0
- package/dist/services/search-all-meetings.js +6 -1
- package/dist/speech/local.d.ts +2 -0
- package/dist/speech/local.js +8 -1
- package/dist/speech/models.d.ts +2 -0
- package/dist/speech/models.js +2 -0
- package/dist/store/decisions.d.ts +5 -1
- package/dist/store/decisions.js +5 -1
- package/dist/store/driver.d.ts +11 -0
- package/dist/store/driver.js +13 -0
- package/dist/store/drivers/node-sqlite.d.ts +1 -1
- package/dist/store/drivers/node-sqlite.js +3 -0
- package/dist/store/evidence-findings.js +1 -0
- package/dist/store/index.d.ts +2 -1
- package/dist/store/index.js +1 -0
- package/dist/store/project-transfer/request.d.ts +1 -1
- package/dist/store/project-transfer/schema.d.ts +2 -2
- package/dist/store/project-transfer/snapshot-model.d.ts +2 -2
- package/dist/store/sqlite/evidence-findings.js +5 -1
- package/dist/store/sqlite/facts.d.ts +13 -0
- package/dist/store/sqlite/facts.js +64 -6
- package/dist/store/sqlite/findings-core.js +14 -1
- package/dist/store/sqlite/knowledge.d.ts +16 -2
- package/dist/store/sqlite/knowledge.js +1 -0
- package/dist/store/sqlite/lucene.d.ts +1 -1
- package/dist/store/sqlite/lucene.js +33 -7
- package/dist/store/sqlite/meeting-hit-search.js +10 -3
- package/dist/store/sqlite/meeting-languages.d.ts +4 -0
- package/dist/store/sqlite/meeting-languages.js +36 -0
- package/dist/store/sqlite/meetings.d.ts +2 -1
- package/dist/store/sqlite/meetings.js +9 -3
- package/dist/store/sqlite/note-search.js +2 -2
- package/dist/store/sqlite/project-transfer/snapshot.d.ts +1 -1
- package/dist/store/sqlite/relation-proposals.d.ts +29 -2
- package/dist/store/sqlite/relation-proposals.js +75 -4
- package/dist/store/store.d.ts +1 -0
- package/package.json +3 -3
- package/parity.json +11 -0
|
@@ -2,7 +2,9 @@ import { CliError } from "@wirecat/cli-base";
|
|
|
2
2
|
import { annotate } from "@wirecat/cli-base/commands";
|
|
3
3
|
import { Command } from "commander";
|
|
4
4
|
import { holdLock, lockPath, releaseLock, takeLock } from "../../background/lock.js";
|
|
5
|
+
import { parseDuration } from "../../domain/duration.js";
|
|
5
6
|
import { failureKey } from "../../errors/failures.js";
|
|
7
|
+
import { capability } from "../../ports/messenger.js";
|
|
6
8
|
import { senderFacts } from "../../replies/dry-run.js";
|
|
7
9
|
import { replyRenderer } from "../../replies/rendering.js";
|
|
8
10
|
import { audienceWarnings, readReplies, repliesPathFor } from "../../replies/rules.js";
|
|
@@ -10,14 +12,17 @@ import { NO_RULES, replyTo } from "../../replies/serve.js";
|
|
|
10
12
|
import { repliesStatePathFor } from "../../replies/state.js";
|
|
11
13
|
import { levelFor } from "../../sends/permissions.js";
|
|
12
14
|
import { newSendId } from "../../sends/send-id.js";
|
|
15
|
+
import { FETCHING } from "../../services/archive.js";
|
|
13
16
|
import { onlineDeps } from "../../services/deps.js";
|
|
14
17
|
import { servicesFor } from "../../services/index.js";
|
|
18
|
+
import { patiently } from "../../services/patience.js";
|
|
15
19
|
import { applyTaskRulesOnArrival, openRequestTask } from "../../services/task-rules.js";
|
|
16
20
|
import { environmentOf } from "../context.js";
|
|
17
21
|
import { REMOTE_WRITE } from "../effects.js";
|
|
18
22
|
import { itemOf, renderAnswer } from "../envelope.js";
|
|
19
23
|
import { recalledAccount } from "./accounts.js";
|
|
20
24
|
import { messengerContext } from "./context.js";
|
|
25
|
+
import { START_SYNC_PER_CHAT, startSync } from "./serve-catch-up.js";
|
|
21
26
|
import { memberFetches } from "./serve-members.js";
|
|
22
27
|
import { noticeFor, recordDiagnostics } from "./serve-notice.js";
|
|
23
28
|
import { serveRelay } from "./serve-relay.js";
|
|
@@ -75,7 +80,7 @@ export const serveCommand = (messenger) => {
|
|
|
75
80
|
const counts = {};
|
|
76
81
|
const rules = replying(context, messenger, Date.parse(startedAt));
|
|
77
82
|
const tasks = tasking(context, warn);
|
|
78
|
-
const { handled } = environmentOf(this);
|
|
83
|
+
const { handled, startSyncDelayMs } = environmentOf(this);
|
|
79
84
|
const count = (event) => {
|
|
80
85
|
counts[event.event] = (counts[event.event] ?? 0) + 1;
|
|
81
86
|
if (event.event === "message") {
|
|
@@ -99,6 +104,50 @@ export const serveCommand = (messenger) => {
|
|
|
99
104
|
}).chats.fetchMembers(chatId, {}),
|
|
100
105
|
warn,
|
|
101
106
|
});
|
|
107
|
+
const fetching = messenger.fetching ?? FETCHING;
|
|
108
|
+
const catchingUp = startSync({
|
|
109
|
+
chats: async (stop) => {
|
|
110
|
+
const list = capability(connection, "chats", "list its chats");
|
|
111
|
+
const all = [];
|
|
112
|
+
for (let offset = 0;;) {
|
|
113
|
+
const page = await patiently(() => list({ offset, limit: 100 }), context.renderer.note, stop);
|
|
114
|
+
for (const chat of page.items)
|
|
115
|
+
all.push(chat);
|
|
116
|
+
offset += page.items.length;
|
|
117
|
+
if (!page.hasMore || page.items.length === 0)
|
|
118
|
+
return all;
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
newestHeld: (chatId) => context.withStore(async (store, account) => {
|
|
122
|
+
// What this serve stored itself would hide the gap before it.
|
|
123
|
+
const [newest] = await store.messages.messagesWindow(account, chatId, {
|
|
124
|
+
at: startedAt,
|
|
125
|
+
before: 1,
|
|
126
|
+
after: 0,
|
|
127
|
+
});
|
|
128
|
+
return newest ? Date.parse(newest.timestamp) : undefined;
|
|
129
|
+
}, { name: "serve catch-up" }),
|
|
130
|
+
fetch: (chatId, sinceMs, stop) => context.withStore((store, account) => servicesFor({
|
|
131
|
+
...onlineDeps(messenger, connection, context.guard, {
|
|
132
|
+
profile: context.profile,
|
|
133
|
+
env: context.env,
|
|
134
|
+
}),
|
|
135
|
+
store: async () => store,
|
|
136
|
+
account: async () => account,
|
|
137
|
+
}).archive.fetch(chatId, {
|
|
138
|
+
limit: START_SYNC_PER_CHAT,
|
|
139
|
+
pageSize: fetching.page,
|
|
140
|
+
pauseMs: parseDuration(fetching.pause, "pause"),
|
|
141
|
+
sinceMs,
|
|
142
|
+
catchUp: false,
|
|
143
|
+
note: context.renderer.note,
|
|
144
|
+
stop,
|
|
145
|
+
onPage: () => { },
|
|
146
|
+
}), { name: "serve catch-up" }),
|
|
147
|
+
note,
|
|
148
|
+
warn,
|
|
149
|
+
...(startSyncDelayMs === undefined ? {} : { delayMs: startSyncDelayMs }),
|
|
150
|
+
});
|
|
102
151
|
const stems = stemFills({
|
|
103
152
|
withStore: (work) => context.withStore((store) => work(store), { name: "serve stems" }),
|
|
104
153
|
warn,
|
|
@@ -113,6 +162,8 @@ export const serveCommand = (messenger) => {
|
|
|
113
162
|
rules.connected(open);
|
|
114
163
|
members.start();
|
|
115
164
|
stems.start();
|
|
165
|
+
if (messenger.history !== "store" && typeof open.chats === "function")
|
|
166
|
+
catchingUp.start();
|
|
116
167
|
},
|
|
117
168
|
});
|
|
118
169
|
}
|
|
@@ -120,6 +171,7 @@ export const serveCommand = (messenger) => {
|
|
|
120
171
|
await relaying;
|
|
121
172
|
await relay?.close();
|
|
122
173
|
rules.stop();
|
|
174
|
+
await catchingUp.stop();
|
|
123
175
|
await members.stop();
|
|
124
176
|
await stems.stop();
|
|
125
177
|
await rules.settled();
|
|
@@ -135,6 +187,7 @@ export const serveCommand = (messenger) => {
|
|
|
135
187
|
...rules.summary(),
|
|
136
188
|
...(members.summary().fetched + members.summary().failed > 0 ? { members: members.summary() } : {}),
|
|
137
189
|
...(stems.summary().stemmed > 0 ? { stems: stems.summary() } : {}),
|
|
190
|
+
...(catchingUp.summary() ? { caught_up: catchingUp.summary() } : {}),
|
|
138
191
|
...tasks.summary(),
|
|
139
192
|
}));
|
|
140
193
|
});
|
|
@@ -35,8 +35,9 @@ export interface ServerOptions {
|
|
|
35
35
|
/**
|
|
36
36
|
* `serve` in the background: `start`, `stop`, `restart`, `status`, `logs`. Without a unit, `start`
|
|
37
37
|
* runs `serve` as a process of its own; with one — `install`, a systemd user unit or a launchd
|
|
38
|
-
* agent — it goes through systemd or launchd. **Nothing starts until `start` is typed** (NEED-9):
|
|
39
|
-
* `install`
|
|
40
|
-
*
|
|
38
|
+
* agent — it goes through systemd or launchd. **Nothing starts now until `start` is typed** (NEED-9):
|
|
39
|
+
* `install` writes the file and enables it, so it starts with every session from the next login (or boot,
|
|
40
|
+
* under linger) until `uninstall`. What the server does stays each CLI's; `options.process` says how to find,
|
|
41
|
+
* start and stop it.
|
|
41
42
|
*/
|
|
42
43
|
export declare const serverCommand: (messenger: Messenger, options?: ServerOptions) => Command;
|
|
@@ -54,9 +54,10 @@ const lockProcess = (app, marker, serveArgv) => (context, system, logPath) => ({
|
|
|
54
54
|
/**
|
|
55
55
|
* `serve` in the background: `start`, `stop`, `restart`, `status`, `logs`. Without a unit, `start`
|
|
56
56
|
* runs `serve` as a process of its own; with one — `install`, a systemd user unit or a launchd
|
|
57
|
-
* agent — it goes through systemd or launchd. **Nothing starts until `start` is typed** (NEED-9):
|
|
58
|
-
* `install`
|
|
59
|
-
*
|
|
57
|
+
* agent — it goes through systemd or launchd. **Nothing starts now until `start` is typed** (NEED-9):
|
|
58
|
+
* `install` writes the file and enables it, so it starts with every session from the next login (or boot,
|
|
59
|
+
* under linger) until `uninstall`. What the server does stays each CLI's; `options.process` says how to find,
|
|
60
|
+
* start and stop it.
|
|
60
61
|
*/
|
|
61
62
|
export const serverCommand = (messenger, options = {}) => {
|
|
62
63
|
const { app } = messenger;
|
|
@@ -226,8 +227,11 @@ export const serverCommand = (messenger, options = {}) => {
|
|
|
226
227
|
: undefined;
|
|
227
228
|
const gaveUpReason = gaveUp === undefined ? undefined : Object.entries(EXIT_CODES).find(([, code]) => code === gaveUp)?.[0];
|
|
228
229
|
const flood = new FloodMemory(floodPathFor(app, unit.profile, context.env)).read();
|
|
230
|
+
const startsAtLogin = isInstalled && state.enabled === true;
|
|
229
231
|
const unitLine = isInstalled
|
|
230
|
-
? `Unit: ${tilde(unit.path, context.env)} — ${state.detail ?? (state.active ? "active" : "inactive")}
|
|
232
|
+
? `Unit: ${tilde(unit.path, context.env)} — ${state.detail ?? (state.active ? "active" : "inactive")}, ${startsAtLogin
|
|
233
|
+
? "starts at login."
|
|
234
|
+
: `does not start at login — \`${app.command} ${unit.profile} server install\` turns that on.`}`
|
|
231
235
|
: `No unit installed — \`${app.command} ${unit.profile} server install\` adds one, for starting under systemd or launchd.`;
|
|
232
236
|
say({
|
|
233
237
|
profile: unit.profile,
|
|
@@ -248,6 +252,7 @@ export const serverCommand = (messenger, options = {}) => {
|
|
|
248
252
|
...(gaveUp !== undefined
|
|
249
253
|
? { stopped: { exitCode: gaveUp, reason: gaveUpReason ?? null, restarts: false } }
|
|
250
254
|
: {}),
|
|
255
|
+
startsAtLogin,
|
|
251
256
|
unit: { name: unit.name, path: unit.path, installed: isInstalled, ...state },
|
|
252
257
|
flood: { deadlines: flood.deadlines, sendBlock: flood.sendBlock ?? null },
|
|
253
258
|
}, [
|
|
@@ -293,16 +298,28 @@ export const serverCommand = (messenger, options = {}) => {
|
|
|
293
298
|
effects: LOCAL_WRITE,
|
|
294
299
|
examples: [`${app.command} personal server install`],
|
|
295
300
|
}))
|
|
296
|
-
.
|
|
297
|
-
.
|
|
298
|
-
|
|
301
|
+
.summary("install this profile's background server to start at every login")
|
|
302
|
+
.description("write a systemd user unit or a launchd agent for this profile and have it start at every login; starts nothing now")
|
|
303
|
+
.action(async function () {
|
|
304
|
+
const { context, platform, unit, each, say } = prepare(this);
|
|
299
305
|
const replaced = existsSync(unit.path);
|
|
300
306
|
writeSecurely(unit.path, platform.text(unit, app), 0o644);
|
|
301
307
|
platform.prepare?.(unit);
|
|
302
|
-
|
|
303
|
-
|
|
308
|
+
// A unit of moved folders runs a development checkout, which must not start at every login on the owner's keyring.
|
|
309
|
+
const moved = Object.keys(locationVariables(app, context.env)).length > 0;
|
|
310
|
+
if (!moved)
|
|
311
|
+
await each(platform.install(unit));
|
|
312
|
+
say({
|
|
313
|
+
profile: unit.profile,
|
|
314
|
+
unit: unit.name,
|
|
315
|
+
path: unit.path,
|
|
316
|
+
command: unit.command,
|
|
317
|
+
replaced,
|
|
318
|
+
startsAtLogin: !moved,
|
|
319
|
+
}, [
|
|
320
|
+
`${replaced ? "Rewrote" : "Wrote"} ${tilde(unit.path, context.env)} — ${moved ? "with moved folders it does not start at login" : "it starts at every login"}, nothing started now.`,
|
|
304
321
|
`It runs: ${unit.command.map((word) => tilde(word, context.env)).join(" ")}`,
|
|
305
|
-
`Start it now: ${app.command} ${unit.profile} server start · ${
|
|
322
|
+
`Start it now: ${app.command} ${unit.profile} server start · never at login: ${app.command} ${unit.profile} server uninstall`,
|
|
306
323
|
// Suggested, never turned on: an agent reading chats every morning is the owner's choice (NEED-161).
|
|
307
324
|
`A daily follow-up review is off. To turn it on: ${app.command} ${unit.profile} schedule agent follow-ups claude "${followUpsPrompt(app.command)}" --at 08:30`,
|
|
308
325
|
]);
|
|
@@ -311,13 +328,14 @@ export const serverCommand = (messenger, options = {}) => {
|
|
|
311
328
|
effects: LOCAL_WRITE,
|
|
312
329
|
examples: [`${app.command} personal server uninstall`],
|
|
313
330
|
}))
|
|
314
|
-
.description("remove this profile's unit; stop it first")
|
|
331
|
+
.description("remove this profile's unit, so it no longer starts at login; stop it first")
|
|
315
332
|
.action(async function () {
|
|
316
|
-
const { context, platform, unit, say } = prepare(this);
|
|
333
|
+
const { context, platform, unit, each, say } = prepare(this);
|
|
317
334
|
installed(unit);
|
|
318
335
|
if ((await platform.state(unit)).active) {
|
|
319
336
|
throw new CliError("validation_error", `the unit is running — \`${app.command} ${unit.profile} server stop\` first`);
|
|
320
337
|
}
|
|
338
|
+
await each(platform.uninstall(unit));
|
|
321
339
|
rmSync(unit.path, { force: true });
|
|
322
340
|
say({ profile: unit.profile, unit: unit.name, path: unit.path, removed: true }, [
|
|
323
341
|
`Removed ${tilde(unit.path, context.env)}.`,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { PaymentFilter } from "../store/store.js";
|
|
2
|
+
/**
|
|
3
|
+
* The terms a facts list adds for payments. `state` is the payment's, not `status`, which stays the review status
|
|
4
|
+
* (proposed, accepted).
|
|
5
|
+
*/
|
|
6
|
+
export declare const PAYMENT_QUERY_FIELDS: import("@wirecat/cli-base/query").FieldSpec[];
|
|
7
|
+
/** The store's payment filter from `queryTerms`' answer; `undefined` when no payment term was given. */
|
|
8
|
+
export declare const paymentFilterOf: (terms: Map<string, string>) => PaymentFilter | undefined;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { CliError } from "@wirecat/cli-base";
|
|
2
|
+
import { PAYMENT_DIRECTIONS, PAYMENT_STATES } from "../meetings/index.js";
|
|
3
|
+
import { termField } from "./list-query.js";
|
|
4
|
+
/**
|
|
5
|
+
* The terms a facts list adds for payments. `state` is the payment's, not `status`, which stays the review status
|
|
6
|
+
* (proposed, accepted).
|
|
7
|
+
*/
|
|
8
|
+
export const PAYMENT_QUERY_FIELDS = [
|
|
9
|
+
termField("open", "bool", "open:true"),
|
|
10
|
+
termField("direction", "enum", "direction:to_us", { values: PAYMENT_DIRECTIONS }),
|
|
11
|
+
termField("state", "enum", "state:paid", { values: PAYMENT_STATES }),
|
|
12
|
+
];
|
|
13
|
+
/** The store's payment filter from `queryTerms`' answer; `undefined` when no payment term was given. */
|
|
14
|
+
export const paymentFilterOf = (terms) => {
|
|
15
|
+
const open = terms.get("open");
|
|
16
|
+
const direction = terms.get("direction");
|
|
17
|
+
const state = terms.get("state");
|
|
18
|
+
if (open === undefined && direction === undefined && state === undefined)
|
|
19
|
+
return undefined;
|
|
20
|
+
if (open !== undefined && open !== "true" && open !== "false")
|
|
21
|
+
throw new CliError("validation_error", "open takes true or false");
|
|
22
|
+
return {
|
|
23
|
+
...(open === undefined ? {} : { open: open === "true" }),
|
|
24
|
+
...(direction === undefined ? {} : { direction }),
|
|
25
|
+
...(state === undefined ? {} : { state }),
|
|
26
|
+
};
|
|
27
|
+
};
|
package/dist/cli/program.js
CHANGED
|
@@ -192,11 +192,15 @@ const runOnce = async (argv, definition, options) => {
|
|
|
192
192
|
};
|
|
193
193
|
const sigint = () => interrupted("SIGINT");
|
|
194
194
|
const sigterm = () => interrupted("SIGTERM");
|
|
195
|
+
// A sync, or a one-shot's wait on work it started (`schedule run`), outlives its reader: a desktop that closed the
|
|
196
|
+
// pipe 18 minutes in must not lose the sync, and the run record still says how it ended. Its later lines go
|
|
197
|
+
// nowhere. A listening command's stream is its work, so it still ends.
|
|
198
|
+
let outlivesReader = false;
|
|
195
199
|
const pipeError = (error) => {
|
|
196
|
-
if (error.code
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
+
if (error.code !== "EPIPE" || outlivesReader || (phased && !listens))
|
|
201
|
+
return;
|
|
202
|
+
signalExit = 0;
|
|
203
|
+
control.abort(error);
|
|
200
204
|
};
|
|
201
205
|
const externalAbort = () => {
|
|
202
206
|
const cancelled = new CliError("cancelled", "command cancelled", { retryable: false });
|
|
@@ -343,6 +347,7 @@ const runOnce = async (argv, definition, options) => {
|
|
|
343
347
|
const { lifetime, cooperative: stopsAtItsPace, partial: answersPartial } = metaOf(action);
|
|
344
348
|
const persistent = lifetime === "persistent";
|
|
345
349
|
listens = persistent;
|
|
350
|
+
outlivesReader = stopsAtItsPace === true;
|
|
346
351
|
const interactiveLogin = !headless && lifetime === "interactive";
|
|
347
352
|
const timeout = flags.timeout ?? (commandEnv[envName(definition.app, "TIMEOUT")]?.trim() || undefined);
|
|
348
353
|
// A background fetch was asked to outlive the shell; only an explicit --timeout bounds it.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { RowShape } from "./compact.js";
|
|
2
|
+
/**
|
|
3
|
+
* The compact row of each status signal's items, by `status.<signal id>`; a signal's file and its row are the two
|
|
4
|
+
* places a new signal touches. They live here because a service may not import the CLI's row type.
|
|
5
|
+
*/
|
|
6
|
+
export declare const STATUS_SIGNAL_ROWS: Readonly<Record<string, RowShape>>;
|
|
7
|
+
/** The rest of a `project_status` answer: its chats, open money, chats left out, and failed signals. */
|
|
8
|
+
export declare const STATUS_ROWS: Readonly<Record<string, RowShape>>;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
const where = [{ field: "ref" }, { field: ["chat_title", "chat"] }];
|
|
2
|
+
const who = { field: ["who.owner", "who.name"], tag: "you" };
|
|
3
|
+
/**
|
|
4
|
+
* The compact row of each status signal's items, by `status.<signal id>`; a signal's file and its row are the two
|
|
5
|
+
* places a new signal touches. They live here because a service may not import the CLI's row type.
|
|
6
|
+
*/
|
|
7
|
+
export const STATUS_SIGNAL_ROWS = {
|
|
8
|
+
"status.last-message": { columns: [...where, { field: "at", as: "time" }, who] },
|
|
9
|
+
"status.reply-next": {
|
|
10
|
+
columns: [
|
|
11
|
+
...where,
|
|
12
|
+
{ field: "data.next" },
|
|
13
|
+
{ field: "data.addressed.name" },
|
|
14
|
+
{ field: "data.waiting_since", as: "day" },
|
|
15
|
+
{ field: "data.overdue", tag: "overdue" },
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
"status.questions": {
|
|
19
|
+
columns: [...where, { field: "at", as: "day" }, { field: "data.side" }, who],
|
|
20
|
+
text: ["data.excerpt"],
|
|
21
|
+
},
|
|
22
|
+
"status.promises": {
|
|
23
|
+
columns: [
|
|
24
|
+
...where,
|
|
25
|
+
{ field: "at", as: "day" },
|
|
26
|
+
{ field: "data.source" },
|
|
27
|
+
{ field: "data.side" },
|
|
28
|
+
{ field: "data.due", as: "day" },
|
|
29
|
+
{ field: "data.status" },
|
|
30
|
+
],
|
|
31
|
+
text: ["data.title"],
|
|
32
|
+
},
|
|
33
|
+
"status.amounts": {
|
|
34
|
+
columns: [
|
|
35
|
+
...where,
|
|
36
|
+
{ field: "data.direction" },
|
|
37
|
+
{ field: "data.amount" },
|
|
38
|
+
{ field: "data.currency" },
|
|
39
|
+
{ field: "data.state" },
|
|
40
|
+
{ field: "data.since", as: "day" },
|
|
41
|
+
{ field: "data.asks", as: "count", tag: "asks" },
|
|
42
|
+
{ field: "basis" },
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
"status.payments": {
|
|
46
|
+
columns: [{ field: "ref" }, { field: "at", as: "day" }, { field: "data.status" }],
|
|
47
|
+
text: ["data.title"],
|
|
48
|
+
},
|
|
49
|
+
"status.topics": {
|
|
50
|
+
columns: [
|
|
51
|
+
...where,
|
|
52
|
+
{ field: "data.query" },
|
|
53
|
+
{ field: "data.theirs_last", as: "day" },
|
|
54
|
+
{ field: "data.yours_last", as: "day" },
|
|
55
|
+
{ field: "data.silent_since", as: "day" },
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
"status.coverage": {
|
|
59
|
+
columns: [
|
|
60
|
+
{ field: "chat" },
|
|
61
|
+
{ field: "chat_title" },
|
|
62
|
+
{ field: "data.state" },
|
|
63
|
+
{ field: "data.held.messages", as: "count", tag: "held" },
|
|
64
|
+
{ field: "data.held.oldest_at", as: "day" },
|
|
65
|
+
{ field: "sync_command" },
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
"status.children": {
|
|
69
|
+
columns: [
|
|
70
|
+
{ field: "data.child" },
|
|
71
|
+
{ field: "data.key" },
|
|
72
|
+
{ field: "data.name" },
|
|
73
|
+
{ field: "data.chats", as: "count", tag: "chats" },
|
|
74
|
+
{ field: "data.cut" },
|
|
75
|
+
],
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
/** The rest of a `project_status` answer: its chats, open money, chats left out, and failed signals. */
|
|
79
|
+
export const STATUS_ROWS = {
|
|
80
|
+
...STATUS_SIGNAL_ROWS,
|
|
81
|
+
"status.chat": {
|
|
82
|
+
columns: [
|
|
83
|
+
{ field: "chat" },
|
|
84
|
+
{ field: "title" },
|
|
85
|
+
{ field: "kind" },
|
|
86
|
+
{ field: "role" },
|
|
87
|
+
{ field: "attention", as: "count", tag: "attention" },
|
|
88
|
+
{ field: "why" },
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
"status.money": {
|
|
92
|
+
columns: [
|
|
93
|
+
{ field: "direction" },
|
|
94
|
+
{ field: "amount" },
|
|
95
|
+
{ field: "currency" },
|
|
96
|
+
{ field: "rows", as: "count", tag: "rows" },
|
|
97
|
+
],
|
|
98
|
+
},
|
|
99
|
+
"status.signal": { columns: [{ field: "id" }, { field: "version" }] },
|
|
100
|
+
"status.out-of-scope": { columns: [{ field: "chat" }, { field: "reason" }, { field: "purpose" }] },
|
|
101
|
+
"status.failed": { columns: [{ field: "signal" }], text: ["why"] },
|
|
102
|
+
};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type { NextStep } from "@wirecat/cli-base/standards";
|
|
2
|
+
import * as v from "valibot";
|
|
3
|
+
import type { ProjectStatus, StatusChatSummary } from "../services/project-status/index.js";
|
|
4
|
+
import type { RowShape } from "./compact.js";
|
|
5
|
+
export interface ProjectStatusHints {
|
|
6
|
+
/** The step that syncs a chat held in part back to `since` (`YYYY-MM-DD`); null when no command here can. */
|
|
7
|
+
sync: (chat: StatusChatSummary, since: string) => NextStep | null;
|
|
8
|
+
/** The step that reads a chat's stored messages from `since`; null when no command here can. */
|
|
9
|
+
read: (chat: StatusChatSummary, since: string) => NextStep | null;
|
|
10
|
+
/** The command line a step runs, for the text form. */
|
|
11
|
+
line: (step: NextStep) => string;
|
|
12
|
+
}
|
|
13
|
+
/** The `project_status` answer drawn by `--compact`: each signal's list with that signal's row. */
|
|
14
|
+
export declare const projectStatusRow: (signals: readonly {
|
|
15
|
+
id: string;
|
|
16
|
+
}[]) => RowShape;
|
|
17
|
+
/** Where a project stands: per chat signals, open money, and what was left out. Empty lists are left out. */
|
|
18
|
+
export declare const projectStatusSchema: v.LooseObjectSchema<{
|
|
19
|
+
readonly project: v.StringSchema<undefined>;
|
|
20
|
+
readonly key: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
21
|
+
readonly name: v.StringSchema<undefined>;
|
|
22
|
+
readonly window: v.StrictObjectSchema<{
|
|
23
|
+
readonly since: v.StringSchema<undefined>;
|
|
24
|
+
readonly until: v.StringSchema<undefined>;
|
|
25
|
+
}, undefined>;
|
|
26
|
+
readonly signals: v.ArraySchema<v.StrictObjectSchema<{
|
|
27
|
+
readonly id: v.StringSchema<undefined>;
|
|
28
|
+
readonly version: v.NumberSchema<undefined>;
|
|
29
|
+
}, undefined>, undefined>;
|
|
30
|
+
readonly money: v.ArraySchema<v.StrictObjectSchema<{
|
|
31
|
+
readonly direction: v.StringSchema<undefined>;
|
|
32
|
+
readonly currency: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
33
|
+
readonly amount: v.StringSchema<undefined>;
|
|
34
|
+
readonly rows: v.NumberSchema<undefined>;
|
|
35
|
+
}, undefined>, undefined>;
|
|
36
|
+
readonly chats: v.ArraySchema<v.StrictObjectSchema<{
|
|
37
|
+
readonly chat: v.StringSchema<undefined>;
|
|
38
|
+
readonly title: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
39
|
+
readonly kind: v.StringSchema<undefined>;
|
|
40
|
+
readonly role: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
41
|
+
readonly purpose: v.StringSchema<undefined>;
|
|
42
|
+
readonly project: v.StringSchema<undefined>;
|
|
43
|
+
readonly coverage: v.StringSchema<undefined>;
|
|
44
|
+
readonly attention: v.NumberSchema<undefined>;
|
|
45
|
+
readonly why: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
46
|
+
}, undefined>, undefined>;
|
|
47
|
+
readonly out_of_scope: v.OptionalSchema<v.ArraySchema<v.StrictObjectSchema<{
|
|
48
|
+
readonly chat: v.StringSchema<undefined>;
|
|
49
|
+
readonly reason: v.StringSchema<undefined>;
|
|
50
|
+
readonly purpose: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
51
|
+
}, undefined>, undefined>, undefined>;
|
|
52
|
+
readonly failed: v.OptionalSchema<v.ArraySchema<v.StrictObjectSchema<{
|
|
53
|
+
readonly signal: v.StringSchema<undefined>;
|
|
54
|
+
readonly why: v.StringSchema<undefined>;
|
|
55
|
+
}, undefined>, undefined>, undefined>;
|
|
56
|
+
}, undefined>;
|
|
57
|
+
export type ProjectStatusItem = v.InferOutput<typeof projectStatusSchema>;
|
|
58
|
+
/** The answer of `projects status`: the item, two steps at most, what is missing, and the window. */
|
|
59
|
+
export declare const projectStatusAnswer: (found: ProjectStatus, hints: ProjectStatusHints) => {
|
|
60
|
+
item: {
|
|
61
|
+
project: string;
|
|
62
|
+
key: string | null;
|
|
63
|
+
name: string;
|
|
64
|
+
window: {
|
|
65
|
+
since: string;
|
|
66
|
+
until: string;
|
|
67
|
+
};
|
|
68
|
+
signals: {
|
|
69
|
+
id: string;
|
|
70
|
+
version: number;
|
|
71
|
+
}[];
|
|
72
|
+
money: {
|
|
73
|
+
direction: string;
|
|
74
|
+
currency: string | null;
|
|
75
|
+
amount: string;
|
|
76
|
+
rows: number;
|
|
77
|
+
}[];
|
|
78
|
+
chats: {
|
|
79
|
+
chat: string;
|
|
80
|
+
title: string | null;
|
|
81
|
+
kind: string;
|
|
82
|
+
role: string | null;
|
|
83
|
+
purpose: string;
|
|
84
|
+
project: string;
|
|
85
|
+
coverage: string;
|
|
86
|
+
attention: number;
|
|
87
|
+
why: string | null;
|
|
88
|
+
}[];
|
|
89
|
+
out_of_scope?: {
|
|
90
|
+
chat: string;
|
|
91
|
+
reason: string;
|
|
92
|
+
purpose?: string | undefined;
|
|
93
|
+
}[] | undefined;
|
|
94
|
+
failed?: {
|
|
95
|
+
signal: string;
|
|
96
|
+
why: string;
|
|
97
|
+
}[] | undefined;
|
|
98
|
+
} & {
|
|
99
|
+
[key: string]: unknown;
|
|
100
|
+
};
|
|
101
|
+
next: ({
|
|
102
|
+
kind: "command";
|
|
103
|
+
step: string;
|
|
104
|
+
command: string;
|
|
105
|
+
args: {
|
|
106
|
+
[x: string]: unknown;
|
|
107
|
+
};
|
|
108
|
+
line?: string | undefined;
|
|
109
|
+
why: string;
|
|
110
|
+
safe_to_auto_execute: boolean;
|
|
111
|
+
} | {
|
|
112
|
+
kind: "user_action";
|
|
113
|
+
step: string;
|
|
114
|
+
why: string;
|
|
115
|
+
safe_to_auto_execute: false;
|
|
116
|
+
})[];
|
|
117
|
+
missing: {
|
|
118
|
+
field: string;
|
|
119
|
+
why: string;
|
|
120
|
+
step?: string | undefined;
|
|
121
|
+
}[];
|
|
122
|
+
meta: {
|
|
123
|
+
window: {
|
|
124
|
+
since: string;
|
|
125
|
+
until: string;
|
|
126
|
+
};
|
|
127
|
+
complete: boolean;
|
|
128
|
+
accounts?: string[] | undefined;
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
/** The human form: header, money, a line per chat with what is open, then the steps. */
|
|
132
|
+
export declare const projectStatusText: (found: ProjectStatus, hints: ProjectStatusHints) => string;
|