@camstack/server 1.2.708 → 1.2.710
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 +13 -0
- package/dist/agent/agent-shutdown.js +67 -2
- package/dist/agent/agent-update-service.js +2 -1
- package/dist/agent/main.js +1 -0
- package/dist/api/core/site-location.js +9 -2
- package/dist/api/trpc/generated-cap-mounts.js +2 -1
- package/dist/api/trpc/generated-cap-routers.js +636 -540
- package/package.json +10 -10
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.2.710] - 2026-10-31
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- The schedulers capability is served over the API (list, create, update, delete, enable, preview, conflicts, status, traces)
|
|
15
|
+
|
|
16
|
+
## [1.2.709] - 2026-10-31
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- A wedged agent shutdown now ends with a self-SIGKILL so exit-time teardown cannot keep the process alive after an update.
|
|
21
|
+
- getSiteLocation also returns the site's IANA time zone
|
|
22
|
+
|
|
10
23
|
## [1.2.708] - 2026-10-31
|
|
11
24
|
|
|
12
25
|
### Changed
|
|
@@ -1,9 +1,74 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.SHUTDOWN_DEADLINE_MS = exports.SHUTDOWN_STEP_TIMEOUT_MS = void 0;
|
|
37
|
+
exports.killSelfHard = killSelfHard;
|
|
4
38
|
exports.createAgentShutdown = createAgentShutdown;
|
|
39
|
+
/**
|
|
40
|
+
* The agent's graceful shutdown, as a testable sequence.
|
|
41
|
+
*
|
|
42
|
+
* A SIGTERM once started a shutdown that awaited one addon's `shutdown()` for 9.5 minutes: the node
|
|
43
|
+
* stayed alive but deregistered, and its desktop shell never noticed. So the pass is re-entry guarded
|
|
44
|
+
* (a second signal forces the exit instead of starting a second pass), every step is bounded, the
|
|
45
|
+
* whole pass has a hard deadline that ends in a non-zero `exit`, and every step logs its elapsed time
|
|
46
|
+
* next to the reason the shutdown started.
|
|
47
|
+
*/
|
|
48
|
+
const fs = __importStar(require("node:fs"));
|
|
5
49
|
exports.SHUTDOWN_STEP_TIMEOUT_MS = 5_000;
|
|
6
50
|
exports.SHUTDOWN_DEADLINE_MS = 15_000;
|
|
51
|
+
/**
|
|
52
|
+
* End this process WITHOUT running exit-time teardown. `process.exit` joins the threadpool and tears
|
|
53
|
+
* down workers and native addons, and on a node that is already wedged that join can itself hang
|
|
54
|
+
* (observed: a desktop agent alive for minutes after its shutdown deadline). A SIGKILL to ourselves
|
|
55
|
+
* cannot be waited on. The last line is written synchronously first, because a buffered log write
|
|
56
|
+
* would be lost.
|
|
57
|
+
*/
|
|
58
|
+
function killSelfHard(finalLine, deps = {}) {
|
|
59
|
+
const write = deps.write ??
|
|
60
|
+
((line) => {
|
|
61
|
+
fs.writeSync(2, `${line}\n`);
|
|
62
|
+
});
|
|
63
|
+
try {
|
|
64
|
+
write(finalLine);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
/* a closed stderr must not stop the kill */
|
|
68
|
+
}
|
|
69
|
+
const kill = deps.kill ?? ((pid, signal) => void process.kill(pid, signal));
|
|
70
|
+
kill(process.pid, 'SIGKILL');
|
|
71
|
+
}
|
|
7
72
|
function errMessage(err) {
|
|
8
73
|
return err instanceof Error ? err.message : String(err);
|
|
9
74
|
}
|
|
@@ -28,7 +93,7 @@ function createAgentShutdown(deps) {
|
|
|
28
93
|
return async (reason) => {
|
|
29
94
|
if (startedAt !== null) {
|
|
30
95
|
logger.error(`shutdown already in progress for ${Date.now() - startedAt}ms; ${reason} forces exit`);
|
|
31
|
-
deps.
|
|
96
|
+
deps.hardExit(1, `shutdown already in progress; ${reason} forces exit`);
|
|
32
97
|
return;
|
|
33
98
|
}
|
|
34
99
|
const began = Date.now();
|
|
@@ -36,7 +101,7 @@ function createAgentShutdown(deps) {
|
|
|
36
101
|
logger.info(`shutdown started by ${reason}`);
|
|
37
102
|
const deadline = setTimeout(() => {
|
|
38
103
|
logger.error(`shutdown deadline of ${deadlineMs}ms exceeded (started by ${reason}); exiting 1`);
|
|
39
|
-
deps.
|
|
104
|
+
deps.hardExit(1, `shutdown deadline of ${deadlineMs}ms exceeded (started by ${reason}); SIGKILL`);
|
|
40
105
|
}, deadlineMs);
|
|
41
106
|
for (const step of deps.steps) {
|
|
42
107
|
const stepStart = Date.now();
|
|
@@ -73,6 +73,7 @@ const update_availability_store_js_1 = require("../core/update-availability-stor
|
|
|
73
73
|
const index_js_1 = require("../server-root/index.js");
|
|
74
74
|
const node_update_announcement_js_1 = require("../core/updates/node-update-announcement.js");
|
|
75
75
|
const restart_marker_js_1 = require("../core/updates/restart-marker.js");
|
|
76
|
+
const agent_shutdown_js_1 = require("./agent-shutdown.js");
|
|
76
77
|
/**
|
|
77
78
|
* The synthetic addonId the agent's OWN runtime registers infra providers
|
|
78
79
|
* under (no addon owns them — the bootstrap does). Appears in the agent's
|
|
@@ -145,7 +146,7 @@ function scheduleAgentRestart(logger, requestedBy, dataDir) {
|
|
|
145
146
|
});
|
|
146
147
|
const grace = setTimeout(() => {
|
|
147
148
|
process.kill(process.pid, 'SIGTERM');
|
|
148
|
-
const hard = setTimeout(() =>
|
|
149
|
+
const hard = setTimeout(() => (0, agent_shutdown_js_1.killSelfHard)('agent restart: shutdown did not finish in 15000ms; SIGKILL'), 15_000);
|
|
149
150
|
hard.unref();
|
|
150
151
|
}, exports.AGENT_RESTART_GRACE_MS);
|
|
151
152
|
grace.unref();
|
package/dist/agent/main.js
CHANGED
|
@@ -1076,6 +1076,7 @@ async function startAgent(configPath) {
|
|
|
1076
1076
|
const shutdown = (0, agent_shutdown_js_1.createAgentShutdown)({
|
|
1077
1077
|
logger: consoleLogger,
|
|
1078
1078
|
exit: (code) => process.exit(code),
|
|
1079
|
+
hardExit: (_code, finalLine) => (0, agent_shutdown_js_1.killSelfHard)(finalLine),
|
|
1079
1080
|
steps: [
|
|
1080
1081
|
{
|
|
1081
1082
|
name: 'abort-registration',
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
49
49
|
exports.SiteLocationService = exports.GEO_IP_ENDPOINTS = exports.GEO_IP_TIMEOUT_MS = exports.SITE_LOCATION_KEY = void 0;
|
|
50
50
|
exports.isUsableCoordinate = isUsableCoordinate;
|
|
51
|
+
exports.hubTimeZone = hubTimeZone;
|
|
51
52
|
exports.toStatus = toStatus;
|
|
52
53
|
exports.parseGeoIpPayload = parseGeoIpPayload;
|
|
53
54
|
exports.geolocatePublicIp = geolocatePublicIp;
|
|
@@ -98,10 +99,15 @@ function isUsableCoordinate(lat, lng) {
|
|
|
98
99
|
return false;
|
|
99
100
|
return lat !== 0 || lng !== 0;
|
|
100
101
|
}
|
|
102
|
+
/** The hub's own IANA zone, `UTC` when the runtime cannot name one. */
|
|
103
|
+
function hubTimeZone() {
|
|
104
|
+
const zone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
105
|
+
return typeof zone === 'string' && zone.length > 0 ? zone : 'UTC';
|
|
106
|
+
}
|
|
101
107
|
/** Project a stored row onto the wire shape. */
|
|
102
|
-
function toStatus(record) {
|
|
108
|
+
function toStatus(record, timeZone = hubTimeZone()) {
|
|
103
109
|
if (record === null) {
|
|
104
|
-
return { location: null, derivationAttemptedAt: null, derivationError: null };
|
|
110
|
+
return { location: null, timeZone, derivationAttemptedAt: null, derivationError: null };
|
|
105
111
|
}
|
|
106
112
|
const attempted = typeof record.derivationAttemptedAt === 'number';
|
|
107
113
|
const status = {
|
|
@@ -117,6 +123,7 @@ function toStatus(record) {
|
|
|
117
123
|
...(record.label !== undefined ? { label: record.label } : {}),
|
|
118
124
|
}
|
|
119
125
|
: null,
|
|
126
|
+
timeZone,
|
|
120
127
|
derivationAttemptedAt: attempted ? Number(record.derivationAttemptedAt) : null,
|
|
121
128
|
derivationError: typeof record.derivationError === 'string' ? record.derivationError : null,
|
|
122
129
|
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// AUTO-GENERATED by scripts/generate-cap-mounts.ts — DO NOT EDIT
|
|
3
3
|
// Re-run: npx tsx scripts/generate-cap-mounts.ts
|
|
4
4
|
//
|
|
5
|
-
// Mounted:
|
|
5
|
+
// Mounted: 191 Skipped (legacy): 3
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.LEGACY_SHAPE_SKIP = void 0;
|
|
8
8
|
exports.mountAllCaps = mountAllCaps;
|
|
@@ -549,6 +549,7 @@ function mountAllCaps(services) {
|
|
|
549
549
|
}, remoteCapProxy),
|
|
550
550
|
runningState: (0, generated_cap_routers_js_1.createCapRouter_runningState)((_ctx) => (0, cap_mount_helpers_js_1.requireDeviceScoped)(reg, 'running-state'), remoteCapProxy),
|
|
551
551
|
sceneMonitor: (0, generated_cap_routers_js_1.createCapRouter_sceneMonitor)((_ctx) => (0, cap_mount_helpers_js_1.requireDeviceScoped)(reg, 'scene-monitor'), remoteCapProxy),
|
|
552
|
+
schedulers: (0, generated_cap_routers_js_1.createCapRouter_schedulers)((_ctx) => reg?.getSingleton('schedulers') ?? null, remoteCapProxy),
|
|
552
553
|
scriptRunner: (0, generated_cap_routers_js_1.createCapRouter_scriptRunner)((_ctx) => (0, cap_mount_helpers_js_1.requireDeviceScoped)(reg, 'script-runner'), remoteCapProxy),
|
|
553
554
|
serverManagement: (0, generated_cap_routers_js_1.createCapRouter_serverManagement)((_ctx) => reg?.getSingleton('server-management') ??
|
|
554
555
|
null, remoteCapProxy),
|