arcane-os 0.55.0 → 0.56.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/README.md +5 -3
- package/browser-runtime/core/client.mjs +75 -14
- package/browser-runtime/core/contracts.mjs +2 -1
- package/docs/consumer-updates.md +82 -0
- package/docs/publishing.md +41 -18
- package/docs/reference/README.md +11 -1
- package/docs/reference/availability-and-normalization.md +11 -3
- package/docs/reference/core-client.md +45 -2
- package/docs/reference/core-runtime.md +10 -0
- package/docs/reference/inventory/package-api.json +22 -8
- package/docs/reference/protocols.md +22 -13
- package/docs/reference/pwa.md +17 -0
- package/package.json +1 -1
- package/schemas/arcane-app.schema.json +10 -0
- package/schemas/arcane-package.schema.json +10 -0
- package/src/app-descriptor.mjs +18 -1
- package/src/app-layout.mjs +6 -2
- package/src/core/hosts/arcane-core-loader.cjs +2 -0
- package/src/core/hosts/linux/arcane_core_process.c +568 -0
- package/src/core/hosts/linux/arcane_core_process.h +127 -0
- package/src/core/hosts/sea-launcher.cjs +17 -0
- package/src/core/hosts/windows/ArcaneCoreProcess.cs +416 -0
- package/src/core/runtime.mjs +15 -3
- package/src/core-classic-source.mjs +2 -2
- package/src/packager/core.mjs +84 -14
- package/src/process.mjs +8 -4
- package/src/toolchain.mjs +5 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.56.0
|
|
4
|
+
|
|
5
|
+
- Add explicit `package.nativeResources` selection for native-only application
|
|
6
|
+
files and directories. Browser selection remains unchanged; non-browser
|
|
7
|
+
packaging keeps additional resources outside browser transformations and
|
|
8
|
+
stages them in a separate target output. Applications select their complete
|
|
9
|
+
required source closure, and target adapters receive the effective target
|
|
10
|
+
and browser-PWA selection.
|
|
11
|
+
- Add `getInstalledCoreClient()` to read the exact live SDK-installed client
|
|
12
|
+
without connecting or changing globals. Terminal `failTransport(error)` and
|
|
13
|
+
the owned native failure callback preserve complete errors, settle pending
|
|
14
|
+
requests, and release the failed installation's callbacks and subscriptions.
|
|
15
|
+
- Add opt-in `replayRuntimeState` for Core clients and classic injection. The
|
|
16
|
+
runtime replays its actual readiness and current service state for newly
|
|
17
|
+
connected documents without restarting services or delaying page startup.
|
|
18
|
+
|
|
19
|
+
- Preserve complete UTF-8 process output when a character spans stdout or stderr
|
|
20
|
+
chunks. Independent readable-stream decoders feed the same text to captured
|
|
21
|
+
results and existing event delivery; ordering, backpressure, cancellation,
|
|
22
|
+
graceful drain, nonzero error details and line-event behavior remain unchanged.
|
|
23
|
+
- Include committed Windows and Linux Core process-transport source and the
|
|
24
|
+
generic Windows SEA launcher source as native-host groundwork. These sources
|
|
25
|
+
do not activate a platform host or deliver a public native executable; app
|
|
26
|
+
host composition and the remaining native integration stay independently owned.
|
|
27
|
+
- Clarify development `latest` tracking, production-designated pins, matching
|
|
28
|
+
requester notices, and required documentation/site release follow-through.
|
|
29
|
+
- No new dependency, application service, automatic runtime installation,
|
|
30
|
+
application default change or saved-data migration is introduced.
|
|
31
|
+
|
|
3
32
|
## 0.55.0
|
|
4
33
|
|
|
5
34
|
- Add public Core client, protocol contracts and classic-script source generation
|
package/README.md
CHANGED
|
@@ -20,9 +20,11 @@ selected SDK projection or an explicit live SDK source mount. Both profiles
|
|
|
20
20
|
share the theme, packaging, event, cancellation,
|
|
21
21
|
and browser run contracts while retaining their selected application layout.
|
|
22
22
|
|
|
23
|
-
This checkout defines the `0.
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
This checkout defines the `0.56.0` SDK contract. Development applications declare
|
|
24
|
+
`arcane-os` as `latest` and retain their lockfile. Exact SDK pins begin when Roshi
|
|
25
|
+
designates a production build. Follow [Consumer updates](docs/consumer-updates.md)
|
|
26
|
+
to refresh and compare the installed SDK and lockfile with the published channel;
|
|
27
|
+
registry state is deliberately not baked into application artifacts.
|
|
26
28
|
|
|
27
29
|
Standalone browser apps use their repository root and installed npm package directly, without a
|
|
28
30
|
generated workspace `arcane/` directory or `arcane.lock.json`. Select the
|
|
@@ -2,6 +2,7 @@ import {arcaneEvents} from '../event-manager.mjs';
|
|
|
2
2
|
import {CORE_PROTOCOL,CORE_READY_EVENTS,CoreError,serializeCoreError} from './contracts.mjs';
|
|
3
3
|
|
|
4
4
|
const CORE_CLIENT_KEY=Symbol.for('arcane-os.core.client');
|
|
5
|
+
const CORE_CLIENT_INSTALLATION=Symbol.for('arcane-os.core.client.installation');
|
|
5
6
|
const CORE_EVENT='core.rpc.event';
|
|
6
7
|
const LONG_OPERATION_TIMEOUT=50*60*1000;
|
|
7
8
|
|
|
@@ -12,6 +13,7 @@ export function createCoreClient({
|
|
|
12
13
|
eventOwner=arcaneEvents,
|
|
13
14
|
eventOwnerReady,
|
|
14
15
|
autoConnect=true,
|
|
16
|
+
replayRuntimeState=false,
|
|
15
17
|
onError=error=>global.console?.error('Arcane Core client failed.',error)
|
|
16
18
|
}={}){
|
|
17
19
|
const pending=new Map();
|
|
@@ -25,8 +27,14 @@ export function createCoreClient({
|
|
|
25
27
|
let transport=null;
|
|
26
28
|
let closed=false;
|
|
27
29
|
let eventOwnerFailure=null;
|
|
30
|
+
let transportFailure=null;
|
|
31
|
+
let transportFailureReported=false;
|
|
28
32
|
|
|
29
33
|
function report(error){
|
|
34
|
+
if(error===transportFailure){
|
|
35
|
+
if(transportFailureReported)return;
|
|
36
|
+
transportFailureReported=true;
|
|
37
|
+
}
|
|
30
38
|
try{onError(error instanceof CoreError?error:new CoreError(error));}
|
|
31
39
|
catch(reportError){global.console?.error('Arcane Core error listener failed.',reportError,error);}
|
|
32
40
|
}
|
|
@@ -260,7 +268,7 @@ export function createCoreClient({
|
|
|
260
268
|
throw new CoreError({code:'ARCANE_TRANSPORT_UNAVAILABLE',message:'This interface is not connected to an Arcane native host.'});
|
|
261
269
|
}
|
|
262
270
|
function connect(){
|
|
263
|
-
if(closed)throw new CoreError({code:'ARCANE_CLIENT_CLOSED',message:'The Core client is closed.'});
|
|
271
|
+
if(closed)throw transportFailure??new CoreError({code:'ARCANE_CLIENT_CLOSED',message:'The Core client is closed.'});
|
|
264
272
|
if(transport)return transport;
|
|
265
273
|
const cleanupStart=cleanups.length;
|
|
266
274
|
try{
|
|
@@ -271,7 +279,11 @@ export function createCoreClient({
|
|
|
271
279
|
transport=selected;
|
|
272
280
|
if(typeof transport.subscribe==='function'){
|
|
273
281
|
const unsubscribe=transport.subscribe(receive);
|
|
274
|
-
if(typeof unsubscribe==='function')
|
|
282
|
+
if(typeof unsubscribe==='function'){
|
|
283
|
+
if(closed){
|
|
284
|
+
try{unsubscribe();}catch(error){report(error);}
|
|
285
|
+
}else cleanups.push(unsubscribe);
|
|
286
|
+
}
|
|
275
287
|
}
|
|
276
288
|
}catch(error){
|
|
277
289
|
transport=null;
|
|
@@ -280,7 +292,11 @@ export function createCoreClient({
|
|
|
280
292
|
}
|
|
281
293
|
throw error;
|
|
282
294
|
}
|
|
295
|
+
if(closed)throw transportFailure??new CoreError({code:'ARCANE_CLIENT_CLOSED',message:'The Core client is closed.'});
|
|
283
296
|
complete('transport.ready',{protocol:CORE_PROTOCOL,transport:transport.name});
|
|
297
|
+
if(replayRuntimeState&&!closed)sendControl({protocol:CORE_PROTOCOL,type:'control',control:'runtime.replay'});
|
|
298
|
+
// A native callback may close the client synchronously during replay.
|
|
299
|
+
if(closed)throw transportFailure??new CoreError({code:'ARCANE_CLIENT_CLOSED',message:'The Core client is closed.'});
|
|
284
300
|
return transport;
|
|
285
301
|
}
|
|
286
302
|
function runtimeSnapshot(){
|
|
@@ -312,6 +328,7 @@ export function createCoreClient({
|
|
|
312
328
|
return true;
|
|
313
329
|
}
|
|
314
330
|
function invoke(method,parameters={},options={}){
|
|
331
|
+
if(transportFailure)return Promise.reject(transportFailure);
|
|
315
332
|
if(eventOwnerFailure)return Promise.reject(eventOwnerFailure);
|
|
316
333
|
let selected;
|
|
317
334
|
try{selected=connect();}catch(error){return Promise.reject(error);}
|
|
@@ -343,23 +360,37 @@ export function createCoreClient({
|
|
|
343
360
|
}catch(error){sendFailed(error);}
|
|
344
361
|
return promise;
|
|
345
362
|
}
|
|
346
|
-
function
|
|
347
|
-
if(closed)return false;
|
|
348
|
-
closed=true;
|
|
349
|
-
cancelAll();
|
|
363
|
+
function disposeTransport(error){
|
|
350
364
|
for(const record of acknowledgements.values()){
|
|
351
365
|
clearTimeout(record.timer);
|
|
352
|
-
record.reject(
|
|
366
|
+
record.reject(error);
|
|
353
367
|
}
|
|
354
368
|
acknowledgements.clear();
|
|
355
369
|
for(const cleanup of cleanups.splice(0)){
|
|
356
370
|
try{cleanup();}catch(error){report(error);}
|
|
357
371
|
}
|
|
358
|
-
source?.dispose();
|
|
372
|
+
try{source?.dispose();}catch(disposeError){report(disposeError);}
|
|
373
|
+
source=null;
|
|
374
|
+
transport=null;
|
|
359
375
|
// Registration closures and already-delivered payloads belong to this
|
|
360
376
|
// client lifetime and are released when the owner explicitly closes it.
|
|
361
377
|
eventActions.length=0;
|
|
362
378
|
completedEvents.clear();
|
|
379
|
+
}
|
|
380
|
+
function failTransport(error){
|
|
381
|
+
if(closed)return false;
|
|
382
|
+
transportFailure=error instanceof CoreError?error:new CoreError(error);
|
|
383
|
+
closed=true;
|
|
384
|
+
for(const id of pending.keys())settle(id,transportFailure);
|
|
385
|
+
disposeTransport(transportFailure);
|
|
386
|
+
report(transportFailure);
|
|
387
|
+
return true;
|
|
388
|
+
}
|
|
389
|
+
function close(){
|
|
390
|
+
if(closed)return false;
|
|
391
|
+
closed=true;
|
|
392
|
+
cancelAll();
|
|
393
|
+
disposeTransport(new CoreError({code:'ARCANE_CLIENT_CLOSED',message:'The Core client is closed.'}));
|
|
363
394
|
return true;
|
|
364
395
|
}
|
|
365
396
|
if(typeof global.addEventListener==='function'){
|
|
@@ -369,7 +400,7 @@ export function createCoreClient({
|
|
|
369
400
|
if(autoConnect){
|
|
370
401
|
try{connect();}catch(error){if(error.code!=='ARCANE_TRANSPORT_UNAVAILABLE')report(error);}
|
|
371
402
|
}
|
|
372
|
-
return {protocol:CORE_PROTOCOL,Error:CoreError,invoke,receive,connect,close,cancelAll,events,eventsReady,runtime:{current:runtimeSnapshot},uuid};
|
|
403
|
+
return {protocol:CORE_PROTOCOL,Error:CoreError,invoke,receive,connect,close,failTransport,cancelAll,events,eventsReady,runtime:{current:runtimeSnapshot},uuid};
|
|
373
404
|
}
|
|
374
405
|
|
|
375
406
|
/** Existing namespace call shapes; method implementations remain host-owned. */
|
|
@@ -461,28 +492,58 @@ export function createCoreFacade(client){
|
|
|
461
492
|
};
|
|
462
493
|
}
|
|
463
494
|
|
|
495
|
+
/** Reads an installed SDK client without creating a transport or changing globals. */
|
|
496
|
+
export function getInstalledCoreClient(global=globalThis){
|
|
497
|
+
const client=global?.[CORE_CLIENT_KEY];
|
|
498
|
+
const installation=client?.[CORE_CLIENT_INSTALLATION];
|
|
499
|
+
return installation?.global===global&&installation.active?client:null;
|
|
500
|
+
}
|
|
501
|
+
|
|
464
502
|
/** Installs the same synchronous facade used by classic native-host scripts. */
|
|
465
503
|
export function installCoreClient(global=globalThis,options={}){
|
|
466
504
|
if(global[CORE_CLIENT_KEY])return global[CORE_CLIENT_KEY];
|
|
467
505
|
const client=createCoreClient({...options,global,autoConnect:false});
|
|
468
506
|
const facade=createCoreFacade(client);
|
|
507
|
+
const installation={global,active:true};
|
|
508
|
+
client[CORE_CLIENT_INSTALLATION]=installation;
|
|
509
|
+
const previousFacade=global.Arcane;
|
|
469
510
|
global.Arcane=facade;
|
|
470
511
|
global[CORE_CLIENT_KEY]=client;
|
|
471
512
|
const previous=global.__arcaneReceive;
|
|
472
513
|
global.__arcaneReceive=client.receive;
|
|
514
|
+
const previousFailure=global.__arcaneTransportFailed;
|
|
515
|
+
function nativeTransportFailed(error){return client.failTransport(error);}
|
|
516
|
+
global.__arcaneTransportFailed=nativeTransportFailed;
|
|
473
517
|
const close=client.close;
|
|
474
|
-
client.
|
|
475
|
-
|
|
518
|
+
const failTransport=client.failTransport;
|
|
519
|
+
let installedTransportFailure=null;
|
|
520
|
+
function releaseInstalledClient(){
|
|
521
|
+
installation.active=false;
|
|
476
522
|
if(global[CORE_CLIENT_KEY]===client)delete global[CORE_CLIENT_KEY];
|
|
477
|
-
if(global.Arcane===facade)
|
|
523
|
+
if(global.Arcane===facade){
|
|
524
|
+
if(previousFacade===undefined)delete global.Arcane;
|
|
525
|
+
else global.Arcane=previousFacade;
|
|
526
|
+
}
|
|
478
527
|
if(global.__arcaneReceive===client.receive){
|
|
479
528
|
if(previous===undefined)delete global.__arcaneReceive;
|
|
480
529
|
else global.__arcaneReceive=previous;
|
|
481
530
|
}
|
|
482
|
-
|
|
531
|
+
if(global.__arcaneTransportFailed===nativeTransportFailed){
|
|
532
|
+
if(previousFailure===undefined)delete global.__arcaneTransportFailed;
|
|
533
|
+
else global.__arcaneTransportFailed=previousFailure;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
client.close=function closeInstalledClient(){
|
|
537
|
+
installation.active=false;
|
|
538
|
+
try{return close();}finally{releaseInstalledClient();}
|
|
539
|
+
};
|
|
540
|
+
client.failTransport=function failInstalledTransport(error){
|
|
541
|
+
if(installation.active)installedTransportFailure=error instanceof CoreError?error:new CoreError(error);
|
|
542
|
+
releaseInstalledClient();
|
|
543
|
+
return failTransport(installedTransportFailure??error);
|
|
483
544
|
};
|
|
484
545
|
try{client.connect();}catch(error){
|
|
485
|
-
if(error.code!=='ARCANE_TRANSPORT_UNAVAILABLE'){
|
|
546
|
+
if(error!==installedTransportFailure&&error.code!=='ARCANE_TRANSPORT_UNAVAILABLE'){
|
|
486
547
|
if(options.onError)options.onError(error);
|
|
487
548
|
else global.console?.error('Arcane Core transport initialization failed.',error);
|
|
488
549
|
}
|
|
@@ -7,7 +7,8 @@ export const CORE_FRAME_CONTRACTS={
|
|
|
7
7
|
response:{type:'response',fields:['protocol','type','id','ok'],success:['result'],failure:['error'],optional:['time']},
|
|
8
8
|
event:{type:'event',fields:['protocol','type','event','data'],optional:['time']},
|
|
9
9
|
cancel:{type:'control',control:'request.cancel',fields:['protocol','type','control','requestId']},
|
|
10
|
-
cancelAll:{type:'control',control:'requests.cancelAll',fields:['protocol','type','control']}
|
|
10
|
+
cancelAll:{type:'control',control:'requests.cancelAll',fields:['protocol','type','control']},
|
|
11
|
+
replay:{type:'control',control:'runtime.replay',fields:['protocol','type','control']}
|
|
11
12
|
};
|
|
12
13
|
|
|
13
14
|
/** These names describe existing methods; installed services determine availability. */
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Consumer updates
|
|
2
|
+
|
|
3
|
+
Development applications track the published npm `latest` channel. Declare
|
|
4
|
+
`"arcane-os": "latest"` in the application's existing dependency section.
|
|
5
|
+
Preserve an existing package alias where the application needs it; Arcane OS,
|
|
6
|
+
for example, uses `"arcane-sdk": "npm:arcane-os@latest"` to avoid a name collision.
|
|
7
|
+
Pin an application's SDK version only when Roshi explicitly designates it as a
|
|
8
|
+
production build. An exact version recorded in `package-lock.json` records what
|
|
9
|
+
was installed; it does not turn a development application into a production pin.
|
|
10
|
+
|
|
11
|
+
## Development refresh
|
|
12
|
+
|
|
13
|
+
Each application's existing owner performs its refresh through the public npm
|
|
14
|
+
package workflow in its canonical `main` checkout. Coordinate the dependency
|
|
15
|
+
and Git mutation window before cleanup, preserve active processes and other
|
|
16
|
+
owners' work, and establish applicable dependency permissions before removing
|
|
17
|
+
the installed tree. Respect explicit project stops, including Lifeline.
|
|
18
|
+
|
|
19
|
+
1. Keep the SDK declaration at `latest` and preserve `package-lock.json`.
|
|
20
|
+
2. Resolve the exact application path and remove only that application's
|
|
21
|
+
disposable `node_modules`, before pulling. Do not sweep sibling projects or
|
|
22
|
+
shared caches.
|
|
23
|
+
3. Run `git pull` and observe the result. Preserve existing changes and stop the
|
|
24
|
+
affected operation if the pull fails or leaves conflicts.
|
|
25
|
+
4. Run `npm i` through the application's supported install workflow, retaining
|
|
26
|
+
its applicable lifecycle-script options.
|
|
27
|
+
5. Explicitly run `npm update arcane-os` to refresh only the SDK. For an existing
|
|
28
|
+
alias, target its dependency key, such as `npm update arcane-sdk`.
|
|
29
|
+
6. Before materializing managed files or building, obtain npm's published
|
|
30
|
+
`latest` version with a fresh registry lookup, such as
|
|
31
|
+
`npm view arcane-os@latest version --prefer-online`. Read the actual installed
|
|
32
|
+
package version and the lockfile's resolved SDK version. Both must match that
|
|
33
|
+
published version. If the tag advances during the operation, refresh the SDK
|
|
34
|
+
again and report the actual selected version.
|
|
35
|
+
7. Update managed SDK projections only through the application's supported
|
|
36
|
+
public package workflow. Commit the intended declaration, retained updated
|
|
37
|
+
lockfile, managed files, and relevant instructions through its normal owner.
|
|
38
|
+
|
|
39
|
+
A `latest` declaration and `npm i` alone are insufficient: an existing lockfile
|
|
40
|
+
can preserve an older resolution. Keep and update the lockfile instead of
|
|
41
|
+
deleting it and re-resolving unrelated dependencies. Surface installation and
|
|
42
|
+
compatibility failures; do not claim current adoption from a manifest label.
|
|
43
|
+
|
|
44
|
+
This dependency-result comparison is part of the requested refresh. It does
|
|
45
|
+
not authorize application tests, unrelated checks or dependency updates,
|
|
46
|
+
recurring polling, broad cleanup, or production administration. A later release
|
|
47
|
+
can advance `latest`; record the exact version selected by each refresh.
|
|
48
|
+
|
|
49
|
+
## Production builds
|
|
50
|
+
|
|
51
|
+
On Roshi's explicit production designation, record the selected exact SDK
|
|
52
|
+
version consistently in the dependency declaration, lockfile, managed
|
|
53
|
+
projections, and application instructions. Use the supported locked install
|
|
54
|
+
workflow, normally `npm ci`, to reproduce that selection. A production pin
|
|
55
|
+
records reproducibility, not a permanent ceiling on otherwise authorized
|
|
56
|
+
needed updates. Preserve an explicit update deferral or actual compatibility
|
|
57
|
+
constraint.
|
|
58
|
+
|
|
59
|
+
## Feature requests and release notices
|
|
60
|
+
|
|
61
|
+
Inspect the published SDK's public API and documentation before implementing a
|
|
62
|
+
request. If it already supports the requested behavior, tell the requesting
|
|
63
|
+
application owner which API and usage provide it and link directly to the
|
|
64
|
+
relevant documentation. Do not present unpublished source as available support.
|
|
65
|
+
|
|
66
|
+
After a release's npm version and channel are confirmed, notify only existing
|
|
67
|
+
application owners whose requests it addresses. Notify every requester covered
|
|
68
|
+
by matching, similar, or bundled requests when one shared feature serves them.
|
|
69
|
+
Do not send routine release broadcasts to unrelated consumers.
|
|
70
|
+
|
|
71
|
+
Each notice includes the exact published version and channel, requested
|
|
72
|
+
functionality delivered, its relevance, release notes or a changelog link,
|
|
73
|
+
direct API and usage documentation references, and required compatibility or
|
|
74
|
+
adoption steps. Keep publication evidence distinct from application behavior.
|
|
75
|
+
Deduplicate by version and recipient and retain send outcomes in the release
|
|
76
|
+
handoff. Report unreachable owners and resume missing deliveries.
|
|
77
|
+
|
|
78
|
+
After every release and applicable requester notices, update the documentation
|
|
79
|
+
and existing SDK website for all affected public contracts, including releases
|
|
80
|
+
with no requesters. Record those delivery outcomes separately. The SDK owner
|
|
81
|
+
retains this follow-through; application upgrades and tests remain with their
|
|
82
|
+
owners and never gate SDK publication. See [npm publication](publishing.md).
|
package/docs/publishing.md
CHANGED
|
@@ -11,8 +11,12 @@ the npm `dev` dist-tag, while bare numeric stable versions select
|
|
|
11
11
|
|
|
12
12
|
The package version and `publishConfig.tag` must agree exactly: `-dev` uses
|
|
13
13
|
`dev`, and a bare numeric stable version uses `latest`. npm is the canonical
|
|
14
|
-
SDK distribution:
|
|
15
|
-
|
|
14
|
+
SDK distribution: development applications declare `arcane-os` as `latest` and
|
|
15
|
+
invoke its local CLI with `npm exec -- arcane`. An exact application SDK pin
|
|
16
|
+
begins only when Roshi designates that application as a production build.
|
|
17
|
+
Keep the lockfile and explicitly refresh and compare its resolution and the
|
|
18
|
+
installed SDK with npm's published version before materializing or building;
|
|
19
|
+
see [Consumer updates](consumer-updates.md). A separate
|
|
16
20
|
global installer, standalone SDK executable, NuGet package, Homebrew formula,
|
|
17
21
|
or OS package is not part of this release surface.
|
|
18
22
|
|
|
@@ -30,8 +34,8 @@ agreement, and required license notices. One unprivileged producer packs one
|
|
|
30
34
|
artifact with a recorded run id, artifact id, version, and source commit. It
|
|
31
35
|
does not impose byte counts, hashes, digests, provenance receipts, or unrelated
|
|
32
36
|
test suites as ordinary development gates. Broader integration, regression,
|
|
33
|
-
platform
|
|
34
|
-
|
|
37
|
+
and platform work remains separately user selected. Documentation and website
|
|
38
|
+
updates are required post-publication follow-through as described below.
|
|
35
39
|
|
|
36
40
|
`publish-dev.yml` can run only when manually dispatched from `main` in
|
|
37
41
|
`TheWizardNexus/arcane-os-sdk`. Dispatch supplies the exact successful Check
|
|
@@ -59,14 +63,15 @@ The unscoped package installs both `arcane` and `arcane-os`. The short command
|
|
|
59
63
|
is the documented default; `arcane-os` is the collision-safe fallback. npm
|
|
60
64
|
package names are unique, but executable names are not globally reserved.
|
|
61
65
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
66
|
+
External applications consume published npm releases. An unavailable release
|
|
67
|
+
does not authorize them to use an unpublished SDK checkout or tarball. Only
|
|
68
|
+
tasks explicitly collaborating on an SDK update inside this project may use
|
|
69
|
+
the local `npm run pack:local` workflow and its resulting `.tgz`. That local
|
|
70
|
+
workflow can scaffold with `node ./bin/arcane.mjs new ...` and install the tarball
|
|
65
71
|
with `npm install --save-dev --save-exact <path>`. Keep the tarball at the path
|
|
66
|
-
recorded by `package-lock.json
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
it may be linked.
|
|
72
|
+
recorded by `package-lock.json` for subsequent `npm ci`. Arcane reads the installed
|
|
73
|
+
package's name and version against the root dependency declaration. A local
|
|
74
|
+
directory `file:` install is unsupported because it may be linked.
|
|
70
75
|
|
|
71
76
|
The npm package already has its trusted-publishing relationship. Each later
|
|
72
77
|
release therefore follows the same direct selected-artifact path: push the
|
|
@@ -77,9 +82,11 @@ dist-tag after publication. Never rebuild or repack the artifact under
|
|
|
77
82
|
publication authority, and never substitute a different source revision.
|
|
78
83
|
|
|
79
84
|
Generated app CI uses `npm ci --ignore-scripts`, so its lock must exist and its
|
|
80
|
-
dependency source must be reachable by the runner.
|
|
81
|
-
|
|
82
|
-
|
|
85
|
+
dependency source must be reachable by the runner. External application CI uses
|
|
86
|
+
the published registry package recorded in the committed lockfile. A locked
|
|
87
|
+
install reproduces that selected version; it does not establish that the SDK is
|
|
88
|
+
currently npm's `latest`. Complete the development refresh before preparing and
|
|
89
|
+
committing managed application files and the updated lockfile for CI.
|
|
83
90
|
|
|
84
91
|
## Reusable application release workflow
|
|
85
92
|
|
|
@@ -111,8 +118,23 @@ GitHub release, and no release creates a Git branch for an npm dist-tag.
|
|
|
111
118
|
|
|
112
119
|
## Documentation publication
|
|
113
120
|
|
|
114
|
-
|
|
115
|
-
|
|
121
|
+
After every successful npm release and its applicable requester notices, the
|
|
122
|
+
SDK owner updates documentation and the existing SDK website for all added,
|
|
123
|
+
removed, or modified features, APIs, members, methods, and affected public
|
|
124
|
+
contracts. Include the relevant signatures, inputs, outputs, defaults,
|
|
125
|
+
lifecycle, errors, platform availability, and usage examples. This standing
|
|
126
|
+
duty also applies when a release has no requesting applications. Record the
|
|
127
|
+
actual documentation and website delivery outcomes in the release handoff and
|
|
128
|
+
report any unfinished work. See [Consumer updates](consumer-updates.md) for
|
|
129
|
+
request intake and notice requirements.
|
|
130
|
+
|
|
131
|
+
Documentation and website content outside the selected npm payload does not
|
|
132
|
+
block npm publication. Prepare and deliver those updates through the existing
|
|
133
|
+
documentation/site workflow after publication; do not wait for consumer
|
|
134
|
+
adoption or application tests. This duty does not authorize ChatGPT Sites or
|
|
135
|
+
production-server administration.
|
|
136
|
+
|
|
137
|
+
The Pages job checks out the selected `main` revision without persistent
|
|
116
138
|
credentials and uploads only the static `site/` tree. It does not automatically
|
|
117
139
|
run the SDK test suite, checks, generators, or repository build code.
|
|
118
140
|
|
|
@@ -128,5 +150,6 @@ One producer creates the tarball. Publication names that producer's exact Check
|
|
|
128
150
|
run, artifact, and version and does not rebuild from a later checkout. The
|
|
129
151
|
release workflow checks only the publication contract and required legal
|
|
130
152
|
inventory for that selected output.
|
|
131
|
-
Platform matrices
|
|
132
|
-
|
|
153
|
+
Platform matrices and full product regressions remain separate user-selected
|
|
154
|
+
operations. Documentation and the existing website are required post-release
|
|
155
|
+
follow-through, with their outcomes recorded separately from npm publication.
|
package/docs/reference/README.md
CHANGED
|
@@ -17,9 +17,14 @@ high-level page links to the relevant deep section instead of repeating it.
|
|
|
17
17
|
Install the SDK in your application:
|
|
18
18
|
|
|
19
19
|
```sh
|
|
20
|
-
npm
|
|
20
|
+
npm pkg set dependencies.arcane-os=latest
|
|
21
|
+
npm install
|
|
21
22
|
```
|
|
22
23
|
|
|
24
|
+
The development declaration tracks the current `latest` channel. The lockfile
|
|
25
|
+
and installed package record the exact version actually selected for the
|
|
26
|
+
workspace.
|
|
27
|
+
|
|
23
28
|
For your first AI call, follow the [TWiN Cloud quick start](ai/twin-cloud.md).
|
|
24
29
|
For local models, native browser voices or cloud speech, follow the [browser speech guide](ai/browser-speech.md)
|
|
25
30
|
and the complete [browser AI demo](https://github.com/TheWizardNexus/arcane-os-sdk/tree/main/examples/wasm-ai-demo).
|
|
@@ -38,14 +43,19 @@ alone does not make bare module names resolve in a browser.
|
|
|
38
43
|
| Use the `arcane` command | [CLI reference](cli.md) |
|
|
39
44
|
| Install an app and cache selected resources offline | [Progressive web applications](pwa.md) |
|
|
40
45
|
| Generate named browser imports or inspect the selected physical runtime | [`arcane import-map`](cli.md#arcane-import-map) and [browser runtime delivery](protocols.md#browser-runtime-delivery) |
|
|
46
|
+
| Connect an application through the shared WebSocket transport | [Shared WebSocket clients](websocket-client.md) |
|
|
41
47
|
| Choose browser, native, cloud, or cross-host behavior | [Availability and normalization](availability-and-normalization.md) |
|
|
42
48
|
| Import a shipped renderer module | [Runtime module catalog](runtime-modules.md) |
|
|
43
49
|
| Use a shared entity | [Runtime entity modules](runtime-entities.md) and [exact export contracts](core/arcane-entities.md) |
|
|
44
50
|
| Load a reusable HTML component | [Runtime component catalog](runtime-components.md) |
|
|
45
51
|
| Submit complete speech parts immediately and play them in order | [`SpeechPlayback`](runtime-modules.md#speechplaybackjs) and the [basic browser example](ai/browser-speech.md#play-a-complete-array-with-speechplayback) |
|
|
46
52
|
| Call `globalThis.Arcane` | [Arcane Core API](core/arcane-api.md) |
|
|
53
|
+
| Connect a browser to one Core transport | [Core browser client](core-client.md) |
|
|
54
|
+
| Compose native Core dispatch, service lifecycle, and stdio hosting | [Native Core runtime](core-runtime.md) |
|
|
55
|
+
| Build an installed-SDK portable Core payload | [Portable Core packaging](core-native-packaging.md) |
|
|
47
56
|
| Subscribe to native events | [Arcane event reference](core/arcane-events.md) |
|
|
48
57
|
| Use provider-neutral AI lifecycle, chat, speech, persistence, or document context | [Normalized AI](#normalized-ai) |
|
|
58
|
+
| Choose a normalized typed browser decision surface | [Browser typed decisions](ai/browser-decisions.md) |
|
|
49
59
|
| Run a caller-selected local LLM in the browser | [Browser-WASM local AI](ai/browser-wasm.md) |
|
|
50
60
|
| Run caller-selected Whisper/Kokoro, native browser recognition/voices or cloud TTS | [Browser speech providers](ai/browser-speech.md) |
|
|
51
61
|
| Send TWiN chat, System One state/questions or image requests | [TWiN Cloud guide](ai/twin-cloud.md) |
|
|
@@ -58,9 +58,12 @@ false when a probe throws; SDK branching continues through its owning error path
|
|
|
58
58
|
policy, package-owned model policy, platform support, and dependency readiness
|
|
59
59
|
are independent checks.
|
|
60
60
|
|
|
61
|
-
The current
|
|
62
|
-
where listed.
|
|
63
|
-
|
|
61
|
+
The current executable host/target matrix covers Microsoft NT, Linux, and
|
|
62
|
+
Android where listed. This SDK exposes no macOS executable target, native
|
|
63
|
+
bridge, artifact, or run contract; WebKitGTK availability must not be
|
|
64
|
+
generalized to macOS. The reusable Node Core runtime, framed stdio transport,
|
|
65
|
+
host composition, and portable payload can be used by a separately composed
|
|
66
|
+
macOS host, but they do not supply that native platform host or bridge.
|
|
64
67
|
|
|
65
68
|
## Capability-first matrix
|
|
66
69
|
|
|
@@ -68,7 +71,9 @@ version; WebKitGTK availability must not be generalized to macOS.
|
|
|
68
71
|
| --- | --- | --- | --- |
|
|
69
72
|
| Scaffold, inspect, test, package, bundle, build, verify, or run an app | `arcane` CLI or `arcane-os` package functions | **Node**; native targets invoke one explicit provider | CLI events and SDK errors/results are normalized by versioned SDK contracts. Tests and checks run only when explicitly selected; verification is separate and selected-output-specific. |
|
|
70
73
|
| Publish application events or review a complete event history | `arcane-os/event-manager` | **Node** and **Browser**; optional DOM capture needs a browser DOM or compatible host | Live listeners receive original arguments. Ordinary `secure:false` recording preserves complete URLs, public details, and captured stack text in deeply frozen `arcane-event-stack/1` snapshots while credential-named fields remain redacted. The stack format is local diagnostic data, not a host transport. |
|
|
74
|
+
| Share one native WebSocket by exact URI and ordered protocols | `arcane-os/websocket-client` | **Node** and **Browser**, using the host's native `WebSocket` | The selected upstream `ws-share` instance owns the connection pool and global observation. Native socket messages and events remain unchanged; there is no reconnect, heartbeat, application envelope, storage, or fallback. |
|
|
71
75
|
| Build browser UI and app-local behavior | `/arcane/modules/*.js`, shared entities, and components | **Browser**; many modules also run inside every native renderer | Pure modules own their result contracts. Modules that call `Arcane` inherit the bridge boundary described below. |
|
|
76
|
+
| Score caller-supplied typed options with Laya or Julia | `arcane-os/ai/browser-decisions` | **Browser** only; caller-selected upstream runtime/model and backend | Typed decisions retain complete rows and return typed values, scores, probabilities, and diagnostics in caller order. This surface is separate from chat and speech and does not silently change model, precision, backend, or family. |
|
|
72
77
|
| Select and observe independent LLM/STT/TTS roles | `/arcane/modules/AIProviderRuntime.js` and `AIRuntimeState.js` | **Cross-host** controller/state; registered providers retain their own host requirements | Required/projected provider members, route/configuration records, and status fields; per-role lifecycle, cancellation, stream cleanup, sticky state, and startup barriers are normalized. `localOnly` creates no fallback. |
|
|
73
78
|
| Run a caller-selected local LLM entirely in a browser renderer | `arcane-os/ai/browser-wasm` through `createArcaneAI()` | **Browser** only; secure context, WebAssembly, and OPFS/DBOPFS; full WebGPU offload by default or explicit CPU with `gpuLayers:0` | The public AI API module normalizes multi-model lifecycle, status, complete all-choice streaming, cancellation, exact ordered structural tool-call visibility, and session persistence. Model sources are canonical ordered file descriptors; licenses and model choice remain application policy. |
|
|
74
79
|
| Run caller-selected Whisper or Kokoro in a browser renderer | `arcane-os/ai/browser-speech` registered with `AIProviderRuntime` | **Browser** only; DBOPFS, Web Locks, Workers, Fetch/object URLs, and a caller-supplied self-contained runtime/model closure are required | Independent provider/2 lifecycle and status, with `auto`, `webnn-npu`, `webgpu`, or `wasm` execution. Whisper owns one Worker/session; Kokoro defaults to four and accepts one through four. Complete model/runtime selection, offline behavior, cancellation, Worker teardown, and request/result shapes are normalized. No runtime/model content or cloud fallback is supplied. |
|
|
@@ -81,6 +86,9 @@ version; WebKitGTK availability must not be generalized to macOS.
|
|
|
81
86
|
| Print rendered editor content or file previews | `/arcane/modules/PrintView.js`, editor `print()`, and file/history `printPreview()` | **Browser / native WebView** with DOM and browser printing | Complete current rendered content, fonts, and resolved media are captured for print. Explicit requests await view preparation; native Print captures synchronously. Shared `print.css` supplies a light paper palette and one-inch margins; snapshots stay owned through `afterprint`. |
|
|
82
87
|
| Search an app-owned document corpus for explicit chat context | `/arcane/modules/DBOPFSDocumentLibrary.js` | **Browser** or compatible injected DBOPFS adapter | Generation/manifest completion, complete lexical search, partial read failures, and untrusted context labels are normalized. Construction does not search; an explicitly wired context builder performs retrieval for each prepared chat send. |
|
|
83
88
|
| Read host identity, capabilities, storage, preferences, appearance, or platform state | `globalThis.Arcane` | **Cross-host** where the method is implemented and admitted | Promise behavior and `Arcane.Error` are normalized. Result fields are normalized unless the method explicitly documents a platform-dependent snapshot. |
|
|
89
|
+
| Connect browser code to an existing Core transport | `arcane-os/core/client` and `arcane-os/core/contracts` | **Browser / native WebView** with an admitted transport; explicit development HTTP remains opt-in | The reusable client owns `arcane/1` correlation, readiness, cancellation, events, facades, and complete native error fidelity. It supplies no service, policy, or host process. |
|
|
90
|
+
| Compose native Core services and framed stdio hosting | `arcane-os/core/runtime`, `arcane-os/core/stdio`, and `arcane-os/core/host` | **Node** on Windows, Linux, or macOS; the composing native host supplies its process and bridge | The SDK owns reusable dispatch, per-service lifecycle, request/service work ownership, cooperative cancellation, drain, and stdio framing. Product identity, policy, privileges, models, storage, and services remain host-owned. |
|
|
91
|
+
| Assemble a portable Core payload from the installed SDK | `arcane-os/native/portable-provider` | **Node**; portable platform selection is currently Windows or Linux, while separately composed hosts may use the payload | The provider copies the selected app, dependencies, installed SDK runtime, Core client/runtime, and explicit service composition into a non-executable directory. It uses no `arcaneRoot` by default and does not prove host behavior or supply a platform bridge. |
|
|
84
92
|
| Use local AI without coupling app code to Ollama HTTP | `Arcane.localAI`, `Arcane.ai`, or `/arcane/modules/Ollama.js` | Primarily **Native**; Android exposes a narrower admitted inference projection | Admission, errors, and managed-operation events are normalized. Direct Ollama response envelopes remain **Provider-native**. |
|
|
85
93
|
| Use TWiN Cloud from the renderer profile | `/arcane/modules/AI.js` | **Cloud** from an allowed browser/native renderer | High-level chat behavior is normalized by the module. The TWiN access key authenticates remote LLM chat; raw provider diagnostics remain provider-specific. No automatic cloud fallback is inferred from local failure. |
|
|
86
94
|
| Send one TWiN Cloud request with an explicit key and model | `fetchRequest` from `arcane-os/ai/twin-cloud` | **Node** and **Browser**, using standard Fetch and a remote HTTPS provider | Keeps complete messages and returns the full parsed provider JSON. Rejected Fetch and HTTP 529 share three retries after 3000 ms; HTTP 429 overload retains unlimited three-second retries. Optional `onRetry` reports waiting/requesting phases without blocking recovery. Shared structured-output mapping and cancellation match browser TWiN transport. No browser profile, AI/user singleton, DBOPFS, or retained request history is created. |
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Core browser client
|
|
2
2
|
|
|
3
3
|
`arcane-os/core/client` is the browser-safe `arcane/1` RPC owner. It exports
|
|
4
|
-
`createCoreClient`, `createCoreFacade` and
|
|
4
|
+
`createCoreClient`, `createCoreFacade`, `installCoreClient` and
|
|
5
|
+
`getInstalledCoreClient`.
|
|
5
6
|
`arcane-os/core/contracts` exports `CORE_PROTOCOL`, `CORE_READY_EVENTS`,
|
|
6
7
|
`CORE_FRAME_CONTRACTS`, `CORE_METHOD_CONTRACTS`, `CoreError` and
|
|
7
8
|
`serializeCoreError`. Contracts describe transport and existing method shapes;
|
|
@@ -25,6 +26,23 @@ remain available through this facade; their implementations and availability
|
|
|
25
26
|
belong to the selected host. No repository, app list, model selection or service
|
|
26
27
|
implementation is installed by importing this client.
|
|
27
28
|
|
|
29
|
+
An application using a host-installed client can read it without installing or
|
|
30
|
+
replacing anything:
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
import {getInstalledCoreClient} from 'arcane-os/core/client';
|
|
34
|
+
|
|
35
|
+
const client=getInstalledCoreClient();
|
|
36
|
+
// null means this global has no live SDK-installed client.
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`getInstalledCoreClient(global=globalThis)` returns the exact live SDK-installed
|
|
40
|
+
client, including one installed by classic document-created injection. It does
|
|
41
|
+
not connect, install a client or replace an existing `Arcane` object. Applications
|
|
42
|
+
need not inspect private installation fields. It returns `null` after that
|
|
43
|
+
installation closes or fails. A
|
|
44
|
+
foreign application's `Arcane` object is not evidence of an SDK installation.
|
|
45
|
+
|
|
28
46
|
The native adapters select WebView2, WebKitGTK or Android WebView. Ordinary
|
|
29
47
|
browsers remain `standalone`; they do not probe a local server. Development
|
|
30
48
|
HTTP is selected only by the existing explicit `__ARCANE_DEV_HTTP__` flag.
|
|
@@ -41,6 +59,14 @@ without callable `send(frame)` remains disconnected and reports
|
|
|
41
59
|
`ARCANE_TRANSPORT_INVALID`; it does not announce `transport.ready`.
|
|
42
60
|
`autoConnect:false` defers connection until `connect()` or the first invocation.
|
|
43
61
|
|
|
62
|
+
`replayRuntimeState:true` sends one `runtime.replay` control after the transport's
|
|
63
|
+
receive listener and `transport.ready` are established. The default is `false`,
|
|
64
|
+
preserving existing hosts. The send is observed without delaying page startup;
|
|
65
|
+
delivery errors reach `onError`. A host supporting this control supplies its
|
|
66
|
+
current dispatcher and service state for a newly connected document, without
|
|
67
|
+
restarting services. Repeated `connect()` calls on the same connection do not
|
|
68
|
+
send another replay. See [runtime replay](core-runtime.md#state-and-frames).
|
|
69
|
+
|
|
44
70
|
`client.invoke(method, parameters, {signal, timeoutMs})` returns the actual
|
|
45
71
|
response result or rejects with `CoreError`. Parameters, result fields and
|
|
46
72
|
event data retain their complete supplied content. JSON encoding belongs only
|
|
@@ -75,6 +101,20 @@ native callbacks, timers and pending renderer requests; it does not kill Core.
|
|
|
75
101
|
Call `stop()` to remove an individual subscription and `client.close()` when
|
|
76
102
|
the client owner ends its lifetime.
|
|
77
103
|
|
|
104
|
+
For an actual terminal child-process or transport failure, the host calls
|
|
105
|
+
`client.failTransport(error)`. Installed clients also own the native callback
|
|
106
|
+
`globalThis.__arcaneTransportFailed(error)`. This boundary rejects pending RPCs
|
|
107
|
+
and WebKit acknowledgements with the complete actual `CoreError`, reports that
|
|
108
|
+
failure once, releases callbacks/subscriptions/timers and rejects future
|
|
109
|
+
invocations with the same failure. It sends no cancellation frames to a failed
|
|
110
|
+
transport and does not invent response identifiers or results. A normal close
|
|
111
|
+
still uses cancellation rather than reporting a transport failure.
|
|
112
|
+
|
|
113
|
+
Closing or failing an installation restores its previous `Arcane` object and
|
|
114
|
+
native callbacks only when those globals still belong to that installation.
|
|
115
|
+
Another owner's later replacements remain unchanged. The read-only accessor
|
|
116
|
+
returns `null` before the installed failure is reported to `onError`.
|
|
117
|
+
|
|
78
118
|
Errors retain complete native fields, including diagnostics and stack/cause
|
|
79
119
|
information. WebView2 and Android bridge failures retain native error codes,
|
|
80
120
|
messages and diagnostic fields; the request method and transport are added
|
|
@@ -90,7 +130,8 @@ transport/event-owner failures outside individual request promises.
|
|
|
90
130
|
import {createCoreClassicSource} from 'arcane-os/core/classic-source';
|
|
91
131
|
|
|
92
132
|
const source=await createCoreClassicSource({
|
|
93
|
-
eventOwnerModuleURL:'/arcane/sdk/event-manager.mjs'
|
|
133
|
+
eventOwnerModuleURL:'/arcane/sdk/event-manager.mjs',
|
|
134
|
+
replayRuntimeState:true
|
|
94
135
|
});
|
|
95
136
|
// Supply source to the native host's existing classic-script injection owner.
|
|
96
137
|
```
|
|
@@ -100,6 +141,8 @@ installed SDK. It emits the same implementation as a classic script, without
|
|
|
100
141
|
copying another client or adding a bundler. The caller supplies the actual
|
|
101
142
|
served SDK event-manager URL; the example URL must match its selected layout.
|
|
102
143
|
That module and its existing managed import map must be included by packaging.
|
|
144
|
+
Select `replayRuntimeState:true` only when composing a host with the current
|
|
145
|
+
runtime replay control; omitting it preserves the existing no-replay default.
|
|
103
146
|
|
|
104
147
|
The generated facade, native receive callback and current-state methods install
|
|
105
148
|
synchronously. When the shared event owner already exists, it is reused
|
|
@@ -107,6 +107,16 @@ Events have `{protocol, type:'event', event, data, time}`:
|
|
|
107
107
|
- `core.service.state`: a service's name, state and serialized error when present.
|
|
108
108
|
- `core.error`: a lifecycle or transport diagnostic.
|
|
109
109
|
|
|
110
|
+
A newly connected document may send
|
|
111
|
+
`{protocol:'arcane/1', type:'control', control:'runtime.replay'}`. The runtime
|
|
112
|
+
publishes one current snapshot: `core.ready` only when the dispatcher is ready,
|
|
113
|
+
then `core.state`, followed by `core.service.state` for every registered service.
|
|
114
|
+
Complete service errors and active request state are retained. Replay does not
|
|
115
|
+
start or restart services, dispatch a request, poll readiness or create a second
|
|
116
|
+
lifecycle owner. The runtime completes that snapshot before publishing lifecycle
|
|
117
|
+
changes triggered reentrantly by its listeners. A draining runtime reports its
|
|
118
|
+
draining state rather than announcing readiness.
|
|
119
|
+
|
|
110
120
|
Service states are `registered`, `starting`, `ready`, `failed`, `draining` and
|
|
111
121
|
`closed`. Dispatcher states are `created`, `ready`, `draining` and `closed`.
|
|
112
122
|
Complete diagnostics belong in the host's developer-facing error handling, not
|