arcane-os 0.66.0 → 0.67.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 +23 -0
- package/README.md +1 -1
- package/browser-runtime/ai/core-image.mjs +84 -34
- package/browser-runtime/ai/core-local.mjs +195 -43
- package/browser-runtime/ai/core-onnx.mjs +71 -12
- package/docs/reference/README.md +1 -0
- package/docs/reference/core-native-packaging.md +11 -4
- package/docs/reference/core-repositories.md +93 -9
- package/docs/reference/core-runtime.md +32 -5
- package/docs/reference/inventory/package-api.json +9 -9
- package/docs/reference/local-ai.md +112 -2
- package/docs/reference/local-image-generation.md +15 -4
- package/docs/reference/model-assets.md +50 -3
- package/docs/reference/native-decisions.md +181 -0
- package/docs/reference/sdk-api.md +55 -12
- package/package.json +5 -2
- package/src/core/repositories.mjs +27 -8
- package/src/core/runtime.mjs +11 -0
- package/src/core/services/decisions.mjs +144 -0
- package/src/core/services/local-ai.mjs +31 -9
- package/src/core/services/model-assets.mjs +98 -13
- package/src/git-identity.mjs +22 -58
- package/src/local-ai/decision-tokenizer-worker.mjs +31 -0
- package/src/local-ai/decision-tokenizer.mjs +109 -0
- package/src/local-ai/decisions.mjs +273 -0
- package/src/local-ai/dev.mjs +8 -2
- package/src/local-ai/onnx-worker.mjs +67 -7
- package/src/local-ai/onnx.mjs +23 -12
- package/src/native/portable-layout.mjs +8 -3
- package/src/repository.mjs +144 -4
package/src/repository.mjs
CHANGED
|
@@ -1,10 +1,142 @@
|
|
|
1
1
|
import {runProcess} from './process.mjs';
|
|
2
2
|
import {ArcaneError,ERROR_CODES,throwIfAborted} from './errors.mjs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import Is from 'strong-type';
|
|
5
|
+
|
|
6
|
+
const is = new Is(false);
|
|
3
7
|
|
|
4
8
|
async function git(args,{run=runProcess,...options}){
|
|
5
9
|
return run('git',args,options);
|
|
6
10
|
}
|
|
7
11
|
|
|
12
|
+
/** Private shared reader for Git text protocols, retaining native diagnostics. */
|
|
13
|
+
export async function readGitText(args, {
|
|
14
|
+
cwd, signal, onEvent, run = runProcess, allowNonzero = false,
|
|
15
|
+
notTextCode = 'ARCANE_GIT_CONFIGURATION_NOT_TEXT', label = 'Git configuration'
|
|
16
|
+
} = {}) {
|
|
17
|
+
const chunks = [];
|
|
18
|
+
try {
|
|
19
|
+
const result = await run('git', args, {
|
|
20
|
+
cwd, signal, onEvent, allowNonzero,
|
|
21
|
+
outputEncoding: {stdout: null}, captureOutput: {stdout: false}, emitOutputEvents: {stdout: false},
|
|
22
|
+
onOutput: function observeOutput({stream, chunk}) {
|
|
23
|
+
if (stream === 'stdout') chunks.push(chunk);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
try { throwIfAborted(signal); }
|
|
27
|
+
catch (error) { error.details = result; throw error; }
|
|
28
|
+
const rawStdout = Buffer.concat(chunks);
|
|
29
|
+
let output = null;
|
|
30
|
+
if (result.code === 0) {
|
|
31
|
+
try { output = new TextDecoder('utf-8', {fatal: true, ignoreBOM: true}).decode(rawStdout); }
|
|
32
|
+
catch (cause) {
|
|
33
|
+
throw new ArcaneError(notTextCode, `${label} cannot be represented as UTF-8 text.`, {cause, details: result});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return {result, output, rawStdout};
|
|
37
|
+
} catch (error) {
|
|
38
|
+
const rawStdout = Buffer.concat(chunks);
|
|
39
|
+
let attached = false;
|
|
40
|
+
let attachmentError;
|
|
41
|
+
try {
|
|
42
|
+
if (error !== null && (is.object(error) || is.function(error)) && !('rawStdout' in error)) {
|
|
43
|
+
Object.defineProperty(error, 'rawStdout', {
|
|
44
|
+
value: rawStdout, writable: true, enumerable: true, configurable: true
|
|
45
|
+
});
|
|
46
|
+
attached = true;
|
|
47
|
+
}
|
|
48
|
+
} catch (cause) { attachmentError = cause; }
|
|
49
|
+
if (attached) throw error;
|
|
50
|
+
const failure = new ArcaneError(error?.code ?? ERROR_CODES.operationFailed,
|
|
51
|
+
error instanceof Error ? error.message : String(error),
|
|
52
|
+
{cause: error, details: error?.details, exitCode: error?.exitCode});
|
|
53
|
+
failure.rawStdout = rawStdout;
|
|
54
|
+
if (attachmentError) failure.attachmentError = attachmentError;
|
|
55
|
+
throw failure;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Capture a caller-selected operation target without changing its strings. */
|
|
60
|
+
export function captureRepositoryTarget(target) {
|
|
61
|
+
if (target === undefined) return undefined;
|
|
62
|
+
const {remote, ref} = target;
|
|
63
|
+
for (const [name, value] of Object.entries({remote, ref})) {
|
|
64
|
+
if (!is.string(value) || value === '' || !value.isWellFormed() || value.includes('\0')) {
|
|
65
|
+
throw new TypeError(`target.${name} must be nonempty text representable as a native process argument.`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return {remote, ref};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function repositoryPushArguments(target) {
|
|
72
|
+
return target === undefined ? ['push'] : ['push', '--no-follow-tags', '--', target.remote, `HEAD:${target.ref}`];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Observe an existing workspace without initializing or refreshing it. */
|
|
76
|
+
export async function repositoryConfiguration({workspaceRoot = process.cwd(), signal, onEvent, run = runProcess} = {}) {
|
|
77
|
+
const repositoryRoot = path.resolve(workspaceRoot);
|
|
78
|
+
const options = {cwd: repositoryRoot, signal, onEvent, run, allowNonzero: true};
|
|
79
|
+
throwIfAborted(signal);
|
|
80
|
+
|
|
81
|
+
function processFailure(observation, message) {
|
|
82
|
+
const failure = new ArcaneError(ERROR_CODES.operationFailed, message, {details: observation.result});
|
|
83
|
+
failure.rawStdout = observation.rawStdout;
|
|
84
|
+
return failure;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function configuration(pattern) {
|
|
88
|
+
const observation = await readGitText(['config', '--includes', '--null', '--get-regexp', pattern], options);
|
|
89
|
+
const values = new Map();
|
|
90
|
+
if (observation.result.code === 1) return values;
|
|
91
|
+
if (observation.result.code !== 0) throw processFailure(observation, 'Git configuration observation failed.');
|
|
92
|
+
for (const record of observation.output.split('\0')) {
|
|
93
|
+
if (record === '') continue;
|
|
94
|
+
const separator = record.indexOf('\n');
|
|
95
|
+
const key = separator === -1 ? record : record.slice(0, separator);
|
|
96
|
+
const value = separator === -1 ? '' : record.slice(separator + 1);
|
|
97
|
+
const entries = values.get(key) ?? [];
|
|
98
|
+
entries.push(value);
|
|
99
|
+
values.set(key, entries);
|
|
100
|
+
}
|
|
101
|
+
return values;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function literalPattern(value) {
|
|
105
|
+
return value.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const root = await readGitText(['rev-parse', '--is-inside-work-tree', '--show-prefix'], options);
|
|
109
|
+
if (root.result.code !== 0) throw processFailure(root, 'Git working repository observation failed.');
|
|
110
|
+
if (root.output !== 'true\n\n' && root.output !== 'true\r\n\r\n') {
|
|
111
|
+
const failure = new ArcaneError('ARCANE_REPOSITORY_DIRECTORY_INVALID',
|
|
112
|
+
'The selected directory is not a Git working repository root.', {details: root.result});
|
|
113
|
+
failure.rawStdout = root.rawStdout;
|
|
114
|
+
throw failure;
|
|
115
|
+
}
|
|
116
|
+
const head = await readGitText(['symbolic-ref', '--quiet', 'HEAD'], options);
|
|
117
|
+
if (head.result.code !== 0 && head.result.code !== 1) throw processFailure(head, 'Git symbolic HEAD observation failed.');
|
|
118
|
+
const headRef = head.result.code === 1 ? null : head.output.replace(/\r?\n$/u, '');
|
|
119
|
+
let upstream = null;
|
|
120
|
+
let remoteName;
|
|
121
|
+
if (headRef?.startsWith('refs/heads/')) {
|
|
122
|
+
const key = `branch.${headRef.slice('refs/heads/'.length)}`;
|
|
123
|
+
const branch = await configuration(`^${literalPattern(key)}[.](remote|merge)$`);
|
|
124
|
+
const remoteNames = branch.get(`${key}.remote`) ?? [];
|
|
125
|
+
remoteName = remoteNames.at(-1);
|
|
126
|
+
upstream = {remoteNames, mergeRefs: branch.get(`${key}.merge`) ?? [], urls: [], pushUrls: []};
|
|
127
|
+
}
|
|
128
|
+
const selectedNames = ['origin'];
|
|
129
|
+
if (remoteName !== undefined && remoteName !== '.' && remoteName !== 'origin') selectedNames.push(remoteName);
|
|
130
|
+
const names = selectedNames.map(literalPattern).join('|');
|
|
131
|
+
const remotes = await configuration(`^remote[.](${names})[.](url|pushurl)$`);
|
|
132
|
+
const origin = {urls: remotes.get('remote.origin.url') ?? [], pushUrls: remotes.get('remote.origin.pushurl') ?? []};
|
|
133
|
+
if (upstream && remoteName !== undefined && remoteName !== '.') {
|
|
134
|
+
upstream.urls = remotes.get(`remote.${remoteName}.url`) ?? [];
|
|
135
|
+
upstream.pushUrls = remotes.get(`remote.${remoteName}.pushurl`) ?? [];
|
|
136
|
+
}
|
|
137
|
+
return {repositoryRoot, headRef, origin, upstream};
|
|
138
|
+
}
|
|
139
|
+
|
|
8
140
|
export async function repositoryStatus({
|
|
9
141
|
workspaceRoot=process.cwd(),
|
|
10
142
|
signal,
|
|
@@ -28,11 +160,13 @@ export async function repositoryStatus({
|
|
|
28
160
|
|
|
29
161
|
export async function repositoryPull({
|
|
30
162
|
workspaceRoot=process.cwd(),
|
|
163
|
+
target,
|
|
31
164
|
signal,
|
|
32
165
|
onEvent,
|
|
33
166
|
run=runProcess
|
|
34
167
|
}={}){
|
|
35
168
|
throwIfAborted(signal);
|
|
169
|
+
const selectedTarget=captureRepositoryTarget(target);
|
|
36
170
|
const before=await repositoryStatus({workspaceRoot,signal,onEvent,run});
|
|
37
171
|
if(!before.clean){
|
|
38
172
|
throw new ArcaneError(
|
|
@@ -41,22 +175,27 @@ export async function repositoryPull({
|
|
|
41
175
|
{details:before}
|
|
42
176
|
);
|
|
43
177
|
}
|
|
44
|
-
const
|
|
178
|
+
const args=selectedTarget===undefined?['pull','--ff-only']
|
|
179
|
+
:['pull','--ff-only','--',selectedTarget.remote,`${selectedTarget.ref}:`];
|
|
180
|
+
const result=await git(args,{cwd:workspaceRoot,signal,onEvent,run});
|
|
45
181
|
return {
|
|
46
182
|
action:'pull',
|
|
47
183
|
repositoryRoot:before.repositoryRoot,
|
|
48
184
|
branch:before.branch,
|
|
49
|
-
output:result.stdout.trim()
|
|
185
|
+
output:result.stdout.trim(),
|
|
186
|
+
...(selectedTarget===undefined?{}:{target:selectedTarget,stdout:result.stdout,stderr:result.stderr})
|
|
50
187
|
};
|
|
51
188
|
}
|
|
52
189
|
|
|
53
190
|
export async function repositoryPush({
|
|
54
191
|
workspaceRoot=process.cwd(),
|
|
192
|
+
target,
|
|
55
193
|
signal,
|
|
56
194
|
onEvent,
|
|
57
195
|
run=runProcess
|
|
58
196
|
}={}){
|
|
59
197
|
throwIfAborted(signal);
|
|
198
|
+
const selectedTarget=captureRepositoryTarget(target);
|
|
60
199
|
const before=await repositoryStatus({workspaceRoot,signal,onEvent,run});
|
|
61
200
|
if(!before.branch){
|
|
62
201
|
throw new ArcaneError(
|
|
@@ -64,12 +203,13 @@ export async function repositoryPush({
|
|
|
64
203
|
'Refusing to push from a detached HEAD.'
|
|
65
204
|
);
|
|
66
205
|
}
|
|
67
|
-
const result=await git(
|
|
206
|
+
const result=await git(repositoryPushArguments(selectedTarget),{cwd:workspaceRoot,signal,onEvent,run});
|
|
68
207
|
return {
|
|
69
208
|
action:'push',
|
|
70
209
|
repositoryRoot:before.repositoryRoot,
|
|
71
210
|
branch:before.branch,
|
|
72
|
-
output:(result.stdout||result.stderr).trim()
|
|
211
|
+
output:(result.stdout||result.stderr).trim(),
|
|
212
|
+
...(selectedTarget===undefined?{}:{target:selectedTarget,stdout:result.stdout,stderr:result.stderr})
|
|
73
213
|
};
|
|
74
214
|
}
|
|
75
215
|
|