arcane-os 0.4.2 → 0.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arcane-os",
3
- "version": "0.4.2",
3
+ "version": "0.5.0",
4
4
  "description": "Arcane OS JavaScript SDK, project-local CLI, browser runtime, and repository-portable application packager.",
5
5
  "type": "module",
6
6
  "main": "./src/index.mjs",
@@ -2053,6 +2053,21 @@
2053
2053
  }
2054
2054
 
2055
2055
  function determinateProgress(){
2056
+ const totalBytes=role?.progress?.totalBytes;
2057
+ const loadedBytes=role?.progress?.loadedBytes;
2058
+ if(
2059
+ Number.isFinite(totalBytes)
2060
+ &&totalBytes>0
2061
+ &&Number.isFinite(loadedBytes)
2062
+ &&loadedBytes>=0
2063
+ ){
2064
+ return {
2065
+ completed:Math.min(loadedBytes,totalBytes),
2066
+ total:totalBytes,
2067
+ unit:'bytes',
2068
+ byteTelemetry:true
2069
+ };
2070
+ }
2056
2071
  const total=Number(role?.progress?.total);
2057
2072
  const completed=Number(role?.progress?.completed);
2058
2073
  const unit=typeof role?.progress?.unit==='string'&&role.progress.unit
@@ -2068,10 +2083,97 @@
2068
2083
  return {
2069
2084
  completed,
2070
2085
  total,
2071
- unit
2086
+ unit,
2087
+ byteTelemetry:false
2072
2088
  };
2073
2089
  }
2074
2090
 
2091
+ function formatBytes(value){
2092
+ if(!Number.isFinite(value)||value<0){
2093
+ return '';
2094
+ }
2095
+ const bytes=value;
2096
+ const units=['B','KB','MB','GB'];
2097
+ let unitIndex=0;
2098
+ let amount=bytes;
2099
+ while(amount>=1000&&unitIndex<units.length-1){
2100
+ amount/=1000;
2101
+ unitIndex+=1;
2102
+ }
2103
+ const decimals=unitIndex===0||amount>=100
2104
+ ?0
2105
+ :amount>=10
2106
+ ?1
2107
+ :2;
2108
+ return `${Number(amount.toFixed(decimals))} ${units[unitIndex]}`;
2109
+ }
2110
+
2111
+ function formatPercentage(loadedBytes,totalBytes){
2112
+ const percentage=Math.min(100,Math.max(0,loadedBytes/totalBytes*100));
2113
+ if(loadedBytes<=0){
2114
+ return '0%';
2115
+ }
2116
+ if(loadedBytes>=totalBytes){
2117
+ return '100%';
2118
+ }
2119
+ return `${Math.min(99.9,percentage).toFixed(1)}%`;
2120
+ }
2121
+
2122
+ function approximateDuration(value){
2123
+ if(!Number.isFinite(value)||value<0){
2124
+ return '';
2125
+ }
2126
+ const suppliedSeconds=value;
2127
+ const seconds=Math.ceil(suppliedSeconds);
2128
+ if(seconds<1){
2129
+ return '<1s';
2130
+ }
2131
+ if(seconds<60){
2132
+ return `${seconds}s`;
2133
+ }
2134
+ const minutes=Math.floor(seconds/60);
2135
+ const remainingSeconds=seconds%60;
2136
+ if(minutes<60){
2137
+ return `${minutes}m ${String(remainingSeconds).padStart(2,'0')}s`;
2138
+ }
2139
+ const hours=Math.floor(minutes/60);
2140
+ const remainingMinutes=minutes%60;
2141
+ return `${hours}h ${String(remainingMinutes).padStart(2,'0')}m`;
2142
+ }
2143
+
2144
+ function activeTransferText(){
2145
+ const active=role?.progress?.activeTransfers;
2146
+ const limit=role?.progress?.transferLimit;
2147
+ const mode=typeof role?.progress?.transferMode==='string'
2148
+ ?role.progress.transferMode.trim().toLowerCase()
2149
+ :'';
2150
+ if(
2151
+ !Number.isSafeInteger(active)
2152
+ ||active<0
2153
+ ||!Number.isSafeInteger(limit)
2154
+ ||limit<1
2155
+ ||!mode
2156
+ ){
2157
+ return '';
2158
+ }
2159
+ if(mode==='single'){
2160
+ return active>0?'single transfer active':'single transfer idle';
2161
+ }
2162
+ if(mode==='probing'){
2163
+ return 'checking parallel Range support';
2164
+ }
2165
+ const noun=['range','ranges'].includes(mode)
2166
+ ?'range transfer worker'
2167
+ :['shard','shards'].includes(mode)
2168
+ ?'shard transfer worker'
2169
+ :['file','files'].includes(mode)
2170
+ ?'file transfer worker'
2171
+ :['single','single-file','monolith'].includes(mode)
2172
+ ?'single-file transfer worker'
2173
+ :`${mode.replaceAll('-',' ')} transfer worker`;
2174
+ return `${active} of ${limit} ${noun}${limit===1?'':'s'} active`;
2175
+ }
2176
+
2075
2177
  function progressElapsedText(){
2076
2178
  const elapsedMilliseconds=Number(role?.progress?.elapsedMs);
2077
2179
  if(!Number.isFinite(elapsedMilliseconds)||elapsedMilliseconds<0){
@@ -2100,16 +2202,32 @@
2100
2202
  `Loading ${modelDisplayName()} through the Arcane SDK`,
2101
2203
  phase
2102
2204
  ];
2103
- if(measured){
2104
- if(
2105
- phaseValue==='download'
2106
- &&measured.unit==='files'
2107
- &&measured.completed<measured.total
2108
- ){
2109
- parts.push(`part ${measured.completed+1} of ${measured.total}`);
2205
+ if(measured?.byteTelemetry){
2206
+ const remainingBytes=Math.max(0,measured.total-measured.completed);
2207
+ parts.push(formatPercentage(measured.completed,measured.total));
2208
+ parts.push(
2209
+ `${formatBytes(measured.completed)} of ${formatBytes(measured.total)} downloaded`
2210
+ );
2211
+ parts.push(`${formatBytes(remainingBytes)} remaining`);
2212
+ const bytesPerSecond=role?.progress?.bytesPerSecond;
2213
+ if(Number.isFinite(bytesPerSecond)&&bytesPerSecond>=0){
2214
+ parts.push(`${formatBytes(bytesPerSecond)}/s`);
2110
2215
  }else{
2111
- parts.push(`${measured.completed} of ${measured.total} ${measured.unit}`);
2216
+ parts.push('Calculating speed');
2217
+ }
2218
+ if(remainingBytes>0){
2219
+ const eta=approximateDuration(role?.progress?.etaSeconds);
2220
+ parts.push(eta?`About ${eta} remaining`:'Estimating time remaining');
2112
2221
  }
2222
+ }else if(measured){
2223
+ const suffix=phaseValue==='download'&&measured.unit==='files'
2224
+ ?'files complete'
2225
+ :measured.unit;
2226
+ parts.push(`${measured.completed} of ${measured.total} ${suffix}`);
2227
+ }
2228
+ const transfers=activeTransferText();
2229
+ if(transfers){
2230
+ parts.push(transfers);
2113
2231
  }
2114
2232
  const elapsed=progressElapsedText();
2115
2233
  if(elapsed){
@@ -2118,7 +2236,9 @@
2118
2236
  parts.push('Still working');
2119
2237
  }
2120
2238
  }
2121
- parts.push('The first activation can take several minutes; keep this tab open');
2239
+ if(!measured?.byteTelemetry){
2240
+ parts.push('The first activation can take several minutes; keep this tab open');
2241
+ }
2122
2242
  return parts.join(' · ');
2123
2243
  }
2124
2244
 
@@ -2128,16 +2248,19 @@
2128
2248
  if(!visible){
2129
2249
  progress.removeAttribute('value');
2130
2250
  progress.removeAttribute('max');
2251
+ progress.removeAttribute('aria-valuetext');
2131
2252
  return;
2132
2253
  }
2133
2254
  const measured=determinateProgress();
2134
2255
  if(!measured){
2135
2256
  progress.removeAttribute('value');
2136
2257
  progress.removeAttribute('max');
2258
+ progress.setAttribute('aria-valuetext',progressMessage());
2137
2259
  return;
2138
2260
  }
2139
2261
  progress.max=measured.total;
2140
2262
  progress.value=measured.completed;
2263
+ progress.setAttribute('aria-valuetext',progressMessage());
2141
2264
  }
2142
2265
 
2143
2266
  function render(){
@@ -278,9 +278,9 @@
278
278
  summary.setAttribute('role','alert');
279
279
  summary.textContent=userManaged
280
280
  ?guidanceMessage
281
- ||'Review the unavailable user-managed Android service, then Retry or open Profile and switch the affected option to OpenAI.'
281
+ ||'Review the unavailable user-managed Android service, then Retry or open Profile and switch the language-model option to TWiN Cloud.'
282
282
  :browser
283
- ?'Start the unavailable local service yourself and Retry, or open Profile, switch each affected option to OpenAI, and enter your OpenAI API key.'
283
+ ?'Start the unavailable local service yourself and Retry, or open Profile, switch the language-model option to TWiN Cloud, and enter your TWiN access key.'
284
284
  :'Arcane could not recover every selected managed local service. Retry or open Arcane Settings > Local AI.';
285
285
  return report;
286
286
  }
@@ -917,7 +917,9 @@ class AI {
917
917
  // This is the enum section for inference configuration
918
918
  #service = {
919
919
  baseURL: {
920
- OPENAI: 'https://api.openai.com/v1'
920
+ // OPENAI remains the legacy route identifier for compatibility;
921
+ // remote LLM chat is provided by TWiN Cloud.
922
+ OPENAI: 'https://inference.do-ai.run/v1'
921
923
  },
922
924
  sttURL: {
923
925
  LOCAL_SPEACH: 'http://127.0.0.1:8011/v1',
@@ -944,7 +946,7 @@ class AI {
944
946
  }
945
947
 
946
948
  #models = {
947
- OPENAI:'gpt-4o'
949
+ OPENAI:'openai-gpt-oss-120b'
948
950
  }
949
951
 
950
952
  #sttModels = {
@@ -1155,11 +1157,14 @@ class AI {
1155
1157
 
1156
1158
  // Browser-delivered framework code must not contain provider credentials.
1157
1159
  // The selected host, application, or user profile supplies one at runtime.
1158
- get license(){
1159
- return this.#license || globalThis.arcane?.config?.openAI?.apiKey || '';
1160
+ get twinKey(){
1161
+ return this.#license
1162
+ ||globalThis.arcane?.config?.twinCloud?.accessKey
1163
+ ||globalThis.arcane?.config?.openAI?.apiKey
1164
+ ||'';
1160
1165
  }
1161
-
1162
- set license(value){
1166
+
1167
+ set twinKey(value){
1163
1168
  this.#license=typeof value==='string' ? value.trim():'';
1164
1169
  this.#retainLegacyLLMReadiness(
1165
1170
  this.#reconcileLegacyLLMReadiness()
@@ -1170,6 +1175,17 @@ class AI {
1170
1175
  return this.#license;
1171
1176
  }
1172
1177
 
1178
+ // Retain the established credential property while consumers move their
1179
+ // user-facing profile field to the TWiN key name.
1180
+ get license(){
1181
+ return this.twinKey;
1182
+ }
1183
+
1184
+ set license(value){
1185
+ this.twinKey=value;
1186
+ return this.#license;
1187
+ }
1188
+
1173
1189
  #legacyLLMCapability(providerId){
1174
1190
  if(providerId==='OPENAI'){
1175
1191
  return this.llmService==='OPENAI'
@@ -718,12 +718,18 @@ function guidance(mode,slots){
718
718
 
719
719
  const services=recoveryServices(slots);
720
720
  const profileSettings=affectedSlots.map(slot=>slot+'Provider');
721
+ const twinCloudOption=affectedSlots.includes('llm')
722
+ ?' Alternatively, switch the Profile language-model setting to TWiN Cloud and enter a TWiN access key.'
723
+ :'';
721
724
  const message=mode==='browser'
722
725
  ?browserManualInstruction(services)
723
- +' Alternatively, switch the affected Profile setting to OpenAI and enter an OpenAI API/license key. Arcane will not switch providers automatically.'
726
+ +twinCloudOption
727
+ +' Arcane will not switch providers automatically.'
724
728
  :mode===USER_MANAGED_LOOPBACK_PROVIDER_MODE
725
729
  ?userManagedLoopbackInstruction(services)
726
- +' Arcane does not install, start, repair, pull, or otherwise manage this service or its models. Alternatively, switch the affected Profile setting to OpenAI. Arcane will not switch providers automatically.'
730
+ +' Arcane does not install, start, repair, pull, or otherwise manage this service or its models.'
731
+ +twinCloudOption
732
+ +' Arcane will not switch providers automatically.'
727
733
  :'Arcane could not recover the selected local AI service. Retry or review Local AI in Arcane Settings. Arcane will not switch providers automatically.';
728
734
 
729
735
  return completeResult({