@wirecat/cli-core 0.462.0 → 0.464.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 +106 -0
- package/dist/background/after-upgrade.d.ts +51 -0
- package/dist/background/after-upgrade.js +245 -0
- package/dist/background/budget.d.ts +37 -0
- package/dist/background/budget.js +48 -0
- package/dist/background/index.d.ts +1 -0
- package/dist/background/index.js +1 -0
- package/dist/calendar/busy.d.ts +68 -0
- package/dist/calendar/busy.js +148 -0
- package/dist/calendar/cli/index.d.ts +7 -0
- package/dist/calendar/cli/index.js +94 -12
- package/dist/calendar/index.d.ts +3 -2
- package/dist/calendar/index.js +2 -1
- package/dist/calendar/mcp/index.d.ts +55 -13
- package/dist/calendar/mcp/index.js +19 -4
- package/dist/calendar/recurrence.d.ts +10 -0
- package/dist/calendar/recurrence.js +65 -0
- package/dist/calendar/source.d.ts +19 -0
- package/dist/calendar/sync.d.ts +86 -7
- package/dist/calendar/sync.js +271 -105
- package/dist/calendar/testing/source-contract.js +14 -0
- package/dist/calendar/testing/source.d.ts +6 -0
- package/dist/calendar/testing/source.js +30 -2
- package/dist/calendar/testing/sync-contract.d.ts +2 -1
- package/dist/calendar/testing/sync-contract.js +80 -5
- package/dist/calendar/testing/writer-contract.d.ts +2 -1
- package/dist/calendar/testing/writer-contract.js +46 -2
- package/dist/calendar/testing/writer.js +1 -1
- package/dist/calendar/writer.d.ts +7 -2
- package/dist/calendar/writes.d.ts +12 -2
- package/dist/calendar/writes.js +32 -6
- package/dist/cli/bot/mcp.js +11 -3
- package/dist/cli/context.d.ts +8 -0
- package/dist/cli/context.js +15 -0
- package/dist/cli/execution.d.ts +7 -0
- package/dist/cli/execution.js +7 -0
- package/dist/cli/guide.js +1 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/mcp-server-command.d.ts +9 -4
- package/dist/cli/mcp-server-command.js +32 -25
- package/dist/cli/meeting-preparation-answer.d.ts +1 -0
- package/dist/cli/meeting-preparation-projects.d.ts +1 -0
- package/dist/cli/meeting-preparation-read.d.ts +1 -0
- package/dist/cli/messenger/chat-answers.js +1 -0
- package/dist/cli/messenger/mcp-command.d.ts +3 -4
- package/dist/cli/messenger/mcp-command.js +42 -26
- package/dist/cli/messenger/search-answers.js +29 -4
- package/dist/cli/messenger/store-maintenance-command.d.ts +1 -1
- package/dist/cli/program.js +15 -5
- package/dist/cli/upgrade-command.d.ts +7 -2
- package/dist/cli/upgrade-command.js +65 -9
- package/dist/entities/fields.d.ts +2 -0
- package/dist/entities/fields.js +2 -0
- package/dist/entities/hit.d.ts +1 -1
- package/dist/entities/hit.js +3 -3
- package/dist/entities/index.d.ts +10 -800
- package/dist/entities/index.js +37 -31
- package/dist/entities/meeting-preparation.d.ts +1 -0
- package/dist/entities/topic.d.ts +1 -0
- package/dist/entities/topic.js +2 -1
- package/dist/errors/reasons.js +4 -0
- package/dist/kit/mcp-stdio.d.ts +2 -2
- package/dist/kit/mcp-stdio.js +2 -2
- package/dist/mcp/http/activity.d.ts +1 -1
- package/dist/mcp/http/activity.js +1 -1
- package/dist/mcp/http/oauth.js +1 -1
- package/dist/mcp/http/state.d.ts +14 -0
- package/dist/mcp/http/state.js +16 -0
- package/dist/mcp/meeting-preparation.d.ts +1 -0
- package/dist/meetings/answers.d.ts +2 -0
- package/dist/meetings/answers.js +3 -0
- package/dist/meetings/mcp/search.d.ts +1 -0
- package/dist/meetings/project-evidence-answer.d.ts +1 -0
- package/dist/ports/app.d.ts +5 -0
- package/dist/services/package-upgrade.d.ts +9 -0
- package/dist/services/package-upgrade.js +8 -1
- package/package.json +3 -3
- package/parity.json +30 -22
|
@@ -17,7 +17,8 @@ export interface CalendarWriterFixture {
|
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
19
|
* What every calendar writer keeps (gcal-cli plan §9, W1): a stale `etag` is a conflict, never an overwrite; a create
|
|
20
|
-
* retried with its id finds the event;
|
|
20
|
+
* retried with its id finds the event; a repeat rule is kept as given and changed on the series; a timed event
|
|
21
|
+
* switches to all-day and back; `notify: false` mails nobody, and a profile that keeps `events.notify` at its
|
|
21
22
|
* default never reaches the provider with a write that would.
|
|
22
23
|
*/
|
|
23
24
|
export declare const calendarWriterContract: (fixture: () => CalendarWriterFixture | Promise<CalendarWriterFixture>) => ContractCase[];
|
|
@@ -17,9 +17,12 @@ const rejectsWith = async (promise, check, message) => {
|
|
|
17
17
|
assert.fail(`${message}, but it succeeded`);
|
|
18
18
|
};
|
|
19
19
|
const ID = "contract0sample0id0001";
|
|
20
|
+
const WEEKLY = "RRULE:FREQ=WEEKLY;COUNT=3";
|
|
21
|
+
const DAILY = "RRULE:FREQ=DAILY;INTERVAL=2;COUNT=5";
|
|
20
22
|
/**
|
|
21
23
|
* What every calendar writer keeps (gcal-cli plan §9, W1): a stale `etag` is a conflict, never an overwrite; a create
|
|
22
|
-
* retried with its id finds the event;
|
|
24
|
+
* retried with its id finds the event; a repeat rule is kept as given and changed on the series; a timed event
|
|
25
|
+
* switches to all-day and back; `notify: false` mails nobody, and a profile that keeps `events.notify` at its
|
|
23
26
|
* default never reaches the provider with a write that would.
|
|
24
27
|
*/
|
|
25
28
|
export const calendarWriterContract = (fixture) => {
|
|
@@ -75,6 +78,47 @@ export const calendarWriterContract = (fixture) => {
|
|
|
75
78
|
: f.writer.respond(f.calendar, id, "declined", options), (error) => reasonOf(error) === "event.conflict", "a stale etag is a calendarConflict()");
|
|
76
79
|
},
|
|
77
80
|
})),
|
|
81
|
+
{
|
|
82
|
+
name: "a create with a repeat rule returns the series holding that rule",
|
|
83
|
+
run: async () => {
|
|
84
|
+
const f = await fixture();
|
|
85
|
+
const made = await f.writer.create(f.calendar, draft(f.at, { recurrence: [WEEKLY] }), {
|
|
86
|
+
id: ID,
|
|
87
|
+
notify: false,
|
|
88
|
+
});
|
|
89
|
+
assert.equal(made.kind, "series");
|
|
90
|
+
assert.deepEqual(made.recurrence, [WEEKLY]);
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: "a change of the series' rule replaces it",
|
|
95
|
+
run: async () => {
|
|
96
|
+
const f = await fixture();
|
|
97
|
+
const made = await f.writer.create(f.calendar, draft(f.at, { recurrence: [WEEKLY] }), {
|
|
98
|
+
id: ID,
|
|
99
|
+
notify: false,
|
|
100
|
+
});
|
|
101
|
+
const changed = await f.writer.update(f.calendar, made.externalId, { recurrence: [DAILY] }, { scope: "series", etag: made.etag, notify: false });
|
|
102
|
+
assert.deepEqual(changed.recurrence, [DAILY]);
|
|
103
|
+
assert.notEqual(changed.etag, made.etag);
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: "a timed event becomes all-day, and back",
|
|
108
|
+
run: async () => {
|
|
109
|
+
const f = await fixture();
|
|
110
|
+
const made = await f.writer.create(f.calendar, draft(f.at), { id: ID, notify: false });
|
|
111
|
+
const day = new Date(f.at).toISOString().slice(0, 10);
|
|
112
|
+
const next = new Date(f.at + 86_400_000).toISOString().slice(0, 10);
|
|
113
|
+
const allDay = await f.writer.update(f.calendar, made.externalId, { startDate: day, endDate: next, timezone: "UTC" }, { scope: "this", etag: made.etag, notify: false });
|
|
114
|
+
assert.equal(allDay.allDay, true);
|
|
115
|
+
assert.equal(allDay.startDate, day);
|
|
116
|
+
const timed = await f.writer.update(f.calendar, made.externalId, { startsAt: f.at, endsAt: f.at + 3_600_000, timezone: "UTC" }, { scope: "this", etag: allDay.etag, notify: false });
|
|
117
|
+
assert.equal(timed.allDay ?? false, false);
|
|
118
|
+
assert.equal(timed.startsAt, f.at);
|
|
119
|
+
assert.equal(timed.startDate ?? null, null);
|
|
120
|
+
},
|
|
121
|
+
},
|
|
78
122
|
{
|
|
79
123
|
name: "notify reaches the provider as given: false mails nobody",
|
|
80
124
|
run: async () => {
|
|
@@ -122,7 +166,7 @@ export const calendarWriterContract = (fixture) => {
|
|
|
122
166
|
ask: async () => { },
|
|
123
167
|
now: f.at,
|
|
124
168
|
zone: "UTC",
|
|
125
|
-
names: { notify: "notify", id: "id", calendar: "calendar", series: "series" },
|
|
169
|
+
names: { notify: "notify", id: "id", calendar: "calendar", series: "series", repeat: "repeat" },
|
|
126
170
|
}, String(calendar?.id), {
|
|
127
171
|
title: "Example planning",
|
|
128
172
|
startTime: new Date(f.at).toISOString(),
|
|
@@ -3,7 +3,8 @@ import type { EntryInput, ParticipantResponse, StoredCalendar } from "../meeting
|
|
|
3
3
|
import type { Level, PermissionKey } from "../sends/permissions.js";
|
|
4
4
|
/**
|
|
5
5
|
* A new event in neutral words: a timed one has `startsAt` and `endsAt` (epoch ms, end exclusive) in `timezone`; an
|
|
6
|
-
* all-day one has `startDate` and `endDate` (`YYYY-MM-DD`, end exclusive).
|
|
6
|
+
* all-day one has `startDate` and `endDate` (`YYYY-MM-DD`, end exclusive). A repeating one has `recurrence`, RFC 5545
|
|
7
|
+
* lines as `EntryInput` holds them; its `timezone` is the zone the rule is expanded in, which Google requires.
|
|
7
8
|
*/
|
|
8
9
|
export interface EventDraft {
|
|
9
10
|
title: string;
|
|
@@ -18,8 +19,12 @@ export interface EventDraft {
|
|
|
18
19
|
email: string;
|
|
19
20
|
optional?: boolean;
|
|
20
21
|
}[];
|
|
22
|
+
recurrence?: string[];
|
|
21
23
|
}
|
|
22
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* Only the fields to change; `null` clears one. `attendees` and `recurrence` replace the whole list. A patch with
|
|
26
|
+
* `startDate` for a timed event, or `startsAt` for an all-day one, switches it: the adapter clears the other kind.
|
|
27
|
+
*/
|
|
23
28
|
export type EventPatch = Partial<EventDraft>;
|
|
24
29
|
/** `this`: the one entry named; `series`: the repeating event it belongs to, named by the series' own id. */
|
|
25
30
|
export type WriteScope = "this" | "series";
|
|
@@ -29,6 +29,8 @@ export interface EventFields {
|
|
|
29
29
|
location?: string;
|
|
30
30
|
description?: string;
|
|
31
31
|
attendees?: string[];
|
|
32
|
+
/** An RFC 5545 rule, with or without `RRULE:`; on a change, only with the series' scope. */
|
|
33
|
+
repeat?: string;
|
|
32
34
|
}
|
|
33
35
|
export interface WriteContext {
|
|
34
36
|
store: EventStore;
|
|
@@ -44,11 +46,16 @@ export interface WriteContext {
|
|
|
44
46
|
id: string;
|
|
45
47
|
calendar: string;
|
|
46
48
|
series: string;
|
|
49
|
+
repeat: string;
|
|
47
50
|
};
|
|
48
51
|
}
|
|
49
|
-
/**
|
|
50
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Typed fields into a draft, or into a patch with only what was given. `allDay` is the kind of the event a patch
|
|
54
|
+
* changes, for a repeat rule's UNTIL when the patch keeps the times.
|
|
55
|
+
*/
|
|
56
|
+
export declare const draftOf: (fields: EventFields, zone: string, { whole, allDay }: {
|
|
51
57
|
whole: boolean;
|
|
58
|
+
allDay?: boolean;
|
|
52
59
|
}) => EventPatch;
|
|
53
60
|
/** An id every provider takes as a client-chosen one: Google's base32hex `[a-v0-9]`, a CalDAV UID, a Graph id. */
|
|
54
61
|
export declare const newEventId: () => string;
|
|
@@ -73,6 +80,7 @@ export declare const createEvent: (context: WriteContext, calendarReference: str
|
|
|
73
80
|
url: string | null;
|
|
74
81
|
deleted_at: string | null;
|
|
75
82
|
} | null;
|
|
83
|
+
hint?: string | undefined;
|
|
76
84
|
}>;
|
|
77
85
|
export declare const updateEvent: (context: WriteContext, reference: string, fields: EventFields, options?: {
|
|
78
86
|
calendar?: string;
|
|
@@ -96,6 +104,7 @@ export declare const updateEvent: (context: WriteContext, reference: string, fie
|
|
|
96
104
|
url: string | null;
|
|
97
105
|
deleted_at: string | null;
|
|
98
106
|
} | null;
|
|
107
|
+
hint?: string | undefined;
|
|
99
108
|
}>;
|
|
100
109
|
export declare const deleteEvent: (context: WriteContext, reference: string, options?: {
|
|
101
110
|
calendar?: string;
|
|
@@ -128,4 +137,5 @@ export declare const respondToEvent: (context: WriteContext, reference: string,
|
|
|
128
137
|
url: string | null;
|
|
129
138
|
deleted_at: string | null;
|
|
130
139
|
} | null;
|
|
140
|
+
hint?: string | undefined;
|
|
131
141
|
}>;
|
package/dist/calendar/writes.js
CHANGED
|
@@ -6,6 +6,7 @@ import { guardedWrite } from "../sends/guarded.js";
|
|
|
6
6
|
import { DEFAULT_PERMISSIONS, levelFor } from "../sends/permissions.js";
|
|
7
7
|
import { newOperationId } from "../sends/send-id.js";
|
|
8
8
|
import { calendarIn, entryItem, eventIdOf, eventOf, plusDays } from "./reads.js";
|
|
9
|
+
import { kindBoundLines, repeatRule } from "./recurrence.js";
|
|
9
10
|
import { CALENDAR_WRITE_DEFAULTS, EVENTS_DELETE_SERIES_KEY, EVENTS_NOTIFY_KEY, } from "./writer.js";
|
|
10
11
|
export const EVENT_RESPONSES = ["accepted", "tentative", "declined"];
|
|
11
12
|
/** Each write is checked by its command's path, the key a declared CLI's `commandPermissions` already knows. */
|
|
@@ -24,8 +25,11 @@ const dateOf = (text, what) => {
|
|
|
24
25
|
return text;
|
|
25
26
|
};
|
|
26
27
|
const momentOf = (text, zone) => zonedMoment(text, zone, { start: 0, end: text.length });
|
|
27
|
-
/**
|
|
28
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Typed fields into a draft, or into a patch with only what was given. `allDay` is the kind of the event a patch
|
|
30
|
+
* changes, for a repeat rule's UNTIL when the patch keeps the times.
|
|
31
|
+
*/
|
|
32
|
+
export const draftOf = (fields, zone, { whole, allDay = false }) => {
|
|
29
33
|
const patch = {};
|
|
30
34
|
if (fields.title !== undefined) {
|
|
31
35
|
if (!fields.title.trim())
|
|
@@ -66,6 +70,12 @@ export const draftOf = (fields, zone, { whole }) => {
|
|
|
66
70
|
throw new CliError("validation_error", `${email} is not an email address`);
|
|
67
71
|
return { email };
|
|
68
72
|
});
|
|
73
|
+
if (fields.repeat !== undefined)
|
|
74
|
+
patch.recurrence = [
|
|
75
|
+
repeatRule(fields.repeat, {
|
|
76
|
+
allDay: patch.startDate !== undefined || (patch.startsAt === undefined && allDay),
|
|
77
|
+
}),
|
|
78
|
+
];
|
|
69
79
|
if (!whole && Object.keys(patch).length === 0)
|
|
70
80
|
throw new CliError("validation_error", "nothing to change");
|
|
71
81
|
return patch;
|
|
@@ -130,10 +140,12 @@ const keysFor = (context, write, notify) => [
|
|
|
130
140
|
...(context.writes.extraKeys?.(write) ?? []),
|
|
131
141
|
...(notify ? [EVENTS_NOTIFY_KEY] : []),
|
|
132
142
|
];
|
|
133
|
-
const answerOf = (operationId, saved) => ({
|
|
143
|
+
const answerOf = (operationId, saved, repeats = false) => ({
|
|
134
144
|
operation_id: operationId,
|
|
135
145
|
event: saved?.eventId == null ? null : `event:${saved.eventId}`,
|
|
136
146
|
entry: saved ? entryItem(saved) : null,
|
|
147
|
+
// Only the series is saved here; its occurrences come with the provider's expansion on the next sync.
|
|
148
|
+
...(repeats ? { hint: "sync to store its occurrences" } : {}),
|
|
137
149
|
});
|
|
138
150
|
/** An id every provider takes as a client-chosen one: Google's base32hex `[a-v0-9]`, a CalDAV UID, a Graph id. */
|
|
139
151
|
export const newEventId = () => randomUUID().replaceAll("-", "");
|
|
@@ -159,7 +171,7 @@ export const createEvent = async (context, calendarReference, fields, options =
|
|
|
159
171
|
throw error;
|
|
160
172
|
}
|
|
161
173
|
const [saved] = await context.store.saveEntries(calendar.id, [entry], context.now);
|
|
162
|
-
return answerOf(id, saved);
|
|
174
|
+
return answerOf(id, saved, draft.recurrence !== undefined);
|
|
163
175
|
};
|
|
164
176
|
const targetOf = async (context, reference, { calendar, scope }) => {
|
|
165
177
|
const details = await eventOf(context.store, context.accountId, eventIdOf(reference));
|
|
@@ -196,14 +208,28 @@ const targetOf = async (context, reference, { calendar, scope }) => {
|
|
|
196
208
|
};
|
|
197
209
|
};
|
|
198
210
|
export const updateEvent = async (context, reference, fields, options = {}) => {
|
|
199
|
-
const patch = draftOf(fields, context.zone, { whole: false });
|
|
200
211
|
const scope = options.series ? "series" : "this";
|
|
212
|
+
if (fields.repeat !== undefined && scope !== "series")
|
|
213
|
+
throw new CliError("validation_error", `a repeat rule belongs to the whole series: add ${context.names.series}`, {
|
|
214
|
+
param: context.names.series,
|
|
215
|
+
});
|
|
201
216
|
const { calendar, target, participants } = await targetOf(context, reference, { ...options, scope });
|
|
217
|
+
const patch = draftOf(fields, context.zone, { whole: false, allDay: target.allDay });
|
|
218
|
+
const kept = (target.recurrence ?? []).filter((line) => !/^RRULE:/iu.test(line));
|
|
219
|
+
if (patch.recurrence)
|
|
220
|
+
patch.recurrence = [...patch.recurrence, ...kept];
|
|
221
|
+
const switching = patch.startDate !== undefined ? !target.allDay : patch.startsAt !== undefined && target.allDay;
|
|
222
|
+
const bound = kindBoundLines(patch.recurrence ? kept : (target.recurrence ?? []));
|
|
223
|
+
if (switching && bound.length)
|
|
224
|
+
throw new CliError("validation_error", `${reference} repeats with ${bound.join(", ")}, which hold a ${target.allDay ? "date" : "time"}; ` +
|
|
225
|
+
(patch.recurrence || bound.some((line) => !/^RRULE:/iu.test(line))
|
|
226
|
+
? "change its kind in the calendar itself"
|
|
227
|
+
: `give its rule again with ${context.names.repeat} and UNTIL as a ${target.allDay ? "UTC time" : "date"}`), { reason: "event.repeat-kind" });
|
|
202
228
|
const notify = notifyOf(context, options.notify, hasOthers(participants) || (patch.attendees?.length ?? 0) > 0);
|
|
203
229
|
const operationId = newOperationId();
|
|
204
230
|
const entry = await guardedWrite(writeGuard(context, keysFor(context, "update", notify)), { chatId: null, kind: "edit", key: COMMAND_KEY.update, operationId, notify }, () => context.writes.writer.update(calendar, target.externalId, patch, { scope, etag: target.etag, notify }));
|
|
205
231
|
const [saved] = await context.store.saveEntries(calendar.id, [entry], context.now);
|
|
206
|
-
return answerOf(operationId, saved);
|
|
232
|
+
return answerOf(operationId, saved, patch.recurrence !== undefined || (switching && scope === "series"));
|
|
207
233
|
};
|
|
208
234
|
export const deleteEvent = async (context, reference, options = {}) => {
|
|
209
235
|
const scope = options.series ? "series" : "this";
|
package/dist/cli/bot/mcp.js
CHANGED
|
@@ -12,8 +12,16 @@ import { botExamples } from "./examples.js";
|
|
|
12
12
|
/** `bot mcp`, mounted only for a messenger that hands in its `run` on `BotMessenger.mcp`. */
|
|
13
13
|
export const botMcpCommand = (bot) => {
|
|
14
14
|
const { app } = bot;
|
|
15
|
-
const command = annotate(new Command("mcp"), { effects:
|
|
16
|
-
.description(`serve this bot to an agent over MCP
|
|
15
|
+
const command = annotate(new Command("mcp"), { effects: LOCAL_READ, mcp: false })
|
|
16
|
+
.description(`serve this bot to an agent over MCP and set it up — \`claude mcp add sales-bot -- ${app.command} sales bot mcp serve\``)
|
|
17
|
+
.summary("serve this bot to an agent over MCP, and set that up");
|
|
18
|
+
command.addCommand(annotate(new Command("serve"), {
|
|
19
|
+
effects: REMOTE_WRITE,
|
|
20
|
+
lifetime: "persistent",
|
|
21
|
+
mcp: false,
|
|
22
|
+
examples: botExamples(bot, "mcp serve"),
|
|
23
|
+
})
|
|
24
|
+
.description(`serve this bot to an agent over MCP, on stdin and stdout — \`claude mcp add sales-bot -- ${app.command} sales bot mcp serve\``)
|
|
17
25
|
.summary("serve this bot to an agent over MCP, on stdin and stdout")
|
|
18
26
|
.action(async function () {
|
|
19
27
|
const context = botContext(this, bot);
|
|
@@ -35,7 +43,7 @@ export const botMcpCommand = (bot) => {
|
|
|
35
43
|
...(mcp.keyOf ? { keyOf: mcp.keyOf } : {}),
|
|
36
44
|
...(mcp.skill ? { skill: mcp.skill } : {}),
|
|
37
45
|
}, context.streams.diagnostic);
|
|
38
|
-
});
|
|
46
|
+
}));
|
|
39
47
|
command.addCommand(annotate(new Command("config"), { effects: LOCAL_READ, examples: botExamples(bot, "mcp config") })
|
|
40
48
|
.description("print the mcpServers entry for Claude Desktop, Cursor and others, with full paths; writes nothing")
|
|
41
49
|
.summary("print the mcpServers entry for an MCP client; writes nothing")
|
package/dist/cli/context.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type KeyringStore, type Renderer, type RenderFormat, type Streams } from "@wirecat/cli-base";
|
|
2
2
|
import type { ErrorObject } from "@wirecat/cli-base/standards";
|
|
3
3
|
import type { Command } from "commander";
|
|
4
|
+
import { type Deadline, type RunBudget } from "../background/budget.js";
|
|
4
5
|
import type { MessageEvent } from "../domain/models.js";
|
|
5
6
|
import type { AppIdentity } from "../ports/app.js";
|
|
6
7
|
import type { MessengerAdapter } from "../ports/messenger.js";
|
|
@@ -71,6 +72,8 @@ export interface BaseEnvironment {
|
|
|
71
72
|
* what each step brings and may stop at any point: the history a phone pushes after linking.
|
|
72
73
|
*/
|
|
73
74
|
untilStopped?: () => AbortSignal;
|
|
75
|
+
/** Set by `run()`: the deadline running now, or none (a persistent command, a phase, a background job). */
|
|
76
|
+
deadline?: () => Deadline | undefined;
|
|
74
77
|
/**
|
|
75
78
|
* Set by `run()`: ends the phase `untilStopped()` began without aborting its signal — the command saw it finish.
|
|
76
79
|
* However a phase ends, the steps after it get the command's one-shot deadline again, started afresh, and a signal
|
|
@@ -101,6 +104,11 @@ export interface RelayedRun {
|
|
|
101
104
|
/** A promise when the lender opens it on first use. */
|
|
102
105
|
connection: () => MessengerAdapter | Promise<MessengerAdapter>;
|
|
103
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* A budget for a resumable sync, from the deadline running now and the CLI's `requestMs`: so a lower `--timeout`
|
|
109
|
+
* means fewer requests. `requests` (`--max-pages`) can only lower it.
|
|
110
|
+
*/
|
|
111
|
+
export declare const runBudgetOf: (command: Command, requests?: number) => RunBudget;
|
|
104
112
|
/** A partial answer's exit code, for this run alone. */
|
|
105
113
|
export declare const setExitCode: (command: Command, code: number) => void;
|
|
106
114
|
export declare const provide: (program: Command, environment: BaseEnvironment) => void;
|
package/dist/cli/context.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CliError } from "@wirecat/cli-base";
|
|
2
|
+
import { runBudget } from "../background/budget.js";
|
|
2
3
|
import { resolveOutput } from "../output.js";
|
|
3
4
|
import { recorded } from "../runs/recording.js";
|
|
4
5
|
import { runContextOf } from "../runs/run-context.js";
|
|
@@ -6,6 +7,20 @@ import { withDeadline } from "./deadline.js";
|
|
|
6
7
|
import { withAbort } from "./execution.js";
|
|
7
8
|
import { rootOf } from "./profile.js";
|
|
8
9
|
import { namedProfile } from "./settings.js";
|
|
10
|
+
/**
|
|
11
|
+
* A budget for a resumable sync, from the deadline running now and the CLI's `requestMs`: so a lower `--timeout`
|
|
12
|
+
* means fewer requests. `requests` (`--max-pages`) can only lower it.
|
|
13
|
+
*/
|
|
14
|
+
export const runBudgetOf = (command, requests) => {
|
|
15
|
+
const { deadline, signal, app } = environmentOf(command);
|
|
16
|
+
const now = deadline?.();
|
|
17
|
+
return runBudget({
|
|
18
|
+
...(requests === undefined ? {} : { requests }),
|
|
19
|
+
...(app?.requestMs === undefined ? {} : { requestMs: app.requestMs }),
|
|
20
|
+
...(now ? { deadline: now } : {}),
|
|
21
|
+
...(signal ? { signal } : {}),
|
|
22
|
+
});
|
|
23
|
+
};
|
|
9
24
|
/** A partial answer's exit code, for this run alone. */
|
|
10
25
|
export const setExitCode = (command, code) => {
|
|
11
26
|
const slot = environmentOf(command).exit;
|
package/dist/cli/execution.d.ts
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { type Streams } from "@wirecat/cli-base";
|
|
2
2
|
import type { Closeable } from "./deadline.js";
|
|
3
3
|
export declare const DEFAULT_COMMAND_MS = 30000;
|
|
4
|
+
/** A `cooperative` command stops on its own before its deadline and keeps its work, so it may run longer. */
|
|
5
|
+
export declare const DEFAULT_COOPERATIVE_MS: number;
|
|
6
|
+
/**
|
|
7
|
+
* The same over MCP: clients cut a tool call at about 60 s (the MCP TypeScript SDK's `DEFAULT_REQUEST_TIMEOUT_MSEC`
|
|
8
|
+
* is 60000), so the command answers well before that.
|
|
9
|
+
*/
|
|
10
|
+
export declare const DEFAULT_AGENT_COOPERATIVE_MS = 45000;
|
|
4
11
|
export declare const DEFAULT_OUTPUT_BYTES: number;
|
|
5
12
|
export declare const byteCount: (value: string, option: string, zero?: boolean) => number;
|
|
6
13
|
export interface Execution {
|
package/dist/cli/execution.js
CHANGED
|
@@ -2,6 +2,13 @@ import { CliError } from "@wirecat/cli-base";
|
|
|
2
2
|
import { stopWrites, withWriteScope } from "../sends/guarded.js";
|
|
3
3
|
import { fieldsOf, projectFields } from "./result-fields.js";
|
|
4
4
|
export const DEFAULT_COMMAND_MS = 30_000;
|
|
5
|
+
/** A `cooperative` command stops on its own before its deadline and keeps its work, so it may run longer. */
|
|
6
|
+
export const DEFAULT_COOPERATIVE_MS = 5 * 60_000;
|
|
7
|
+
/**
|
|
8
|
+
* The same over MCP: clients cut a tool call at about 60 s (the MCP TypeScript SDK's `DEFAULT_REQUEST_TIMEOUT_MSEC`
|
|
9
|
+
* is 60000), so the command answers well before that.
|
|
10
|
+
*/
|
|
11
|
+
export const DEFAULT_AGENT_COOPERATIVE_MS = 45_000;
|
|
5
12
|
export const DEFAULT_OUTPUT_BYTES = 4 * 1024 * 1024;
|
|
6
13
|
export const byteCount = (value, option, zero = false) => {
|
|
7
14
|
const result = Number(value);
|
package/dist/cli/guide.js
CHANGED
|
@@ -291,7 +291,7 @@ export const checkStep = (program, run) => {
|
|
|
291
291
|
const beforeDashes = rest.slice(index, rest.includes("--") ? rest.indexOf("--") : undefined);
|
|
292
292
|
if (help && beforeDashes.some((word) => word === help.long || word === help.short))
|
|
293
293
|
return [];
|
|
294
|
-
// A group with an action of its own runs as typed
|
|
294
|
+
// A group with an action of its own runs as typed rather than asking for one of its children.
|
|
295
295
|
const runsItself = at._actionHandler != null;
|
|
296
296
|
if (at.commands.length > 0 && !runsItself)
|
|
297
297
|
return [`needs one of: ${at.commands.map((one) => one.name()).join(", ")}`];
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -57,7 +57,7 @@ export { CONTRACT } from "./commands-command.js";
|
|
|
57
57
|
export { refuseUnknownKey } from "./config-command.js";
|
|
58
58
|
export { type ConfigIssue, type ConfigIssueKind, checkConfigText, issueMessage, readConfigFile, readConfigText, settingPathOf, withoutSetting, } from "./config-issues.js";
|
|
59
59
|
export { loadSnakeCaseConfig } from "./config-keys.js";
|
|
60
|
-
export { type AgentRun, type BaseContext, type BaseEnvironment, baseContext, environmentOf, outputFor, provide, secretInputOf, } from "./context.js";
|
|
60
|
+
export { type AgentRun, type BaseContext, type BaseEnvironment, baseContext, environmentOf, outputFor, provide, runBudgetOf, secretInputOf, } from "./context.js";
|
|
61
61
|
export { addContractInfo, type ContractEvidenceOptions, contractEvidence } from "./contract-evidence.js";
|
|
62
62
|
export { sharedCommandTemplate } from "./contract-templates.js";
|
|
63
63
|
export { type Closeable, withDeadline } from "./deadline.js";
|
package/dist/cli/index.js
CHANGED
|
@@ -50,7 +50,7 @@ export { CONTRACT } from "./commands-command.js";
|
|
|
50
50
|
export { refuseUnknownKey } from "./config-command.js";
|
|
51
51
|
export { checkConfigText, issueMessage, readConfigFile, readConfigText, settingPathOf, withoutSetting, } from "./config-issues.js";
|
|
52
52
|
export { loadSnakeCaseConfig } from "./config-keys.js";
|
|
53
|
-
export { baseContext, environmentOf, outputFor, provide, secretInputOf, } from "./context.js";
|
|
53
|
+
export { baseContext, environmentOf, outputFor, provide, runBudgetOf, secretInputOf, } from "./context.js";
|
|
54
54
|
export { addContractInfo, contractEvidence } from "./contract-evidence.js";
|
|
55
55
|
export { sharedCommandTemplate } from "./contract-templates.js";
|
|
56
56
|
export { withDeadline } from "./deadline.js";
|
|
@@ -10,7 +10,7 @@ export interface McpServerTarget {
|
|
|
10
10
|
envPrefix: string;
|
|
11
11
|
env: NodeJS.ProcessEnv;
|
|
12
12
|
tokenFile: string;
|
|
13
|
-
/** The words after the CLI's own entry that run `mcp --http` in the foreground, before `--port`. */
|
|
13
|
+
/** The words after the CLI's own entry that run `mcp serve --http` in the foreground, before `--port`. */
|
|
14
14
|
argv: string[];
|
|
15
15
|
/** Added to the server's environment: the profile, for a CLI that reads it from there. */
|
|
16
16
|
childEnv?: Record<string, string>;
|
|
@@ -19,17 +19,22 @@ export interface McpServerTarget {
|
|
|
19
19
|
export interface McpServerOptions {
|
|
20
20
|
/** How help shows the CLI before `mcp`: `tg personal`. */
|
|
21
21
|
example: string;
|
|
22
|
-
/** The CLI's own `mcp --http` default, when it is not 8765. */
|
|
22
|
+
/** The CLI's own `mcp serve --http` default, when it is not 8765. */
|
|
23
23
|
defaultPort?: number;
|
|
24
|
+
/**
|
|
25
|
+
* `start` takes `--permission` and hands it to the server: only for a CLI whose `mcp serve` takes it too,
|
|
26
|
+
* since a server that refuses the option dies before it listens.
|
|
27
|
+
*/
|
|
28
|
+
permission?: boolean;
|
|
24
29
|
target: (self: Command) => McpServerTarget;
|
|
25
30
|
/** The CLI's own answer: the item in `--json`, else the lines. */
|
|
26
31
|
answer: (self: Command, item: object, lines: string[]) => void;
|
|
27
32
|
system?: (self: Command) => ServerSystem | undefined;
|
|
28
33
|
}
|
|
29
34
|
/**
|
|
30
|
-
* `mcp --http` in the background: `start`, `stop`, `status`, as `server` does for `serve`. The server
|
|
35
|
+
* `mcp serve --http` in the background: `start`, `stop`, `status`, as `server` does for `serve`. The server
|
|
31
36
|
* keeps a lock beside its token file (`state.ts`) with its port, address and login code, so status
|
|
32
37
|
* survives the shell that started it, and `stop` ends only a process that carries this CLI's marker.
|
|
33
38
|
* None of it is offered over MCP: status holds a live login code.
|
|
34
39
|
*/
|
|
35
|
-
export declare const mcpServerCommand: ({ example, defaultPort, target, answer, system, }: McpServerOptions) => Command;
|
|
40
|
+
export declare const mcpServerCommand: ({ example, defaultPort, permission, target, answer, system, }: McpServerOptions) => Command;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { CliError } from "@wirecat/cli-base";
|
|
1
|
+
import { CliError, writeSecurely } from "@wirecat/cli-base";
|
|
2
2
|
import { annotate } from "@wirecat/cli-base/commands";
|
|
3
3
|
import { Command } from "commander";
|
|
4
4
|
import { alive, carries } from "../background/processes.js";
|
|
5
5
|
import { thisMachine } from "../background/system.js";
|
|
6
6
|
import { readableLog, tail } from "../background/units.js";
|
|
7
7
|
import { mcpUrlOf, portOf, publicUrlOf } from "../http/public-url.js";
|
|
8
|
-
import { httpLockFile, httpLogFile, mcpHttpMarker, readHttpLock, signedInClients, } from "../mcp/http/state.js";
|
|
8
|
+
import { httpLaunchFile, httpLockFile, httpLogFile, mcpHttpMarker, readHttpLock, signedInClients, } from "../mcp/http/state.js";
|
|
9
9
|
import { destroysNothing, LOCAL_READ, LOCAL_WRITE } from "./effects.js";
|
|
10
10
|
export const MCP_HTTP_DEFAULT_PORT = 8765;
|
|
11
11
|
const LOOKS = 120;
|
|
@@ -40,28 +40,29 @@ const clientLines = (clients) => clients.length === 0
|
|
|
40
40
|
.join(", ")}.`,
|
|
41
41
|
];
|
|
42
42
|
/**
|
|
43
|
-
* `mcp --http` in the background: `start`, `stop`, `status`, as `server` does for `serve`. The server
|
|
43
|
+
* `mcp serve --http` in the background: `start`, `stop`, `status`, as `server` does for `serve`. The server
|
|
44
44
|
* keeps a lock beside its token file (`state.ts`) with its port, address and login code, so status
|
|
45
45
|
* survives the shell that started it, and `stop` ends only a process that carries this CLI's marker.
|
|
46
46
|
* None of it is offered over MCP: status holds a live login code.
|
|
47
47
|
*/
|
|
48
|
-
export const mcpServerCommand = ({ example, defaultPort = MCP_HTTP_DEFAULT_PORT, target, answer, system, }) => {
|
|
48
|
+
export const mcpServerCommand = ({ example, defaultPort = MCP_HTTP_DEFAULT_PORT, permission = false, target, answer, system, }) => {
|
|
49
49
|
const command = annotate(new Command("server"), { effects: LOCAL_READ, mcp: false })
|
|
50
50
|
.summary("run the HTTP MCP server in the background")
|
|
51
|
-
.description("start, stop and check `mcp --http` in the background; status shows the login code for a browser app");
|
|
51
|
+
.description("start, stop and check `mcp serve --http` in the background; status shows the login code for a browser app");
|
|
52
52
|
const machine = (self) => system?.(self) ?? thisMachine();
|
|
53
|
-
destroysNothing(annotate(command.command("start"), {
|
|
53
|
+
const start = destroysNothing(annotate(command.command("start"), {
|
|
54
54
|
effects: LOCAL_WRITE,
|
|
55
55
|
mcp: false,
|
|
56
56
|
examples: [`${example} mcp server start --public-url https://laptop.example.ts.net`],
|
|
57
57
|
}))
|
|
58
58
|
.summary("start the HTTP MCP server in the background")
|
|
59
|
-
.description("start `mcp --http` apart from this shell and answer once it listens, with its login code")
|
|
59
|
+
.description("start `mcp serve --http` apart from this shell and answer once it listens, with its login code")
|
|
60
60
|
.option("--port <port>", `the local port (default ${defaultPort})`)
|
|
61
|
-
.option("--public-url <url>", "the tunnel's https address the browser apps use, e.g. https://<name>.ts.net or https://<name>.ts.net/tg")
|
|
62
|
-
|
|
63
|
-
.
|
|
64
|
-
|
|
61
|
+
.option("--public-url <url>", "the tunnel's https address the browser apps use, e.g. https://<name>.ts.net or https://<name>.ts.net/tg");
|
|
62
|
+
if (permission)
|
|
63
|
+
start.option("--permission <key=level>", "make a permission stricter for this server only; repeat for more keys", (value, previous = []) => [...previous, value]);
|
|
64
|
+
start.action(async function () {
|
|
65
|
+
// A CLI with a root-wide `--permission` hands it over too, so the server is never looser than asked.
|
|
65
66
|
const flags = this.optsWithGlobals();
|
|
66
67
|
const one = target(this);
|
|
67
68
|
const url = publicUrlOf(flags.publicUrl, {
|
|
@@ -81,14 +82,20 @@ export const mcpServerCommand = ({ example, defaultPort = MCP_HTTP_DEFAULT_PORT,
|
|
|
81
82
|
const sys = machine(this);
|
|
82
83
|
const log = httpLogFile(one.tokenFile);
|
|
83
84
|
const { [`${one.envPrefix}_TIMEOUT`]: _timeout, ...inherited } = one.env;
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
85
|
+
const launch = {
|
|
86
|
+
invocation: one.invocation,
|
|
87
|
+
argv: [
|
|
88
|
+
...one.argv,
|
|
89
|
+
"--port",
|
|
90
|
+
String(port),
|
|
91
|
+
"--public-url",
|
|
92
|
+
url.pathname === "/" ? url.origin : url.href,
|
|
93
|
+
...(flags.permission ?? []).flatMap((entry) => ["--permission", entry]),
|
|
94
|
+
],
|
|
95
|
+
env: { ...one.childEnv, [mcpHttpMarker(one.envPrefix)]: one.profile ?? "default" },
|
|
96
|
+
};
|
|
97
|
+
writeSecurely(httpLaunchFile(lockFile), `${JSON.stringify(launch)}\n`, 0o600);
|
|
98
|
+
const pid = sys.spawn(launch.argv, { ...inherited, ...launch.env }, log);
|
|
92
99
|
for (let look = 0; look < LOOKS; look++) {
|
|
93
100
|
const now = readHttpLock(lockFile);
|
|
94
101
|
if (now?.pid === pid && now.listeningAt) {
|
|
@@ -102,15 +109,15 @@ export const mcpServerCommand = ({ example, defaultPort = MCP_HTTP_DEFAULT_PORT,
|
|
|
102
109
|
}
|
|
103
110
|
if (!alive(pid)) {
|
|
104
111
|
const last = readableLog(tail(log, 5)).trim();
|
|
105
|
-
throw new CliError("provider_unavailable", `mcp --http stopped before it listened${last ? `:\n${last}` : ""}
|
|
112
|
+
throw new CliError("provider_unavailable", `mcp serve --http stopped before it listened${last ? `:\n${last}` : ""}`, { retryable: false });
|
|
106
113
|
}
|
|
107
114
|
await sys.pause();
|
|
108
115
|
}
|
|
109
|
-
throw new CliError("timeout", `mcp --http is running but not listening yet — \`${one.invocation} mcp server status\`; its log: ${log}`);
|
|
116
|
+
throw new CliError("timeout", `mcp serve --http is running but not listening yet — \`${one.invocation} mcp server status\`; its log: ${log}`);
|
|
110
117
|
});
|
|
111
118
|
destroysNothing(annotate(command.command("stop"), { effects: LOCAL_WRITE, mcp: false, examples: [`${example} mcp server stop`] }))
|
|
112
119
|
.summary("stop the background HTTP MCP server")
|
|
113
|
-
.description("stop `mcp --http` — only one that `mcp server start` started")
|
|
120
|
+
.description("stop `mcp serve --http` — only one that `mcp server start` started")
|
|
114
121
|
.action(async function () {
|
|
115
122
|
const one = target(this);
|
|
116
123
|
const lock = readHttpLock(httpLockFile(one.tokenFile));
|
|
@@ -121,14 +128,14 @@ export const mcpServerCommand = ({ example, defaultPort = MCP_HTTP_DEFAULT_PORT,
|
|
|
121
128
|
return;
|
|
122
129
|
}
|
|
123
130
|
if (byOf(one, lock.pid) !== "server") {
|
|
124
|
-
throw new CliError("validation_error", `mcp --http for ${one.invocation} (PID ${lock.pid}) was not started by \`${one.invocation} mcp server start\` — stop it where it runs`);
|
|
131
|
+
throw new CliError("validation_error", `mcp serve --http for ${one.invocation} (PID ${lock.pid}) was not started by \`${one.invocation} mcp server start\` — stop it where it runs`);
|
|
125
132
|
}
|
|
126
133
|
process.kill(lock.pid, "SIGTERM");
|
|
127
134
|
const sys = machine(this);
|
|
128
135
|
for (let look = 0; look < LOOKS && alive(lock.pid); look++)
|
|
129
136
|
await sys.pause();
|
|
130
137
|
if (alive(lock.pid))
|
|
131
|
-
throw new CliError("timeout", `mcp --http (PID ${lock.pid}) has not stopped yet`);
|
|
138
|
+
throw new CliError("timeout", `mcp serve --http (PID ${lock.pid}) has not stopped yet`);
|
|
132
139
|
answer(this, { profile: one.profile ?? null, stopped: true, pid: lock.pid }, [
|
|
133
140
|
`Stopped serving ${one.invocation} over HTTP (PID ${lock.pid}).`,
|
|
134
141
|
]);
|
|
@@ -139,7 +146,7 @@ export const mcpServerCommand = ({ example, defaultPort = MCP_HTTP_DEFAULT_PORT,
|
|
|
139
146
|
examples: [`${example} mcp server status --json`],
|
|
140
147
|
})
|
|
141
148
|
.summary("whether the HTTP MCP server runs, its login code and the apps logged in")
|
|
142
|
-
.description("whether `mcp --http` runs, on which port and address, the login code for a new browser app, and the apps logged in")
|
|
149
|
+
.description("whether `mcp serve --http` runs, on which port and address, the login code for a new browser app, and the apps logged in")
|
|
143
150
|
.action(function () {
|
|
144
151
|
const one = target(this);
|
|
145
152
|
const lock = readHttpLock(httpLockFile(one.tokenFile));
|
|
@@ -320,6 +320,7 @@ export declare const meetingPreparationAnswer: (packet: MeetingPreparation) => i
|
|
|
320
320
|
started_at: string | null;
|
|
321
321
|
url: string | null;
|
|
322
322
|
} | undefined;
|
|
323
|
+
untrusted?: string[] | undefined;
|
|
323
324
|
}[];
|
|
324
325
|
association: "unconfirmed-word-evidence";
|
|
325
326
|
vocabulary_fingerprint: string;
|
|
@@ -40,6 +40,7 @@ export declare const mapPreparationProjectEvidence: (section: NonNullable<Meetin
|
|
|
40
40
|
started_at: string | null;
|
|
41
41
|
url: string | null;
|
|
42
42
|
} | undefined;
|
|
43
|
+
untrusted?: string[] | undefined;
|
|
43
44
|
}[];
|
|
44
45
|
association: "unconfirmed-word-evidence";
|
|
45
46
|
vocabulary_fingerprint: string;
|
|
@@ -349,6 +349,7 @@ export declare const readMeetingPreparation: (read: MeetingPreparationReader, in
|
|
|
349
349
|
started_at: string | null;
|
|
350
350
|
url: string | null;
|
|
351
351
|
} | undefined;
|
|
352
|
+
untrusted?: string[] | undefined;
|
|
352
353
|
}[];
|
|
353
354
|
association: "unconfirmed-word-evidence";
|
|
354
355
|
vocabulary_fingerprint: string;
|
|
@@ -139,6 +139,7 @@ export const topicAnswer = (topic) => defined({
|
|
|
139
139
|
unread_count: topic.unreadCount,
|
|
140
140
|
last_message_at: topic.lastMessageAt,
|
|
141
141
|
created_at: topic.createdAt,
|
|
142
|
+
untrusted: ["title"],
|
|
142
143
|
});
|
|
143
144
|
export const folderAnswer = (folder) => {
|
|
144
145
|
const shown = "chats" in folder ? folder : undefined;
|
|
@@ -17,11 +17,10 @@ export interface McpFlags {
|
|
|
17
17
|
http?: boolean;
|
|
18
18
|
port?: string;
|
|
19
19
|
publicUrl?: string;
|
|
20
|
-
revoke?: boolean;
|
|
21
20
|
}
|
|
22
|
-
/** The token file of `<tool> mcp --http` for every profile: no profile name can start with `_`. */
|
|
21
|
+
/** The token file of `<tool> mcp serve --http` for every profile: no profile name can start with `_`. */
|
|
23
22
|
export declare const ALL_PROFILES = "_all";
|
|
24
|
-
/** Where `mcp --http` keeps the hashes of the tokens it issued — per profile, in the state folder. */
|
|
23
|
+
/** Where `mcp serve --http` keeps the hashes of the tokens it issued — per profile, in the state folder. */
|
|
25
24
|
export declare const httpTokenFile: (app: AppIdentity, profile: string, env: NodeJS.ProcessEnv) => string;
|
|
26
25
|
export declare const mcpCommand: (messenger: Messenger) => Command;
|
|
27
26
|
/**
|
|
@@ -44,7 +43,7 @@ export declare const redactTail: (text: string, home: string | undefined) => str
|
|
|
44
43
|
* servers with a trimmed environment leaves it out (measured 2026-09-28). Credentials are never copied.
|
|
45
44
|
*/
|
|
46
45
|
export declare const serverEntry: (app: AppIdentity, { profile, flags, execPath, scriptPath, env, }: {
|
|
47
|
-
/** Left out, the entry is `<tool> mcp`, the server of every profile. */
|
|
46
|
+
/** Left out, the entry is `<tool> mcp serve`, the server of every profile. */
|
|
48
47
|
profile?: string;
|
|
49
48
|
flags?: McpFlags;
|
|
50
49
|
execPath: string;
|