arcane-os 0.57.0 → 0.59.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 +37 -0
- package/README.md +6 -2
- package/browser-runtime/ai/core-onnx.mjs +96 -0
- package/browser-runtime/ai/onnx-tensors.mjs +85 -0
- package/browser-runtime/core/client.mjs +152 -17
- package/docs/architecture.md +5 -2
- package/docs/platform-targets.md +30 -13
- package/docs/reference/README.md +22 -3
- package/docs/reference/availability-and-normalization.md +14 -7
- package/docs/reference/cli.md +22 -10
- package/docs/reference/core-client.md +87 -0
- package/docs/reference/core-native-packaging.md +129 -9
- package/docs/reference/core-preferences.md +49 -0
- package/docs/reference/diarization.md +206 -0
- package/docs/reference/git-text-snapshot.md +104 -0
- package/docs/reference/inventory/package-api.json +205 -23
- package/docs/reference/local-ai.md +145 -11
- package/docs/reference/protocols.md +28 -19
- package/docs/reference/pwa.md +74 -4
- package/docs/reference/sdk-api.md +589 -39
- package/package.json +11 -3
- package/schemas/arcane-app.schema.json +3 -3
- package/schemas/arcane-package.schema.json +2 -2
- package/schemas/native-build-plan.schema.json +27 -1
- package/schemas/target-adapter.schema.json +2 -0
- package/src/app-descriptor.mjs +1 -1
- package/src/cli/main.mjs +14 -4
- package/src/constants.mjs +2 -0
- package/src/core/host.mjs +14 -5
- package/src/core/hosts/macos/ArcaneHost.h +86 -0
- package/src/core/hosts/macos/ArcaneHost.m +455 -0
- package/src/core/hosts/macos/ArcaneLauncher.m +432 -0
- package/src/core/hosts/windows/ArcaneLauncher.cs +4 -1
- package/src/core/services/local-ai.mjs +82 -4
- package/src/core/services/packaged-web.mjs +89 -0
- package/src/core/services/preferences.mjs +201 -0
- package/src/core-classic-source.mjs +2 -2
- package/src/dev-server.mjs +2 -5
- package/src/diarization/build.mjs +35 -0
- package/src/diarization/index.mjs +432 -0
- package/src/diarization/native/CMakeLists.txt +9 -0
- package/src/diarization/native/main.cpp +269 -0
- package/src/diarization/service.mjs +129 -0
- package/src/git-text-snapshot.mjs +265 -0
- package/src/import-map.mjs +9 -4
- package/src/index.mjs +1 -0
- package/src/local-ai/config.mjs +1 -1
- package/src/local-ai/install.mjs +134 -19
- package/src/local-ai/onnx-worker.mjs +85 -0
- package/src/local-ai/onnx.mjs +368 -0
- package/src/native/macos-provider.mjs +291 -0
- package/src/native/portable-layout.mjs +10 -2
- package/src/native/portable-provider.mjs +10 -5
- package/src/native-plan.mjs +9 -1
- package/src/native-provider-loader.mjs +3 -1
- package/src/process.mjs +123 -17
- package/src/scaffold.mjs +1 -1
- package/src/targets/index.mjs +11 -1
- package/src/templates/workspace-template.mjs +6 -5
- package/src/toolchain.mjs +42 -13
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# Nemotron 3 speaker diarization
|
|
2
|
+
|
|
3
|
+
The native diarization API uses the NVIDIA `Nemotron-3-Diarization.q8_0.gguf`
|
|
4
|
+
model through the official NeMo-Speech.cpp 0.2.0 CPU libraries. It returns
|
|
5
|
+
generic, one-based speaker labels and timed segments, independently of speech
|
|
6
|
+
recognition. A speaker label does not identify a person. The model supports
|
|
7
|
+
eight speaker channels; the helper reports the loaded model's actual channel
|
|
8
|
+
count and output cadence.
|
|
9
|
+
|
|
10
|
+
## Runtime and helper
|
|
11
|
+
|
|
12
|
+
Select the `nemo-speech` runtime through the existing local-AI installer. Keep
|
|
13
|
+
its complete upstream `bin`/`lib`/`include` and CMake layout. Model storage is
|
|
14
|
+
separate and caller-selected. Obtain the selected GGUF from the
|
|
15
|
+
[official NVIDIA model repository](https://huggingface.co/nvidia/Nemotron-3-Diarization).
|
|
16
|
+
This API does not select an ASR model, install Python, or enable a GPU backend.
|
|
17
|
+
|
|
18
|
+
`buildDiarizationHelper({runtime,outputRoot,signal,onEvent})` builds the
|
|
19
|
+
first-party helper for the current host with installed CMake and its C++17
|
|
20
|
+
toolchain. `runtime.cmakeDirectory` is the installer-discovered CMake package
|
|
21
|
+
directory, passed as `NeMoSpeech_DIR` without assuming an archive root. The result
|
|
22
|
+
is `{platform,architecture,root,executable,diagnostics}`; diagnostics retain the
|
|
23
|
+
complete configure/compile/install process results. The installed helper is under
|
|
24
|
+
`outputRoot/bin`; its build directory is under `outputRoot/build`. The helper
|
|
25
|
+
links the upstream `NeMoSpeech::Diarization` component and is separate from
|
|
26
|
+
the downloaded runtime. No compiler is downloaded by this function.
|
|
27
|
+
|
|
28
|
+
Official 0.2.0 CPU archives cover Windows x64, Linux x64/ARM64, and macOS
|
|
29
|
+
x64/ARM64. Linux requires glibc 2.31 or later. Android needs a host-adapted
|
|
30
|
+
runtime/helper; this release's official inventory does not supply an Android
|
|
31
|
+
or Windows ARM64 archive. Source portability is distinct from execution on an
|
|
32
|
+
actual platform.
|
|
33
|
+
|
|
34
|
+
The public Node flow uses the existing installer and one first-party helper:
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
import {ensureLocalAIRuntimes} from 'arcane-os/local-ai';
|
|
38
|
+
import {buildDiarizationHelper} from 'arcane-os/diarization/build';
|
|
39
|
+
import {createDiarization} from 'arcane-os/diarization';
|
|
40
|
+
|
|
41
|
+
const [runtime] = await ensureLocalAIRuntimes({
|
|
42
|
+
runtimes: [{id: 'nemo-speech', version: '0.2.0'}],
|
|
43
|
+
directory: selectedRuntimeDirectory
|
|
44
|
+
});
|
|
45
|
+
const helper = await buildDiarizationHelper({runtime, outputRoot: selectedHelperDirectory});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Keep the selected directories and model path under their application/host
|
|
49
|
+
owner. A native artifact can place the first-party executable under
|
|
50
|
+
`runtime/diarization/bin`, independently of the complete upstream library tree
|
|
51
|
+
under `runtime/local-ai/nemo-speech`.
|
|
52
|
+
|
|
53
|
+
## Retained model and audio streams
|
|
54
|
+
|
|
55
|
+
`createDiarization({executable,modelPath,runtime,onEvent})` returns immediately
|
|
56
|
+
and starts its owned helper. Supply absolute helper/model paths and the
|
|
57
|
+
installer's absolute `runtime.binaryDirectory`/`runtime.libraryDirectory` paths. They configure the
|
|
58
|
+
child's platform library search path; the parent environment is unchanged.
|
|
59
|
+
|
|
60
|
+
The returned model has `ready`, `completion`, `current()`,
|
|
61
|
+
`subscribe(listener,{emitCurrent=true,signal})`, `openStream(options)`,
|
|
62
|
+
`diarize(options)`, and `close()`. `ready` resolves when the actual model is
|
|
63
|
+
loaded; a failed load rejects. `completion` covers the complete helper lifetime.
|
|
64
|
+
The sticky state is `loading`, `ready`, `closing`, `closed`, or `error`.
|
|
65
|
+
Subscribe to this owner instead of polling or delaying unrelated UI startup.
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
const diarization = createDiarization({
|
|
69
|
+
executable: helper.executable,
|
|
70
|
+
modelPath: selectedModelPath,
|
|
71
|
+
runtime
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const stream = await diarization.openStream({
|
|
75
|
+
sampleRate: 16000,
|
|
76
|
+
onUpdate: function showSpeakers(result) {
|
|
77
|
+
renderSpeakerTimeline(result.segments);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
await stream.push(firstMonoSamples);
|
|
82
|
+
await stream.push(nextMonoSamples);
|
|
83
|
+
const final = await stream.finish();
|
|
84
|
+
await diarization.close();
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`push(audio)` accepts mono `Float32Array` samples and preserves their float32
|
|
88
|
+
representation. The caller owns decoding an encoded recording and choosing
|
|
89
|
+
its mono channel; this API does not alter the source recording, mix channels,
|
|
90
|
+
normalize amplitudes, or capture a microphone. Keep the supplied array stable
|
|
91
|
+
until `push` settles. Each stream accepts one fixed sample rate: 8000–96000 Hz,
|
|
92
|
+
or `0` for model-rate samples. The default is 16000 Hz. Upstream owns necessary
|
|
93
|
+
resampling. Await each push for backpressure; independent streams share the
|
|
94
|
+
retained model without sharing speaker/session state. Upstream serializes
|
|
95
|
+
model compute internally.
|
|
96
|
+
|
|
97
|
+
`openStream({sampleRate,onUpdate,onProbabilities,signal})` returns a stream with
|
|
98
|
+
`push`, `finish`, `cancel`, `completion`, and `current()`. A push resolves to
|
|
99
|
+
its latest segment snapshot, or `null` until native frames are available. `finish()`
|
|
100
|
+
flushes all remaining audio and resampler output, returns the complete final
|
|
101
|
+
result, and releases the native stream. It is idempotent. `current()` retains
|
|
102
|
+
only the latest successful snapshot. Results have:
|
|
103
|
+
|
|
104
|
+
```text
|
|
105
|
+
{final, speakers, secondsPerFrame, frameCount,
|
|
106
|
+
segments:[{speaker,startTime,endTime}, ...]}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Times are seconds from the start of that caller's stream. Intermediate segment
|
|
110
|
+
snapshots may revise earlier segment boundaries. Replace the displayed
|
|
111
|
+
snapshot; do not append whole snapshots as new segments. Final results include
|
|
112
|
+
all upstream segments, including those finalized before upstream probability
|
|
113
|
+
compaction. The API adds no diarization thresholds or segment filtering.
|
|
114
|
+
|
|
115
|
+
For recorded mono samples, `await diarization.diarize({audio,sampleRate,signal,
|
|
116
|
+
onUpdate,onProbabilities})` uses the same complete streaming path, then finishes
|
|
117
|
+
and closes that stream. It does not use upstream's length-constrained stateless
|
|
118
|
+
offline operation.
|
|
119
|
+
|
|
120
|
+
## Probabilities, callbacks, and cancellation
|
|
121
|
+
|
|
122
|
+
Supply `onProbabilities` to receive every available probability frame as
|
|
123
|
+
`{startFrame,values,speakers,secondsPerFrame}`. Values are frame-major, with one
|
|
124
|
+
value per speaker channel per frame. Frame indices are absolute within the
|
|
125
|
+
stream. The adapter reads between 160 ms input pushes, before upstream's old
|
|
126
|
+
probability buffer can be compacted. It reports an error if an upstream gap is
|
|
127
|
+
ever observed; it does not manufacture missing values. Without this callback,
|
|
128
|
+
the helper does not request raw probabilities.
|
|
129
|
+
|
|
130
|
+
Callbacks are awaited and their failures propagate. The caller owns retaining
|
|
131
|
+
probability history, audio, and any earlier snapshots it needs. The SDK does
|
|
132
|
+
not accumulate a second transcript of continuous protocol output. `onEvent`
|
|
133
|
+
receives ordinary process lifecycle events and complete stderr diagnostics;
|
|
134
|
+
raw stdout protocol belongs to the result callbacks instead. Keep engineering
|
|
135
|
+
diagnostics in a selected developer surface, outside ordinary UI/chat history.
|
|
136
|
+
|
|
137
|
+
`cancel(reason)` and the stream's abort signal stop subsequent pushes and
|
|
138
|
+
suppress later result commits for that stream. Cancellation closes the stream
|
|
139
|
+
after native commands already ahead of its close have returned; the C ABI
|
|
140
|
+
does not offer an immediate interrupt. Other streams keep their own state.
|
|
141
|
+
An already-running caller callback finishes under its caller's ownership.
|
|
142
|
+
`cancel()` can be awaited inside a callback. Await the same stream's `push()`,
|
|
143
|
+
`finish()`, or model `close()`
|
|
144
|
+
only after the callback returns; a recursive wait reports
|
|
145
|
+
`DIARIZATION_CALLBACK_WAIT` instead of deadlocking. Stream `completion`
|
|
146
|
+
retains finish/cancellation cleanup failures.
|
|
147
|
+
|
|
148
|
+
`close()` stops new streams, drains in-flight openings and accepted stream work,
|
|
149
|
+
finishes the remaining streams, closes helper stdin, and waits for native
|
|
150
|
+
cleanup/process exit. The model always outlives its streams. Complete failures
|
|
151
|
+
from an operation and its cleanup remain available together.
|
|
152
|
+
|
|
153
|
+
## Core service
|
|
154
|
+
|
|
155
|
+
Import the named or default factory from `arcane-os/core/diarization`.
|
|
156
|
+
|
|
157
|
+
`createDiarizationService({executable,modelPath,runtime,onEvent},launchContext)`
|
|
158
|
+
is a synchronous `native.services` factory. Relative executable/model paths
|
|
159
|
+
resolve against explicit `launchContext.workspaceRoot`, otherwise the native
|
|
160
|
+
launcher's `appRoot` (or the current directory for standalone factories). Model loading is lazy at the
|
|
161
|
+
first diarization operation, not a prerequisite for Core startup. The returned
|
|
162
|
+
service implements `drain()` and `dispose()` and the following methods:
|
|
163
|
+
|
|
164
|
+
| Method | Input / result |
|
|
165
|
+
| --- | --- |
|
|
166
|
+
| `diarization.status` | Current model lifecycle snapshot. |
|
|
167
|
+
| `diarization.load` | Load the configured model; return readiness. |
|
|
168
|
+
| `diarization.open` | `{sampleRate,probabilities}` → `{streamId,sampleRate}`. |
|
|
169
|
+
| `diarization.push` | `{streamId,audio}` → current timed segment snapshot. |
|
|
170
|
+
| `diarization.finish` | `{streamId}` → complete final result, releasing the stream. |
|
|
171
|
+
| `diarization.cancel` | `{streamId}` → cleanup completion for that stream. |
|
|
172
|
+
| `diarization.recording` | `{audio,sampleRate}` → complete final result. |
|
|
173
|
+
|
|
174
|
+
Core audio is `{encoding:'f32le',data:BASE64}`: transport encoding of the exact
|
|
175
|
+
little-endian float32 samples, with control metadata outside the audio. The
|
|
176
|
+
service emits `diarization.state`, `diarization.update` with `{streamId,result}`,
|
|
177
|
+
and optional `diarization.probabilities` with the stream ID and frame data.
|
|
178
|
+
Subscribe before opening a stream. Session operations have service lifetime;
|
|
179
|
+
renderer request cancellation does not discard accepted audio. Call the
|
|
180
|
+
explicit cancel method for a session. A recording operation has request
|
|
181
|
+
lifetime and its cancellation closes only that recording's stream.
|
|
182
|
+
|
|
183
|
+
## Shared process transport
|
|
184
|
+
|
|
185
|
+
The existing internal `runProcess` owner accepts scalar input as before and
|
|
186
|
+
also an `AsyncIterable<string|Uint8Array>`. Iteration is backpressured by the
|
|
187
|
+
child's stdin writes; iterable completion sends EOF, including in `close-input`
|
|
188
|
+
mode. Cancellation/early child exit calls the iterator's `return()` and waits
|
|
189
|
+
for input cleanup. The producer must make a pending `next()` settle when it is
|
|
190
|
+
returned/cancelled. The diarization-owned producer does this explicitly.
|
|
191
|
+
|
|
192
|
+
`onOutput({stream,chunk})` receives complete decoded UTF-8 chunks with original
|
|
193
|
+
delimiters and is awaited per stream. It is not a binary stdout interface.
|
|
194
|
+
`captureOutput` and `emitOutputEvents` default to `true`; each also accepts
|
|
195
|
+
`{stdout,stderr}` overrides. Disabling capture requires an output consumer and
|
|
196
|
+
returns `null` for that process-result field; it does not return a shortened
|
|
197
|
+
transcript. Disabling output events leaves those events absent. Existing
|
|
198
|
+
scalar input, full text results and ordinary event defaults remain unchanged.
|
|
199
|
+
Failed output callbacks retain their complete input chunk and cause in the
|
|
200
|
+
process error, including when successful output is caller-owned.
|
|
201
|
+
|
|
202
|
+
## Upstream contracts
|
|
203
|
+
|
|
204
|
+
The implementation follows the [stable C ABI](https://github.com/NVIDIA/NeMo-Speech.cpp/blob/v0.2.0/include/nemo_speech/diar.h),
|
|
205
|
+
[SDK linkage guide](https://github.com/NVIDIA/NeMo-Speech.cpp/blob/v0.2.0/docs/sdk.md),
|
|
206
|
+
and [official 0.2.0 release](https://github.com/NVIDIA/NeMo-Speech.cpp/releases/tag/v0.2.0).
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Complete Git text snapshots
|
|
2
|
+
|
|
3
|
+
`createGitTextSnapshot` from `arcane-os` reads application-selected UTF-8 text
|
|
4
|
+
from one fetched Git revision. It uses a dedicated **bare object cache**, not a
|
|
5
|
+
clone with a working checkout. It never reads uncommitted application files,
|
|
6
|
+
checks out files, modifies a user checkout/index, or creates another worktree.
|
|
7
|
+
Construction performs no I/O. The native host needs Git available on its PATH;
|
|
8
|
+
Git owns remote access, authentication and its actual failures.
|
|
9
|
+
|
|
10
|
+
```js
|
|
11
|
+
import {createGitTextSnapshot} from 'arcane-os';
|
|
12
|
+
|
|
13
|
+
const snapshot = createGitTextSnapshot({
|
|
14
|
+
cacheDirectory: applicationCacheDirectory,
|
|
15
|
+
remote: applicationRepository,
|
|
16
|
+
ref: applicationRef,
|
|
17
|
+
selectPath: function selectMoonDispatches(filename) {
|
|
18
|
+
return filename.startsWith('dispatches/') && filename.endsWith('.md');
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const {revision, files} = await snapshot.refresh({signal});
|
|
23
|
+
// files: [{path: 'dispatches/cheese-emergency.md', content: completeText}]
|
|
24
|
+
await snapshot.close();
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The application owns its remote, ref, path policy, repository identity, refresh
|
|
28
|
+
schedule and Core service factory. The SDK does not select `main`, poll, parse
|
|
29
|
+
Markdown/YAML, label or rewrite documents, or choose a product RPC method.
|
|
30
|
+
`selectPath(path)` may return a boolean or a promise of one. It receives each
|
|
31
|
+
complete repository-relative leaf path in Git tree order. Keep the selector's
|
|
32
|
+
policy stable for the lifetime of its snapshot owner.
|
|
33
|
+
|
|
34
|
+
## API and lifecycle
|
|
35
|
+
|
|
36
|
+
`createGitTextSnapshot({cacheDirectory,remote,ref,selectPath,onEvent,run})`
|
|
37
|
+
returns `{refresh,close,drain,dispose}`. `run` defaults to the SDK's existing
|
|
38
|
+
`runProcess`; applications normally omit it. It is the same process adapter
|
|
39
|
+
contract, not a second process supervisor. `onEvent` is the ordinary SDK event
|
|
40
|
+
callback and follows its asynchronous completion/error contract.
|
|
41
|
+
|
|
42
|
+
- `refresh({signal}={})` fetches the selected ref and returns
|
|
43
|
+
`{revision,files:[{path,content}]}`. `revision` is Git's opaque selected commit
|
|
44
|
+
name, not an SDK integrity record. Every file comes from that exact revision,
|
|
45
|
+
even if the remote moves during retrieval. Order follows Git's tree listing.
|
|
46
|
+
- Concurrent calls share the active refresh. A later call fetches again; when
|
|
47
|
+
the selected revision is unchanged, it reuses the completed text retrieval.
|
|
48
|
+
Returned arrays and records are independent copies, so caller edits do not
|
|
49
|
+
alter retained results. Failed refreshes report errors, never stale success.
|
|
50
|
+
- A caller's abort cancels only that caller's wait. Accepted shared refresh
|
|
51
|
+
continues, including when every caller leaves. There is no incomplete result
|
|
52
|
+
presented as a complete snapshot. A pre-aborted call starts no work.
|
|
53
|
+
- `close()`, `drain()` and `dispose()` are the same idempotent operation. They
|
|
54
|
+
stop acceptance and await accepted refresh work and its process/event cleanup.
|
|
55
|
+
Calls after close reject with `CORE_CLOSING`. A failure during drain rejects
|
|
56
|
+
close. Connect this hook to the Core service's `drain`/`dispose` lifecycle;
|
|
57
|
+
do not terminate its process before accepted work settles.
|
|
58
|
+
|
|
59
|
+
One application service owns each cache directory. Sharing that mutable bare
|
|
60
|
+
cache across independent instances or processes requires coordination by its
|
|
61
|
+
application owner; this factory does not install a daemon or cross-process lock.
|
|
62
|
+
An empty cache is initialized once. An existing non-bare checkout is rejected
|
|
63
|
+
without repurposing it. The cache is retained when the owner closes.
|
|
64
|
+
|
|
65
|
+
## Complete text and observable failures
|
|
66
|
+
|
|
67
|
+
Content is the exact Git blob text, including whitespace, CRLF, newlines,
|
|
68
|
+
leading BOM and embedded NUL characters. No checkout filters, `textconv`,
|
|
69
|
+
normalization, trimming, truncation, freezing or application size gate applies.
|
|
70
|
+
Symlink blobs expose their stored target text; they are never followed.
|
|
71
|
+
Selected submodules are not text blobs and produce an explicit error rather
|
|
72
|
+
than being skipped or fetched as another repository. A selected path or blob
|
|
73
|
+
that cannot be decoded as UTF-8 fails with `ARCANE_GIT_SNAPSHOT_NOT_TEXT` instead
|
|
74
|
+
of silently substituting replacement characters. Git errors and complete
|
|
75
|
+
diagnostics remain available to the caller and the existing process event owner.
|
|
76
|
+
|
|
77
|
+
`git.snapshot.refreshing`, `git.snapshot.completed` and `git.snapshot.failed`
|
|
78
|
+
describe the shared operation. Completion data contains `{revision,reused}`;
|
|
79
|
+
failure data contains the actual error. These events supplement the complete
|
|
80
|
+
result, never replace it. An event callback must not await a new refresh of the
|
|
81
|
+
same owner from inside the operation it is currently observing.
|
|
82
|
+
|
|
83
|
+
Initial refresh performs bare initialization/role discovery, then fetch,
|
|
84
|
+
revision resolution, one tree listing and one batched blob process. Later
|
|
85
|
+
unchanged revisions need only fetch and revision resolution. A changed revision
|
|
86
|
+
uses one tree process and one blob process regardless of the selected file
|
|
87
|
+
count. An empty selection needs no blob process. Blob requests use the shared
|
|
88
|
+
async process-input owner; raw stdout is consumed by the Git parser with
|
|
89
|
+
backpressure. Git's encoded body extent and object names remain local to its
|
|
90
|
+
[batch protocol](https://git-scm.com/docs/git-cat-file#_batch_output); they do
|
|
91
|
+
not become product metadata, byte progress, admission or integrity policy.
|
|
92
|
+
|
|
93
|
+
## Core service composition
|
|
94
|
+
|
|
95
|
+
The application wraps this mechanism in its own service factory and supplies its
|
|
96
|
+
own public method and result fields. The handler can pass `context.signal` to
|
|
97
|
+
`refresh` for request cancellation, then map `revision` to its chosen remote-ref
|
|
98
|
+
field without changing any `content`. Return the snapshot owner's `drain` and
|
|
99
|
+
`dispose` hooks from that service. Native launch configuration selects the
|
|
100
|
+
cache location; browser code does not launch Git or reconstruct its protocol.
|
|
101
|
+
|
|
102
|
+
This contract is portable Node source for Windows, Linux and macOS. Android
|
|
103
|
+
requires a host that supplies the same Git/process capability; it is not a
|
|
104
|
+
claim that every Android host ships Git.
|