lark-remote 0.1.1 → 0.1.3
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.en.md +2 -5
- package/README.md +1 -4
- package/dist/bridge/index.d.ts +1 -3
- package/dist/bridge/index.js +11 -12
- package/dist/card/card-budget.js +1 -2
- package/dist/card/card-shared.d.ts +8 -0
- package/dist/card/card-shared.js +14 -0
- package/dist/card/run-renderer.js +1 -0
- package/dist/card/run-state.d.ts +4 -0
- package/dist/config/codex-config.d.ts +3 -5
- package/dist/config/codex-config.js +7 -7
- package/dist/config/dir.d.ts +1 -0
- package/dist/config/dir.js +4 -0
- package/dist/config/index.d.ts +0 -10
- package/dist/config/index.js +0 -11
- package/dist/connector/index.js +28 -0
- package/dist/index.js +89 -41
- package/dist/router/config/codex.js +8 -12
- package/dist/router/index.d.ts +15 -2
- package/dist/router/index.js +314 -44
- package/dist/router/utils.d.ts +3 -0
- package/dist/router/utils.js +15 -3
- package/dist/runner/claude/runner.d.ts +0 -1
- package/dist/runner/claude/runner.js +1 -1
- package/dist/runner/codex/runner.d.ts +0 -2
- package/dist/runner/codex/runner.js +1 -1
- package/dist/runner/common/spawning-runner.d.ts +0 -1
- package/dist/runner/common/spawning-runner.js +2 -4
- package/dist/runner/kimi/runner.d.ts +0 -1
- package/dist/runner/kimi/runner.js +1 -1
- package/dist/runner/opencode/runner.d.ts +0 -2
- package/dist/runner/opencode/runner.js +1 -1
- package/dist/runner/pi/runner.d.ts +0 -1
- package/dist/runner/pi/runner.js +1 -1
- package/dist/runner/probe.d.ts +45 -0
- package/dist/runner/probe.js +87 -0
- package/dist/runner/types.d.ts +3 -0
- package/dist/session/claude/sessions.d.ts +8 -6
- package/dist/session/claude/sessions.js +178 -26
- package/dist/session/codex/rollout-reader.d.ts +1 -0
- package/dist/session/codex/rollout-reader.js +15 -0
- package/dist/session/codex/sessions.d.ts +1 -1
- package/dist/session/codex/sessions.js +2 -1
- package/dist/session/kimi/sessions.d.ts +3 -1
- package/dist/session/kimi/sessions.js +42 -6
- package/dist/session/opencode/sessions.d.ts +0 -2
- package/dist/session/opencode/sessions.js +1 -1
- package/dist/startup-contact.d.ts +4 -2
- package/dist/startup-contact.js +7 -4
- package/package.json +39 -4
|
@@ -27,10 +27,11 @@ export class CodexSessionReader {
|
|
|
27
27
|
getNewestSession(cwd) {
|
|
28
28
|
return this.listSessions(cwd, { limit: 1 }).sessions[0] ?? null;
|
|
29
29
|
}
|
|
30
|
-
readSessionContent(sessionId,
|
|
30
|
+
readSessionContent(sessionId, cwd, opts) {
|
|
31
31
|
return readCodexSessionContent(sessionId, {
|
|
32
32
|
codexHome: this.codexHome,
|
|
33
33
|
maxEvents: opts?.maxEvents,
|
|
34
|
+
cwd,
|
|
34
35
|
});
|
|
35
36
|
}
|
|
36
37
|
isSessionActive(sessionId, _cwd) {
|
|
@@ -24,7 +24,9 @@ export type CwdGuardResult = 'verified' | 'failed' | 'unverifiable';
|
|
|
24
24
|
* - failed: session has a workDir but it doesn't match realCwd
|
|
25
25
|
* - unverifiable: session has no workDir field (cannot verify)
|
|
26
26
|
*
|
|
27
|
-
* Unverifiable
|
|
27
|
+
* Unverifiable sessions are handled by the caller: when no cwd source can
|
|
28
|
+
* verify the session, it is rejected (fail-closed), aligned with claude's
|
|
29
|
+
* cwd guard semantics.
|
|
28
30
|
*/
|
|
29
31
|
export declare function checkCwdGuard(state: KimiSessionState, realCwd: string): CwdGuardResult;
|
|
30
32
|
/**
|
|
@@ -105,7 +105,9 @@ export function extractWorkDir(state) {
|
|
|
105
105
|
* - failed: session has a workDir but it doesn't match realCwd
|
|
106
106
|
* - unverifiable: session has no workDir field (cannot verify)
|
|
107
107
|
*
|
|
108
|
-
* Unverifiable
|
|
108
|
+
* Unverifiable sessions are handled by the caller: when no cwd source can
|
|
109
|
+
* verify the session, it is rejected (fail-closed), aligned with claude's
|
|
110
|
+
* cwd guard semantics.
|
|
109
111
|
*/
|
|
110
112
|
export function checkCwdGuard(state, realCwd) {
|
|
111
113
|
const sessionWorkDir = extractWorkDir(state);
|
|
@@ -283,27 +285,61 @@ export class KimiSessionReader {
|
|
|
283
285
|
* Read session content (events + usage)
|
|
284
286
|
*/
|
|
285
287
|
readSessionContent(sessionId, cwd, opts) {
|
|
286
|
-
// Find session directory from index
|
|
287
|
-
|
|
288
|
+
// Find session directory from index (also retrieve index entry for
|
|
289
|
+
// fallback cwd guard when state.json is missing or unparseable)
|
|
290
|
+
const indexEntry = this.findSessionIndexEntry(sessionId);
|
|
291
|
+
const sessionDir = indexEntry?.sessionDir ?? null;
|
|
288
292
|
if (!sessionDir) {
|
|
289
293
|
return { events: [] };
|
|
290
294
|
}
|
|
291
295
|
const statePath = path.join(sessionDir, 'state.json');
|
|
296
|
+
let realCwd;
|
|
297
|
+
try {
|
|
298
|
+
realCwd = fs.realpathSync(cwd);
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
getLogger().warn(`[kimi-session-reader] fs.realpathSync failed for cwd=${cwd} (session ${sessionId}), returning empty`);
|
|
302
|
+
return { events: [] };
|
|
303
|
+
}
|
|
304
|
+
let cwdGuardPassed = false;
|
|
292
305
|
try {
|
|
293
306
|
if (fs.existsSync(statePath)) {
|
|
294
307
|
const state = JSON.parse(fs.readFileSync(statePath, 'utf-8'));
|
|
295
|
-
const realCwd = fs.realpathSync(cwd);
|
|
296
308
|
const guardResult = checkCwdGuard(state, realCwd);
|
|
297
309
|
if (guardResult === 'failed') {
|
|
298
310
|
return { events: [] };
|
|
299
311
|
}
|
|
300
312
|
if (guardResult === 'unverifiable') {
|
|
301
|
-
getLogger().warn(`[kimi-session-reader] state.json has no workDir/cwd field for session ${sessionId},
|
|
313
|
+
getLogger().warn(`[kimi-session-reader] state.json has no workDir/cwd field for session ${sessionId}, falling back to index workDir`);
|
|
314
|
+
// Fall through to index-based guard below
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
// verified — state.json cwd matches
|
|
318
|
+
cwdGuardPassed = true;
|
|
302
319
|
}
|
|
303
320
|
}
|
|
304
321
|
}
|
|
305
322
|
catch {
|
|
306
|
-
//
|
|
323
|
+
// state.json unparseable — fall through to index-based guard
|
|
324
|
+
getLogger().warn(`[kimi-session-reader] state.json parse failed for session ${sessionId}, falling back to index workDir`);
|
|
325
|
+
}
|
|
326
|
+
// Fallback: when state.json is missing, unparseable, or has no workDir/cwd,
|
|
327
|
+
// use the workDir from the session index entry as a secondary guard.
|
|
328
|
+
// This prevents /resume <id> from accessing a session belonging to a
|
|
329
|
+
// different workspace when state.json is unavailable.
|
|
330
|
+
if (!cwdGuardPassed) {
|
|
331
|
+
const indexWorkDir = indexEntry?.workDir;
|
|
332
|
+
if (indexWorkDir && indexWorkDir !== realCwd) {
|
|
333
|
+
return { events: [] };
|
|
334
|
+
}
|
|
335
|
+
// v2 sessions have empty index workDir (v1-only field) and may lack
|
|
336
|
+
// state.json cwd. When no cwd source can verify the session belongs to
|
|
337
|
+
// the requested workspace, fail-closed to prevent cross-workspace access
|
|
338
|
+
// (aligned with claude's fail-closed cwd guard).
|
|
339
|
+
if (!indexWorkDir) {
|
|
340
|
+
getLogger().warn(`[kimi-session-reader] no cwd source (state.json + index) for session ${sessionId}, rejecting (fail-closed)`);
|
|
341
|
+
return { events: [] };
|
|
342
|
+
}
|
|
307
343
|
}
|
|
308
344
|
const wirePath = path.join(sessionDir, 'agents', 'main', 'wire.jsonl');
|
|
309
345
|
if (!fs.existsSync(wirePath)) {
|
|
@@ -15,8 +15,6 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import type { AgentSession, AgentSessionReader, SessionContent } from '../../runner/index.js';
|
|
17
17
|
interface OpencodeSessionReaderOptions {
|
|
18
|
-
/** Path to opencode binary. Default: 'opencode' (in PATH). */
|
|
19
|
-
binary?: string;
|
|
20
18
|
/** Cache TTL for session list (ms). Default: 10_000. */
|
|
21
19
|
cacheTtlMs?: number;
|
|
22
20
|
/**
|
|
@@ -28,7 +28,7 @@ export class OpencodeSessionReader {
|
|
|
28
28
|
/** Cache keyed by cwd - each cwd has its own cache entry */
|
|
29
29
|
listCache = new Map();
|
|
30
30
|
constructor(opts = {}) {
|
|
31
|
-
this.binary =
|
|
31
|
+
this.binary = 'opencode';
|
|
32
32
|
this.cacheTtlMs = opts.cacheTtlMs ?? 10_000;
|
|
33
33
|
this.captureExportOverride = opts.captureExport;
|
|
34
34
|
}
|
|
@@ -13,6 +13,8 @@ export declare class StartupContactStore {
|
|
|
13
13
|
save(contact: StartupContact): void;
|
|
14
14
|
getContact(): StartupContact | undefined;
|
|
15
15
|
}
|
|
16
|
-
export declare function formatStartupHello(now?: Date, pid?: number): string;
|
|
17
|
-
export declare function sendStartupHello(connector: StartupHelloChannel, store: StartupContactStore
|
|
16
|
+
export declare function formatStartupHello(now?: Date, pid?: number, dev?: boolean): string;
|
|
17
|
+
export declare function sendStartupHello(connector: StartupHelloChannel, store: StartupContactStore, opts?: {
|
|
18
|
+
dev?: boolean;
|
|
19
|
+
}): Promise<void>;
|
|
18
20
|
export {};
|
package/dist/startup-contact.js
CHANGED
|
@@ -21,7 +21,7 @@ export class StartupContactStore {
|
|
|
21
21
|
return contact.chatId || contact.userId ? contact : undefined;
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
|
-
export function formatStartupHello(now = new Date(), pid = process.pid) {
|
|
24
|
+
export function formatStartupHello(now = new Date(), pid = process.pid, dev = false) {
|
|
25
25
|
const startedAt = new Intl.DateTimeFormat('zh-CN', {
|
|
26
26
|
year: 'numeric',
|
|
27
27
|
month: '2-digit',
|
|
@@ -31,9 +31,10 @@ export function formatStartupHello(now = new Date(), pid = process.pid) {
|
|
|
31
31
|
second: '2-digit',
|
|
32
32
|
hour12: false,
|
|
33
33
|
}).format(now);
|
|
34
|
-
|
|
34
|
+
const modeTag = dev ? ' 🔧 dev' : '';
|
|
35
|
+
return `lark-remote${modeTag} 已启动\n启动时间:${startedAt}\n进程号:${pid}`;
|
|
35
36
|
}
|
|
36
|
-
export async function sendStartupHello(connector, store) {
|
|
37
|
+
export async function sendStartupHello(connector, store, opts) {
|
|
37
38
|
const contact = store.getContact();
|
|
38
39
|
const recipient = contact?.chatId ?? contact?.userId;
|
|
39
40
|
if (!recipient) {
|
|
@@ -41,7 +42,9 @@ export async function sendStartupHello(connector, store) {
|
|
|
41
42
|
return;
|
|
42
43
|
}
|
|
43
44
|
try {
|
|
44
|
-
await connector.sendWithRetry(recipient, {
|
|
45
|
+
await connector.sendWithRetry(recipient, {
|
|
46
|
+
text: formatStartupHello(undefined, undefined, opts?.dev),
|
|
47
|
+
});
|
|
45
48
|
getLogger().info('[startup] hello message sent');
|
|
46
49
|
}
|
|
47
50
|
catch (err) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lark-remote",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
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",
|
|
@@ -18,13 +18,13 @@
|
|
|
18
18
|
"!dist/**/*.js.map",
|
|
19
19
|
"!dist/**/*.d.ts.map",
|
|
20
20
|
"!dist/config/codex-bundled-test-helpers.*",
|
|
21
|
-
"!dist/test-helpers.*",
|
|
22
21
|
"README.md",
|
|
23
22
|
"README.en.md",
|
|
24
23
|
"LICENSE",
|
|
25
24
|
"CHANGELOG.md"
|
|
26
25
|
],
|
|
27
26
|
"scripts": {
|
|
27
|
+
"prepare": "husky",
|
|
28
28
|
"dev": "bun src/index.ts",
|
|
29
29
|
"build": "rm -rf dist && tsc && chmod +x dist/index.js dist/cli.js",
|
|
30
30
|
"typecheck": "tsc --noEmit",
|
|
@@ -34,12 +34,35 @@
|
|
|
34
34
|
"format:check": "prettier --check 'src/**/*.ts' 'tests/**/*.ts'",
|
|
35
35
|
"test": "vitest run",
|
|
36
36
|
"test:watch": "vitest",
|
|
37
|
+
"test:bridge": "vitest run --project infra-bridge",
|
|
38
|
+
"test:card": "vitest run --project infra-card",
|
|
39
|
+
"test:connector": "vitest run --project infra-connector",
|
|
40
|
+
"test:router": "vitest run --project infra-router",
|
|
41
|
+
"test:session": "vitest run --project infra-session",
|
|
42
|
+
"test:runner": "vitest run --project infra-runner",
|
|
43
|
+
"test:config": "vitest run --project infra-config",
|
|
44
|
+
"test:order": "vitest run --project infra-order",
|
|
45
|
+
"test:misc": "vitest run --project infra-misc",
|
|
46
|
+
"test:claude": "vitest run --project agent-claude",
|
|
47
|
+
"test:codex": "vitest run --project agent-codex",
|
|
48
|
+
"test:opencode": "vitest run --project agent-opencode",
|
|
49
|
+
"test:pi": "vitest run --project agent-pi",
|
|
50
|
+
"test:kimi": "vitest run --project agent-kimi",
|
|
51
|
+
"test:bash": "vitest run --project agent-bash",
|
|
52
|
+
"test:infra": "vitest run --project 'infra-*'",
|
|
53
|
+
"test:agents": "vitest run --project 'agent-*'",
|
|
54
|
+
"test:codex-full": "vitest run --project agent-codex --project infra-runner --project infra-session",
|
|
55
|
+
"test:claude-full": "vitest run --project agent-claude --project infra-runner --project infra-session",
|
|
56
|
+
"test:opencode-full": "vitest run --project agent-opencode --project infra-runner --project infra-session",
|
|
57
|
+
"test:pi-full": "vitest run --project agent-pi --project infra-runner --project infra-session",
|
|
58
|
+
"test:kimi-full": "vitest run --project agent-kimi --project infra-runner --project infra-session",
|
|
59
|
+
"test:live": "vitest run --project live",
|
|
37
60
|
"prepublishOnly": "tsc --noEmit && vitest run && rm -rf dist && tsc && chmod +x dist/index.js dist/cli.js",
|
|
38
61
|
"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)}\""
|
|
39
62
|
},
|
|
40
63
|
"publishConfig": {
|
|
41
64
|
"access": "public",
|
|
42
|
-
"provenance":
|
|
65
|
+
"provenance": true
|
|
43
66
|
},
|
|
44
67
|
"keywords": [
|
|
45
68
|
"feishu",
|
|
@@ -85,9 +108,21 @@
|
|
|
85
108
|
"eslint": "^10.7.0",
|
|
86
109
|
"eslint-config-prettier": "^10.1.8",
|
|
87
110
|
"eslint-plugin-prettier": "^5.5.6",
|
|
111
|
+
"husky": "^9.1.7",
|
|
112
|
+
"lint-staged": "^17.3.0",
|
|
88
113
|
"prettier": "^3.9.5",
|
|
89
114
|
"typescript": "^6.0.0",
|
|
90
115
|
"typescript-eslint": "^8.63.0",
|
|
91
|
-
"vitest": "^4.1.
|
|
116
|
+
"vitest": "^4.1.10"
|
|
117
|
+
},
|
|
118
|
+
"lint-staged": {
|
|
119
|
+
"src/**/*.ts": [
|
|
120
|
+
"eslint --fix",
|
|
121
|
+
"prettier --write"
|
|
122
|
+
],
|
|
123
|
+
"tests/**/*.ts": [
|
|
124
|
+
"eslint --fix",
|
|
125
|
+
"prettier --write"
|
|
126
|
+
]
|
|
92
127
|
}
|
|
93
128
|
}
|