noteconnection 1.8.0 → 1.9.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 (195) hide show
  1. package/README.md +257 -1803
  2. package/dist/src/agent_workspace.contract.parity.test.js +105 -421
  3. package/dist/src/agent_workspace.frontend.test.js +410 -11
  4. package/dist/src/android.knowledgebase.picker.contract.test.js +102 -0
  5. package/dist/src/appimage.patchelf.test.js +113 -0
  6. package/dist/src/appimage.portability.test.js +156 -0
  7. package/dist/src/backend/FileLoader.js +18 -4
  8. package/dist/src/backend/FileLoader.test.js +125 -0
  9. package/dist/src/backend/GraphBuilder.identity.test.js +86 -0
  10. package/dist/src/backend/GraphBuilder.js +66 -18
  11. package/dist/src/backend/GraphBuilder.matching.test.js +127 -0
  12. package/dist/src/backend/GraphMetrics.js +0 -3
  13. package/dist/src/backend/ResourceIdentity.js +155 -0
  14. package/dist/src/backend/ResourceIdentity.test.js +113 -0
  15. package/dist/src/backend/algorithms/CycleDetection.js +0 -1
  16. package/dist/src/backend/algorithms/HybridEngine.js +0 -3
  17. package/dist/src/backend/algorithms/LayoutEngine.js +0 -1
  18. package/dist/src/backend/algorithms/StatisticalAnalyzer.js +0 -4
  19. package/dist/src/backend/algorithms/TopologicalSort.js +0 -2
  20. package/dist/src/backend/build_dag.js +0 -1
  21. package/dist/src/capacitor.bridge.serialization.contract.test.js +3 -0
  22. package/dist/src/capacitor.runtime.contract.test.js +11 -2
  23. package/dist/src/core/Graph.js +138 -42
  24. package/dist/src/core/Graph.test.js +74 -0
  25. package/dist/src/core/NoteConnection.js +2 -2
  26. package/dist/src/core/PathBridge.js +265 -2
  27. package/dist/src/core/ResourceReference.js +17 -0
  28. package/dist/src/desktop.godot.pack.test.js +142 -0
  29. package/dist/src/desktop.runtime.graph.test.js +66 -0
  30. package/dist/src/export/WorkspaceExportBundle.js +44 -0
  31. package/dist/src/export/WorkspaceExportBundle.test.js +61 -0
  32. package/dist/src/foundation.release.evidence.contract.test.js +127 -9
  33. package/dist/src/foundation.sqlite.runtime.contract.test.js +6 -0
  34. package/dist/src/frontend/README.md +257 -1803
  35. package/dist/src/frontend/agent_workspace.js +665 -7
  36. package/dist/src/frontend/index.html +31 -2
  37. package/dist/src/frontend/knowledge_projection_contract.js +230 -0
  38. package/dist/src/frontend/knowledge_projection_store.js +214 -0
  39. package/dist/src/frontend/libs/path_core.js +158 -42
  40. package/dist/src/frontend/locales/en.json +32 -0
  41. package/dist/src/frontend/locales/zh.json +32 -0
  42. package/dist/src/frontend/mobile_budget_runtime.js +33 -0
  43. package/dist/src/frontend/mobile_exact_analyzer.js +393 -0
  44. package/dist/src/frontend/mobile_identity_contract.js +221 -0
  45. package/dist/src/frontend/mobile_semantic_comparator.js +264 -0
  46. package/dist/src/frontend/path.html +6 -2
  47. package/dist/src/frontend/simulationWorker.js +1 -1
  48. package/dist/src/frontend/source_manager.js +110 -4
  49. package/dist/src/frontend/storage_provider.js +661 -71
  50. package/dist/src/frontend/styles.css +107 -13
  51. package/dist/src/frontend/workspace_panes.js +111 -7
  52. package/dist/src/frontend.runtime.assets.contract.test.js +81 -0
  53. package/dist/src/graph.answer.operator.contract.test.js +72 -0
  54. package/dist/src/index.js +6 -2
  55. package/dist/src/index.knowledge-root.test.js +118 -0
  56. package/dist/src/indexing/IndexLifecycle.js +22 -0
  57. package/dist/src/indexing/IndexLifecycle.test.js +24 -0
  58. package/dist/src/knowledge.api.contract.test.js +4 -2
  59. package/dist/src/knowledge_projection.contract.test.js +134 -0
  60. package/dist/src/knowledge_projection_store.test.js +200 -0
  61. package/dist/src/learning/AnswerQualityEvaluation.js +153 -0
  62. package/dist/src/learning/AnswerQualityEvaluation.test.js +118 -0
  63. package/dist/src/learning/KnowledgeLearningPlatform.js +4687 -3892
  64. package/dist/src/learning/KnowledgeLearningPlatform.persistence.test.js +418 -0
  65. package/dist/src/learning/KnowledgeLearningPlatform.test.js +243 -6
  66. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.js +25 -1
  67. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.test.js +13 -5
  68. package/dist/src/learning/MeasurementEvidenceConflicts.test.js +78 -0
  69. package/dist/src/learning/PublicAnswerContract.test.js +85 -0
  70. package/dist/src/learning/PublicEvidenceSelection.test.js +94 -0
  71. package/dist/src/learning/PublicMeasurementDisclosure.test.js +54 -0
  72. package/dist/src/learning/agentConversationExecution.js +211 -0
  73. package/dist/src/learning/agentConversationExecution.test.js +209 -0
  74. package/dist/src/learning/agentConversationSerialization.js +266 -0
  75. package/dist/src/learning/agentConversationSerialization.test.js +165 -0
  76. package/dist/src/learning/agentResponseBudget.js +179 -0
  77. package/dist/src/learning/agentResponseBudget.test.js +97 -0
  78. package/dist/src/learning/agentWorkspaceCapabilityContracts.js +65 -0
  79. package/dist/src/learning/answerReleaseReview.js +1191 -250
  80. package/dist/src/learning/answerReleaseReview.test.js +1293 -7
  81. package/dist/src/learning/answerTaskPlan.js +473 -0
  82. package/dist/src/learning/conversationComposer.js +627 -386
  83. package/dist/src/learning/conversationComposer.test.js +1403 -0
  84. package/dist/src/learning/evidenceContextAssembler.js +312 -120
  85. package/dist/src/learning/graphAnswerCoverage.js +37 -0
  86. package/dist/src/learning/graphAnswerCoverage.test.js +93 -0
  87. package/dist/src/learning/graphAnswerCoverageCalibration.js +59 -0
  88. package/dist/src/learning/graphAnswerCoverageCalibration.test.js +46 -0
  89. package/dist/src/learning/graphAnswerFacts.js +94 -0
  90. package/dist/src/learning/graphAnswerFacts.test.js +39 -0
  91. package/dist/src/learning/graphAnswerPlan.js +908 -0
  92. package/dist/src/learning/graphAnswerPlan.test.js +907 -0
  93. package/dist/src/learning/graphAnswerQualityCalibration.js +255 -0
  94. package/dist/src/learning/graphAnswerQualityCalibration.test.js +72 -0
  95. package/dist/src/learning/graphAnswerQualityPolicy.js +30 -0
  96. package/dist/src/learning/graphClaimMatcher.js +104 -0
  97. package/dist/src/learning/graphConditionedContext.js +109 -0
  98. package/dist/src/learning/graphConditionedContext.test.js +99 -0
  99. package/dist/src/learning/graphContextAssembler.js +3 -13
  100. package/dist/src/learning/graphContextAssembler.test.js +2 -2
  101. package/dist/src/learning/graphExpansionPolicy.js +22 -0
  102. package/dist/src/learning/graphExpansionPolicy.test.js +33 -0
  103. package/dist/src/learning/identityMetadata.test.js +26 -0
  104. package/dist/src/learning/index.js +1 -0
  105. package/dist/src/learning/mobileAnswerProjection.js +104 -0
  106. package/dist/src/learning/mobileAnswerProjection.test.js +102 -0
  107. package/dist/src/learning/queryBackend.js +34 -10
  108. package/dist/src/learning/queryBackend.test.js +21 -0
  109. package/dist/src/learning/ragContextPack.js +115 -16
  110. package/dist/src/learning/ragContextPack.test.js +151 -0
  111. package/dist/src/learning/ragEvidenceQuality.js +96 -0
  112. package/dist/src/learning/ragEvidenceQuality.test.js +29 -0
  113. package/dist/src/learning/ragPublicText.js +67 -4
  114. package/dist/src/learning/ragPublicText.test.js +57 -0
  115. package/dist/src/learning/ragSufficiencyJudge.js +11 -1
  116. package/dist/src/learning/ragSufficiencyProviderJudge.js +16 -26
  117. package/dist/src/learning/ragSufficiencyProviderJudge.test.js +51 -0
  118. package/dist/src/learning/requestNormalization.js +106 -0
  119. package/dist/src/learning/requestNormalization.test.js +101 -0
  120. package/dist/src/learning/store.js +116 -12
  121. package/dist/src/learning/store.test.js +299 -0
  122. package/dist/src/learning/vectorAccelerationAdapter.js +22 -6
  123. package/dist/src/learning/vectorAccelerationAdapter.test.js +30 -0
  124. package/dist/src/learning/workspaceHydration.js +168 -0
  125. package/dist/src/learning/workspaceHydration.test.js +30 -0
  126. package/dist/src/license.policy.contract.test.js +9 -4
  127. package/dist/src/markdown/MarkdownGateway.js +4 -0
  128. package/dist/src/markdown/MarkdownGateway.test.js +59 -0
  129. package/dist/src/middleware/SseResponseWriter.js +122 -0
  130. package/dist/src/middleware/SseResponseWriter.test.js +54 -0
  131. package/dist/src/middleware/auth.js +28 -7
  132. package/dist/src/middleware/auth.test.js +33 -0
  133. package/dist/src/middleware/index.js +2 -1
  134. package/dist/src/mobile.android.evidence.contract.test.js +146 -0
  135. package/dist/src/mobile.android.signing.contract.test.js +66 -0
  136. package/dist/src/mobile.artifact.contract.test.js +144 -0
  137. package/dist/src/mobile.budget.contract.test.js +90 -0
  138. package/dist/src/mobile.canonical-id.readiness.test.js +54 -0
  139. package/dist/src/mobile.graph.parity.contract.test.js +207 -0
  140. package/dist/src/mobile.identity.contract.test.js +66 -0
  141. package/dist/src/mobile.identity.corpus.manifest.test.js +72 -0
  142. package/dist/src/mobile.identity.corpus.test.js +40 -0
  143. package/dist/src/mobile.native.recovery.contract.test.js +64 -0
  144. package/dist/src/mobile.pipeline.test.js +11 -1
  145. package/dist/src/mobile.profile.contract.test.js +225 -0
  146. package/dist/src/mobile.semantic.parity.test.js +92 -0
  147. package/dist/src/mobile_exact_analyzer.test.js +113 -0
  148. package/dist/src/notemd/ContentGenerator.js +4 -9
  149. package/dist/src/notemd/FileProcessor.js +4 -9
  150. package/dist/src/notemd/NotemdService.js +12 -17
  151. package/dist/src/notemd/Translator.js +4 -9
  152. package/dist/src/notemd/progressReporter.js +9 -0
  153. package/dist/src/notemd.server.integration.test.js +119 -4
  154. package/dist/src/notemd.workflow.pipeline.test.js +1 -1
  155. package/dist/src/pathbridge.handshake.contract.test.js +29 -0
  156. package/dist/src/pathbridge.host-adapter.contract.test.js +133 -0
  157. package/dist/src/pathcore.bundle.contract.test.js +8 -0
  158. package/dist/src/platform/ExportProfile.js +28 -0
  159. package/dist/src/platform/MobileBudget.js +11 -0
  160. package/dist/src/platform/PlatformCapabilities.js +14 -0
  161. package/dist/src/platform/PlatformCapabilities.test.js +6 -0
  162. package/dist/src/query_backend.external_http.integration.test.js +2 -2
  163. package/dist/src/release.godot.mirror.contract.test.js +28 -4
  164. package/dist/src/resources/ResourceRegistry.js +40 -0
  165. package/dist/src/resources/ResourceRegistry.test.js +36 -0
  166. package/dist/src/routes/index.js +0 -4
  167. package/dist/src/routes/knowledge.js +46 -33
  168. package/dist/src/routes/knowledgePayload.js +185 -0
  169. package/dist/src/routes/knowledgePayload.test.js +53 -0
  170. package/dist/src/routes/markdown.js +8 -0
  171. package/dist/src/routes/notemd.js +990 -234
  172. package/dist/src/routes/registry.contract.test.js +5 -2
  173. package/dist/src/routes/registry.shadow.contract.test.js +22 -0
  174. package/dist/src/routes/render.js +14 -51
  175. package/dist/src/runtime.capabilities.test.js +18 -2
  176. package/dist/src/server.js +566 -1090
  177. package/dist/src/server.migration.test.js +417 -15
  178. package/dist/src/sidecar.build.lock.contract.test.js +201 -0
  179. package/dist/src/sidecar.freshness.contract.test.js +184 -0
  180. package/dist/src/sidecar.replacement.boundary.contract.test.js +6 -3
  181. package/dist/src/sidecar.supply.readiness.contract.test.js +11 -0
  182. package/dist/src/simulation.worker.offline.test.js +104 -0
  183. package/dist/src/storage.provider.capacitor.content.contract.test.js +4 -1
  184. package/dist/src/storage.provider.contract.test.js +152 -1
  185. package/dist/src/tauri.frontend.build.contract.test.js +8 -0
  186. package/dist/src/tauri.window.evidence.execution.test.js +68 -0
  187. package/dist/src/utils/RuntimePaths.js +6 -0
  188. package/dist/src/utils/RuntimePaths.test.js +27 -0
  189. package/dist/src/windows.installer.build.test.js +109 -0
  190. package/dist/src/windows.installer.payload.test.js +76 -0
  191. package/dist/src/windows.installer.runtime.test.js +69 -0
  192. package/dist/src/workspace/WorkspaceRegistry.js +13 -0
  193. package/package.json +32 -10
  194. package/dist/src/routes/data.js +0 -267
  195. package/dist/src/routes/diagnostics.js +0 -51
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const fs = __importStar(require("fs"));
37
+ const os = __importStar(require("os"));
38
+ const path = __importStar(require("path"));
39
+ const child_process_1 = require("child_process");
40
+ const { sha256File } = require('../scripts/sidecar-build-fingerprint');
41
+ const describeLinux = process.platform === 'linux' ? describe : describe.skip;
42
+ describeLinux('AppImage pkg sidecar patchelf boundary', () => {
43
+ let directory;
44
+ let server;
45
+ let transcript;
46
+ let buildEnvironment;
47
+ const adapter = path.resolve(__dirname, '../scripts/appimage-patchelf.js');
48
+ beforeEach(() => {
49
+ directory = fs.mkdtempSync(path.join(os.tmpdir(), 'appimage patchelf '));
50
+ server = path.join(directory, 'NoteConnection.AppDir/usr/bin/server');
51
+ transcript = path.join(directory, 'patchelf-calls.jsonl');
52
+ fs.mkdirSync(path.dirname(server), { recursive: true });
53
+ fs.writeFileSync(server, Buffer.from('\x7fELF embedded offsets\0appended pkg payload'));
54
+ const originalPatchelf = path.join(directory, 'actual-patchelf');
55
+ fs.writeFileSync(originalPatchelf, `#!${process.execPath}\n
56
+ const fs = require('fs');
57
+ const args = process.argv.slice(2);
58
+ fs.appendFileSync(process.env.APPIMAGE_PATCHELF_TEST_LOG, JSON.stringify(args) + '\\n');
59
+ if (args[0] === '--set-rpath') fs.appendFileSync(args[2], ' ELF rewrite');
60
+ process.stdout.write('original stdout\\n');
61
+ process.stderr.write('original stderr\\n');
62
+ process.exit(args[0] === '--fail' ? 23 : 0);
63
+ `, { mode: 0o755 });
64
+ buildEnvironment = {
65
+ ...process.env,
66
+ NOTE_CONNECTION_APPIMAGE_PATCHELF: originalPatchelf,
67
+ NOTE_CONNECTION_APPIMAGE_SERVER_SUFFIX: 'NoteConnection.AppDir/usr/bin/server',
68
+ NOTE_CONNECTION_APPIMAGE_SERVER_SHA256: sha256File(server),
69
+ APPIMAGE_PATCHELF_TEST_LOG: transcript,
70
+ };
71
+ });
72
+ afterEach(() => fs.rmSync(directory, { recursive: true, force: true }));
73
+ test('preserves the complete pkg sidecar through repeated RPATH writes', () => {
74
+ for (let scan = 0; scan < 2; scan += 1) {
75
+ const command = (0, child_process_1.spawnSync)(adapter, ['--set-rpath', '$ORIGIN/../lib', server], { env: buildEnvironment, encoding: 'utf8' });
76
+ expect(command.status).toBe(0);
77
+ expect(command.stderr).toBe('');
78
+ }
79
+ expect(sha256File(server)).toBe(buildEnvironment.NOTE_CONNECTION_APPIMAGE_SERVER_SHA256);
80
+ expect(fs.existsSync(transcript)).toBe(false);
81
+ });
82
+ test('forwards read queries and preserves stdout, stderr, and argument boundaries', () => {
83
+ const args = ['--print-rpath', server];
84
+ const command = (0, child_process_1.spawnSync)(adapter, args, { env: buildEnvironment, encoding: 'utf8' });
85
+ expect(command.status).toBe(0);
86
+ expect(command.stdout).toBe('original stdout\n');
87
+ expect(command.stderr).toBe('original stderr\n');
88
+ expect(JSON.parse(fs.readFileSync(transcript, 'utf8'))).toEqual(args);
89
+ expect(sha256File(server)).toBe(buildEnvironment.NOTE_CONNECTION_APPIMAGE_SERVER_SHA256);
90
+ });
91
+ test.each(['other.AppDir/usr/bin/server', 'usr/bin/server', 'NoteConnection.AppDir/usr/bin/npm', 'NoteConnection.AppDir/usr/lib/libexample.so'])('forwards RPATH writes for %s', (relative) => {
92
+ const executable = path.join(directory, relative);
93
+ fs.mkdirSync(path.dirname(executable), { recursive: true });
94
+ fs.writeFileSync(executable, 'other ELF');
95
+ const args = ['--set-rpath', '$ORIGIN/path with spaces', executable];
96
+ const command = (0, child_process_1.spawnSync)(adapter, args, { env: buildEnvironment, encoding: 'utf8' });
97
+ expect(command.status).toBe(0);
98
+ expect(JSON.parse(fs.readFileSync(transcript, 'utf8'))).toEqual(args);
99
+ expect(fs.readFileSync(executable, 'utf8')).toBe('other ELF ELF rewrite');
100
+ });
101
+ test('rejects an already changed packaged server without rewriting it again', () => {
102
+ fs.appendFileSync(server, 'changed');
103
+ const changedSha256 = sha256File(server);
104
+ const command = (0, child_process_1.spawnSync)(adapter, ['--set-rpath', '$ORIGIN/../lib', server], { env: buildEnvironment, encoding: 'utf8' });
105
+ expect(command.status).toBe(1);
106
+ expect(command.stderr).toMatch(/differs from the original pkg sidecar/);
107
+ expect(sha256File(server)).toBe(changedSha256);
108
+ expect(fs.existsSync(transcript)).toBe(false);
109
+ });
110
+ test('preserves a delegated failure exit status', () => {
111
+ expect((0, child_process_1.spawnSync)(adapter, ['--fail'], { env: buildEnvironment }).status).toBe(23);
112
+ });
113
+ });
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const fs = __importStar(require("fs"));
37
+ const os = __importStar(require("os"));
38
+ const path = __importStar(require("path"));
39
+ const { verifyAppDir, verifySquashfsPermissions } = require('../scripts/verify-appimage');
40
+ const { sha256File } = require('../scripts/sidecar-build-fingerprint');
41
+ describe('stored AppImage permissions', () => {
42
+ test('accepts portable files, directories, and symlinks regardless of stored owner', () => {
43
+ expect(() => verifySquashfsPermissions([
44
+ 'drwxr-xr-x 0/0 64 2026-10-03 00:00 squashfs-root',
45
+ '-rwxr-xr-x 0/0 64 2026-10-03 00:00 squashfs-root/AppRun.wrapped',
46
+ '-rw-r--r-- 1001/1001 64 2026-10-03 00:00 squashfs-root/NoteConnection.png',
47
+ 'lrwxrwxrwx 0/0 18 2026-10-03 00:00 squashfs-root/.DirIcon -> NoteConnection.png',
48
+ ].join('\n'))).not.toThrow();
49
+ });
50
+ test.each(['-rwxrwx---', '-rwxr--r--', '-rw-rw----', 'drwx------', 'drwxr--r--'])('rejects stored mode %s even when the build owner could use it', (mode) => {
51
+ expect(() => verifySquashfsPermissions(`${mode} 0/0 64 2026-10-03 00:00 squashfs-root/entry`))
52
+ .toThrow(/inaccessible to other users/);
53
+ });
54
+ test('rejects missing manifest output', () => {
55
+ expect(() => verifySquashfsPermissions('')).toThrow(/contains no file modes/);
56
+ });
57
+ });
58
+ const describeLinux = process.platform === 'linux' ? describe : describe.skip;
59
+ describeLinux('AppImage desktop integration portability', () => {
60
+ let directory;
61
+ let appDir;
62
+ let sourceServer;
63
+ const desktop = '[Desktop Entry]\nName=NoteConnection\nType=Application\nExec=npm %U\nIcon=npm\nTerminal=false\n';
64
+ beforeEach(() => {
65
+ directory = fs.mkdtempSync(path.join(os.tmpdir(), 'appimage portability '));
66
+ appDir = path.join(directory, 'NoteConnection.AppDir');
67
+ fs.mkdirSync(path.join(appDir, 'usr/bin'), { recursive: true });
68
+ fs.mkdirSync(path.join(appDir, 'usr/share/applications'), { recursive: true });
69
+ fs.mkdirSync(path.join(appDir, 'usr/share/icons/hicolor/128x128/apps'), { recursive: true });
70
+ fs.writeFileSync(path.join(appDir, 'AppRun'), '#!/bin/sh\nexec "$APPDIR/AppRun.wrapped" "$@"\n', { mode: 0o755 });
71
+ fs.writeFileSync(path.join(appDir, 'AppRun.wrapped'), '#!/bin/sh\nexit 0\n', { mode: 0o755 });
72
+ fs.writeFileSync(path.join(appDir, 'usr/bin/npm'), '#!/bin/sh\nexit 0\n', { mode: 0o755 });
73
+ sourceServer = path.join(directory, 'original-server');
74
+ fs.writeFileSync(sourceServer, Buffer.from('\x7fELF embedded offsets\0pkg payload'), { mode: 0o755 });
75
+ fs.copyFileSync(sourceServer, path.join(appDir, 'usr/bin/server'));
76
+ fs.writeFileSync(path.join(appDir, 'usr/share/applications/NoteConnection.desktop'), desktop);
77
+ fs.writeFileSync(path.join(appDir, 'NoteConnection.png'), 'icon fixture');
78
+ fs.symlinkSync('usr/share/applications/NoteConnection.desktop', path.join(appDir, 'NoteConnection.desktop'));
79
+ fs.symlinkSync('NoteConnection.png', path.join(appDir, '.DirIcon'));
80
+ fs.symlinkSync('../../../../../../NoteConnection.png', path.join(appDir, 'usr/share/icons/hicolor/128x128/apps/npm.png'));
81
+ });
82
+ afterEach(() => fs.rmSync(directory, { recursive: true, force: true }));
83
+ test('accepts relative integration links after moving the complete image', () => {
84
+ const relocated = path.join(directory, 'relocated image');
85
+ fs.renameSync(appDir, relocated);
86
+ expect(verifyAppDir(relocated, sourceServer)).toEqual({
87
+ desktopPath: path.join(relocated, 'usr/share/applications/NoteConnection.desktop'),
88
+ iconPath: path.join(relocated, 'NoteConnection.png'),
89
+ executablePath: path.join(relocated, 'usr/bin/npm'),
90
+ serverSha256: sha256File(sourceServer),
91
+ name: 'NoteConnection',
92
+ });
93
+ });
94
+ test('rejects the original absolute .DirIcon link even while its build-host target exists', () => {
95
+ fs.unlinkSync(path.join(appDir, '.DirIcon'));
96
+ fs.symlinkSync(path.join(appDir, 'NoteConnection.png'), path.join(appDir, '.DirIcon'));
97
+ expect(fs.readFileSync(path.join(appDir, '.DirIcon'), 'utf8')).toBe('icon fixture');
98
+ expect(() => verifyAppDir(appDir, sourceServer)).toThrow(/\.DirIcon: absolute symlink/);
99
+ });
100
+ test.each(['.DirIcon', 'NoteConnection.desktop', 'AppRun', 'AppRun.wrapped', 'usr/bin/npm', 'usr/bin/server'])('rejects dangling integration entry %s', (entry) => {
101
+ fs.unlinkSync(path.join(appDir, entry));
102
+ fs.symlinkSync('missing-file', path.join(appDir, entry));
103
+ expect(() => verifyAppDir(appDir, sourceServer)).toThrow(/ENOENT/);
104
+ });
105
+ test('rejects a relative link outside the image even when the target exists', () => {
106
+ fs.writeFileSync(path.join(directory, 'outside.png'), 'external icon');
107
+ fs.unlinkSync(path.join(appDir, '.DirIcon'));
108
+ fs.symlinkSync('../outside.png', path.join(appDir, '.DirIcon'));
109
+ expect(() => verifyAppDir(appDir, sourceServer)).toThrow(/path leaves the AppImage/);
110
+ });
111
+ test('rejects an absolute link hidden in a relative icon chain', () => {
112
+ fs.unlinkSync(path.join(appDir, 'NoteConnection.png'));
113
+ fs.writeFileSync(path.join(directory, 'outside.png'), 'external icon');
114
+ fs.symlinkSync(path.join(directory, 'outside.png'), path.join(appDir, 'NoteConnection.png'));
115
+ expect(() => verifyAppDir(appDir, sourceServer)).toThrow(/absolute symlink/);
116
+ });
117
+ test('rejects a symlinked ancestor directory leading outside the image', () => {
118
+ fs.renameSync(path.join(appDir, 'usr'), path.join(directory, 'usr'));
119
+ fs.symlinkSync('../usr', path.join(appDir, 'usr'));
120
+ expect(() => verifyAppDir(appDir, sourceServer)).toThrow(/path leaves the AppImage/);
121
+ });
122
+ test('rejects a symlink cycle', () => {
123
+ fs.unlinkSync(path.join(appDir, '.DirIcon'));
124
+ fs.symlinkSync('.DirIcon', path.join(appDir, '.DirIcon'));
125
+ expect(() => verifyAppDir(appDir, sourceServer)).toThrow(/symlink loop/);
126
+ });
127
+ test.each(['AppRun', 'AppRun.wrapped', 'usr/bin/npm'])('rejects %s when only the build owner and group can execute it', (entry) => {
128
+ fs.chmodSync(path.join(appDir, entry), 0o770);
129
+ expect(() => verifyAppDir(appDir, sourceServer)).toThrow(/readable by other users|executable by other users/);
130
+ });
131
+ test('rejects an unreadable icon', () => {
132
+ fs.chmodSync(path.join(appDir, 'NoteConnection.png'), 0o600);
133
+ expect(() => verifyAppDir(appDir, sourceServer)).toThrow(/readable by other users/);
134
+ });
135
+ test('rejects a sidecar whose appended payload moved during packaging', () => {
136
+ const bytes = fs.readFileSync(sourceServer);
137
+ fs.writeFileSync(path.join(appDir, 'usr/bin/server'), Buffer.concat([bytes.subarray(0, 21), Buffer.alloc(4096), bytes.subarray(21)]));
138
+ expect(() => verifyAppDir(appDir, sourceServer)).toThrow(/Packaged server differs/);
139
+ });
140
+ test('rejects same-size sidecar corruption', () => {
141
+ const bytes = fs.readFileSync(sourceServer);
142
+ bytes[bytes.length - 1] ^= 1;
143
+ fs.writeFileSync(path.join(appDir, 'usr/bin/server'), bytes);
144
+ expect(() => verifyAppDir(appDir, sourceServer)).toThrow(/Packaged server differs/);
145
+ });
146
+ test.each([
147
+ ['Name=NoteConnection\n', '', /missing Name/],
148
+ ['Type=Application', 'Type=Link', /Type must be Application/],
149
+ ['Exec=npm %U', 'Exec=missing', /ENOENT/],
150
+ ['Exec=npm %U', 'Exec=/usr/bin/npm', /must name a bundled executable/],
151
+ ['Icon=npm', 'Icon=missing', /no bundled theme image/],
152
+ ])('rejects invalid desktop metadata (%s)', (before, after, message) => {
153
+ fs.writeFileSync(path.join(appDir, 'usr/share/applications/NoteConnection.desktop'), desktop.replace(before, after));
154
+ expect(() => verifyAppDir(appDir, sourceServer)).toThrow(message);
155
+ });
156
+ });
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.FileLoader = void 0;
37
37
  const fs = __importStar(require("fs"));
38
38
  const path = __importStar(require("path"));
39
+ const ResourceIdentity_1 = require("./ResourceIdentity");
39
40
  /**
40
41
  * Service to load files from a directory.
41
42
  * 从目录加载文件的服务。
@@ -47,13 +48,19 @@ class FileLoader {
47
48
  * @param dirPath Directory path | 目录路径
48
49
  * @param extensions File extensions to include (e.g., ['.md']) | 要包含的文件扩展名
49
50
  */
50
- static async loadFiles(dirPath, extensions = ['.md']) {
51
+ static async loadFiles(dirPath, extensions = ['.md'], workspaceRoot = dirPath) {
51
52
  const filePaths = [];
52
- // Check if directory exists
53
53
  if (!fs.existsSync(dirPath)) {
54
54
  console.warn(`Directory not found: ${dirPath}`);
55
55
  return [];
56
56
  }
57
+ // Compare canonical directories before scanning: an alias must neither
58
+ // escape the workspace nor give the same source a second relative identity.
59
+ const identityRoot = fs.realpathSync.native(workspaceRoot);
60
+ const scanRoot = fs.realpathSync.native(dirPath);
61
+ if (scanRoot !== identityRoot) {
62
+ (0, ResourceIdentity_1.normalizeResourceRelativePath)(identityRoot, scanRoot);
63
+ }
57
64
  // 1. Gather all file paths first (Sequential directory scan is safer for handles)
58
65
  async function scanDir(currentPath) {
59
66
  // Use withFileTypes to avoid extra stat calls
@@ -76,7 +83,7 @@ class FileLoader {
76
83
  console.warn(`Failed to read directory: ${currentPath}`, e);
77
84
  }
78
85
  }
79
- await scanDir(dirPath);
86
+ await scanDir(scanRoot);
80
87
  // 2. Read files with concurrency limit
81
88
  const results = [];
82
89
  const CONCURRENCY_LIMIT = 100; // Conservative limit
@@ -87,10 +94,17 @@ class FileLoader {
87
94
  try {
88
95
  const content = await fs.promises.readFile(fullPath, 'utf-8');
89
96
  const filename = path.basename(fullPath, path.extname(fullPath));
97
+ const relativePath = (0, ResourceIdentity_1.normalizeResourceRelativePath)(identityRoot, fullPath);
98
+ const identity = (0, ResourceIdentity_1.createResourceIdentity)(relativePath, filename, content);
90
99
  return {
91
100
  filepath: fullPath,
92
101
  filename: filename,
93
- content: content
102
+ content: content,
103
+ relativePath,
104
+ sourceUri: identity.sourceUri,
105
+ canonicalId: identity.canonicalId,
106
+ revision: identity.revision,
107
+ identityAliases: identity.identityAliases,
94
108
  };
95
109
  }
96
110
  catch (e) {
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const fs = __importStar(require("fs"));
37
+ const os = __importStar(require("os"));
38
+ const path = __importStar(require("path"));
39
+ const FileLoader_1 = require("./FileLoader");
40
+ describe('FileLoader resource paths', () => {
41
+ test('records a normalized path relative to the loaded workspace', async () => {
42
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'noteconnection-file-loader-'));
43
+ const nested = path.join(root, 'algebra');
44
+ fs.mkdirSync(nested, { recursive: true });
45
+ fs.writeFileSync(path.join(nested, 'index.md'), '# Algebra', 'utf8');
46
+ try {
47
+ const files = await FileLoader_1.FileLoader.loadFiles(root);
48
+ expect(files).toHaveLength(1);
49
+ expect(files[0].relativePath).toBe('algebra/index.md');
50
+ expect(files[0].sourceUri).toBe('note://workspace/v1/algebra/index.md');
51
+ expect(files[0].canonicalId).toBe('algebra/index');
52
+ expect(files[0].revision).toMatch(/^sha256:[0-9a-f]{64}$/);
53
+ expect(files[0].identityAliases).toEqual(expect.arrayContaining([
54
+ 'index',
55
+ 'algebra/index.md',
56
+ ]));
57
+ }
58
+ finally {
59
+ fs.rmSync(root, { recursive: true, force: true });
60
+ }
61
+ });
62
+ test('keeps identity stable when loading a workspace subdirectory', async () => {
63
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'noteconnection-file-loader-root-'));
64
+ const nested = path.join(root, 'algebra');
65
+ fs.mkdirSync(nested, { recursive: true });
66
+ fs.writeFileSync(path.join(nested, 'index.md'), '# Algebra', 'utf8');
67
+ try {
68
+ const files = await FileLoader_1.FileLoader.loadFiles(nested, ['.md'], root);
69
+ expect(files).toHaveLength(1);
70
+ expect(files[0].relativePath).toBe('algebra/index.md');
71
+ expect(files[0].sourceUri).toBe('note://workspace/v1/algebra/index.md');
72
+ expect(files[0].canonicalId).toBe('algebra/index');
73
+ }
74
+ finally {
75
+ fs.rmSync(root, { recursive: true, force: true });
76
+ }
77
+ });
78
+ test('rejects an outside scan directory instead of returning an empty successful load', async () => {
79
+ const fixtureRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'noteconnection-file-loader-boundary-'));
80
+ const workspaceRoot = path.join(fixtureRoot, 'workspace');
81
+ const outside = path.join(fixtureRoot, 'outside');
82
+ fs.mkdirSync(workspaceRoot);
83
+ fs.mkdirSync(outside);
84
+ fs.writeFileSync(path.join(outside, 'private.md'), '# Outside');
85
+ try {
86
+ await expect(FileLoader_1.FileLoader.loadFiles(outside, ['.md'], workspaceRoot)).rejects.toThrow(/outside workspace root/i);
87
+ }
88
+ finally {
89
+ fs.rmSync(fixtureRoot, { recursive: true, force: true });
90
+ }
91
+ });
92
+ test('rejects a scan directory that escapes the workspace through a symlink', async () => {
93
+ const fixtureRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'noteconnection-file-loader-symlink-'));
94
+ const workspaceRoot = path.join(fixtureRoot, 'workspace');
95
+ const outside = path.join(fixtureRoot, 'outside');
96
+ fs.mkdirSync(workspaceRoot);
97
+ fs.mkdirSync(outside);
98
+ fs.writeFileSync(path.join(outside, 'private.md'), '# Outside');
99
+ const link = path.join(workspaceRoot, 'linked');
100
+ fs.symlinkSync(outside, link, process.platform === 'win32' ? 'junction' : 'dir');
101
+ try {
102
+ await expect(FileLoader_1.FileLoader.loadFiles(link, ['.md'], workspaceRoot)).rejects.toThrow(/outside workspace root/i);
103
+ }
104
+ finally {
105
+ fs.rmSync(fixtureRoot, { recursive: true, force: true });
106
+ }
107
+ });
108
+ test('keeps canonical identities stable through an in-workspace directory alias', async () => {
109
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'noteconnection-file-loader-alias-'));
110
+ const nested = path.join(root, 'algebra');
111
+ fs.mkdirSync(nested);
112
+ fs.writeFileSync(path.join(nested, 'index.md'), '# Algebra');
113
+ const link = path.join(root, 'alias');
114
+ fs.symlinkSync(nested, link, process.platform === 'win32' ? 'junction' : 'dir');
115
+ try {
116
+ const files = await FileLoader_1.FileLoader.loadFiles(link, ['.md'], root);
117
+ expect(files).toHaveLength(1);
118
+ expect(files[0].relativePath).toBe('algebra/index.md');
119
+ expect(files[0].sourceUri).toBe('note://workspace/v1/algebra/index.md');
120
+ }
121
+ finally {
122
+ fs.rmSync(root, { recursive: true, force: true });
123
+ }
124
+ });
125
+ });
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const GraphBuilder_1 = require("./GraphBuilder");
4
+ const config_1 = require("./config");
5
+ describe('GraphBuilder identity compatibility', () => {
6
+ const originalConfig = {
7
+ ...config_1.config,
8
+ exclusionList: [...config_1.config.exclusionList],
9
+ };
10
+ beforeEach(() => {
11
+ config_1.config.enableTags = false;
12
+ config_1.config.enableStatisticalInference = false;
13
+ config_1.config.enableVectorSimilarity = false;
14
+ config_1.config.enableHybridInference = false;
15
+ config_1.config.enableGPU = false;
16
+ config_1.config.enableGPULayout = false;
17
+ config_1.config.clusteringStrategy = 'folder';
18
+ config_1.config.exclusionList = [];
19
+ });
20
+ afterEach(() => {
21
+ Object.assign(config_1.config, originalConfig);
22
+ });
23
+ test('preserves identity metadata, reads URI frontmatter, and accepts URI layouts', async () => {
24
+ const files = [
25
+ {
26
+ filepath: 'workspace/algebra/a.md',
27
+ filename: 'A',
28
+ content: '# A',
29
+ relativePath: 'algebra/a.md',
30
+ sourceUri: 'note://workspace/v1/algebra/a.md',
31
+ canonicalId: 'algebra/a',
32
+ revision: 'sha256:a',
33
+ identityAliases: ['A', 'algebra/a.md'],
34
+ },
35
+ {
36
+ filepath: 'workspace/algebra/b.md',
37
+ filename: 'B',
38
+ content: '---\nprerequisites:\n - note://workspace/v1/algebra/a.md\n---\n# B',
39
+ relativePath: 'algebra/b.md',
40
+ sourceUri: 'note://workspace/v1/algebra/b.md',
41
+ revision: 'sha256:b',
42
+ identityAliases: ['B', 'algebra/b.md'],
43
+ },
44
+ ];
45
+ const graph = await GraphBuilder_1.GraphBuilder.build(files, new Map([
46
+ ['note://workspace/v1/algebra/a.md', { x: 12, y: 34 }],
47
+ ]));
48
+ expect(graph.getNode('A')).toEqual(expect.objectContaining({
49
+ sourceUri: 'note://workspace/v1/algebra/a.md',
50
+ canonicalId: 'algebra/a',
51
+ revision: 'sha256:a',
52
+ identityAliases: expect.arrayContaining(['algebra/a.md']),
53
+ x: 12,
54
+ y: 34,
55
+ }));
56
+ expect(graph.getIncomingEdges('B')).toEqual(expect.arrayContaining([
57
+ expect.objectContaining({ source: 'A', target: 'B', type: 'explicit-prerequisite' }),
58
+ ]));
59
+ });
60
+ test('uses indexed keyword candidates without changing exact-match semantics', async () => {
61
+ const files = [
62
+ {
63
+ filepath: 'workspace/alpha.md',
64
+ filename: 'Alpha',
65
+ content: 'Alpha is a prerequisite concept.',
66
+ },
67
+ {
68
+ filepath: 'workspace/context.md',
69
+ filename: 'Context',
70
+ content: 'This context mentions Alpha as an explicit phrase.',
71
+ },
72
+ {
73
+ filepath: 'workspace/beta.md',
74
+ filename: 'Beta',
75
+ content: 'This context mentions alphabet but not the standalone concept.',
76
+ },
77
+ ];
78
+ const graph = await GraphBuilder_1.GraphBuilder.build(files);
79
+ expect(graph.getOutgoingEdges('Alpha')).toEqual(expect.arrayContaining([
80
+ expect.objectContaining({ target: 'Context', type: 'keyword-match' }),
81
+ ]));
82
+ expect(graph.getOutgoingEdges('Alpha')).not.toEqual(expect.arrayContaining([
83
+ expect.objectContaining({ target: 'Beta', type: 'keyword-match' }),
84
+ ]));
85
+ });
86
+ });