dsh-foreman-node 0.1.0 → 0.1.1
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 +17 -3
- package/lib/index.d.ts +20 -4
- package/lib/index.js +32 -4
- package/package.json +1 -1
- package/src/index.ts +36 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
## 0.1.1 — 2026-11-07
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
### 修复:装上去就把整个 profile 弄成「异常」
|
|
4
|
+
|
|
5
|
+
设备包一进 profile 的 `bundles`,它的 patch 就会插入 `dsh-foreman-node` 这一行,
|
|
6
|
+
而这一行**还没有任何 config**——`0.1.0` 的 schema 把 `url` / `deviceId` / `token` 都写成 `.required()`,
|
|
7
|
+
于是激活直接失败:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
invalid config:
|
|
11
|
+
- $.url missing required value (at url)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
整个 profile 因此显示为异常,而不是「这个设备还没配好」。
|
|
15
|
+
|
|
16
|
+
现在这三项都有空字符串默认值,`apply` 在缺值时**干净地不激活**,只记一行说明缺哪几项、
|
|
17
|
+
去哪儿填,profile 保持健康。值填齐后才开始拨号。
|
|
18
|
+
只有空白的值也算缺——否则设备会一直拨一个空地址,看起来只是离线。
|
|
5
19
|
|
|
6
20
|
## 0.1.0 — 2026-11-07
|
|
7
21
|
|
package/lib/index.d.ts
CHANGED
|
@@ -186,11 +186,11 @@ interface NodeRoot {
|
|
|
186
186
|
}
|
|
187
187
|
/** What the `dsh-foreman-node` entry in a profile's patch configures. */
|
|
188
188
|
interface NodeConfig {
|
|
189
|
-
/** The hub's stable address; the device dials out to it. */
|
|
189
|
+
/** The hub's stable address; the device dials out to it. Blank until someone fills it in. */
|
|
190
190
|
url: string;
|
|
191
|
-
/** This device's id, as registered on the hub with `Devices.add`. */
|
|
191
|
+
/** This device's id, as registered on the hub with `Devices.add`. Blank until someone fills it in. */
|
|
192
192
|
deviceId: string;
|
|
193
|
-
/** This device's token; sent in the hello and nowhere else. */
|
|
193
|
+
/** This device's token; sent in the hello and nowhere else. Blank until someone fills it in. */
|
|
194
194
|
token: string;
|
|
195
195
|
/** The projects this device can work on. */
|
|
196
196
|
roots: NodeRoot[];
|
|
@@ -202,18 +202,34 @@ interface NodeConfig {
|
|
|
202
202
|
/**
|
|
203
203
|
* Configuration of one device, from the `dsh-foreman-node` entry in the profile's patch.
|
|
204
204
|
*
|
|
205
|
+
* Everything is optional on purpose. A bundle patch inserts this row the moment the package is added to a profile,
|
|
206
|
+
* so `apply` runs before anyone has filled the values in — and a schema that demanded them would turn the whole
|
|
207
|
+
* profile red with "invalid config" on a fresh install. `apply` treats a blank address as "not configured yet":
|
|
208
|
+
* it says what is missing and stops, instead of dialling nowhere or refusing to activate.
|
|
209
|
+
*
|
|
205
210
|
* The annotation names the schema type through the global `Schemastery` namespace rather than letting it be inferred:
|
|
206
211
|
* an inferred type here names a `cosmokit` symbol the emitted declaration cannot reference portably.
|
|
207
212
|
*/
|
|
208
213
|
declare const Config: Schemastery<NodeConfig>;
|
|
214
|
+
/**
|
|
215
|
+
* What this configuration still needs before the device can dial its hub, or undefined when it is complete.
|
|
216
|
+
*
|
|
217
|
+
* A missing value is reported by name so an operator can fill it in without reading the source; a value that is only
|
|
218
|
+
* whitespace counts as missing, because a device that dials a blank address retries forever and looks merely offline.
|
|
219
|
+
*/
|
|
220
|
+
declare function missingConfiguration(config: NodeConfig): string | undefined;
|
|
209
221
|
declare const name = "dsh-foreman-node";
|
|
210
222
|
/** DSH services a job needs; all are the host's own, and none is a foreman one. */
|
|
211
223
|
declare const inject: string[];
|
|
212
224
|
/**
|
|
213
225
|
* Start the device.
|
|
214
226
|
*
|
|
227
|
+
* A package that has just been installed has its row in the profile patch with no values in it yet. Activating
|
|
228
|
+
* cleanly and saying what is still missing keeps the whole profile healthy; throwing here is what made a fresh
|
|
229
|
+
* install report the profile as broken.
|
|
230
|
+
*
|
|
215
231
|
* The plugin returns once the connection is attempted, not once it succeeds: a device must boot and stay usable
|
|
216
232
|
* while its hub is unreachable, and the client keeps retrying in the background.
|
|
217
233
|
*/
|
|
218
234
|
declare function apply(ctx: Context, config: NodeConfig): Promise<void>;
|
|
219
|
-
export { Config, type Frame, type FrameAttachment, JobSessions, NodeClient, NodeConfig, NodeRoot, REPORT_STATES, type Root, VERSION, apply, foremanReportTool, inject, name };
|
|
235
|
+
export { Config, type Frame, type FrameAttachment, JobSessions, NodeClient, NodeConfig, NodeRoot, REPORT_STATES, type Root, VERSION, apply, foremanReportTool, inject, missingConfiguration, name };
|
package/lib/index.js
CHANGED
|
@@ -477,13 +477,18 @@ const VERSION = "0.1.0";
|
|
|
477
477
|
/**
|
|
478
478
|
* Configuration of one device, from the `dsh-foreman-node` entry in the profile's patch.
|
|
479
479
|
*
|
|
480
|
+
* Everything is optional on purpose. A bundle patch inserts this row the moment the package is added to a profile,
|
|
481
|
+
* so `apply` runs before anyone has filled the values in — and a schema that demanded them would turn the whole
|
|
482
|
+
* profile red with "invalid config" on a fresh install. `apply` treats a blank address as "not configured yet":
|
|
483
|
+
* it says what is missing and stops, instead of dialling nowhere or refusing to activate.
|
|
484
|
+
*
|
|
480
485
|
* The annotation names the schema type through the global `Schemastery` namespace rather than letting it be inferred:
|
|
481
486
|
* an inferred type here names a `cosmokit` symbol the emitted declaration cannot reference portably.
|
|
482
487
|
*/
|
|
483
488
|
const Config = s.object({
|
|
484
|
-
url: s.string().
|
|
485
|
-
deviceId: s.string().
|
|
486
|
-
token: s.string().
|
|
489
|
+
url: s.string().default("").description("The hub's address, such as wss://foreman.example.com. The device dials out to it."),
|
|
490
|
+
deviceId: s.string().default("").description("This device's id, as registered on the hub with `Devices.add`."),
|
|
491
|
+
token: s.string().default("").role("secret").description("This device's token; it is sent in the hello frame and nowhere else."),
|
|
487
492
|
roots: s.array(s.object({
|
|
488
493
|
project: s.string().required(),
|
|
489
494
|
path: s.string().required()
|
|
@@ -491,6 +496,20 @@ const Config = s.object({
|
|
|
491
496
|
heartbeatMs: s.natural().default(15e3).description("How often to tell the hub this device is alive, in milliseconds."),
|
|
492
497
|
reconnectMs: s.natural().default(1e3).description("Delay before the first reconnect attempt, in milliseconds.")
|
|
493
498
|
});
|
|
499
|
+
/**
|
|
500
|
+
* What this configuration still needs before the device can dial its hub, or undefined when it is complete.
|
|
501
|
+
*
|
|
502
|
+
* A missing value is reported by name so an operator can fill it in without reading the source; a value that is only
|
|
503
|
+
* whitespace counts as missing, because a device that dials a blank address retries forever and looks merely offline.
|
|
504
|
+
*/
|
|
505
|
+
function missingConfiguration(config) {
|
|
506
|
+
const missing = [];
|
|
507
|
+
if (config.url.trim() === "") missing.push("url");
|
|
508
|
+
if (config.deviceId.trim() === "") missing.push("deviceId");
|
|
509
|
+
if (config.token.trim() === "") missing.push("token");
|
|
510
|
+
if (missing.length === 0) return void 0;
|
|
511
|
+
return `dsh-foreman-node is not configured yet: ${missing.join(", ")} missing. Set them on the dsh-foreman-node row in the profile patch (the hub prints the token when you register the device).`;
|
|
512
|
+
}
|
|
494
513
|
const name = "dsh-foreman-node";
|
|
495
514
|
/** DSH services a job needs; all are the host's own, and none is a foreman one. */
|
|
496
515
|
const inject = [
|
|
@@ -504,10 +523,19 @@ const inject = [
|
|
|
504
523
|
/**
|
|
505
524
|
* Start the device.
|
|
506
525
|
*
|
|
526
|
+
* A package that has just been installed has its row in the profile patch with no values in it yet. Activating
|
|
527
|
+
* cleanly and saying what is still missing keeps the whole profile healthy; throwing here is what made a fresh
|
|
528
|
+
* install report the profile as broken.
|
|
529
|
+
*
|
|
507
530
|
* The plugin returns once the connection is attempted, not once it succeeds: a device must boot and stay usable
|
|
508
531
|
* while its hub is unreachable, and the client keeps retrying in the background.
|
|
509
532
|
*/
|
|
510
533
|
async function apply(ctx, config) {
|
|
534
|
+
const missing = missingConfiguration(config);
|
|
535
|
+
if (missing !== void 0) {
|
|
536
|
+
ctx.logger.info("%s", missing);
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
511
539
|
const jobs = new JobSessions(ctx);
|
|
512
540
|
const client = new NodeClient({
|
|
513
541
|
url: config.url,
|
|
@@ -570,4 +598,4 @@ async function receive(ctx, jobs, client, frame) {
|
|
|
570
598
|
});
|
|
571
599
|
}
|
|
572
600
|
}
|
|
573
|
-
export { Config, JobSessions, NodeClient, REPORT_STATES, VERSION, apply, foremanReportTool, inject, name };
|
|
601
|
+
export { Config, JobSessions, NodeClient, REPORT_STATES, VERSION, apply, foremanReportTool, inject, missingConfiguration, name };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-foreman-node",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Device side of dsh-foreman: connects out to a foreman hub, takes a job into a local DSH session, reports back. No ledger, no policy, no database.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
package/src/index.ts
CHANGED
|
@@ -24,11 +24,11 @@ export interface NodeRoot { project: string; path: string }
|
|
|
24
24
|
|
|
25
25
|
/** What the `dsh-foreman-node` entry in a profile's patch configures. */
|
|
26
26
|
export interface NodeConfig {
|
|
27
|
-
/** The hub's stable address; the device dials out to it. */
|
|
27
|
+
/** The hub's stable address; the device dials out to it. Blank until someone fills it in. */
|
|
28
28
|
url: string
|
|
29
|
-
/** This device's id, as registered on the hub with `Devices.add`. */
|
|
29
|
+
/** This device's id, as registered on the hub with `Devices.add`. Blank until someone fills it in. */
|
|
30
30
|
deviceId: string
|
|
31
|
-
/** This device's token; sent in the hello and nowhere else. */
|
|
31
|
+
/** This device's token; sent in the hello and nowhere else. Blank until someone fills it in. */
|
|
32
32
|
token: string
|
|
33
33
|
/** The projects this device can work on. */
|
|
34
34
|
roots: NodeRoot[]
|
|
@@ -41,19 +41,40 @@ export interface NodeConfig {
|
|
|
41
41
|
/**
|
|
42
42
|
* Configuration of one device, from the `dsh-foreman-node` entry in the profile's patch.
|
|
43
43
|
*
|
|
44
|
+
* Everything is optional on purpose. A bundle patch inserts this row the moment the package is added to a profile,
|
|
45
|
+
* so `apply` runs before anyone has filled the values in — and a schema that demanded them would turn the whole
|
|
46
|
+
* profile red with "invalid config" on a fresh install. `apply` treats a blank address as "not configured yet":
|
|
47
|
+
* it says what is missing and stops, instead of dialling nowhere or refusing to activate.
|
|
48
|
+
*
|
|
44
49
|
* The annotation names the schema type through the global `Schemastery` namespace rather than letting it be inferred:
|
|
45
50
|
* an inferred type here names a `cosmokit` symbol the emitted declaration cannot reference portably.
|
|
46
51
|
*/
|
|
47
52
|
export const Config: Schemastery<NodeConfig> = s.object({
|
|
48
|
-
url: s.string().
|
|
49
|
-
deviceId: s.string().
|
|
50
|
-
token: s.string().
|
|
53
|
+
url: s.string().default('').description('The hub\'s address, such as wss://foreman.example.com. The device dials out to it.'),
|
|
54
|
+
deviceId: s.string().default('').description('This device\'s id, as registered on the hub with `Devices.add`.'),
|
|
55
|
+
token: s.string().default('').role('secret').description('This device\'s token; it is sent in the hello frame and nowhere else.'),
|
|
51
56
|
roots: s.array(s.object({ project: s.string().required(), path: s.string().required() })).default([])
|
|
52
57
|
.description('Projects this device can work on: a logical name and this machine\'s own absolute path, kept verbatim.'),
|
|
53
58
|
heartbeatMs: s.natural().default(15_000).description('How often to tell the hub this device is alive, in milliseconds.'),
|
|
54
59
|
reconnectMs: s.natural().default(1_000).description('Delay before the first reconnect attempt, in milliseconds.'),
|
|
55
60
|
})
|
|
56
61
|
|
|
62
|
+
/**
|
|
63
|
+
* What this configuration still needs before the device can dial its hub, or undefined when it is complete.
|
|
64
|
+
*
|
|
65
|
+
* A missing value is reported by name so an operator can fill it in without reading the source; a value that is only
|
|
66
|
+
* whitespace counts as missing, because a device that dials a blank address retries forever and looks merely offline.
|
|
67
|
+
*/
|
|
68
|
+
export function missingConfiguration(config: NodeConfig): string | undefined {
|
|
69
|
+
const missing: string[] = []
|
|
70
|
+
if (config.url.trim() === '') missing.push('url')
|
|
71
|
+
if (config.deviceId.trim() === '') missing.push('deviceId')
|
|
72
|
+
if (config.token.trim() === '') missing.push('token')
|
|
73
|
+
if (missing.length === 0) return undefined
|
|
74
|
+
return `dsh-foreman-node is not configured yet: ${missing.join(', ')} ` +
|
|
75
|
+
`missing. Set them on the dsh-foreman-node row in the profile patch (the hub prints the token when you register the device).`
|
|
76
|
+
}
|
|
77
|
+
|
|
57
78
|
export const name = 'dsh-foreman-node'
|
|
58
79
|
/** DSH services a job needs; all are the host's own, and none is a foreman one. */
|
|
59
80
|
export const inject = ['sessionController', 'sessionPersistence', 'workspaceRegistry', 'permissionPresets', 'tools', 'agents']
|
|
@@ -61,10 +82,19 @@ export const inject = ['sessionController', 'sessionPersistence', 'workspaceRegi
|
|
|
61
82
|
/**
|
|
62
83
|
* Start the device.
|
|
63
84
|
*
|
|
85
|
+
* A package that has just been installed has its row in the profile patch with no values in it yet. Activating
|
|
86
|
+
* cleanly and saying what is still missing keeps the whole profile healthy; throwing here is what made a fresh
|
|
87
|
+
* install report the profile as broken.
|
|
88
|
+
*
|
|
64
89
|
* The plugin returns once the connection is attempted, not once it succeeds: a device must boot and stay usable
|
|
65
90
|
* while its hub is unreachable, and the client keeps retrying in the background.
|
|
66
91
|
*/
|
|
67
92
|
export async function apply(ctx: Context, config: NodeConfig): Promise<void> {
|
|
93
|
+
const missing = missingConfiguration(config)
|
|
94
|
+
if (missing !== undefined) {
|
|
95
|
+
ctx.logger.info('%s', missing)
|
|
96
|
+
return
|
|
97
|
+
}
|
|
68
98
|
const jobs = new JobSessions(ctx)
|
|
69
99
|
const client = new NodeClient({
|
|
70
100
|
url: config.url,
|