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,1335 @@
1
+ import path from 'node:path';
2
+ import {readdir,lstat,realpath} from 'node:fs/promises';
3
+ import {fileURLToPath} from 'node:url';
4
+ import {createWorkspace,initWorkspace} from './scaffold.mjs';
5
+ import {
6
+ discoverApps as discoverWorkspaceApps,
7
+ inspectWorkspaceProfile,
8
+ resolveWorkspace,
9
+ validateDiscoveredApplication,
10
+ validateWorkspace
11
+ } from './workspace.mjs';
12
+ import {loadArcaneIntegratedProvider} from './integrated-provider-loader.mjs';
13
+ import {startDevServer} from './dev-server.mjs';
14
+ import {authenticateRuntimeReceipt,loadRuntimeRelease,verifyRuntime} from './runtime.mjs';
15
+ import {
16
+ authenticateSharedPayloadSnapshot,
17
+ packageApp,
18
+ prepareSharedPayloadSnapshot,
19
+ verifyApp
20
+ } from './packager/core.mjs';
21
+ import {assertNativeToolchainCompatibility} from './native-plan.mjs';
22
+ import {runDoctor} from './doctor.mjs';
23
+ import {runProcess} from './process.mjs';
24
+ import {
25
+ buildTarget,
26
+ createNativeTargetAdapter,
27
+ getTargetAdapter,
28
+ listTargets,
29
+ runTarget
30
+ } from './targets/index.mjs';
31
+ import {runRepositoryAction} from './repository.mjs';
32
+ import {ArcaneError,ERROR_CODES,throwIfAborted} from './errors.mjs';
33
+ import {createEventQueue} from './event-queue.mjs';
34
+ import {ARCANE_MACHINE_BUNDLE_VERSION} from './constants.mjs';
35
+ import {checkForSdkUpdate} from './update-check.mjs';
36
+ import {
37
+ APP_BUNDLE_EXTENSION,
38
+ createAppReleaseBundle,
39
+ verifyAppReleaseBundle
40
+ } from './release-bundle.mjs';
41
+
42
+ const TEST_RUNNER_PATH=fileURLToPath(new URL('../bin/arcane-test.mjs',import.meta.url));
43
+ const MAX_TEST_FAILURE_STREAM_BYTES=32*1024;
44
+ const MAX_TEST_FAILURE_AGGREGATE_BYTES=256*1024;
45
+ const MAX_TEST_FAILURE_MESSAGE_BYTES=24*1024;
46
+ const TEST_OUTPUT_TRUNCATION_NOTICE='[... earlier test output omitted ...]\n';
47
+
48
+ function normalizedTestOutput(value){
49
+ return typeof value==='string'?value.trim():'';
50
+ }
51
+
52
+ function boundedTestOutputTail(value,maxBytes){
53
+ const source=normalizedTestOutput(value);
54
+ const sourceBytes=Buffer.byteLength(source,'utf8');
55
+ if(sourceBytes<=maxBytes){
56
+ return {text:source,bytes:sourceBytes,truncated:false};
57
+ }
58
+ const noticeBytes=Buffer.byteLength(TEST_OUTPUT_TRUNCATION_NOTICE,'utf8');
59
+ if(maxBytes<noticeBytes){
60
+ return {text:'',bytes:0,truncated:sourceBytes>0};
61
+ }
62
+ const buffer=Buffer.from(source,'utf8');
63
+ let start=buffer.length-(maxBytes-noticeBytes);
64
+ while(start<buffer.length&&(buffer[start]&0xc0)===0x80){
65
+ start+=1;
66
+ }
67
+ const text=`${TEST_OUTPUT_TRUNCATION_NOTICE}${buffer.subarray(start).toString('utf8')}`;
68
+ return {text,bytes:Buffer.byteLength(text,'utf8'),truncated:true};
69
+ }
70
+
71
+ function failedTestOutputBudgets(stderr,stdout,maxBytes){
72
+ const desiredStderr=Math.min(
73
+ MAX_TEST_FAILURE_STREAM_BYTES,
74
+ Buffer.byteLength(normalizedTestOutput(stderr),'utf8')
75
+ );
76
+ const desiredStdout=Math.min(
77
+ MAX_TEST_FAILURE_STREAM_BYTES,
78
+ Buffer.byteLength(normalizedTestOutput(stdout),'utf8')
79
+ );
80
+ if(desiredStderr+desiredStdout<=maxBytes){
81
+ return {stderr:desiredStderr,stdout:desiredStdout};
82
+ }
83
+ const shared=Math.floor(maxBytes/2);
84
+ let stderrBudget=Math.min(desiredStderr,shared);
85
+ let stdoutBudget=Math.min(desiredStdout,shared);
86
+ let remaining=maxBytes-stderrBudget-stdoutBudget;
87
+ const additionalStderr=Math.min(remaining,desiredStderr-stderrBudget);
88
+ stderrBudget+=additionalStderr;
89
+ remaining-=additionalStderr;
90
+ stdoutBudget+=Math.min(remaining,desiredStdout-stdoutBudget);
91
+ return {stderr:stderrBudget,stdout:stdoutBudget};
92
+ }
93
+
94
+ function captureFailedTest(testFile,result,workspaceRoot,maxBytes){
95
+ const budgets=failedTestOutputBudgets(result.stderr,result.stdout,maxBytes);
96
+ const stderr=boundedTestOutputTail(result.stderr,budgets.stderr);
97
+ const stdout=boundedTestOutputTail(result.stdout,budgets.stdout);
98
+ return {
99
+ failure:Object.freeze({
100
+ testFile:path.relative(workspaceRoot,testFile).replaceAll('\\','/'),
101
+ exitCode:result.code,
102
+ signal:result.signal,
103
+ stdout:stdout.text,
104
+ stderr:stderr.text,
105
+ stdoutTruncated:stdout.truncated,
106
+ stderrTruncated:stderr.truncated
107
+ }),
108
+ bytes:stdout.bytes+stderr.bytes
109
+ };
110
+ }
111
+
112
+ function failedTestMessage(failures){
113
+ const summary=`${String(failures.length)} isolated test file${failures.length===1?'':'s'} failed.`;
114
+ const actionable=failures.find(failure=>failure.stderr||failure.stdout);
115
+ if(!actionable){
116
+ return summary;
117
+ }
118
+ const heading=`\n\n${actionable.testFile}:\n`;
119
+ const available=Math.max(
120
+ 0,
121
+ MAX_TEST_FAILURE_MESSAGE_BYTES-Buffer.byteLength(`${summary}${heading}`,'utf8')
122
+ );
123
+ const diagnostic=boundedTestOutputTail(
124
+ actionable.stderr||actionable.stdout,
125
+ available
126
+ ).text;
127
+ return diagnostic?`${summary}${heading}${diagnostic}`:summary;
128
+ }
129
+
130
+ async function emit(onEvent,event){
131
+ await onEvent?.(event);
132
+ }
133
+
134
+ function nativeArtifactReceipt(result){
135
+ return result?.artifactReceipt??result?.built?.artifactReceipt??null;
136
+ }
137
+
138
+ function withEventDeliveryFailure(result,error){
139
+ const eventDelivery=Object.freeze({
140
+ status:'degraded',
141
+ errorCode:'ARCANE_EVENT_DELIVERY_FAILED',
142
+ message:String(error?.message??error)
143
+ });
144
+ if(result?.built?.artifactReceipt){
145
+ return {...result,built:{...result.built,eventDelivery}};
146
+ }
147
+ return {...result,eventDelivery};
148
+ }
149
+
150
+ function isNativeCommitEvent(_label,event){
151
+ return event?.type==='native.build.committed'
152
+ ||(event?.phase==='publish'&&event?.status==='completed');
153
+ }
154
+
155
+ async function ownedWork(label,work,{signal,onEvent,heartbeatMs=5000}={}){
156
+ throwIfAborted(signal);
157
+ const controller=new AbortController();
158
+ const forwardAbort=()=>controller.abort(signal?.reason);
159
+ signal?.addEventListener('abort',forwardAbort,{once:true});
160
+ if(signal?.aborted){
161
+ forwardAbort();
162
+ }
163
+ throwIfAborted(controller.signal);
164
+ let committed=false;
165
+ const events=createEventQueue(onEvent,{
166
+ onFailure:error=>{
167
+ if(!committed||label==='run')controller.abort(error);
168
+ }
169
+ });
170
+ const forwardEvent=async event=>{
171
+ if(isNativeCommitEvent(label,event))committed=true;
172
+ try{
173
+ await events.send(event);
174
+ }catch(error){
175
+ if(!committed||label==='run')throw error;
176
+ }
177
+ };
178
+ const abort=()=>{
179
+ void events.enqueue({
180
+ type:`${label}.cancellation-pending`,
181
+ message:`${label} will stop at the next safe boundary.`
182
+ });
183
+ };
184
+ let heartbeat=null;
185
+ let producersStopped=false;
186
+ const stopProducers=()=>{
187
+ if(producersStopped){
188
+ return;
189
+ }
190
+ producersStopped=true;
191
+ clearInterval(heartbeat);
192
+ controller.signal.removeEventListener('abort',abort);
193
+ signal?.removeEventListener('abort',forwardAbort);
194
+ };
195
+ try{
196
+ await events.send({type:`${label}.started`,message:`${label} started.`});
197
+ controller.signal.addEventListener('abort',abort,{once:true});
198
+ heartbeat=setInterval(()=>{
199
+ void events.enqueue(
200
+ {type:`${label}.heartbeat`,message:`${label} is still running.`},
201
+ {coalesce:`${label}.heartbeat`}
202
+ );
203
+ },Math.max(1000,heartbeatMs));
204
+ heartbeat.unref?.();
205
+ const result=await work({
206
+ signal:controller.signal,
207
+ onEvent:forwardEvent
208
+ });
209
+ if(nativeArtifactReceipt(result))committed=true;
210
+ stopProducers();
211
+ if(!committed||label==='run')throwIfAborted(controller.signal);
212
+ try{
213
+ await events.send({type:`${label}.completed`,message:`${label} completed.`});
214
+ await events.drain();
215
+ }catch(error){
216
+ if(committed){
217
+ if(label==='run')throwIfAborted(controller.signal);
218
+ return withEventDeliveryFailure(result,error);
219
+ }
220
+ throw error;
221
+ }
222
+ if(!committed||label==='run')throwIfAborted(controller.signal);
223
+ if(events.error&&committed)return withEventDeliveryFailure(result,events.error);
224
+ return result;
225
+ }catch(error){
226
+ stopProducers();
227
+ try{
228
+ await events.drain();
229
+ }catch(callbackFailure){
230
+ throw callbackFailure;
231
+ }
232
+ throw error;
233
+ }finally{
234
+ stopProducers();
235
+ }
236
+ }
237
+
238
+ async function collectTests(root,files,signal){
239
+ let entries;
240
+ try{
241
+ entries=await readdir(root,{withFileTypes:true});
242
+ }catch(error){
243
+ if(error?.code==='ENOENT'){
244
+ return;
245
+ }
246
+ throw error;
247
+ }
248
+ for(const entry of entries.sort((left,right)=>left.name.localeCompare(right.name,'en'))){
249
+ throwIfAborted(signal);
250
+ const absolute=path.join(root,entry.name);
251
+ if(entry.isSymbolicLink()){
252
+ continue;
253
+ }
254
+ if(entry.isDirectory()){
255
+ await collectTests(absolute,files,signal);
256
+ }else if(entry.isFile()&&/\.test\.(?:mjs|cjs|js)$/u.test(entry.name)){
257
+ const info=await lstat(absolute);
258
+ if(info.isFile()&&!info.isSymbolicLink()){
259
+ files.add(absolute);
260
+ }
261
+ }
262
+ }
263
+ }
264
+
265
+ async function selectedWorkspace(options){
266
+ const resolved=await resolveWorkspace({
267
+ workspaceRoot:options.workspaceRoot,
268
+ appId:options.appId
269
+ });
270
+ return {
271
+ workspaceRoot:resolved.workspaceRoot,
272
+ workspaceMode:resolved.config.workspaceMode,
273
+ appId:resolved.appId??resolved.app?.id??resolved.app?.appId,
274
+ appRoot:resolved.appRoot??resolved.app?.appRoot
275
+ };
276
+ }
277
+
278
+ function operationScope(options){
279
+ const scope=options.scope??'app';
280
+ if(!['app','shared'].includes(scope)){
281
+ throw new ArcaneError(
282
+ ERROR_CODES.usage,
283
+ `Unsupported Arcane development scope: ${String(scope)}. Expected app or shared.`
284
+ );
285
+ }
286
+ return scope;
287
+ }
288
+
289
+ function assertApplicationScope(options,operation){
290
+ if(operationScope(options)!=='app'){
291
+ throw new ArcaneError(
292
+ ERROR_CODES.usage,
293
+ `${operation} supports only --scope app. Shared development cannot package or build application output.`
294
+ );
295
+ }
296
+ }
297
+
298
+ async function executeIntegratedSharedOperation(operation,options){
299
+ const label=operation==='focused-test'?'shared.test':'shared.check';
300
+ return ownedWork(
301
+ label,
302
+ async({signal,onEvent})=>{
303
+ const profile=await inspectWorkspaceProfile(options.workspaceRoot);
304
+ if(profile.workspaceMode!=='integrated'){
305
+ throw new ArcaneError(
306
+ ERROR_CODES.policyDenied,
307
+ `--scope shared is available only in an integrated Arcane OS workspace.`
308
+ );
309
+ }
310
+ const provider=await loadArcaneIntegratedProvider({
311
+ arcaneRoot:profile.workspaceRoot,
312
+ signal,
313
+ onEvent,
314
+ run:options.processRunner??runProcess
315
+ });
316
+ const result=await provider.execute({
317
+ operation,
318
+ ...(operation==='focused-test'?{testFile:options.testFile}:{}),
319
+ signal,
320
+ onEvent
321
+ });
322
+ return Object.freeze({
323
+ scope:'shared',
324
+ workspaceMode:'integrated',
325
+ workspaceRoot:profile.workspaceRoot,
326
+ providerGeneration:provider.providerGeneration,
327
+ result
328
+ });
329
+ },
330
+ options
331
+ );
332
+ }
333
+
334
+ async function preparedWorkspace(options){
335
+ const workspace=await validateWorkspace({
336
+ workspaceRoot:options.workspaceRoot,
337
+ appId:options.appId,
338
+ signal:options.signal,
339
+ onEvent:options.onEvent
340
+ });
341
+ const external=workspace.workspaceMode==='external';
342
+ const runtimeRoot=external
343
+ ?path.join(workspace.workspaceRoot,'node_modules','arcane-os','runtime')
344
+ :workspace.workspaceRoot;
345
+ const runtimeReceipt=external&&!options.deferRuntimeVerification
346
+ ?await verifyRuntime({
347
+ runtimeRoot,
348
+ signal:options.signal,
349
+ onEvent:options.onEvent
350
+ })
351
+ :null;
352
+ return {
353
+ runtimeReceipt,
354
+ runtimeRoot,
355
+ workspaceMode:workspace.workspaceMode,
356
+ descriptor:workspace.app.descriptor,
357
+ descriptorSource:workspace.app.descriptorSource,
358
+ workspaceRoot:workspace.workspaceRoot,
359
+ appId:workspace.appId,
360
+ appRoot:workspace.appRoot,
361
+ validation:workspace
362
+ };
363
+ }
364
+
365
+ function compareRuntimeInventory(left,right){
366
+ return left.path.localeCompare(right.path,'en');
367
+ }
368
+
369
+ async function externalRuntimeSnapshotReceipt(prepared,sharedPayloadSnapshot,sharedPayloadIds,{signal,onEvent}={}){
370
+ throwIfAborted(signal);
371
+ await authenticateSharedPayloadSnapshot(sharedPayloadSnapshot,{
372
+ workspaceRoot:prepared.workspaceRoot,
373
+ sharedPayloadIds,
374
+ signal
375
+ });
376
+ const release=await loadRuntimeRelease({runtimeRoot:prepared.runtimeRoot});
377
+ const runtimePrefix='node_modules/arcane-os/runtime/';
378
+ const files=sharedPayloadSnapshot.files
379
+ .filter(file=>file.source.startsWith(runtimePrefix))
380
+ .map(file=>Object.freeze({
381
+ path:file.source.slice(runtimePrefix.length),
382
+ bytes:file.bytes,
383
+ sha256:file.sha256
384
+ }))
385
+ .sort(compareRuntimeInventory);
386
+ const expected=[...release.files].sort(compareRuntimeInventory);
387
+ if(JSON.stringify(files)!==JSON.stringify(expected)){
388
+ throw new ArcaneError(
389
+ ERROR_CODES.integrityFailed,
390
+ 'The retained shared payload does not match the installed Arcane runtime manifest.'
391
+ );
392
+ }
393
+ const receipt=Object.freeze({
394
+ schemaVersion:1,
395
+ kind:'arcane-sdk-runtime-shared-payload',
396
+ canonicalLocation:prepared.runtimeRoot,
397
+ source:Object.freeze({...release.source}),
398
+ files:Object.freeze(files),
399
+ fileCount:release.fileCount,
400
+ totalBytes:release.totalBytes,
401
+ contentSha256:release.contentSha256,
402
+ sharedPayloadContentSha256:sharedPayloadSnapshot.contentSha256
403
+ });
404
+ await emit(onEvent,{
405
+ type:'runtime.snapshot.verified',
406
+ contentSha256:receipt.contentSha256,
407
+ fileCount:receipt.fileCount,
408
+ totalBytes:receipt.totalBytes
409
+ });
410
+ return receipt;
411
+ }
412
+
413
+ async function validatePreparedRuntime(prepared,{signal}={}){
414
+ if(prepared.workspaceMode==='external'){
415
+ return authenticateRuntimeReceipt(prepared.runtimeReceipt,{
416
+ runtimeRoot:prepared.runtimeRoot,
417
+ signal
418
+ });
419
+ }
420
+ const validation=await validateWorkspace({
421
+ workspaceRoot:prepared.workspaceRoot,
422
+ appId:prepared.appId,
423
+ signal
424
+ });
425
+ if(validation.workspaceMode!=='integrated'){
426
+ throw new ArcaneError(ERROR_CODES.workspaceInvalid,'The integrated Arcane workspace profile changed during the operation.');
427
+ }
428
+ return validation;
429
+ }
430
+
431
+ export async function createApplication(options={}){
432
+ return createWorkspace(options);
433
+ }
434
+
435
+ export async function initializeApplication(options={}){
436
+ return initWorkspace(options);
437
+ }
438
+
439
+ export async function doctorApplication(options={}){
440
+ const result=await runDoctor(options);
441
+ if(!result.ok){
442
+ throw new ArcaneError(
443
+ ERROR_CODES.prerequisiteMissing,
444
+ 'One or more required Arcane development prerequisites failed.',
445
+ {details:result}
446
+ );
447
+ }
448
+ return result;
449
+ }
450
+
451
+ export async function testApplication(options={}){
452
+ throwIfAborted(options.signal);
453
+ if(operationScope(options)==='shared'){
454
+ if(typeof options.testFile!=='string'||!options.testFile){
455
+ throw new ArcaneError(
456
+ ERROR_CODES.usage,
457
+ 'Shared testing requires one exact repo-relative .test.mjs file.'
458
+ );
459
+ }
460
+ return executeIntegratedSharedOperation('focused-test',options);
461
+ }
462
+ const workspace=options.workspaceRoot&&options.appId&&options.appRoot
463
+ ?{
464
+ workspaceRoot:options.workspaceRoot,
465
+ workspaceMode:options.workspaceMode
466
+ ??(await inspectWorkspaceProfile(options.workspaceRoot)).workspaceMode,
467
+ appId:options.appId,
468
+ appRoot:options.appRoot
469
+ }
470
+ :await selectedWorkspace(options);
471
+ const files=new Set();
472
+ if(workspace.workspaceMode==='external'){
473
+ await collectTests(path.join(workspace.workspaceRoot,'test'),files,options.signal);
474
+ }
475
+ if(workspace.appRoot){
476
+ await collectTests(path.join(workspace.appRoot,'test'),files,options.signal);
477
+ }
478
+ const testFiles=[...files].sort();
479
+ if(testFiles.length===0){
480
+ await emit(options.onEvent,{
481
+ type:'test.skipped',
482
+ message:'No JavaScript test files were found.',
483
+ data:{workspaceRoot:workspace.workspaceRoot,appId:workspace.appId}
484
+ });
485
+ return {...workspace,passed:true,skipped:true,testFiles:[]};
486
+ }
487
+ const outputs=[];
488
+ const failures=[];
489
+ let failureOutputBytes=0;
490
+ const perFileFailureOutputBytes=Math.min(
491
+ MAX_TEST_FAILURE_STREAM_BYTES*2,
492
+ Math.floor(MAX_TEST_FAILURE_AGGREGATE_BYTES/testFiles.length)
493
+ );
494
+ for(const testFile of testFiles){
495
+ throwIfAborted(options.signal);
496
+ const result=await runProcess(
497
+ process.execPath,
498
+ [TEST_RUNNER_PATH,testFile],
499
+ {
500
+ cwd:workspace.workspaceRoot,
501
+ signal:options.signal,
502
+ onEvent:options.onEvent,
503
+ allowNonzero:true
504
+ }
505
+ );
506
+ if(result.code===0){
507
+ if(result.stdout.trim())outputs.push(result.stdout.trim());
508
+ }else if(result.code===1){
509
+ const captured=captureFailedTest(
510
+ testFile,
511
+ result,
512
+ workspace.workspaceRoot,
513
+ Math.min(
514
+ perFileFailureOutputBytes,
515
+ MAX_TEST_FAILURE_AGGREGATE_BYTES-failureOutputBytes
516
+ )
517
+ );
518
+ failures.push(captured.failure);
519
+ failureOutputBytes+=captured.bytes;
520
+ }else if(result.code!==0){
521
+ throw new ArcaneError(
522
+ ERROR_CODES.operationFailed,
523
+ `The isolated test runner exited with code ${String(result.code)} for ${path.basename(testFile)}.`,
524
+ {details:{testFile,result}}
525
+ );
526
+ }
527
+ }
528
+ if(failures.length>0){
529
+ throw new ArcaneError(
530
+ ERROR_CODES.operationFailed,
531
+ failedTestMessage(failures),
532
+ {
533
+ details:{
534
+ testFiles:failures.map(failure=>failure.testFile),
535
+ failures,
536
+ outputBytes:failureOutputBytes,
537
+ outputLimitBytes:MAX_TEST_FAILURE_AGGREGATE_BYTES,
538
+ outputTruncated:failures.some(
539
+ failure=>failure.stdoutTruncated||failure.stderrTruncated
540
+ )
541
+ }
542
+ }
543
+ );
544
+ }
545
+ return {
546
+ ...workspace,
547
+ passed:true,
548
+ skipped:false,
549
+ testFiles:testFiles.map(file=>path.relative(workspace.workspaceRoot,file).replaceAll('\\','/')),
550
+ output:outputs.join('\n')
551
+ };
552
+ }
553
+
554
+ export async function checkApplication(options={}){
555
+ if(operationScope(options)==='shared'){
556
+ if(options.skipTests){
557
+ throw new ArcaneError(ERROR_CODES.usage,'Shared check does not accept skipTests.');
558
+ }
559
+ return executeIntegratedSharedOperation('development-check',options);
560
+ }
561
+ const prepared=await preparedWorkspace(options);
562
+ const tests=options.skipTests
563
+ ?{passed:true,skipped:true,testFiles:[]}
564
+ :await testApplication({...options,...prepared});
565
+ await validatePreparedRuntime(prepared,{signal:options.signal});
566
+ return {
567
+ ok:true,
568
+ workspaceMode:prepared.workspaceMode,
569
+ workspaceRoot:prepared.workspaceRoot,
570
+ appId:prepared.appId,
571
+ descriptorSource:prepared.descriptorSource,
572
+ runtime:prepared.runtimeReceipt
573
+ ?{
574
+ mode:'sdk',
575
+ manifestSha256:prepared.runtimeReceipt.manifestSha256,
576
+ contentSha256:prepared.runtimeReceipt.contentSha256,
577
+ fileCount:prepared.runtimeReceipt.fileCount
578
+ }
579
+ :{
580
+ mode:'workspace',
581
+ sourceRoot:'arcane'
582
+ },
583
+ checks:prepared.validation.checks,
584
+ tests
585
+ };
586
+ }
587
+
588
+ export async function developApplication(options={}){
589
+ assertApplicationScope(options,'Development serving');
590
+ const prepared=await preparedWorkspace(options);
591
+ const server=await startDevServer({
592
+ workspaceRoot:prepared.workspaceRoot,
593
+ appId:prepared.appId,
594
+ mode:'source',
595
+ runtimeReceipt:prepared.runtimeReceipt,
596
+ workspaceMode:prepared.workspaceMode,
597
+ host:options.host,
598
+ port:options.port,
599
+ signal:options.signal,
600
+ onEvent:options.onEvent
601
+ });
602
+ return {...server,appId:prepared.appId,mode:'source'};
603
+ }
604
+
605
+ export async function packageApplication(options={}){
606
+ assertApplicationScope(options,'Packaging');
607
+ const prepared=await preparedWorkspace(options);
608
+ const release=await ownedWork(
609
+ 'package',
610
+ ({signal,onEvent})=>packageApp({
611
+ workspaceRoot:prepared.workspaceRoot,
612
+ appId:prepared.appId,
613
+ dryRun:Boolean(options.dryRun),
614
+ signal,
615
+ onEvent,
616
+ validateSourceState:({signal}={})=>validatePreparedRuntime(prepared,{signal})
617
+ }),
618
+ options
619
+ );
620
+ return {
621
+ workspaceRoot:prepared.workspaceRoot,
622
+ workspaceMode:prepared.workspaceMode,
623
+ appId:prepared.appId,
624
+ runtimeContentSha256:prepared.runtimeReceipt?.contentSha256??null,
625
+ release
626
+ };
627
+ }
628
+
629
+ export async function verifyApplication(options={}){
630
+ assertApplicationScope(options,'Package verification');
631
+ const prepared=await preparedWorkspace(options);
632
+ const release=await ownedWork(
633
+ 'verify',
634
+ ({signal,onEvent})=>verifyApp({
635
+ workspaceRoot:prepared.workspaceRoot,
636
+ appId:prepared.appId,
637
+ signal,
638
+ onEvent
639
+ }),
640
+ options
641
+ );
642
+ await validatePreparedRuntime(prepared,{signal:options.signal});
643
+ return {
644
+ workspaceRoot:prepared.workspaceRoot,
645
+ workspaceMode:prepared.workspaceMode,
646
+ appId:prepared.appId,
647
+ runtimeContentSha256:prepared.runtimeReceipt?.contentSha256??null,
648
+ release
649
+ };
650
+ }
651
+
652
+ export async function bundleApplication(options={}){
653
+ assertApplicationScope(options,'Release bundling');
654
+ if(options.overwrite!==undefined&&typeof options.overwrite!=='boolean'){
655
+ throw new ArcaneError(ERROR_CODES.usage,'overwrite must be a literal boolean.');
656
+ }
657
+ const prepared=await preparedWorkspace(options);
658
+ const bundle=await ownedWork(
659
+ 'bundle',
660
+ async({signal,onEvent})=>{
661
+ const verified=await verifyApp({
662
+ workspaceRoot:prepared.workspaceRoot,
663
+ appId:prepared.appId,
664
+ signal,
665
+ onEvent
666
+ });
667
+ await validatePreparedRuntime(prepared,{signal});
668
+ const releaseRoot=verified.receipt.canonicalLocation;
669
+ const outputPath=options.artifactPath??path.join(
670
+ path.dirname(releaseRoot),
671
+ `${prepared.appId}-${verified.version}${APP_BUNDLE_EXTENSION}`
672
+ );
673
+ return createAppReleaseBundle({
674
+ receipt:verified.receipt,
675
+ releaseRoot,
676
+ outputPath,
677
+ overwrite:options.overwrite,
678
+ signal,
679
+ onEvent
680
+ });
681
+ },
682
+ options
683
+ );
684
+ return {
685
+ workspaceRoot:prepared.workspaceRoot,
686
+ workspaceMode:prepared.workspaceMode,
687
+ appId:prepared.appId,
688
+ runtimeContentSha256:prepared.runtimeReceipt?.contentSha256??null,
689
+ bundle
690
+ };
691
+ }
692
+
693
+ export async function verifyBundleApplication(options={}){
694
+ return ownedWork(
695
+ 'verify-bundle',
696
+ ({signal,onEvent})=>verifyAppReleaseBundle({
697
+ bundlePath:options.artifactPath,
698
+ signal,
699
+ onEvent
700
+ }),
701
+ options
702
+ );
703
+ }
704
+
705
+ function targetSelection(options){
706
+ const target=options.target??options.targetRequest?.target;
707
+ const registered=getTargetAdapter(target);
708
+ if(target!=='browser'&&options.nativeBuilder!=null){
709
+ return {
710
+ target,
711
+ adapter:createNativeTargetAdapter({
712
+ targetId:target,
713
+ nativeBuilder:options.nativeBuilder
714
+ })
715
+ };
716
+ }
717
+ return {target,adapter:registered};
718
+ }
719
+
720
+ export async function planApplication(options={}){
721
+ assertApplicationScope(options,'Target planning');
722
+ const {target,adapter}=targetSelection(options);
723
+ const targetDescription=await adapter.describe();
724
+ if(targetDescription.status!=='available'){
725
+ return adapter.plan({...options,target});
726
+ }
727
+ if(target==='browser'){
728
+ const prepared=await preparedWorkspace(options);
729
+ return ownedWork(
730
+ 'plan',
731
+ ({signal,onEvent})=>adapter.plan({
732
+ ...options,
733
+ ...prepared,
734
+ target,
735
+ signal,
736
+ onEvent
737
+ }),
738
+ options
739
+ );
740
+ }
741
+ return ownedWork(
742
+ 'plan',
743
+ ({signal,onEvent})=>adapter.plan({...options,target,signal,onEvent}),
744
+ options
745
+ );
746
+ }
747
+
748
+ export async function doctorNativeTarget(options={}){
749
+ const {target,adapter}=targetSelection(options);
750
+ const targetDescription=await adapter.describe();
751
+ if(targetDescription.status!=='available'){
752
+ return adapter.doctor({...options,target});
753
+ }
754
+ if(target==='browser'){
755
+ throw new ArcaneError(
756
+ ERROR_CODES.targetUnavailable,
757
+ 'Native target doctor requires one explicitly selected native target.'
758
+ );
759
+ }
760
+ return ownedWork(
761
+ 'native.doctor',
762
+ ({signal,onEvent})=>adapter.doctor({...options,target,signal,onEvent}),
763
+ options
764
+ );
765
+ }
766
+
767
+ export async function prepareNativeTarget(options={}){
768
+ const {target,adapter}=targetSelection(options);
769
+ const targetDescription=await adapter.describe();
770
+ if(targetDescription.status!=='available'){
771
+ return adapter.prepare({...options,target});
772
+ }
773
+ if(target==='browser'){
774
+ throw new ArcaneError(
775
+ ERROR_CODES.targetUnavailable,
776
+ 'Native target preparation requires one explicitly selected native target.'
777
+ );
778
+ }
779
+ return ownedWork(
780
+ 'native.prepare',
781
+ ({signal,onEvent})=>adapter.prepare({...options,target,signal,onEvent}),
782
+ options
783
+ );
784
+ }
785
+
786
+ function sameOrDescendant(parent,candidate){
787
+ const relative=path.relative(path.resolve(parent),path.resolve(candidate));
788
+ return relative===''||Boolean(relative&&!relative.startsWith('..')&&!path.isAbsolute(relative));
789
+ }
790
+
791
+ export function resolveNativeBuildOutputRoot({target,workspaceMode,workspaceRoot,outputRoot}={}){
792
+ if(typeof target!=='string'||!target||target==='browser'){
793
+ throw new ArcaneError(ERROR_CODES.usage,'A native output root requires one explicit native target.');
794
+ }
795
+ if(workspaceMode==='integrated'&&!outputRoot){
796
+ throw new ArcaneError(
797
+ ERROR_CODES.usage,
798
+ `An integrated ${target} build requires --output-root outside the Arcane OS checkout.`
799
+ );
800
+ }
801
+ const resolved=path.resolve(outputRoot??path.join(workspaceRoot,'build',target));
802
+ if(workspaceMode==='integrated'&&sameOrDescendant(workspaceRoot,resolved)){
803
+ throw new ArcaneError(
804
+ ERROR_CODES.policyDenied,
805
+ `An integrated ${target} output root must be outside the Arcane OS checkout.`
806
+ );
807
+ }
808
+ if(workspaceMode==='external'&&sameOrDescendant(workspaceRoot,resolved)){
809
+ const buildRoot=path.resolve(workspaceRoot,'build');
810
+ if(!sameOrDescendant(buildRoot,resolved)){
811
+ throw new ArcaneError(
812
+ ERROR_CODES.policyDenied,
813
+ `An external ${target} output inside the app workspace must remain under its dedicated build/ namespace.`
814
+ );
815
+ }
816
+ }
817
+ return resolved;
818
+ }
819
+
820
+ export function resolvePortableBuildOutputRoot(options={}){
821
+ return resolveNativeBuildOutputRoot({...options,target:'portable'});
822
+ }
823
+
824
+ function sameCanonicalPath(left,right){
825
+ const normalize=value=>{
826
+ const normalized=path.normalize(path.resolve(value));
827
+ return process.platform==='win32'?normalized.toLowerCase():normalized;
828
+ };
829
+ return normalize(left)===normalize(right);
830
+ }
831
+
832
+ export function assertIntegratedNativeToolchain({workspaceMode,workspaceRoot,toolchainRoot,target}={}){
833
+ if(workspaceMode!=='integrated')return;
834
+ if(typeof toolchainRoot!=='string'||!sameCanonicalPath(workspaceRoot,toolchainRoot)){
835
+ throw new ArcaneError(
836
+ ERROR_CODES.policyDenied,
837
+ `An integrated ${target??'native'} build must use the same Arcane OS checkout for --workspace and --arcane-root.`
838
+ );
839
+ }
840
+ }
841
+
842
+ export function assertIntegratedPortableToolchain(options={}){
843
+ return assertIntegratedNativeToolchain({...options,target:'portable'});
844
+ }
845
+
846
+ export function assertNativeApplicationToolchainCompatibility({prepared,toolchainReceipt}={}){
847
+ return assertNativeToolchainCompatibility({
848
+ appDescriptor:prepared?.descriptor,
849
+ toolchainReceipt,
850
+ minimumCoreVersion:prepared?.runtimeReceipt?.source?.bundleVersion
851
+ ??ARCANE_MACHINE_BUNDLE_VERSION
852
+ });
853
+ }
854
+
855
+ export function assertPortableToolchainCompatibility(options={}){
856
+ return assertNativeApplicationToolchainCompatibility(options);
857
+ }
858
+
859
+ export async function verifyNativeArtifact(options={}){
860
+ const {target,adapter}=targetSelection(options);
861
+ const targetDescription=await adapter.describe();
862
+ if(targetDescription.status!=='available'){
863
+ return adapter.verify({...options,target});
864
+ }
865
+ if(target==='browser'){
866
+ throw new ArcaneError(
867
+ ERROR_CODES.targetUnavailable,
868
+ 'Native artifact verification requires one explicitly selected native target.'
869
+ );
870
+ }
871
+ return ownedWork(
872
+ 'native.verify',
873
+ ({signal,onEvent})=>adapter.verify({...options,target,signal,onEvent}),
874
+ options
875
+ );
876
+ }
877
+
878
+ function nativeDependencyError(message,code=ERROR_CODES.workspaceInvalid){
879
+ throw new ArcaneError(code,message);
880
+ }
881
+
882
+ async function nativeDependencyClosure(prepared,{target,signal,onEvent}={}){
883
+ throwIfAborted(signal);
884
+ const apps=await discoverWorkspaceApps(prepared.workspaceRoot);
885
+ const byId=new Map(apps.map(app=>[app.appId,app]));
886
+ const selected=byId.get(prepared.appId);
887
+ if(!selected){
888
+ nativeDependencyError(`The selected app ${prepared.appId} disappeared during native build preflight.`);
889
+ }
890
+ if(JSON.stringify(selected.descriptor)!==JSON.stringify(prepared.descriptor)){
891
+ nativeDependencyError(
892
+ `The selected app ${prepared.appId} descriptor changed during native build preflight.`,
893
+ ERROR_CODES.integrityFailed
894
+ );
895
+ }
896
+ if(!prepared.descriptor.targets.includes(target)){
897
+ nativeDependencyError(
898
+ `App ${prepared.appId} does not declare target ${target}.`,
899
+ ERROR_CODES.targetUnavailable
900
+ );
901
+ }
902
+
903
+ const visiting=new Set([prepared.appId]);
904
+ const visited=new Set();
905
+ const discovered=new Set();
906
+ const dependencies=[];
907
+ function visit(descriptor){
908
+ for(const dependencyId of descriptor.native.bundledApps){
909
+ if(visiting.has(dependencyId)){
910
+ nativeDependencyError(`Bundled app dependency cycle includes ${dependencyId}.`);
911
+ }
912
+ if(visited.has(dependencyId))continue;
913
+ const dependency=byId.get(dependencyId);
914
+ if(!dependency){
915
+ nativeDependencyError(
916
+ `Bundled app dependency ${dependencyId} must be present in the same Arcane workspace.`
917
+ );
918
+ }
919
+ if(!dependency.descriptor.targets.includes(target)){
920
+ nativeDependencyError(
921
+ `Bundled app dependency ${dependencyId} does not declare target ${target}.`,
922
+ ERROR_CODES.targetUnavailable
923
+ );
924
+ }
925
+ if(discovered.size>=64){
926
+ nativeDependencyError('A native app dependency closure may contain at most 64 apps.');
927
+ }
928
+ discovered.add(dependencyId);
929
+ visiting.add(dependencyId);
930
+ visit(dependency.descriptor);
931
+ visiting.delete(dependencyId);
932
+ visited.add(dependencyId);
933
+ dependencies.push(dependency);
934
+ }
935
+ }
936
+ visit(prepared.descriptor);
937
+ dependencies.sort((left,right)=>left.appId.localeCompare(right.appId,'en'));
938
+
939
+ const validated=[];
940
+ for(const dependency of dependencies){
941
+ throwIfAborted(signal);
942
+ const validation=await validateDiscoveredApplication({
943
+ workspaceRoot:prepared.workspaceRoot,
944
+ workspaceMode:prepared.workspaceMode,
945
+ app:dependency,
946
+ signal,
947
+ onEvent
948
+ });
949
+ validated.push(Object.freeze({
950
+ ...dependency,
951
+ descriptor:validation.app.descriptor,
952
+ validation
953
+ }));
954
+ }
955
+ return Object.freeze(validated);
956
+ }
957
+
958
+ async function externalWorkspaceProtectedRoots(prepared,{signal}={}){
959
+ if(prepared.workspaceMode!=='external')return [await realpath(prepared.workspaceRoot)];
960
+ const protectedRoots=[];
961
+ const entries=await readdir(prepared.workspaceRoot,{withFileTypes:true});
962
+ for(const entry of entries.sort((left,right)=>left.name.localeCompare(right.name,'en'))){
963
+ throwIfAborted(signal);
964
+ if(entry.name==='build')continue;
965
+ if(entry.isDirectory()||entry.isSymbolicLink()){
966
+ const candidate=path.join(prepared.workspaceRoot,entry.name);
967
+ const info=await lstat(candidate);
968
+ if(info.isSymbolicLink()){
969
+ const canonical=await realpath(candidate);
970
+ const canonicalInfo=await lstat(canonical);
971
+ if(canonicalInfo.isDirectory())protectedRoots.push(canonical);
972
+ }else if(info.isDirectory()){
973
+ protectedRoots.push(await realpath(candidate));
974
+ }
975
+ }
976
+ }
977
+ return protectedRoots;
978
+ }
979
+
980
+ async function canonicalNativeOutputRoot(outputRoot,{signal}={}){
981
+ let cursor=path.resolve(outputRoot);
982
+ const missing=[];
983
+ while(true){
984
+ throwIfAborted(signal);
985
+ try{
986
+ const info=await lstat(cursor);
987
+ if(info.isSymbolicLink()||!info.isDirectory()){
988
+ throw new ArcaneError(
989
+ ERROR_CODES.policyDenied,
990
+ 'The native output root must not use a linked or non-directory ancestor.'
991
+ );
992
+ }
993
+ return path.join(await realpath(cursor),...missing.reverse());
994
+ }catch(error){
995
+ if(error?.code!=='ENOENT')throw error;
996
+ const parent=path.dirname(cursor);
997
+ if(parent===cursor)throw error;
998
+ missing.push(path.basename(cursor));
999
+ cursor=parent;
1000
+ }
1001
+ }
1002
+ }
1003
+
1004
+ function pathsOverlap(left,right){
1005
+ return sameOrDescendant(left,right)||sameOrDescendant(right,left);
1006
+ }
1007
+
1008
+ async function packageNativeRelease(prepared,app,{sharedPayloadSnapshot,signal,onEvent}={}){
1009
+ const sourceState={
1010
+ ...prepared,
1011
+ appId:app.appId,
1012
+ appRoot:app.appRoot,
1013
+ descriptor:app.descriptor,
1014
+ validation:app.validation??prepared.validation
1015
+ };
1016
+ const release=await packageApp({
1017
+ workspaceRoot:prepared.workspaceRoot,
1018
+ appId:app.appId,
1019
+ sharedPayloadSnapshot,
1020
+ signal,
1021
+ onEvent,
1022
+ validateSourceState:({signal:validationSignal}={})=>validateDiscoveredApplication({
1023
+ workspaceRoot:sourceState.workspaceRoot,
1024
+ workspaceMode:sourceState.workspaceMode,
1025
+ workspaceConfig:prepared.validation.config,
1026
+ app,
1027
+ signal:validationSignal
1028
+ })
1029
+ });
1030
+ return Object.freeze({
1031
+ appId:app.appId,
1032
+ appRoot:app.appRoot,
1033
+ descriptor:app.descriptor,
1034
+ release,
1035
+ nativeInput:Object.freeze({
1036
+ appDescriptor:app.descriptor,
1037
+ appReleaseRoot:path.resolve(prepared.workspaceRoot,release.output),
1038
+ appReleaseReceipt:release.receipt
1039
+ })
1040
+ });
1041
+ }
1042
+
1043
+ async function executePairedNativeBuild(options,adapter,{signal,onEvent}={}){
1044
+ const target=options.target??options.targetRequest?.target;
1045
+ const initialPrepared=await preparedWorkspace({
1046
+ ...options,
1047
+ deferRuntimeVerification:true,
1048
+ signal,
1049
+ onEvent
1050
+ });
1051
+ assertIntegratedNativeToolchain({
1052
+ target,
1053
+ workspaceMode:initialPrepared.workspaceMode,
1054
+ workspaceRoot:initialPrepared.workspaceRoot,
1055
+ toolchainRoot:options.toolchainRoot
1056
+ });
1057
+ if(options.dryRun){
1058
+ throw new ArcaneError(
1059
+ ERROR_CODES.usage,
1060
+ `${target} native build does not support --dry-run because verified app release receipts are required.`
1061
+ );
1062
+ }
1063
+ const requestedOutputRoot=resolveNativeBuildOutputRoot({
1064
+ target,
1065
+ workspaceMode:initialPrepared.workspaceMode,
1066
+ workspaceRoot:initialPrepared.workspaceRoot,
1067
+ outputRoot:options.outputRoot
1068
+ });
1069
+ const protectedRoots=await externalWorkspaceProtectedRoots(initialPrepared,{signal});
1070
+ const outputRoot=await canonicalNativeOutputRoot(requestedOutputRoot,{signal});
1071
+ if(protectedRoots.some(root=>pathsOverlap(root,outputRoot))){
1072
+ throw new ArcaneError(
1073
+ ERROR_CODES.policyDenied,
1074
+ 'The native output root must not overlap an application workspace source or control directory.'
1075
+ );
1076
+ }
1077
+ const dependencyApps=await nativeDependencyClosure(initialPrepared,{target,signal,onEvent});
1078
+ const sharedPayloadIds=[...new Set([
1079
+ ...initialPrepared.validation.app.manifest.shared,
1080
+ ...dependencyApps.flatMap(app=>app.manifest.shared)
1081
+ ])].sort((left,right)=>left.localeCompare(right,'en'));
1082
+ const sharedPayloadSnapshot=await prepareSharedPayloadSnapshot({
1083
+ workspaceRoot:initialPrepared.workspaceRoot,
1084
+ sharedPayloadIds,
1085
+ signal,
1086
+ onEvent
1087
+ });
1088
+ const runtimeReceipt=initialPrepared.workspaceMode==='external'
1089
+ ?await externalRuntimeSnapshotReceipt(
1090
+ initialPrepared,
1091
+ sharedPayloadSnapshot,
1092
+ sharedPayloadIds,
1093
+ {signal,onEvent}
1094
+ )
1095
+ :null;
1096
+ const prepared={...initialPrepared,runtimeReceipt};
1097
+ const selectedRelease=await packageNativeRelease(prepared,{
1098
+ appId:prepared.appId,
1099
+ appRoot:prepared.appRoot,
1100
+ manifest:prepared.validation.app.manifest,
1101
+ descriptor:prepared.descriptor,
1102
+ descriptorSource:prepared.validation.app.descriptorSource,
1103
+ descriptorPath:prepared.validation.app.descriptorPath,
1104
+ validation:prepared.validation
1105
+ },{sharedPayloadSnapshot,signal,onEvent});
1106
+ const dependencyReleases=[];
1107
+ for(const dependency of dependencyApps){
1108
+ dependencyReleases.push(await packageNativeRelease(
1109
+ prepared,
1110
+ dependency,
1111
+ {sharedPayloadSnapshot,signal,onEvent}
1112
+ ));
1113
+ }
1114
+ const toolchainReceipt=await adapter.prepare({
1115
+ toolchainRoot:options.toolchainRoot,
1116
+ targetRequest:options.targetRequest,
1117
+ signal,
1118
+ onEvent
1119
+ });
1120
+ protectedRoots.push(...(options.protectedRoots??[]));
1121
+ const built=await adapter.build({
1122
+ nativeBuilder:options.nativeBuilder,
1123
+ toolchainRoot:options.toolchainRoot,
1124
+ toolchainReceipt,
1125
+ ...selectedRelease.nativeInput,
1126
+ dependencyReleases:dependencyReleases.map(item=>item.nativeInput),
1127
+ providerGeneration:options.providerGeneration??options.nativeBuilder?.providerGeneration,
1128
+ minimumCoreVersion:prepared.runtimeReceipt?.source?.bundleVersion
1129
+ ??ARCANE_MACHINE_BUNDLE_VERSION,
1130
+ protectedRoots,
1131
+ outputRoot,
1132
+ targetRequest:options.targetRequest,
1133
+ signal,
1134
+ onEvent
1135
+ });
1136
+ return Object.freeze({
1137
+ prepared,
1138
+ outputRoot,
1139
+ toolchainReceipt,
1140
+ selectedRelease,
1141
+ dependencyReleases:Object.freeze(dependencyReleases),
1142
+ built
1143
+ });
1144
+ }
1145
+
1146
+ function nativeBuildResult(assembly){
1147
+ const {prepared}=assembly;
1148
+ return {
1149
+ ...assembly.built,
1150
+ workspaceRoot:prepared.workspaceRoot,
1151
+ workspaceMode:prepared.workspaceMode,
1152
+ appId:prepared.appId,
1153
+ runtimeContentSha256:prepared.runtimeReceipt?.contentSha256??null,
1154
+ release:assembly.selectedRelease.release,
1155
+ dependencyReleases:assembly.dependencyReleases.map(item=>Object.freeze({
1156
+ appId:item.appId,
1157
+ release:item.release
1158
+ }))
1159
+ };
1160
+ }
1161
+
1162
+ function usesWorkspaceNativeAssembly(options){
1163
+ return options.nativeBuilder!=null
1164
+ &&typeof options.workspaceRoot==='string'
1165
+ &&options.appReleaseReceipt==null
1166
+ &&options.appReleaseRoot==null
1167
+ &&options.appDescriptor==null;
1168
+ }
1169
+
1170
+ export async function buildApplication(options={}){
1171
+ assertApplicationScope(options,'Building');
1172
+ const {target,adapter}=targetSelection(options);
1173
+ const targetDescription=await adapter.describe();
1174
+ if(targetDescription.status!=='available'){
1175
+ return adapter.build({...options,target});
1176
+ }
1177
+ if(target!=='browser'){
1178
+ if(usesWorkspaceNativeAssembly(options)&&options.nativePlan==null){
1179
+ const assembly=await ownedWork(
1180
+ 'build',
1181
+ context=>executePairedNativeBuild(options,adapter,context),
1182
+ options
1183
+ );
1184
+ return nativeBuildResult(assembly);
1185
+ }
1186
+ return ownedWork(
1187
+ 'build',
1188
+ ({signal,onEvent})=>adapter.build({...options,target,signal,onEvent}),
1189
+ options
1190
+ );
1191
+ }
1192
+ const prepared=await preparedWorkspace(options);
1193
+ const result=await ownedWork(
1194
+ 'build',
1195
+ ({signal,onEvent})=>buildTarget({
1196
+ ...options,
1197
+ ...prepared,
1198
+ signal,
1199
+ onEvent,
1200
+ validateSourceState:({signal}={})=>validatePreparedRuntime(prepared,{signal})
1201
+ }),
1202
+ options
1203
+ );
1204
+ return {
1205
+ ...result,
1206
+ workspaceMode:prepared.workspaceMode,
1207
+ runtimeContentSha256:prepared.runtimeReceipt?.contentSha256??null
1208
+ };
1209
+ }
1210
+
1211
+ export async function runApplication(options={}){
1212
+ assertApplicationScope(options,'Running');
1213
+ const selectedOptions=options.target==null&&options.targetRequest?.target==null
1214
+ ?{...options,target:'browser'}
1215
+ :options;
1216
+ const {target,adapter}=targetSelection(selectedOptions);
1217
+ if(target==='portable'){
1218
+ throw new ArcaneError(
1219
+ ERROR_CODES.nativeRunUnsupported,
1220
+ 'The portable target is a verified directory payload and cannot be launched. Select an executable target such as windows-x64 or linux-x64.'
1221
+ );
1222
+ }
1223
+ const targetDescription=await adapter.describe();
1224
+ if(targetDescription.status!=='available'){
1225
+ return adapter.run({...selectedOptions,target});
1226
+ }
1227
+ if(target!=='browser'){
1228
+ if(usesWorkspaceNativeAssembly(selectedOptions)&&selectedOptions.artifactReceipt==null){
1229
+ return ownedWork(
1230
+ 'run',
1231
+ async context=>{
1232
+ const assembly=await executePairedNativeBuild(selectedOptions,adapter,context);
1233
+ const launched=await adapter.run({
1234
+ toolchainRoot:selectedOptions.toolchainRoot,
1235
+ toolchainReceipt:assembly.toolchainReceipt,
1236
+ artifactReceipt:assembly.built.artifactReceipt,
1237
+ targetRequest:selectedOptions.targetRequest,
1238
+ signal:context.signal,
1239
+ onEvent:context.onEvent
1240
+ });
1241
+ return {...nativeBuildResult(assembly),run:launched};
1242
+ },
1243
+ selectedOptions
1244
+ );
1245
+ }
1246
+ return ownedWork(
1247
+ 'run',
1248
+ ({signal,onEvent})=>adapter.run({...selectedOptions,target,signal,onEvent}),
1249
+ selectedOptions
1250
+ );
1251
+ }
1252
+ const prepared=await preparedWorkspace(selectedOptions);
1253
+ return runTarget({...selectedOptions,...prepared,target});
1254
+ }
1255
+
1256
+ export async function describeTargets(options={}){
1257
+ const targets=listTargets();
1258
+ const pairedTarget=options.target??options.targetRequest?.target;
1259
+ if(options.nativeBuilder==null||pairedTarget==null||pairedTarget==='browser'){
1260
+ return {protocol:'arcane-target-adapter/1',targets};
1261
+ }
1262
+ const pairedAdapter=createNativeTargetAdapter({
1263
+ targetId:pairedTarget,
1264
+ nativeBuilder:options.nativeBuilder
1265
+ });
1266
+ await pairedAdapter.describe();
1267
+ return {
1268
+ protocol:'arcane-target-adapter/1',
1269
+ targets:targets.map(target=>target.id===pairedTarget
1270
+ ?{...target,status:'available',reason:null}
1271
+ :target)
1272
+ };
1273
+ }
1274
+
1275
+ export async function repositoryApplication(options={}){
1276
+ return runRepositoryAction(options.action,options);
1277
+ }
1278
+
1279
+ export async function checkSdkUpdate(options={}){
1280
+ return checkForSdkUpdate(options);
1281
+ }
1282
+
1283
+ export async function executeOperation(command,options={}){
1284
+ const operations={
1285
+ new:createApplication,
1286
+ init:initializeApplication,
1287
+ doctor:doctorApplication,
1288
+ dev:developApplication,
1289
+ test:testApplication,
1290
+ check:checkApplication,
1291
+ package:packageApplication,
1292
+ verify:verifyApplication,
1293
+ bundle:bundleApplication,
1294
+ 'verify-bundle':verifyBundleApplication,
1295
+ 'native-doctor':doctorNativeTarget,
1296
+ 'native-prepare':prepareNativeTarget,
1297
+ 'native-verify':verifyNativeArtifact,
1298
+ plan:planApplication,
1299
+ build:buildApplication,
1300
+ run:runApplication,
1301
+ 'update-check':checkSdkUpdate,
1302
+ targets:async options=>describeTargets(options),
1303
+ repo:repositoryApplication
1304
+ };
1305
+ const operation=operations[command];
1306
+ if(!operation){
1307
+ throw new ArcaneError(ERROR_CODES.usage,`Unknown Arcane command: ${String(command)}.`);
1308
+ }
1309
+ return operation(options);
1310
+ }
1311
+
1312
+ export function createToolchain(defaults={}){
1313
+ return Object.freeze({
1314
+ execute:(command,options={})=>executeOperation(command,{...defaults,...options}),
1315
+ create:options=>createApplication({...defaults,...options}),
1316
+ init:options=>initializeApplication({...defaults,...options}),
1317
+ doctor:options=>doctorApplication({...defaults,...options}),
1318
+ dev:options=>developApplication({...defaults,...options}),
1319
+ test:options=>testApplication({...defaults,...options}),
1320
+ check:options=>checkApplication({...defaults,...options}),
1321
+ package:options=>packageApplication({...defaults,...options}),
1322
+ verify:options=>verifyApplication({...defaults,...options}),
1323
+ bundle:options=>bundleApplication({...defaults,...options}),
1324
+ verifyBundle:options=>verifyBundleApplication({...defaults,...options}),
1325
+ doctorNative:options=>doctorNativeTarget({...defaults,...options}),
1326
+ prepareNative:options=>prepareNativeTarget({...defaults,...options}),
1327
+ verifyNative:options=>verifyNativeArtifact({...defaults,...options}),
1328
+ plan:options=>planApplication({...defaults,...options}),
1329
+ build:options=>buildApplication({...defaults,...options}),
1330
+ run:options=>runApplication({...defaults,...options}),
1331
+ updateCheck:options=>checkSdkUpdate({...defaults,...options}),
1332
+ targets:options=>describeTargets({...defaults,...options}),
1333
+ repository:options=>repositoryApplication({...defaults,...options})
1334
+ });
1335
+ }