@camstack/system 1.2.23 → 1.2.24
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/dist/addon-runner.js +1 -1
- package/dist/addon-runner.mjs +1 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/kernel/transport/local-child-registry.d.ts +20 -0
- package/dist/kernel/transport/local-transport.d.ts +11 -1
- package/dist/kernel/transport/socket-channel.d.ts +28 -1
- package/dist/{manifest-python-deps-CqDPWqEE.js → manifest-python-deps-BbOZZbVt.js} +42 -7
- package/dist/{manifest-python-deps-57PA4rHm.mjs → manifest-python-deps-C9qhvMUi.mjs} +42 -7
- package/package.json +1 -1
package/dist/addon-runner.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-Cek0wNdY.js");
|
|
2
|
-
const require_manifest_python_deps = require("./manifest-python-deps-
|
|
2
|
+
const require_manifest_python_deps = require("./manifest-python-deps-BbOZZbVt.js");
|
|
3
3
|
const require_custom_action_registry = require("./custom-action-registry-jY0NOZK8.js");
|
|
4
4
|
let node_fs = require("node:fs");
|
|
5
5
|
node_fs = require_chunk.__toESM(node_fs);
|
package/dist/addon-runner.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Ct as resolveAddonClass, I as createUdsLoggerWithControl, L as LocalChildClient, at as setWorkerNativeCapsChangeListener, bt as installManifestNativeDeps, i as createUdsAddonContext, nt as getWorkerNativeCapProvider, rt as getWorkerNativeCapSnapshot, st as validateProviderRegistrations, t as installManifestPythonDeps } from "./manifest-python-deps-
|
|
1
|
+
import { Ct as resolveAddonClass, I as createUdsLoggerWithControl, L as LocalChildClient, at as setWorkerNativeCapsChangeListener, bt as installManifestNativeDeps, i as createUdsAddonContext, nt as getWorkerNativeCapProvider, rt as getWorkerNativeCapSnapshot, st as validateProviderRegistrations, t as installManifestPythonDeps } from "./manifest-python-deps-C9qhvMUi.mjs";
|
|
2
2
|
import { t as CustomActionRegistry } from "./custom-action-registry-F__gp_VX.mjs";
|
|
3
3
|
import { register } from "node:module";
|
|
4
4
|
import * as fs from "node:fs";
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
2
2
|
const require_chunk = require("./chunk-Cek0wNdY.js");
|
|
3
3
|
const require_dist = require("./dist-CS0pdC8l.js");
|
|
4
4
|
const require_model_download_service = require("./model-download-service-hf0ookyy.js");
|
|
5
|
-
const require_manifest_python_deps = require("./manifest-python-deps-
|
|
5
|
+
const require_manifest_python_deps = require("./manifest-python-deps-BbOZZbVt.js");
|
|
6
6
|
const require_resource_monitor = require("./resource-monitor-DNNomR-i.js");
|
|
7
7
|
const require_builtins_sqlite_storage_filesystem_storage_addon = require("./builtins/sqlite-storage/filesystem-storage.addon.js");
|
|
8
8
|
const require_builtins_sqlite_storage_sqlite_settings_addon = require("./builtins/sqlite-storage/sqlite-settings.addon.js");
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as __toCommonJS, i as __require, n as __esmMin, o as __toESM$1, r as __exportAll, t as __commonJSMin$1 } from "./chunk-CNf5ZN-e.mjs";
|
|
2
2
|
import { B as logLevelAtMost, Dt as readinessKey, Et as parseJsonUnknown$1, F as isVoidInput, I as kebabToCamel, L as lifecycleJobSchema, M as isArrayOutputSchema, N as isCollectionArrayMethod, Ot as resolveCapMount, P as isObjectInput, Tt as parseJsonObject, V as looseSchema, W as objectInputDeclaresAddonId, _t as asString$1, c as RUNTIME_DEFAULTS, ct as DEVICE_STATUS_METHOD, ft as ReadinessRegistry, gt as asNumber, ht as asJsonObject$1, it as errMsg$1, jt as EventCategory$1, k as extractNestedAddonId, kt as scopeKey, m as addonSettingsCapability, ot as DATAPLANE_SECRET_HEADER$1, pt as ReadinessTimeoutError, q as procedureAuthKey, s as METHOD_ACCESS_MAP, st as DEVICE_SETTINGS_CONTRIBUTION_METHODS, t as ALL_CAPABILITY_DEFINITIONS, vt as createEvent, xt as expandCapMethods, yt as emitDownForOwnedCaps } from "./dist-CIek6F9X.mjs";
|
|
3
3
|
import { a as downloadModel, c as getModelFilePath, d as contentTypeFor, f as createAuthenticatedFileServer, h as resolveFilePath, i as downloadFile, l as isModelDownloaded, m as parseTokenizedUrl, n as collectModelFiles, o as ensureModel, p as parseRangeHeader, r as deleteModelFromDisk, s as fetchJson, t as ModelDownloadService, u as createFileDataPlaneHandler } from "./model-download-service-Cp9f4dk6.mjs";
|
|
4
|
-
import { $ as buildNativeCapProxy, A as createHubCapForwardService, B as AGENT_CAP_FWD_ACTION, C as brokerTransportLink, Ct as resolveAddonClass, D as localProviderLink, E as ipcParentLink, F as createUdsLogger, G as callWithServiceDiscovery, H as CapRouteResolver, I as createUdsLoggerWithControl, J as UdsLocalTransportServer, K as createLocalTransport, L as LocalChildClient, M as createUdsEventBridge, N as createUdsEventBus, O as HUB_CAP_FWD_ACTION, P as udsChildLogToWorkerEntry, Q as encodeFrame, R as LocalChildRegistry, S as brokerCallForCap, St as runNpm, T as ipcChildLink, U as CapRouteError, V as AGENT_CAP_FWD_SERVICE, W as classifyCapRoute, X as localEndpointPath, Y as SocketChannel, Z as FrameDecoder, _ as createKernelHwAccel, _t as CapabilityHandle, a as getWorkerDeviceRegistry, b as __resetCapUsageRegistryForTests, bt as installManifestNativeDeps, c as setHubConnected, ct as NATIVE_PROVIDER_SERVICE_INFIX, d as getBrokerEventBus, dt as capBareAction, et as buildUdsNativeCapProxy, f as getMoleculerEventStats, ft as capServiceName, g as AddonDepsManager, gt as DeviceRegistry, h as subscribePassthrough, ht as serializeTypedArrays, i as createUdsAddonContext, it as mountNativeCapService, j as createParentUnownedCallHandler, k as HUB_CAP_FWD_SERVICE, l as EVENT_TOPIC_PREFIX, lt as capActionName, m as setNodeEventInterest, mt as deserializeTypedArrays, n as adaptBrokerToCluster, o as getOrInitReadinessRegistry, ot as createAddonService, p as registerEventBusService, pt as parseCapAction, q as UdsLocalTransportClient, r as createAddonContext, s as getOrInitReadinessRegistryForClient, st as validateProviderRegistrations, t as installManifestPythonDeps, tt as createBrokerDeviceManagerApi, u as clusterEventTopic, ut as capActionSuffix, v as resolveHwAccel, vt as CapabilityUnavailableError, w as buildLinkChain, wt as createAddonDataPlaneFacility, x as getCapUsageRegistry, xt as resolveNpmInvocation, y as CapUsageRegistry, yt as copyBundledNativeModules, z as UDS_NO_ROUTE_PREFIX } from "./manifest-python-deps-
|
|
4
|
+
import { $ as buildNativeCapProxy, A as createHubCapForwardService, B as AGENT_CAP_FWD_ACTION, C as brokerTransportLink, Ct as resolveAddonClass, D as localProviderLink, E as ipcParentLink, F as createUdsLogger, G as callWithServiceDiscovery, H as CapRouteResolver, I as createUdsLoggerWithControl, J as UdsLocalTransportServer, K as createLocalTransport, L as LocalChildClient, M as createUdsEventBridge, N as createUdsEventBus, O as HUB_CAP_FWD_ACTION, P as udsChildLogToWorkerEntry, Q as encodeFrame, R as LocalChildRegistry, S as brokerCallForCap, St as runNpm, T as ipcChildLink, U as CapRouteError, V as AGENT_CAP_FWD_SERVICE, W as classifyCapRoute, X as localEndpointPath, Y as SocketChannel, Z as FrameDecoder, _ as createKernelHwAccel, _t as CapabilityHandle, a as getWorkerDeviceRegistry, b as __resetCapUsageRegistryForTests, bt as installManifestNativeDeps, c as setHubConnected, ct as NATIVE_PROVIDER_SERVICE_INFIX, d as getBrokerEventBus, dt as capBareAction, et as buildUdsNativeCapProxy, f as getMoleculerEventStats, ft as capServiceName, g as AddonDepsManager, gt as DeviceRegistry, h as subscribePassthrough, ht as serializeTypedArrays, i as createUdsAddonContext, it as mountNativeCapService, j as createParentUnownedCallHandler, k as HUB_CAP_FWD_SERVICE, l as EVENT_TOPIC_PREFIX, lt as capActionName, m as setNodeEventInterest, mt as deserializeTypedArrays, n as adaptBrokerToCluster, o as getOrInitReadinessRegistry, ot as createAddonService, p as registerEventBusService, pt as parseCapAction, q as UdsLocalTransportClient, r as createAddonContext, s as getOrInitReadinessRegistryForClient, st as validateProviderRegistrations, t as installManifestPythonDeps, tt as createBrokerDeviceManagerApi, u as clusterEventTopic, ut as capActionSuffix, v as resolveHwAccel, vt as CapabilityUnavailableError, w as buildLinkChain, wt as createAddonDataPlaneFacility, x as getCapUsageRegistry, xt as resolveNpmInvocation, y as CapUsageRegistry, yt as copyBundledNativeModules, z as UDS_NO_ROUTE_PREFIX } from "./manifest-python-deps-C9qhvMUi.mjs";
|
|
5
5
|
import { n as getSinglePidStats, t as getPidStats } from "./resource-monitor-BkP504Vq.mjs";
|
|
6
6
|
import { FilesystemStorageAddon, t as FilesystemStorageProvider } from "./builtins/sqlite-storage/filesystem-storage.addon.mjs";
|
|
7
7
|
import { SqliteSettingsAddon, t as SqliteSettingsBackend } from "./builtins/sqlite-storage/sqlite-settings.addon.mjs";
|
|
@@ -95,6 +95,18 @@ export interface LocalChildRegistryOptions {
|
|
|
95
95
|
* everything). The kernel has no cap registry, so the wiring side injects it.
|
|
96
96
|
*/
|
|
97
97
|
readonly isAggregatedCollectionMethod?: (capName: string, method: string) => boolean;
|
|
98
|
+
/**
|
|
99
|
+
* Per-cap-method bound for UDS cap calls, in ms — read from the cap
|
|
100
|
+
* definition's declared `timeoutMs`. The SAME resolver the remote plane uses
|
|
101
|
+
* (`CapRouteResolver.capTimeoutMs`), so the two planes agree by construction
|
|
102
|
+
* instead of by coincidence.
|
|
103
|
+
*
|
|
104
|
+
* Without this the channel default (60 s) would apply to everything, and a
|
|
105
|
+
* legitimately long method — `applyServerUpdate` declares 16 min because it
|
|
106
|
+
* awaits a full npm install — would be cut off mid-run. Omitted ⇒ the
|
|
107
|
+
* channel default for every cap call.
|
|
108
|
+
*/
|
|
109
|
+
readonly capTimeoutMs?: (capName: string, method: string) => number | undefined;
|
|
98
110
|
}
|
|
99
111
|
/**
|
|
100
112
|
* Parent-side authority for local addon-runners reachable over a
|
|
@@ -118,6 +130,8 @@ export declare class LocalChildRegistry {
|
|
|
118
130
|
private readonly ownNodeId?;
|
|
119
131
|
/** See {@link LocalChildRegistryOptions.isAggregatedCollectionMethod}. */
|
|
120
132
|
private readonly isAggregatedCollectionMethod?;
|
|
133
|
+
/** See {@link LocalChildRegistryOptions.capTimeoutMs}. */
|
|
134
|
+
private readonly capTimeoutMs?;
|
|
121
135
|
/** Tracks capNames already logged as UDS-routed; one INFO line per capName per process. */
|
|
122
136
|
private readonly egressRoutedCaps;
|
|
123
137
|
/** Active event fan-out mode, read once from `CAMSTACK_UDS_EVENT_FANOUT`. */
|
|
@@ -227,6 +241,12 @@ export declare class LocalChildRegistry {
|
|
|
227
241
|
*/
|
|
228
242
|
isChildKnown(childId: string): boolean;
|
|
229
243
|
childProvides(childId: string, capName: string, deviceId?: number): boolean;
|
|
244
|
+
/**
|
|
245
|
+
* Per-request options for a cap call: the cap method's declared `timeoutMs`
|
|
246
|
+
* when one exists, else `undefined` so the channel applies its default.
|
|
247
|
+
* Mirrors `CapRouteResolver.callTimeout` for the remote plane.
|
|
248
|
+
*/
|
|
249
|
+
private capRequestOpts;
|
|
230
250
|
/** Forward a cap method call to a SPECIFIC child by id over UDS; rejects if that child is absent. */
|
|
231
251
|
callCapOnChild(childId: string, input: CapCallInput): Promise<unknown>;
|
|
232
252
|
/** Forward a cap method call to the owning child over UDS; rejects if none. */
|
|
@@ -19,9 +19,19 @@ export type Frame = {
|
|
|
19
19
|
};
|
|
20
20
|
export type RequestHandler = (body: unknown) => Promise<unknown>;
|
|
21
21
|
export type EventHandler = (body: unknown) => void;
|
|
22
|
+
/** Per-request options a {@link LocalChannel} implementation may honour. */
|
|
23
|
+
export interface LocalRequestOptions {
|
|
24
|
+
/**
|
|
25
|
+
* Bound on this request in ms; `0` or negative disables it. Omitted ⇒ the
|
|
26
|
+
* implementation's default. Cap calls pass the cap method's declared
|
|
27
|
+
* `timeoutMs` so a legitimately long method (`applyServerUpdate`, 16 min) is
|
|
28
|
+
* not cut short.
|
|
29
|
+
*/
|
|
30
|
+
readonly timeoutMs?: number;
|
|
31
|
+
}
|
|
22
32
|
/** A bidirectional request/response + one-way event channel over one socket. */
|
|
23
33
|
export interface LocalChannel {
|
|
24
|
-
request(body: unknown): Promise<unknown>;
|
|
34
|
+
request(body: unknown, opts?: LocalRequestOptions): Promise<unknown>;
|
|
25
35
|
emit(body: unknown): void;
|
|
26
36
|
onRequest(handler: RequestHandler): void;
|
|
27
37
|
onEvent(handler: EventHandler): void;
|
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
import { Socket } from 'node:net';
|
|
2
2
|
import { LocalChannel, RequestHandler, EventHandler } from './local-transport.js';
|
|
3
|
+
/**
|
|
4
|
+
* Default bound on one UDS request, when the caller names none.
|
|
5
|
+
*
|
|
6
|
+
* Chosen to match `REMOTE_CALL_TIMEOUT_MS` in `cap-route-resolver.ts`: the SAME
|
|
7
|
+
* cap method already dies at 60 s when it resolves to a remote node, so
|
|
8
|
+
* applying the same bound locally makes the two planes consistent rather than
|
|
9
|
+
* newly restrictive. Callers with a legitimately longer method (the cap
|
|
10
|
+
* layer's declared `timeoutMs`, e.g. `applyServerUpdate` at 16 min) pass it
|
|
11
|
+
* explicitly.
|
|
12
|
+
*/
|
|
13
|
+
export declare const DEFAULT_UDS_REQUEST_TIMEOUT_MS = 60000;
|
|
14
|
+
/** Per-request options for {@link SocketChannel.request}. */
|
|
15
|
+
export interface SocketRequestOptions {
|
|
16
|
+
/** Bound in ms; `0` or negative disables the timer entirely. */
|
|
17
|
+
readonly timeoutMs?: number;
|
|
18
|
+
}
|
|
3
19
|
export declare class SocketChannel implements LocalChannel {
|
|
4
20
|
private readonly socket;
|
|
5
21
|
private nextId;
|
|
@@ -11,7 +27,18 @@ export declare class SocketChannel implements LocalChannel {
|
|
|
11
27
|
private closed;
|
|
12
28
|
private closeFired;
|
|
13
29
|
constructor(socket: Socket);
|
|
14
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Send a request and wait for the peer's response.
|
|
32
|
+
*
|
|
33
|
+
* Bounded. Without a timer this settles on exactly two events — a matching
|
|
34
|
+
* `res` frame, or the socket's `close` (which drains `pending`) — so a peer
|
|
35
|
+
* that receives the request and then neither answers nor closes leaves the
|
|
36
|
+
* caller pending forever. That is a wedged handler, or a half-open socket
|
|
37
|
+
* after a child died without a clean teardown, and it is how camera 615 spent
|
|
38
|
+
* 30+ minutes with every inference permit held by a call that was never
|
|
39
|
+
* coming back.
|
|
40
|
+
*/
|
|
41
|
+
request(body: unknown, opts?: SocketRequestOptions): Promise<unknown>;
|
|
15
42
|
emit(body: unknown): void;
|
|
16
43
|
/** Replace the request handler. Only one handler is active at a time. */
|
|
17
44
|
onRequest(handler: RequestHandler): void;
|
|
@@ -3526,8 +3526,6 @@ function localEndpointPath(nodeId) {
|
|
|
3526
3526
|
if (process.platform === "win32") return `\\\\.\\pipe\\camstack-${safe}`;
|
|
3527
3527
|
return (0, node_path.join)((0, node_os.tmpdir)(), `camstack-${safe}.sock`);
|
|
3528
3528
|
}
|
|
3529
|
-
//#endregion
|
|
3530
|
-
//#region src/kernel/transport/socket-channel.ts
|
|
3531
3529
|
var SocketChannel = class {
|
|
3532
3530
|
socket;
|
|
3533
3531
|
nextId = 1;
|
|
@@ -3546,13 +3544,34 @@ var SocketChannel = class {
|
|
|
3546
3544
|
socket.on("close", () => this.handleClose());
|
|
3547
3545
|
socket.on("error", () => {});
|
|
3548
3546
|
}
|
|
3549
|
-
|
|
3547
|
+
/**
|
|
3548
|
+
* Send a request and wait for the peer's response.
|
|
3549
|
+
*
|
|
3550
|
+
* Bounded. Without a timer this settles on exactly two events — a matching
|
|
3551
|
+
* `res` frame, or the socket's `close` (which drains `pending`) — so a peer
|
|
3552
|
+
* that receives the request and then neither answers nor closes leaves the
|
|
3553
|
+
* caller pending forever. That is a wedged handler, or a half-open socket
|
|
3554
|
+
* after a child died without a clean teardown, and it is how camera 615 spent
|
|
3555
|
+
* 30+ minutes with every inference permit held by a call that was never
|
|
3556
|
+
* coming back.
|
|
3557
|
+
*/
|
|
3558
|
+
request(body, opts) {
|
|
3550
3559
|
if (this.closed) return Promise.reject(/* @__PURE__ */ new Error("channel closed"));
|
|
3551
3560
|
const id = this.nextId++;
|
|
3561
|
+
const timeoutMs = opts?.timeoutMs ?? 6e4;
|
|
3552
3562
|
return new Promise((resolve, reject) => {
|
|
3563
|
+
let timer = null;
|
|
3564
|
+
if (timeoutMs > 0) {
|
|
3565
|
+
timer = setTimeout(() => {
|
|
3566
|
+
this.pending.delete(id);
|
|
3567
|
+
reject(/* @__PURE__ */ new Error(`UDS request timed out after ${timeoutMs}ms`));
|
|
3568
|
+
}, timeoutMs);
|
|
3569
|
+
timer.unref?.();
|
|
3570
|
+
}
|
|
3553
3571
|
this.pending.set(id, {
|
|
3554
3572
|
resolve,
|
|
3555
|
-
reject
|
|
3573
|
+
reject,
|
|
3574
|
+
timer
|
|
3556
3575
|
});
|
|
3557
3576
|
this.send({
|
|
3558
3577
|
k: "req",
|
|
@@ -3616,6 +3635,7 @@ var SocketChannel = class {
|
|
|
3616
3635
|
const p = this.pending.get(frame.id);
|
|
3617
3636
|
if (!p) return;
|
|
3618
3637
|
this.pending.delete(frame.id);
|
|
3638
|
+
if (p.timer !== null) clearTimeout(p.timer);
|
|
3619
3639
|
if (frame.ok) p.resolve(frame.body);
|
|
3620
3640
|
else p.reject(new Error(frame.err));
|
|
3621
3641
|
return;
|
|
@@ -3626,7 +3646,10 @@ var SocketChannel = class {
|
|
|
3626
3646
|
if (this.closeFired) return;
|
|
3627
3647
|
this.closeFired = true;
|
|
3628
3648
|
this.closed = true;
|
|
3629
|
-
for (const [, p] of this.pending)
|
|
3649
|
+
for (const [, p] of this.pending) {
|
|
3650
|
+
if (p.timer !== null) clearTimeout(p.timer);
|
|
3651
|
+
p.reject(/* @__PURE__ */ new Error("channel closed"));
|
|
3652
|
+
}
|
|
3630
3653
|
this.pending.clear();
|
|
3631
3654
|
this.closeHandler();
|
|
3632
3655
|
}
|
|
@@ -4305,6 +4328,8 @@ var LocalChildRegistry = class {
|
|
|
4305
4328
|
ownNodeId;
|
|
4306
4329
|
/** See {@link LocalChildRegistryOptions.isAggregatedCollectionMethod}. */
|
|
4307
4330
|
isAggregatedCollectionMethod;
|
|
4331
|
+
/** See {@link LocalChildRegistryOptions.capTimeoutMs}. */
|
|
4332
|
+
capTimeoutMs;
|
|
4308
4333
|
/** Tracks capNames already logged as UDS-routed; one INFO line per capName per process. */
|
|
4309
4334
|
egressRoutedCaps = /* @__PURE__ */ new Set();
|
|
4310
4335
|
/** Active event fan-out mode, read once from `CAMSTACK_UDS_EVENT_FANOUT`. */
|
|
@@ -4334,6 +4359,7 @@ var LocalChildRegistry = class {
|
|
|
4334
4359
|
this.resolveChildIdForAddon = opts.resolveChildIdForAddon;
|
|
4335
4360
|
this.ownNodeId = opts.ownNodeId;
|
|
4336
4361
|
this.isAggregatedCollectionMethod = opts.isAggregatedCollectionMethod;
|
|
4362
|
+
this.capTimeoutMs = opts.capTimeoutMs;
|
|
4337
4363
|
} else {
|
|
4338
4364
|
this.server = serverOrOptions;
|
|
4339
4365
|
this.onUnownedCall = onUnownedCallArg;
|
|
@@ -4496,11 +4522,20 @@ var LocalChildRegistry = class {
|
|
|
4496
4522
|
if (deviceId !== void 0 && entry.caps.some((cap) => cap.capName === capName && cap.deviceId === deviceId)) return true;
|
|
4497
4523
|
return entry.caps.some((cap) => cap.capName === capName && cap.deviceId === void 0);
|
|
4498
4524
|
}
|
|
4525
|
+
/**
|
|
4526
|
+
* Per-request options for a cap call: the cap method's declared `timeoutMs`
|
|
4527
|
+
* when one exists, else `undefined` so the channel applies its default.
|
|
4528
|
+
* Mirrors `CapRouteResolver.callTimeout` for the remote plane.
|
|
4529
|
+
*/
|
|
4530
|
+
capRequestOpts(input) {
|
|
4531
|
+
const declared = this.capTimeoutMs?.(input.capName, input.method);
|
|
4532
|
+
return typeof declared === "number" ? { timeoutMs: declared } : void 0;
|
|
4533
|
+
}
|
|
4499
4534
|
/** Forward a cap method call to a SPECIFIC child by id over UDS; rejects if that child is absent. */
|
|
4500
4535
|
async callCapOnChild(childId, input) {
|
|
4501
4536
|
const entry = this.children.get(childId);
|
|
4502
4537
|
if (!entry) throw new Error(`no local child "${childId}" for cap "${input.capName}"`);
|
|
4503
|
-
return entry.channel.request(this.toCapCall(input));
|
|
4538
|
+
return entry.channel.request(this.toCapCall(input), this.capRequestOpts(input));
|
|
4504
4539
|
}
|
|
4505
4540
|
/** Forward a cap method call to the owning child over UDS; rejects if none. */
|
|
4506
4541
|
async callCap(input) {
|
|
@@ -4510,7 +4545,7 @@ var LocalChildRegistry = class {
|
|
|
4510
4545
|
const where = input.deviceId === void 0 ? "" : ` for device ${input.deviceId}`;
|
|
4511
4546
|
throw new Error(`no local child provides cap "${input.capName}"${where}`);
|
|
4512
4547
|
}
|
|
4513
|
-
return entry.channel.request(this.toCapCall(input));
|
|
4548
|
+
return entry.channel.request(this.toCapCall(input), this.capRequestOpts(input));
|
|
4514
4549
|
}
|
|
4515
4550
|
/**
|
|
4516
4551
|
* Forward an ADDON-LEVEL call (routes / custom-action) to a SPECIFIC child
|
|
@@ -3524,8 +3524,6 @@ function localEndpointPath(nodeId) {
|
|
|
3524
3524
|
if (process.platform === "win32") return `\\\\.\\pipe\\camstack-${safe}`;
|
|
3525
3525
|
return join(tmpdir(), `camstack-${safe}.sock`);
|
|
3526
3526
|
}
|
|
3527
|
-
//#endregion
|
|
3528
|
-
//#region src/kernel/transport/socket-channel.ts
|
|
3529
3527
|
var SocketChannel = class {
|
|
3530
3528
|
socket;
|
|
3531
3529
|
nextId = 1;
|
|
@@ -3544,13 +3542,34 @@ var SocketChannel = class {
|
|
|
3544
3542
|
socket.on("close", () => this.handleClose());
|
|
3545
3543
|
socket.on("error", () => {});
|
|
3546
3544
|
}
|
|
3547
|
-
|
|
3545
|
+
/**
|
|
3546
|
+
* Send a request and wait for the peer's response.
|
|
3547
|
+
*
|
|
3548
|
+
* Bounded. Without a timer this settles on exactly two events — a matching
|
|
3549
|
+
* `res` frame, or the socket's `close` (which drains `pending`) — so a peer
|
|
3550
|
+
* that receives the request and then neither answers nor closes leaves the
|
|
3551
|
+
* caller pending forever. That is a wedged handler, or a half-open socket
|
|
3552
|
+
* after a child died without a clean teardown, and it is how camera 615 spent
|
|
3553
|
+
* 30+ minutes with every inference permit held by a call that was never
|
|
3554
|
+
* coming back.
|
|
3555
|
+
*/
|
|
3556
|
+
request(body, opts) {
|
|
3548
3557
|
if (this.closed) return Promise.reject(/* @__PURE__ */ new Error("channel closed"));
|
|
3549
3558
|
const id = this.nextId++;
|
|
3559
|
+
const timeoutMs = opts?.timeoutMs ?? 6e4;
|
|
3550
3560
|
return new Promise((resolve, reject) => {
|
|
3561
|
+
let timer = null;
|
|
3562
|
+
if (timeoutMs > 0) {
|
|
3563
|
+
timer = setTimeout(() => {
|
|
3564
|
+
this.pending.delete(id);
|
|
3565
|
+
reject(/* @__PURE__ */ new Error(`UDS request timed out after ${timeoutMs}ms`));
|
|
3566
|
+
}, timeoutMs);
|
|
3567
|
+
timer.unref?.();
|
|
3568
|
+
}
|
|
3551
3569
|
this.pending.set(id, {
|
|
3552
3570
|
resolve,
|
|
3553
|
-
reject
|
|
3571
|
+
reject,
|
|
3572
|
+
timer
|
|
3554
3573
|
});
|
|
3555
3574
|
this.send({
|
|
3556
3575
|
k: "req",
|
|
@@ -3614,6 +3633,7 @@ var SocketChannel = class {
|
|
|
3614
3633
|
const p = this.pending.get(frame.id);
|
|
3615
3634
|
if (!p) return;
|
|
3616
3635
|
this.pending.delete(frame.id);
|
|
3636
|
+
if (p.timer !== null) clearTimeout(p.timer);
|
|
3617
3637
|
if (frame.ok) p.resolve(frame.body);
|
|
3618
3638
|
else p.reject(new Error(frame.err));
|
|
3619
3639
|
return;
|
|
@@ -3624,7 +3644,10 @@ var SocketChannel = class {
|
|
|
3624
3644
|
if (this.closeFired) return;
|
|
3625
3645
|
this.closeFired = true;
|
|
3626
3646
|
this.closed = true;
|
|
3627
|
-
for (const [, p] of this.pending)
|
|
3647
|
+
for (const [, p] of this.pending) {
|
|
3648
|
+
if (p.timer !== null) clearTimeout(p.timer);
|
|
3649
|
+
p.reject(/* @__PURE__ */ new Error("channel closed"));
|
|
3650
|
+
}
|
|
3628
3651
|
this.pending.clear();
|
|
3629
3652
|
this.closeHandler();
|
|
3630
3653
|
}
|
|
@@ -4303,6 +4326,8 @@ var LocalChildRegistry = class {
|
|
|
4303
4326
|
ownNodeId;
|
|
4304
4327
|
/** See {@link LocalChildRegistryOptions.isAggregatedCollectionMethod}. */
|
|
4305
4328
|
isAggregatedCollectionMethod;
|
|
4329
|
+
/** See {@link LocalChildRegistryOptions.capTimeoutMs}. */
|
|
4330
|
+
capTimeoutMs;
|
|
4306
4331
|
/** Tracks capNames already logged as UDS-routed; one INFO line per capName per process. */
|
|
4307
4332
|
egressRoutedCaps = /* @__PURE__ */ new Set();
|
|
4308
4333
|
/** Active event fan-out mode, read once from `CAMSTACK_UDS_EVENT_FANOUT`. */
|
|
@@ -4332,6 +4357,7 @@ var LocalChildRegistry = class {
|
|
|
4332
4357
|
this.resolveChildIdForAddon = opts.resolveChildIdForAddon;
|
|
4333
4358
|
this.ownNodeId = opts.ownNodeId;
|
|
4334
4359
|
this.isAggregatedCollectionMethod = opts.isAggregatedCollectionMethod;
|
|
4360
|
+
this.capTimeoutMs = opts.capTimeoutMs;
|
|
4335
4361
|
} else {
|
|
4336
4362
|
this.server = serverOrOptions;
|
|
4337
4363
|
this.onUnownedCall = onUnownedCallArg;
|
|
@@ -4494,11 +4520,20 @@ var LocalChildRegistry = class {
|
|
|
4494
4520
|
if (deviceId !== void 0 && entry.caps.some((cap) => cap.capName === capName && cap.deviceId === deviceId)) return true;
|
|
4495
4521
|
return entry.caps.some((cap) => cap.capName === capName && cap.deviceId === void 0);
|
|
4496
4522
|
}
|
|
4523
|
+
/**
|
|
4524
|
+
* Per-request options for a cap call: the cap method's declared `timeoutMs`
|
|
4525
|
+
* when one exists, else `undefined` so the channel applies its default.
|
|
4526
|
+
* Mirrors `CapRouteResolver.callTimeout` for the remote plane.
|
|
4527
|
+
*/
|
|
4528
|
+
capRequestOpts(input) {
|
|
4529
|
+
const declared = this.capTimeoutMs?.(input.capName, input.method);
|
|
4530
|
+
return typeof declared === "number" ? { timeoutMs: declared } : void 0;
|
|
4531
|
+
}
|
|
4497
4532
|
/** Forward a cap method call to a SPECIFIC child by id over UDS; rejects if that child is absent. */
|
|
4498
4533
|
async callCapOnChild(childId, input) {
|
|
4499
4534
|
const entry = this.children.get(childId);
|
|
4500
4535
|
if (!entry) throw new Error(`no local child "${childId}" for cap "${input.capName}"`);
|
|
4501
|
-
return entry.channel.request(this.toCapCall(input));
|
|
4536
|
+
return entry.channel.request(this.toCapCall(input), this.capRequestOpts(input));
|
|
4502
4537
|
}
|
|
4503
4538
|
/** Forward a cap method call to the owning child over UDS; rejects if none. */
|
|
4504
4539
|
async callCap(input) {
|
|
@@ -4508,7 +4543,7 @@ var LocalChildRegistry = class {
|
|
|
4508
4543
|
const where = input.deviceId === void 0 ? "" : ` for device ${input.deviceId}`;
|
|
4509
4544
|
throw new Error(`no local child provides cap "${input.capName}"${where}`);
|
|
4510
4545
|
}
|
|
4511
|
-
return entry.channel.request(this.toCapCall(input));
|
|
4546
|
+
return entry.channel.request(this.toCapCall(input), this.capRequestOpts(input));
|
|
4512
4547
|
}
|
|
4513
4548
|
/**
|
|
4514
4549
|
* Forward an ADDON-LEVEL call (routes / custom-action) to a SPECIFIC child
|