@arcfold/dsh 0.0.0-stage → 0.4.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.
Files changed (127) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +8 -2
  3. package/THIRD_PARTY_NOTICES.md +11 -0
  4. package/cordis.patch.yml +138 -0
  5. package/package.json +70 -4
  6. package/patches/deepseek-0.2.2-rc.1-transport.json +65 -0
  7. package/patches/deepseek-0.2.2-rc.1-transport.patch +61 -0
  8. package/presets/arcfold.patch.yml +110 -0
  9. package/runtime/README.md +19 -0
  10. package/runtime/bootstrap.json +15 -0
  11. package/runtime/desktop-python.tar.gz +0 -0
  12. package/runtime/host/package.json +11 -0
  13. package/runtime/host/pnpm-workspace.yaml +9 -0
  14. package/runtime/initial-policy.json +94 -0
  15. package/runtime/profile/pnpm-workspace.yaml +7 -0
  16. package/scripts/exact-package-patch.mjs +106 -0
  17. package/scripts/patch-deepseek-transport.mjs +14 -0
  18. package/src/agent.mjs +56 -0
  19. package/src/browse-scope.mjs +14 -0
  20. package/src/budget.mjs +369 -0
  21. package/src/business.mjs +334 -0
  22. package/src/context-material.mjs +119 -0
  23. package/src/deepseek-budget-plugin.mjs +58 -0
  24. package/src/deepseek-provider.mjs +57 -0
  25. package/src/deepseek-receipt.mjs +97 -0
  26. package/src/deepseek-transport.mjs +97 -0
  27. package/src/desktop/adapter.mjs +16 -0
  28. package/src/desktop/bootstrap.mjs +84 -0
  29. package/src/desktop/configuration.mjs +117 -0
  30. package/src/desktop/provider.mjs +46 -0
  31. package/src/desktop/python-runtime.mjs +105 -0
  32. package/src/desktop/setup-service.mjs +81 -0
  33. package/src/desktop/vendor/LICENSE.deepseek +21 -0
  34. package/src/desktop/vendor/NOTICE.md +5 -0
  35. package/src/desktop/vendor/deepseek-0.2.2-rc.1.mjs +2919 -0
  36. package/src/evidence-ownership.mjs +51 -0
  37. package/src/evidence.mjs +35 -0
  38. package/src/goal-context.mjs +52 -0
  39. package/src/goal-execution-tool.mjs +74 -0
  40. package/src/goal-execution.mjs +333 -0
  41. package/src/goal-tool.mjs +56 -0
  42. package/src/goal-work-control.mjs +63 -0
  43. package/src/goal-work.mjs +257 -0
  44. package/src/goals.mjs +578 -0
  45. package/src/importer.mjs +298 -0
  46. package/src/index.mjs +53 -0
  47. package/src/learning-contracts.mjs +220 -0
  48. package/src/learning-defaults.mjs +98 -0
  49. package/src/learning-goals.mjs +165 -0
  50. package/src/learning-plugin.mjs +28 -0
  51. package/src/learning-runner.mjs +275 -0
  52. package/src/learning.mjs +534 -0
  53. package/src/legacy-export.mjs +332 -0
  54. package/src/legacy-import-source.mjs +356 -0
  55. package/src/metered-provider-host.mjs +101 -0
  56. package/src/metered-transport.mjs +435 -0
  57. package/src/method-context.mjs +57 -0
  58. package/src/method-lifecycle.mjs +34 -0
  59. package/src/method-tool.mjs +61 -0
  60. package/src/methods.mjs +425 -0
  61. package/src/model-selection.mjs +41 -0
  62. package/src/native-context-call.mjs +88 -0
  63. package/src/native-continuation.mjs +217 -0
  64. package/src/native-request-authority.mjs +90 -0
  65. package/src/openai-models.mjs +4 -0
  66. package/src/openai-receipt.mjs +98 -0
  67. package/src/openai-transport.mjs +167 -0
  68. package/src/openai-usage.mjs +194 -0
  69. package/src/opportunities.mjs +295 -0
  70. package/src/preparation-artifacts.mjs +213 -0
  71. package/src/preparation-plugin.mjs +290 -0
  72. package/src/runtime-command.mjs +264 -0
  73. package/src/runtime-config.mjs +417 -0
  74. package/src/runtime-guard.mjs +284 -0
  75. package/src/runtime-host.mjs +212 -0
  76. package/src/runtime-init.mjs +278 -0
  77. package/src/runtime-installation.mjs +518 -0
  78. package/src/runtime-lifecycle.mjs +227 -0
  79. package/src/runtime-model-settings.mjs +111 -0
  80. package/src/runtime-readiness.mjs +285 -0
  81. package/src/runtime-workspace.mjs +18 -0
  82. package/src/session-bridge.mjs +365 -0
  83. package/src/store.mjs +394 -0
  84. package/src/tree/LICENSE +21 -0
  85. package/src/tree/attribution.mjs +120 -0
  86. package/src/tree/clusters.mjs +61 -0
  87. package/src/tree/consolidation.mjs +103 -0
  88. package/src/tree/context.mjs +211 -0
  89. package/src/tree/contracts.mjs +142 -0
  90. package/src/tree/experience.mjs +140 -0
  91. package/src/tree/experiment-budget.mjs +84 -0
  92. package/src/tree/hooks.mjs +254 -0
  93. package/src/tree/host-context.mjs +107 -0
  94. package/src/tree/host-process.mjs +288 -0
  95. package/src/tree/host.mjs +37 -0
  96. package/src/tree/improvement.mjs +106 -0
  97. package/src/tree/items.mjs +294 -0
  98. package/src/tree/jobs.mjs +184 -0
  99. package/src/tree/learning.mjs +202 -0
  100. package/src/tree/metrics.mjs +203 -0
  101. package/src/tree/model-owner.mjs +254 -0
  102. package/src/tree/observations.mjs +73 -0
  103. package/src/tree/prompts.mjs +500 -0
  104. package/src/tree/reports.mjs +67 -0
  105. package/src/tree/repository.mjs +236 -0
  106. package/src/tree/runtime.mjs +745 -0
  107. package/src/tree/script-writer.mjs +149 -0
  108. package/src/tree/scripts.mjs +208 -0
  109. package/src/tree/signals.mjs +118 -0
  110. package/src/tree/snapshots.mjs +379 -0
  111. package/src/tree/token-counter.mjs +75 -0
  112. package/src/tree/token-counter.py +37 -0
  113. package/src/tree/tokenizer-resources/LICENSE +21 -0
  114. package/src/tree/tokenizer-resources/README.md +31 -0
  115. package/src/tree/tokenizer-resources/deepseek_recipe-0.2.0-cp310-abi3-macosx_11_0_arm64.whl +0 -0
  116. package/src/tree/tokenizer-resources/manifest.json +61 -0
  117. package/src/tree/tokenizer-resources/native.pyi +1042 -0
  118. package/src/tree/tokenizer-resources/tokenizer-NOTICE.md +26 -0
  119. package/src/tree/tokenizer-resources/tokenizer-v4-NOTICE.md +27 -0
  120. package/src/tree/tokenizer-resources/tokenizer-v4.json +267359 -0
  121. package/src/tree/tokenizer-resources/tokenizer.json +267359 -0
  122. package/src/tree/tools.mjs +99 -0
  123. package/src/tree/tree.mjs +202 -0
  124. package/src/tree/ui.mjs +285 -0
  125. package/src/tree/verification.mjs +143 -0
  126. package/src/understandings.mjs +309 -0
  127. package/src/web-budget-recovery.mjs +181 -0
@@ -0,0 +1,264 @@
1
+ // The unpacked release entry uses Node builtins only. Runtime code is selected
2
+ // from the verified installation, so this directory needs no node_modules.
3
+ import assert from 'node:assert/strict';
4
+ import { createHash, randomUUID } from 'node:crypto';
5
+ import { constants, closeSync, existsSync, fstatSync, openSync, readFileSync, realpathSync } from 'node:fs';
6
+ import { open, rename, unlink } from 'node:fs/promises';
7
+ import { createRequire } from 'node:module';
8
+ import { dirname, join, relative, sep } from 'node:path';
9
+ import { pathToFileURL } from 'node:url';
10
+ import { canonicalRuntimePath, loadRuntimeIdentity, loadRuntimePlan, runtimeTransport } from './runtime-config.mjs';
11
+ import { installRelease, readInstallation } from './runtime-installation.mjs';
12
+ import { canonicalJson } from './store.mjs';
13
+
14
+ const sha = bytes => createHash('sha256').update(bytes).digest('hex');
15
+ const inside = (root, path) => path === root || path.startsWith(root + sep);
16
+ const protocol = 'arcfold-runtime-v1';
17
+ const runIdValid = id => typeof id === 'string' && /^[a-zA-Z0-9_-]{1,128}$/.test(id);
18
+
19
+ function privateJson(path) {
20
+ const fd = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW);
21
+ try {
22
+ const info = fstatSync(fd);
23
+ assert.ok(info.isFile() && info.uid === process.getuid() && (info.mode & 0o777) === 0o600 && info.size <= 2 * 1024 * 1024,
24
+ 'Runtime ownership records must be private regular files');
25
+ return JSON.parse(readFileSync(fd, 'utf8'));
26
+ } finally { closeSync(fd); }
27
+ }
28
+
29
+ /** A stop must still work after the selected model/version is edited. Verify
30
+ * only the old owner's management code and native lock, not its model or UI. */
31
+ export function managementInstallation(identity) {
32
+ const currentPath = join(identity.runtimeDirectory, 'current.json');
33
+ if (!existsSync(currentPath)) return null;
34
+ const current = privateJson(currentPath);
35
+ assert.ok(current.schema === protocol && current.dataRoot === identity.dataRoot && runIdValid(current.runId),
36
+ 'Current runtime belongs to a different data directory');
37
+ const runDirectory = join(identity.runtimeDirectory, 'runs', current.runId);
38
+ const launch = privateJson(join(runDirectory, 'launch.json'));
39
+ assert.ok(launch.schema === protocol && launch.runId === current.runId && launch.runDirectory === runDirectory &&
40
+ launch.plan?.dataRoot === identity.dataRoot && launch.plan.runtimeDirectory === identity.runtimeDirectory &&
41
+ current.configurationHash === launch.plan.configurationHash && /^[a-f0-9]{64}$/.test(current.configurationHash),
42
+ 'The current owner and launch record disagree');
43
+ const installation = launch.plan.installation;
44
+ assert.ok(installation?.schema === 'arcfold-installation-v1', 'The owner has no installation identity');
45
+ const root = realpathSync(installation.installationDir);
46
+ assert.equal(installation.installationDir, root);
47
+ assert.equal(installation.installationFile, join(root, 'installation.json'));
48
+ const receipt = privateJson(installation.installationFile);
49
+ const { installationFile, ...snapshot } = installation;
50
+ assert.equal(canonicalJson(receipt), canonicalJson(snapshot), 'The old owner installation receipt changed');
51
+ assert.equal(installation.identities.node.path, process.execPath, 'Use the Node installation recorded for this runtime');
52
+ const node = readFileSync(process.execPath);
53
+ assert.equal(sha(node), installation.identities.node.sha256, 'The recorded Node executable changed');
54
+ assert.equal(node.length, installation.identities.node.bytes);
55
+ const manifest = installation.identities.fileManifest;
56
+ assert.equal(manifest.path, 'installation-files.json');
57
+ const bytes = readFileSync(join(root, manifest.path));
58
+ assert.equal(sha(bytes), manifest.sha256); assert.equal(bytes.length, manifest.bytes);
59
+ const files = JSON.parse(bytes); assert.equal(files.length, manifest.count);
60
+ const byPath = new Map(files.map(file => [file.path, file]));
61
+ assert.equal(byPath.size, files.length);
62
+ function verify(filename) {
63
+ const actual = realpathSync(filename);
64
+ assert.ok(inside(root, actual), 'Management code escaped its recorded installation');
65
+ const record = byPath.get(relative(root, actual));
66
+ assert.ok(record && record.link === undefined, 'Management code is absent from the recorded installation');
67
+ const content = readFileSync(actual);
68
+ assert.equal(sha(content), record.sha256, 'Management code changed: ' + relative(root, actual));
69
+ assert.equal(content.length, record.bytes);
70
+ return actual;
71
+ }
72
+ const core = realpathSync(installation.coreRoot);
73
+ verify(join(core, 'package.json'));
74
+ for (const name of ['runtime-lifecycle.mjs', 'runtime-config.mjs', 'store.mjs']) verify(join(core, 'src', name));
75
+ // The verified old manager determines its own known import closure. Earlier
76
+ // installations do not import this module and need none of these new files.
77
+ const configSource = readFileSync(join(core, 'src/runtime-config.mjs'), 'utf8');
78
+ if (/\bfrom\s+['"]\.\/web-budget-recovery\.mjs['"]/.test(configSource))
79
+ for (const name of ['web-budget-recovery.mjs', 'budget.mjs', 'evidence-ownership.mjs'])
80
+ verify(join(core, 'src', name));
81
+ if (/\bfrom\s+['\"]\.\/openai-models\.mjs['\"]/.test(configSource))
82
+ verify(join(core, 'src/openai-models.mjs'));
83
+ const require = createRequire(join(core, 'package.json'));
84
+ const flock = verify(require.resolve('@deepseek-ai/node-addon-system/flock'));
85
+ const flockPackage = verify(require.resolve('@deepseek-ai/node-addon-system/package.json'));
86
+ assert.equal(JSON.parse(readFileSync(flockPackage)).version, '0.1.2');
87
+ const platformPackage = verify(createRequire(flock).resolve(`@deepseek-ai/node-addon-system-${process.platform}-${process.arch}/package.json`));
88
+ assert.equal(process.platform, 'darwin', 'This release currently supports macOS only');
89
+ assert.equal(JSON.parse(readFileSync(platformPackage)).version, '0.1.2');
90
+ verify(join(dirname(platformPackage), 'bin/system.node'));
91
+ return installation;
92
+ }
93
+
94
+ async function runtimeApi(configPath, { currentOwner = false } = {}) {
95
+ const identity = loadRuntimeIdentity(configPath);
96
+ let installation = currentOwner ? managementInstallation(identity) : null;
97
+ if (!installation) {
98
+ // Only locating a fresh/stopped installation: model configuration is not parsed.
99
+ const config = JSON.parse(readFileSync(identity.configPath, 'utf8'));
100
+ installation = readInstallation(canonicalRuntimePath(config.installationFile, 'installationFile'));
101
+ }
102
+ const api = await import(pathToFileURL(join(installation.coreRoot, 'src/runtime-lifecycle.mjs')).href);
103
+ return { api, identity, installation };
104
+ }
105
+
106
+ /** Only for a proposed new selection, after readInstallation has verified its
107
+ * complete files and dependencies. Importing the private entry does not launch
108
+ * it: runtime-host runs main only when Node executes that file as argv[1].
109
+ * These are the v1 handoff interfaces, not a second list of internal modules.
110
+ * Existing-owner stop/status deliberately keep their narrower management path. */
111
+ async function requireVersionRuntime(installation) {
112
+ try {
113
+ const api = await import(pathToFileURL(join(installation.coreRoot, 'src/runtime-lifecycle.mjs')).href);
114
+ assert.equal(api.RUNTIME_PROTOCOL, protocol, 'Unsupported runtime management protocol');
115
+ for (const name of ['acquireRuntimeLease', 'startRuntime', 'stopRuntime', 'statusRuntime', 'openRuntime'])
116
+ assert.equal(typeof api[name], 'function', `Missing runtime operation: ${name}`);
117
+ const host = await import(pathToFileURL(join(installation.coreRoot, 'src/runtime-host.mjs')).href);
118
+ for (const name of ['runOwnedRuntime', 'openRuntimeBrowser'])
119
+ assert.equal(typeof host[name], 'function', `Missing private host entry: ${name}`);
120
+ } catch (cause) {
121
+ throw Object.assign(new Error('The selected installation does not provide a compatible standalone runtime.', { cause }),
122
+ { code: 'version_not_launchable' });
123
+ }
124
+ }
125
+
126
+ export async function selectRuntimeVersion({ configPath, installationFile }) {
127
+ const { api, identity } = await runtimeApi(configPath, { currentOwner: true });
128
+ // A kernel lease covers validation and replacement, so another start cannot
129
+ // open business state while the code selection changes.
130
+ const lease = await api.acquireRuntimeLease(identity.runtimeDirectory);
131
+ const temporary = join(dirname(identity.configPath), '.arcfold-version-' + randomUUID() + '.json');
132
+ try {
133
+ assert.deepEqual(loadRuntimeIdentity(configPath), identity, 'Data ownership changed before version selection acquired its lock');
134
+ const original = readFileSync(identity.configPath);
135
+ const config = JSON.parse(original);
136
+ const previousInstallationFile = config.installationFile;
137
+ config.installationFile = canonicalRuntimePath(installationFile, 'installationFile');
138
+ const staged = await open(temporary, 'wx', 0o600);
139
+ try { await staged.writeFile(JSON.stringify(config, null, 2) + '\n'); await staged.sync(); } finally { await staged.close(); }
140
+ const next = await loadRuntimePlan(temporary);
141
+ await requireVersionRuntime(next.installation);
142
+ const transport = runtimeTransport(next.config);
143
+ if (Object.values(transport.routes).some(route => route.mode === 'web-only')) {
144
+ // Delegate only after full installation verification. The unpacked CLI
145
+ // does not resolve native addons or inherit a local development install.
146
+ const target = await import(pathToFileURL(join(next.installation.coreRoot, 'src/web-budget-recovery.mjs')).href)
147
+ .catch(cause => { throw Object.assign(new Error('The selected version cannot validate web-only recovery.', { cause }), { code: 'version_not_launchable' }); });
148
+ assert.equal(target.WEB_BUDGET_RECOVERY_VERSION, 'web-only-recovery-v1', 'Unsupported target web-only recovery capability');
149
+ const targetConfig = await import(pathToFileURL(join(next.installation.coreRoot, 'src/runtime-config.mjs')).href);
150
+ targetConfig.validateRuntimeConfig(next.config);
151
+ await target.validateWebBudgetVersion(transport.routes, transport.artifactDirectory);
152
+ }
153
+ assert.deepEqual(readFileSync(identity.configPath), original, 'Configuration changed during version selection');
154
+ await rename(temporary, identity.configPath);
155
+ const directory = await open(dirname(identity.configPath), 'r');
156
+ try { await directory.sync(); } finally { await directory.close(); }
157
+ return { configPath: identity.configPath, previousInstallationFile, installationFile: next.installation.installationFile,
158
+ sourceCommit: next.installation.sourceCommit, dataPreserved: true, started: false };
159
+ } finally {
160
+ try { await unlink(temporary).catch(error => { if (error.code !== 'ENOENT') throw error; }); }
161
+ finally { await lease.close(); }
162
+ }
163
+ }
164
+
165
+ const FLAGS = {
166
+ install: ['release', 'installation', 'supply', 'offline-store'],
167
+ 'export-legacy': ['installation-file', 'source-db', 'output'],
168
+ init: ['config', 'installation-file', 'data', 'workspace', 'port', 'web-unlimited', 'main-provider', 'main-model', 'main-reasoning', 'learn-provider', 'learn-model', 'learn-reasoning', 'proxy'],
169
+ start: ['config', 'timeout-ms'], status: ['config'], stop: ['config', 'timeout-ms'],
170
+ restart: ['config', 'timeout-ms'], open: ['config', 'timeout-ms'],
171
+ 'use-version': ['config', 'installation-file'],
172
+ };
173
+ const REQUIRED = { 'export-legacy': ['installation-file', 'source-db', 'output'], install: ['release', 'installation'], init: ['config', 'installation-file', 'data', 'workspace'],
174
+ start: ['config'], status: ['config'], stop: ['config'], restart: ['config'], open: ['config'], 'use-version': ['config', 'installation-file'] };
175
+ export function parseRuntimeCommand(argv) {
176
+ if (argv.length === 0 || argv.length === 1 && ['help', '--help', '-h'].includes(argv[0])) return { command: 'help', options: {} };
177
+ const [command, ...args] = argv; assert.ok(Object.hasOwn(FLAGS, command), 'Unknown command; use --help');
178
+ const options = {};
179
+ for (let i = 0; i < args.length;) {
180
+ const flag = args[i++], name = flag?.slice(2);
181
+ assert.ok(flag?.startsWith('--') && FLAGS[command].includes(name) && !Object.hasOwn(options, name),
182
+ 'Unsupported or duplicate option; use --help');
183
+ if (name === 'web-unlimited') { options[name] = true; continue; }
184
+ const value = args[i++];
185
+ assert.ok(value && !value.startsWith('--'), 'Incomplete option; use --help');
186
+ options[name] = value;
187
+ }
188
+ for (const name of REQUIRED[command]) assert.ok(options[name], `--${name} is required`);
189
+ for (const name of ['port', 'timeout-ms']) if (options[name] !== undefined) {
190
+ assert.match(options[name], /^\d+$/, `--${name} must be an integer`); options[name] = Number(options[name]);
191
+ }
192
+ for (const name of Object.keys(options).filter(name => ['config','installation-file','data','workspace','release','installation','supply','offline-store','source-db','output'].includes(name))) {
193
+ const path = canonicalRuntimePath(options[name], name);
194
+ if (command === 'export-legacy' && ['source-db','output'].includes(name))
195
+ assert.equal(options[name], path, 'Legacy export requires canonical paths without aliases');
196
+ options[name] = path;
197
+ }
198
+ if (command === 'init') {
199
+ assert.equal(options['web-unlimited'], true, 'Explicit --web-unlimited confirmation is required');
200
+ for (const prefix of ['main','learn']) {
201
+ const fields = ['provider','model','reasoning'].map(name => prefix + '-' + name);
202
+ assert.ok(fields.every(name => options[name] === undefined) || fields.every(name => typeof options[name] === 'string'),
203
+ 'Supply all three model choices');
204
+ }
205
+ if (options.proxy !== undefined) {
206
+ const proxy = new URL(options.proxy);
207
+ assert.ok(['http:','https:'].includes(proxy.protocol) && !proxy.username && !proxy.password &&
208
+ proxy.pathname === '/' && !proxy.search && !proxy.hash, 'Use an explicit unauthenticated HTTP CONNECT proxy');
209
+ }
210
+ }
211
+ return { command, options };
212
+ }
213
+
214
+ export const runtimeHelp = `Arcfold local product (macOS arm64, Node 24.15.0)
215
+ install --release ABS --installation NEW_ABS [--supply ABS] [--offline-store ABS_JSON]
216
+ init --config NEW_ABS --installation-file ABS --data ABS --workspace ABS --web-unlimited [--port 4381]
217
+ [--proxy URL] [--main-provider ID --main-model ID --main-reasoning ID]
218
+ [--learn-provider ID --learn-model ID --learn-reasoning ID]
219
+ start|stop|restart|open --config ABS [--timeout-ms 60000]
220
+ status --config ABS
221
+ use-version --config ABS --installation-file ABS
222
+ export-legacy --installation-file ABS --source-db ABS --output NEW_ABS
223
+ Start never installs packages. Models and credential references are explicit configuration.
224
+ Open hands the authenticated address directly to the system browser; it does not print credentials.
225
+ Version selection requires a stopped owner and preserves the existing data. See docs/local-runtime.md.`;
226
+
227
+ export async function runRuntimeCommand(argv, { signal } = {}) {
228
+ const { command, options: o } = parseRuntimeCommand(argv); signal?.throwIfAborted();
229
+ if (command === 'help') return runtimeHelp;
230
+ if (command === 'install') {
231
+ const result = await installRelease({ releaseDir: o.release, installationDir: o.installation,
232
+ supplyManifest: o.supply, offlineStore: o['offline-store'] ? JSON.parse(readFileSync(o['offline-store'], 'utf8')) : undefined, signal });
233
+ return { installationFile: result.installationFile, sourceCommit: result.sourceCommit, hostStarted: false };
234
+ }
235
+ if (command === 'export-legacy') {
236
+ const installation = readInstallation(o['installation-file']);
237
+ const api = await import(pathToFileURL(join(installation.coreRoot, 'src/legacy-export.mjs')).href);
238
+ assert.equal(api.LEGACY_EXPORT_VERSION, 'legacy3-finite-projection-v1', 'Unsupported legacy exporter');
239
+ return api.exportLegacy3({ sourceDb: o['source-db'], output: o.output, signal });
240
+ }
241
+ if (command === 'init') {
242
+ const installation = readInstallation(o['installation-file']);
243
+ assert.deepEqual(Object.keys(installation.providers ?? {}).sort(), ['deepseek-official','openai'],
244
+ 'First use requires both actual provider proofs');
245
+ assert.ok(installation.modelSelection, 'First use requires the native model-selection proof');
246
+ const api = await import(pathToFileURL(join(installation.coreRoot, 'src/runtime-init.mjs')).href);
247
+ const choice = prefix => o[prefix + '-provider'] === undefined ? undefined : {
248
+ provider: o[prefix + '-provider'], model: o[prefix + '-model'], reasoningEffort: o[prefix + '-reasoning'] };
249
+ return api.initializeRuntimeDeployment({ configPath: o.config, installationFile: installation.installationFile,
250
+ dataRoot: o.data, workspace: o.workspace, webUnlimited: o['web-unlimited'],
251
+ main: choice('main'), learn: choice('learn'), network: o.proxy ? { mode:'proxy',url:o.proxy } : { mode:'direct' },
252
+ ...(o.port === undefined ? {} : { port: o.port }) });
253
+ }
254
+ if (command === 'use-version') return selectRuntimeVersion({ configPath: o.config, installationFile: o['installation-file'] });
255
+ const parameters = { configPath: o.config, timeoutMs: o['timeout-ms'], signal };
256
+ if (command === 'restart') {
257
+ const old = await runtimeApi(o.config, { currentOwner: true });
258
+ const previous = await old.api.stopRuntime(parameters); signal?.throwIfAborted();
259
+ const next = await runtimeApi(o.config);
260
+ return { ...await next.api.startRuntime(parameters), previous };
261
+ }
262
+ const { api } = await runtimeApi(o.config, { currentOwner: command !== 'start' });
263
+ return api[command + 'Runtime'](parameters);
264
+ }