car-runtime 0.8.2 → 0.9.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/assets.json +21 -0
- package/bin/car-server +46 -0
- package/index.d.ts +120 -6
- package/index.js +2 -1
- package/install.js +97 -46
- package/package.json +6 -1
package/assets.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_doc": "Canonical per-platform asset name registry for car-runtime npm. Read by install.js, scripts/release.sh, and .github/workflows/build.yml so the three never drift. Add a platform here first, then verify each consumer picks it up. x86_64-apple-darwin was dropped — Apple Silicon only on macOS.",
|
|
3
|
+
"platforms": {
|
|
4
|
+
"darwin-arm64": {
|
|
5
|
+
"node": "car-runtime.darwin-arm64.node",
|
|
6
|
+
"server": "car-server-darwin-arm64"
|
|
7
|
+
},
|
|
8
|
+
"linux-x64": {
|
|
9
|
+
"node": "car-runtime.linux-x64-gnu.node",
|
|
10
|
+
"server": "car-server-linux-x64-gnu"
|
|
11
|
+
},
|
|
12
|
+
"linux-arm64": {
|
|
13
|
+
"node": "car-runtime.linux-arm64-gnu.node",
|
|
14
|
+
"server": "car-server-linux-arm64-gnu"
|
|
15
|
+
},
|
|
16
|
+
"win32-x64": {
|
|
17
|
+
"node": "car-runtime.win32-x64-msvc.node",
|
|
18
|
+
"server": "car-server-win32-x64-msvc.exe"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
package/bin/car-server
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Cross-platform shim that execs the platform-specific car-server
|
|
3
|
+
// binary downloaded by install.js. Pointed at by package.json's
|
|
4
|
+
// `bin` field so `npx --package=car-runtime car-server` works.
|
|
5
|
+
//
|
|
6
|
+
// Why a JS shim, not a direct binary in `bin`: npm's bin entries
|
|
7
|
+
// must be JS files (or have a hashbang) on POSIX, and the binary
|
|
8
|
+
// suffix (`.exe`) varies on Windows. A 20-line dispatcher keeps
|
|
9
|
+
// the package layout portable. Closes Parslee-ai/car-releases#36.
|
|
10
|
+
|
|
11
|
+
const path = require('node:path');
|
|
12
|
+
const fs = require('node:fs');
|
|
13
|
+
const { spawnSync } = require('node:child_process');
|
|
14
|
+
|
|
15
|
+
const pkgRoot = path.join(__dirname, '..');
|
|
16
|
+
const ASSETS = JSON.parse(
|
|
17
|
+
fs.readFileSync(path.join(pkgRoot, 'assets.json'), 'utf8'),
|
|
18
|
+
).platforms;
|
|
19
|
+
|
|
20
|
+
const key = `${process.platform}-${process.arch}`;
|
|
21
|
+
const entry = ASSETS[key];
|
|
22
|
+
if (!entry) {
|
|
23
|
+
console.error(
|
|
24
|
+
`car-server is not available for ${key}. Supported: ${Object.keys(ASSETS).join(', ')}.`
|
|
25
|
+
);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const binary = path.join(pkgRoot, entry.server);
|
|
30
|
+
|
|
31
|
+
if (!fs.existsSync(binary)) {
|
|
32
|
+
console.error(
|
|
33
|
+
`car-server binary not found at ${binary}.\n` +
|
|
34
|
+
`The car-runtime install script downloads it from the GitHub release; ` +
|
|
35
|
+
`re-run \`npm install car-runtime\`, or set CAR_RUNTIME_SKIP_DOWNLOAD=1 ` +
|
|
36
|
+
`and place the binary at that path manually.`
|
|
37
|
+
);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const result = spawnSync(binary, process.argv.slice(2), { stdio: 'inherit' });
|
|
42
|
+
if (result.error) {
|
|
43
|
+
console.error(`car-server failed to spawn: ${result.error.message}`);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
process.exit(result.status ?? 1);
|
package/index.d.ts
CHANGED
|
@@ -23,7 +23,11 @@
|
|
|
23
23
|
* - `openSession`, `closeSession`, `registerPolicy(sessionId)` — use
|
|
24
24
|
* `session.open` / `session.close` JSON-RPC methods
|
|
25
25
|
* - `stateSnapshot`, `stateKeys` — daemon-side endpoints pending
|
|
26
|
-
* - `removeModel
|
|
26
|
+
* - `removeModel` — daemon owns models_dir / models.json
|
|
27
|
+
*
|
|
28
|
+
* (`registerModel` was re-exposed in #39 — it now proxies to the
|
|
29
|
+
* daemon's `models.register` JSON-RPC. See its docstring for
|
|
30
|
+
* the visibility caveat.)
|
|
27
31
|
*
|
|
28
32
|
* Daemon URL override: `CAR_DAEMON_URL=ws://...` (default
|
|
29
33
|
* `ws://127.0.0.1:9100`).
|
|
@@ -359,8 +363,23 @@ export class CarRuntime {
|
|
|
359
363
|
*/
|
|
360
364
|
listModelsUnified(): string;
|
|
361
365
|
|
|
362
|
-
/**
|
|
363
|
-
|
|
366
|
+
/**
|
|
367
|
+
* Register a `ModelSchema` via the daemon's `models.register`
|
|
368
|
+
* JSON-RPC method (Parslee-ai/car-releases#39). The schema is
|
|
369
|
+
* persisted to `~/.car/models.json` (replacing any existing
|
|
370
|
+
* entry with the same `id`).
|
|
371
|
+
*
|
|
372
|
+
* **Visibility limitation**: the model becomes visible to
|
|
373
|
+
* `infer` / `models.list` / `models.list_unified` on the **next
|
|
374
|
+
* daemon boot**. Live hot-update inside a running daemon is
|
|
375
|
+
* tracked as a separate follow-up that requires interior
|
|
376
|
+
* mutability on the `UnifiedRegistry`. Register before
|
|
377
|
+
* starting the daemon's inference path, or restart the daemon
|
|
378
|
+
* after a batch of registrations.
|
|
379
|
+
*
|
|
380
|
+
* Returns JSON `{id, registered, path, note}`.
|
|
381
|
+
*/
|
|
382
|
+
registerModel(schemaJson: string): Promise<string>;
|
|
364
383
|
|
|
365
384
|
/** Route a prompt. Returns the routing decision as JSON. */
|
|
366
385
|
routeModel(prompt: string): Promise<string>;
|
|
@@ -385,6 +404,26 @@ export class CarRuntime {
|
|
|
385
404
|
/** Verify a proposal against this runtime's state + tools. Returns JSON. */
|
|
386
405
|
verifyProposal(proposalJson: string): Promise<string>;
|
|
387
406
|
|
|
407
|
+
/**
|
|
408
|
+
* Submit a proposal for daemon-side execution using the
|
|
409
|
+
* persistent `tools.execute` handler set by
|
|
410
|
+
* `registerToolHandler` (Parslee-ai/car-releases#38).
|
|
411
|
+
*
|
|
412
|
+
* Symmetric to `executeProposal` but without the per-call
|
|
413
|
+
* handler argument — the handler is process-wide. Fails up
|
|
414
|
+
* front if no handler is registered.
|
|
415
|
+
*
|
|
416
|
+
* `sessionId`, when provided, scopes per-action policy
|
|
417
|
+
* validation to a session opened via the daemon's
|
|
418
|
+
* `session.policy.open` JSON-RPC method.
|
|
419
|
+
*
|
|
420
|
+
* Returns the JSON-encoded execution result.
|
|
421
|
+
*/
|
|
422
|
+
submitProposal(
|
|
423
|
+
proposalJson: string,
|
|
424
|
+
sessionId?: string | null,
|
|
425
|
+
): Promise<string>;
|
|
426
|
+
|
|
388
427
|
// --- Browser automation ---
|
|
389
428
|
|
|
390
429
|
/**
|
|
@@ -611,12 +650,21 @@ export class CarRuntime {
|
|
|
611
650
|
* policies still apply, plus the session's. Without a session id the
|
|
612
651
|
* behavior matches the no-scope path bit-for-bit. See
|
|
613
652
|
* `docs/proposals/per-session-policy-scoping.md`.
|
|
653
|
+
*
|
|
654
|
+
* `scopeJson`, when provided, is a serialized `RuntimeScope` —
|
|
655
|
+
* `{ callerId?: string, tenantId?: string, claims?: Record<string, any> }`
|
|
656
|
+
* — attaching per-execution caller / tenant identity. When `tenantId`
|
|
657
|
+
* is set, the runtime routes per-action state R/W through the
|
|
658
|
+
* tenant-scoped view so distinct tenants can't observe each other's
|
|
659
|
+
* keys (Parslee-ai/car#187 phase 3). Single-tenant in-process callers
|
|
660
|
+
* pass `null` / omit and see no behaviour change.
|
|
614
661
|
*/
|
|
615
662
|
export function executeProposal(
|
|
616
663
|
rt: CarRuntime,
|
|
617
664
|
proposalJson: string,
|
|
618
665
|
toolFn: (callJson: string) => Promise<string>,
|
|
619
666
|
sessionId?: string | null,
|
|
667
|
+
scopeJson?: string | null,
|
|
620
668
|
): Promise<string>;
|
|
621
669
|
|
|
622
670
|
/**
|
|
@@ -790,6 +838,36 @@ export function registerVoiceEventHandler(
|
|
|
790
838
|
onEvent: (sessionId: string, eventJson: string) => void,
|
|
791
839
|
): void;
|
|
792
840
|
|
|
841
|
+
/**
|
|
842
|
+
* Register the JS `tools.execute` handler for `submitProposal`
|
|
843
|
+
* (Parslee-ai/car-releases#38). When the daemon dispatches a
|
|
844
|
+
* proposal carrying host-owned tools, every tool routes through
|
|
845
|
+
* this handler.
|
|
846
|
+
*
|
|
847
|
+
* `handlerFn(callJson)` receives `{"tool":"name","params":{...}}`
|
|
848
|
+
* as a JSON string and MUST return a Promise resolving to the
|
|
849
|
+
* tool's JSON-encoded result. Throwing rejects the daemon-side
|
|
850
|
+
* action with a -32000 JSON-RPC error.
|
|
851
|
+
*
|
|
852
|
+
* Process-wide setter — re-calling overwrites the previous
|
|
853
|
+
* handler. Pair with `unregisterToolHandler` to clear. Symmetric
|
|
854
|
+
* to `registerInferenceRunner` / `registerAgentRunner`: only one
|
|
855
|
+
* handler can be active at a time.
|
|
856
|
+
*
|
|
857
|
+
* Required before `submitProposal`. `executeProposal` continues
|
|
858
|
+
* to accept a per-call handler and does not use this registration.
|
|
859
|
+
*/
|
|
860
|
+
export function registerToolHandler(
|
|
861
|
+
handlerFn: (callJson: string) => Promise<string>,
|
|
862
|
+
): void;
|
|
863
|
+
|
|
864
|
+
/**
|
|
865
|
+
* Clear the registered `tools.execute` handler. `submitProposal`
|
|
866
|
+
* calls after this will fail if the proposal carries any
|
|
867
|
+
* host-tool actions.
|
|
868
|
+
*/
|
|
869
|
+
export function unregisterToolHandler(): void;
|
|
870
|
+
|
|
793
871
|
export function transcribeStream(
|
|
794
872
|
rt: CarRuntime,
|
|
795
873
|
sessionId: string,
|
|
@@ -1049,13 +1127,25 @@ export function reapStaleAgents(
|
|
|
1049
1127
|
* "agent_name": "...", // optional
|
|
1050
1128
|
* "agent_description": "...", // optional
|
|
1051
1129
|
* "organization": "...", // optional
|
|
1052
|
-
* "organization_url": "..."
|
|
1130
|
+
* "organization_url": "...", // optional
|
|
1131
|
+
* "share_session_runtime": false // optional, default false
|
|
1053
1132
|
* }
|
|
1054
1133
|
* ```
|
|
1055
1134
|
*
|
|
1135
|
+
* `share_session_runtime` — when `true`, the A2A dispatcher uses
|
|
1136
|
+
* the calling `CarRuntime`'s session runtime instead of spawning a
|
|
1137
|
+
* fresh one. Tools registered on the session via
|
|
1138
|
+
* `registerToolSchema` then appear on the Agent Card's `skills`
|
|
1139
|
+
* list, and A2A peer `message/send` calls for those tools route
|
|
1140
|
+
* back to the handler installed via `registerToolHandler`. This is
|
|
1141
|
+
* the canonical path for host-language agents to project themselves
|
|
1142
|
+
* over A2A. Default `false` preserves the legacy fresh-Runtime
|
|
1143
|
+
* behaviour (only `register_agent_basics` tools, dispatch in Rust).
|
|
1144
|
+
*
|
|
1056
1145
|
* Returns `'{"bound":"127.0.0.1:8731"}'` on success. Errors if a
|
|
1057
|
-
* server is already running, the bind fails,
|
|
1058
|
-
*
|
|
1146
|
+
* server is already running, the bind fails, `share_session_runtime`
|
|
1147
|
+
* is set but no session runtime is available (e.g. invoked from a
|
|
1148
|
+
* non-WS path), or `paramsJson` is malformed.
|
|
1059
1149
|
*/
|
|
1060
1150
|
export function startA2aServer(paramsJson: string): Promise<string>;
|
|
1061
1151
|
|
|
@@ -1363,6 +1453,30 @@ export function agentsHealth(): Promise<string>;
|
|
|
1363
1453
|
*/
|
|
1364
1454
|
export function agentsUpsert(specJson: string): Promise<string>;
|
|
1365
1455
|
|
|
1456
|
+
/**
|
|
1457
|
+
* Install a contributed-agent `AgentManifest` (Parslee-ai/car#182
|
|
1458
|
+
* phase 3). Runs install-time validation against the daemon's
|
|
1459
|
+
* default host capability advertisement:
|
|
1460
|
+
*
|
|
1461
|
+
* - `runtime.car_min_version` must be satisfied by the runtime's
|
|
1462
|
+
* own semver.
|
|
1463
|
+
* - Every `capabilities.required[namespace][feature]` must be
|
|
1464
|
+
* advertised by the host. Fail-closed on any miss.
|
|
1465
|
+
* - `capabilities.optional` is reported back as `missingOptional`
|
|
1466
|
+
* when the host can't satisfy it — informational, not blocking.
|
|
1467
|
+
*
|
|
1468
|
+
* For `external_process` manifests with a `command`, the
|
|
1469
|
+
* supervisor adopts the agent and returns it. For `pure_data`
|
|
1470
|
+
* and `health_url`-only manifests, the manifest is written to
|
|
1471
|
+
* `~/.car/agents/<id>/manifest.toml` but no `AgentSpec` is
|
|
1472
|
+
* adopted (the supervisor only spawns command-shaped externals
|
|
1473
|
+
* in this phase).
|
|
1474
|
+
*
|
|
1475
|
+
* Returns JSON
|
|
1476
|
+
* `{report: {missingOptional: [{namespace, feature}]}, agent: ManagedAgent|null}`.
|
|
1477
|
+
*/
|
|
1478
|
+
export function agentsInstall(manifestJson: string): Promise<string>;
|
|
1479
|
+
|
|
1366
1480
|
/**
|
|
1367
1481
|
* Remove an agent's spec. Stops the running child first if it's up.
|
|
1368
1482
|
* Idempotent — `{removed: false}` when nothing matched.
|
package/index.js
CHANGED
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
const os = require('node:os');
|
|
5
5
|
const path = require('node:path');
|
|
6
6
|
|
|
7
|
+
// darwin-x64 was dropped — Apple Silicon only on macOS. See the
|
|
8
|
+
// "macOS x86_64 support dropped" CHANGELOG entry.
|
|
7
9
|
const platformMap = {
|
|
8
10
|
'darwin-arm64': 'darwin-arm64',
|
|
9
|
-
'darwin-x64': 'darwin-x64',
|
|
10
11
|
'linux-x64': 'linux-x64-gnu',
|
|
11
12
|
'linux-arm64': 'linux-arm64-gnu',
|
|
12
13
|
'win32-x64': 'win32-x64-msvc',
|
package/install.js
CHANGED
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// Downloads the native Node.js
|
|
3
|
-
//
|
|
4
|
-
//
|
|
2
|
+
// Downloads the native artifacts (Node.js `.node` module + the
|
|
3
|
+
// `car-server` daemon binary) matching this package's version and
|
|
4
|
+
// the host platform from the car-releases GitHub repo. Cached
|
|
5
|
+
// next to this script so `require('./car-runtime.<platform>.node')`
|
|
6
|
+
// and `npx --package=car-runtime car-server` both resolve without
|
|
5
7
|
// another network round-trip.
|
|
8
|
+
//
|
|
9
|
+
// Closes Parslee-ai/car-releases#36 followup: prior versions
|
|
10
|
+
// shipped only the .node and broke `npx … car-server` despite the
|
|
11
|
+
// README promising both.
|
|
12
|
+
//
|
|
13
|
+
// Asset names live in `assets.json` — single source of truth
|
|
14
|
+
// across install.js, scripts/release.sh, and the CI build flow.
|
|
15
|
+
// Per neo + Linus review feedback (this PR): consolidating the
|
|
16
|
+
// list closes a 3-source drift hazard the prior commit left open.
|
|
6
17
|
|
|
7
18
|
const fs = require('node:fs');
|
|
8
19
|
const path = require('node:path');
|
|
@@ -10,23 +21,22 @@ const http = require('node:http');
|
|
|
10
21
|
const https = require('node:https');
|
|
11
22
|
const { pipeline } = require('node:stream/promises');
|
|
12
23
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
'
|
|
18
|
-
|
|
19
|
-
};
|
|
24
|
+
// Platform → asset names read from the canonical assets.json registry.
|
|
25
|
+
// darwin-x64 was removed there — Apple Silicon only on macOS. See the
|
|
26
|
+
// "macOS x86_64 support dropped" CHANGELOG entry.
|
|
27
|
+
const ASSETS = JSON.parse(
|
|
28
|
+
fs.readFileSync(path.join(__dirname, 'assets.json'), 'utf8'),
|
|
29
|
+
).platforms;
|
|
20
30
|
|
|
21
31
|
function targetForHost() {
|
|
22
32
|
const key = `${process.platform}-${process.arch}`;
|
|
23
|
-
const
|
|
24
|
-
if (!
|
|
33
|
+
const entry = ASSETS[key];
|
|
34
|
+
if (!entry) {
|
|
25
35
|
throw new Error(
|
|
26
|
-
`Unsupported platform ${key}. Supported: ${Object.keys(
|
|
36
|
+
`Unsupported platform ${key}. Supported: ${Object.keys(ASSETS).join(', ')}.`,
|
|
27
37
|
);
|
|
28
38
|
}
|
|
29
|
-
return
|
|
39
|
+
return { key, nodeName: entry.node, serverName: entry.server };
|
|
30
40
|
}
|
|
31
41
|
|
|
32
42
|
function pkgVersion() {
|
|
@@ -61,46 +71,87 @@ function get(url) {
|
|
|
61
71
|
});
|
|
62
72
|
}
|
|
63
73
|
|
|
74
|
+
async function downloadAsset(assetName, destPath, { executable = false } = {}) {
|
|
75
|
+
if (fs.existsSync(destPath)) {
|
|
76
|
+
return { skipped: true };
|
|
77
|
+
}
|
|
78
|
+
const tmp = `${destPath}.part`;
|
|
79
|
+
// Clean stale .part files from a prior crashed install — without
|
|
80
|
+
// this, fs.createWriteStream's default flag is 'w' which truncates
|
|
81
|
+
// (fine for content) but a half-written file from a *previous*
|
|
82
|
+
// process exit between writeStream end + rename leaves an orphan
|
|
83
|
+
// .part on disk that nothing cleans up. Best-effort unlink.
|
|
84
|
+
if (fs.existsSync(tmp)) {
|
|
85
|
+
try {
|
|
86
|
+
fs.unlinkSync(tmp);
|
|
87
|
+
} catch (_) {
|
|
88
|
+
// Non-fatal — pipeline will overwrite with 'w' mode regardless.
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const version = pkgVersion();
|
|
92
|
+
const base = process.env.CAR_RUNTIME_DOWNLOAD_BASE;
|
|
93
|
+
const url = base
|
|
94
|
+
? `${base}/${assetName}`
|
|
95
|
+
: `https://github.com/Parslee-ai/car-releases/releases/download/v${version}/${assetName}`;
|
|
96
|
+
console.log(`[car-runtime] downloading ${url}`);
|
|
97
|
+
const res = await get(url);
|
|
98
|
+
await pipeline(res, fs.createWriteStream(tmp));
|
|
99
|
+
fs.renameSync(tmp, destPath);
|
|
100
|
+
if (executable && process.platform !== 'win32') {
|
|
101
|
+
// npm strips the execute bit from non-bin files. car-server is
|
|
102
|
+
// exec'd by bin/car-server through child_process.spawn, which
|
|
103
|
+
// needs the bit. chmod here so the bin shim doesn't have to
|
|
104
|
+
// re-stat + chmod on first run.
|
|
105
|
+
try {
|
|
106
|
+
fs.chmodSync(destPath, 0o755);
|
|
107
|
+
} catch (e) {
|
|
108
|
+
console.warn(
|
|
109
|
+
`[car-runtime] chmod +x on ${destPath} failed: ${e.message}. ` +
|
|
110
|
+
`\`npx --package=car-runtime car-server\` may fail with EACCES.`
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
console.log(`[car-runtime] installed ${assetName}`);
|
|
115
|
+
return { skipped: false };
|
|
116
|
+
}
|
|
117
|
+
|
|
64
118
|
async function main() {
|
|
65
|
-
const
|
|
66
|
-
const dest = path.join(__dirname, name);
|
|
119
|
+
const { nodeName, serverName } = targetForHost();
|
|
67
120
|
|
|
68
|
-
// Opt-out for air-gapped installs — user is expected to drop
|
|
69
|
-
// file into this directory
|
|
121
|
+
// Opt-out for air-gapped installs — user is expected to drop both
|
|
122
|
+
// the .node file AND the car-server binary into this directory
|
|
123
|
+
// themselves before requiring the package.
|
|
70
124
|
if (process.env.CAR_RUNTIME_SKIP_DOWNLOAD === '1') {
|
|
71
125
|
console.log('[car-runtime] CAR_RUNTIME_SKIP_DOWNLOAD=1 — skipping download.');
|
|
72
126
|
return;
|
|
73
127
|
}
|
|
74
128
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const version = pkgVersion();
|
|
81
|
-
const url = `https://github.com/Parslee-ai/car-releases/releases/download/v${version}/${name}`;
|
|
129
|
+
const nodeDest = path.join(__dirname, nodeName);
|
|
130
|
+
const serverDest = path.join(__dirname, serverName);
|
|
82
131
|
|
|
83
|
-
//
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
132
|
+
// Both downloads are required. Failing one but not the other
|
|
133
|
+
// produces a half-working package (e.g. `require('car-runtime')`
|
|
134
|
+
// works but `npx --package=car-runtime car-server` fails at
|
|
135
|
+
// exec time) — strictly worse than `npm install` failing loudly
|
|
136
|
+
// here. The CI preflight gate ensures the release has both
|
|
137
|
+
// assets before publish, so under normal operation neither
|
|
138
|
+
// download fails for a missing asset; the only failure mode
|
|
139
|
+
// is network / mirror flakiness, which retries handle better
|
|
140
|
+
// than runtime ENOENT.
|
|
141
|
+
for (const { name, dest, executable, kind } of [
|
|
142
|
+
{ name: nodeName, dest: nodeDest, executable: false, kind: '.node module' },
|
|
143
|
+
{ name: serverName, dest: serverDest, executable: true, kind: 'car-server binary' },
|
|
144
|
+
]) {
|
|
145
|
+
try {
|
|
146
|
+
await downloadAsset(name, dest, { executable });
|
|
147
|
+
} catch (err) {
|
|
148
|
+
console.error(
|
|
149
|
+
`[car-runtime] ${kind} download failed: ${err.message}\n` +
|
|
150
|
+
`Place ${name} manually in ${__dirname} or set CAR_RUNTIME_SKIP_DOWNLOAD=1 ` +
|
|
151
|
+
`and ensure both the .node module and car-server binary are present.`,
|
|
152
|
+
);
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
104
155
|
}
|
|
105
156
|
}
|
|
106
157
|
|
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "car-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Common Agent Runtime — a deterministic execution layer for AI agents",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"car-server": "bin/car-server"
|
|
9
|
+
},
|
|
7
10
|
"keywords": [
|
|
8
11
|
"ai",
|
|
9
12
|
"agent",
|
|
@@ -34,6 +37,8 @@
|
|
|
34
37
|
"index.js",
|
|
35
38
|
"index.d.ts",
|
|
36
39
|
"install.js",
|
|
40
|
+
"assets.json",
|
|
41
|
+
"bin/car-server",
|
|
37
42
|
"README.md",
|
|
38
43
|
"LICENSE"
|
|
39
44
|
],
|