@rezti/dsh-rez-wechat 0.1.7 → 0.1.8

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/lib/web-shim.d.ts CHANGED
@@ -35,9 +35,16 @@ export declare function sessionStorePath(env?: NodeJS.ProcessEnv, home?: string)
35
35
  export declare function webBaseUrl(env?: NodeJS.ProcessEnv): string;
36
36
  export declare function loadSessionStore(path: string): SessionStore;
37
37
  export declare function saveSessionStore(path: string, store: SessionStore): void;
38
+ export declare function encodeClientRequest(method: string, payload: Record<string, unknown>, rpcId?: `${string}-${string}-${string}-${string}-${string}`): {
39
+ type: 'client-request';
40
+ rpcId: string;
41
+ method: string;
42
+ payload: Record<string, unknown>;
43
+ };
38
44
  export declare function unwrapRpc(body: unknown): unknown;
39
45
  export declare function rpcErrorMessage(body: unknown): string | undefined;
40
46
  export declare function extractSessionId(body: unknown): string | undefined;
47
+ export declare function extractWorkspaceId(body: unknown): string | undefined;
41
48
  export declare function historyEvents(body: unknown): unknown[];
42
49
  export declare function lastAssistantText(events: unknown[]): string;
43
50
  export declare function turnIsIdle(events: unknown[]): boolean;
package/lib/web-shim.js CHANGED
@@ -7,6 +7,7 @@
7
7
  * folder is named 微信, turns continue, and the model follows the web default
8
8
  * (Kimi if that is what the user selected).
9
9
  */
10
+ import { randomUUID } from 'node:crypto';
10
11
  import { chmodSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
11
12
  import { homedir } from 'node:os';
12
13
  import { basename, dirname, join } from 'node:path';
@@ -70,10 +71,19 @@ export function saveSessionStore(path, store) {
70
71
  mkdirSync(dirname(path), { recursive: true });
71
72
  writeFileSync(path, JSON.stringify(store, null, 2), 'utf8');
72
73
  }
74
+ export function encodeClientRequest(method, payload, rpcId = randomUUID()) {
75
+ return { type: 'client-request', rpcId, method, payload };
76
+ }
73
77
  export function unwrapRpc(body) {
74
78
  if (typeof body !== 'object' || body === null)
75
79
  return body;
76
80
  const rec = body;
81
+ if (rec.type === 'server-response' && typeof rec.result === 'object' && rec.result !== null) {
82
+ const result = rec.result;
83
+ if (result.ok === true)
84
+ return result.value;
85
+ return rec.result;
86
+ }
77
87
  if (rec.error !== undefined)
78
88
  return rec;
79
89
  if (typeof rec.result === 'object' && rec.result !== null)
@@ -116,6 +126,21 @@ export function extractSessionId(body) {
116
126
  }
117
127
  return undefined;
118
128
  }
129
+ export function extractWorkspaceId(body) {
130
+ if (typeof body !== 'object' || body === null)
131
+ return undefined;
132
+ const rec = body;
133
+ if (typeof rec.workspaceId === 'string')
134
+ return rec.workspaceId;
135
+ if (typeof rec.workspace === 'object' && rec.workspace !== null) {
136
+ const nested = rec.workspace;
137
+ if (typeof nested.workspaceId === 'string')
138
+ return nested.workspaceId;
139
+ if (typeof nested.id === 'string')
140
+ return nested.id;
141
+ }
142
+ return undefined;
143
+ }
119
144
  function flattenText(content) {
120
145
  if (typeof content === 'string')
121
146
  return content;
@@ -134,22 +159,25 @@ function flattenText(content) {
134
159
  })
135
160
  .join('');
136
161
  }
162
+ function unwrapHistoryItem(item) {
163
+ if (typeof item !== 'object' || item === null)
164
+ return item;
165
+ const rec = item;
166
+ return rec.event ?? item;
167
+ }
137
168
  export function historyEvents(body) {
138
169
  const unwrapped = unwrapRpc(body);
139
170
  if (Array.isArray(unwrapped))
140
- return unwrapped;
171
+ return unwrapped.map(unwrapHistoryItem);
141
172
  if (typeof unwrapped !== 'object' || unwrapped === null)
142
173
  return [];
143
174
  const rec = unwrapped;
144
- if (Array.isArray(rec.events))
145
- return rec.events;
146
- if (Array.isArray(rec.messages))
147
- return rec.messages;
148
- if (Array.isArray(rec.items))
149
- return rec.items;
150
- if (Array.isArray(rec.log))
151
- return rec.log;
152
- return [];
175
+ const raw = Array.isArray(rec.events) ? rec.events
176
+ : Array.isArray(rec.messages) ? rec.messages
177
+ : Array.isArray(rec.items) ? rec.items
178
+ : Array.isArray(rec.log) ? rec.log
179
+ : [];
180
+ return raw.map(unwrapHistoryItem);
153
181
  }
154
182
  export function lastAssistantText(events) {
155
183
  let text = '';
@@ -197,12 +225,14 @@ function collectModelRows(input, out, depth = 0, inheritedProvider) {
197
225
  if (typeof input !== 'object')
198
226
  return;
199
227
  const rec = input;
228
+ const isGroup = Array.isArray(rec.models);
229
+ const groupProvider = isGroup && typeof rec.id === 'string' ? rec.id : inheritedProvider;
200
230
  const provider = typeof rec.provider === 'string' ? rec.provider
201
231
  : typeof rec.providerID === 'string' ? rec.providerID
202
232
  : typeof rec.adapter === 'string' ? rec.adapter
203
- : inheritedProvider;
233
+ : groupProvider;
204
234
  const model = typeof rec.model === 'string' ? rec.model
205
- : typeof rec.id === 'string' && !rec.id.startsWith('session') ? rec.id
235
+ : !isGroup && typeof rec.id === 'string' && !rec.id.startsWith('session') ? rec.id
206
236
  : undefined;
207
237
  if (model !== undefined)
208
238
  out.push(provider !== undefined ? { provider, model } : { model });
@@ -250,12 +280,9 @@ export function pickWechatModel(models, env = process.env) {
250
280
  return coding ?? kimi[0];
251
281
  }
252
282
  export function selectModelPayloads(sessionId, ref) {
253
- const payloads = [
254
- { sessionId, model: ref.model, ...(ref.provider !== undefined ? { provider: ref.provider } : {}) },
255
- { sessionId, selection: ref },
256
- { sessionId, model: ref },
257
- ];
258
- return payloads;
283
+ if (ref.provider === undefined)
284
+ return [];
285
+ return [{ sessionId, provider: ref.provider, model: ref.model }];
259
286
  }
260
287
  export function writeWeixinDshLauncher(opts) {
261
288
  mkdirSync(opts.dataDir, { recursive: true });
@@ -283,12 +310,13 @@ async function sleep(ms) {
283
310
  }
284
311
  export async function postRpc(baseUrl, method, params) {
285
312
  const url = `${baseUrl.replace(/\/$/, '')}/api/${method}`;
313
+ const envelope = encodeClientRequest(method, params);
286
314
  let response;
287
315
  try {
288
316
  response = await fetch(url, {
289
317
  method: 'POST',
290
318
  headers: { 'content-type': 'application/json' },
291
- body: JSON.stringify(params),
319
+ body: JSON.stringify(envelope),
292
320
  });
293
321
  }
294
322
  catch (error) {
@@ -312,10 +340,12 @@ export async function postRpc(baseUrl, method, params) {
312
340
  }
313
341
  async function ensureWorkspace(rpc, cwd) {
314
342
  mkdirSync(cwd, { recursive: true });
315
- await rpc('workspace.create', { path: cwd, title: WECHAT_WORKSPACE_NAME });
343
+ const created = await rpc('workspace.create', { path: cwd });
344
+ return extractWorkspaceId(created);
316
345
  }
317
- async function createSession(rpc, cwd) {
318
- const created = await rpc('session.create', { cwd });
346
+ async function createSession(rpc, cwd, workspaceId) {
347
+ const payload = workspaceId !== undefined ? { workspaceId } : { cwd };
348
+ const created = await rpc('session.create', payload);
319
349
  const id = extractSessionId(created);
320
350
  if (id === undefined)
321
351
  throw new Error('session.create 没有返回 sessionId');
@@ -348,13 +378,7 @@ async function sessionAlive(rpc, sessionId) {
348
378
  return true;
349
379
  }
350
380
  catch {
351
- try {
352
- await rpc('session.get', { sessionId });
353
- return true;
354
- }
355
- catch {
356
- return false;
357
- }
381
+ return false;
358
382
  }
359
383
  }
360
384
  async function waitForAssistant(rpc, sessionId, beforeText, timeoutMs) {
@@ -367,12 +391,7 @@ async function waitForAssistant(rpc, sessionId, beforeText, timeoutMs) {
367
391
  events = historyEvents(await rpc('session.history', { sessionId, maxMessages: 40 }));
368
392
  }
369
393
  catch {
370
- try {
371
- events = historyEvents(await rpc('session.get', { sessionId }));
372
- }
373
- catch {
374
- events = [];
375
- }
394
+ events = [];
376
395
  }
377
396
  const text = lastAssistantText(events);
378
397
  if (text !== '')
@@ -400,7 +419,7 @@ export async function runHeadlessViaWeb(opts) {
400
419
  const key = basename(opts.cwd ?? process.cwd()) || 'default';
401
420
  const rpc = opts.rpc ?? ((method, params) => postRpc(webBaseUrl(env), method, params));
402
421
  const timeoutMs = opts.timeoutMs ?? Number(env.DSH_BRIDGE_TIMEOUT_MS ?? 10 * 60 * 1000);
403
- await ensureWorkspace(rpc, workspace);
422
+ const workspaceId = await ensureWorkspace(rpc, workspace);
404
423
  const store = loadSessionStore(storeFile);
405
424
  let sessionId = store.sessions[key]?.sessionId;
406
425
  const fresh = isFreshBridgeTurn(opts.task);
@@ -413,7 +432,7 @@ export async function runHeadlessViaWeb(opts) {
413
432
  sessionId = undefined;
414
433
  }
415
434
  if (sessionId === undefined) {
416
- sessionId = await createSession(rpc, workspace);
435
+ sessionId = await createSession(rpc, workspace, workspaceId);
417
436
  await maybeSelectModel(rpc, sessionId, env);
418
437
  }
419
438
  store.sessions[key] = { sessionId, updatedAt: Date.now() };
@@ -427,6 +446,7 @@ export async function runHeadlessViaWeb(opts) {
427
446
  }
428
447
  await rpc('session.prompt', {
429
448
  sessionId,
449
+ mode: 'queue',
430
450
  content: [{ type: 'text', text }],
431
451
  });
432
452
  return await waitForAssistant(rpc, sessionId, before, timeoutMs);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rezti/dsh-rez-wechat",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "ReZ-TI WeChat/WeCom bridges. Personal WeChat is QClaw/ClawBot scan-and-chat via dsh-wechat-bridge; WeCom group send uses wecom-mcp.",
5
5
  "type": "module",
6
6
  "engines": {