lark-remote 0.1.12 → 0.2.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 +37 -0
- package/README.en.md +2 -6
- package/README.md +3 -7
- package/dist/bridge/final-usage.d.ts +24 -0
- package/dist/bridge/final-usage.js +55 -0
- package/dist/bridge/inbound-media.d.ts +0 -3
- package/dist/bridge/inbound-media.js +0 -1
- package/dist/bridge/index.d.ts +7 -7
- package/dist/bridge/index.js +100 -133
- package/dist/bridge/queue-manager.js +4 -3
- package/dist/card/approval-render.js +39 -94
- package/dist/card/bash-renderer.js +12 -21
- package/dist/card/card-shared.d.ts +14 -5
- package/dist/card/card-shared.js +23 -34
- package/dist/card/run-renderer.d.ts +0 -22
- package/dist/card/run-renderer.js +206 -153
- package/dist/card/run-state.d.ts +29 -1
- package/dist/card/run-state.js +172 -84
- package/dist/card/text-truncate.d.ts +0 -9
- package/dist/card/text-truncate.js +0 -9
- package/dist/card/tool-render.d.ts +4 -1
- package/dist/card/tool-render.js +76 -32
- package/dist/cli.js +2 -2
- package/dist/common/fs.d.ts +14 -0
- package/dist/common/fs.js +57 -3
- package/dist/common/truncate.d.ts +0 -6
- package/dist/common/truncate.js +1 -1
- package/dist/common/ttl-cache.d.ts +24 -0
- package/dist/common/ttl-cache.js +53 -0
- package/dist/config/codex-config.js +14 -11
- package/dist/config/index.d.ts +0 -8
- package/dist/config/index.js +0 -16
- package/dist/config/kimi-config.d.ts +1 -1
- package/dist/config/kimi-config.js +2 -1
- package/dist/config/opencode-config.d.ts +1 -1
- package/dist/config/opencode-config.js +16 -10
- package/dist/config/qr.d.ts +36 -0
- package/dist/config/qr.js +124 -0
- package/dist/config/wizard.js +69 -27
- package/dist/connector/index.d.ts +3 -2
- package/dist/connector/index.js +5 -3
- package/dist/index.js +14 -41
- package/dist/instance-lock.js +7 -0
- package/dist/persistence/atomic-write.d.ts +2 -0
- package/dist/persistence/atomic-write.js +20 -5
- package/dist/persistence/load-json-file.d.ts +1 -2
- package/dist/persistence/load-json-file.js +2 -3
- package/dist/platform/agent-stopper.d.ts +22 -0
- package/dist/platform/agent-stopper.js +19 -0
- package/dist/platform/command.d.ts +11 -0
- package/dist/platform/command.js +107 -0
- package/dist/platform/identity.d.ts +33 -0
- package/dist/platform/identity.js +187 -0
- package/dist/platform/path.d.ts +24 -0
- package/dist/platform/path.js +110 -0
- package/dist/platform/select.d.ts +9 -0
- package/dist/platform/select.js +12 -0
- package/dist/platform/shell.d.ts +51 -0
- package/dist/platform/shell.js +75 -0
- package/dist/platform/spawn.d.ts +41 -0
- package/dist/platform/spawn.js +77 -0
- package/dist/platform/terminator-posix.d.ts +38 -0
- package/dist/platform/terminator-posix.js +103 -0
- package/dist/platform/terminator-win32.d.ts +21 -0
- package/dist/platform/terminator-win32.js +110 -0
- package/dist/platform/terminator.d.ts +44 -0
- package/dist/platform/terminator.js +24 -0
- package/dist/platform/types.d.ts +23 -0
- package/dist/platform/types.js +5 -0
- package/dist/restart.js +4 -2
- package/dist/router/card-helpers.d.ts +33 -6
- package/dist/router/card-helpers.js +67 -11
- package/dist/router/config/common/render.d.ts +1 -1
- package/dist/router/config/common/render.js +1 -1
- package/dist/router/config-diff.d.ts +4 -0
- package/dist/router/config-diff.js +70 -0
- package/dist/router/index.d.ts +73 -6
- package/dist/router/index.js +663 -590
- package/dist/router/utils.d.ts +6 -0
- package/dist/router/utils.js +12 -0
- package/dist/runner/bash/runner.d.ts +9 -2
- package/dist/runner/bash/runner.js +45 -20
- package/dist/runner/claude/session.d.ts +4 -4
- package/dist/runner/claude/session.js +18 -9
- package/dist/runner/codex/app-server/protocol-types.d.ts +35 -1
- package/dist/runner/codex/app-server/runner.d.ts +1 -16
- package/dist/runner/codex/app-server/runner.js +2 -2
- package/dist/runner/codex/app-server/translator.d.ts +18 -0
- package/dist/runner/codex/app-server/translator.js +146 -0
- package/dist/runner/common/acp/base-acp-runner.d.ts +116 -0
- package/dist/runner/common/acp/base-acp-runner.js +248 -0
- package/dist/runner/common/acp/base-translator.d.ts +141 -0
- package/dist/runner/common/acp/base-translator.js +271 -0
- package/dist/runner/common/acp/protocol-helpers.d.ts +70 -0
- package/dist/runner/common/acp/protocol-helpers.js +82 -0
- package/dist/runner/common/connection-based-runner.d.ts +0 -2
- package/dist/runner/common/connection-based-runner.js +1 -1
- package/dist/runner/common/jsonrpc/transport.d.ts +2 -0
- package/dist/runner/common/jsonrpc/transport.js +25 -5
- package/dist/runner/common/process-stopper.d.ts +4 -26
- package/dist/runner/common/process-stopper.js +4 -76
- package/dist/runner/common/runner-utils.d.ts +0 -5
- package/dist/runner/common/runner-utils.js +0 -7
- package/dist/runner/common/spawning-runner.d.ts +42 -132
- package/dist/runner/common/spawning-runner.js +52 -271
- package/dist/runner/common/usage.d.ts +2 -0
- package/dist/runner/common/usage.js +2 -0
- package/dist/runner/dsh/index.d.ts +0 -2
- package/dist/runner/dsh/index.js +0 -2
- package/dist/runner/dsh/runner.d.ts +0 -2
- package/dist/runner/dsh/runner.js +1 -1
- package/dist/runner/index.d.ts +1 -1
- package/dist/runner/kimi/acp/runner.d.ts +63 -53
- package/dist/runner/kimi/acp/runner.js +189 -334
- package/dist/runner/kimi/acp/translator.d.ts +18 -101
- package/dist/runner/kimi/acp/translator.js +61 -255
- package/dist/runner/opencode/acp/runner.d.ts +22 -46
- package/dist/runner/opencode/acp/runner.js +74 -297
- package/dist/runner/opencode/acp/translator.d.ts +28 -104
- package/dist/runner/opencode/acp/translator.js +54 -249
- package/dist/runner/pi/rpc/index.d.ts +0 -2
- package/dist/runner/pi/rpc/index.js +0 -2
- package/dist/runner/pi/rpc/translator.d.ts +3 -0
- package/dist/runner/pi/rpc/translator.js +47 -9
- package/dist/runner/probe.d.ts +8 -11
- package/dist/runner/probe.js +17 -21
- package/dist/runner/registry.d.ts +4 -4
- package/dist/runner/registry.js +4 -4
- package/dist/runner/resolve-agent-choices.js +6 -2
- package/dist/runner/sync-agent-choices.js +1 -1
- package/dist/runner/types.d.ts +38 -1
- package/dist/session/claude/session-index.js +3 -1
- package/dist/session/claude/sessions.js +37 -64
- package/dist/session/codex/rollout-reader.d.ts +10 -14
- package/dist/session/codex/rollout-reader.js +126 -48
- package/dist/session/common/constants.d.ts +7 -0
- package/dist/session/common/constants.js +9 -0
- package/dist/session/common/pagination.d.ts +0 -2
- package/dist/session/common/pagination.js +1 -1
- package/dist/session/common/recency.d.ts +7 -0
- package/dist/session/common/recency.js +10 -0
- package/dist/session/common/two-pass.d.ts +22 -0
- package/dist/session/common/two-pass.js +70 -0
- package/dist/session/common/usage-accumulator.d.ts +14 -0
- package/dist/session/common/usage-accumulator.js +15 -0
- package/dist/session/dsh/sessions.js +15 -11
- package/dist/session/kimi/sessions.d.ts +24 -4
- package/dist/session/kimi/sessions.js +99 -103
- package/dist/session/opencode/sessions.d.ts +2 -14
- package/dist/session/opencode/sessions.js +35 -15
- package/dist/session/pi/sessions.js +64 -95
- package/dist/update/index.d.ts +2 -3
- package/dist/update/index.js +1 -2
- package/dist/update/install.d.ts +2 -1
- package/dist/update/install.js +58 -9
- package/dist/update/version-check.d.ts +5 -0
- package/dist/update/version-check.js +9 -0
- package/package.json +9 -7
- package/dist/update/startup-hint.d.ts +0 -5
- package/dist/update/startup-hint.js +0 -11
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import os from 'node:os';
|
|
4
|
-
import { findJsonlLine
|
|
4
|
+
import { findJsonlLine } from '../common/jsonl.js';
|
|
5
|
+
import { scanJsonlOnce, tailOffsetAfter, scanTailEvents } from '../common/two-pass.js';
|
|
5
6
|
import { extractContentBlocks } from '../common/content-blocks.js';
|
|
6
|
-
import { UsageAccumulator } from '../common/usage-accumulator.js';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { UsageAccumulator, contextWindowOccupancy, cumulativeUsageFields, } from '../common/usage-accumulator.js';
|
|
8
|
+
import { isStale } from '../common/constants.js';
|
|
9
|
+
import { encodeProjectDirName } from '../../platform/path.js';
|
|
10
|
+
import { paginate } from '../common/pagination.js';
|
|
11
|
+
import { sortByRecencyDesc } from '../common/recency.js';
|
|
12
|
+
import { TtlCache } from '../../common/ttl-cache.js';
|
|
9
13
|
function defaultPiDir() {
|
|
10
14
|
return path.join(os.homedir(), '.pi', 'agent');
|
|
11
15
|
}
|
|
12
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Encode cwd to pi's directory name format: '--<cwd with / \ : -> - --'
|
|
18
|
+
*
|
|
19
|
+
* 与 claude 共用 `encodeProjectDirName`(同一套 win32 归一规则),再去掉
|
|
20
|
+
* 前导分隔符产生的首个 `-`:`/Users/x` -> `Users-x`(不是 `-Users-x`)。
|
|
21
|
+
* win32 同理:`C:\Users\x` -> `C--Users-x`。
|
|
22
|
+
*/
|
|
13
23
|
function projectDirForCwd(cwd, sessionsDir) {
|
|
14
|
-
const encodedCwd = cwd
|
|
24
|
+
const encodedCwd = encodeProjectDirName(cwd).replace(/^-+/, '');
|
|
15
25
|
const encoded = `--${encodedCwd}--`;
|
|
16
26
|
return path.join(sessionsDir, encoded);
|
|
17
27
|
}
|
|
@@ -19,10 +29,7 @@ function projectDirForCwd(cwd, sessionsDir) {
|
|
|
19
29
|
* P1-19: TTL cache for pi directory scans (mirror codex getSessionIndex's
|
|
20
30
|
* 5s TTL). Repeated /resume pages re-scan the whole sessions dir per call.
|
|
21
31
|
*/
|
|
22
|
-
const
|
|
23
|
-
/** Upper bound on cached listings; FIFO eviction past this. */
|
|
24
|
-
const PI_LIST_MAX_ENTRIES = 32;
|
|
25
|
-
const piListCache = new Map();
|
|
32
|
+
const piListCache = new TtlCache(5_000, 32);
|
|
26
33
|
/** Pi-specific field-name mapping for content block extraction. */
|
|
27
34
|
export const PI_MAPPING = {
|
|
28
35
|
toolUseType: 'toolCall',
|
|
@@ -144,14 +151,8 @@ function piScalarScan(filePath) {
|
|
|
144
151
|
const acc = new UsageAccumulator();
|
|
145
152
|
let tailOffset = -1;
|
|
146
153
|
let lastUserText;
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
try {
|
|
150
|
-
obj = JSON.parse(line);
|
|
151
|
-
}
|
|
152
|
-
catch {
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
154
|
+
scanJsonlOnce(filePath, (rawObj, line, offset) => {
|
|
155
|
+
const obj = rawObj;
|
|
155
156
|
if (obj.type === 'message') {
|
|
156
157
|
const msg = obj.message;
|
|
157
158
|
if (!msg)
|
|
@@ -169,9 +170,9 @@ function piScalarScan(filePath) {
|
|
|
169
170
|
}
|
|
170
171
|
// last user message offset + displayTitle. Record the byte offset
|
|
171
172
|
// where the tail should start: the beginning of the NEXT line after
|
|
172
|
-
// this user message.
|
|
173
|
+
// this user message (see tailOffsetAfter).
|
|
173
174
|
if (msg.role === 'user') {
|
|
174
|
-
tailOffset =
|
|
175
|
+
tailOffset = tailOffsetAfter(line, offset);
|
|
175
176
|
const text = extractPiText(msg.content);
|
|
176
177
|
// Compress skill-injection bodies so the "最近输入" label shows what
|
|
177
178
|
// the user invoked (skill:x) + any trailing real input, not a
|
|
@@ -207,62 +208,48 @@ function buildPiUsage(acc) {
|
|
|
207
208
|
return undefined;
|
|
208
209
|
}
|
|
209
210
|
const l = acc.last;
|
|
210
|
-
const contextLength = l.input + l.cacheRead + l.cacheCreation;
|
|
211
211
|
return {
|
|
212
212
|
inputTokens: l.input,
|
|
213
213
|
outputTokens: l.output,
|
|
214
|
-
contextLength,
|
|
214
|
+
contextLength: contextWindowOccupancy(l),
|
|
215
215
|
compactCount: acc.compactCount > 0 ? acc.compactCount : undefined,
|
|
216
216
|
cacheReadTokens: l.cacheRead > 0 ? l.cacheRead : 0,
|
|
217
217
|
cacheCreationTokens: l.cacheCreation > 0 ? l.cacheCreation : 0,
|
|
218
218
|
totalTokens: l.total > 0 ? l.total : undefined,
|
|
219
219
|
// Cumulative (session-wide): sum of ALL runs in the jsonl file.
|
|
220
|
-
|
|
221
|
-
cumulativeInputTokens: t.input,
|
|
222
|
-
cumulativeOutputTokens: t.output,
|
|
223
|
-
cumulativeCacheReadTokens: t.cacheRead,
|
|
224
|
-
cumulativeCacheCreationTokens: t.cacheCreation,
|
|
220
|
+
...cumulativeUsageFields(t),
|
|
225
221
|
};
|
|
226
222
|
}
|
|
227
223
|
/**
|
|
228
|
-
* P2-6 + P2-5 second-pass:
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
224
|
+
* P2-6 + P2-5 second-pass tail mapper: expand one parsed tail line into
|
|
225
|
+
* content-block events. Re-parses only tail lines (scanTailEvents does the
|
|
226
|
+
* offset re-read + line filtering), so both raw line-string memory and
|
|
227
|
+
* parsed-object memory are O(tail), not O(whole file) — P2-6 + P2-5.
|
|
228
|
+
* `maxEvents` capping is applied by scanTailEvents.
|
|
233
229
|
*/
|
|
234
|
-
function
|
|
230
|
+
function mapPiTailLine(rawObj) {
|
|
235
231
|
const events = [];
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
events.push({ type: block.type, content: block.content, timestamp: obj.timestamp });
|
|
258
|
-
}
|
|
259
|
-
if (blocks.length === 0 && message.role) {
|
|
260
|
-
events.push({ type: role, content: `(${role} event)`, timestamp: obj.timestamp });
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
catch {
|
|
264
|
-
/* skip */
|
|
265
|
-
}
|
|
232
|
+
const obj = rawObj;
|
|
233
|
+
if (obj.type !== 'message' || !obj.message)
|
|
234
|
+
return events;
|
|
235
|
+
const message = obj.message;
|
|
236
|
+
// Handle error messages: when stopReason is "error", content is empty
|
|
237
|
+
// but errorMessage has details
|
|
238
|
+
if (message.stopReason === 'error' && message.errorMessage) {
|
|
239
|
+
events.push({
|
|
240
|
+
type: 'error',
|
|
241
|
+
content: `❌ ${message.errorMessage}`,
|
|
242
|
+
timestamp: obj.timestamp,
|
|
243
|
+
});
|
|
244
|
+
return events;
|
|
245
|
+
}
|
|
246
|
+
const blocks = extractContentBlocks(message.content, PI_MAPPING);
|
|
247
|
+
const role = message.role ?? 'unknown';
|
|
248
|
+
for (const block of blocks) {
|
|
249
|
+
events.push({ type: block.type, content: block.content, timestamp: obj.timestamp });
|
|
250
|
+
}
|
|
251
|
+
if (blocks.length === 0 && message.role) {
|
|
252
|
+
events.push({ type: role, content: `(${role} event)`, timestamp: obj.timestamp });
|
|
266
253
|
}
|
|
267
254
|
return events;
|
|
268
255
|
}
|
|
@@ -303,29 +290,17 @@ export class PiSessionReader {
|
|
|
303
290
|
if (fileCwd && fileCwd !== cwd) {
|
|
304
291
|
return { events: [] };
|
|
305
292
|
}
|
|
306
|
-
// P2-5 + P2-6:
|
|
307
|
-
//
|
|
308
|
-
// scalars (usage, tailOffset, displayTitle)
|
|
309
|
-
//
|
|
310
|
-
//
|
|
311
|
-
//
|
|
312
|
-
//
|
|
313
|
-
// memory for both raw line strings and parsed objects, instead of
|
|
314
|
-
// O(whole file).
|
|
315
|
-
//
|
|
316
|
-
// Parse ratio: ≈1.0–1.5× line count when a user message exists.
|
|
317
|
-
// When the session has NO user message, tailOffset stays -1 and the
|
|
318
|
-
// tail IS the whole file → scan(N) + tail(N) = 2.0× — the known
|
|
319
|
-
// asymptotic upper bound of the two-phase design.
|
|
293
|
+
// P2-5 + P2-6: Two-pass scan (see `scanJsonlOnce` / `scanTailEvents` in
|
|
294
|
+
// session/common/two-pass.ts): pass 1 streams the file once collecting
|
|
295
|
+
// scalars (usage, tailOffset, displayTitle) with O(1) parsed-object
|
|
296
|
+
// retention; pass 2 re-reads ONLY the tail and re-parses it for events,
|
|
297
|
+
// capped at maxEvents (keeps the LAST N events, matching
|
|
298
|
+
// CodexSessionReader's slice(-maxEvents), so auto-resume cards don't
|
|
299
|
+
// load the entire session).
|
|
320
300
|
const scan = piScalarScan(filePath);
|
|
321
|
-
const
|
|
322
|
-
const events = extractPiEventsFromTail(tailLines);
|
|
323
|
-
// Apply maxEvents cap: keep the LAST N events (most recent), matching
|
|
324
|
-
// CodexSessionReader's slice(-maxEvents). maxEvents limits the catch-up
|
|
325
|
-
// tail so auto-resume cards don't load the entire session.
|
|
326
|
-
const cappedEvents = capEvents(events, opts?.maxEvents);
|
|
301
|
+
const events = scanTailEvents(filePath, scan.tailOffset, mapPiTailLine, opts?.maxEvents);
|
|
327
302
|
return {
|
|
328
|
-
events
|
|
303
|
+
events,
|
|
329
304
|
usage: scan.usage,
|
|
330
305
|
displayTitle: scan.displayTitle,
|
|
331
306
|
};
|
|
@@ -336,7 +311,7 @@ export class PiSessionReader {
|
|
|
336
311
|
return false;
|
|
337
312
|
try {
|
|
338
313
|
const st = fs.statSync(filePath);
|
|
339
|
-
return
|
|
314
|
+
return !isStale(st.mtimeMs);
|
|
340
315
|
}
|
|
341
316
|
catch {
|
|
342
317
|
return false;
|
|
@@ -346,8 +321,8 @@ export class PiSessionReader {
|
|
|
346
321
|
listSessionsByScan(cwd) {
|
|
347
322
|
const cacheKey = `${this.sessionsDir}\u0000${cwd}`;
|
|
348
323
|
const cached = piListCache.get(cacheKey);
|
|
349
|
-
if (cached
|
|
350
|
-
return cached
|
|
324
|
+
if (cached) {
|
|
325
|
+
return cached;
|
|
351
326
|
}
|
|
352
327
|
const dir = projectDirForCwd(cwd, this.sessionsDir);
|
|
353
328
|
let files;
|
|
@@ -381,14 +356,8 @@ export class PiSessionReader {
|
|
|
381
356
|
}
|
|
382
357
|
}
|
|
383
358
|
// Same-mtime ties: secondary key keeps the full order deterministic.
|
|
384
|
-
sessions
|
|
385
|
-
piListCache.set(cacheKey,
|
|
386
|
-
// Bound the cache (map iteration order = insertion order).
|
|
387
|
-
if (piListCache.size > PI_LIST_MAX_ENTRIES) {
|
|
388
|
-
const oldest = piListCache.keys().next().value;
|
|
389
|
-
if (oldest !== undefined)
|
|
390
|
-
piListCache.delete(oldest);
|
|
391
|
-
}
|
|
359
|
+
sortByRecencyDesc(sessions, (s) => s.mtime, (s) => s.sessionId);
|
|
360
|
+
piListCache.set(cacheKey, sessions);
|
|
392
361
|
return sessions;
|
|
393
362
|
}
|
|
394
363
|
}
|
package/dist/update/index.d.ts
CHANGED
|
@@ -5,6 +5,5 @@
|
|
|
5
5
|
* package installation (detect package manager + global install),
|
|
6
6
|
* and the combined workflow for the /update command and --update CLI flag.
|
|
7
7
|
*/
|
|
8
|
-
export { isNewer, checkLatestVersion,
|
|
9
|
-
export { runInstallLatest, type
|
|
10
|
-
export { formatUpdateHint } from './startup-hint.js';
|
|
8
|
+
export { isNewer, checkLatestVersion, formatUpdateHint, type VersionCheckResult, } from './version-check.js';
|
|
9
|
+
export { runInstallLatest, type InstallResult } from './install.js';
|
package/dist/update/index.js
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* package installation (detect package manager + global install),
|
|
6
6
|
* and the combined workflow for the /update command and --update CLI flag.
|
|
7
7
|
*/
|
|
8
|
-
export { isNewer, checkLatestVersion, } from './version-check.js';
|
|
8
|
+
export { isNewer, checkLatestVersion, formatUpdateHint, } from './version-check.js';
|
|
9
9
|
export { runInstallLatest } from './install.js';
|
|
10
|
-
export { formatUpdateHint } from './startup-hint.js';
|
|
11
10
|
//# sourceMappingURL=index.js.map
|
package/dist/update/install.d.ts
CHANGED
|
@@ -38,7 +38,8 @@ export declare function detectPackageManager(scriptPath?: string): PackageManage
|
|
|
38
38
|
* Run the global install command to upgrade lark-remote to the latest version.
|
|
39
39
|
*
|
|
40
40
|
* @param opts.packageManager - Override detected package manager
|
|
41
|
-
* @param opts.execFn - Override
|
|
41
|
+
* @param opts.execFn - Override exec for testing; default routes through
|
|
42
|
+
* platform/spawn.ts (cross-spawn, win32 .cmd shim safe)
|
|
42
43
|
*/
|
|
43
44
|
export declare function runInstallLatest(opts?: {
|
|
44
45
|
packageManager?: PackageManager | null;
|
package/dist/update/install.js
CHANGED
|
@@ -1,6 +1,53 @@
|
|
|
1
|
-
import { execFile, execFileSync } from 'node:child_process';
|
|
2
1
|
import fs from 'node:fs';
|
|
3
2
|
import { getLogger } from '../logger/index.js';
|
|
3
|
+
import { resolveExecutable } from '../platform/command.js';
|
|
4
|
+
import { currentPlatform, isWin32 } from '../platform/select.js';
|
|
5
|
+
import { spawnProcess, spawnProcessSync } from '../platform/spawn.js';
|
|
6
|
+
/**
|
|
7
|
+
* execFile 回调风格适配 cross-spawn(platform/spawn.ts)。
|
|
8
|
+
*
|
|
9
|
+
* npm/pnpm 在 Windows 是 .cmd 垫片,execFile 不经 shell 无法执行——Node 自
|
|
10
|
+
* CVE-2024-27980 收紧后对无 shell 启动 .cmd 直接抛错,win32 上 /update 会
|
|
11
|
+
* 永远失败。执行统一走 spawnProcess(cross-spawn 处理 PATHEXT + .cmd 经
|
|
12
|
+
* cmd.exe 受控执行),错误/退出码语义对齐 execFile:
|
|
13
|
+
* - 非 0 退出 → err.message 为 execFile 同款 "Command failed: ..." 形态
|
|
14
|
+
* (含 stderr 首段,EACCES 提示逻辑依赖它);
|
|
15
|
+
* - 'error' 事件(ENOENT 等)→ 原样回调。
|
|
16
|
+
*/
|
|
17
|
+
function execViaSpawnProcess(file, args, options, callback) {
|
|
18
|
+
const proc = spawnProcess(file, args, {
|
|
19
|
+
timeout: options.timeout,
|
|
20
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
21
|
+
windowsHide: true,
|
|
22
|
+
});
|
|
23
|
+
let stdout = '';
|
|
24
|
+
let stderr = '';
|
|
25
|
+
let settled = false;
|
|
26
|
+
const finish = (error) => {
|
|
27
|
+
if (settled)
|
|
28
|
+
return;
|
|
29
|
+
settled = true;
|
|
30
|
+
callback(error, stdout, stderr);
|
|
31
|
+
};
|
|
32
|
+
proc.stdout?.setEncoding('utf8');
|
|
33
|
+
proc.stdout?.on('data', (chunk) => {
|
|
34
|
+
stdout += chunk;
|
|
35
|
+
});
|
|
36
|
+
proc.stderr?.setEncoding('utf8');
|
|
37
|
+
proc.stderr?.on('data', (chunk) => {
|
|
38
|
+
stderr += chunk;
|
|
39
|
+
});
|
|
40
|
+
proc.on('error', (err) => finish(err));
|
|
41
|
+
proc.on('close', (code, signal) => {
|
|
42
|
+
if (code === 0) {
|
|
43
|
+
finish(null);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const detail = stderr.trim() || stdout.trim();
|
|
47
|
+
const how = signal ? ` terminated with ${signal}` : ` with exit code ${code ?? 'unknown'}`;
|
|
48
|
+
finish(new Error(`Command failed: ${file} ${args.join(' ')}${how}${detail ? `\n${detail}` : ''}`));
|
|
49
|
+
});
|
|
50
|
+
}
|
|
4
51
|
/**
|
|
5
52
|
* Infer the installing package manager from the real path of the running
|
|
6
53
|
* script (e.g. `.../node_modules/lark-remote/dist/index.js`).
|
|
@@ -54,15 +101,16 @@ export function detectPackageManager(scriptPath) {
|
|
|
54
101
|
if (inferred)
|
|
55
102
|
return inferred;
|
|
56
103
|
}
|
|
57
|
-
// 3.
|
|
104
|
+
// 3. availability fallback: 纯 Node PATH 解析(win32 PATHEXT),不再依赖外部 which
|
|
105
|
+
// (Windows 无 which);win32 再用 where.exe 兜底罕见的解析失败场景(v2 §4.3)
|
|
58
106
|
const candidates = ['npm', 'bun', 'pnpm'];
|
|
59
107
|
for (const cmd of candidates) {
|
|
60
|
-
|
|
61
|
-
execFileSync('which', [cmd], { stdio: 'pipe', timeout: 3000 });
|
|
108
|
+
if (resolveExecutable(cmd))
|
|
62
109
|
return cmd;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
110
|
+
if (isWin32(currentPlatform)) {
|
|
111
|
+
const res = spawnProcessSync('where.exe', [cmd], { stdio: 'pipe', timeout: 3000 });
|
|
112
|
+
if (!res.error && res.status === 0)
|
|
113
|
+
return cmd;
|
|
66
114
|
}
|
|
67
115
|
}
|
|
68
116
|
return null;
|
|
@@ -82,7 +130,8 @@ function getInstallCommand(pm) {
|
|
|
82
130
|
* Run the global install command to upgrade lark-remote to the latest version.
|
|
83
131
|
*
|
|
84
132
|
* @param opts.packageManager - Override detected package manager
|
|
85
|
-
* @param opts.execFn - Override
|
|
133
|
+
* @param opts.execFn - Override exec for testing; default routes through
|
|
134
|
+
* platform/spawn.ts (cross-spawn, win32 .cmd shim safe)
|
|
86
135
|
*/
|
|
87
136
|
export function runInstallLatest(opts) {
|
|
88
137
|
// If packageManager is explicitly null, skip detection and fail immediately.
|
|
@@ -104,7 +153,7 @@ export function runInstallLatest(opts) {
|
|
|
104
153
|
});
|
|
105
154
|
}
|
|
106
155
|
const { cmd, args } = getInstallCommand(pm);
|
|
107
|
-
const execFn = opts?.execFn ??
|
|
156
|
+
const execFn = opts?.execFn ?? execViaSpawnProcess;
|
|
108
157
|
const logger = getLogger();
|
|
109
158
|
logger.info(`[update] running: ${cmd} ${args.join(' ')}`);
|
|
110
159
|
return new Promise((resolve) => {
|
|
@@ -29,3 +29,8 @@ export declare function checkLatestVersion(opts?: {
|
|
|
29
29
|
bypassCache?: boolean;
|
|
30
30
|
registryUrl?: string;
|
|
31
31
|
}): Promise<VersionCheckResult>;
|
|
32
|
+
/**
|
|
33
|
+
* Format an update hint string for the startup notification.
|
|
34
|
+
* Returns null if no newer version is available.
|
|
35
|
+
*/
|
|
36
|
+
export declare function formatUpdateHint(current: string, latest: string): string | null;
|
|
@@ -146,4 +146,13 @@ export async function checkLatestVersion(opts) {
|
|
|
146
146
|
}
|
|
147
147
|
return { current, latest };
|
|
148
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Format an update hint string for the startup notification.
|
|
151
|
+
* Returns null if no newer version is available.
|
|
152
|
+
*/
|
|
153
|
+
export function formatUpdateHint(current, latest) {
|
|
154
|
+
if (!isNewer(current, latest))
|
|
155
|
+
return null;
|
|
156
|
+
return `📦 有新版本 ${latest} 可用(当前 ${current}),发送 /update 升级`;
|
|
157
|
+
}
|
|
149
158
|
//# sourceMappingURL=version-check.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lark-remote",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Feishu (Lark) private-chat bridge to local coding agents — Claude Code / Codex / opencode / pi / kimi, with live-streaming CardKit 2.0 cards",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -25,8 +25,9 @@
|
|
|
25
25
|
],
|
|
26
26
|
"scripts": {
|
|
27
27
|
"dev": "bun src/index.ts",
|
|
28
|
-
"build": "
|
|
29
|
-
"typecheck": "tsc --noEmit",
|
|
28
|
+
"build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsc && node -e \"if(process.platform!=='win32'){const fs=require('fs');for(const f of ['dist/index.js','dist/cli.js'])fs.chmodSync(f,0o755)}\"",
|
|
29
|
+
"typecheck": "tsc --noEmit && tsc -p tsconfig.test-infra.json",
|
|
30
|
+
"typecheck:test-infra": "tsc -p tsconfig.test-infra.json",
|
|
30
31
|
"lint": "eslint src tests",
|
|
31
32
|
"lint:fix": "eslint src tests --fix",
|
|
32
33
|
"format": "prettier --write 'src/**/*.ts' 'tests/**/*.ts'",
|
|
@@ -42,6 +43,7 @@
|
|
|
42
43
|
"test:config": "vitest run --project infra-config",
|
|
43
44
|
"test:order": "vitest run --project infra-order",
|
|
44
45
|
"test:misc": "vitest run --project infra-misc",
|
|
46
|
+
"test:platform": "vitest run --project infra-platform",
|
|
45
47
|
"test:claude": "vitest run --project agent-claude",
|
|
46
48
|
"test:codex": "vitest run --project agent-codex",
|
|
47
49
|
"test:opencode": "vitest run --project agent-opencode",
|
|
@@ -57,7 +59,7 @@
|
|
|
57
59
|
"test:pi-full": "vitest run --project agent-pi --project infra-runner --project infra-session",
|
|
58
60
|
"test:kimi-full": "vitest run --project agent-kimi --project infra-runner --project infra-session",
|
|
59
61
|
"test:live": "vitest run --project live",
|
|
60
|
-
"prepublishOnly": "
|
|
62
|
+
"prepublishOnly": "npm run typecheck && vitest run && npm run build",
|
|
61
63
|
"prepack": "node -e \"const fs=require('fs');if(!fs.existsSync('dist/cli.js')){console.error('dist/ missing — run bun run build first');process.exit(1)}\""
|
|
62
64
|
},
|
|
63
65
|
"publishConfig": {
|
|
@@ -94,16 +96,16 @@
|
|
|
94
96
|
"@iarna/toml": "^2.2.5",
|
|
95
97
|
"@larksuite/channel": "^0.3.0",
|
|
96
98
|
"axios": "^1.18.0",
|
|
99
|
+
"cross-spawn": "^7.0.6",
|
|
97
100
|
"form-data": "^4.0.6",
|
|
98
|
-
"qrcode-
|
|
101
|
+
"qrcode-generator": "^2.0.4",
|
|
99
102
|
"yaml": "^2.9.0",
|
|
100
103
|
"zod": "^4.4.3"
|
|
101
104
|
},
|
|
102
105
|
"devDependencies": {
|
|
103
106
|
"@eslint/js": "^10.0.1",
|
|
104
|
-
"@types/
|
|
107
|
+
"@types/cross-spawn": "^6.0.6",
|
|
105
108
|
"@types/node": "^26.1.2",
|
|
106
|
-
"@types/qrcode-terminal": "^0.12.2",
|
|
107
109
|
"@vitest/coverage-v8": "4.1.10",
|
|
108
110
|
"eslint": "^10.7.0",
|
|
109
111
|
"eslint-config-prettier": "^10.1.8",
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { isNewer } from './version-check.js';
|
|
2
|
-
/**
|
|
3
|
-
* Format an update hint string for the startup notification.
|
|
4
|
-
* Returns null if no newer version is available.
|
|
5
|
-
*/
|
|
6
|
-
export function formatUpdateHint(current, latest) {
|
|
7
|
-
if (!isNewer(current, latest))
|
|
8
|
-
return null;
|
|
9
|
-
return `📦 有新版本 ${latest} 可用(当前 ${current}),发送 /update 升级`;
|
|
10
|
-
}
|
|
11
|
-
//# sourceMappingURL=startup-hint.js.map
|