@jesonliu/lark-claudecode-bridge 0.3.1 → 0.7.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.
Files changed (59) hide show
  1. package/README.md +90 -12
  2. package/config.example.yaml +42 -17
  3. package/dist/auth-precheck.d.ts +3 -3
  4. package/dist/auth-precheck.js +6 -7
  5. package/dist/auth-precheck.js.map +1 -1
  6. package/dist/cli/app-manager.d.ts +2 -3
  7. package/dist/cli/app-manager.js +2 -7
  8. package/dist/cli/app-manager.js.map +1 -1
  9. package/dist/cli/setup-wizard.d.ts +1 -0
  10. package/dist/cli/setup-wizard.js +5 -6
  11. package/dist/cli/setup-wizard.js.map +1 -1
  12. package/dist/config.js +73 -10
  13. package/dist/config.js.map +1 -1
  14. package/dist/executor/claude-executor.d.ts +8 -2
  15. package/dist/executor/claude-executor.js +20 -2
  16. package/dist/executor/claude-executor.js.map +1 -1
  17. package/dist/executor/notify-server.d.ts +2 -5
  18. package/dist/executor/notify-server.js +3 -48
  19. package/dist/executor/notify-server.js.map +1 -1
  20. package/dist/executor/permission-gate.d.ts +30 -0
  21. package/dist/executor/permission-gate.js +56 -5
  22. package/dist/executor/permission-gate.js.map +1 -1
  23. package/dist/executor/plugin-discovery.d.ts +26 -0
  24. package/dist/executor/plugin-discovery.js +131 -0
  25. package/dist/executor/plugin-discovery.js.map +1 -0
  26. package/dist/gateway/card-builder.d.ts +16 -1
  27. package/dist/gateway/card-builder.js +58 -0
  28. package/dist/gateway/card-builder.js.map +1 -1
  29. package/dist/gateway/diff-card.d.ts +16 -0
  30. package/dist/gateway/diff-card.js +39 -0
  31. package/dist/gateway/diff-card.js.map +1 -0
  32. package/dist/gateway/feishu-gateway.js +9 -2
  33. package/dist/gateway/feishu-gateway.js.map +1 -1
  34. package/dist/index.js +163 -32
  35. package/dist/index.js.map +1 -1
  36. package/dist/session/commands.d.ts +13 -2
  37. package/dist/session/commands.js +97 -9
  38. package/dist/session/commands.js.map +1 -1
  39. package/dist/session/session-store.d.ts +3 -0
  40. package/dist/session/session-store.js.map +1 -1
  41. package/dist/session/triggers.d.ts +1 -1
  42. package/dist/session/triggers.js +3 -2
  43. package/dist/session/triggers.js.map +1 -1
  44. package/dist/types.d.ts +49 -9
  45. package/dist/util/beijing-time.d.ts +5 -0
  46. package/dist/util/beijing-time.js +33 -0
  47. package/dist/util/beijing-time.js.map +1 -0
  48. package/dist/util/chunk-text.d.ts +2 -0
  49. package/dist/util/chunk-text.js +48 -0
  50. package/dist/util/chunk-text.js.map +1 -0
  51. package/dist/util/diff.d.ts +2 -0
  52. package/dist/util/diff.js +70 -0
  53. package/dist/util/diff.js.map +1 -0
  54. package/dist/util/workspace-diff.d.ts +9 -0
  55. package/dist/util/workspace-diff.js +56 -0
  56. package/dist/util/workspace-diff.js.map +1 -0
  57. package/dist/version.d.ts +1 -1
  58. package/dist/version.js +1 -1
  59. package/package.json +3 -1
@@ -0,0 +1,70 @@
1
+ // 文件差异工具:为写工具(Write/Edit)的确认卡片生成 unified diff 文本。
2
+ // 基于 jsdiff(零依赖),上下文行数 3(与 git diff 默认一致)。
3
+ import { readFileSync } from 'node:fs';
4
+ import { createTwoFilesPatch } from 'diff';
5
+ /** 生成写工具的 unified diff;无内容差异或无法生成时返回 ''(调用方回退显示 summary) */
6
+ export function generateFileDiff(toolName, input) {
7
+ try {
8
+ if (toolName === 'Write')
9
+ return writeDiff(input);
10
+ if (toolName === 'Edit')
11
+ return editDiff(input);
12
+ // NotebookEdit 的 input 是单元格级结构(new_cell 等),单元格 diff 可读性差,不生成
13
+ return '';
14
+ }
15
+ catch {
16
+ // 读文件失败(权限/编码等)不应阻断确认流程:无 diff 就显示原 summary
17
+ return '';
18
+ }
19
+ }
20
+ /** Write:新文件(原不存在)= 全 + 行;覆盖写 = 与原内容全文对比 */
21
+ function writeDiff(input) {
22
+ const filePath = input.file_path;
23
+ if (typeof filePath !== 'string' || !filePath)
24
+ return '';
25
+ const newContent = typeof input.content === 'string' ? input.content : '';
26
+ const oldExists = exists(filePath);
27
+ const oldContent = oldExists ? readFile(filePath) : undefined;
28
+ return toPatch(filePath, oldContent, newContent, oldExists);
29
+ }
30
+ /** Edit:读原文件应用 old_string→new_string 替换后整体对比(比仅对比 old/new 片段多上下文,可读性更好) */
31
+ function editDiff(input) {
32
+ const filePath = input.file_path;
33
+ if (typeof filePath !== 'string' || !filePath)
34
+ return '';
35
+ const { oldString, newString } = { oldString: input.old_string, newString: input.new_string };
36
+ if (typeof oldString !== 'string' || typeof newString !== 'string')
37
+ return '';
38
+ const raw = readFile(filePath);
39
+ const content = normalizeEol(raw);
40
+ // old_string 不在文件中(模型定位失误):不生成误导性的空 diff,回退 summary
41
+ if (!content.includes(oldString))
42
+ return '';
43
+ const replaced = input.replace_all === true
44
+ ? content.split(oldString).join(newString)
45
+ : content.replace(oldString, () => newString); // 函数形式替换:避免 newString 含 $& 等替换模式被二次解释
46
+ return toPatch(filePath, content, replaced, true);
47
+ }
48
+ /** 统一输出:jsdiff 补丁文本,去掉 Index/=== 头部行;CRLF 归一为 LF 防整文件误报全改 */
49
+ function toPatch(filePath, oldContent, newContent, oldExists) {
50
+ const patch = createTwoFilesPatch(oldExists ? filePath : '/dev/null', filePath, oldExists ? normalizeEol(oldContent ?? '') : '', // jsdiff v9 不接受 undefined:空串生成 -0,0 全新增
51
+ normalizeEol(newContent), undefined, undefined, { context: 3 });
52
+ // 去掉 jsdiff 加的头部噪音('Index: ...' 与 '====' 分隔行),保留 ---/+++ 起始的正文
53
+ return patch.replace(/^(Index: [^\n]*\n)?(===+[^\n]*\n)?/, '');
54
+ }
55
+ function exists(p) {
56
+ try {
57
+ readFileSync(p);
58
+ return true;
59
+ }
60
+ catch {
61
+ return false;
62
+ }
63
+ }
64
+ function readFile(p) {
65
+ return readFileSync(p, 'utf8');
66
+ }
67
+ function normalizeEol(s) {
68
+ return s.replace(/\r\n/g, '\n');
69
+ }
70
+ //# sourceMappingURL=diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/util/diff.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export interface WorkspaceDiff {
2
+ diff: string;
3
+ files: number;
4
+ }
5
+ /**
6
+ * 收集工作区全部未提交改动(staged + unstaged + untracked)为 unified diff 文本与文件数。
7
+ * 返回 null = 非 git 仓库(调用方回退旧行为);diff 空串 = git 仓库但无改动。
8
+ */
9
+ export declare function collectWorkspaceDiff(wsPath: string): Promise<WorkspaceDiff | null>;
@@ -0,0 +1,56 @@
1
+ // 工作区改动收集:code-dev 工作区任务收尾时用 git diff 生成全量 unified diff。
2
+ // 不做文件快照——git 是 code-dev 工作区的既有事实(非 git 仓库回退旧上传行为,由调用方处理)
3
+ import { execFile } from 'node:child_process';
4
+ import { readFile } from 'node:fs/promises';
5
+ import { join } from 'node:path';
6
+ import { promisify } from 'node:util';
7
+ import { createTwoFilesPatch } from 'diff';
8
+ const execFileP = promisify(execFile);
9
+ // untracked 合成新增 diff 的文件数上限:防 node_modules 误入 status 时 diff 爆炸
10
+ const MAX_UNTRACKED_FILES = 20;
11
+ function isGitRepo(wsPath) {
12
+ return execFileP('git', ['rev-parse', '--is-inside-work-tree'], { cwd: wsPath })
13
+ .then((r) => r.stdout.trim() === 'true')
14
+ .catch(() => false);
15
+ }
16
+ function normalizeEol(s) {
17
+ return s.replace(/\r\n/g, '\n');
18
+ }
19
+ /** untracked 文件合成为「全新增」diff(git diff 不覆盖未跟踪文件) */
20
+ async function untrackedDiff(wsPath) {
21
+ const status = await execFileP('git', ['status', '--porcelain'], { cwd: wsPath }).catch(() => ({ stdout: '' }));
22
+ const files = status.stdout
23
+ .split('\n')
24
+ .filter((l) => l.startsWith('??'))
25
+ .map((l) => l.slice(3).trim())
26
+ .filter((f) => f && !f.endsWith('/')) // 目录条目(?? dir/)无法按文件读取,跳过
27
+ .slice(0, MAX_UNTRACKED_FILES);
28
+ const patches = [];
29
+ for (const f of files) {
30
+ const content = await readFile(join(wsPath, f), 'utf8').then(normalizeEol).catch(() => null);
31
+ if (content === null)
32
+ continue;
33
+ patches.push(createTwoFilesPatch('/dev/null', f, '', content, undefined, undefined, { context: 3 })
34
+ .replace(/^(Index: [^\n]*\n)?(===+[^\n]*\n)?/, ''));
35
+ }
36
+ return patches.join('\n');
37
+ }
38
+ /**
39
+ * 收集工作区全部未提交改动(staged + unstaged + untracked)为 unified diff 文本与文件数。
40
+ * 返回 null = 非 git 仓库(调用方回退旧行为);diff 空串 = git 仓库但无改动。
41
+ */
42
+ export async function collectWorkspaceDiff(wsPath) {
43
+ if (!(await isGitRepo(wsPath)))
44
+ return null;
45
+ const tracked = await execFileP('git', ['diff', 'HEAD', '--unified=3'], { cwd: wsPath, maxBuffer: 16 * 1024 * 1024 })
46
+ .then((r) => r.stdout)
47
+ .catch(() => '');
48
+ const untracked = await untrackedDiff(wsPath);
49
+ const trackedFiles = (tracked.match(/^diff --git /gm) ?? []).length;
50
+ const untrackedFiles = (untracked.match(/^\+\+\+ /gm) ?? []).length;
51
+ return {
52
+ diff: [tracked.trimEnd(), untracked.trimEnd()].filter(Boolean).join('\n'),
53
+ files: trackedFiles + untrackedFiles,
54
+ };
55
+ }
56
+ //# sourceMappingURL=workspace-diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-diff.js","sourceRoot":"","sources":["../../src/util/workspace-diff.ts"],"names":[],"mappings":"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"}
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "0.3.0";
1
+ export declare const VERSION = "0.6.0";
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // 版本号单一来源:package.json / lcb version / smoke 测试均以此为准
2
- export const VERSION = '0.3.0';
2
+ export const VERSION = '0.6.0';
3
3
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jesonliu/lark-claudecode-bridge",
3
- "version": "0.3.1",
3
+ "version": "0.7.0",
4
4
  "description": "飞书 ↔ Claude Code 桥接器:卡片确认走长连接,免公网 IP",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -25,11 +25,13 @@
25
25
  "dependencies": {
26
26
  "@anthropic-ai/claude-agent-sdk": "^0.3.247",
27
27
  "@larksuiteoapi/node-sdk": "^1.73.0",
28
+ "diff": "^9.0.0",
28
29
  "picocolors": "^1.1.1",
29
30
  "yaml": "^2.9.0",
30
31
  "zod": "^4.4.0"
31
32
  },
32
33
  "devDependencies": {
34
+ "@types/diff": "^7.0.2",
33
35
  "@types/node": "^26.4.0",
34
36
  "tsx": "^4.23.12",
35
37
  "typescript": "^7.0.2",