@camstack/addon-terminal 0.1.28 → 0.1.30
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.js +229 -23
- package/dist/addon.mjs +229 -23
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -31,7 +31,7 @@ let node_module = require("node:module");
|
|
|
31
31
|
let sharp = require("sharp");
|
|
32
32
|
sharp = __toESM(sharp);
|
|
33
33
|
let node_http = require("node:http");
|
|
34
|
-
//#region ../types/dist/event-category-
|
|
34
|
+
//#region ../types/dist/event-category-CIa_iT6b.mjs
|
|
35
35
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
36
36
|
EventCategory["SystemBoot"] = "system.boot";
|
|
37
37
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -47,6 +47,15 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
47
47
|
*/
|
|
48
48
|
EventCategory["SystemRestartCompleted"] = "system.restart-completed";
|
|
49
49
|
/**
|
|
50
|
+
* The hub reissued its own TLS certificate at boot (`ensureTlsCert`).
|
|
51
|
+
* Emitted only when the material on disk actually changed, so an
|
|
52
|
+
* operator who trusted the old certificate by hand is told rather than
|
|
53
|
+
* discovering it as a browser error. Payload `TlsCertChangedPayload`.
|
|
54
|
+
*
|
|
55
|
+
* Rule: docs/decisions/adr-0227-*.md
|
|
56
|
+
*/
|
|
57
|
+
EventCategory["SystemTlsCertChanged"] = "system.tls-cert-changed";
|
|
58
|
+
/**
|
|
50
59
|
* A newer addon or server-root package version was found by the
|
|
51
60
|
* authoritative registry check. Emitted once when any observed
|
|
52
61
|
* `latestVersion` changes (or a package/node first appears behind);
|
|
@@ -20023,7 +20032,12 @@ var TerminalSessionInfoSchema = object({
|
|
|
20023
20032
|
var TerminalProfileInfoSchema = object({
|
|
20024
20033
|
profileId: string(),
|
|
20025
20034
|
label: string(),
|
|
20026
|
-
description: string().optional()
|
|
20035
|
+
description: string().optional(),
|
|
20036
|
+
/** Spawn defaults the instance form copies on create. */
|
|
20037
|
+
executable: string().optional(),
|
|
20038
|
+
args: array(string()).readonly().optional(),
|
|
20039
|
+
cwd: string().optional(),
|
|
20040
|
+
environment: array(string()).readonly().optional()
|
|
20027
20041
|
});
|
|
20028
20042
|
/**
|
|
20029
20043
|
* A durable operator-created Terminal instance. Profiles are templates; only
|
|
@@ -20036,7 +20050,11 @@ var TerminalInstanceInfoSchema = object({
|
|
|
20036
20050
|
profileId: string(),
|
|
20037
20051
|
profileLabel: string(),
|
|
20038
20052
|
name: string(),
|
|
20039
|
-
enabled: boolean()
|
|
20053
|
+
enabled: boolean(),
|
|
20054
|
+
executable: string(),
|
|
20055
|
+
args: array(string()).readonly(),
|
|
20056
|
+
cwd: string(),
|
|
20057
|
+
environment: array(string()).readonly()
|
|
20040
20058
|
});
|
|
20041
20059
|
var TerminalLegacyCameraSchema = object({
|
|
20042
20060
|
stableId: string(),
|
|
@@ -20082,7 +20100,22 @@ var terminalSessionCapability = {
|
|
|
20082
20100
|
createInstance: method(object({
|
|
20083
20101
|
targetNodeId: string().min(1),
|
|
20084
20102
|
profileId: string().min(1),
|
|
20085
|
-
name: string().trim().min(1).max(160).optional()
|
|
20103
|
+
name: string().trim().min(1).max(160).optional(),
|
|
20104
|
+
executable: string().max(1024).optional(),
|
|
20105
|
+
args: array(string().max(2048)).max(64).optional(),
|
|
20106
|
+
cwd: string().max(1024).optional(),
|
|
20107
|
+
environment: array(string().max(4096)).max(64).optional()
|
|
20108
|
+
}), TerminalInstanceInfoSchema, {
|
|
20109
|
+
kind: "mutation",
|
|
20110
|
+
auth: "admin"
|
|
20111
|
+
}),
|
|
20112
|
+
updateInstance: method(object({
|
|
20113
|
+
instanceId: string().min(1),
|
|
20114
|
+
name: string().trim().min(1).max(160).optional(),
|
|
20115
|
+
executable: string().max(1024).optional(),
|
|
20116
|
+
args: array(string().max(2048)).max(64).optional(),
|
|
20117
|
+
cwd: string().max(1024).optional(),
|
|
20118
|
+
environment: array(string().max(4096)).max(64).optional()
|
|
20086
20119
|
}), TerminalInstanceInfoSchema, {
|
|
20087
20120
|
kind: "mutation",
|
|
20088
20121
|
auth: "admin"
|
|
@@ -20117,7 +20150,11 @@ var terminalSessionCapability = {
|
|
|
20117
20150
|
openSession: method(object({
|
|
20118
20151
|
profileId: string(),
|
|
20119
20152
|
cols: number().int().positive(),
|
|
20120
|
-
rows: number().int().positive()
|
|
20153
|
+
rows: number().int().positive(),
|
|
20154
|
+
executable: string().max(1024).optional(),
|
|
20155
|
+
args: array(string().max(2048)).max(64).optional(),
|
|
20156
|
+
cwd: string().max(1024).optional(),
|
|
20157
|
+
environment: array(string().max(4096)).max(64).optional()
|
|
20121
20158
|
}), TerminalSessionInfoSchema, {
|
|
20122
20159
|
kind: "mutation",
|
|
20123
20160
|
auth: "admin"
|
|
@@ -24172,6 +24209,20 @@ var AllowedAddressesSchema = object({
|
|
|
24172
24209
|
* Network Addresses admin page and persisted by the addon.
|
|
24173
24210
|
*/
|
|
24174
24211
|
addresses: array(string()).readonly() });
|
|
24212
|
+
var TlsStatusSchema = object({
|
|
24213
|
+
mode: _enum([
|
|
24214
|
+
"generated",
|
|
24215
|
+
"uploaded",
|
|
24216
|
+
"disabled"
|
|
24217
|
+
]),
|
|
24218
|
+
leafFingerprintSha256: string().nullable(),
|
|
24219
|
+
caFingerprintSha256: string().nullable(),
|
|
24220
|
+
validTo: string().nullable(),
|
|
24221
|
+
sans: array(string()),
|
|
24222
|
+
caCertPem: string().nullable(),
|
|
24223
|
+
reissueError: string().nullable(),
|
|
24224
|
+
restartRequired: boolean()
|
|
24225
|
+
});
|
|
24175
24226
|
method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
|
|
24176
24227
|
/**
|
|
24177
24228
|
* LEGACY HINT — do not send from new code. Kept optional so clients
|
|
@@ -24191,7 +24242,20 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
|
|
|
24191
24242
|
* to avoid mixed-content blocks in the browser. The public
|
|
24192
24243
|
* tunnel always emits `https://` regardless. */
|
|
24193
24244
|
scheme: _enum(["http", "https"]).optional()
|
|
24194
|
-
}), GetConnectionEndpointsResultSchema), method(_void(), NotificationEndpointSchema), method(object({ baseUrl: string().nullable() }), NotificationEndpointSchema, { kind: "mutation" }), method(_void(), AllowedAddressesSchema), method(AllowedAddressesSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), AllowedAddressesSchema, { kind: "mutation" })
|
|
24245
|
+
}), GetConnectionEndpointsResultSchema), method(_void(), NotificationEndpointSchema), method(object({ baseUrl: string().nullable() }), NotificationEndpointSchema, { kind: "mutation" }), method(_void(), AllowedAddressesSchema), method(AllowedAddressesSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), AllowedAddressesSchema, { kind: "mutation" }), method(_void(), TlsStatusSchema), method(object({ reason: string().optional() }), TlsStatusSchema, {
|
|
24246
|
+
kind: "mutation",
|
|
24247
|
+
auth: "admin"
|
|
24248
|
+
}), method(object({
|
|
24249
|
+
certPem: string().min(1),
|
|
24250
|
+
keyPem: string().min(1),
|
|
24251
|
+
caPem: string().optional()
|
|
24252
|
+
}), TlsStatusSchema, {
|
|
24253
|
+
kind: "mutation",
|
|
24254
|
+
auth: "admin"
|
|
24255
|
+
}), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
|
|
24256
|
+
kind: "mutation",
|
|
24257
|
+
auth: "admin"
|
|
24258
|
+
});
|
|
24195
24259
|
var LockControlStatusSchema = object({
|
|
24196
24260
|
/** Lifecycle state of the lock. `jammed` means the motor reported
|
|
24197
24261
|
* failure to reach the target — operator intervention required. */
|
|
@@ -32216,6 +32280,12 @@ Object.freeze({
|
|
|
32216
32280
|
addonId: null,
|
|
32217
32281
|
access: "create"
|
|
32218
32282
|
},
|
|
32283
|
+
"localNetwork.downloadCa": {
|
|
32284
|
+
capName: "local-network",
|
|
32285
|
+
capScope: "system",
|
|
32286
|
+
addonId: null,
|
|
32287
|
+
access: "view"
|
|
32288
|
+
},
|
|
32219
32289
|
"localNetwork.getAllowedAddresses": {
|
|
32220
32290
|
capName: "local-network",
|
|
32221
32291
|
capScope: "system",
|
|
@@ -32240,18 +32310,36 @@ Object.freeze({
|
|
|
32240
32310
|
addonId: null,
|
|
32241
32311
|
access: "view"
|
|
32242
32312
|
},
|
|
32313
|
+
"localNetwork.getTlsStatus": {
|
|
32314
|
+
capName: "local-network",
|
|
32315
|
+
capScope: "system",
|
|
32316
|
+
addonId: null,
|
|
32317
|
+
access: "view"
|
|
32318
|
+
},
|
|
32243
32319
|
"localNetwork.list": {
|
|
32244
32320
|
capName: "local-network",
|
|
32245
32321
|
capScope: "system",
|
|
32246
32322
|
addonId: null,
|
|
32247
32323
|
access: "view"
|
|
32248
32324
|
},
|
|
32325
|
+
"localNetwork.regenerateCertificate": {
|
|
32326
|
+
capName: "local-network",
|
|
32327
|
+
capScope: "system",
|
|
32328
|
+
addonId: null,
|
|
32329
|
+
access: "create"
|
|
32330
|
+
},
|
|
32249
32331
|
"localNetwork.resetAllowlistToBestMatch": {
|
|
32250
32332
|
capName: "local-network",
|
|
32251
32333
|
capScope: "system",
|
|
32252
32334
|
addonId: null,
|
|
32253
32335
|
access: "delete"
|
|
32254
32336
|
},
|
|
32337
|
+
"localNetwork.revertToGeneratedCertificate": {
|
|
32338
|
+
capName: "local-network",
|
|
32339
|
+
capScope: "system",
|
|
32340
|
+
addonId: null,
|
|
32341
|
+
access: "create"
|
|
32342
|
+
},
|
|
32255
32343
|
"localNetwork.setAllowedAddresses": {
|
|
32256
32344
|
capName: "local-network",
|
|
32257
32345
|
capScope: "system",
|
|
@@ -32264,6 +32352,12 @@ Object.freeze({
|
|
|
32264
32352
|
addonId: null,
|
|
32265
32353
|
access: "create"
|
|
32266
32354
|
},
|
|
32355
|
+
"localNetwork.uploadCertificate": {
|
|
32356
|
+
capName: "local-network",
|
|
32357
|
+
capScope: "system",
|
|
32358
|
+
addonId: null,
|
|
32359
|
+
access: "create"
|
|
32360
|
+
},
|
|
32267
32361
|
"lockControl.lock": {
|
|
32268
32362
|
capName: "lock-control",
|
|
32269
32363
|
capScope: "device",
|
|
@@ -35144,6 +35238,12 @@ Object.freeze({
|
|
|
35144
35238
|
addonId: null,
|
|
35145
35239
|
access: "create"
|
|
35146
35240
|
},
|
|
35241
|
+
"terminalSession.updateInstance": {
|
|
35242
|
+
capName: "terminal-session",
|
|
35243
|
+
capScope: "system",
|
|
35244
|
+
addonId: null,
|
|
35245
|
+
access: "create"
|
|
35246
|
+
},
|
|
35147
35247
|
"terminalSession.writeInput": {
|
|
35148
35248
|
capName: "terminal-session",
|
|
35149
35249
|
capScope: "system",
|
|
@@ -37631,6 +37731,35 @@ Object.freeze(Object.fromEntries([{
|
|
|
37631
37731
|
}]
|
|
37632
37732
|
}].map((s) => [s.stepId, s.defaultModelId])));
|
|
37633
37733
|
string().min(1);
|
|
37734
|
+
var CLUSTER_STEP_SETTING_FIELDS = [{
|
|
37735
|
+
stepId: "face-embedding",
|
|
37736
|
+
key: "minLandmarkFaceSize",
|
|
37737
|
+
label: "Min face size for recognition (detection px)",
|
|
37738
|
+
description: "Refuse to embed a face smaller than this IN THE DETECTION FRAME. Applies to every node — the enrolled gallery is one index, and two admission floors would fill it from two policies. 0 disables the gate.",
|
|
37739
|
+
type: "slider",
|
|
37740
|
+
min: 0,
|
|
37741
|
+
max: 64,
|
|
37742
|
+
step: 2,
|
|
37743
|
+
default: 24
|
|
37744
|
+
}];
|
|
37745
|
+
function clusterStepSettingKey(stepId, fieldKey) {
|
|
37746
|
+
return `clusterStepSetting:${stepId}:${fieldKey}`;
|
|
37747
|
+
}
|
|
37748
|
+
var ClusterSettingNumberSchema = number().finite();
|
|
37749
|
+
function readClusterStepSettings(config) {
|
|
37750
|
+
const out = {};
|
|
37751
|
+
for (const field of CLUSTER_STEP_SETTING_FIELDS) {
|
|
37752
|
+
const parsed = ClusterSettingNumberSchema.safeParse(config[clusterStepSettingKey(field.stepId, field.key)]);
|
|
37753
|
+
const value = parsed.success ? parsed.data : field.default;
|
|
37754
|
+
const existing = out[field.stepId] ?? {};
|
|
37755
|
+
out[field.stepId] = {
|
|
37756
|
+
...existing,
|
|
37757
|
+
[field.key]: value
|
|
37758
|
+
};
|
|
37759
|
+
}
|
|
37760
|
+
return out;
|
|
37761
|
+
}
|
|
37762
|
+
readClusterStepSettings({});
|
|
37634
37763
|
object({
|
|
37635
37764
|
/**
|
|
37636
37765
|
* Fraction of the box's own size added on EACH side before cutting.
|
|
@@ -43338,7 +43467,8 @@ var TerminalCameraRelay = class {
|
|
|
43338
43467
|
const opening = this.api.openSession(state.nodeId, {
|
|
43339
43468
|
profileId: state.profileId,
|
|
43340
43469
|
cols: 120,
|
|
43341
|
-
rows: 40
|
|
43470
|
+
rows: 40,
|
|
43471
|
+
instanceId: state.instanceId
|
|
43342
43472
|
});
|
|
43343
43473
|
state.openPromise = opening;
|
|
43344
43474
|
try {
|
|
@@ -43725,8 +43855,25 @@ var TerminalInstanceSchema = object({
|
|
|
43725
43855
|
profileId: string().min(1).max(64),
|
|
43726
43856
|
profileLabel: string().min(1).max(120),
|
|
43727
43857
|
name: string().min(1).max(160),
|
|
43728
|
-
enabled: boolean()
|
|
43858
|
+
enabled: boolean(),
|
|
43859
|
+
executable: string().max(1024).default(""),
|
|
43860
|
+
args: array(string().max(2048)).max(64).default([]),
|
|
43861
|
+
cwd: string().max(1024).default(""),
|
|
43862
|
+
environment: array(string().max(4096)).max(64).default([])
|
|
43729
43863
|
});
|
|
43864
|
+
function environmentEntriesToRecord(entries) {
|
|
43865
|
+
const out = {};
|
|
43866
|
+
for (const entry of entries) {
|
|
43867
|
+
const eq = entry.indexOf("=");
|
|
43868
|
+
if (eq <= 0) continue;
|
|
43869
|
+
out[entry.slice(0, eq)] = entry.slice(eq + 1);
|
|
43870
|
+
}
|
|
43871
|
+
return out;
|
|
43872
|
+
}
|
|
43873
|
+
function environmentRecordToEntries(env) {
|
|
43874
|
+
if (env === void 0) return [];
|
|
43875
|
+
return Object.entries(env).map(([name, value]) => `${name}=${value}`);
|
|
43876
|
+
}
|
|
43730
43877
|
/**
|
|
43731
43878
|
* Config is operator-writable, so malformed or duplicate rows are ignored
|
|
43732
43879
|
* rather than allowed to make declaration reconciliation destructive.
|
|
@@ -43990,7 +44137,11 @@ var TerminalSessionManager = class {
|
|
|
43990
44137
|
return this.profiles.map((p) => ({
|
|
43991
44138
|
profileId: p.profileId,
|
|
43992
44139
|
label: p.label,
|
|
43993
|
-
...p.description !== void 0 ? { description: p.description } : {}
|
|
44140
|
+
...p.description !== void 0 ? { description: p.description } : {},
|
|
44141
|
+
executable: p.file,
|
|
44142
|
+
args: [...p.args],
|
|
44143
|
+
cwd: p.cwd ?? "",
|
|
44144
|
+
environment: environmentRecordToEntries(p.env)
|
|
43994
44145
|
}));
|
|
43995
44146
|
}
|
|
43996
44147
|
setInstanceControl(control) {
|
|
@@ -44002,6 +44153,9 @@ var TerminalSessionManager = class {
|
|
|
44002
44153
|
async createInstance(input) {
|
|
44003
44154
|
return this.requireInstanceControl().createInstance(input);
|
|
44004
44155
|
}
|
|
44156
|
+
async updateInstance(input) {
|
|
44157
|
+
return this.requireInstanceControl().updateInstance(input);
|
|
44158
|
+
}
|
|
44005
44159
|
async deleteInstance(input) {
|
|
44006
44160
|
await this.requireInstanceControl().deleteInstance(input);
|
|
44007
44161
|
}
|
|
@@ -44024,24 +44178,29 @@ var TerminalSessionManager = class {
|
|
|
44024
44178
|
const cols = clampDimension(input.cols, MAX_COLS);
|
|
44025
44179
|
const rows = clampDimension(input.rows, MAX_ROWS);
|
|
44026
44180
|
const sessionId = this.mintId();
|
|
44181
|
+
const file = input.executable?.trim() || profile.file;
|
|
44182
|
+
const args = input.args !== void 0 ? [...input.args] : [...profile.args];
|
|
44183
|
+
const cwd = input.cwd?.trim() || profile.cwd || this.opts.cwd;
|
|
44184
|
+
const env = {
|
|
44185
|
+
...this.opts.env,
|
|
44186
|
+
...profile.env,
|
|
44187
|
+
...environmentEntriesToRecord(input.environment ?? [])
|
|
44188
|
+
};
|
|
44027
44189
|
const executable = [{
|
|
44028
|
-
file
|
|
44029
|
-
args
|
|
44030
|
-
}, ...profile.fallbacks ?? []].map((candidate) => ({
|
|
44190
|
+
file,
|
|
44191
|
+
args
|
|
44192
|
+
}, ...input.executable?.trim() ? [] : profile.fallbacks ?? []].map((candidate) => ({
|
|
44031
44193
|
...candidate,
|
|
44032
44194
|
resolvedFile: this.resolveBinary(candidate.file)
|
|
44033
44195
|
})).find((candidate) => candidate.resolvedFile !== null);
|
|
44034
|
-
if (executable === void 0) throw new Error(`Terminal profile '${profile.profileId}' cannot start: executable '${
|
|
44196
|
+
if (executable === void 0) throw new Error(`Terminal profile '${profile.profileId}' cannot start: executable '${file}' was not found on PATH. Install it in the server image or set its path in Terminal settings.`);
|
|
44035
44197
|
const pty = this.opts.spawn({
|
|
44036
44198
|
file: executable.resolvedFile,
|
|
44037
44199
|
args: executable.args,
|
|
44038
44200
|
cols,
|
|
44039
44201
|
rows,
|
|
44040
|
-
...
|
|
44041
|
-
...
|
|
44042
|
-
...this.opts.env,
|
|
44043
|
-
...profile.env
|
|
44044
|
-
} } : {}
|
|
44202
|
+
...cwd !== void 0 && cwd !== "" ? { cwd } : {},
|
|
44203
|
+
...Object.keys(env).length > 0 ? { env } : {}
|
|
44045
44204
|
});
|
|
44046
44205
|
const screen = this.opts.screenFactory(cols, rows);
|
|
44047
44206
|
const info = {
|
|
@@ -44139,7 +44298,7 @@ var TerminalSessionManager = class {
|
|
|
44139
44298
|
const oldestSeq = session.output[0]?.seq ?? session.nextSeq;
|
|
44140
44299
|
if (input.afterSeq === 0 || input.afterSeq < oldestSeq - 1 || input.afterSeq > cursor) {
|
|
44141
44300
|
await session.screen.flush();
|
|
44142
|
-
const exit =
|
|
44301
|
+
const exit = session.output.toReversed().find((event) => event.kind === "exit");
|
|
44143
44302
|
return {
|
|
44144
44303
|
cursor,
|
|
44145
44304
|
reset: true,
|
|
@@ -44383,7 +44542,21 @@ var TerminalAddon = class extends BaseAddon {
|
|
|
44383
44542
|
const localNodeId = declarationOwnerNodeId(this.ctx.kernel.localNodeId);
|
|
44384
44543
|
const cameraRelay = new TerminalCameraRelay({
|
|
44385
44544
|
listProfiles: (nodeId) => nodeId === localNodeId ? manager.listProfiles() : this.ctx.api.terminalSession.listProfiles.query(void 0, nodePin(nodeId)),
|
|
44386
|
-
openSession: (nodeId, input) =>
|
|
44545
|
+
openSession: (nodeId, input) => {
|
|
44546
|
+
const instance = input.instanceId ? this.terminalInstances().find((row) => row.id === input.instanceId) : void 0;
|
|
44547
|
+
const payload = {
|
|
44548
|
+
profileId: input.profileId,
|
|
44549
|
+
cols: input.cols,
|
|
44550
|
+
rows: input.rows,
|
|
44551
|
+
...instance ? {
|
|
44552
|
+
...instance.executable ? { executable: instance.executable } : {},
|
|
44553
|
+
...instance.args.length > 0 ? { args: [...instance.args] } : {},
|
|
44554
|
+
...instance.cwd ? { cwd: instance.cwd } : {},
|
|
44555
|
+
...instance.environment.length > 0 ? { environment: [...instance.environment] } : {}
|
|
44556
|
+
} : {}
|
|
44557
|
+
};
|
|
44558
|
+
return nodeId === localNodeId ? manager.openSession(payload) : this.ctx.api.terminalSession.openSession.mutate(payload, nodePin(nodeId));
|
|
44559
|
+
},
|
|
44387
44560
|
pullOutput: (nodeId, input) => nodeId === localNodeId ? manager.pullOutput(input) : this.ctx.api.terminalSession.pullOutput.mutate(input, nodePin(nodeId)),
|
|
44388
44561
|
closeSession: async (nodeId, sessionId) => {
|
|
44389
44562
|
if (nodeId === localNodeId) await manager.close({ sessionId });
|
|
@@ -44588,6 +44761,7 @@ var TerminalAddon = class extends BaseAddon {
|
|
|
44588
44761
|
return {
|
|
44589
44762
|
listInstances: async () => this.terminalInstances().map((instance) => this.instanceInfo(instance)),
|
|
44590
44763
|
createInstance: async (input) => this.createTerminalInstance(input),
|
|
44764
|
+
updateInstance: async (input) => this.updateTerminalInstance(input),
|
|
44591
44765
|
deleteInstance: async ({ instanceId }) => this.deleteTerminalInstance(instanceId),
|
|
44592
44766
|
setInstanceEnabled: async ({ instanceId, enabled }) => this.setTerminalInstanceEnabled(instanceId, enabled),
|
|
44593
44767
|
listLegacyCameras: async () => this.listLegacyTerminalCameras(),
|
|
@@ -44607,7 +44781,11 @@ var TerminalAddon = class extends BaseAddon {
|
|
|
44607
44781
|
profileId: instance.profileId,
|
|
44608
44782
|
profileLabel: instance.profileLabel,
|
|
44609
44783
|
name: instance.name,
|
|
44610
|
-
enabled: instance.enabled
|
|
44784
|
+
enabled: instance.enabled,
|
|
44785
|
+
executable: instance.executable,
|
|
44786
|
+
args: instance.args,
|
|
44787
|
+
cwd: instance.cwd,
|
|
44788
|
+
environment: instance.environment
|
|
44611
44789
|
};
|
|
44612
44790
|
}
|
|
44613
44791
|
replaceTerminalCameraTombstones(stableIds) {
|
|
@@ -44635,11 +44813,35 @@ var TerminalAddon = class extends BaseAddon {
|
|
|
44635
44813
|
profileId: profile.profileId,
|
|
44636
44814
|
profileLabel: profile.label,
|
|
44637
44815
|
name,
|
|
44638
|
-
enabled: true
|
|
44816
|
+
enabled: true,
|
|
44817
|
+
executable: input.executable?.trim() || profile.executable || "",
|
|
44818
|
+
args: input.args ? [...input.args] : [...profile.args ?? []],
|
|
44819
|
+
cwd: input.cwd?.trim() || profile.cwd || "",
|
|
44820
|
+
environment: input.environment ? [...input.environment] : [...profile.environment ?? []]
|
|
44639
44821
|
};
|
|
44640
44822
|
await this.updateGlobalSettings({ terminalInstances: [...this.config.terminalInstances, instance] });
|
|
44641
44823
|
return this.instanceInfo(instance);
|
|
44642
44824
|
}
|
|
44825
|
+
async updateTerminalInstance(input) {
|
|
44826
|
+
const updated = await this.instanceMutationQueue.run(() => this.updateTerminalInstanceUnlocked(input));
|
|
44827
|
+
await this.cameraRelay?.closeInstance(updated.instanceId);
|
|
44828
|
+
await this.reconcileTerminalCameras();
|
|
44829
|
+
return updated;
|
|
44830
|
+
}
|
|
44831
|
+
async updateTerminalInstanceUnlocked(input) {
|
|
44832
|
+
const instance = this.terminalInstances().find((candidate) => candidate.id === input.instanceId);
|
|
44833
|
+
if (!instance) throw new Error(`No such Terminal instance: ${input.instanceId}`);
|
|
44834
|
+
const updated = {
|
|
44835
|
+
...instance,
|
|
44836
|
+
...input.name !== void 0 ? { name: input.name.trim() } : {},
|
|
44837
|
+
...input.executable !== void 0 ? { executable: input.executable.trim() } : {},
|
|
44838
|
+
...input.args !== void 0 ? { args: [...input.args] } : {},
|
|
44839
|
+
...input.cwd !== void 0 ? { cwd: input.cwd.trim() } : {},
|
|
44840
|
+
...input.environment !== void 0 ? { environment: [...input.environment] } : {}
|
|
44841
|
+
};
|
|
44842
|
+
await this.updateGlobalSettings({ terminalInstances: this.config.terminalInstances.map((candidate) => candidate.id === input.instanceId ? updated : candidate) });
|
|
44843
|
+
return this.instanceInfo(updated);
|
|
44844
|
+
}
|
|
44643
44845
|
async deleteTerminalInstance(instanceId) {
|
|
44644
44846
|
await this.instanceMutationQueue.run(() => this.deleteTerminalInstanceUnlocked(instanceId));
|
|
44645
44847
|
await this.reconcileTerminalCameras();
|
|
@@ -44699,7 +44901,11 @@ var TerminalAddon = class extends BaseAddon {
|
|
|
44699
44901
|
profileId: "monitor",
|
|
44700
44902
|
profileLabel: legacy.profileLabel,
|
|
44701
44903
|
name: requestedName?.trim() || legacy.name,
|
|
44702
|
-
enabled: true
|
|
44904
|
+
enabled: true,
|
|
44905
|
+
executable: "",
|
|
44906
|
+
args: [],
|
|
44907
|
+
cwd: "",
|
|
44908
|
+
environment: []
|
|
44703
44909
|
};
|
|
44704
44910
|
await this.updateGlobalSettings({ terminalInstances: [...this.config.terminalInstances, instance] });
|
|
44705
44911
|
return this.instanceInfo(instance);
|
package/dist/addon.mjs
CHANGED
|
@@ -8,7 +8,7 @@ import { createServer } from "node:http";
|
|
|
8
8
|
//#region \0rolldown/runtime.js
|
|
9
9
|
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
10
10
|
//#endregion
|
|
11
|
-
//#region ../types/dist/event-category-
|
|
11
|
+
//#region ../types/dist/event-category-CIa_iT6b.mjs
|
|
12
12
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
13
13
|
EventCategory["SystemBoot"] = "system.boot";
|
|
14
14
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -24,6 +24,15 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
24
24
|
*/
|
|
25
25
|
EventCategory["SystemRestartCompleted"] = "system.restart-completed";
|
|
26
26
|
/**
|
|
27
|
+
* The hub reissued its own TLS certificate at boot (`ensureTlsCert`).
|
|
28
|
+
* Emitted only when the material on disk actually changed, so an
|
|
29
|
+
* operator who trusted the old certificate by hand is told rather than
|
|
30
|
+
* discovering it as a browser error. Payload `TlsCertChangedPayload`.
|
|
31
|
+
*
|
|
32
|
+
* Rule: docs/decisions/adr-0227-*.md
|
|
33
|
+
*/
|
|
34
|
+
EventCategory["SystemTlsCertChanged"] = "system.tls-cert-changed";
|
|
35
|
+
/**
|
|
27
36
|
* A newer addon or server-root package version was found by the
|
|
28
37
|
* authoritative registry check. Emitted once when any observed
|
|
29
38
|
* `latestVersion` changes (or a package/node first appears behind);
|
|
@@ -20000,7 +20009,12 @@ var TerminalSessionInfoSchema = object({
|
|
|
20000
20009
|
var TerminalProfileInfoSchema = object({
|
|
20001
20010
|
profileId: string(),
|
|
20002
20011
|
label: string(),
|
|
20003
|
-
description: string().optional()
|
|
20012
|
+
description: string().optional(),
|
|
20013
|
+
/** Spawn defaults the instance form copies on create. */
|
|
20014
|
+
executable: string().optional(),
|
|
20015
|
+
args: array(string()).readonly().optional(),
|
|
20016
|
+
cwd: string().optional(),
|
|
20017
|
+
environment: array(string()).readonly().optional()
|
|
20004
20018
|
});
|
|
20005
20019
|
/**
|
|
20006
20020
|
* A durable operator-created Terminal instance. Profiles are templates; only
|
|
@@ -20013,7 +20027,11 @@ var TerminalInstanceInfoSchema = object({
|
|
|
20013
20027
|
profileId: string(),
|
|
20014
20028
|
profileLabel: string(),
|
|
20015
20029
|
name: string(),
|
|
20016
|
-
enabled: boolean()
|
|
20030
|
+
enabled: boolean(),
|
|
20031
|
+
executable: string(),
|
|
20032
|
+
args: array(string()).readonly(),
|
|
20033
|
+
cwd: string(),
|
|
20034
|
+
environment: array(string()).readonly()
|
|
20017
20035
|
});
|
|
20018
20036
|
var TerminalLegacyCameraSchema = object({
|
|
20019
20037
|
stableId: string(),
|
|
@@ -20059,7 +20077,22 @@ var terminalSessionCapability = {
|
|
|
20059
20077
|
createInstance: method(object({
|
|
20060
20078
|
targetNodeId: string().min(1),
|
|
20061
20079
|
profileId: string().min(1),
|
|
20062
|
-
name: string().trim().min(1).max(160).optional()
|
|
20080
|
+
name: string().trim().min(1).max(160).optional(),
|
|
20081
|
+
executable: string().max(1024).optional(),
|
|
20082
|
+
args: array(string().max(2048)).max(64).optional(),
|
|
20083
|
+
cwd: string().max(1024).optional(),
|
|
20084
|
+
environment: array(string().max(4096)).max(64).optional()
|
|
20085
|
+
}), TerminalInstanceInfoSchema, {
|
|
20086
|
+
kind: "mutation",
|
|
20087
|
+
auth: "admin"
|
|
20088
|
+
}),
|
|
20089
|
+
updateInstance: method(object({
|
|
20090
|
+
instanceId: string().min(1),
|
|
20091
|
+
name: string().trim().min(1).max(160).optional(),
|
|
20092
|
+
executable: string().max(1024).optional(),
|
|
20093
|
+
args: array(string().max(2048)).max(64).optional(),
|
|
20094
|
+
cwd: string().max(1024).optional(),
|
|
20095
|
+
environment: array(string().max(4096)).max(64).optional()
|
|
20063
20096
|
}), TerminalInstanceInfoSchema, {
|
|
20064
20097
|
kind: "mutation",
|
|
20065
20098
|
auth: "admin"
|
|
@@ -20094,7 +20127,11 @@ var terminalSessionCapability = {
|
|
|
20094
20127
|
openSession: method(object({
|
|
20095
20128
|
profileId: string(),
|
|
20096
20129
|
cols: number().int().positive(),
|
|
20097
|
-
rows: number().int().positive()
|
|
20130
|
+
rows: number().int().positive(),
|
|
20131
|
+
executable: string().max(1024).optional(),
|
|
20132
|
+
args: array(string().max(2048)).max(64).optional(),
|
|
20133
|
+
cwd: string().max(1024).optional(),
|
|
20134
|
+
environment: array(string().max(4096)).max(64).optional()
|
|
20098
20135
|
}), TerminalSessionInfoSchema, {
|
|
20099
20136
|
kind: "mutation",
|
|
20100
20137
|
auth: "admin"
|
|
@@ -24149,6 +24186,20 @@ var AllowedAddressesSchema = object({
|
|
|
24149
24186
|
* Network Addresses admin page and persisted by the addon.
|
|
24150
24187
|
*/
|
|
24151
24188
|
addresses: array(string()).readonly() });
|
|
24189
|
+
var TlsStatusSchema = object({
|
|
24190
|
+
mode: _enum([
|
|
24191
|
+
"generated",
|
|
24192
|
+
"uploaded",
|
|
24193
|
+
"disabled"
|
|
24194
|
+
]),
|
|
24195
|
+
leafFingerprintSha256: string().nullable(),
|
|
24196
|
+
caFingerprintSha256: string().nullable(),
|
|
24197
|
+
validTo: string().nullable(),
|
|
24198
|
+
sans: array(string()),
|
|
24199
|
+
caCertPem: string().nullable(),
|
|
24200
|
+
reissueError: string().nullable(),
|
|
24201
|
+
restartRequired: boolean()
|
|
24202
|
+
});
|
|
24152
24203
|
method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
|
|
24153
24204
|
/**
|
|
24154
24205
|
* LEGACY HINT — do not send from new code. Kept optional so clients
|
|
@@ -24168,7 +24219,20 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
|
|
|
24168
24219
|
* to avoid mixed-content blocks in the browser. The public
|
|
24169
24220
|
* tunnel always emits `https://` regardless. */
|
|
24170
24221
|
scheme: _enum(["http", "https"]).optional()
|
|
24171
|
-
}), GetConnectionEndpointsResultSchema), method(_void(), NotificationEndpointSchema), method(object({ baseUrl: string().nullable() }), NotificationEndpointSchema, { kind: "mutation" }), method(_void(), AllowedAddressesSchema), method(AllowedAddressesSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), AllowedAddressesSchema, { kind: "mutation" })
|
|
24222
|
+
}), GetConnectionEndpointsResultSchema), method(_void(), NotificationEndpointSchema), method(object({ baseUrl: string().nullable() }), NotificationEndpointSchema, { kind: "mutation" }), method(_void(), AllowedAddressesSchema), method(AllowedAddressesSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), AllowedAddressesSchema, { kind: "mutation" }), method(_void(), TlsStatusSchema), method(object({ reason: string().optional() }), TlsStatusSchema, {
|
|
24223
|
+
kind: "mutation",
|
|
24224
|
+
auth: "admin"
|
|
24225
|
+
}), method(object({
|
|
24226
|
+
certPem: string().min(1),
|
|
24227
|
+
keyPem: string().min(1),
|
|
24228
|
+
caPem: string().optional()
|
|
24229
|
+
}), TlsStatusSchema, {
|
|
24230
|
+
kind: "mutation",
|
|
24231
|
+
auth: "admin"
|
|
24232
|
+
}), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
|
|
24233
|
+
kind: "mutation",
|
|
24234
|
+
auth: "admin"
|
|
24235
|
+
});
|
|
24172
24236
|
var LockControlStatusSchema = object({
|
|
24173
24237
|
/** Lifecycle state of the lock. `jammed` means the motor reported
|
|
24174
24238
|
* failure to reach the target — operator intervention required. */
|
|
@@ -32193,6 +32257,12 @@ Object.freeze({
|
|
|
32193
32257
|
addonId: null,
|
|
32194
32258
|
access: "create"
|
|
32195
32259
|
},
|
|
32260
|
+
"localNetwork.downloadCa": {
|
|
32261
|
+
capName: "local-network",
|
|
32262
|
+
capScope: "system",
|
|
32263
|
+
addonId: null,
|
|
32264
|
+
access: "view"
|
|
32265
|
+
},
|
|
32196
32266
|
"localNetwork.getAllowedAddresses": {
|
|
32197
32267
|
capName: "local-network",
|
|
32198
32268
|
capScope: "system",
|
|
@@ -32217,18 +32287,36 @@ Object.freeze({
|
|
|
32217
32287
|
addonId: null,
|
|
32218
32288
|
access: "view"
|
|
32219
32289
|
},
|
|
32290
|
+
"localNetwork.getTlsStatus": {
|
|
32291
|
+
capName: "local-network",
|
|
32292
|
+
capScope: "system",
|
|
32293
|
+
addonId: null,
|
|
32294
|
+
access: "view"
|
|
32295
|
+
},
|
|
32220
32296
|
"localNetwork.list": {
|
|
32221
32297
|
capName: "local-network",
|
|
32222
32298
|
capScope: "system",
|
|
32223
32299
|
addonId: null,
|
|
32224
32300
|
access: "view"
|
|
32225
32301
|
},
|
|
32302
|
+
"localNetwork.regenerateCertificate": {
|
|
32303
|
+
capName: "local-network",
|
|
32304
|
+
capScope: "system",
|
|
32305
|
+
addonId: null,
|
|
32306
|
+
access: "create"
|
|
32307
|
+
},
|
|
32226
32308
|
"localNetwork.resetAllowlistToBestMatch": {
|
|
32227
32309
|
capName: "local-network",
|
|
32228
32310
|
capScope: "system",
|
|
32229
32311
|
addonId: null,
|
|
32230
32312
|
access: "delete"
|
|
32231
32313
|
},
|
|
32314
|
+
"localNetwork.revertToGeneratedCertificate": {
|
|
32315
|
+
capName: "local-network",
|
|
32316
|
+
capScope: "system",
|
|
32317
|
+
addonId: null,
|
|
32318
|
+
access: "create"
|
|
32319
|
+
},
|
|
32232
32320
|
"localNetwork.setAllowedAddresses": {
|
|
32233
32321
|
capName: "local-network",
|
|
32234
32322
|
capScope: "system",
|
|
@@ -32241,6 +32329,12 @@ Object.freeze({
|
|
|
32241
32329
|
addonId: null,
|
|
32242
32330
|
access: "create"
|
|
32243
32331
|
},
|
|
32332
|
+
"localNetwork.uploadCertificate": {
|
|
32333
|
+
capName: "local-network",
|
|
32334
|
+
capScope: "system",
|
|
32335
|
+
addonId: null,
|
|
32336
|
+
access: "create"
|
|
32337
|
+
},
|
|
32244
32338
|
"lockControl.lock": {
|
|
32245
32339
|
capName: "lock-control",
|
|
32246
32340
|
capScope: "device",
|
|
@@ -35121,6 +35215,12 @@ Object.freeze({
|
|
|
35121
35215
|
addonId: null,
|
|
35122
35216
|
access: "create"
|
|
35123
35217
|
},
|
|
35218
|
+
"terminalSession.updateInstance": {
|
|
35219
|
+
capName: "terminal-session",
|
|
35220
|
+
capScope: "system",
|
|
35221
|
+
addonId: null,
|
|
35222
|
+
access: "create"
|
|
35223
|
+
},
|
|
35124
35224
|
"terminalSession.writeInput": {
|
|
35125
35225
|
capName: "terminal-session",
|
|
35126
35226
|
capScope: "system",
|
|
@@ -37608,6 +37708,35 @@ Object.freeze(Object.fromEntries([{
|
|
|
37608
37708
|
}]
|
|
37609
37709
|
}].map((s) => [s.stepId, s.defaultModelId])));
|
|
37610
37710
|
string().min(1);
|
|
37711
|
+
var CLUSTER_STEP_SETTING_FIELDS = [{
|
|
37712
|
+
stepId: "face-embedding",
|
|
37713
|
+
key: "minLandmarkFaceSize",
|
|
37714
|
+
label: "Min face size for recognition (detection px)",
|
|
37715
|
+
description: "Refuse to embed a face smaller than this IN THE DETECTION FRAME. Applies to every node — the enrolled gallery is one index, and two admission floors would fill it from two policies. 0 disables the gate.",
|
|
37716
|
+
type: "slider",
|
|
37717
|
+
min: 0,
|
|
37718
|
+
max: 64,
|
|
37719
|
+
step: 2,
|
|
37720
|
+
default: 24
|
|
37721
|
+
}];
|
|
37722
|
+
function clusterStepSettingKey(stepId, fieldKey) {
|
|
37723
|
+
return `clusterStepSetting:${stepId}:${fieldKey}`;
|
|
37724
|
+
}
|
|
37725
|
+
var ClusterSettingNumberSchema = number().finite();
|
|
37726
|
+
function readClusterStepSettings(config) {
|
|
37727
|
+
const out = {};
|
|
37728
|
+
for (const field of CLUSTER_STEP_SETTING_FIELDS) {
|
|
37729
|
+
const parsed = ClusterSettingNumberSchema.safeParse(config[clusterStepSettingKey(field.stepId, field.key)]);
|
|
37730
|
+
const value = parsed.success ? parsed.data : field.default;
|
|
37731
|
+
const existing = out[field.stepId] ?? {};
|
|
37732
|
+
out[field.stepId] = {
|
|
37733
|
+
...existing,
|
|
37734
|
+
[field.key]: value
|
|
37735
|
+
};
|
|
37736
|
+
}
|
|
37737
|
+
return out;
|
|
37738
|
+
}
|
|
37739
|
+
readClusterStepSettings({});
|
|
37611
37740
|
object({
|
|
37612
37741
|
/**
|
|
37613
37742
|
* Fraction of the box's own size added on EACH side before cutting.
|
|
@@ -43315,7 +43444,8 @@ var TerminalCameraRelay = class {
|
|
|
43315
43444
|
const opening = this.api.openSession(state.nodeId, {
|
|
43316
43445
|
profileId: state.profileId,
|
|
43317
43446
|
cols: 120,
|
|
43318
|
-
rows: 40
|
|
43447
|
+
rows: 40,
|
|
43448
|
+
instanceId: state.instanceId
|
|
43319
43449
|
});
|
|
43320
43450
|
state.openPromise = opening;
|
|
43321
43451
|
try {
|
|
@@ -43702,8 +43832,25 @@ var TerminalInstanceSchema = object({
|
|
|
43702
43832
|
profileId: string().min(1).max(64),
|
|
43703
43833
|
profileLabel: string().min(1).max(120),
|
|
43704
43834
|
name: string().min(1).max(160),
|
|
43705
|
-
enabled: boolean()
|
|
43835
|
+
enabled: boolean(),
|
|
43836
|
+
executable: string().max(1024).default(""),
|
|
43837
|
+
args: array(string().max(2048)).max(64).default([]),
|
|
43838
|
+
cwd: string().max(1024).default(""),
|
|
43839
|
+
environment: array(string().max(4096)).max(64).default([])
|
|
43706
43840
|
});
|
|
43841
|
+
function environmentEntriesToRecord(entries) {
|
|
43842
|
+
const out = {};
|
|
43843
|
+
for (const entry of entries) {
|
|
43844
|
+
const eq = entry.indexOf("=");
|
|
43845
|
+
if (eq <= 0) continue;
|
|
43846
|
+
out[entry.slice(0, eq)] = entry.slice(eq + 1);
|
|
43847
|
+
}
|
|
43848
|
+
return out;
|
|
43849
|
+
}
|
|
43850
|
+
function environmentRecordToEntries(env) {
|
|
43851
|
+
if (env === void 0) return [];
|
|
43852
|
+
return Object.entries(env).map(([name, value]) => `${name}=${value}`);
|
|
43853
|
+
}
|
|
43707
43854
|
/**
|
|
43708
43855
|
* Config is operator-writable, so malformed or duplicate rows are ignored
|
|
43709
43856
|
* rather than allowed to make declaration reconciliation destructive.
|
|
@@ -43967,7 +44114,11 @@ var TerminalSessionManager = class {
|
|
|
43967
44114
|
return this.profiles.map((p) => ({
|
|
43968
44115
|
profileId: p.profileId,
|
|
43969
44116
|
label: p.label,
|
|
43970
|
-
...p.description !== void 0 ? { description: p.description } : {}
|
|
44117
|
+
...p.description !== void 0 ? { description: p.description } : {},
|
|
44118
|
+
executable: p.file,
|
|
44119
|
+
args: [...p.args],
|
|
44120
|
+
cwd: p.cwd ?? "",
|
|
44121
|
+
environment: environmentRecordToEntries(p.env)
|
|
43971
44122
|
}));
|
|
43972
44123
|
}
|
|
43973
44124
|
setInstanceControl(control) {
|
|
@@ -43979,6 +44130,9 @@ var TerminalSessionManager = class {
|
|
|
43979
44130
|
async createInstance(input) {
|
|
43980
44131
|
return this.requireInstanceControl().createInstance(input);
|
|
43981
44132
|
}
|
|
44133
|
+
async updateInstance(input) {
|
|
44134
|
+
return this.requireInstanceControl().updateInstance(input);
|
|
44135
|
+
}
|
|
43982
44136
|
async deleteInstance(input) {
|
|
43983
44137
|
await this.requireInstanceControl().deleteInstance(input);
|
|
43984
44138
|
}
|
|
@@ -44001,24 +44155,29 @@ var TerminalSessionManager = class {
|
|
|
44001
44155
|
const cols = clampDimension(input.cols, MAX_COLS);
|
|
44002
44156
|
const rows = clampDimension(input.rows, MAX_ROWS);
|
|
44003
44157
|
const sessionId = this.mintId();
|
|
44158
|
+
const file = input.executable?.trim() || profile.file;
|
|
44159
|
+
const args = input.args !== void 0 ? [...input.args] : [...profile.args];
|
|
44160
|
+
const cwd = input.cwd?.trim() || profile.cwd || this.opts.cwd;
|
|
44161
|
+
const env = {
|
|
44162
|
+
...this.opts.env,
|
|
44163
|
+
...profile.env,
|
|
44164
|
+
...environmentEntriesToRecord(input.environment ?? [])
|
|
44165
|
+
};
|
|
44004
44166
|
const executable = [{
|
|
44005
|
-
file
|
|
44006
|
-
args
|
|
44007
|
-
}, ...profile.fallbacks ?? []].map((candidate) => ({
|
|
44167
|
+
file,
|
|
44168
|
+
args
|
|
44169
|
+
}, ...input.executable?.trim() ? [] : profile.fallbacks ?? []].map((candidate) => ({
|
|
44008
44170
|
...candidate,
|
|
44009
44171
|
resolvedFile: this.resolveBinary(candidate.file)
|
|
44010
44172
|
})).find((candidate) => candidate.resolvedFile !== null);
|
|
44011
|
-
if (executable === void 0) throw new Error(`Terminal profile '${profile.profileId}' cannot start: executable '${
|
|
44173
|
+
if (executable === void 0) throw new Error(`Terminal profile '${profile.profileId}' cannot start: executable '${file}' was not found on PATH. Install it in the server image or set its path in Terminal settings.`);
|
|
44012
44174
|
const pty = this.opts.spawn({
|
|
44013
44175
|
file: executable.resolvedFile,
|
|
44014
44176
|
args: executable.args,
|
|
44015
44177
|
cols,
|
|
44016
44178
|
rows,
|
|
44017
|
-
...
|
|
44018
|
-
...
|
|
44019
|
-
...this.opts.env,
|
|
44020
|
-
...profile.env
|
|
44021
|
-
} } : {}
|
|
44179
|
+
...cwd !== void 0 && cwd !== "" ? { cwd } : {},
|
|
44180
|
+
...Object.keys(env).length > 0 ? { env } : {}
|
|
44022
44181
|
});
|
|
44023
44182
|
const screen = this.opts.screenFactory(cols, rows);
|
|
44024
44183
|
const info = {
|
|
@@ -44116,7 +44275,7 @@ var TerminalSessionManager = class {
|
|
|
44116
44275
|
const oldestSeq = session.output[0]?.seq ?? session.nextSeq;
|
|
44117
44276
|
if (input.afterSeq === 0 || input.afterSeq < oldestSeq - 1 || input.afterSeq > cursor) {
|
|
44118
44277
|
await session.screen.flush();
|
|
44119
|
-
const exit =
|
|
44278
|
+
const exit = session.output.toReversed().find((event) => event.kind === "exit");
|
|
44120
44279
|
return {
|
|
44121
44280
|
cursor,
|
|
44122
44281
|
reset: true,
|
|
@@ -44360,7 +44519,21 @@ var TerminalAddon = class extends BaseAddon {
|
|
|
44360
44519
|
const localNodeId = declarationOwnerNodeId(this.ctx.kernel.localNodeId);
|
|
44361
44520
|
const cameraRelay = new TerminalCameraRelay({
|
|
44362
44521
|
listProfiles: (nodeId) => nodeId === localNodeId ? manager.listProfiles() : this.ctx.api.terminalSession.listProfiles.query(void 0, nodePin(nodeId)),
|
|
44363
|
-
openSession: (nodeId, input) =>
|
|
44522
|
+
openSession: (nodeId, input) => {
|
|
44523
|
+
const instance = input.instanceId ? this.terminalInstances().find((row) => row.id === input.instanceId) : void 0;
|
|
44524
|
+
const payload = {
|
|
44525
|
+
profileId: input.profileId,
|
|
44526
|
+
cols: input.cols,
|
|
44527
|
+
rows: input.rows,
|
|
44528
|
+
...instance ? {
|
|
44529
|
+
...instance.executable ? { executable: instance.executable } : {},
|
|
44530
|
+
...instance.args.length > 0 ? { args: [...instance.args] } : {},
|
|
44531
|
+
...instance.cwd ? { cwd: instance.cwd } : {},
|
|
44532
|
+
...instance.environment.length > 0 ? { environment: [...instance.environment] } : {}
|
|
44533
|
+
} : {}
|
|
44534
|
+
};
|
|
44535
|
+
return nodeId === localNodeId ? manager.openSession(payload) : this.ctx.api.terminalSession.openSession.mutate(payload, nodePin(nodeId));
|
|
44536
|
+
},
|
|
44364
44537
|
pullOutput: (nodeId, input) => nodeId === localNodeId ? manager.pullOutput(input) : this.ctx.api.terminalSession.pullOutput.mutate(input, nodePin(nodeId)),
|
|
44365
44538
|
closeSession: async (nodeId, sessionId) => {
|
|
44366
44539
|
if (nodeId === localNodeId) await manager.close({ sessionId });
|
|
@@ -44565,6 +44738,7 @@ var TerminalAddon = class extends BaseAddon {
|
|
|
44565
44738
|
return {
|
|
44566
44739
|
listInstances: async () => this.terminalInstances().map((instance) => this.instanceInfo(instance)),
|
|
44567
44740
|
createInstance: async (input) => this.createTerminalInstance(input),
|
|
44741
|
+
updateInstance: async (input) => this.updateTerminalInstance(input),
|
|
44568
44742
|
deleteInstance: async ({ instanceId }) => this.deleteTerminalInstance(instanceId),
|
|
44569
44743
|
setInstanceEnabled: async ({ instanceId, enabled }) => this.setTerminalInstanceEnabled(instanceId, enabled),
|
|
44570
44744
|
listLegacyCameras: async () => this.listLegacyTerminalCameras(),
|
|
@@ -44584,7 +44758,11 @@ var TerminalAddon = class extends BaseAddon {
|
|
|
44584
44758
|
profileId: instance.profileId,
|
|
44585
44759
|
profileLabel: instance.profileLabel,
|
|
44586
44760
|
name: instance.name,
|
|
44587
|
-
enabled: instance.enabled
|
|
44761
|
+
enabled: instance.enabled,
|
|
44762
|
+
executable: instance.executable,
|
|
44763
|
+
args: instance.args,
|
|
44764
|
+
cwd: instance.cwd,
|
|
44765
|
+
environment: instance.environment
|
|
44588
44766
|
};
|
|
44589
44767
|
}
|
|
44590
44768
|
replaceTerminalCameraTombstones(stableIds) {
|
|
@@ -44612,11 +44790,35 @@ var TerminalAddon = class extends BaseAddon {
|
|
|
44612
44790
|
profileId: profile.profileId,
|
|
44613
44791
|
profileLabel: profile.label,
|
|
44614
44792
|
name,
|
|
44615
|
-
enabled: true
|
|
44793
|
+
enabled: true,
|
|
44794
|
+
executable: input.executable?.trim() || profile.executable || "",
|
|
44795
|
+
args: input.args ? [...input.args] : [...profile.args ?? []],
|
|
44796
|
+
cwd: input.cwd?.trim() || profile.cwd || "",
|
|
44797
|
+
environment: input.environment ? [...input.environment] : [...profile.environment ?? []]
|
|
44616
44798
|
};
|
|
44617
44799
|
await this.updateGlobalSettings({ terminalInstances: [...this.config.terminalInstances, instance] });
|
|
44618
44800
|
return this.instanceInfo(instance);
|
|
44619
44801
|
}
|
|
44802
|
+
async updateTerminalInstance(input) {
|
|
44803
|
+
const updated = await this.instanceMutationQueue.run(() => this.updateTerminalInstanceUnlocked(input));
|
|
44804
|
+
await this.cameraRelay?.closeInstance(updated.instanceId);
|
|
44805
|
+
await this.reconcileTerminalCameras();
|
|
44806
|
+
return updated;
|
|
44807
|
+
}
|
|
44808
|
+
async updateTerminalInstanceUnlocked(input) {
|
|
44809
|
+
const instance = this.terminalInstances().find((candidate) => candidate.id === input.instanceId);
|
|
44810
|
+
if (!instance) throw new Error(`No such Terminal instance: ${input.instanceId}`);
|
|
44811
|
+
const updated = {
|
|
44812
|
+
...instance,
|
|
44813
|
+
...input.name !== void 0 ? { name: input.name.trim() } : {},
|
|
44814
|
+
...input.executable !== void 0 ? { executable: input.executable.trim() } : {},
|
|
44815
|
+
...input.args !== void 0 ? { args: [...input.args] } : {},
|
|
44816
|
+
...input.cwd !== void 0 ? { cwd: input.cwd.trim() } : {},
|
|
44817
|
+
...input.environment !== void 0 ? { environment: [...input.environment] } : {}
|
|
44818
|
+
};
|
|
44819
|
+
await this.updateGlobalSettings({ terminalInstances: this.config.terminalInstances.map((candidate) => candidate.id === input.instanceId ? updated : candidate) });
|
|
44820
|
+
return this.instanceInfo(updated);
|
|
44821
|
+
}
|
|
44620
44822
|
async deleteTerminalInstance(instanceId) {
|
|
44621
44823
|
await this.instanceMutationQueue.run(() => this.deleteTerminalInstanceUnlocked(instanceId));
|
|
44622
44824
|
await this.reconcileTerminalCameras();
|
|
@@ -44676,7 +44878,11 @@ var TerminalAddon = class extends BaseAddon {
|
|
|
44676
44878
|
profileId: "monitor",
|
|
44677
44879
|
profileLabel: legacy.profileLabel,
|
|
44678
44880
|
name: requestedName?.trim() || legacy.name,
|
|
44679
|
-
enabled: true
|
|
44881
|
+
enabled: true,
|
|
44882
|
+
executable: "",
|
|
44883
|
+
args: [],
|
|
44884
|
+
cwd: "",
|
|
44885
|
+
environment: []
|
|
44680
44886
|
};
|
|
44681
44887
|
await this.updateGlobalSettings({ terminalInstances: [...this.config.terminalInstances, instance] });
|
|
44682
44888
|
return this.instanceInfo(instance);
|