arcane-os 0.65.0 → 0.66.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 +24 -0
- package/README.md +1 -1
- package/browser-runtime/ai/browser-decisions.mjs +3 -2
- package/browser-runtime/ai/decision-runtime.mjs +14 -24
- package/docs/publishing.md +39 -0
- package/docs/reference/README.md +7 -1
- package/docs/reference/ai/browser-decisions.md +7 -4
- package/docs/reference/core-repositories.md +122 -3
- package/docs/reference/git-text-snapshot.md +15 -1
- package/docs/reference/inventory/package-api.json +34 -6
- package/docs/reference/inventory/runtime-modules.json +5 -4
- package/docs/reference/local-ai.md +7 -0
- package/docs/reference/local-whisper.md +7 -1
- package/docs/reference/mcp-stdio.md +1 -1
- package/docs/reference/native-speech.md +6 -0
- package/docs/reference/sdk-api.md +96 -4
- package/package.json +1 -1
- package/src/core/repositories.mjs +12 -8
- package/src/git-identity.mjs +132 -0
- package/src/git-text-snapshot.mjs +5 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.66.0
|
|
4
|
+
|
|
5
|
+
- Add `readGitIdentity` from `arcane-os/core/repositories` for separate global,
|
|
6
|
+
repository-local and effective Git configuration observations. Preserve full
|
|
7
|
+
name/email/githubUser values, empty versus unset fields, configured includes,
|
|
8
|
+
joined cancellation and complete process/observer failures.
|
|
9
|
+
- Add optional construction-time `gitIdentity: {name, email, username}` to
|
|
10
|
+
repository workspaces and bare text snapshots. Explicit author fields apply
|
|
11
|
+
only to child Git configuration/environment; omitted fields retain ordinary
|
|
12
|
+
inheritance. Username is a credential hint, not authenticated-account proof.
|
|
13
|
+
- Preserve complete selected text, existing clone/pull/commit/push behavior,
|
|
14
|
+
ordered workspace operations and shared snapshot acquisition. No Git config
|
|
15
|
+
file, remote, credentials, application preferences or data is rewritten.
|
|
16
|
+
- Preserve original process errors when an observer failure is already included,
|
|
17
|
+
and report non-UTF-8 Git identity output with its complete raw diagnostics
|
|
18
|
+
instead of returning substituted identity text.
|
|
19
|
+
- Honor an explicit browser decision `dtype` while preserving the existing
|
|
20
|
+
Laya FP16 and Julia FP32 defaults. Share the complete tensor-record preparation
|
|
21
|
+
with the separately developing native owner; this does not deliver native Laya.
|
|
22
|
+
- Keep the dependency tree unchanged. Native Laya and target-bound repository
|
|
23
|
+
publication remain separate work. Retained compatible native assets continue
|
|
24
|
+
to use matching numeric-release routes; optional NPU upload follow-through
|
|
25
|
+
remains separately tracked.
|
|
26
|
+
|
|
3
27
|
## 0.65.0
|
|
4
28
|
|
|
5
29
|
- Add optional precompiled Windows x64 Intel NPU encoding for native Whisper
|
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ 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.
|
|
23
|
+
This checkout defines the `0.66.0` SDK contract. Development applications declare
|
|
24
24
|
`arcane-os` as `latest` and retain their lockfile. Exact SDK pins begin when Roshi
|
|
25
25
|
designates a production build. Follow [Consumer updates](docs/consumer-updates.md)
|
|
26
26
|
to refresh and compare the installed SDK and lockfile with the published channel;
|
|
@@ -74,7 +74,8 @@ class BrowserDecisionModel {
|
|
|
74
74
|
#error = null;
|
|
75
75
|
#disposed = false;
|
|
76
76
|
|
|
77
|
-
constructor({family, model, revision = 'main', device = 'webgpu',
|
|
77
|
+
constructor({family, model, revision = 'main', device = 'webgpu',
|
|
78
|
+
dtype = family === 'laya' ? 'fp16' : 'fp32', runtime = {}} = {}) {
|
|
78
79
|
if (family !== 'laya' && family !== 'julia') {
|
|
79
80
|
throw new TypeError('family must be "laya" or "julia".');
|
|
80
81
|
}
|
|
@@ -89,7 +90,7 @@ class BrowserDecisionModel {
|
|
|
89
90
|
model: requiredText(model, 'model'),
|
|
90
91
|
revision: requiredText(revision, 'revision'),
|
|
91
92
|
device: requiredText(device, 'device'),
|
|
92
|
-
dtype:
|
|
93
|
+
dtype: requiredText(dtype, 'dtype'),
|
|
93
94
|
runtime: {...runtime, moduleUrl: requiredText(moduleUrl, 'runtime.moduleUrl')}
|
|
94
95
|
};
|
|
95
96
|
this.#events = createArcaneEventSource(
|
|
@@ -82,7 +82,8 @@ export function encodeDecisionRows(tokenizer, rows, family) {
|
|
|
82
82
|
return {encoded, pad};
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
/** Plain tensor records shared by browser and native ONNX execution owners. */
|
|
86
|
+
export function createDecisionInputs(encoded, pad) {
|
|
86
87
|
let sequenceLength = 0;
|
|
87
88
|
let optionCount = 0;
|
|
88
89
|
for (const row of encoded) {
|
|
@@ -108,26 +109,11 @@ function decisionInputs(Tensor, encoded, pad) {
|
|
|
108
109
|
types[rowIndex] = BigInt(row.qtype);
|
|
109
110
|
}
|
|
110
111
|
return {
|
|
111
|
-
input_ids:
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
'int64', attention,
|
|
117
|
-
[batch, sequenceLength]
|
|
118
|
-
),
|
|
119
|
-
marker_pos: new Tensor(
|
|
120
|
-
'int64', positions,
|
|
121
|
-
[batch, optionCount]
|
|
122
|
-
),
|
|
123
|
-
marker_mask: new Tensor(
|
|
124
|
-
'bool', mask,
|
|
125
|
-
[batch, optionCount]
|
|
126
|
-
),
|
|
127
|
-
qtype: new Tensor(
|
|
128
|
-
'int64', types,
|
|
129
|
-
[batch]
|
|
130
|
-
)
|
|
112
|
+
input_ids: {type: 'int64', data: ids, dims: [batch, sequenceLength]},
|
|
113
|
+
attention_mask: {type: 'int64', data: attention, dims: [batch, sequenceLength]},
|
|
114
|
+
marker_pos: {type: 'int64', data: positions, dims: [batch, optionCount]},
|
|
115
|
+
marker_mask: {type: 'bool', data: mask, dims: [batch, optionCount]},
|
|
116
|
+
qtype: {type: 'int64', data: types, dims: [batch]}
|
|
131
117
|
};
|
|
132
118
|
}
|
|
133
119
|
|
|
@@ -210,7 +196,8 @@ export function decodeDecisionOutputs(rows, outputs) {
|
|
|
210
196
|
}
|
|
211
197
|
|
|
212
198
|
export async function loadDecisionRuntime(configuration, report) {
|
|
213
|
-
const {family, model: repository, revision = 'main', device = 'webgpu',
|
|
199
|
+
const {family, model: repository, revision = 'main', device = 'webgpu',
|
|
200
|
+
dtype = family === 'laya' ? 'fp16' : 'fp32', runtime} = configuration;
|
|
214
201
|
report(
|
|
215
202
|
{phase: 'loading-runtime'}
|
|
216
203
|
);
|
|
@@ -219,7 +206,7 @@ export async function loadDecisionRuntime(configuration, report) {
|
|
|
219
206
|
namespace.env.backends.onnx.wasm.wasmPaths = runtime.wasmPaths;
|
|
220
207
|
}
|
|
221
208
|
const {AutoTokenizer, AutoModel, PreTrainedModel, Tensor} = namespace;
|
|
222
|
-
const options = {revision, device, dtype
|
|
209
|
+
const options = {revision, device, dtype};
|
|
223
210
|
// No progress_callback: upstream 4.3 otherwise performs file-size
|
|
224
211
|
// aggregation. This owner reports actual semantic lifecycle transitions.
|
|
225
212
|
report(
|
|
@@ -261,7 +248,10 @@ export async function loadDecisionRuntime(configuration, report) {
|
|
|
261
248
|
async function evaluate(rows) {
|
|
262
249
|
const {encoded, pad} = encodeDecisionRows(tokenizer, rows, family);
|
|
263
250
|
if (encoded.length === 0) return {decisions: [], outputs: {}};
|
|
264
|
-
const inputs =
|
|
251
|
+
const inputs = {};
|
|
252
|
+
for (const [name, record] of Object.entries(createDecisionInputs(encoded, pad))) {
|
|
253
|
+
inputs[name] = new Tensor(record.type, record.data, record.dims);
|
|
254
|
+
}
|
|
265
255
|
let output;
|
|
266
256
|
try {
|
|
267
257
|
output = await model(inputs);
|
package/docs/publishing.md
CHANGED
|
@@ -116,6 +116,45 @@ tag and GitHub release title must both be the same bare numeric
|
|
|
116
116
|
`MAJOR.MINOR.PATCH`. Prerelease versions do not get a misleading numeric
|
|
117
117
|
GitHub release, and no release creates a Git branch for an npm dist-tag.
|
|
118
118
|
|
|
119
|
+
## Native runtime assets on numeric releases
|
|
120
|
+
|
|
121
|
+
Default SDK-hosted native asset URLs use the installed SDK's version and its
|
|
122
|
+
matching numeric GitHub release. Publishing npm alone does not make those
|
|
123
|
+
downloads available. Every applicable numeric release therefore includes its
|
|
124
|
+
compatible retained SDK-hosted assets as release follow-through:
|
|
125
|
+
|
|
126
|
+
| Selected capability | Windows x64 release asset |
|
|
127
|
+
| --- | --- |
|
|
128
|
+
| Native application host | `arcane-native-windows-x64.tar.gz` |
|
|
129
|
+
| Ordinary native Whisper helper | `arcane-whisper-windows-x64.tar.gz` |
|
|
130
|
+
| Optional Intel NPU Whisper encoder | `arcane-whisper-openvino-windows-x64.tar.gz` |
|
|
131
|
+
|
|
132
|
+
Attach each asset used by the selected package's default SDK-hosted download
|
|
133
|
+
paths, including later releases whose native implementation is unchanged.
|
|
134
|
+
Include the optional NPU asset when that release supplies `encoder:
|
|
135
|
+
"openvino-npu"`; applications still opt in, and the ordinary Whisper asset
|
|
136
|
+
remains available. Upstream runtime and model versions retain their separate
|
|
137
|
+
upstream distribution URLs; they are not renamed to the SDK version.
|
|
138
|
+
|
|
139
|
+
Reuse the producer's retained compatible archive and its recorded selected
|
|
140
|
+
evidence. Do not rebuild or redownload an unchanged archive for each release.
|
|
141
|
+
An actual native change belongs to its producer under the existing build and
|
|
142
|
+
dependency authority. Preserve the complete runtime closure and its packaging
|
|
143
|
+
notices together. Models continue through their upstream acquisition path
|
|
144
|
+
unless their redistribution is separately requested.
|
|
145
|
+
|
|
146
|
+
The release owner assigns each upload, observes its outcome and records the
|
|
147
|
+
exact asset name and public release URL in the durable release handoff. If an
|
|
148
|
+
upload result is uncertain, inspect that release's existing asset before
|
|
149
|
+
retrying. Retain the exact pending operation, executing owner and last result
|
|
150
|
+
until delivery finishes; reassign idle delegated work rather than treating
|
|
151
|
+
delegation as completion.
|
|
152
|
+
|
|
153
|
+
Source delivery, selected package verification, npm publication, native asset
|
|
154
|
+
delivery, requester notices and documentation/site delivery are distinct.
|
|
155
|
+
Native archive follow-through does not gate npm publication or require a new
|
|
156
|
+
package build. Consumer adoption remains with each application owner.
|
|
157
|
+
|
|
119
158
|
## Documentation publication
|
|
120
159
|
|
|
121
160
|
After every successful npm release and its applicable requester notices, the
|
package/docs/reference/README.md
CHANGED
|
@@ -54,18 +54,23 @@ alone does not make bare module names resolve in a browser.
|
|
|
54
54
|
| Connect a browser to one Core transport | [Core browser client](core-client.md) |
|
|
55
55
|
| Cancel one Core speech synthesis request without changing its payload | [Speech request lifetime](core-client.md#events-and-request-lifetime) and [Core facade](sdk-api.md#createcorefacade) |
|
|
56
56
|
| Compose native Core dispatch, service lifecycle, and stdio hosting | [Native Core runtime](core-runtime.md) |
|
|
57
|
+
| Compose application Core services during source development, independently of local AI | [Development Core services](core-development.md) |
|
|
58
|
+
| Own an explicitly selected native Codex App Server session | [Codex App Server](codex-app-server.md) |
|
|
59
|
+
| Expose application-owned tools and static resources through Node STDIO | [MCP STDIO server](mcp-stdio.md) |
|
|
57
60
|
| Run application-selected llama.cpp, Ollama or ONNX through Core | [Local AI through Core](local-ai.md) |
|
|
58
61
|
| Generate complete local PNG images with a retained native model | [Local image generation](local-image-generation.md) |
|
|
59
62
|
| Select FLUX.2 Klein 4B and its three complementary model resources | [FLUX.2 Klein 4B through Core](local-image-flux.md) |
|
|
60
63
|
| Select the complete SDXL Base 1.0 checkpoint for text-to-image generation | [SDXL Base 1.0 through Core](local-image-sdxl.md) |
|
|
61
64
|
| Compose independently loaded transcription and synthesis engines | [Native speech service](native-speech.md) |
|
|
62
65
|
| Keep a native Whisper model loaded across complete recordings | [Native Whisper transcription](local-whisper.md) |
|
|
66
|
+
| Select the optional Intel NPU encoder with independent CUDA or CPU decoding | [Intel NPU Whisper](../guides/native-whisper-openvino.md) |
|
|
63
67
|
| Edit an original PNG with a complete prompt and explicit strength | [Local image editing](local-image-editing.md) |
|
|
64
68
|
| Project complete stored model files into native working files | [Model assets through Core](model-assets.md) |
|
|
65
69
|
| Retain a Nemotron model and diarize caller-fed audio streams | [Native speaker diarization](diarization.md) |
|
|
66
70
|
| Persist application-selected preferences through Core | [Core preferences service](core-preferences.md) |
|
|
67
71
|
| Read complete selected Git text without changing a checkout | [Git text snapshots](git-text-snapshot.md) |
|
|
68
72
|
| Own a native working checkout under per-user ArcaneData/Repos | [Native repository workspaces](core-repositories.md) |
|
|
73
|
+
| Write, commit and push complete selected repository text | [Selected-text repository writing](core-repositories.md#write-commit-and-push-selected-text) |
|
|
69
74
|
| Build an installed-SDK portable Core payload | [Portable Core packaging](core-native-packaging.md) |
|
|
70
75
|
| Pass host-selected state defaults and unchanged explicit launch values to services | [Launch-time locations](core-native-packaging.md#launch-time-locations) |
|
|
71
76
|
| Build a Windows executable without an OS checkout | [Windows executable packaging](core-native-packaging.md#windows-executable) |
|
|
@@ -142,7 +147,8 @@ node tools/build-reference-site.mjs --write --source-ref <documentation-commit>
|
|
|
142
147
|
|
|
143
148
|
The same option is accepted by `createReferenceSite({sourceRef})` and
|
|
144
149
|
`writeReferenceSite({sourceRef})`. It reads the selected commit's package
|
|
145
|
-
version, complete reference documents and inventories,
|
|
150
|
+
version, complete reference documents and inventories, the canonical
|
|
151
|
+
`docs/guides/native-whisper-openvino.md` guide when present, inventory-selected
|
|
146
152
|
runtime sources, and authored site inputs directly from Git in memory. It
|
|
147
153
|
creates no checkout or export and leaves source drafts untouched. Rendering
|
|
148
154
|
uses the current canonical generator and its authored semantic contracts;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Browser typed decisions
|
|
2
2
|
|
|
3
|
-
`arcane-os/ai/browser-decisions` owns browser-local Laya typed
|
|
4
|
-
and Julia-1
|
|
3
|
+
`arcane-os/ai/browser-decisions` owns browser-local Laya typed decisions
|
|
4
|
+
and Julia-1 inference. Laya defaults to FP16 and Julia to FP32; an explicit
|
|
5
|
+
`dtype` is forwarded unchanged to the selected upstream loader. It is separate from chat and speech because these
|
|
5
6
|
models score caller-supplied options instead of generating conversational text.
|
|
6
7
|
Applications supply the model repository, state, questions, option meanings and
|
|
7
8
|
policy; the SDK owns the reusable tokenizer, tensors, inference and lifecycle.
|
|
@@ -66,6 +67,7 @@ it does not substitute a quantized graph or another model.
|
|
|
66
67
|
| `model` | Required upstream repository/base URL accepted by Transformers.js. The application owns its selection. |
|
|
67
68
|
| `revision` | Upstream revision, default `main`; select a stable revision when repeatable model selection is needed. |
|
|
68
69
|
| `device` | Transformers.js backend, default `webgpu`; an explicit supported alternative is caller-owned. No automatic backend fallback. |
|
|
70
|
+
| `dtype` | Exact upstream precision selection; defaults to `fp16` for Laya and `fp32` for Julia. For Laya FP32, supply `dtype: 'fp32'`. Unsupported selections fail at the selected upstream loader rather than being replaced. |
|
|
69
71
|
| `runtime.moduleUrl` | Defaults to the self-contained CDN entry `https://cdn.jsdelivr.net/npm/@huggingface/transformers@4.3.0/dist/transformers.min.js`. The `.web.js` build expects a bundler to resolve its bare ONNX Runtime import and is unsuitable for a native module Worker. An explicit compatible public runtime URL may be supplied. |
|
|
70
72
|
| `runtime.wasmPaths` | Optional upstream ONNX WASM location passed to the selected runtime's public environment API. |
|
|
71
73
|
|
|
@@ -75,10 +77,11 @@ network, cross-origin and backend availability still apply. WebGPU needs a
|
|
|
75
77
|
compatible browser/device context. Unsupported precision or backend errors
|
|
76
78
|
are returned, not replaced with a different model, backend or precision.
|
|
77
79
|
|
|
78
|
-
Laya
|
|
80
|
+
Laya defaults to `dtype: 'fp16'`, loading `onnx/model_fp16.onnx` and its
|
|
79
81
|
`model_fp16.onnx_data` companion through Transformers.js. The published mixed
|
|
80
82
|
precision graph retains selected operations/outputs in FP32; FP16 selection
|
|
81
|
-
does not claim every graph operation runs in half precision.
|
|
83
|
+
does not claim every graph operation runs in half precision. Explicit
|
|
84
|
+
`dtype: 'fp32'` selects the repository's FP32 graph through that same loader.
|
|
82
85
|
|
|
83
86
|
Julia selects `dtype: 'fp32'`, loading root `model.onnx` with the exact
|
|
84
87
|
`model.onnx.data` external-data name. Its custom five-input graph is loaded
|
|
@@ -46,7 +46,7 @@ const status = await repository.status({signal});
|
|
|
46
46
|
await repository.close();
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
`createRepositoryWorkspace({name,directory,dataRoot,remote,branch,onEvent,run}={})`
|
|
49
|
+
`createRepositoryWorkspace({name,directory,dataRoot,remote,branch,gitIdentity,onEvent,run}={})`
|
|
50
50
|
returns `{directory,open,status,pull,push,write,close,drain,dispose}`. The `directory`
|
|
51
51
|
property is the absolute selected working path.
|
|
52
52
|
|
|
@@ -87,6 +87,124 @@ A failed or cancelled clone reports the actual failure; any remaining directory
|
|
|
87
87
|
is retained for application-owned inspection. There is no automatic deletion or
|
|
88
88
|
destructive retry. Filesystem failures and complete Git diagnostics propagate.
|
|
89
89
|
|
|
90
|
+
## Git identity configuration
|
|
91
|
+
|
|
92
|
+
```javascript
|
|
93
|
+
import {readGitIdentity, createRepositoryWorkspace} from 'arcane-os/core/repositories';
|
|
94
|
+
|
|
95
|
+
const defaults = await readGitIdentity({signal});
|
|
96
|
+
const observations = await readGitIdentity({directory: existingRepositoryDirectory, signal});
|
|
97
|
+
const repository = createRepositoryWorkspace({
|
|
98
|
+
name: 'moon-cheese-dispatches',
|
|
99
|
+
remote: applicationConnection.remote,
|
|
100
|
+
gitIdentity: {name: 'Moon Dispatcher', email: 'moon@example.invalid', username: 'moon-account'}
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`readGitIdentity({directory,signal,onEvent,run=runProcess}={})` reads only
|
|
105
|
+
`user.name`, `user.email` and `github.user`. It returns:
|
|
106
|
+
|
|
107
|
+
```text
|
|
108
|
+
{
|
|
109
|
+
global: {name, email, githubUser},
|
|
110
|
+
local: {name, email, githubUser} | null,
|
|
111
|
+
effective: {name, email, githubUser} | null
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Every observed field is the complete UTF-8 Git configuration string, including
|
|
116
|
+
an empty string, leading U+FEFF and embedded newlines, or `null` when unset.
|
|
117
|
+
Git can store values outside UTF-8; those produce
|
|
118
|
+
`ARCANE_GIT_IDENTITY_NOT_TEXT` instead of replacement characters. Global and
|
|
119
|
+
local observations select Git's corresponding file scopes and honor configured
|
|
120
|
+
includes. With an existing
|
|
121
|
+
repository `directory`, `effective` reads ordinary Git configuration in that
|
|
122
|
+
repository context, including system, global, local, worktree and command
|
|
123
|
+
configuration where applicable. Equal local and global values remain separate
|
|
124
|
+
observations; the SDK does not infer who wrote or owns a setting. The last
|
|
125
|
+
configured scalar value wins within each observation.
|
|
126
|
+
|
|
127
|
+
Without `directory`, only global configuration is read and `local`/`effective`
|
|
128
|
+
are `null`. Relative directories resolve from the process working directory.
|
|
129
|
+
Git retains its normal environment and conditional-include context; when no
|
|
130
|
+
directory is supplied, that context is the process working directory. These
|
|
131
|
+
are independent configuration observations, not a transaction or a claim about
|
|
132
|
+
the identity of a prior commit, environment-derived author, or authenticated
|
|
133
|
+
GitHub account. `github.user` is a non-secret configured default, not proof of
|
|
134
|
+
authentication.
|
|
135
|
+
|
|
136
|
+
One targeted Git command reads each requested scope. Independent observations
|
|
137
|
+
start concurrently and all accepted commands finish their process cleanup
|
|
138
|
+
before the reader settles. `signal` uses the existing process owner; the shared
|
|
139
|
+
SDK event queue serializes `onEvent` delivery with its usual backpressure.
|
|
140
|
+
The reader consumes raw stdout through the existing process contract; stdout
|
|
141
|
+
is parsed as configuration rather than emitted as text events. Process
|
|
142
|
+
lifecycle and stderr events retain their ordinary route. A supplied `run`
|
|
143
|
+
adapter must honor that raw-output contract.
|
|
144
|
+
Git's no-match result produces unset fields; other process,
|
|
145
|
+
configuration, observer and cancellation failures remain complete. A single
|
|
146
|
+
failure retains its original object, code, cause and process `details`; the
|
|
147
|
+
reader adds complete captured stdout as the `rawStdout` Buffer on that error.
|
|
148
|
+
If the error already has that property or cannot accept it, a new error retains
|
|
149
|
+
the original as `cause`, preserves its code, process details and exit code, and
|
|
150
|
+
holds this observation's `rawStdout`. Original causal failures stay on that
|
|
151
|
+
original error; an attachment exception is retained as `attachmentError`.
|
|
152
|
+
An existing `rawStdout` property is never overwritten or invoked to read its value.
|
|
153
|
+
The process result's `stdout` remains `null` in raw mode, and its stderr and
|
|
154
|
+
other diagnostics remain intact. A decoding failure likewise carries its
|
|
155
|
+
original decoding `cause`, complete process `details` and `rawStdout`.
|
|
156
|
+
An observer failure already represented by a process error is not added again;
|
|
157
|
+
genuinely separate failures use `AggregateError.errors`, each retaining its
|
|
158
|
+
own diagnostics.
|
|
159
|
+
No configuration file is written and no credential helper is queried by this
|
|
160
|
+
reader. The application owns any saved non-secret defaults and repository
|
|
161
|
+
overrides through its existing preferences.
|
|
162
|
+
|
|
163
|
+
Both this workspace factory and
|
|
164
|
+
[`createGitTextSnapshot`](git-text-snapshot.md) accept optional
|
|
165
|
+
`gitIdentity:{name?,email?,username?}`. They capture supplied strings once during
|
|
166
|
+
I/O-free construction. Later edits to that object or a UI selection do not
|
|
167
|
+
change accepted work. Omitted or `undefined` fields preserve ordinary inherited
|
|
168
|
+
behavior; removing an application override means omitting that field on the
|
|
169
|
+
next owner, not changing unmanaged repository configuration. Empty strings are
|
|
170
|
+
passed explicitly to Git. Non-string values, NUL and text that cannot be
|
|
171
|
+
represented by the native process transport report `TypeError` without silently
|
|
172
|
+
rewriting the input.
|
|
173
|
+
|
|
174
|
+
An application's explicit "global author" choice differs from ordinary Git
|
|
175
|
+
inheritance. Pass the observed global `name` and `email` as selected strings to
|
|
176
|
+
override repository-local author values for those fields. A global `null` means
|
|
177
|
+
that field is unset, not a selected value: passing `null` reports `TypeError`,
|
|
178
|
+
and omitting the field allows normal inheritance, including repository-local
|
|
179
|
+
configuration. The application must describe that partial/inherited choice
|
|
180
|
+
accurately or obtain the missing value before presenting an all-global author
|
|
181
|
+
selection. An empty string is a distinct explicit value that Git may reject.
|
|
182
|
+
Changed application choices apply to future owners; accepted operations retain
|
|
183
|
+
their original selection and may finish without being discarded.
|
|
184
|
+
|
|
185
|
+
Each supplied name/email becomes command-local `-c user.name=...` or
|
|
186
|
+
`-c user.email=...` and the corresponding child-only `GIT_AUTHOR_*` and
|
|
187
|
+
`GIT_COMMITTER_*` fields. Those selected fields take precedence over inherited
|
|
188
|
+
author/committer settings and environment values; omitted fields, dates and
|
|
189
|
+
unrelated environment remain inherited. Git retains its ordinary commit
|
|
190
|
+
identity formatting and may reject an unusable identity. The SDK changes
|
|
191
|
+
neither `process.env` nor global/local Git files.
|
|
192
|
+
|
|
193
|
+
`username` supplies only command-local `credential.username`. Git's existing
|
|
194
|
+
credential helper and remote protocol still own actual authentication. A
|
|
195
|
+
URL-embedded username or helper behavior can affect the selected credential;
|
|
196
|
+
this option neither rewrites the remote nor selects an SSH account or proves a
|
|
197
|
+
GitHub login. No account enumeration, secret storage or `gh` dependency is added.
|
|
198
|
+
See Git's [configuration](https://git-scm.com/docs/git-config),
|
|
199
|
+
[author environment](https://git-scm.com/docs/git#_git_commits) and
|
|
200
|
+
[credential username](https://git-scm.com/docs/gitcredentials) contracts.
|
|
201
|
+
|
|
202
|
+
The selected settings accompany every workspace Git command, including clone,
|
|
203
|
+
pull, commit and push, and every bare-snapshot Git command, including init and
|
|
204
|
+
fetch. Existing arguments, content, output, errors, queue and cancellation
|
|
205
|
+
semantics remain unchanged. Other repository helpers and CLI cwd defaults keep
|
|
206
|
+
their existing behavior.
|
|
207
|
+
|
|
90
208
|
## Write, commit and push selected text
|
|
91
209
|
|
|
92
210
|
```javascript
|
|
@@ -122,8 +240,9 @@ One `git --literal-pathspecs add -- <paths>` stages only those exact names.
|
|
|
122
240
|
One `git --literal-pathspecs commit --only --cleanup=verbatim --file - -- <paths>`
|
|
123
241
|
commits only those paths, leaving unrelated staged paths staged. The original
|
|
124
242
|
message is streamed unchanged through standard input. One ordinary `git push`
|
|
125
|
-
then uses that repository's existing remote/ref configuration,
|
|
126
|
-
|
|
243
|
+
then uses that repository's existing remote/ref configuration, the optional
|
|
244
|
+
process-local identity selection above, existing credentials and hooks.
|
|
245
|
+
There is no implicit pull, branch switch, forced push,
|
|
127
246
|
hook bypass, automatic retry, rollback or reset. If Git reports no changes to
|
|
128
247
|
commit, that actual failure is returned; the SDK creates no empty commit.
|
|
129
248
|
These are Git's documented [selected-path commit](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt---only)
|
|
@@ -33,12 +33,26 @@ policy stable for the lifetime of its snapshot owner.
|
|
|
33
33
|
|
|
34
34
|
## API and lifecycle
|
|
35
35
|
|
|
36
|
-
`createGitTextSnapshot({cacheDirectory,remote,ref,selectPath,onEvent,run})`
|
|
36
|
+
`createGitTextSnapshot({cacheDirectory,remote,ref,selectPath,gitIdentity,onEvent,run})`
|
|
37
37
|
returns `{refresh,close,drain,dispose}`. `run` defaults to the SDK's existing
|
|
38
38
|
`runProcess`; applications normally omit it. It is the same process adapter
|
|
39
39
|
contract, not a second process supervisor. `onEvent` is the ordinary SDK event
|
|
40
40
|
callback and follows its asynchronous completion/error contract.
|
|
41
41
|
|
|
42
|
+
Optional `gitIdentity:{name?,email?,username?}` uses the shared
|
|
43
|
+
[Git identity configuration contract](core-repositories.md#git-identity-configuration).
|
|
44
|
+
Supplied strings are captured during construction and accompany every Git
|
|
45
|
+
command, including bare initialization and fetch, without changing the remote,
|
|
46
|
+
ref, config files or process environment. Explicit name/email use command-local
|
|
47
|
+
configuration and child-only author/committer fields; omitted fields remain
|
|
48
|
+
inherited. `username` is only Git's credential username hint, not authenticated
|
|
49
|
+
account proof or SSH account selection. Read separate global/local/effective
|
|
50
|
+
configuration through `readGitIdentity` from `arcane-os/core/repositories`;
|
|
51
|
+
the reader also accepts an existing bare cache directory. Applications retain
|
|
52
|
+
saved non-secret choices and per-connection overrides. Identity selection adds
|
|
53
|
+
no process or query to refresh and does not attach a caller's abort to shared
|
|
54
|
+
acquisition.
|
|
55
|
+
|
|
42
56
|
- `refresh({signal}={})` fetches the selected ref and returns
|
|
43
57
|
`{revision,files:[{path,content}]}`. `revision` is Git's opaque selected commit
|
|
44
58
|
name, not an SDK integrity record. Every file comes from that exact revision,
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"sdkVersion": "0.
|
|
3
|
+
"sdkVersion": "0.65.0",
|
|
4
4
|
"environment": {
|
|
5
5
|
"runtime": "Node.js for Node entrypoints; browser for browser-only entrypoints",
|
|
6
6
|
"minimumVersion": "22.23.2 for Node entrypoints",
|
|
7
7
|
"moduleSystem": "ESM"
|
|
8
8
|
},
|
|
9
|
-
"memberCount":
|
|
9
|
+
"memberCount": 290,
|
|
10
10
|
"runtimeSubpathPatterns": {
|
|
11
11
|
"arcane-os/modules/*": "./runtime/arcane/modules/*",
|
|
12
12
|
"arcane-os/entities/*": "./runtime/arcane/entities/*"
|
|
@@ -54,6 +54,20 @@
|
|
|
54
54
|
"protocol": "Owned configure/build/install process lifecycle",
|
|
55
55
|
"normalization": "Returns platform/architecture/root/executable/runtimeDirectory and complete configure/compile/install diagnostics; preserves cancellation and process failures, installs no model or toolchain, performs no inference"
|
|
56
56
|
},
|
|
57
|
+
{
|
|
58
|
+
"id": "local-ai-whisper-openvino-build:buildWhisperOpenVinoRuntime",
|
|
59
|
+
"name": "buildWhisperOpenVinoRuntime",
|
|
60
|
+
"displayName": "buildWhisperOpenVinoRuntime()",
|
|
61
|
+
"kind": "function",
|
|
62
|
+
"signature": "async buildWhisperOpenVinoRuntime({sourceDirectory,openvinoDirectory,cudaDirectory,outputRoot,cmake='cmake',generator,cmakeArgs=[],env,signal,onEvent}={})",
|
|
63
|
+
"entrypoints": ["arcane-os/local-ai/whisper/openvino-build"],
|
|
64
|
+
"primaryImport": "arcane-os/local-ai/whisper/openvino-build",
|
|
65
|
+
"group": "Core native speech",
|
|
66
|
+
"summary": "Produces the optional Intel NPU encoder runtime from installed matching Whisper/OpenVINO sources and an existing Windows x64 toolchain.",
|
|
67
|
+
"availability": "Windows x64 Node with Whisper 1.9.4 source, OpenVINO 2026.3.1 development tree and compatible CMake/MSVC; other platform distributions require separate native producers",
|
|
68
|
+
"protocol": "Owned configure/build process lifecycle and complete native runtime assembly",
|
|
69
|
+
"normalization": "Returns version/openvinoVersion/platform/architecture/encoder/backend/backends/root/directory/libraryDirectory/copied sourceDirectory and complete configure/compile diagnostics; retains NPU/CPU and optional supplied CUDA libraries plus notices, preserves process failures and cancellation, installs no model, driver or toolchain"
|
|
70
|
+
},
|
|
57
71
|
{
|
|
58
72
|
"id": "codex-app-server:createCodexAppServerSession",
|
|
59
73
|
"name": "createCodexAppServerSession",
|
|
@@ -143,14 +157,28 @@
|
|
|
143
157
|
"name": "createRepositoryWorkspace",
|
|
144
158
|
"displayName": "createRepositoryWorkspace()",
|
|
145
159
|
"kind": "function",
|
|
146
|
-
"signature": "createRepositoryWorkspace({name,directory,dataRoot,remote,branch,onEvent,run=runProcess}={})",
|
|
160
|
+
"signature": "createRepositoryWorkspace({name,directory,dataRoot,remote,branch,gitIdentity,onEvent,run=runProcess}={})",
|
|
147
161
|
"entrypoints": ["arcane-os/core/repositories"],
|
|
148
162
|
"primaryImport": "arcane-os/core/repositories",
|
|
149
163
|
"group": "Native repository workspaces",
|
|
150
164
|
"summary": "Owns one selected native working checkout with ArcaneData/Repos defaults and existing Git/process lifecycle.",
|
|
151
165
|
"availability": "Node with Git on Windows, Linux and macOS; Android requires host process adaptation",
|
|
152
166
|
"protocol": "Git working repository and SDK process adapter",
|
|
153
|
-
"normalization": "Returns directory, open, status, pull, push, write and identical close/drain/dispose hooks. Only missing or empty destinations clone; existing roots are inspected without rewriting, resetting or pulling. Explicit paths remain selected. Same-directory operations serialize within the process; cancellation and shutdown join accepted process work. write snapshots exact selected text and paths, writes files, stages/commits only literal owned paths and non-force pushes, preserving unrelated staged files and ordinary Git filters. Complete outputs and causal failures retain confirmed local/committed/pushed and uncertain outcomes."
|
|
167
|
+
"normalization": "Returns directory, open, status, pull, push, write and identical close/drain/dispose hooks. Only missing or empty destinations clone; existing roots are inspected without rewriting, resetting or pulling. Explicit paths remain selected. Same-directory operations serialize within the process; cancellation and shutdown join accepted process work. write snapshots exact selected text and paths, writes files, stages/commits only literal owned paths and non-force pushes, preserving unrelated staged files and ordinary Git filters. Complete outputs and causal failures retain confirmed local/committed/pushed and uncertain outcomes. Optional gitIdentity captures name/email/username for child Git operations only; omitted fields remain inherited and no config file is written."
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"id": "core-repositories:readGitIdentity",
|
|
171
|
+
"name": "readGitIdentity",
|
|
172
|
+
"displayName": "readGitIdentity()",
|
|
173
|
+
"kind": "function",
|
|
174
|
+
"signature": "readGitIdentity({directory,signal,onEvent,run=runProcess}={})",
|
|
175
|
+
"entrypoints": ["arcane-os/core/repositories"],
|
|
176
|
+
"primaryImport": "arcane-os/core/repositories",
|
|
177
|
+
"group": "Native repository workspaces",
|
|
178
|
+
"summary": "Reads separate global, repository-local and effective non-secret Git identity configuration.",
|
|
179
|
+
"availability": "Node with Git on Windows, Linux and macOS; Android requires host process adaptation",
|
|
180
|
+
"protocol": "Git config NUL-delimited transport through the SDK process adapter",
|
|
181
|
+
"normalization": "Returns {global,local,effective} observations with complete name/email/githubUser strings or null when unset. Empty strings and configured includes are preserved. Without directory only global is observed and local/effective are null. Concurrent scope reads join process cleanup; complete failures propagate singly or through AggregateError. Changes no config files and claims no authenticated account or environment-resolved author."
|
|
154
182
|
},
|
|
155
183
|
{
|
|
156
184
|
"id": "core-speech:createSpeechService",
|
|
@@ -311,14 +339,14 @@
|
|
|
311
339
|
"name": "createGitTextSnapshot",
|
|
312
340
|
"displayName": "createGitTextSnapshot()",
|
|
313
341
|
"kind": "function",
|
|
314
|
-
"signature": "createGitTextSnapshot({cacheDirectory,remote,ref,selectPath,onEvent,run=runProcess}={})",
|
|
342
|
+
"signature": "createGitTextSnapshot({cacheDirectory,remote,ref,selectPath,gitIdentity,onEvent,run=runProcess}={})",
|
|
315
343
|
"entrypoints": ["arcane-os"],
|
|
316
344
|
"primaryImport": "arcane-os",
|
|
317
345
|
"group": "Workspace, doctor, repository, and server",
|
|
318
346
|
"summary": "Reads complete selected text from one fetched revision using a dedicated bare Git object cache and batched retrieval.",
|
|
319
347
|
"availability": "Node with Git; Android requires a host supplying Git/process capability",
|
|
320
348
|
"protocol": "Git tree and batched blob transport through the shared process owner",
|
|
321
|
-
"normalization": "Returns {refresh,close,drain,dispose}; refresh yields {revision,files:[{path,content}]}, shares concurrent work, reuses unchanged revisions, preserves exact UTF-8 text, isolates caller cancellation and drains accepted work. See git-text-snapshot.md."
|
|
349
|
+
"normalization": "Returns {refresh,close,drain,dispose}; refresh yields {revision,files:[{path,content}]}, shares concurrent work, reuses unchanged revisions, preserves exact UTF-8 text, isolates caller cancellation and drains accepted work. Optional gitIdentity captures name/email/username for child Git commands including bare init and fetch, without changing config files, payloads or shared acquisition lifetime. See git-text-snapshot.md."
|
|
322
350
|
},
|
|
323
351
|
{
|
|
324
352
|
"id": "diarization:createDiarization",
|
|
@@ -1013,13 +1013,14 @@
|
|
|
1013
1013
|
"kind": "esm",
|
|
1014
1014
|
"exports": [
|
|
1015
1015
|
"isOllamaModelIdentifier",
|
|
1016
|
-
"normalizeOllamaModelIdentifier"
|
|
1016
|
+
"normalizeOllamaModelIdentifier",
|
|
1017
|
+
"sameOllamaModelIdentifier"
|
|
1017
1018
|
],
|
|
1018
|
-
"summary": "
|
|
1019
|
+
"summary": "Recognizes Ollama model identifier syntax and compares ordinary resident-model aliases without changing requests.",
|
|
1019
1020
|
"availability": "Cross-host",
|
|
1020
1021
|
"protocol": "In-process only",
|
|
1021
|
-
"normalization": "
|
|
1022
|
-
"surface": "`normalizeOllamaModelIdentifier()`, `isOllamaModelIdentifier()`."
|
|
1022
|
+
"normalization": "Exact identifier/null or boolean results; alias comparison applies Ollama's default registry, namespace and tag without changing either input.",
|
|
1023
|
+
"surface": "`normalizeOllamaModelIdentifier()`, `isOllamaModelIdentifier()`, `sameOllamaModelIdentifier(left, right)`."
|
|
1023
1024
|
},
|
|
1024
1025
|
{
|
|
1025
1026
|
"file": "runtime/arcane/modules/OllamaSettings.js",
|
|
@@ -70,6 +70,13 @@ assembly compose it through the shared speech service without changing the
|
|
|
70
70
|
browser's selected STT route. See [native Whisper](local-whisper.md) for the
|
|
71
71
|
complete configuration, platform, media and shutdown contracts.
|
|
72
72
|
|
|
73
|
+
Adding `encoder: 'openvino-npu'` selects the optional Intel NPU audio encoder
|
|
74
|
+
with a separately selected CUDA or CPU decoder. The Windows x64 precompiled
|
|
75
|
+
runtime uses paired GGML/XML/BIN model resources and the computer's installed
|
|
76
|
+
compatible Intel NPU driver. See the [Intel NPU Whisper guide](../guides/native-whisper-openvino.md)
|
|
77
|
+
for preparation, observed initialization, cancellation/reload and custom
|
|
78
|
+
runtime production. Omitting the encoder retains ordinary Whisper preparation.
|
|
79
|
+
|
|
73
80
|
The selected model stays loaded across recordings. An eligible native
|
|
74
81
|
accelerated failure may drain that helper and retry the same complete recording
|
|
75
82
|
once on the prepared CPU backend. That accepted request retains its actual
|
|
@@ -7,6 +7,12 @@ the model or recording files are reused. The browser's existing
|
|
|
7
7
|
`AI.fetchSTT(audioFile, signal)` and Core `speech.transcribe` routes retain the
|
|
8
8
|
application's selected model and cancellation signal.
|
|
9
9
|
|
|
10
|
+
For the optional Intel NPU encoder, add `encoder: "openvino-npu"` to the
|
|
11
|
+
runtime record. The [Intel NPU guide](../guides/native-whisper-openvino.md)
|
|
12
|
+
covers the Windows x64 precompiled distribution, paired GGML/XML/BIN model
|
|
13
|
+
resources, independent CUDA/CPU decoding and joined cancellation. Omitting
|
|
14
|
+
`encoder` preserves the ordinary runtime and model selection below.
|
|
15
|
+
|
|
10
16
|
## Select the runtime and model
|
|
11
17
|
|
|
12
18
|
An application's `arcane-app.json` selects native speech alongside its other
|
|
@@ -130,7 +136,7 @@ prepared descriptor returned by `ensureLocalAIRuntimes`; alternatively,
|
|
|
130
136
|
|
|
131
137
|
| Member | Contract |
|
|
132
138
|
| --- | --- |
|
|
133
|
-
| `current()` | Returns `providerId`, selected `modelId`, lifecycle `state`, `loaded`, `busy`, correlated `requestId` or null,
|
|
139
|
+
| `current()` | Returns `providerId`, selected `modelId`, lifecycle `state`, `loaded`, `busy`, correlated `requestId` or null, `requestedBackend`, `observedBackend`, `backendEvidence`, `requestedEncoder`, `observedEncoder`, `encoderEvidence` and any error. Encoder initialization evidence is separate from decoder evidence. |
|
|
134
140
|
| `subscribe(listener, {replay = true, signal} = {})` | Immediately replays current state by default and returns an unsubscribe function. |
|
|
135
141
|
| `load({modelId, signal} = {})` | Prepares the runtime if necessary, then waits for actual native model readiness. With no selected model it remains unloaded. |
|
|
136
142
|
| `transcribe(request, {signal, onProgress, requestId} = {})` | Consumes the complete `audioBase64` recording. Optional `language` defaults to native automatic detection; `translate: true` selects Whisper's translation operation. `model` must match the loaded model. Optional `requestId` carries the caller's existing operation identity through lifecycle snapshots as separate control information; it does not alter the recording or request payload. |
|
|
@@ -216,7 +216,7 @@ normal close or EOF and rejects with an `AggregateError` when transport or
|
|
|
216
216
|
diagnostic delivery failed. Observe it even when the initiating caller continues
|
|
217
217
|
other work. Request failures alone do not close the session.
|
|
218
218
|
|
|
219
|
-
## MCP_PROTOCOL_VERSION
|
|
219
|
+
## `MCP_PROTOCOL_VERSION`
|
|
220
220
|
|
|
221
221
|
`MCP_PROTOCOL_VERSION` is exactly `'2025-11-25'`. Initialization always returns
|
|
222
222
|
that supported version. A client proposing another version must accept this
|
|
@@ -26,6 +26,12 @@ selection, supported formats, and native process ownership remain with the
|
|
|
26
26
|
selected engine and its host composition. Browser-only operation remains
|
|
27
27
|
independent of this native service.
|
|
28
28
|
|
|
29
|
+
The SDK's [retained Whisper engine](local-whisper.md) supplies native STT.
|
|
30
|
+
Its optional [Intel NPU encoder](../guides/native-whisper-openvino.md) uses the
|
|
31
|
+
same service, readiness events, complete request/result contract and cancellation
|
|
32
|
+
path. The host selects that encoder and its paired model resources; the browser
|
|
33
|
+
continues calling `speech.transcribe` without loading native libraries itself.
|
|
34
|
+
|
|
29
35
|
## Startup and status
|
|
30
36
|
|
|
31
37
|
`start(context)` subscribes to each configured engine and starts their loads
|
|
@@ -60,7 +60,7 @@ runtime layouts.
|
|
|
60
60
|
| `arcane-os/core/host` | Node-only reusable Core host lifecycle with explicitly supplied services. |
|
|
61
61
|
| `arcane-os/codex/app-server` | Explicit native Codex App Server session, complete live turns, server requests and recovery. |
|
|
62
62
|
| `arcane-os/mcp/stdio` | Node MCP STDIO server for app-owned tools and static resources. |
|
|
63
|
-
| `arcane-os/core/repositories` | Native persistent ArcaneData paths and
|
|
63
|
+
| `arcane-os/core/repositories` | Native persistent ArcaneData paths, connected working repositories and scoped non-secret Git identity configuration. |
|
|
64
64
|
| `arcane-os/core/packaged-web` | Core-owned packaged web listener with a persisted loopback origin and ready/failure events. |
|
|
65
65
|
| `arcane-os/core/preferences` | Existing preference RPC methods with an application-selected file, ordered writes and owned drain. |
|
|
66
66
|
| `arcane-os/core/diarization` | Lazy native speaker-diarization service, explicit stream methods and lifecycle/result events. |
|
|
@@ -74,6 +74,9 @@ runtime layouts.
|
|
|
74
74
|
| `arcane-os/ai/core-image` | Browser image lifecycle and complete PNG Blobs through an available Core service. |
|
|
75
75
|
| `arcane-os/core/image` | Native image Core service with independent runtime preparation and retained model ownership. |
|
|
76
76
|
| `arcane-os/core/speech` | Independent injected STT/TTS engine loading, request cancellation and joined Core service lifetime. |
|
|
77
|
+
| `arcane-os/local-ai/whisper` | Retained native Whisper model, complete recordings and joined helper lifetime. |
|
|
78
|
+
| `arcane-os/local-ai/whisper/build` | First-party persistent helper build against selected installed Whisper headers and libraries. |
|
|
79
|
+
| `arcane-os/local-ai/whisper/openvino-build` | Optional Windows x64 Intel NPU encoder runtime producer using selected installed sources and toolchain. |
|
|
77
80
|
| `arcane-os/ai/core-model-assets` | Complete stored browser model members projected through Core into native working files. |
|
|
78
81
|
| `arcane-os/core/model-assets` | Native working-file preparation, engine retain handles and joined cleanup. |
|
|
79
82
|
| `arcane-os/integrated-provider` | Fixed integrated shared-development provider. |
|
|
@@ -405,6 +408,7 @@ The remaining data-only subpaths are eight JSON Schemas and package metadata.
|
|
|
405
408
|
| `readCoreLaunchContext()` | function | `arcane-os/core/host` | Native Core host composition | Node; reads explicitly selected launch JSON |
|
|
406
409
|
| `resolveArcaneDataPaths()` | function | `arcane-os/core/repositories` | Native repository workspaces | Node on Windows, Linux and macOS; Android supplies its data root |
|
|
407
410
|
| `createRepositoryWorkspace()` | function | `arcane-os/core/repositories` | Native repository workspaces | Node with Git; Android requires host process adaptation |
|
|
411
|
+
| `readGitIdentity()` | function | `arcane-os/core/repositories` | Native repository workspaces | Node with Git; Android requires host process adaptation |
|
|
408
412
|
| `createWindowsNativeProvider()` | function | `arcane-os/native/windows-provider` | Windows native provider | Node assembly; Windows x64/WebView2 execution |
|
|
409
413
|
| `arcaneNativeBuilderProvider` | singleton | `arcane-os/native/windows-provider` | Windows native provider | Node assembly; Windows x64/WebView2 execution |
|
|
410
414
|
| `arcaneNativeBuilderProvider default export` | singleton | `arcane-os/native/windows-provider` | Windows native provider | Node assembly; Windows x64/WebView2 execution |
|
|
@@ -430,6 +434,7 @@ The remaining data-only subpaths are eight JSON Schemas and package metadata.
|
|
|
430
434
|
| `createWhisperRuntime()` | function | `arcane-os/local-ai/whisper` | Core native speech | Node with a prepared matching helper, runtime, decoder and selected model |
|
|
431
435
|
| `createWhisperRuntime default export` | function | `arcane-os/local-ai/whisper` | Core native speech | Same retained native engine factory |
|
|
432
436
|
| `buildWhisperHelper()` | function | `arcane-os/local-ai/whisper/build` | Core native speech | Node with installed CMake/C++17 toolchain and matching Whisper source/runtime |
|
|
437
|
+
| `buildWhisperOpenVinoRuntime()` | function | `arcane-os/local-ai/whisper/openvino-build` | Core native speech | Windows x64 Node with installed matching Whisper/OpenVINO sources and CMake/MSVC toolchain |
|
|
433
438
|
| `createSpeechService default export` | function | `arcane-os/core/speech` | Core native speech | Same shared speech service factory |
|
|
434
439
|
| `prepareCoreModelAssets()` | function | `arcane-os/ai/core-model-assets` | Core model assets | Browser with complete Blob/File members and available Core service |
|
|
435
440
|
| `createModelAssetService()` | function | `arcane-os/core/model-assets` | Core model assets | Node Core with an application-selected working directory |
|
|
@@ -5209,7 +5214,7 @@ a dedicated bare Git cache. It does not create or alter a working checkout.
|
|
|
5209
5214
|
### Signature, lifecycle, and result
|
|
5210
5215
|
|
|
5211
5216
|
```text
|
|
5212
|
-
createGitTextSnapshot({cacheDirectory,remote,ref,selectPath,onEvent,run=runProcess}={})
|
|
5217
|
+
createGitTextSnapshot({cacheDirectory,remote,ref,selectPath,gitIdentity,onEvent,run=runProcess}={})
|
|
5213
5218
|
```
|
|
5214
5219
|
|
|
5215
5220
|
Import from `arcane-os`. The application owns the cache location, remote, ref,
|
|
@@ -5221,6 +5226,11 @@ await accepted refresh/process cleanup. Errors remain observable instead of
|
|
|
5221
5226
|
returning stale or partial success. See [Git text snapshots](git-text-snapshot.md)
|
|
5222
5227
|
for complete inputs, text semantics, errors, events and Core service composition.
|
|
5223
5228
|
|
|
5229
|
+
Optional `gitIdentity:{name?,email?,username?}` captures a child-process-only
|
|
5230
|
+
selection for init/fetch and subsequent Git commands, preserving shared refresh
|
|
5231
|
+
cancellation and complete content. See [Git identity configuration](core-repositories.md#git-identity-configuration)
|
|
5232
|
+
for author precedence, scoped defaults and credential username semantics.
|
|
5233
|
+
|
|
5224
5234
|
### Example
|
|
5225
5235
|
|
|
5226
5236
|
```js
|
|
@@ -7881,7 +7891,7 @@ explicit existing path. The application owns the remote, branch and Core API.
|
|
|
7881
7891
|
### Signature and result
|
|
7882
7892
|
|
|
7883
7893
|
```text
|
|
7884
|
-
createRepositoryWorkspace({name,directory,dataRoot,remote,branch,onEvent,run=runProcess}={})
|
|
7894
|
+
createRepositoryWorkspace({name,directory,dataRoot,remote,branch,gitIdentity,onEvent,run=runProcess}={})
|
|
7885
7895
|
```
|
|
7886
7896
|
|
|
7887
7897
|
Returns `{directory,open,status,pull,push,write,close,drain,dispose}`. Construction does
|
|
@@ -7892,6 +7902,12 @@ work and preserves the checkout. No CLI cwd, launch state, preference, snapshot
|
|
|
7892
7902
|
cache or existing user-data location changes. See the [complete inputs, results,
|
|
7893
7903
|
errors and lifecycle](core-repositories.md#one-connected-working-checkout).
|
|
7894
7904
|
|
|
7905
|
+
Optional `gitIdentity:{name?,email?,username?}` captures explicit non-secret
|
|
7906
|
+
settings for child Git operations without writing global/local configuration.
|
|
7907
|
+
Selected author fields override inherited child author/committer fields;
|
|
7908
|
+
omitted fields remain inherited. Username remains a credential hint, with
|
|
7909
|
+
authentication owned by ordinary Git and its configured helper.
|
|
7910
|
+
|
|
7895
7911
|
`write({files:[{path,content}],message,signal})` snapshots the exact selected
|
|
7896
7912
|
strings, writes those repository-relative files, commits only their literal
|
|
7897
7913
|
paths and performs an ordinary non-force push. It retains unrelated staged
|
|
@@ -7911,6 +7927,45 @@ await repository.open({signal});
|
|
|
7911
7927
|
await repository.close();
|
|
7912
7928
|
```
|
|
7913
7929
|
|
|
7930
|
+
## readGitIdentity()
|
|
7931
|
+
|
|
7932
|
+
### Overview
|
|
7933
|
+
|
|
7934
|
+
Reads non-secret global, repository-local and effective Git configuration
|
|
7935
|
+
without changing files or querying credentials.
|
|
7936
|
+
|
|
7937
|
+
### Signature and result
|
|
7938
|
+
|
|
7939
|
+
```text
|
|
7940
|
+
readGitIdentity({directory,signal,onEvent,run=runProcess}={})
|
|
7941
|
+
```
|
|
7942
|
+
|
|
7943
|
+
Import from `arcane-os/core/repositories`. Returns a promise for
|
|
7944
|
+
`{global,local,effective}`, each observed scope containing `{name,email,githubUser}`.
|
|
7945
|
+
Fields preserve complete UTF-8 strings, including empty values, leading U+FEFF
|
|
7946
|
+
and newlines; unset fields are `null`. Git values outside UTF-8 fail with
|
|
7947
|
+
`ARCANE_GIT_IDENTITY_NOT_TEXT` instead of replacement characters.
|
|
7948
|
+
With no directory, only global configuration is read and local/effective
|
|
7949
|
+
are `null`. Existing repository directories, including bare caches, select the
|
|
7950
|
+
context for all three observations. Configured includes and ordinary Git
|
|
7951
|
+
precedence remain in effect. These are config observations, not authenticated
|
|
7952
|
+
accounts or environment-resolved commit identities.
|
|
7953
|
+
|
|
7954
|
+
Independent scope queries run concurrently and join cleanup before settlement.
|
|
7955
|
+
Raw stdout is parser-owned rather than emitted as text events; an injected
|
|
7956
|
+
`run` must honor the existing raw-output process contract. Lifecycle and stderr
|
|
7957
|
+
events retain their ordinary route. Errors preserve their original object,
|
|
7958
|
+
code, cause and process diagnostics, with complete captured output added as
|
|
7959
|
+
`rawStdout` (a Buffer). The raw-mode process result retains `stdout:null`.
|
|
7960
|
+
If that property already exists or cannot be attached, a wrapper retains the
|
|
7961
|
+
original as `cause`, its code/details/exit code and this observation's raw
|
|
7962
|
+
output; original causal failures remain on the cause. An attachment exception
|
|
7963
|
+
is retained as `attachmentError`. Existing property values remain untouched.
|
|
7964
|
+
Decoding failures include their cause, complete process details and raw output.
|
|
7965
|
+
Already-represented observer errors appear once; separate failures use
|
|
7966
|
+
`AggregateError`. See the [complete contract and
|
|
7967
|
+
per-process selection example](core-repositories.md#git-identity-configuration).
|
|
7968
|
+
|
|
7914
7969
|
## createCoreLocalAIProvider()
|
|
7915
7970
|
|
|
7916
7971
|
### Overview
|
|
@@ -8141,7 +8196,7 @@ Whisper backend libraries, decoder and model files; see the complete
|
|
|
8141
8196
|
|
|
8142
8197
|
| Member | Contract |
|
|
8143
8198
|
| --- | --- |
|
|
8144
|
-
| `current()` | Returns `providerId`, `modelId`, `state`, `loaded`, `busy`, `requestId`, `requestedBackend`, `observedBackend`, `backendEvidence` and `error`. State reports actual helper/model readiness. |
|
|
8199
|
+
| `current()` | Returns `providerId`, `modelId`, `state`, `loaded`, `busy`, `requestId`, `requestedBackend`, `observedBackend`, `backendEvidence`, `requestedEncoder`, `observedEncoder`, `encoderEvidence` and `error`. State reports actual helper/model readiness; encoder and decoder initialization evidence remain separate. |
|
|
8145
8200
|
| `subscribe(listener,{replay=true,signal}={})` | Observes the current snapshot immediately by default, then each state change; returns the unsubscribe closure. |
|
|
8146
8201
|
| `load({modelId,signal}={})` | Prepares the selected runtime if necessary and retains its initialized helper/model. Same-model loading is shared; replacement remains explicit. |
|
|
8147
8202
|
| `transcribe(request,{signal,onProgress,requestId}={})` | Accepts the complete `{audioBase64,mimeType?,model?,language?,translate?}` recording for the loaded model and returns `{text,language,duration,segments}`. `audioBase64` transports the original media; the decoder recognizes its actual format. Progress is ordered; control metadata stays outside the request. |
|
|
@@ -8205,6 +8260,43 @@ includes the selected compiler's redistributable runtime closure. Use a
|
|
|
8205
8260
|
toolchain and prepared runtime matching the target platform/architecture; a
|
|
8206
8261
|
source package alone is not a compiled helper. See the [helper build procedure](local-whisper.md).
|
|
8207
8262
|
|
|
8263
|
+
## buildWhisperOpenVinoRuntime()
|
|
8264
|
+
|
|
8265
|
+
`buildWhisperOpenVinoRuntime({sourceDirectory,openvinoDirectory,cudaDirectory,
|
|
8266
|
+
outputRoot,cmake='cmake',generator,cmakeArgs=[],env,signal,onEvent}={})` from
|
|
8267
|
+
`arcane-os/local-ai/whisper/openvino-build` produces the optional native Intel
|
|
8268
|
+
NPU encoder runtime on Windows x64. This producer uses an already installed
|
|
8269
|
+
compatible CMake/MSVC environment, Whisper 1.9.4 source and OpenVINO 2026.3.1
|
|
8270
|
+
development tree. Required `sourceDirectory`, `openvinoDirectory` and
|
|
8271
|
+
`outputRoot` name those inputs and the producer-owned output. It installs no
|
|
8272
|
+
driver, toolchain or model and performs no inference.
|
|
8273
|
+
|
|
8274
|
+
The producer copies Whisper source into `outputRoot/source`, applies its
|
|
8275
|
+
encoder-failure propagation and stderr diagnostic corrections there, and runs
|
|
8276
|
+
CMake configure/build into `outputRoot/build`. The original source stays
|
|
8277
|
+
unchanged. It assembles the runtime under `outputRoot/runtime`, retaining
|
|
8278
|
+
OpenVINO/NPU, CPU, TBB, import libraries and packaging notices. Optional
|
|
8279
|
+
`cudaDirectory` names the matching upstream b5130 CUDA library directory;
|
|
8280
|
+
its selected precompiled backend libraries add CUDA alongside CPU. Omission
|
|
8281
|
+
produces the NPU-plus-CPU tree. `cmake`, `generator`, `cmakeArgs` and `env`
|
|
8282
|
+
configure the existing build environment.
|
|
8283
|
+
|
|
8284
|
+
The result is `{version,openvinoVersion,platform,architecture,encoder,backend,
|
|
8285
|
+
backends,root,directory,libraryDirectory,sourceDirectory,diagnostics}`.
|
|
8286
|
+
`directory` equals `root`; the returned `sourceDirectory` is the copied source.
|
|
8287
|
+
`diagnostics` retains complete `configure` and `compile` process results.
|
|
8288
|
+
`signal` and `onEvent` use the existing process cancellation and diagnostic
|
|
8289
|
+
contract. Missing required strings produce a `TypeError`; an unsupported
|
|
8290
|
+
producer host or unmatched source correction reports an error. Filesystem,
|
|
8291
|
+
process, cancellation and observer failures remain observable, with partial
|
|
8292
|
+
producer output left for its owner.
|
|
8293
|
+
|
|
8294
|
+
Build the matching first-party helper separately with `buildWhisperHelper`,
|
|
8295
|
+
then retain its complete installed `bin` alongside this runtime. The
|
|
8296
|
+
[complete producer example](../guides/native-whisper-openvino.md#produce-the-optional-native-runtime)
|
|
8297
|
+
covers that composition. Applications normally select the precompiled asset
|
|
8298
|
+
through `encoder: 'openvino-npu'`; they do not run this producer at startup.
|
|
8299
|
+
|
|
8208
8300
|
## createSpeechService()
|
|
8209
8301
|
|
|
8210
8302
|
### Overview and usage
|
package/package.json
CHANGED
|
@@ -5,6 +5,9 @@ import Is from 'strong-type';
|
|
|
5
5
|
import {ArcaneError, throwIfAborted} from '../errors.mjs';
|
|
6
6
|
import {runProcess} from '../process.mjs';
|
|
7
7
|
import {repositoryPull, repositoryPush, repositoryStatus} from '../repository.mjs';
|
|
8
|
+
import {createGitIdentityRunner} from '../git-identity.mjs';
|
|
9
|
+
|
|
10
|
+
export {readGitIdentity} from '../git-identity.mjs';
|
|
8
11
|
|
|
9
12
|
const is = new Is(false);
|
|
10
13
|
const directoryOperations = new Map();
|
|
@@ -41,7 +44,7 @@ export function resolveArcaneDataPaths({dataRoot} = {}) {
|
|
|
41
44
|
|
|
42
45
|
/** One connected checkout, composed into an application's own Core service. */
|
|
43
46
|
export function createRepositoryWorkspace(
|
|
44
|
-
{name, directory, dataRoot, remote, branch, onEvent, run = runProcess} = {}
|
|
47
|
+
{name, directory, dataRoot, remote, branch, gitIdentity, onEvent, run = runProcess} = {}
|
|
45
48
|
) {
|
|
46
49
|
let selected = directory;
|
|
47
50
|
if (selected === undefined) {
|
|
@@ -55,12 +58,13 @@ export function createRepositoryWorkspace(
|
|
|
55
58
|
if (remote !== undefined) requireString(remote, 'remote');
|
|
56
59
|
if (branch !== undefined) requireString(branch, 'branch');
|
|
57
60
|
if (!is.function(run)) throw new TypeError('run must implement the SDK process adapter.');
|
|
61
|
+
const execute = createGitIdentityRunner(run, gitIdentity);
|
|
58
62
|
const repositoryDirectory = path.resolve(selected);
|
|
59
63
|
const pending = new Set();
|
|
60
64
|
let opened = false;
|
|
61
65
|
let closing = null;
|
|
62
66
|
|
|
63
|
-
async function prepare(signal,
|
|
67
|
+
async function prepare(signal, prepareRun = execute) {
|
|
64
68
|
throwIfAborted(signal);
|
|
65
69
|
if (opened) return {directory: repositoryDirectory, cloned: false};
|
|
66
70
|
let entries;
|
|
@@ -74,7 +78,7 @@ export function createRepositoryWorkspace(
|
|
|
74
78
|
if (entries.length) {
|
|
75
79
|
// A parent checkout or a bare object cache is not this connection's
|
|
76
80
|
// working root. Inspection never changes an existing destination.
|
|
77
|
-
const result = await
|
|
81
|
+
const result = await prepareRun(
|
|
78
82
|
'git',
|
|
79
83
|
['rev-parse', '--is-inside-work-tree', '--show-prefix'],
|
|
80
84
|
{cwd: repositoryDirectory, signal, onEvent}
|
|
@@ -97,7 +101,7 @@ export function createRepositoryWorkspace(
|
|
|
97
101
|
const arguments_ = ['clone', '--progress'];
|
|
98
102
|
if (branch !== undefined) arguments_.push('--branch', branch);
|
|
99
103
|
arguments_.push('--', remote, repositoryDirectory);
|
|
100
|
-
const result = await
|
|
104
|
+
const result = await prepareRun(
|
|
101
105
|
'git',
|
|
102
106
|
arguments_,
|
|
103
107
|
{cwd: path.dirname(repositoryDirectory), signal, onEvent}
|
|
@@ -142,7 +146,7 @@ export function createRepositoryWorkspace(
|
|
|
142
146
|
return accept(
|
|
143
147
|
async function readRepositoryStatus() {
|
|
144
148
|
await prepare(signal);
|
|
145
|
-
return repositoryStatus({workspaceRoot: repositoryDirectory, signal, onEvent, run});
|
|
149
|
+
return repositoryStatus({workspaceRoot: repositoryDirectory, signal, onEvent, run: execute});
|
|
146
150
|
},
|
|
147
151
|
signal
|
|
148
152
|
);
|
|
@@ -152,7 +156,7 @@ export function createRepositoryWorkspace(
|
|
|
152
156
|
return accept(
|
|
153
157
|
async function pullRepository() {
|
|
154
158
|
await prepare(signal);
|
|
155
|
-
return repositoryPull({workspaceRoot: repositoryDirectory, signal, onEvent, run});
|
|
159
|
+
return repositoryPull({workspaceRoot: repositoryDirectory, signal, onEvent, run: execute});
|
|
156
160
|
},
|
|
157
161
|
signal
|
|
158
162
|
);
|
|
@@ -162,7 +166,7 @@ export function createRepositoryWorkspace(
|
|
|
162
166
|
return accept(
|
|
163
167
|
async function pushRepository() {
|
|
164
168
|
await prepare(signal);
|
|
165
|
-
return repositoryPush({workspaceRoot: repositoryDirectory, signal, onEvent, run});
|
|
169
|
+
return repositoryPush({workspaceRoot: repositoryDirectory, signal, onEvent, run: execute});
|
|
166
170
|
},
|
|
167
171
|
signal
|
|
168
172
|
);
|
|
@@ -260,7 +264,7 @@ export function createRepositoryWorkspace(
|
|
|
260
264
|
}
|
|
261
265
|
|
|
262
266
|
try {
|
|
263
|
-
const result = await
|
|
267
|
+
const result = await execute(
|
|
264
268
|
'git',
|
|
265
269
|
args,
|
|
266
270
|
{cwd: options.cwd ?? repositoryDirectory, signal, onEvent: observeEvent,
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import Is from 'strong-type';
|
|
3
|
+
import {ArcaneError, throwIfAborted} from './errors.mjs';
|
|
4
|
+
import {runProcess} from './process.mjs';
|
|
5
|
+
import {createEventQueue} from './event-queue.mjs';
|
|
6
|
+
|
|
7
|
+
const is = new Is(false);
|
|
8
|
+
|
|
9
|
+
/** Read only non-secret identity configuration through ordinary Git scopes. */
|
|
10
|
+
export async function readGitIdentity({directory, signal, onEvent, run = runProcess} = {}) {
|
|
11
|
+
const cwd = directory === undefined ? undefined : path.resolve(directory);
|
|
12
|
+
throwIfAborted(signal);
|
|
13
|
+
const events = createEventQueue(onEvent);
|
|
14
|
+
|
|
15
|
+
async function observeScope(scope) {
|
|
16
|
+
const args = ['config'];
|
|
17
|
+
if (scope !== 'effective') args.push(`--${scope}`);
|
|
18
|
+
args.push('--includes', '--null', '--get-regexp', '^(user[.](name|email)|github[.]user)$');
|
|
19
|
+
const chunks = [];
|
|
20
|
+
try {
|
|
21
|
+
const result = await run('git', args, {
|
|
22
|
+
cwd, signal, onEvent: events.send, allowNonzero: true,
|
|
23
|
+
outputEncoding: {stdout: null},
|
|
24
|
+
captureOutput: {stdout: false},
|
|
25
|
+
emitOutputEvents: {stdout: false},
|
|
26
|
+
// Git configuration may contain non-UTF-8 values. Keep its raw
|
|
27
|
+
// output until close rather than silently replacing characters.
|
|
28
|
+
onOutput: function observeOutput({stream, chunk}) {
|
|
29
|
+
if (stream === 'stdout') chunks.push(chunk);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
try { throwIfAborted(signal); }
|
|
33
|
+
catch (error) { error.details = result; throw error; }
|
|
34
|
+
const identity = {name: null, email: null, githubUser: null};
|
|
35
|
+
if (result.code === 1) return identity;
|
|
36
|
+
if (result.code !== 0) {
|
|
37
|
+
throw new ArcaneError('ARCANE_OPERATION_FAILED',
|
|
38
|
+
`Git ${scope} identity lookup exited with code ${String(result.code)}.`, {details: result});
|
|
39
|
+
}
|
|
40
|
+
let output;
|
|
41
|
+
try { output = new TextDecoder('utf-8', {fatal: true, ignoreBOM: true}).decode(Buffer.concat(chunks)); }
|
|
42
|
+
catch (cause) {
|
|
43
|
+
throw new ArcaneError('ARCANE_GIT_IDENTITY_NOT_TEXT',
|
|
44
|
+
`Git ${scope} identity configuration cannot be represented as UTF-8 text.`, {cause, details: result});
|
|
45
|
+
}
|
|
46
|
+
const fields = {'user.name': 'name', 'user.email': 'email', 'github.user': 'githubUser'};
|
|
47
|
+
// Git's NUL record framing preserves complete values, including newlines
|
|
48
|
+
// and empty strings. The last occurrence is the effective scalar value.
|
|
49
|
+
for (const record of output.split('\0')) {
|
|
50
|
+
if (record === '') continue;
|
|
51
|
+
const separator = record.indexOf('\n');
|
|
52
|
+
const key = separator === -1 ? record : record.slice(0, separator);
|
|
53
|
+
if (Object.hasOwn(fields, key)) {
|
|
54
|
+
identity[fields[key]] = separator === -1 ? '' : record.slice(separator + 1);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return identity;
|
|
58
|
+
} catch (error) {
|
|
59
|
+
// Keep the process error and its native details intact. Raw stdout
|
|
60
|
+
// belongs to this reader because the process owner did not decode it.
|
|
61
|
+
const rawStdout = Buffer.concat(chunks);
|
|
62
|
+
let attached = false;
|
|
63
|
+
let attachmentError;
|
|
64
|
+
try {
|
|
65
|
+
if (error !== null && (is.object(error) || is.function(error)) && !('rawStdout' in error)) {
|
|
66
|
+
Object.defineProperty(error, 'rawStdout', {
|
|
67
|
+
value: rawStdout, writable: true, enumerable: true, configurable: true
|
|
68
|
+
});
|
|
69
|
+
attached = true;
|
|
70
|
+
}
|
|
71
|
+
} catch (cause) { attachmentError = cause; }
|
|
72
|
+
if (attached) throw error;
|
|
73
|
+
const failure = new ArcaneError(error?.code ?? 'ARCANE_OPERATION_FAILED',
|
|
74
|
+
error instanceof Error ? error.message : String(error),
|
|
75
|
+
{cause: error, details: error?.details, exitCode: error?.exitCode});
|
|
76
|
+
failure.rawStdout = rawStdout;
|
|
77
|
+
if (attachmentError) failure.attachmentError = attachmentError;
|
|
78
|
+
throw failure;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const scopes = cwd === undefined ? ['global'] : ['global', 'local', 'effective'];
|
|
83
|
+
const observations = await Promise.allSettled(scopes.map(observeScope));
|
|
84
|
+
const failures = observations.filter(function failed(result) {
|
|
85
|
+
return result.status === 'rejected';
|
|
86
|
+
}).map(function cause(result) { return result.reason; });
|
|
87
|
+
try { await events.drain(); }
|
|
88
|
+
catch (error) {
|
|
89
|
+
const visited = new Set();
|
|
90
|
+
function containsObserverFailure(failure) {
|
|
91
|
+
if (failure === error) return true;
|
|
92
|
+
if (failure === null || failure === undefined || visited.has(failure)) return false;
|
|
93
|
+
visited.add(failure);
|
|
94
|
+
return containsObserverFailure(failure.cause)
|
|
95
|
+
|| (is.array(failure.errors) && failure.errors.some(containsObserverFailure));
|
|
96
|
+
}
|
|
97
|
+
const represented = failures.some(containsObserverFailure);
|
|
98
|
+
if (!represented) failures.push(error);
|
|
99
|
+
}
|
|
100
|
+
if (failures.length === 1) throw failures[0];
|
|
101
|
+
if (failures.length) throw new AggregateError(failures, 'Git identity observations failed.');
|
|
102
|
+
const result = {global: null, local: null, effective: null};
|
|
103
|
+
for (const [index, scope] of scopes.entries()) result[scope] = observations[index].value;
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Capture one factory's selection without changing Git files or process.env. */
|
|
108
|
+
export function createGitIdentityRunner(run, {name, email, username} = {}) {
|
|
109
|
+
const configuration = [];
|
|
110
|
+
const environment = {};
|
|
111
|
+
for (const [field, value] of Object.entries({name, email, username})) {
|
|
112
|
+
if (value === undefined) continue;
|
|
113
|
+
if (!is.string(value) || !value.isWellFormed() || value.includes('\0')) {
|
|
114
|
+
throw new TypeError(`gitIdentity.${field} must be text representable as a native process argument.`);
|
|
115
|
+
}
|
|
116
|
+
configuration.push('-c', `${field === 'username' ? 'credential.username' : `user.${field}`}=${value}`);
|
|
117
|
+
if (field === 'name') {
|
|
118
|
+
environment.GIT_AUTHOR_NAME = value;
|
|
119
|
+
environment.GIT_COMMITTER_NAME = value;
|
|
120
|
+
} else if (field === 'email') {
|
|
121
|
+
environment.GIT_AUTHOR_EMAIL = value;
|
|
122
|
+
environment.GIT_COMMITTER_EMAIL = value;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (!configuration.length) return run;
|
|
126
|
+
const selectsAuthor = name !== undefined || email !== undefined;
|
|
127
|
+
return function runSelectedGit(command, args, options = {}) {
|
|
128
|
+
return run(command, [...configuration, ...args], selectsAuthor
|
|
129
|
+
? {...options, env: {...options.env, ...environment}}
|
|
130
|
+
: options);
|
|
131
|
+
};
|
|
132
|
+
}
|
|
@@ -4,6 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import {runProcess} from './process.mjs';
|
|
5
5
|
import {ArcaneError, ERROR_CODES, throwIfAborted} from './errors.mjs';
|
|
6
6
|
import {createEventQueue} from './event-queue.mjs';
|
|
7
|
+
import {createGitIdentityRunner} from './git-identity.mjs';
|
|
7
8
|
|
|
8
9
|
const is = new Is(false);
|
|
9
10
|
const snapshotRef = 'refs/arcane/text-snapshot';
|
|
@@ -13,11 +14,12 @@ const snapshotRef = 'refs/arcane/text-snapshot';
|
|
|
13
14
|
* One application service owns each dedicated cache directory. Construction
|
|
14
15
|
* performs no I/O; refresh coalesces callers and close drains accepted work.
|
|
15
16
|
*/
|
|
16
|
-
export function createGitTextSnapshot({cacheDirectory, remote, ref, selectPath, onEvent, run = runProcess} = {}) {
|
|
17
|
+
export function createGitTextSnapshot({cacheDirectory, remote, ref, selectPath, gitIdentity, onEvent, run = runProcess} = {}) {
|
|
17
18
|
for (const [name, value] of Object.entries({cacheDirectory, remote, ref})) {
|
|
18
19
|
if (!is.string(value) || value === '') throw new TypeError(`${name} must be a nonempty string.`);
|
|
19
20
|
}
|
|
20
21
|
if (!is.function(selectPath)) throw new TypeError('selectPath must be a function.');
|
|
22
|
+
const execute = createGitIdentityRunner(run, gitIdentity);
|
|
21
23
|
const directory = path.resolve(cacheDirectory);
|
|
22
24
|
let prepared = false;
|
|
23
25
|
let retained = null;
|
|
@@ -34,7 +36,7 @@ export function createGitTextSnapshot({cacheDirectory, remote, ref, selectPath,
|
|
|
34
36
|
};
|
|
35
37
|
}
|
|
36
38
|
try {
|
|
37
|
-
return await
|
|
39
|
+
return await execute('git', ['--git-dir', directory, ...args], execution);
|
|
38
40
|
} catch (error) {
|
|
39
41
|
// runProcess first drains its child and preserves complete transport
|
|
40
42
|
// failures. Keep the Git parser's public code with that full cause.
|
|
@@ -50,7 +52,7 @@ export function createGitTextSnapshot({cacheDirectory, remote, ref, selectPath,
|
|
|
50
52
|
if (prepared) return;
|
|
51
53
|
await mkdir(directory, {recursive: true});
|
|
52
54
|
if ((await readdir(directory)).length === 0) {
|
|
53
|
-
await
|
|
55
|
+
await execute('git', ['init', '--bare', directory], {onEvent: events.send});
|
|
54
56
|
}
|
|
55
57
|
const result = await git(['rev-parse', '--is-bare-repository'], {onEvent: events.send});
|
|
56
58
|
// This is the selected cache's functional role: never reinterpret an
|