arcane-os 0.1.0-dev.5

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 (248) hide show
  1. package/CHANGELOG.md +154 -0
  2. package/COMMERCIAL-LICENSE.md +13 -0
  3. package/LICENSE +661 -0
  4. package/NOTICE +70 -0
  5. package/README.md +448 -0
  6. package/bin/arcane-test.mjs +741 -0
  7. package/bin/arcane.mjs +5 -0
  8. package/docs/architecture.md +234 -0
  9. package/docs/compatibility.md +36 -0
  10. package/docs/event-manager.md +166 -0
  11. package/docs/platform-targets.md +108 -0
  12. package/docs/publishing.md +203 -0
  13. package/docs/reference/README.md +117 -0
  14. package/docs/reference/arcane-ollama.md +288 -0
  15. package/docs/reference/availability-and-normalization.md +123 -0
  16. package/docs/reference/behavioral-testing.md +86 -0
  17. package/docs/reference/cli.md +569 -0
  18. package/docs/reference/core/README.md +62 -0
  19. package/docs/reference/core/arcane-ai-contracts.md +873 -0
  20. package/docs/reference/core/arcane-api.md +601 -0
  21. package/docs/reference/core/arcane-entities.md +65 -0
  22. package/docs/reference/core/arcane-events.md +134 -0
  23. package/docs/reference/core/ollama-module.md +181 -0
  24. package/docs/reference/core/reference/arcane-api/ai-and-ollama.md +1909 -0
  25. package/docs/reference/core/reference/arcane-api/applications-terminal-capabilities.md +1057 -0
  26. package/docs/reference/core/reference/arcane-api/core-and-events.md +320 -0
  27. package/docs/reference/core/reference/arcane-api/filesystem-storage-preferences-appearance.md +610 -0
  28. package/docs/reference/core/reference/arcane-api/namespaces.md +1157 -0
  29. package/docs/reference/core/reference/arcane-api/platform-installation-users-system.md +1423 -0
  30. package/docs/reference/core/reference/arcane-api/session-provisioning-diagnostics-development.md +315 -0
  31. package/docs/reference/event-manager.md +957 -0
  32. package/docs/reference/inventory/package-api.json +2632 -0
  33. package/docs/reference/inventory/runtime-components.json +934 -0
  34. package/docs/reference/inventory/runtime-entities.json +26 -0
  35. package/docs/reference/inventory/runtime-modules.json +1249 -0
  36. package/docs/reference/protocols.md +242 -0
  37. package/docs/reference/runtime-components.md +1098 -0
  38. package/docs/reference/runtime-entities.md +303 -0
  39. package/docs/reference/runtime-modules.md +2010 -0
  40. package/docs/reference/sdk-api.md +4901 -0
  41. package/docs/roadmap.md +79 -0
  42. package/docs/work-amplification.md +124 -0
  43. package/node_modules/event-pubsub/CHANGELOG.md +55 -0
  44. package/node_modules/event-pubsub/MIGRATION.md +70 -0
  45. package/node_modules/event-pubsub/README.md +363 -0
  46. package/node_modules/event-pubsub/SECURITY.md +37 -0
  47. package/node_modules/event-pubsub/index.js +141 -0
  48. package/node_modules/event-pubsub/licence +21 -0
  49. package/node_modules/event-pubsub/package.json +59 -0
  50. package/node_modules/strong-type/README.md +408 -0
  51. package/node_modules/strong-type/assets/strong-type-header.png +0 -0
  52. package/node_modules/strong-type/index.js +1151 -0
  53. package/node_modules/strong-type/licence +21 -0
  54. package/node_modules/strong-type/node.js +125 -0
  55. package/node_modules/strong-type/package.json +61 -0
  56. package/package.json +95 -0
  57. package/runtime/ARCANE_RUNTIME_RELEASE.json +791 -0
  58. package/runtime/arcane/components/app-bar.html +468 -0
  59. package/runtime/arcane/components/assistant-panel.html +715 -0
  60. package/runtime/arcane/components/calculator.html +8 -0
  61. package/runtime/arcane/components/chart.html +655 -0
  62. package/runtime/arcane/components/chat.html +1225 -0
  63. package/runtime/arcane/components/conversation-view.html +13 -0
  64. package/runtime/arcane/components/dashboard-config.html +341 -0
  65. package/runtime/arcane/components/data-maintenance.html +112 -0
  66. package/runtime/arcane/components/data-view.html +92 -0
  67. package/runtime/arcane/components/directory-picker.html +197 -0
  68. package/runtime/arcane/components/document-inspector.html +252 -0
  69. package/runtime/arcane/components/file-drop.html +264 -0
  70. package/runtime/arcane/components/file-inspector.html +293 -0
  71. package/runtime/arcane/components/file-manager.html +1715 -0
  72. package/runtime/arcane/components/header.html +142 -0
  73. package/runtime/arcane/components/integration-settings.html +14 -0
  74. package/runtime/arcane/components/local-ai-status.html +360 -0
  75. package/runtime/arcane/components/markdown-document.html +1048 -0
  76. package/runtime/arcane/components/markdown-editor.html +360 -0
  77. package/runtime/arcane/components/media-embed.html +8 -0
  78. package/runtime/arcane/components/modal.html +402 -0
  79. package/runtime/arcane/components/output-panel.html +259 -0
  80. package/runtime/arcane/components/preferences-form.html +135 -0
  81. package/runtime/arcane/components/record-timeline.html +105 -0
  82. package/runtime/arcane/components/relationship-board.html +116 -0
  83. package/runtime/arcane/components/screen-capture.html +8 -0
  84. package/runtime/arcane/components/source-code-viewer.html +441 -0
  85. package/runtime/arcane/components/source-explanation.html +124 -0
  86. package/runtime/arcane/components/speech.html +365 -0
  87. package/runtime/arcane/components/summary-strip.html +177 -0
  88. package/runtime/arcane/components/table.html +77 -0
  89. package/runtime/arcane/components/task-progress.html +282 -0
  90. package/runtime/arcane/components/terminal-workspace.html +65 -0
  91. package/runtime/arcane/components/theme-editor.html +41 -0
  92. package/runtime/arcane/components/theme-switcher.html +46 -0
  93. package/runtime/arcane/components/unified-inbox.html +20 -0
  94. package/runtime/arcane/components/voice-transcription.html +476 -0
  95. package/runtime/arcane/components/weather-widget.html +8 -0
  96. package/runtime/arcane/components/web-navigator.html +239 -0
  97. package/runtime/arcane/css/communications.css +1 -0
  98. package/runtime/arcane/css/dashboard-config.css +45 -0
  99. package/runtime/arcane/css/document-site.css +981 -0
  100. package/runtime/arcane/css/layout.css +438 -0
  101. package/runtime/arcane/css/primitives.css +321 -0
  102. package/runtime/arcane/css/theme.css +112 -0
  103. package/runtime/arcane/css/utility-workspace.css +1 -0
  104. package/runtime/arcane/entities/ApiModelRecord.js +20 -0
  105. package/runtime/arcane/entities/Calculation.js +13 -0
  106. package/runtime/arcane/entities/Chat.js +581 -0
  107. package/runtime/arcane/entities/CommunicationMessage.js +29 -0
  108. package/runtime/arcane/entities/CommunicationThread.js +21 -0
  109. package/runtime/arcane/entities/Document.js +10 -0
  110. package/runtime/arcane/entities/File.js +143 -0
  111. package/runtime/arcane/entities/Image.js +135 -0
  112. package/runtime/arcane/entities/IntentEnvelope.js +834 -0
  113. package/runtime/arcane/entities/Preference.js +83 -0
  114. package/runtime/arcane/entities/TWiNPolicyDecision.js +1092 -0
  115. package/runtime/arcane/entities/TerminalSession.js +46 -0
  116. package/runtime/arcane/entities/Theme.js +107 -0
  117. package/runtime/arcane/entities/User.js +1046 -0
  118. package/runtime/arcane/entities/Weather.js +23 -0
  119. package/runtime/arcane/img/arcane-os-everywhere.png +0 -0
  120. package/runtime/arcane/img/arrow-left.png +0 -0
  121. package/runtime/arcane/img/arrow-right.png +0 -0
  122. package/runtime/arcane/img/doc.svg +5 -0
  123. package/runtime/arcane/img/folder.svg +4 -0
  124. package/runtime/arcane/img/image.svg +5 -0
  125. package/runtime/arcane/img/refresh.png +0 -0
  126. package/runtime/arcane/img/send.svg +9 -0
  127. package/runtime/arcane/img/trash.svg +5 -0
  128. package/runtime/arcane/img/upload.svg +5 -0
  129. package/runtime/arcane/modules/AI.js +2048 -0
  130. package/runtime/arcane/modules/AIPreferenceRuntime.js +32 -0
  131. package/runtime/arcane/modules/AIPreferenceTuple.js +92 -0
  132. package/runtime/arcane/modules/AIResponseLength.js +42 -0
  133. package/runtime/arcane/modules/AIResponseURLPolicy.js +626 -0
  134. package/runtime/arcane/modules/AnsiText.js +53 -0
  135. package/runtime/arcane/modules/ApiModelDatabase.js +25 -0
  136. package/runtime/arcane/modules/AppDataScope.js +245 -0
  137. package/runtime/arcane/modules/AppearancePreferences.js +28 -0
  138. package/runtime/arcane/modules/ArcaneCommunicationBridge.js +22 -0
  139. package/runtime/arcane/modules/ArcaneNavigationPolicy.js +135 -0
  140. package/runtime/arcane/modules/ArcaneNetworkPolicy.js +255 -0
  141. package/runtime/arcane/modules/AsyncBoundary.js +161 -0
  142. package/runtime/arcane/modules/BrowserTestSuite.js +326 -0
  143. package/runtime/arcane/modules/CalculatorEngine.js +20 -0
  144. package/runtime/arcane/modules/CaseEvidenceIndexer.js +134 -0
  145. package/runtime/arcane/modules/ChartLibrary.js +35 -0
  146. package/runtime/arcane/modules/ChatRecords.js +13 -0
  147. package/runtime/arcane/modules/CommunicationAppController.js +43 -0
  148. package/runtime/arcane/modules/CommunicationHub.js +16 -0
  149. package/runtime/arcane/modules/CommunicationPreferences.js +13 -0
  150. package/runtime/arcane/modules/CommunicationProviderRegistry.js +16 -0
  151. package/runtime/arcane/modules/ComponentContracts.js +586 -0
  152. package/runtime/arcane/modules/ConfiguredAIChatSession.js +288 -0
  153. package/runtime/arcane/modules/ConversationActionItems.js +488 -0
  154. package/runtime/arcane/modules/ConversationClosingReport.js +274 -0
  155. package/runtime/arcane/modules/ConversationTimebox.js +527 -0
  156. package/runtime/arcane/modules/CoreLocalModelCatalog.js +255 -0
  157. package/runtime/arcane/modules/DBLS.js +171 -0
  158. package/runtime/arcane/modules/DBOPFS.js +1154 -0
  159. package/runtime/arcane/modules/DBOPFSWorker.js +116 -0
  160. package/runtime/arcane/modules/DataMaintenance.js +91 -0
  161. package/runtime/arcane/modules/DevelopmentWorkspace.js +74 -0
  162. package/runtime/arcane/modules/DirectoryPicker.js +109 -0
  163. package/runtime/arcane/modules/DocumentNavigation.js +223 -0
  164. package/runtime/arcane/modules/Errors.js +1025 -0
  165. package/runtime/arcane/modules/GifEncoder.js +29 -0
  166. package/runtime/arcane/modules/HTMLImport.js +114 -0
  167. package/runtime/arcane/modules/InMemoryCommunicationProvider.js +14 -0
  168. package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +275 -0
  169. package/runtime/arcane/modules/LocalAIReadiness.js +870 -0
  170. package/runtime/arcane/modules/LocalAIReadinessController.js +156 -0
  171. package/runtime/arcane/modules/MD.js +111 -0
  172. package/runtime/arcane/modules/Mail.js +352 -0
  173. package/runtime/arcane/modules/MailTransport.mjs +180 -0
  174. package/runtime/arcane/modules/Marked.min.js +71 -0
  175. package/runtime/arcane/modules/MemoryRecords.js +44 -0
  176. package/runtime/arcane/modules/MessageAdvisory.js +38 -0
  177. package/runtime/arcane/modules/ModelDefinition.js +189 -0
  178. package/runtime/arcane/modules/Ollama.js +74 -0
  179. package/runtime/arcane/modules/OllamaModelIdentifier.js +21 -0
  180. package/runtime/arcane/modules/OllamaSettings.js +24 -0
  181. package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +14 -0
  182. package/runtime/arcane/modules/PreferenceStore.js +109 -0
  183. package/runtime/arcane/modules/QRCode.min.js +1 -0
  184. package/runtime/arcane/modules/Questionnaire.js +61 -0
  185. package/runtime/arcane/modules/RecordLinkIndex.js +24 -0
  186. package/runtime/arcane/modules/RecordPassageIndex.js +222 -0
  187. package/runtime/arcane/modules/RecordReviewStore.js +94 -0
  188. package/runtime/arcane/modules/RevocableProjectionLedger.js +1623 -0
  189. package/runtime/arcane/modules/RiskSignalAnalyzer.js +37 -0
  190. package/runtime/arcane/modules/ScamRiskPolicy.js +62 -0
  191. package/runtime/arcane/modules/ScopedOPFSCache.js +183 -0
  192. package/runtime/arcane/modules/ScreenCapture.js +20 -0
  193. package/runtime/arcane/modules/SpeechPlayback.js +581 -0
  194. package/runtime/arcane/modules/StaticDocumentCatalog.js +1248 -0
  195. package/runtime/arcane/modules/SystemAppearance.js +20 -0
  196. package/runtime/arcane/modules/SystemPlatformPresentation.js +51 -0
  197. package/runtime/arcane/modules/SystemToolRegistry.js +28 -0
  198. package/runtime/arcane/modules/TerminalClient.js +52 -0
  199. package/runtime/arcane/modules/TerminalCommandRegistry.js +50 -0
  200. package/runtime/arcane/modules/ThemeBootstrap.js +26 -0
  201. package/runtime/arcane/modules/ThemeManager.js +131 -0
  202. package/runtime/arcane/modules/TimeGuard.js +149 -0
  203. package/runtime/arcane/modules/ToolCallRouter.js +83 -0
  204. package/runtime/arcane/modules/WaitForComponent.js +102 -0
  205. package/runtime/arcane/modules/YouTubeMedia.js +16 -0
  206. package/runtime/arcane/modules/uPlot.LICENSE.txt +21 -0
  207. package/runtime/arcane/modules/uPlot.iife.min.js +2 -0
  208. package/runtime/arcane/modules/uPlot.min.css +1 -0
  209. package/runtime/arcane/security/arcane-network-policy.json +6 -0
  210. package/runtime/strong-type/index.js +352 -0
  211. package/runtime/strong-type/licence +21 -0
  212. package/runtime/strong-type/package.json +45 -0
  213. package/schemas/arcane-app-bundle.schema.json +125 -0
  214. package/schemas/arcane-app.schema.json +329 -0
  215. package/schemas/arcane-lock.schema.json +86 -0
  216. package/schemas/arcane-package.schema.json +224 -0
  217. package/schemas/cli-event.schema.json +122 -0
  218. package/schemas/event-stack.schema.json +152 -0
  219. package/schemas/native-build-plan.schema.json +176 -0
  220. package/schemas/target-adapter.schema.json +117 -0
  221. package/src/app-descriptor.mjs +500 -0
  222. package/src/cli/main.mjs +561 -0
  223. package/src/constants.mjs +31 -0
  224. package/src/dev-server.mjs +718 -0
  225. package/src/doctor.mjs +315 -0
  226. package/src/dom-event-instrumentation.mjs +594 -0
  227. package/src/errors.mjs +75 -0
  228. package/src/event-manager.mjs +1342 -0
  229. package/src/event-queue.mjs +138 -0
  230. package/src/events.mjs +219 -0
  231. package/src/index.mjs +177 -0
  232. package/src/integrated-provider-loader.mjs +432 -0
  233. package/src/native-plan.mjs +698 -0
  234. package/src/native-provider-loader.mjs +1126 -0
  235. package/src/packager/core.mjs +2691 -0
  236. package/src/process.mjs +353 -0
  237. package/src/release-bundle.mjs +2523 -0
  238. package/src/repository.mjs +90 -0
  239. package/src/runtime.mjs +452 -0
  240. package/src/scaffold.mjs +380 -0
  241. package/src/targets/index.mjs +436 -0
  242. package/src/templates/assets/app-icon.png +0 -0
  243. package/src/templates/workspace-template.mjs +388 -0
  244. package/src/testing-loader.mjs +9 -0
  245. package/src/testing.mjs +427 -0
  246. package/src/toolchain.mjs +1335 -0
  247. package/src/update-check.mjs +307 -0
  248. package/src/workspace.mjs +449 -0
@@ -0,0 +1,741 @@
1
+ #!/usr/bin/env node
2
+ import {spawn} from 'node:child_process';
3
+ import {randomUUID} from 'node:crypto';
4
+ import {writeSync} from 'node:fs';
5
+ import {lstat,readdir,realpath} from 'node:fs/promises';
6
+ import {register} from 'node:module';
7
+ import path from 'node:path';
8
+ import {fileURLToPath,pathToFileURL} from 'node:url';
9
+ import test,{DEFAULT_TEST_TIMEOUT_MS,runRegisteredTests} from '../src/testing.mjs';
10
+
11
+ const SINGLE_FILE_ARGUMENT='--arcane-single-test-file';
12
+ const RUNNER_PATH=fileURLToPath(import.meta.url);
13
+ const TEST_FILE_PATTERN=/\.test\.(?:mjs|cjs|js)$/u;
14
+ const CANCELLATION_FORCE_WAIT_MS=1500;
15
+ const WATCHDOG_REPORT_GRACE_MS=500;
16
+ const FILE_HEARTBEAT_MS=30_000;
17
+ const OUTPUT_FLUSH_TIMEOUT_MS=5_000;
18
+ const BETWEEN_PHASE_TIMEOUT_MS=30_000;
19
+ const TREE_DRAIN_DIAGNOSTIC_BYTES=4_096;
20
+ const IPC_PROTOCOL=1;
21
+ const IPC_TOKEN_ENV='ARCANE_TEST_IPC_TOKEN';
22
+ const ISOLATED_MODE=process.argv[2]===SINGLE_FILE_ARGUMENT;
23
+ const ISOLATED_TOKEN=ISOLATED_MODE?process.env[IPC_TOKEN_ENV]:null;
24
+ const ISOLATED_SEND=ISOLATED_MODE&&typeof process.send==='function'
25
+ ?process.send.bind(process)
26
+ :null;
27
+ if(ISOLATED_MODE)delete process.env[IPC_TOKEN_ENV];
28
+
29
+ function comparePaths(left,right){
30
+ return left.localeCompare(right,'en');
31
+ }
32
+
33
+ function throwIfCancelled(signal){
34
+ if(signal?.aborted){
35
+ throw signal.reason??new Error('The test run was cancelled.');
36
+ }
37
+ }
38
+
39
+ async function collectDirectory(root,files,signal){
40
+ throwIfCancelled(signal);
41
+ const entries=await readdir(root,{withFileTypes:true});
42
+ throwIfCancelled(signal);
43
+ for(const entry of entries.sort((left,right)=>comparePaths(left.name,right.name))){
44
+ throwIfCancelled(signal);
45
+ const candidate=path.join(root,entry.name);
46
+ if(entry.isSymbolicLink()){
47
+ throw new Error(`Test discovery refuses symbolic links: ${candidate}`);
48
+ }
49
+ if(entry.isDirectory()){
50
+ await collectDirectory(candidate,files,signal);
51
+ }else if(entry.isFile()&&TEST_FILE_PATTERN.test(entry.name)){
52
+ files.add(await realpath(candidate));
53
+ }
54
+ }
55
+ }
56
+
57
+ async function collectTestFiles(arguments_,signal){
58
+ const requested=arguments_.length>0
59
+ ?arguments_.map(candidate=>path.resolve(candidate))
60
+ :[path.join(process.cwd(),'test')];
61
+ const files=new Set();
62
+ for(const candidate of requested.sort(comparePaths)){
63
+ throwIfCancelled(signal);
64
+ let info;
65
+ try{
66
+ info=await lstat(candidate);
67
+ }catch(error){
68
+ if(error?.code==='ENOENT'&&arguments_.length===0)continue;
69
+ throw error;
70
+ }
71
+ throwIfCancelled(signal);
72
+ if(info.isSymbolicLink()){
73
+ throw new Error(`Test paths must not be symbolic links: ${candidate}`);
74
+ }
75
+ if(info.isDirectory()){
76
+ await collectDirectory(candidate,files,signal);
77
+ }else if(info.isFile()&&TEST_FILE_PATTERN.test(path.basename(candidate))){
78
+ files.add(await realpath(candidate));
79
+ }else{
80
+ throw new Error(`Expected a .test.js, .test.cjs, or .test.mjs file: ${candidate}`);
81
+ }
82
+ }
83
+ return [...files].sort(comparePaths);
84
+ }
85
+
86
+ function formatError(error){
87
+ return error instanceof Error?(error.stack??`${error.name}: ${error.message}`):String(error);
88
+ }
89
+
90
+ function sendIsolatedMessage(message){
91
+ if(typeof ISOLATED_TOKEN!=='string'||ISOLATED_TOKEN===''||ISOLATED_SEND===null){
92
+ return Promise.reject(
93
+ new Error('Internal single-file mode requires its authenticated coordinator channel.')
94
+ );
95
+ }
96
+ return new Promise((resolve,reject)=>{
97
+ ISOLATED_SEND(
98
+ {...message,protocol:IPC_PROTOCOL,token:ISOLATED_TOKEN},
99
+ error=>error?reject(error):resolve()
100
+ );
101
+ });
102
+ }
103
+
104
+ function interceptIsolatedExit(){
105
+ process.exit=function interceptedTestExit(code){
106
+ const error=new Error(
107
+ `process.exit(${code===undefined?'':String(code)}) is not permitted inside an isolated test file.`
108
+ );
109
+ error.code='ARCANE_TEST_PREMATURE_EXIT';
110
+ throw error;
111
+ };
112
+ }
113
+
114
+ function displayFile(file){
115
+ const relative=path.relative(process.cwd(),file);
116
+ return relative===''?path.basename(file):relative;
117
+ }
118
+
119
+ function startHeartbeat(label){
120
+ const started=Date.now();
121
+ const heartbeat=setInterval(()=>{
122
+ const elapsedSeconds=Math.max(1,Math.floor((Date.now()-started)/1000));
123
+ console.log(`[arcane-test] HEARTBEAT ${label} (${String(elapsedSeconds)}s elapsed)`);
124
+ },FILE_HEARTBEAT_MS);
125
+ heartbeat.unref?.();
126
+ return ()=>clearInterval(heartbeat);
127
+ }
128
+
129
+ function childIsRunning(child){
130
+ return Boolean(child?.pid)&&child.exitCode===null&&child.signalCode===null;
131
+ }
132
+
133
+ function killDirectChild(child,force){
134
+ if(!childIsRunning(child))return;
135
+ try{
136
+ child.kill(force?'SIGKILL':'SIGTERM');
137
+ }catch{}
138
+ }
139
+
140
+ function parseTaskkillTargets(output,leaderPid){
141
+ const entries=[];
142
+ for(const line of output.split(/\r?\n/u).map(value=>value.trim()).filter(Boolean)){
143
+ if(/^Reason\s*:/iu.test(line)){
144
+ if(/\d/u.test(line))return null;
145
+ continue;
146
+ }
147
+ if(!/^(?:ERROR|SUCCESS)\s*:/iu.test(line))return null;
148
+ const matches=[...line.matchAll(/\bPID\s+(\d+)\b/giu)];
149
+ if(matches.length<1||matches.length>2)return null;
150
+ const target=Number(matches[0][1]);
151
+ const parent=matches.length===2?Number(matches[1][1]):null;
152
+ if(
153
+ !Number.isSafeInteger(target)||target<=0
154
+ ||(parent!==null&&(!Number.isSafeInteger(parent)||parent<=0))
155
+ ||/\d/u.test(line.replaceAll(/\bPID\s+\d+\b/giu,'PID'))
156
+ )return null;
157
+ entries.push({target,parent});
158
+ }
159
+ const targets=new Set(entries.map(entry=>entry.target));
160
+ if(entries.length===0||!targets.has(leaderPid))return null;
161
+ const parents=new Map();
162
+ for(const entry of entries){
163
+ if(parents.has(entry.target)&&parents.get(entry.target)!==entry.parent)return null;
164
+ parents.set(entry.target,entry.parent);
165
+ }
166
+ for(const target of targets){
167
+ const seen=new Set();
168
+ let current=target;
169
+ while(current!==leaderPid){
170
+ if(seen.has(current))return null;
171
+ seen.add(current);
172
+ const parent=parents.get(current);
173
+ if(parent===null||!targets.has(parent))return null;
174
+ current=parent;
175
+ }
176
+ }
177
+ return targets;
178
+ }
179
+
180
+ function windowsPidIsAbsent(pid){
181
+ try{
182
+ process.kill(pid,0);
183
+ return false;
184
+ }catch(error){
185
+ return error?.code==='ESRCH';
186
+ }
187
+ }
188
+
189
+ async function waitForWindowsPidsAbsent(pids,deadline){
190
+ while(performance.now()<deadline){
191
+ if([...pids].every(windowsPidIsAbsent))return true;
192
+ await new Promise(resolve=>setTimeout(resolve,20));
193
+ }
194
+ return [...pids].every(windowsPidIsAbsent);
195
+ }
196
+
197
+ function forceDrainWindowsTree(child){
198
+ if(!childIsRunning(child)){
199
+ console.error(
200
+ `[arcane-test] TREE DRAIN FAILED for PID ${String(child?.pid??'unknown')}: `+
201
+ 'the isolated leader closed before tree draining began.'
202
+ );
203
+ return Promise.resolve(false);
204
+ }
205
+ return new Promise(resolve=>{
206
+ let settled=false;
207
+ let killer;
208
+ let stage='taskkill';
209
+ let stdoutDiagnostic='';
210
+ let stderrDiagnostic='';
211
+ const deadline=performance.now()+CANCELLATION_FORCE_WAIT_MS;
212
+ const capture=(stream,chunk)=>{
213
+ const value=`${stream}${chunk.toString()}`;
214
+ return value.slice(-TREE_DRAIN_DIAGNOSTIC_BYTES);
215
+ };
216
+ const diagnostic=()=>[stdoutDiagnostic,stderrDiagnostic]
217
+ .map(value=>value.trim()).filter(Boolean).join('\n');
218
+ const reportFailure=reason=>{
219
+ const detail=diagnostic();
220
+ console.error(
221
+ `[arcane-test] TREE DRAIN FAILED for PID ${String(child.pid)}: ${reason}`+
222
+ `${detail?`\n${detail}`:''}`
223
+ );
224
+ };
225
+ const finish=proven=>{
226
+ if(settled)return;
227
+ settled=true;
228
+ clearTimeout(timeout);
229
+ killer?.removeAllListeners();
230
+ resolve(proven);
231
+ };
232
+ const timeout=setTimeout(()=>{
233
+ killDirectChild(child,true);
234
+ try{killer?.kill('SIGKILL');}catch{}
235
+ reportFailure(`${stage} exceeded ${String(CANCELLATION_FORCE_WAIT_MS)} ms.`);
236
+ finish(false);
237
+ },CANCELLATION_FORCE_WAIT_MS);
238
+ try{
239
+ killer=spawn(
240
+ 'taskkill.exe',
241
+ ['/PID',String(child.pid),'/T','/F'],
242
+ {shell:false,windowsHide:true,stdio:['ignore','pipe','pipe']}
243
+ );
244
+ killer.stdout?.on('data',chunk=>{
245
+ stdoutDiagnostic=capture(stdoutDiagnostic,chunk);
246
+ });
247
+ killer.stderr?.on('data',chunk=>{
248
+ stderrDiagnostic=capture(stderrDiagnostic,chunk);
249
+ });
250
+ }catch(error){
251
+ killDirectChild(child,true);
252
+ reportFailure(`taskkill could not start: ${formatError(error)}`);
253
+ finish(false);
254
+ return;
255
+ }
256
+ killer.once('error',error=>{
257
+ if(settled)return;
258
+ killDirectChild(child,true);
259
+ reportFailure(`taskkill failed: ${formatError(error)}`);
260
+ finish(false);
261
+ });
262
+ killer.once('close',code=>{
263
+ if(settled)return;
264
+ if(code===0){
265
+ finish(true);
266
+ return;
267
+ }
268
+ const targets=parseTaskkillTargets(diagnostic(),child.pid);
269
+ if(targets===null){
270
+ killDirectChild(child,true);
271
+ reportFailure(
272
+ `taskkill exited with code ${String(code)} and its PID report was not provable.`
273
+ );
274
+ finish(false);
275
+ return;
276
+ }
277
+ stage='taskkill PID absence proof';
278
+ void waitForWindowsPidsAbsent(targets,deadline).then(proven=>{
279
+ if(settled)return;
280
+ if(!proven){
281
+ killDirectChild(child,true);
282
+ reportFailure(
283
+ `taskkill exited with code ${String(code)} and at least one reported PID `+
284
+ 'remained live or was reused.'
285
+ );
286
+ }
287
+ finish(proven);
288
+ });
289
+ });
290
+ });
291
+ }
292
+
293
+ async function forceDrainUnixGroup(child){
294
+ if(!childIsRunning(child))return false;
295
+ const group=-child.pid;
296
+ try{
297
+ process.kill(group,'SIGKILL');
298
+ }catch(error){
299
+ if(error?.code!=='ESRCH')killDirectChild(child,true);
300
+ return false;
301
+ }
302
+ const deadline=performance.now()+CANCELLATION_FORCE_WAIT_MS;
303
+ while(performance.now()<deadline){
304
+ await new Promise(resolve=>setTimeout(resolve,20));
305
+ try{
306
+ process.kill(group,0);
307
+ }catch(error){
308
+ if(error?.code==='ESRCH')return true;
309
+ return false;
310
+ }
311
+ }
312
+ return false;
313
+ }
314
+
315
+ function forceDrainProcessTree(child){
316
+ return process.platform==='win32'
317
+ ?forceDrainWindowsTree(child)
318
+ :forceDrainUnixGroup(child);
319
+ }
320
+
321
+ function runIsolatedFile(file,signal){
322
+ if(signal.aborted)return Promise.reject(signal.reason);
323
+ return new Promise((resolve,reject)=>{
324
+ const token=randomUUID();
325
+ let settled=false;
326
+ let escalation=null;
327
+ let forceSettlement=null;
328
+ let idleTimer=null;
329
+ let completion=null;
330
+ let watchdogFailure=null;
331
+ let closed=null;
332
+ let drainStarted=false;
333
+ let drainProven=null;
334
+ let child;
335
+ const phaseTimers=new Map();
336
+ const stopHeartbeat=startHeartbeat(displayFile(file));
337
+ const clearPhases=()=>{
338
+ for(const timer of phaseTimers.values())clearTimeout(timer);
339
+ phaseTimers.clear();
340
+ };
341
+ const finish=(callback,value)=>{
342
+ if(settled)return;
343
+ settled=true;
344
+ clearTimeout(escalation);
345
+ clearTimeout(forceSettlement);
346
+ clearTimeout(idleTimer);
347
+ clearPhases();
348
+ stopHeartbeat();
349
+ signal.removeEventListener('abort',abort);
350
+ callback(value);
351
+ };
352
+ const sendAbort=reason=>{
353
+ if(!child?.connected)return;
354
+ try{
355
+ child.send(
356
+ {protocol:IPC_PROTOCOL,token,type:'abort',reason},
357
+ ()=>{}
358
+ );
359
+ }catch{}
360
+ };
361
+ const settleClosed=()=>{
362
+ if(closed===null)return;
363
+ if(signal.aborted){
364
+ if(drainProven===null)return;
365
+ if(!drainProven){
366
+ const error=new Error(
367
+ `Could not drain the cancelled process tree for ${displayFile(file)}.`
368
+ );
369
+ error.code='ARCANE_TEST_DRAIN_FAILED';
370
+ finish(
371
+ reject,
372
+ error
373
+ );
374
+ }else{
375
+ finish(reject,signal.reason);
376
+ }
377
+ return;
378
+ }
379
+ if(completion!==null||watchdogFailure!==null){
380
+ if(drainProven===null)return;
381
+ finish(resolve,{
382
+ code:drainProven
383
+ ?(watchdogFailure===null?completion?.status??1:1)
384
+ :2,
385
+ signal:closed.signal,
386
+ watchdog:watchdogFailure,
387
+ drainProven,
388
+ fatalIsolation:!drainProven
389
+ });
390
+ return;
391
+ }
392
+ console.error(
393
+ `[arcane-test] ${displayFile(file)} closed without an authenticated harness report.`
394
+ );
395
+ finish(resolve,{
396
+ code:2,
397
+ signal:closed.signal,
398
+ rawCode:closed.code,
399
+ fatalIsolation:true
400
+ });
401
+ };
402
+ const beginDrain=()=>{
403
+ if(drainStarted||settled)return;
404
+ drainStarted=true;
405
+ clearTimeout(escalation);
406
+ clearTimeout(idleTimer);
407
+ clearPhases();
408
+ const armCloseDeadline=()=>{
409
+ if(closed!==null||settled)return;
410
+ forceSettlement=setTimeout(()=>{
411
+ child?.unref?.();
412
+ finish(resolve,{
413
+ code:2,
414
+ signal:null,
415
+ watchdog:watchdogFailure,
416
+ drainProven:false,
417
+ fatalIsolation:true
418
+ });
419
+ },CANCELLATION_FORCE_WAIT_MS);
420
+ forceSettlement.unref?.();
421
+ };
422
+ void forceDrainProcessTree(child).then(proven=>{
423
+ drainProven=proven;
424
+ if(!proven)killDirectChild(child,true);
425
+ settleClosed();
426
+ armCloseDeadline();
427
+ }).catch(()=>{
428
+ drainProven=false;
429
+ killDirectChild(child,true);
430
+ settleClosed();
431
+ armCloseDeadline();
432
+ });
433
+ };
434
+ const watchdog=phase=>{
435
+ if(watchdogFailure!==null||settled)return;
436
+ watchdogFailure={
437
+ kind:phase.kind,
438
+ name:phase.name,
439
+ timeoutMs:phase.timeoutMs
440
+ };
441
+ clearTimeout(idleTimer);
442
+ clearPhases();
443
+ console.error(
444
+ `[arcane-test] WATCHDOG ${phase.kind} "${phase.name}" exceeded `+
445
+ `${String(phase.timeoutMs)} ms.`
446
+ );
447
+ sendAbort(`The ${phase.kind} phase exceeded ${String(phase.timeoutMs)} ms.`);
448
+ escalation=setTimeout(beginDrain,WATCHDOG_REPORT_GRACE_MS);
449
+ escalation.unref?.();
450
+ };
451
+ const scheduleIdle=()=>{
452
+ clearTimeout(idleTimer);
453
+ if(
454
+ settled||completion!==null||watchdogFailure!==null
455
+ ||phaseTimers.size>0
456
+ )return;
457
+ const phase={
458
+ kind:'coordination',
459
+ name:`${displayFile(file)} next phase or completion`,
460
+ timeoutMs:BETWEEN_PHASE_TIMEOUT_MS
461
+ };
462
+ idleTimer=setTimeout(()=>watchdog(phase),BETWEEN_PHASE_TIMEOUT_MS);
463
+ idleTimer.unref?.();
464
+ };
465
+ const onMessage=message=>{
466
+ if(
467
+ message?.protocol!==IPC_PROTOCOL
468
+ ||message?.token!==token
469
+ ||typeof message.type!=='string'
470
+ )return;
471
+ if(message.type==='phase'){
472
+ if(
473
+ completion!==null
474
+ ||watchdogFailure!==null
475
+ ||typeof message.id!=='string'
476
+ ||message.id===''||message.id.length>200
477
+ )return;
478
+ if(message.status==='started'){
479
+ if(
480
+ typeof message.kind!=='string'
481
+ ||typeof message.name!=='string'
482
+ ||!Number.isSafeInteger(message.timeoutMs)
483
+ ||message.timeoutMs<1
484
+ ||message.timeoutMs>3_600_000
485
+ ||phaseTimers.has(message.id)
486
+ )return;
487
+ clearTimeout(idleTimer);
488
+ const timer=setTimeout(()=>watchdog(message),message.timeoutMs);
489
+ phaseTimers.set(message.id,timer);
490
+ }else if(message.status==='completed'){
491
+ const timer=phaseTimers.get(message.id);
492
+ if(timer!==undefined){
493
+ clearTimeout(timer);
494
+ phaseTimers.delete(message.id);
495
+ scheduleIdle();
496
+ }
497
+ }
498
+ return;
499
+ }
500
+ if(message.type==='complete'){
501
+ if(completion!==null||![0,1,2,130].includes(message.status))return;
502
+ completion={status:message.status};
503
+ clearTimeout(escalation);
504
+ clearTimeout(idleTimer);
505
+ clearPhases();
506
+ beginDrain();
507
+ }
508
+ };
509
+ const abort=()=>{
510
+ clearPhases();
511
+ sendAbort('The coordinator cancelled the test run.');
512
+ beginDrain();
513
+ };
514
+ try{
515
+ child=spawn(process.execPath,[RUNNER_PATH,SINGLE_FILE_ARGUMENT,file],{
516
+ env:{...process.env,[IPC_TOKEN_ENV]:token},
517
+ shell:false,
518
+ windowsHide:true,
519
+ detached:process.platform!=='win32',
520
+ stdio:['inherit','inherit','inherit','ipc']
521
+ });
522
+ }catch(error){
523
+ finish(reject,error);
524
+ return;
525
+ }
526
+ child.on('message',onMessage);
527
+ child.once('error',error=>finish(reject,error));
528
+ child.once('close',(code,childSignal)=>{
529
+ closed={code,signal:childSignal};
530
+ settleClosed();
531
+ });
532
+ signal.addEventListener('abort',abort,{once:true});
533
+ scheduleIdle();
534
+ if(signal.aborted)abort();
535
+ });
536
+ }
537
+
538
+ async function runSingleFile(file,signal){
539
+ console.log(`[arcane-test] IMPORT ${displayFile(file)}`);
540
+ register(new URL('../src/testing-loader.mjs',import.meta.url),import.meta.url);
541
+ const importPhase={
542
+ id:'module-import',
543
+ kind:'import',
544
+ name:displayFile(file),
545
+ status:'started',
546
+ timeoutMs:DEFAULT_TEST_TIMEOUT_MS,
547
+ type:'phase'
548
+ };
549
+ await sendIsolatedMessage(importPhase);
550
+ const importStarted=performance.now();
551
+ let importError=null;
552
+ try{
553
+ try{
554
+ await import(pathToFileURL(file).href);
555
+ }catch(error){
556
+ importError=error;
557
+ }
558
+ }finally{
559
+ await sendIsolatedMessage({
560
+ ...importPhase,
561
+ status:'completed',
562
+ elapsedMs:performance.now()-importStarted
563
+ });
564
+ }
565
+ if(importError!==null){
566
+ test(`[module import] ${path.basename(file)}`,function reportImportFailure(){
567
+ throw importError;
568
+ });
569
+ }
570
+ const result=await runRegisteredTests({
571
+ signal,
572
+ requireTests:true,
573
+ onPhase:message=>sendIsolatedMessage({...message,type:'phase'})
574
+ });
575
+ return result.ok?0:1;
576
+ }
577
+
578
+ async function runCoordinator(arguments_,signal){
579
+ console.log('[arcane-test] Discovering test files.');
580
+ const stopHeartbeat=startHeartbeat('test discovery');
581
+ let files;
582
+ try{
583
+ files=await collectTestFiles(arguments_,signal);
584
+ }finally{
585
+ stopHeartbeat();
586
+ }
587
+ console.log(`[arcane-test] Discovered ${String(files.length)} test file(s).`);
588
+ if(files.length===0){
589
+ const result=await runRegisteredTests({signal,requireTests:true});
590
+ return result.ok?0:1;
591
+ }
592
+ let status=0;
593
+ for(const file of files){
594
+ if(signal.aborted)throw signal.reason;
595
+ console.log(`[arcane-test] START ${displayFile(file)}`);
596
+ const result=await runIsolatedFile(file,signal);
597
+ console.log(
598
+ `[arcane-test] COMPLETE ${displayFile(file)} (exit ${String(result.code)}`+
599
+ `${result.signal===null?'':`, signal ${result.signal}`})`
600
+ );
601
+ if(result.fatalIsolation)return 2;
602
+ if(result.code===130)return 130;
603
+ if(result.code===1){
604
+ if(status===0)status=1;
605
+ }else if(result.code!==0){
606
+ status=2;
607
+ }
608
+ }
609
+ return status;
610
+ }
611
+
612
+ function flushStream(stream){
613
+ if(!stream||stream.writableEnded)return Promise.resolve(true);
614
+ if(stream.destroyed||stream.writable===false)return Promise.resolve(false);
615
+ return new Promise(resolve=>{
616
+ let settled=false;
617
+ const finish=flushed=>{
618
+ if(settled)return;
619
+ settled=true;
620
+ clearTimeout(timeout);
621
+ stream.removeListener('error',failed);
622
+ resolve(flushed);
623
+ };
624
+ const failed=()=>finish(false);
625
+ const timeout=setTimeout(failed,OUTPUT_FLUSH_TIMEOUT_MS);
626
+ stream.once('error',failed);
627
+ try{
628
+ stream.write('',()=>finish(true));
629
+ }catch{
630
+ failed();
631
+ }
632
+ });
633
+ }
634
+
635
+ async function flushOutput(){
636
+ const flushed=await Promise.all([flushStream(process.stdout),flushStream(process.stderr)]);
637
+ if(flushed.every(Boolean))return;
638
+ try{
639
+ writeSync(
640
+ 2,
641
+ '[arcane-test] Output flush exceeded its bounded drain; diagnostics may be truncated.\n'
642
+ );
643
+ }catch{}
644
+ }
645
+
646
+ async function main(){
647
+ console.log('[arcane-test] Test request accepted.');
648
+ const controller=new AbortController();
649
+ const cancel=signal=>{
650
+ const error=new Error(`Test run interrupted by ${signal}.`);
651
+ error.code='ARCANE_TEST_CANCELLED';
652
+ controller.abort(error);
653
+ };
654
+ const interrupt=()=>cancel('SIGINT');
655
+ const terminateRun=()=>cancel('SIGTERM');
656
+ const parentAbort=message=>{
657
+ if(
658
+ message?.protocol!==IPC_PROTOCOL
659
+ ||message?.token!==ISOLATED_TOKEN
660
+ ||message?.type!=='abort'
661
+ )return;
662
+ const error=new Error(
663
+ typeof message.reason==='string'?message.reason:'The coordinator cancelled the test file.'
664
+ );
665
+ error.code='ARCANE_TEST_CANCELLED';
666
+ controller.abort(error);
667
+ };
668
+ process.once('SIGINT',interrupt);
669
+ process.once('SIGTERM',terminateRun);
670
+ if(ISOLATED_MODE)process.on('message',parentAbort);
671
+ try{
672
+ if(process.argv[2]===SINGLE_FILE_ARGUMENT){
673
+ if(process.argv.length!==4){
674
+ throw new Error(`${SINGLE_FILE_ARGUMENT} requires one exact test file.`);
675
+ }
676
+ if(typeof ISOLATED_TOKEN!=='string'||ISOLATED_TOKEN===''||ISOLATED_SEND===null){
677
+ throw new Error(
678
+ 'Internal single-file mode requires its authenticated coordinator channel.'
679
+ );
680
+ }
681
+ interceptIsolatedExit();
682
+ return await runSingleFile(path.resolve(process.argv[3]),controller.signal);
683
+ }
684
+ return await runCoordinator(process.argv.slice(2),controller.signal);
685
+ }catch(error){
686
+ if(error?.code==='ARCANE_TEST_DRAIN_FAILED'){
687
+ console.error(formatError(error));
688
+ return 2;
689
+ }
690
+ if(controller.signal.aborted){
691
+ console.error(formatError(controller.signal.reason??error));
692
+ return 130;
693
+ }
694
+ console.error(formatError(error));
695
+ return 2;
696
+ }finally{
697
+ process.removeListener('SIGINT',interrupt);
698
+ process.removeListener('SIGTERM',terminateRun);
699
+ if(ISOLATED_MODE)process.removeListener('message',parentAbort);
700
+ }
701
+ }
702
+
703
+ const isolatedHold=ISOLATED_MODE?setInterval(()=>{},60_000):null;
704
+ const releaseIsolatedHold=()=>{
705
+ if(isolatedHold!==null)clearInterval(isolatedHold);
706
+ };
707
+ if(ISOLATED_MODE){
708
+ process.once('disconnect',()=>{
709
+ releaseIsolatedHold();
710
+ process.exitCode=2;
711
+ });
712
+ }
713
+ const status=await main();
714
+ if(ISOLATED_MODE){
715
+ try{
716
+ const flushPhase={
717
+ id:'output-flush',
718
+ kind:'flush',
719
+ name:'test report output flush',
720
+ status:'started',
721
+ timeoutMs:OUTPUT_FLUSH_TIMEOUT_MS,
722
+ type:'phase'
723
+ };
724
+ await sendIsolatedMessage(flushPhase);
725
+ const flushStarted=performance.now();
726
+ await flushOutput();
727
+ await sendIsolatedMessage({
728
+ ...flushPhase,
729
+ status:'completed',
730
+ elapsedMs:performance.now()-flushStarted
731
+ });
732
+ await sendIsolatedMessage({type:'complete',status});
733
+ }catch(error){
734
+ releaseIsolatedHold();
735
+ console.error(formatError(error));
736
+ process.exitCode=2;
737
+ process.disconnect?.();
738
+ }
739
+ }else{
740
+ process.exitCode=status;
741
+ }