remnote-bridge 0.1.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 (135) hide show
  1. package/dist/cli/commands/connect.d.ts +12 -0
  2. package/dist/cli/commands/connect.js +124 -0
  3. package/dist/cli/commands/disconnect.d.ts +11 -0
  4. package/dist/cli/commands/disconnect.js +100 -0
  5. package/dist/cli/commands/edit-rem.d.ts +13 -0
  6. package/dist/cli/commands/edit-rem.js +83 -0
  7. package/dist/cli/commands/edit-tree.d.ts +14 -0
  8. package/dist/cli/commands/edit-tree.js +67 -0
  9. package/dist/cli/commands/health.d.ts +12 -0
  10. package/dist/cli/commands/health.js +100 -0
  11. package/dist/cli/commands/install-skill.d.ts +6 -0
  12. package/dist/cli/commands/install-skill.js +39 -0
  13. package/dist/cli/commands/read-context.d.ts +20 -0
  14. package/dist/cli/commands/read-context.js +77 -0
  15. package/dist/cli/commands/read-globe.d.ts +16 -0
  16. package/dist/cli/commands/read-globe.js +60 -0
  17. package/dist/cli/commands/read-rem.d.ts +16 -0
  18. package/dist/cli/commands/read-rem.js +80 -0
  19. package/dist/cli/commands/read-tree.d.ts +17 -0
  20. package/dist/cli/commands/read-tree.js +85 -0
  21. package/dist/cli/commands/search.d.ts +12 -0
  22. package/dist/cli/commands/search.js +65 -0
  23. package/dist/cli/config.d.ts +55 -0
  24. package/dist/cli/config.js +139 -0
  25. package/dist/cli/daemon/daemon.d.ts +11 -0
  26. package/dist/cli/daemon/daemon.js +186 -0
  27. package/dist/cli/daemon/dev-server.d.ts +26 -0
  28. package/dist/cli/daemon/dev-server.js +81 -0
  29. package/dist/cli/daemon/pid.d.ts +34 -0
  30. package/dist/cli/daemon/pid.js +67 -0
  31. package/dist/cli/daemon/send-request.d.ts +24 -0
  32. package/dist/cli/daemon/send-request.js +92 -0
  33. package/dist/cli/handlers/context-read-handler.d.ts +18 -0
  34. package/dist/cli/handlers/context-read-handler.js +24 -0
  35. package/dist/cli/handlers/edit-handler.d.ts +30 -0
  36. package/dist/cli/handlers/edit-handler.js +133 -0
  37. package/dist/cli/handlers/globe-read-handler.d.ts +17 -0
  38. package/dist/cli/handlers/globe-read-handler.js +23 -0
  39. package/dist/cli/handlers/read-handler.d.ts +16 -0
  40. package/dist/cli/handlers/read-handler.js +78 -0
  41. package/dist/cli/handlers/rem-cache.d.ts +19 -0
  42. package/dist/cli/handlers/rem-cache.js +63 -0
  43. package/dist/cli/handlers/tree-edit-handler.d.ts +30 -0
  44. package/dist/cli/handlers/tree-edit-handler.js +188 -0
  45. package/dist/cli/handlers/tree-parser.d.ts +95 -0
  46. package/dist/cli/handlers/tree-parser.js +506 -0
  47. package/dist/cli/handlers/tree-read-handler.d.ts +28 -0
  48. package/dist/cli/handlers/tree-read-handler.js +53 -0
  49. package/dist/cli/main.d.ts +7 -0
  50. package/dist/cli/main.js +300 -0
  51. package/dist/cli/protocol.d.ts +39 -0
  52. package/dist/cli/protocol.js +35 -0
  53. package/dist/cli/server/config-server.d.ts +26 -0
  54. package/dist/cli/server/config-server.js +363 -0
  55. package/dist/cli/server/ws-server.d.ts +68 -0
  56. package/dist/cli/server/ws-server.js +335 -0
  57. package/dist/cli/utils/output.d.ts +11 -0
  58. package/dist/cli/utils/output.js +13 -0
  59. package/dist/mcp/daemon-client.d.ts +31 -0
  60. package/dist/mcp/daemon-client.js +99 -0
  61. package/dist/mcp/index.d.ts +7 -0
  62. package/dist/mcp/index.js +68 -0
  63. package/dist/mcp/instructions.d.ts +1 -0
  64. package/dist/mcp/instructions.js +249 -0
  65. package/dist/mcp/resources/edit-tree-guide.d.ts +1 -0
  66. package/dist/mcp/resources/edit-tree-guide.js +197 -0
  67. package/dist/mcp/resources/error-reference.d.ts +1 -0
  68. package/dist/mcp/resources/error-reference.js +132 -0
  69. package/dist/mcp/resources/outline-format.d.ts +1 -0
  70. package/dist/mcp/resources/outline-format.js +104 -0
  71. package/dist/mcp/resources/rem-object-fields.d.ts +1 -0
  72. package/dist/mcp/resources/rem-object-fields.js +331 -0
  73. package/dist/mcp/resources/separator-flashcard.d.ts +1 -0
  74. package/dist/mcp/resources/separator-flashcard.js +120 -0
  75. package/dist/mcp/tools/edit-tools.d.ts +5 -0
  76. package/dist/mcp/tools/edit-tools.js +47 -0
  77. package/dist/mcp/tools/infra-tools.d.ts +5 -0
  78. package/dist/mcp/tools/infra-tools.js +43 -0
  79. package/dist/mcp/tools/read-tools.d.ts +5 -0
  80. package/dist/mcp/tools/read-tools.js +195 -0
  81. package/dist/mcp/types.d.ts +12 -0
  82. package/dist/mcp/types.js +4 -0
  83. package/docs/instruction/connect.md +158 -0
  84. package/docs/instruction/disconnect.md +146 -0
  85. package/docs/instruction/edit-rem.md +509 -0
  86. package/docs/instruction/edit-tree.md +419 -0
  87. package/docs/instruction/health.md +159 -0
  88. package/docs/instruction/overall.md +751 -0
  89. package/docs/instruction/read-context.md +353 -0
  90. package/docs/instruction/read-globe.md +206 -0
  91. package/docs/instruction/read-rem.md +476 -0
  92. package/docs/instruction/read-tree.md +428 -0
  93. package/docs/instruction/search.md +196 -0
  94. package/package.json +41 -0
  95. package/remnote-plugin/package.json +48 -0
  96. package/remnote-plugin/postcss.config.js +5 -0
  97. package/remnote-plugin/public/bridge-icon.svg +8 -0
  98. package/remnote-plugin/public/manifest.json +22 -0
  99. package/remnote-plugin/src/bridge/message-router.ts +57 -0
  100. package/remnote-plugin/src/bridge/websocket-client.ts +245 -0
  101. package/remnote-plugin/src/index.css +1 -0
  102. package/remnote-plugin/src/services/breadcrumb.ts +26 -0
  103. package/remnote-plugin/src/services/create-rem.ts +59 -0
  104. package/remnote-plugin/src/services/delete-rem.ts +29 -0
  105. package/remnote-plugin/src/services/index.ts +16 -0
  106. package/remnote-plugin/src/services/move-rem.ts +39 -0
  107. package/remnote-plugin/src/services/powerup-filter.ts +31 -0
  108. package/remnote-plugin/src/services/read-context.ts +368 -0
  109. package/remnote-plugin/src/services/read-globe.ts +197 -0
  110. package/remnote-plugin/src/services/read-rem.ts +284 -0
  111. package/remnote-plugin/src/services/read-tree.ts +222 -0
  112. package/remnote-plugin/src/services/rem-builder.ts +124 -0
  113. package/remnote-plugin/src/services/reorder-children.ts +61 -0
  114. package/remnote-plugin/src/services/search.ts +56 -0
  115. package/remnote-plugin/src/services/write-rem-fields.ts +254 -0
  116. package/remnote-plugin/src/settings.ts +12 -0
  117. package/remnote-plugin/src/style.css +45 -0
  118. package/remnote-plugin/src/test-scripts/AGENTS.md +46 -0
  119. package/remnote-plugin/src/test-scripts/test-actions.ts +230 -0
  120. package/remnote-plugin/src/test-scripts/test-powerup-rendering.ts +722 -0
  121. package/remnote-plugin/src/test-scripts/test-rem-type-mapping.ts +283 -0
  122. package/remnote-plugin/src/test-scripts/test-richtext-builder.ts +207 -0
  123. package/remnote-plugin/src/test-scripts/test-richtext-matrix.ts +332 -0
  124. package/remnote-plugin/src/test-scripts/test-richtext-remaining.ts +245 -0
  125. package/remnote-plugin/src/test-scripts/test-rw-fields.ts +399 -0
  126. package/remnote-plugin/src/types.ts +419 -0
  127. package/remnote-plugin/src/utils/elision.ts +45 -0
  128. package/remnote-plugin/src/utils/index.ts +10 -0
  129. package/remnote-plugin/src/utils/tree-serializer.ts +269 -0
  130. package/remnote-plugin/src/widgets/bridge_widget.tsx +170 -0
  131. package/remnote-plugin/src/widgets/index.tsx +82 -0
  132. package/remnote-plugin/tailwind.config.js +7 -0
  133. package/remnote-plugin/tsconfig.json +21 -0
  134. package/remnote-plugin/webpack.config.js +125 -0
  135. package/skill/SKILL.md +428 -0
@@ -0,0 +1,77 @@
1
+ /**
2
+ * read-context 命令
3
+ *
4
+ * 读取当前上下文视图。
5
+ * - --mode focus|page(默认 focus)
6
+ * - --ancestor-levels N 向上追溯几层祖先(默认 2,仅 focus 模式)
7
+ * - --depth N 展开深度(默认 3,仅 page 模式)
8
+ * - --max-nodes N 全局节点上限(默认 200)
9
+ * - --max-siblings N 每个父节点下展示的 children 上限(默认 20)
10
+ * - --json 结构化 JSON 输出
11
+ */
12
+ import { sendDaemonRequest, DaemonNotRunningError, DaemonUnreachableError } from '../daemon/send-request.js';
13
+ import { jsonOutput } from '../utils/output.js';
14
+ export async function readContextCommand(options = {}) {
15
+ const { json } = options;
16
+ const mode = options.mode || 'focus';
17
+ if (mode !== 'focus' && mode !== 'page') {
18
+ const errMsg = '--mode must be "focus" or "page"';
19
+ if (json) {
20
+ jsonOutput({ ok: false, command: 'read-context', error: errMsg });
21
+ }
22
+ else {
23
+ console.error(`错误: ${errMsg}`);
24
+ }
25
+ process.exitCode = 1;
26
+ return;
27
+ }
28
+ const ancestorLevels = options.ancestorLevels !== undefined ? parseInt(options.ancestorLevels, 10) : undefined;
29
+ const depth = options.depth !== undefined ? parseInt(options.depth, 10) : undefined;
30
+ const maxNodes = options.maxNodes !== undefined ? parseInt(options.maxNodes, 10) : undefined;
31
+ const maxSiblings = options.maxSiblings !== undefined ? parseInt(options.maxSiblings, 10) : undefined;
32
+ if ((ancestorLevels !== undefined && isNaN(ancestorLevels)) || (depth !== undefined && isNaN(depth)) || (maxNodes !== undefined && isNaN(maxNodes)) || (maxSiblings !== undefined && isNaN(maxSiblings))) {
33
+ const errMsg = 'numeric options must be numbers';
34
+ if (json) {
35
+ jsonOutput({ ok: false, command: 'read-context', error: errMsg });
36
+ }
37
+ else {
38
+ console.error(`错误: ${errMsg}`);
39
+ }
40
+ process.exitCode = 1;
41
+ return;
42
+ }
43
+ let result;
44
+ try {
45
+ result = await sendDaemonRequest('read_context', {
46
+ mode, ancestorLevels, depth, maxNodes, maxSiblings,
47
+ });
48
+ }
49
+ catch (err) {
50
+ if (err instanceof DaemonNotRunningError || err instanceof DaemonUnreachableError) {
51
+ if (json) {
52
+ jsonOutput({ ok: false, command: 'read-context', error: err.message });
53
+ }
54
+ else {
55
+ console.error(`错误: ${err.message}`);
56
+ }
57
+ process.exitCode = 2;
58
+ return;
59
+ }
60
+ const errorMsg = err instanceof Error ? err.message : String(err);
61
+ if (json) {
62
+ jsonOutput({ ok: false, command: 'read-context', error: errorMsg });
63
+ }
64
+ else {
65
+ console.error(`错误: ${errorMsg}`);
66
+ }
67
+ process.exitCode = 1;
68
+ return;
69
+ }
70
+ const data = result;
71
+ if (json) {
72
+ jsonOutput({ ok: true, command: 'read-context', data });
73
+ }
74
+ else {
75
+ console.log(data.outline);
76
+ }
77
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * read-globe 命令
3
+ *
4
+ * 读取知识库全局概览(仅 Document 层级)。
5
+ * - --depth N 控制 Document 嵌套深度(默认 -1 无限)
6
+ * - --max-nodes N 全局节点上限(默认 200)
7
+ * - --max-siblings N 每个父节点下展示的 children 上限(默认 20)
8
+ * - --json 结构化 JSON 输出
9
+ */
10
+ export interface ReadGlobeOptions {
11
+ json?: boolean;
12
+ depth?: string;
13
+ maxNodes?: string;
14
+ maxSiblings?: string;
15
+ }
16
+ export declare function readGlobeCommand(options?: ReadGlobeOptions): Promise<void>;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * read-globe 命令
3
+ *
4
+ * 读取知识库全局概览(仅 Document 层级)。
5
+ * - --depth N 控制 Document 嵌套深度(默认 -1 无限)
6
+ * - --max-nodes N 全局节点上限(默认 200)
7
+ * - --max-siblings N 每个父节点下展示的 children 上限(默认 20)
8
+ * - --json 结构化 JSON 输出
9
+ */
10
+ import { sendDaemonRequest, DaemonNotRunningError, DaemonUnreachableError } from '../daemon/send-request.js';
11
+ import { jsonOutput } from '../utils/output.js';
12
+ export async function readGlobeCommand(options = {}) {
13
+ const { json } = options;
14
+ const depth = options.depth !== undefined ? parseInt(options.depth, 10) : undefined;
15
+ const maxNodes = options.maxNodes !== undefined ? parseInt(options.maxNodes, 10) : undefined;
16
+ const maxSiblings = options.maxSiblings !== undefined ? parseInt(options.maxSiblings, 10) : undefined;
17
+ if ((depth !== undefined && isNaN(depth)) || (maxNodes !== undefined && isNaN(maxNodes)) || (maxSiblings !== undefined && isNaN(maxSiblings))) {
18
+ const errMsg = '--depth, --max-nodes, --max-siblings must be numbers';
19
+ if (json) {
20
+ jsonOutput({ ok: false, command: 'read-globe', error: errMsg });
21
+ }
22
+ else {
23
+ console.error(`错误: ${errMsg}`);
24
+ }
25
+ process.exitCode = 1;
26
+ return;
27
+ }
28
+ let result;
29
+ try {
30
+ result = await sendDaemonRequest('read_globe', { depth, maxNodes, maxSiblings });
31
+ }
32
+ catch (err) {
33
+ if (err instanceof DaemonNotRunningError || err instanceof DaemonUnreachableError) {
34
+ if (json) {
35
+ jsonOutput({ ok: false, command: 'read-globe', error: err.message });
36
+ }
37
+ else {
38
+ console.error(`错误: ${err.message}`);
39
+ }
40
+ process.exitCode = 2;
41
+ return;
42
+ }
43
+ const errorMsg = err instanceof Error ? err.message : String(err);
44
+ if (json) {
45
+ jsonOutput({ ok: false, command: 'read-globe', error: errorMsg });
46
+ }
47
+ else {
48
+ console.error(`错误: ${errorMsg}`);
49
+ }
50
+ process.exitCode = 1;
51
+ return;
52
+ }
53
+ const data = result;
54
+ if (json) {
55
+ jsonOutput({ ok: true, command: 'read-globe', data });
56
+ }
57
+ else {
58
+ console.log(data.outline);
59
+ }
60
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * read-rem 命令
3
+ *
4
+ * 读取单个 Rem 的完整 JSON 对象。
5
+ * - 默认输出 [RW] + [R] 字段(34 个)
6
+ * - --full 输出全部 51 个字段(含 [R-F])
7
+ * - --fields 指定输出字段子集
8
+ * - 退出码:0 成功 / 1 业务错误 / 2 守护进程不可达
9
+ */
10
+ export interface ReadRemOptions {
11
+ json?: boolean;
12
+ fields?: string;
13
+ full?: boolean;
14
+ includePowerup?: boolean;
15
+ }
16
+ export declare function readRemCommand(remId: string, options?: ReadRemOptions): Promise<void>;
@@ -0,0 +1,80 @@
1
+ /**
2
+ * read-rem 命令
3
+ *
4
+ * 读取单个 Rem 的完整 JSON 对象。
5
+ * - 默认输出 [RW] + [R] 字段(34 个)
6
+ * - --full 输出全部 51 个字段(含 [R-F])
7
+ * - --fields 指定输出字段子集
8
+ * - 退出码:0 成功 / 1 业务错误 / 2 守护进程不可达
9
+ */
10
+ import { sendDaemonRequest, DaemonNotRunningError, DaemonUnreachableError } from '../daemon/send-request.js';
11
+ import { jsonOutput } from '../utils/output.js';
12
+ export async function readRemCommand(remId, options = {}) {
13
+ const { json, fields, full, includePowerup } = options;
14
+ // 构造 payload
15
+ const payload = { remId };
16
+ if (includePowerup) {
17
+ payload.includePowerup = true;
18
+ }
19
+ if (full) {
20
+ payload.full = true;
21
+ }
22
+ else if (fields) {
23
+ payload.fields = fields.split(',').map(f => f.trim()).filter(Boolean);
24
+ }
25
+ let result;
26
+ try {
27
+ result = await sendDaemonRequest('read_rem', payload);
28
+ }
29
+ catch (err) {
30
+ if (err instanceof DaemonNotRunningError) {
31
+ if (json) {
32
+ jsonOutput({ ok: false, command: 'read-rem', error: err.message });
33
+ }
34
+ else {
35
+ console.error(`错误: ${err.message}`);
36
+ }
37
+ process.exitCode = 2;
38
+ return;
39
+ }
40
+ if (err instanceof DaemonUnreachableError) {
41
+ if (json) {
42
+ jsonOutput({ ok: false, command: 'read-rem', error: err.message });
43
+ }
44
+ else {
45
+ console.error(`错误: ${err.message}`);
46
+ }
47
+ process.exitCode = 2;
48
+ return;
49
+ }
50
+ // 业务错误(Rem not found, Plugin 未连接等)
51
+ const errorMsg = err instanceof Error ? err.message : String(err);
52
+ if (json) {
53
+ jsonOutput({ ok: false, command: 'read-rem', error: errorMsg });
54
+ }
55
+ else {
56
+ console.error(`错误: ${errorMsg}`);
57
+ }
58
+ process.exitCode = 1;
59
+ return;
60
+ }
61
+ const data = result;
62
+ const cacheOverridden = data._cacheOverridden;
63
+ delete data._cacheOverridden;
64
+ const powerupFiltered = data.powerupFiltered;
65
+ delete data.powerupFiltered;
66
+ if (json) {
67
+ jsonOutput({ ok: true, command: 'read-rem', data, ...(cacheOverridden ? { cacheOverridden } : {}), ...(powerupFiltered ? { powerupFiltered } : {}) });
68
+ }
69
+ else {
70
+ if (cacheOverridden) {
71
+ console.warn(`注意: Rem ${cacheOverridden.id} 的缓存(${cacheOverridden.previousCachedAt})已被本次 read 覆盖`);
72
+ }
73
+ if (powerupFiltered) {
74
+ console.warn(`⚠ 已过滤 Powerup 系统数据(${powerupFiltered.tags} 个 Tag、${powerupFiltered.children} 个隐藏子 Rem)。`);
75
+ console.warn(' Powerup 是 RemNote 的格式渲染机制,其产生的 Tag 和子 Rem 在 UI 中不可见。');
76
+ console.warn(' 如需查看完整数据,请加 --includePowerup 选项。');
77
+ }
78
+ console.log(JSON.stringify(data, null, 2));
79
+ }
80
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * read-tree 命令
3
+ *
4
+ * 读取 Rem 子树并序列化为带缩进的 Markdown 大纲。
5
+ * - --depth N 控制展开深度(默认 3,-1 = 全部展开)
6
+ * - --json 结构化 JSON 输出
7
+ * - 退出码:0 成功 / 1 业务错误 / 2 守护进程不可达
8
+ */
9
+ export interface ReadTreeOptions {
10
+ json?: boolean;
11
+ depth?: string;
12
+ maxNodes?: string;
13
+ maxSiblings?: string;
14
+ ancestorLevels?: string;
15
+ includePowerup?: boolean;
16
+ }
17
+ export declare function readTreeCommand(remId: string, options?: ReadTreeOptions): Promise<void>;
@@ -0,0 +1,85 @@
1
+ /**
2
+ * read-tree 命令
3
+ *
4
+ * 读取 Rem 子树并序列化为带缩进的 Markdown 大纲。
5
+ * - --depth N 控制展开深度(默认 3,-1 = 全部展开)
6
+ * - --json 结构化 JSON 输出
7
+ * - 退出码:0 成功 / 1 业务错误 / 2 守护进程不可达
8
+ */
9
+ import { sendDaemonRequest, DaemonNotRunningError, DaemonUnreachableError } from '../daemon/send-request.js';
10
+ import { jsonOutput } from '../utils/output.js';
11
+ export async function readTreeCommand(remId, options = {}) {
12
+ const { json } = options;
13
+ const depth = options.depth !== undefined ? parseInt(options.depth, 10) : undefined;
14
+ const maxNodes = options.maxNodes !== undefined ? parseInt(options.maxNodes, 10) : undefined;
15
+ const maxSiblings = options.maxSiblings !== undefined ? parseInt(options.maxSiblings, 10) : undefined;
16
+ const ancestorLevels = options.ancestorLevels !== undefined ? parseInt(options.ancestorLevels, 10) : undefined;
17
+ if ((depth !== undefined && isNaN(depth)) || (maxNodes !== undefined && isNaN(maxNodes)) || (maxSiblings !== undefined && isNaN(maxSiblings)) || (ancestorLevels !== undefined && isNaN(ancestorLevels))) {
18
+ const errMsg = '--depth, --max-nodes, --max-siblings, --ancestor-levels must be numbers';
19
+ if (json) {
20
+ jsonOutput({ ok: false, command: 'read-tree', error: errMsg });
21
+ }
22
+ else {
23
+ console.error(`错误: ${errMsg}`);
24
+ }
25
+ process.exitCode = 1;
26
+ return;
27
+ }
28
+ let result;
29
+ try {
30
+ result = await sendDaemonRequest('read_tree', {
31
+ remId, depth, maxNodes, maxSiblings, ancestorLevels,
32
+ includePowerup: options.includePowerup,
33
+ });
34
+ }
35
+ catch (err) {
36
+ if (err instanceof DaemonNotRunningError || err instanceof DaemonUnreachableError) {
37
+ if (json) {
38
+ jsonOutput({ ok: false, command: 'read-tree', error: err.message });
39
+ }
40
+ else {
41
+ console.error(`错误: ${err.message}`);
42
+ }
43
+ process.exitCode = 2;
44
+ return;
45
+ }
46
+ const errorMsg = err instanceof Error ? err.message : String(err);
47
+ if (json) {
48
+ jsonOutput({ ok: false, command: 'read-tree', error: errorMsg });
49
+ }
50
+ else {
51
+ console.error(`错误: ${errorMsg}`);
52
+ }
53
+ process.exitCode = 1;
54
+ return;
55
+ }
56
+ const data = result;
57
+ const cacheOverridden = data._cacheOverridden;
58
+ delete data._cacheOverridden;
59
+ const powerupFiltered = data.powerupFiltered;
60
+ delete data.powerupFiltered;
61
+ const ancestors = data.ancestors;
62
+ delete data.ancestors;
63
+ if (json) {
64
+ jsonOutput({ ok: true, command: 'read-tree', data, ...(ancestors ? { ancestors } : {}), ...(cacheOverridden ? { cacheOverridden } : {}), ...(powerupFiltered ? { powerupFiltered } : {}) });
65
+ }
66
+ else {
67
+ if (cacheOverridden) {
68
+ console.warn(`注意: Tree ${cacheOverridden.id} 的缓存(${cacheOverridden.previousCachedAt})已被本次 read 覆盖`);
69
+ }
70
+ if (powerupFiltered) {
71
+ console.warn(`⚠ 已过滤 Powerup 系统数据(${powerupFiltered.tags} 个 Tag、${powerupFiltered.children} 个隐藏子 Rem)。`);
72
+ console.warn(' Powerup 是 RemNote 的格式渲染机制,其产生的 Tag 和子 Rem 在 UI 中不可见。');
73
+ console.warn(' 如需查看完整数据,请加 --includePowerup 选项。');
74
+ }
75
+ if (ancestors && ancestors.length > 0) {
76
+ // 由近及远 → 反转为由远及近,构建路径
77
+ const pathParts = [...ancestors].reverse().map(a => {
78
+ const topMark = a.isTopLevel ? ' [top]' : '';
79
+ return `${a.name} (${a.id}${topMark})`;
80
+ });
81
+ console.log(`<!-- ancestors: ${pathParts.join(' > ')} -->`);
82
+ }
83
+ console.log(data.outline);
84
+ }
85
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * search 命令
3
+ *
4
+ * 在知识库中按文本搜索 Rem。
5
+ * - --limit N 结果数量上限(默认 20)
6
+ * - --json 结构化 JSON 输出
7
+ */
8
+ export interface SearchOptions {
9
+ json?: boolean;
10
+ limit?: string;
11
+ }
12
+ export declare function searchCommand(query: string, options?: SearchOptions): Promise<void>;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * search 命令
3
+ *
4
+ * 在知识库中按文本搜索 Rem。
5
+ * - --limit N 结果数量上限(默认 20)
6
+ * - --json 结构化 JSON 输出
7
+ */
8
+ import { sendDaemonRequest, DaemonNotRunningError, DaemonUnreachableError } from '../daemon/send-request.js';
9
+ import { jsonOutput } from '../utils/output.js';
10
+ export async function searchCommand(query, options = {}) {
11
+ const { json } = options;
12
+ const numResults = options.limit !== undefined ? parseInt(options.limit, 10) : undefined;
13
+ if (numResults !== undefined && isNaN(numResults)) {
14
+ const errMsg = '--limit must be a number';
15
+ if (json) {
16
+ jsonOutput({ ok: false, command: 'search', error: errMsg });
17
+ }
18
+ else {
19
+ console.error(`错误: ${errMsg}`);
20
+ }
21
+ process.exitCode = 1;
22
+ return;
23
+ }
24
+ let result;
25
+ try {
26
+ result = await sendDaemonRequest('search', { query, numResults });
27
+ }
28
+ catch (err) {
29
+ if (err instanceof DaemonNotRunningError || err instanceof DaemonUnreachableError) {
30
+ if (json) {
31
+ jsonOutput({ ok: false, command: 'search', error: err.message });
32
+ }
33
+ else {
34
+ console.error(`错误: ${err.message}`);
35
+ }
36
+ process.exitCode = 2;
37
+ return;
38
+ }
39
+ const errorMsg = err instanceof Error ? err.message : String(err);
40
+ if (json) {
41
+ jsonOutput({ ok: false, command: 'search', error: errorMsg });
42
+ }
43
+ else {
44
+ console.error(`错误: ${errorMsg}`);
45
+ }
46
+ process.exitCode = 1;
47
+ return;
48
+ }
49
+ const data = result;
50
+ if (json) {
51
+ jsonOutput({ ok: true, command: 'search', data });
52
+ }
53
+ else {
54
+ if (data.results.length === 0) {
55
+ console.log(`未找到与 "${data.query}" 相关的结果`);
56
+ }
57
+ else {
58
+ console.log(`搜索 "${data.query}",找到 ${data.totalFound} 条结果:\n`);
59
+ for (const item of data.results) {
60
+ const docTag = item.isDocument ? ' [Doc]' : '';
61
+ console.log(` [${item.remId}]${docTag} ${item.text}`);
62
+ }
63
+ }
64
+ }
65
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * 配置加载
3
+ *
4
+ * 从项目根目录读取 .remnote-bridge.json,合并默认值。
5
+ * 文件不存在时使用全部默认值,不报错。
6
+ */
7
+ export interface DefaultsConfig {
8
+ maxNodes: number;
9
+ maxSiblings: number;
10
+ cacheMaxSize: number;
11
+ readTreeDepth: number;
12
+ readTreeAncestorLevels: number;
13
+ readTreeIncludePowerup: boolean;
14
+ readGlobeDepth: number;
15
+ readContextMode: 'focus' | 'page';
16
+ readContextAncestorLevels: number;
17
+ readContextDepth: number;
18
+ searchNumResults: number;
19
+ }
20
+ export declare const DEFAULT_DEFAULTS: Readonly<DefaultsConfig>;
21
+ export interface BridgeConfig {
22
+ wsPort: number;
23
+ devServerPort: number;
24
+ configPort: number;
25
+ daemonTimeoutMinutes: number;
26
+ defaults: DefaultsConfig;
27
+ }
28
+ export declare const DEFAULT_CONFIG: Readonly<BridgeConfig>;
29
+ /**
30
+ * 查找项目根目录(monorepo 根:包含 .git 目录的最近祖先)
31
+ *
32
+ * 从 startDir 向上查找 .git 目录,找到即返回。
33
+ * 到达文件系统根仍未找到时回退到 cwd。
34
+ */
35
+ export declare function findProjectRoot(startDir?: string): string;
36
+ /**
37
+ * 加载配置。不存在时返回默认值。
38
+ */
39
+ export declare function loadConfig(projectRoot?: string): BridgeConfig;
40
+ /**
41
+ * 获取配置文件路径
42
+ */
43
+ export declare function configFilePath(projectRoot?: string): string;
44
+ /**
45
+ * 原子写入配置文件(写临时文件 → rename)
46
+ */
47
+ export declare function saveConfig(filePath: string, config: BridgeConfig): void;
48
+ /**
49
+ * PID 文件路径
50
+ */
51
+ export declare function pidFilePath(projectRoot?: string): string;
52
+ /**
53
+ * 日志文件路径
54
+ */
55
+ export declare function logFilePath(projectRoot?: string): string;
@@ -0,0 +1,139 @@
1
+ /**
2
+ * 配置加载
3
+ *
4
+ * 从项目根目录读取 .remnote-bridge.json,合并默认值。
5
+ * 文件不存在时使用全部默认值,不报错。
6
+ */
7
+ import fs from 'fs';
8
+ import path from 'path';
9
+ export const DEFAULT_DEFAULTS = {
10
+ maxNodes: 200,
11
+ maxSiblings: 20,
12
+ cacheMaxSize: 200,
13
+ readTreeDepth: 3,
14
+ readTreeAncestorLevels: 0,
15
+ readTreeIncludePowerup: false,
16
+ readGlobeDepth: -1,
17
+ readContextMode: 'focus',
18
+ readContextAncestorLevels: 2,
19
+ readContextDepth: 3,
20
+ searchNumResults: 20,
21
+ };
22
+ export const DEFAULT_CONFIG = {
23
+ wsPort: 3002,
24
+ devServerPort: 8080,
25
+ configPort: 3003,
26
+ daemonTimeoutMinutes: 30,
27
+ defaults: { ...DEFAULT_DEFAULTS },
28
+ };
29
+ const CONFIG_FILENAME = '.remnote-bridge.json';
30
+ function isValidPort(value) {
31
+ return typeof value === 'number' && Number.isInteger(value) && value >= 1 && value <= 65535;
32
+ }
33
+ /**
34
+ * 查找项目根目录(monorepo 根:包含 .git 目录的最近祖先)
35
+ *
36
+ * 从 startDir 向上查找 .git 目录,找到即返回。
37
+ * 到达文件系统根仍未找到时回退到 cwd。
38
+ */
39
+ export function findProjectRoot(startDir = process.cwd()) {
40
+ let dir = path.resolve(startDir);
41
+ while (true) {
42
+ // 优先匹配 .git 目录(monorepo 根标识)
43
+ if (fs.existsSync(path.join(dir, '.git'))) {
44
+ return dir;
45
+ }
46
+ const parent = path.dirname(dir);
47
+ if (parent === dir) {
48
+ // 到达文件系统根,回退到 cwd
49
+ return process.cwd();
50
+ }
51
+ dir = parent;
52
+ }
53
+ }
54
+ function isPositiveNumber(value) {
55
+ return typeof value === 'number' && value > 0;
56
+ }
57
+ function mergeDefaults(parsed) {
58
+ if (!parsed)
59
+ return { ...DEFAULT_DEFAULTS };
60
+ return {
61
+ maxNodes: isPositiveNumber(parsed.maxNodes) ? parsed.maxNodes : DEFAULT_DEFAULTS.maxNodes,
62
+ maxSiblings: isPositiveNumber(parsed.maxSiblings) ? parsed.maxSiblings : DEFAULT_DEFAULTS.maxSiblings,
63
+ cacheMaxSize: isPositiveNumber(parsed.cacheMaxSize) ? parsed.cacheMaxSize : DEFAULT_DEFAULTS.cacheMaxSize,
64
+ readTreeDepth: typeof parsed.readTreeDepth === 'number' ? parsed.readTreeDepth : DEFAULT_DEFAULTS.readTreeDepth,
65
+ readTreeAncestorLevels: typeof parsed.readTreeAncestorLevels === 'number' && parsed.readTreeAncestorLevels >= 0
66
+ ? parsed.readTreeAncestorLevels : DEFAULT_DEFAULTS.readTreeAncestorLevels,
67
+ readTreeIncludePowerup: typeof parsed.readTreeIncludePowerup === 'boolean'
68
+ ? parsed.readTreeIncludePowerup : DEFAULT_DEFAULTS.readTreeIncludePowerup,
69
+ readGlobeDepth: typeof parsed.readGlobeDepth === 'number' ? parsed.readGlobeDepth : DEFAULT_DEFAULTS.readGlobeDepth,
70
+ readContextMode: parsed.readContextMode === 'focus' || parsed.readContextMode === 'page'
71
+ ? parsed.readContextMode : DEFAULT_DEFAULTS.readContextMode,
72
+ readContextAncestorLevels: typeof parsed.readContextAncestorLevels === 'number' && parsed.readContextAncestorLevels >= 0
73
+ ? parsed.readContextAncestorLevels : DEFAULT_DEFAULTS.readContextAncestorLevels,
74
+ readContextDepth: typeof parsed.readContextDepth === 'number' ? parsed.readContextDepth : DEFAULT_DEFAULTS.readContextDepth,
75
+ searchNumResults: isPositiveNumber(parsed.searchNumResults) ? parsed.searchNumResults : DEFAULT_DEFAULTS.searchNumResults,
76
+ };
77
+ }
78
+ /**
79
+ * 加载配置。不存在时返回默认值。
80
+ */
81
+ export function loadConfig(projectRoot) {
82
+ const root = projectRoot ?? findProjectRoot();
83
+ const configPath = path.join(root, CONFIG_FILENAME);
84
+ if (!fs.existsSync(configPath)) {
85
+ return { ...DEFAULT_CONFIG, defaults: { ...DEFAULT_DEFAULTS } };
86
+ }
87
+ try {
88
+ const raw = fs.readFileSync(configPath, 'utf-8');
89
+ const parsed = JSON.parse(raw);
90
+ const config = {
91
+ wsPort: isValidPort(parsed.wsPort) ? parsed.wsPort : DEFAULT_CONFIG.wsPort,
92
+ devServerPort: isValidPort(parsed.devServerPort) ? parsed.devServerPort : DEFAULT_CONFIG.devServerPort,
93
+ configPort: isValidPort(parsed.configPort) ? parsed.configPort : DEFAULT_CONFIG.configPort,
94
+ daemonTimeoutMinutes: typeof parsed.daemonTimeoutMinutes === 'number' && parsed.daemonTimeoutMinutes > 0
95
+ ? parsed.daemonTimeoutMinutes
96
+ : DEFAULT_CONFIG.daemonTimeoutMinutes,
97
+ defaults: mergeDefaults(parsed.defaults),
98
+ };
99
+ // 端口冲突校验
100
+ const ports = [config.wsPort, config.devServerPort, config.configPort];
101
+ if (new Set(ports).size !== ports.length) {
102
+ throw new Error('wsPort, devServerPort, configPort must be different');
103
+ }
104
+ return config;
105
+ }
106
+ catch {
107
+ // 配置文件损坏时使用默认值
108
+ return { ...DEFAULT_CONFIG, defaults: { ...DEFAULT_DEFAULTS } };
109
+ }
110
+ }
111
+ /**
112
+ * 获取配置文件路径
113
+ */
114
+ export function configFilePath(projectRoot) {
115
+ const root = projectRoot ?? findProjectRoot();
116
+ return path.join(root, CONFIG_FILENAME);
117
+ }
118
+ /**
119
+ * 原子写入配置文件(写临时文件 → rename)
120
+ */
121
+ export function saveConfig(filePath, config) {
122
+ const tmpPath = filePath + '.tmp.' + process.pid;
123
+ fs.writeFileSync(tmpPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
124
+ fs.renameSync(tmpPath, filePath);
125
+ }
126
+ /**
127
+ * PID 文件路径
128
+ */
129
+ export function pidFilePath(projectRoot) {
130
+ const root = projectRoot ?? findProjectRoot();
131
+ return path.join(root, '.remnote-bridge.pid');
132
+ }
133
+ /**
134
+ * 日志文件路径
135
+ */
136
+ export function logFilePath(projectRoot) {
137
+ const root = projectRoot ?? findProjectRoot();
138
+ return path.join(root, '.remnote-bridge.log');
139
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * 守护进程主逻辑
3
+ *
4
+ * 作为 fork 子进程运行:
5
+ * 1. 启动 WS Server
6
+ * 2. 启动 webpack-dev-server 子进程
7
+ * 3. 写入 PID 文件
8
+ * 4. 管理自动超时关闭
9
+ * 5. 通过 IPC 向父进程发送 ready 信号
10
+ */
11
+ export {};