pi-courier 0.1.37 → 0.1.39
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/README.md +56 -3
- package/README.zh-CN.md +56 -3
- package/dist/auth/headless-login.d.ts +140 -0
- package/dist/auth/headless-login.d.ts.map +1 -0
- package/dist/auth/headless-login.js +386 -0
- package/dist/auth/headless-login.js.map +1 -0
- package/dist/quote-cache.d.ts +35 -0
- package/dist/quote-cache.d.ts.map +1 -0
- package/dist/quote-cache.js +51 -0
- package/dist/quote-cache.js.map +1 -0
- package/dist/rpc/command-map.d.ts +63 -0
- package/dist/rpc/command-map.d.ts.map +1 -1
- package/dist/rpc/command-map.js +327 -3
- package/dist/rpc/command-map.js.map +1 -1
- package/dist/rpc/message-router.d.ts +67 -2
- package/dist/rpc/message-router.d.ts.map +1 -1
- package/dist/rpc/message-router.js +297 -3
- package/dist/rpc/message-router.js.map +1 -1
- package/dist/rpc/pi-rpc.d.ts +64 -3
- package/dist/rpc/pi-rpc.d.ts.map +1 -1
- package/dist/rpc/pi-rpc.js +73 -16
- package/dist/rpc/pi-rpc.js.map +1 -1
- package/dist/rpc/project-manager.d.ts +5 -0
- package/dist/rpc/project-manager.d.ts.map +1 -1
- package/dist/rpc/project-manager.js +7 -0
- package/dist/rpc/project-manager.js.map +1 -1
- package/dist/transports/matrix.d.ts +2 -0
- package/dist/transports/matrix.d.ts.map +1 -1
- package/dist/transports/matrix.js +17 -0
- package/dist/transports/matrix.js.map +1 -1
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/scripts/crypto-native-hashes.json +3 -0
- package/scripts/ensure-crypto-native.d.mts +84 -1
- package/scripts/ensure-crypto-native.mjs +338 -10
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Headless login (issue #55, spec #51 ticket 4) — provider login without
|
|
3
|
+
* leaving Matrix.
|
|
4
|
+
*
|
|
5
|
+
* The admin runs `/login <provider> <oauth|api_key>` in the management room
|
|
6
|
+
* (single-project mode: a DM). An independent ModelRuntime (authPath pointing
|
|
7
|
+
* at pi's standard credential file, shared with every pi subprocess) drives
|
|
8
|
+
* the upstream login flow; the AuthInteraction callbacks are translated into
|
|
9
|
+
* chat messages — auth_url / device_code / progress become display messages,
|
|
10
|
+
* prompts (secret/text/select/manual_code) become questions whose answer is
|
|
11
|
+
* the room's next plain message, and 「取消」 aborts the flow at any time.
|
|
12
|
+
*
|
|
13
|
+
* The courier holds no credentials itself: the runtime writes them straight
|
|
14
|
+
* to <agentDir>/auth.json (file-locked merge write). pi subprocesses only
|
|
15
|
+
* read that file at startup, so after a successful login the caller restarts
|
|
16
|
+
* the IDLE rpcs (restartIdleRpcs in command-map) and tells the room to
|
|
17
|
+
* /reload the busy ones later.
|
|
18
|
+
*
|
|
19
|
+
* Capture priority: while a login flow in a room waits for an answer, plain
|
|
20
|
+
* messages there are captured BEFORE pending extension_ui questions (ticket
|
|
21
|
+
* requirement) — see the router's handleIncoming ordering.
|
|
22
|
+
*
|
|
23
|
+
* Upstream contract verified against
|
|
24
|
+
* node_modules/@earendil-works/pi-ai/dist/auth/types.d.ts (AuthInteraction:
|
|
25
|
+
* prompt rejects = abnormal exit, which is exactly the cancel path) and
|
|
26
|
+
* dist/core/model-runtime.d.ts (login/logout/listCredentials; listCredentials
|
|
27
|
+
* is ASYNC upstream — Promise<readonly CredentialInfo[]>).
|
|
28
|
+
*/
|
|
29
|
+
import * as path from "node:path";
|
|
30
|
+
import { builtinProviders } from "@earendil-works/pi-ai/providers/all";
|
|
31
|
+
import { getAgentDir, ModelRuntime } from "@earendil-works/pi-coding-agent";
|
|
32
|
+
import { formatReloadAllResult, restartIdleRpcs } from "../rpc/command-map.js";
|
|
33
|
+
/**
|
|
34
|
+
* Enumerate the builtin providers that support an INTERACTIVE login. Presence
|
|
35
|
+
* of `auth.apiKey`/`auth.oauth` alone is not enough — ambient-only providers
|
|
36
|
+
* (env vars, AWS profiles) omit `login` and cannot be logged in from a chat.
|
|
37
|
+
* Sorted by id for a stable listing.
|
|
38
|
+
*/
|
|
39
|
+
export function listLoginProviders() {
|
|
40
|
+
return builtinProviders()
|
|
41
|
+
.filter((p) => p.auth?.oauth?.login || p.auth?.apiKey?.login)
|
|
42
|
+
.map((p) => ({
|
|
43
|
+
id: p.id,
|
|
44
|
+
name: p.name,
|
|
45
|
+
oauth: Boolean(p.auth?.oauth?.login),
|
|
46
|
+
apiKey: Boolean(p.auth?.apiKey?.login),
|
|
47
|
+
}))
|
|
48
|
+
.sort((a, b) => a.id.localeCompare(b.id));
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Render the /login (no-arg) listing: one line per provider with its methods
|
|
52
|
+
* and a ✅ 已认证(<type>) badge for each stored credential. Pure.
|
|
53
|
+
*/
|
|
54
|
+
export function formatLoginProviders(providers, credentials = []) {
|
|
55
|
+
if (providers.length === 0)
|
|
56
|
+
return "没有可登录的 provider。";
|
|
57
|
+
const byProvider = new Map();
|
|
58
|
+
for (const c of credentials) {
|
|
59
|
+
const list = byProvider.get(c.providerId) ?? [];
|
|
60
|
+
list.push(c);
|
|
61
|
+
byProvider.set(c.providerId, list);
|
|
62
|
+
}
|
|
63
|
+
const lines = providers.map((p) => {
|
|
64
|
+
const methods = [p.oauth ? "oauth" : null, p.apiKey ? "api_key" : null]
|
|
65
|
+
.filter(Boolean)
|
|
66
|
+
.join(" / ");
|
|
67
|
+
const creds = byProvider.get(p.id);
|
|
68
|
+
const badge = creds?.length
|
|
69
|
+
? ` ✅ 已认证(${creds.map((c) => c.type).join(" + ")})`
|
|
70
|
+
: "";
|
|
71
|
+
return `• ${p.id} — ${p.name}(${methods})${badge}`;
|
|
72
|
+
});
|
|
73
|
+
return `🔐 可登录 provider(${providers.length}):\n${lines.join("\n")}\n\n用 /login <provider> <oauth|api_key> 开始登录。`;
|
|
74
|
+
}
|
|
75
|
+
/** /auth — the stored credentials, one line each (no secrets, metadata only). */
|
|
76
|
+
export function formatCredentials(credentials) {
|
|
77
|
+
if (credentials.length === 0) {
|
|
78
|
+
return "💤 暂无已保存凭据(用 /login <provider> <oauth|api_key> 登录)。";
|
|
79
|
+
}
|
|
80
|
+
const lines = credentials.map((c) => `• ${c.providerId} — ${c.type}`);
|
|
81
|
+
return `🔐 已保存凭据 (${credentials.length}):\n${lines.join("\n")}`;
|
|
82
|
+
}
|
|
83
|
+
// ===========================================================================
|
|
84
|
+
// AuthInteraction translation (pure — directly testable)
|
|
85
|
+
// ===========================================================================
|
|
86
|
+
/** The room message for an upstream login prompt (issue #55). Secret prompts
|
|
87
|
+
* must warn that the key stays in the room history. */
|
|
88
|
+
export function translateAuthPrompt(prompt) {
|
|
89
|
+
switch (prompt.type) {
|
|
90
|
+
case "select": {
|
|
91
|
+
const lines = prompt.options.map((o, i) => {
|
|
92
|
+
const desc = o.description ? ` — ${o.description}` : "";
|
|
93
|
+
return `${i + 1}. ${o.label}${desc}`;
|
|
94
|
+
});
|
|
95
|
+
return `❓ ${prompt.message}\n${lines.join("\n")}\n回复序号选择(发送「取消」放弃)`;
|
|
96
|
+
}
|
|
97
|
+
case "secret":
|
|
98
|
+
return [
|
|
99
|
+
`❓ ${prompt.message}`,
|
|
100
|
+
"直接回复密钥内容。",
|
|
101
|
+
"⚠️ 密钥将留在房间历史,建议用后删除该消息(发送「取消」放弃)",
|
|
102
|
+
].join("\n");
|
|
103
|
+
case "manual_code":
|
|
104
|
+
return `❓ ${prompt.message}\n把浏览器授权后最终跳转到的完整 URL 直接粘贴回来(发送「取消」放弃)`;
|
|
105
|
+
default: // text
|
|
106
|
+
return `❓ ${prompt.message}\n直接回复内容作为答案(发送「取消」放弃)`;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/** The room message for an upstream login event: auth_url / device_code /
|
|
110
|
+
* progress / info → display-only lines (no answer expected). */
|
|
111
|
+
export function translateAuthEvent(event) {
|
|
112
|
+
switch (event.type) {
|
|
113
|
+
case "auth_url": {
|
|
114
|
+
const lines = [`🌐 请在浏览器打开以下链接完成授权:\n${event.url}`];
|
|
115
|
+
if (event.instructions)
|
|
116
|
+
lines.push(event.instructions);
|
|
117
|
+
return lines.join("\n");
|
|
118
|
+
}
|
|
119
|
+
case "device_code": {
|
|
120
|
+
const lines = [
|
|
121
|
+
`🔑 设备码: ${event.userCode}`,
|
|
122
|
+
`请在浏览器打开 ${event.verificationUri} 并输入上述设备码。`,
|
|
123
|
+
];
|
|
124
|
+
if (event.expiresInSeconds) {
|
|
125
|
+
lines.push(`(有效期约 ${Math.max(1, Math.round(event.expiresInSeconds / 60))} 分钟)`);
|
|
126
|
+
}
|
|
127
|
+
return lines.join("\n");
|
|
128
|
+
}
|
|
129
|
+
case "progress":
|
|
130
|
+
return `⏳ ${event.message}`;
|
|
131
|
+
default: { // info
|
|
132
|
+
const linkLines = (event.links ?? []).map((l) => `🔗 ${l.label ? `${l.label}: ` : ""}${l.url}`);
|
|
133
|
+
return [`ℹ️ ${event.message}`, ...linkLines].join("\n");
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/** Map a room message onto a login prompt's answer (issue #55). Pure.
|
|
138
|
+
* 「取消」 is exact; select maps the 1-based index onto the option ID
|
|
139
|
+
* (out of range re-asks); every other prompt type takes the whole message. */
|
|
140
|
+
export function parseLoginAnswer(prompt, text) {
|
|
141
|
+
if (text === "取消")
|
|
142
|
+
return { kind: "cancel" };
|
|
143
|
+
if (prompt.type === "select") {
|
|
144
|
+
const count = prompt.options.length;
|
|
145
|
+
const index = /^\d+$/.test(text) ? Number.parseInt(text, 10) : 0;
|
|
146
|
+
if (index >= 1 && index <= count) {
|
|
147
|
+
return { kind: "value", value: prompt.options[index - 1].id };
|
|
148
|
+
}
|
|
149
|
+
return { kind: "invalid", hint: `⚠️ 请回复 1 到 ${count} 之间的序号(发送「取消」放弃)` };
|
|
150
|
+
}
|
|
151
|
+
return { kind: "value", value: text };
|
|
152
|
+
}
|
|
153
|
+
/** The credential file this module writes: pi's standard auth.json under the
|
|
154
|
+
* agent dir (PI_CODING_AGENT_DIR respected) — shared with every pi subprocess. */
|
|
155
|
+
export function defaultAuthPath() {
|
|
156
|
+
return path.join(getAgentDir(), "auth.json");
|
|
157
|
+
}
|
|
158
|
+
async function defaultRuntimeFactory(authPath) {
|
|
159
|
+
// No model-catalog network access needed for login (allowModelNetwork false
|
|
160
|
+
// is upstream's default) and no key validation — we only write credentials.
|
|
161
|
+
return ModelRuntime.create({ authPath });
|
|
162
|
+
}
|
|
163
|
+
/** Rejection carried out of a cancelled login flow (prompt rejects = upstream
|
|
164
|
+
* exits abnormally — the documented cancel path). */
|
|
165
|
+
export class LoginCancelledError extends Error {
|
|
166
|
+
constructor() {
|
|
167
|
+
super("login cancelled");
|
|
168
|
+
this.name = "LoginCancelledError";
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
export class LoginManager {
|
|
172
|
+
deps;
|
|
173
|
+
pending = new Map();
|
|
174
|
+
runtime;
|
|
175
|
+
constructor(deps) {
|
|
176
|
+
this.deps = deps;
|
|
177
|
+
}
|
|
178
|
+
/** Whether a login flow is active in this room (router capture gate). */
|
|
179
|
+
isPending(chatId) {
|
|
180
|
+
return this.pending.has(chatId);
|
|
181
|
+
}
|
|
182
|
+
/** /login with no arguments — the login-able provider list with auth badges. */
|
|
183
|
+
async listProviders(chatId, transport) {
|
|
184
|
+
let credentials = [];
|
|
185
|
+
try {
|
|
186
|
+
credentials = [...(await (await this.getRuntime()).listCredentials())];
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
// The badge is best-effort — the capability listing still stands.
|
|
190
|
+
}
|
|
191
|
+
await this.deps.sendReply(chatId, transport, formatLoginProviders(listLoginProviders(), credentials));
|
|
192
|
+
}
|
|
193
|
+
/** /login <provider> [oauth|api_key] — start a flow. Gates (admin +
|
|
194
|
+
* management room) are the router's job; this validates the target. */
|
|
195
|
+
async startLogin(chatId, transport, providerId, method) {
|
|
196
|
+
if (this.pending.has(chatId)) {
|
|
197
|
+
await this.deps.sendReply(chatId, transport, "⚠️ 本房间已有登录流程进行中(发送「取消」中止后再试)。");
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
const provider = listLoginProviders().find((p) => p.id === providerId);
|
|
201
|
+
if (!provider) {
|
|
202
|
+
await this.deps.sendReply(chatId, transport, `❌ 未知 provider: ${providerId}(用 /login 查看可登录列表)`);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const supported = [];
|
|
206
|
+
if (provider.oauth)
|
|
207
|
+
supported.push("oauth");
|
|
208
|
+
if (provider.apiKey)
|
|
209
|
+
supported.push("api_key");
|
|
210
|
+
const requested = method?.toLowerCase();
|
|
211
|
+
let chosen;
|
|
212
|
+
if (requested === "oauth" || requested === "api_key") {
|
|
213
|
+
chosen = requested;
|
|
214
|
+
}
|
|
215
|
+
else if (requested) {
|
|
216
|
+
await this.deps.sendReply(chatId, transport, "用法: /login <provider> <oauth|api_key>");
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
else if (supported.length === 1) {
|
|
220
|
+
chosen = supported[0]; // unambiguous — pick the only way
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
await this.deps.sendReply(chatId, transport, `⚠️ ${providerId} 支持多种登录方式(${supported.join(" / ")}),请指定:\n` +
|
|
224
|
+
`/login ${providerId} oauth\n/login ${providerId} api_key`);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
if (!supported.includes(chosen)) {
|
|
228
|
+
await this.deps.sendReply(chatId, transport, `❌ ${providerId} 不支持 ${chosen} 登录(支持: ${supported.join(" / ")})`);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
const entry = {
|
|
232
|
+
chatId,
|
|
233
|
+
transport,
|
|
234
|
+
providerId,
|
|
235
|
+
method: chosen,
|
|
236
|
+
controller: new AbortController(),
|
|
237
|
+
finished: false,
|
|
238
|
+
};
|
|
239
|
+
this.pending.set(chatId, entry);
|
|
240
|
+
await this.deps.sendReply(chatId, transport, `🔑 已开始 ${providerId} ${chosen} 登录流程,请按提示操作(任意时刻发送「取消」中止)。`);
|
|
241
|
+
// Long-running by design: never block the message pipeline — the flow
|
|
242
|
+
// continues over the capture channel (deliver / cancel).
|
|
243
|
+
void this.run(entry);
|
|
244
|
+
}
|
|
245
|
+
/** Feed a plain room message into the pending flow. Returns true when the
|
|
246
|
+
* message was consumed. 「取消」 aborts at ANY moment; otherwise only
|
|
247
|
+
* messages arriving while a prompt is waiting count as the answer — the
|
|
248
|
+
* room stays usable during long OAuth waits (device polling etc.). */
|
|
249
|
+
async deliver(chatId, text) {
|
|
250
|
+
const entry = this.pending.get(chatId);
|
|
251
|
+
if (!entry || entry.finished)
|
|
252
|
+
return false;
|
|
253
|
+
if (text === "取消") {
|
|
254
|
+
await this.cancel(chatId);
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
const current = entry.current;
|
|
258
|
+
if (!current)
|
|
259
|
+
return false;
|
|
260
|
+
const answer = parseLoginAnswer(current.prompt, text);
|
|
261
|
+
if (answer.kind === "invalid") {
|
|
262
|
+
await this.deps.sendReply(chatId, entry.transport, answer.hint);
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
if (answer.kind === "cancel")
|
|
266
|
+
return true; // unreachable: 取消 handled above
|
|
267
|
+
current.submit(answer.value);
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
/** Abort the room's pending flow (「取消」 or a replacement command). */
|
|
271
|
+
async cancel(chatId) {
|
|
272
|
+
const entry = this.pending.get(chatId);
|
|
273
|
+
if (!entry || entry.finished)
|
|
274
|
+
return false;
|
|
275
|
+
entry.finished = true;
|
|
276
|
+
this.pending.delete(chatId);
|
|
277
|
+
// Aborting rejects the parked prompt (LoginCancelledError) → upstream
|
|
278
|
+
// login exits abnormally; run() stays silent for this path.
|
|
279
|
+
entry.controller.abort();
|
|
280
|
+
await this.deps.sendReply(chatId, entry.transport, `🛑 已取消 ${entry.providerId} 的登录流程`);
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
/** /logout <provider> — delete the stored credential (metadata check first;
|
|
284
|
+
* logout itself never sees the secret). Running pi processes keep their
|
|
285
|
+
* in-memory credential until restarted. */
|
|
286
|
+
async logout(chatId, transport, providerId) {
|
|
287
|
+
try {
|
|
288
|
+
const runtime = await this.getRuntime();
|
|
289
|
+
const credentials = await runtime.listCredentials();
|
|
290
|
+
if (!credentials.some((c) => c.providerId === providerId)) {
|
|
291
|
+
await this.deps.sendReply(chatId, transport, `❌ ${providerId} 没有已保存的凭据(用 /auth 查看)`);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
await runtime.logout(providerId);
|
|
295
|
+
await this.deps.sendReply(chatId, transport, `✅ 已删除 ${providerId} 的凭据。运行中的 pi 进程仍持有旧凭据,空闲后执行 /reload all 使登出生效。`);
|
|
296
|
+
}
|
|
297
|
+
catch (err) {
|
|
298
|
+
await this.deps.sendReply(chatId, transport, `❌ 登出失败: ${err.message}`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
/** /auth — which providers are authenticated and how. */
|
|
302
|
+
async authStatus(chatId, transport) {
|
|
303
|
+
try {
|
|
304
|
+
const runtime = await this.getRuntime();
|
|
305
|
+
await this.deps.sendReply(chatId, transport, formatCredentials([...(await runtime.listCredentials())]));
|
|
306
|
+
}
|
|
307
|
+
catch (err) {
|
|
308
|
+
await this.deps.sendReply(chatId, transport, `❌ 读取凭据失败: ${err.message}`);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
// -------------------------------------------------------------------------
|
|
312
|
+
async getRuntime() {
|
|
313
|
+
this.runtime ??= (this.deps.runtimeFactory ?? defaultRuntimeFactory)(this.deps.authPath ?? defaultAuthPath());
|
|
314
|
+
return this.runtime;
|
|
315
|
+
}
|
|
316
|
+
/** Drive one login flow to completion: translate the AuthInteraction into
|
|
317
|
+
* chat round-trips, then restart the idle rpcs on success. */
|
|
318
|
+
async run(entry) {
|
|
319
|
+
const { chatId, transport, providerId, method } = entry;
|
|
320
|
+
const interaction = {
|
|
321
|
+
signal: entry.controller.signal,
|
|
322
|
+
prompt: (p) => this.promptUser(entry, p),
|
|
323
|
+
notify: (e) => {
|
|
324
|
+
void this.deps.sendReply(chatId, transport, translateAuthEvent(e));
|
|
325
|
+
},
|
|
326
|
+
};
|
|
327
|
+
try {
|
|
328
|
+
const runtime = await this.getRuntime();
|
|
329
|
+
await runtime.login(providerId, method, interaction);
|
|
330
|
+
if (entry.controller.signal.aborted)
|
|
331
|
+
return; // cancelled mid-write
|
|
332
|
+
entry.finished = true;
|
|
333
|
+
this.drop(entry);
|
|
334
|
+
const lines = [`✅ ${providerId} 登录成功,凭据已写入 ${this.deps.authPath ?? defaultAuthPath()}`];
|
|
335
|
+
try {
|
|
336
|
+
// pi subprocesses read the credential file once at startup — restart
|
|
337
|
+
// the idle ones now, tell the room about the busy ones (issue #55).
|
|
338
|
+
lines.push(formatReloadAllResult(await restartIdleRpcs(this.deps.allRpcs(), this.deps.onRestarted)));
|
|
339
|
+
}
|
|
340
|
+
catch {
|
|
341
|
+
// Restart trouble must never fail the (already persisted) login reply.
|
|
342
|
+
}
|
|
343
|
+
await this.deps.sendReply(chatId, transport, lines.join("\n"));
|
|
344
|
+
}
|
|
345
|
+
catch (err) {
|
|
346
|
+
entry.finished = true;
|
|
347
|
+
this.drop(entry);
|
|
348
|
+
if (err instanceof LoginCancelledError || entry.controller.signal.aborted)
|
|
349
|
+
return;
|
|
350
|
+
await this.deps.sendReply(chatId, transport, `❌ ${providerId} 登录失败: ${err.message}`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
/** Remove this flow's pending entry — and only its own: after a quick
|
|
354
|
+
* cancel + re-login in the same room, a still-draining old flow must not
|
|
355
|
+
* evict the NEW entry (the map is keyed by chat). */
|
|
356
|
+
drop(entry) {
|
|
357
|
+
if (this.pending.get(entry.chatId) === entry)
|
|
358
|
+
this.pending.delete(entry.chatId);
|
|
359
|
+
}
|
|
360
|
+
/** Park a prompt: post the question, resolve with the room's next answer,
|
|
361
|
+
* reject on abort (upstream treats a rejected prompt as abnormal exit —
|
|
362
|
+
* the documented cancel path). */
|
|
363
|
+
async promptUser(entry, prompt) {
|
|
364
|
+
await this.deps.sendReply(entry.chatId, entry.transport, translateAuthPrompt(prompt));
|
|
365
|
+
return new Promise((resolve, reject) => {
|
|
366
|
+
if (entry.controller.signal.aborted) {
|
|
367
|
+
reject(new LoginCancelledError());
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
const onAbort = () => {
|
|
371
|
+
entry.current = undefined;
|
|
372
|
+
reject(new LoginCancelledError());
|
|
373
|
+
};
|
|
374
|
+
entry.controller.signal.addEventListener("abort", onAbort, { once: true });
|
|
375
|
+
entry.current = {
|
|
376
|
+
prompt,
|
|
377
|
+
submit: (value) => {
|
|
378
|
+
entry.controller.signal.removeEventListener("abort", onAbort);
|
|
379
|
+
entry.current = undefined;
|
|
380
|
+
resolve(value);
|
|
381
|
+
},
|
|
382
|
+
};
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
//# sourceMappingURL=headless-login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"headless-login.js","sourceRoot":"","sources":["../../src/auth/headless-login.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reply-quote ring cache (issue #56 票5) — pure, in-memory, no I/O.
|
|
3
|
+
*
|
|
4
|
+
* The Matrix transport records every inbound message's event_id → excerpt
|
|
5
|
+
* (per room, FIFO eviction at MAX_ENTRIES_PER_ROOM) so a later reply to a
|
|
6
|
+
* historical message can carry a short quote of the referenced text into the
|
|
7
|
+
* prompt. This is how "这个"/"上面那个" become resolvable for the agent.
|
|
8
|
+
*
|
|
9
|
+
* Deliberately memory-bounded: 50 entries/room, excerpt capped at 200 chars.
|
|
10
|
+
* Cache misses are a normal, silent downgrade — quotes are best-effort context.
|
|
11
|
+
*/
|
|
12
|
+
/** A cleaned one-line excerpt of a quoted message plus its sender. */
|
|
13
|
+
export interface QuoteEntry {
|
|
14
|
+
username: string;
|
|
15
|
+
excerpt: string;
|
|
16
|
+
}
|
|
17
|
+
export interface QuoteCache {
|
|
18
|
+
/** Record a message for later quote lookups in that room. */
|
|
19
|
+
record(roomId: string, eventId: string, entry: QuoteEntry): void;
|
|
20
|
+
/** The recorded entry, or undefined when unknown (miss = silent no-quote). */
|
|
21
|
+
lookup(roomId: string, eventId: string): QuoteEntry | undefined;
|
|
22
|
+
}
|
|
23
|
+
/** Ring capacity per room; beyond this the oldest messages fall out. */
|
|
24
|
+
export declare const MAX_ENTRIES_PER_ROOM = 50;
|
|
25
|
+
/** Excerpt cap: enough context to identify a message, small enough for a prompt prefix. */
|
|
26
|
+
export declare const EXCERPT_MAX_CHARS = 200;
|
|
27
|
+
/** Single-line, trimmed excerpt capped at EXCERPT_MAX_CHARS ("…" marks truncation). */
|
|
28
|
+
export declare function toExcerpt(body: string, max?: number): string;
|
|
29
|
+
/**
|
|
30
|
+
* Per-room FIFO cache. Insertion-ordered Maps give O(1) refresh/evict:
|
|
31
|
+
* re-recording an existing event id deletes then re-inserts it, so the ring
|
|
32
|
+
* evicts by last-seen order and a refresh never wastes a slot.
|
|
33
|
+
*/
|
|
34
|
+
export declare function createQuoteCache(): QuoteCache;
|
|
35
|
+
//# sourceMappingURL=quote-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quote-cache.d.ts","sourceRoot":"","sources":["../src/quote-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,sEAAsE;AACtE,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,6DAA6D;IAC7D,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IACjE,8EAA8E;IAC9E,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;CACjE;AAED,wEAAwE;AACxE,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC,2FAA2F;AAC3F,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAErC,uFAAuF;AACvF,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,SAAoB,GAAG,MAAM,CAGvE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,UAAU,CAsB7C"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reply-quote ring cache (issue #56 票5) — pure, in-memory, no I/O.
|
|
3
|
+
*
|
|
4
|
+
* The Matrix transport records every inbound message's event_id → excerpt
|
|
5
|
+
* (per room, FIFO eviction at MAX_ENTRIES_PER_ROOM) so a later reply to a
|
|
6
|
+
* historical message can carry a short quote of the referenced text into the
|
|
7
|
+
* prompt. This is how "这个"/"上面那个" become resolvable for the agent.
|
|
8
|
+
*
|
|
9
|
+
* Deliberately memory-bounded: 50 entries/room, excerpt capped at 200 chars.
|
|
10
|
+
* Cache misses are a normal, silent downgrade — quotes are best-effort context.
|
|
11
|
+
*/
|
|
12
|
+
/** Ring capacity per room; beyond this the oldest messages fall out. */
|
|
13
|
+
export const MAX_ENTRIES_PER_ROOM = 50;
|
|
14
|
+
/** Excerpt cap: enough context to identify a message, small enough for a prompt prefix. */
|
|
15
|
+
export const EXCERPT_MAX_CHARS = 200;
|
|
16
|
+
/** Single-line, trimmed excerpt capped at EXCERPT_MAX_CHARS ("…" marks truncation). */
|
|
17
|
+
export function toExcerpt(body, max = EXCERPT_MAX_CHARS) {
|
|
18
|
+
const oneLine = body.replace(/\s+/g, " ").trim();
|
|
19
|
+
return oneLine.length > max ? `${oneLine.slice(0, max)}…` : oneLine;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Per-room FIFO cache. Insertion-ordered Maps give O(1) refresh/evict:
|
|
23
|
+
* re-recording an existing event id deletes then re-inserts it, so the ring
|
|
24
|
+
* evicts by last-seen order and a refresh never wastes a slot.
|
|
25
|
+
*/
|
|
26
|
+
export function createQuoteCache() {
|
|
27
|
+
const rooms = new Map();
|
|
28
|
+
return {
|
|
29
|
+
record(roomId, eventId, entry) {
|
|
30
|
+
if (!eventId || !entry.excerpt)
|
|
31
|
+
return;
|
|
32
|
+
let room = rooms.get(roomId);
|
|
33
|
+
if (!room) {
|
|
34
|
+
room = new Map();
|
|
35
|
+
rooms.set(roomId, room);
|
|
36
|
+
}
|
|
37
|
+
room.delete(eventId);
|
|
38
|
+
room.set(eventId, entry);
|
|
39
|
+
while (room.size > MAX_ENTRIES_PER_ROOM) {
|
|
40
|
+
const oldest = room.keys().next().value;
|
|
41
|
+
if (oldest === undefined)
|
|
42
|
+
break;
|
|
43
|
+
room.delete(oldest);
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
lookup(roomId, eventId) {
|
|
47
|
+
return rooms.get(roomId)?.get(eventId);
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=quote-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quote-cache.js","sourceRoot":"","sources":["../src/quote-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAeH,wEAAwE;AACxE,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAEvC,2FAA2F;AAC3F,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAErC,uFAAuF;AACvF,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,GAAG,GAAG,iBAAiB;IAC7D,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACjD,OAAO,OAAO,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC;AACtE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB;IAC9B,MAAM,KAAK,GAAG,IAAI,GAAG,EAAmC,CAAC;IACzD,OAAO;QACL,MAAM,CAAC,MAAc,EAAE,OAAe,EAAE,KAAiB;YACvD,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO;gBAAE,OAAO;YACvC,IAAI,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC7B,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,IAAI,GAAG,IAAI,GAAG,EAAE,CAAC;gBACjB,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YAC1B,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACrB,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YACzB,OAAO,IAAI,CAAC,IAAI,GAAG,oBAAoB,EAAE,CAAC;gBACxC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;gBACxC,IAAI,MAAM,KAAK,SAAS;oBAAE,MAAM;gBAChC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACtB,CAAC;QACH,CAAC;QACD,MAAM,CAAC,MAAc,EAAE,OAAe;YACpC,OAAO,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;QACzC,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -15,11 +15,74 @@
|
|
|
15
15
|
* help surface; ChallengeAuth no longer has its own help text.
|
|
16
16
|
*/
|
|
17
17
|
import type { PiRpc } from "./pi-rpc.js";
|
|
18
|
+
/** Mirror of the upstream steering/followUp queues (router's queue_update view). */
|
|
19
|
+
export interface QueueSnapshot {
|
|
20
|
+
steering: string[];
|
|
21
|
+
followUp: string[];
|
|
22
|
+
}
|
|
18
23
|
export interface SlashCommandContext {
|
|
19
24
|
rpc: PiRpc;
|
|
20
25
|
/** Send a reply back to the originating chat */
|
|
21
26
|
reply: (text: string) => Promise<void>;
|
|
27
|
+
/** Live queue snapshot maintained by the router (queue_update mirror).
|
|
28
|
+
* Absent/undefined entries mean "no queue seen yet" = empty. */
|
|
29
|
+
queueView?: () => QueueSnapshot | undefined;
|
|
30
|
+
/** Every rpc of this instance (default + started project rpcs) — enables
|
|
31
|
+
* `/reload all` (issue #55). Absent = /reload stays single-process. */
|
|
32
|
+
allRpcs?: () => PiRpc[];
|
|
33
|
+
/** Router-owned per-rpc transient state (queue mirror, pending extension
|
|
34
|
+
* questions) must not survive a restart: the new subprocess knows nothing
|
|
35
|
+
* of old question ids, and a stale question would swallow the room's next
|
|
36
|
+
* message as a bogus "answer". */
|
|
37
|
+
clearRpcState?: (rpc: PiRpc) => void;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Unified queue warning for /stop and /interrupt replies: abort preserves the
|
|
41
|
+
* upstream queues (RPC has no clear_queue), so remaining messages would fire
|
|
42
|
+
* on the NEXT turn — surface that explicitly. Null when nothing is queued.
|
|
43
|
+
*/
|
|
44
|
+
export declare function queueWarning(snapshot: QueueSnapshot | undefined): string | null;
|
|
45
|
+
export interface ReloadAllResult {
|
|
46
|
+
/** Labels of rpcs that were idle and got restarted. */
|
|
47
|
+
restarted: string[];
|
|
48
|
+
/** Labels of rpcs that were streaming — skipped, need a later /reload. */
|
|
49
|
+
busy: string[];
|
|
50
|
+
/** Labels of rpcs that could not be queried (not started / unreachable) —
|
|
51
|
+
* they pick the new credentials up on their next start anyway. */
|
|
52
|
+
skipped: string[];
|
|
22
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Restart every IDLE rpc of the instance so a fresh pi subprocess re-reads
|
|
56
|
+
* pi's credential/config files (issue #55: after /login writes credentials,
|
|
57
|
+
* and for /reload all). Busy (streaming) rpcs are skipped — an abrupt restart
|
|
58
|
+
* would kill a running turn; the room is told to /reload them later.
|
|
59
|
+
* Unqueryable rpcs (lazy, never started) are skipped: their FIRST start reads
|
|
60
|
+
* the fresh file anyway.
|
|
61
|
+
*/
|
|
62
|
+
export declare function restartIdleRpcs(rpcs: readonly PiRpc[], onRestarted?: (rpc: PiRpc) => void): Promise<ReloadAllResult>;
|
|
63
|
+
/** Render a reload summary for the room. Pure — directly testable. */
|
|
64
|
+
export declare function formatReloadAllResult(result: ReloadAllResult): string;
|
|
65
|
+
export interface SessionSummary {
|
|
66
|
+
/** Absolute path — what /switch hands to switchSession. */
|
|
67
|
+
path: string;
|
|
68
|
+
/** File name, e.g. 2026-09-05T12-00-00-000Z_<sessionId>.jsonl. */
|
|
69
|
+
file: string;
|
|
70
|
+
/** Last-modified time (ms epoch) — the list sorts by it, newest first. */
|
|
71
|
+
mtimeMs: number;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Scan a session dir for pi session files (*.jsonl), newest first. A missing
|
|
75
|
+
* or unreadable dir yields [] (the caller reports "找不到会话目录").
|
|
76
|
+
*/
|
|
77
|
+
export declare function listSessions(dir: string): SessionSummary[];
|
|
78
|
+
/** The session dir a process scans: its own --session-dir, else pi's default. */
|
|
79
|
+
export declare function resolveSessionDir(rpc: PiRpc): string;
|
|
80
|
+
/**
|
|
81
|
+
* Display name of a session: the LAST "session_info" entry's name field.
|
|
82
|
+
* Regex-scanned per jsonl line (no full JSON parse); giant files skip the
|
|
83
|
+
* lookup — the name is a display nicety, never load-bearing.
|
|
84
|
+
*/
|
|
85
|
+
export declare function readSessionName(filePath: string): string | undefined;
|
|
23
86
|
/** Returns true if the command was handled (something was done / replied). */
|
|
24
87
|
export declare function handleSlashCommand(text: string, ctx: SlashCommandContext): Promise<boolean>;
|
|
25
88
|
//# sourceMappingURL=command-map.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-map.d.ts","sourceRoot":"","sources":["../../src/rpc/command-map.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;
|
|
1
|
+
{"version":3,"file":"command-map.d.ts","sourceRoot":"","sources":["../../src/rpc/command-map.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAMH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,oFAAoF;AACpF,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,KAAK,CAAC;IACX,gDAAgD;IAChD,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC;qEACiE;IACjE,SAAS,CAAC,EAAE,MAAM,aAAa,GAAG,SAAS,CAAC;IAC5C;4EACwE;IACxE,OAAO,CAAC,EAAE,MAAM,KAAK,EAAE,CAAC;IACxB;;;uCAGmC;IACnC,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC;CACtC;AAQD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,aAAa,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAO/E;AAUD,MAAM,WAAW,eAAe;IAC9B,uDAAuD;IACvD,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,0EAA0E;IAC1E,IAAI,EAAE,MAAM,EAAE,CAAC;IACf;uEACmE;IACnE,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAOD;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,SAAS,KAAK,EAAE,EACtB,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,GACjC,OAAO,CAAC,eAAe,CAAC,CAkB1B;AAED,sEAAsE;AACtE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAcrE;AA4CD,MAAM,WAAW,cAAc;IAC7B,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,EAAE,CAkB1D;AAED,iFAAiF;AACjF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,KAAK,GAAG,MAAM,CAEpD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAiBpE;AAyBD,8EAA8E;AAC9E,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,mBAAmB,GACvB,OAAO,CAAC,OAAO,CAAC,CAyTlB"}
|