rterm-backend 2.7.0 → 2.7.2
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/bin/gybackend.js +218 -20
- package/package.json +1 -1
package/bin/gybackend.js
CHANGED
|
@@ -254727,7 +254727,7 @@ var require_websocket = __commonJS({
|
|
|
254727
254727
|
var http2 = __require("http");
|
|
254728
254728
|
var net2 = __require("net");
|
|
254729
254729
|
var tls = __require("tls");
|
|
254730
|
-
var { randomBytes: randomBytes2, createHash } = __require("crypto");
|
|
254730
|
+
var { randomBytes: randomBytes2, createHash: createHash3 } = __require("crypto");
|
|
254731
254731
|
var { Duplex, Readable } = __require("stream");
|
|
254732
254732
|
var { URL: URL5 } = __require("url");
|
|
254733
254733
|
var PerMessageDeflate = require_permessage_deflate();
|
|
@@ -255387,7 +255387,7 @@ var require_websocket = __commonJS({
|
|
|
255387
255387
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
255388
255388
|
return;
|
|
255389
255389
|
}
|
|
255390
|
-
const digest =
|
|
255390
|
+
const digest = createHash3("sha1").update(key + GUID).digest("base64");
|
|
255391
255391
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
255392
255392
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
255393
255393
|
return;
|
|
@@ -255754,7 +255754,7 @@ var require_websocket_server = __commonJS({
|
|
|
255754
255754
|
var EventEmitter4 = __require("events");
|
|
255755
255755
|
var http2 = __require("http");
|
|
255756
255756
|
var { Duplex } = __require("stream");
|
|
255757
|
-
var { createHash } = __require("crypto");
|
|
255757
|
+
var { createHash: createHash3 } = __require("crypto");
|
|
255758
255758
|
var extension = require_extension();
|
|
255759
255759
|
var PerMessageDeflate = require_permessage_deflate();
|
|
255760
255760
|
var subprotocol = require_subprotocol();
|
|
@@ -256055,7 +256055,7 @@ var require_websocket_server = __commonJS({
|
|
|
256055
256055
|
);
|
|
256056
256056
|
}
|
|
256057
256057
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
256058
|
-
const digest =
|
|
256058
|
+
const digest = createHash3("sha1").update(key + GUID).digest("base64");
|
|
256059
256059
|
const headers = [
|
|
256060
256060
|
"HTTP/1.1 101 Switching Protocols",
|
|
256061
256061
|
"Upgrade: websocket",
|
|
@@ -360739,7 +360739,7 @@ var AgentService_v2 = class {
|
|
|
360739
360739
|
state.messages
|
|
360740
360740
|
);
|
|
360741
360741
|
const messages = [...state.messages];
|
|
360742
|
-
const lastMessage = messages[messages.length - 1];
|
|
360742
|
+
const lastMessage = messages.length > 0 ? messages[messages.length - 1] : void 0;
|
|
360743
360743
|
let pendingToolCalls = [];
|
|
360744
360744
|
if (AIMessage.isInstance(lastMessage) && (hasEmptyMalformedToolCallFinishFlag(lastMessage) || isEmptyMalformedToolCallFinish(lastMessage, []))) {
|
|
360745
360745
|
this.helpers.sendEvent(sessionId, {
|
|
@@ -361623,7 +361623,7 @@ ${reminder}`;
|
|
|
361623
361623
|
const sessionId = state.sessionId;
|
|
361624
361624
|
if (!sessionId) throw new Error("No session ID in state");
|
|
361625
361625
|
const messages = [...state.messages];
|
|
361626
|
-
const lastMessage = messages[messages.length - 1];
|
|
361626
|
+
const lastMessage = messages.length > 0 ? messages[messages.length - 1] : void 0;
|
|
361627
361627
|
const lastMessageIsAi = AIMessage.isInstance(lastMessage);
|
|
361628
361628
|
const guardMessages = lastMessageIsAi || messages.length === 0 ? messages : messages.slice(0, -1);
|
|
361629
361629
|
if (!lastMessageIsAi && lastMessage) {
|
|
@@ -361800,7 +361800,7 @@ ${reminder}`;
|
|
|
361800
361800
|
const sessionId = state.sessionId;
|
|
361801
361801
|
if (!sessionId) return state;
|
|
361802
361802
|
const messages = Array.isArray(state.messages) ? [...state.messages] : [];
|
|
361803
|
-
const lastMessage = messages[messages.length - 1];
|
|
361803
|
+
const lastMessage = messages.length > 0 ? messages[messages.length - 1] : void 0;
|
|
361804
361804
|
const finalBoundaryMessages = AIMessage.isInstance(lastMessage) || messages.length === 0 ? messages : messages.slice(0, -1);
|
|
361805
361805
|
const queuedInsertionResult = this.appendQueuedInsertionMessagesForContinue(
|
|
361806
361806
|
sessionId,
|
|
@@ -362362,7 +362362,7 @@ ${reminder}`;
|
|
|
362362
362362
|
return "tools";
|
|
362363
362363
|
}
|
|
362364
362364
|
const messages = Array.isArray(state.messages) ? state.messages : [];
|
|
362365
|
-
const lastMessage = messages[messages.length - 1];
|
|
362365
|
+
const lastMessage = messages.length > 0 ? messages[messages.length - 1] : void 0;
|
|
362366
362366
|
if (AIMessage.isInstance(lastMessage) && (hasEmptyMalformedToolCallFinishFlag(lastMessage) || isEmptyMalformedToolCallFinish(lastMessage, []))) {
|
|
362367
362367
|
return "final_output";
|
|
362368
362368
|
}
|
|
@@ -362409,8 +362409,8 @@ ${reminder}`;
|
|
|
362409
362409
|
}
|
|
362410
362410
|
}
|
|
362411
362411
|
trimInterruptedToolCallHistory(messages, executedToolCall) {
|
|
362412
|
-
const lastMessage = messages[messages.length - 1];
|
|
362413
|
-
if (!AIMessage.isInstance(lastMessage)) {
|
|
362412
|
+
const lastMessage = messages.length > 0 ? messages[messages.length - 1] : void 0;
|
|
362413
|
+
if (!lastMessage || !AIMessage.isInstance(lastMessage)) {
|
|
362414
362414
|
return;
|
|
362415
362415
|
}
|
|
362416
362416
|
const toolCalls = Array.isArray(lastMessage.tool_calls) ? lastMessage.tool_calls : [];
|
|
@@ -377401,8 +377401,8 @@ var MetricsLedger = class {
|
|
|
377401
377401
|
var DEFAULT_WINDOW = 36e5;
|
|
377402
377402
|
function percentile(sorted, p) {
|
|
377403
377403
|
if (sorted.length === 0) return void 0;
|
|
377404
|
-
const
|
|
377405
|
-
return sorted[
|
|
377404
|
+
const rank = Math.max(1, Math.ceil(p / 100 * sorted.length));
|
|
377405
|
+
return sorted[Math.min(rank, sorted.length) - 1];
|
|
377406
377406
|
}
|
|
377407
377407
|
var GoldenSignals = class {
|
|
377408
377408
|
constructor(deps) {
|
|
@@ -381941,16 +381941,19 @@ function parseAperfReport(text) {
|
|
|
381941
381941
|
topProcs.sort((a, b) => b.cpuPercent - a.cpuPercent);
|
|
381942
381942
|
if (topProcs.length > 0) summary.topCpuProcesses = topProcs.slice(0, 5);
|
|
381943
381943
|
if (findings.length === 0) {
|
|
381944
|
-
|
|
381945
|
-
|
|
381946
|
-
|
|
381947
|
-
|
|
381944
|
+
const cpu = summary.cpuUsagePercent;
|
|
381945
|
+
if (cpu !== void 0 && cpu >= 90) {
|
|
381946
|
+
findings.push({ metric: "cpu_utilization", value: cpu, severity: "critical", message: `aggregate CPU utilization is ${cpu}% (>= 90%)` });
|
|
381947
|
+
} else if (cpu !== void 0 && cpu >= 75) {
|
|
381948
|
+
findings.push({ metric: "cpu_utilization", value: cpu, severity: "warning", message: `aggregate CPU utilization is ${cpu}% (>= 75%)` });
|
|
381948
381949
|
}
|
|
381949
|
-
|
|
381950
|
-
|
|
381950
|
+
const mem = summary.memUsagePercent;
|
|
381951
|
+
if (mem !== void 0 && mem >= 90) {
|
|
381952
|
+
findings.push({ metric: "memory", value: mem, severity: "critical", message: `memory utilization is ${mem}% (>= 90%)` });
|
|
381951
381953
|
}
|
|
381952
|
-
|
|
381953
|
-
|
|
381954
|
+
const disk = summary.diskUsagePercentMax;
|
|
381955
|
+
if (disk !== void 0 && disk >= 90) {
|
|
381956
|
+
findings.push({ metric: "disk", value: disk, severity: "critical", message: `a disk is at ${disk}% utilization (>= 90%)` });
|
|
381954
381957
|
}
|
|
381955
381958
|
if (summary.topCpuProcesses && summary.topCpuProcesses.length > 0) {
|
|
381956
381959
|
const top = summary.topCpuProcesses[0];
|
|
@@ -382011,6 +382014,198 @@ function aperfSummaryToMetricPoint(result) {
|
|
|
382011
382014
|
};
|
|
382012
382015
|
}
|
|
382013
382016
|
|
|
382017
|
+
// ../../packages/backend/src/services/audit/auditLedger.ts
|
|
382018
|
+
import { createHash, randomUUID as randomUUID21 } from "crypto";
|
|
382019
|
+
var GENESIS_HASH = "0".repeat(64);
|
|
382020
|
+
function computeRecordHash(kind, actor, target, summary, detail, at, seq2) {
|
|
382021
|
+
const payload = JSON.stringify({ kind, actor, target, summary, detail, at, seq: seq2 });
|
|
382022
|
+
return createHash("sha256").update(payload).digest("hex");
|
|
382023
|
+
}
|
|
382024
|
+
var AuditLedger = class _AuditLedger {
|
|
382025
|
+
records = [];
|
|
382026
|
+
hashFn;
|
|
382027
|
+
now;
|
|
382028
|
+
constructor(deps = {}) {
|
|
382029
|
+
this.hashFn = deps.hashFn ?? ((data) => createHash("sha256").update(data).digest("hex"));
|
|
382030
|
+
this.now = deps.now ?? (() => Date.now());
|
|
382031
|
+
}
|
|
382032
|
+
/** Append an event to the audit ledger. Returns the record with hash + chain. */
|
|
382033
|
+
append(event) {
|
|
382034
|
+
const seq2 = this.records.length + 1;
|
|
382035
|
+
const at = event.at ?? this.now();
|
|
382036
|
+
const prevHash = this.records.length > 0 ? this.records[this.records.length - 1].hash : GENESIS_HASH;
|
|
382037
|
+
const contentHash = computeRecordHash(
|
|
382038
|
+
event.kind,
|
|
382039
|
+
event.actor,
|
|
382040
|
+
event.target,
|
|
382041
|
+
event.summary,
|
|
382042
|
+
event.detail,
|
|
382043
|
+
at,
|
|
382044
|
+
seq2
|
|
382045
|
+
);
|
|
382046
|
+
const hash2 = createHash("sha256").update(contentHash + prevHash).digest("hex");
|
|
382047
|
+
const record2 = {
|
|
382048
|
+
id: `audit-${randomUUID21().slice(0, 12)}`,
|
|
382049
|
+
kind: event.kind,
|
|
382050
|
+
actor: event.actor,
|
|
382051
|
+
target: event.target,
|
|
382052
|
+
summary: event.summary,
|
|
382053
|
+
...event.detail ? { detail: event.detail } : {},
|
|
382054
|
+
at,
|
|
382055
|
+
seq: seq2,
|
|
382056
|
+
hash: hash2,
|
|
382057
|
+
prevHash
|
|
382058
|
+
};
|
|
382059
|
+
this.records.push(record2);
|
|
382060
|
+
return record2;
|
|
382061
|
+
}
|
|
382062
|
+
/** Get all records (ordered by seq). */
|
|
382063
|
+
list() {
|
|
382064
|
+
return [...this.records];
|
|
382065
|
+
}
|
|
382066
|
+
/** Get records by kind. */
|
|
382067
|
+
listByKind(kind) {
|
|
382068
|
+
return this.records.filter((r) => r.kind === kind);
|
|
382069
|
+
}
|
|
382070
|
+
/** Get records for a specific target. */
|
|
382071
|
+
listByTarget(target) {
|
|
382072
|
+
return this.records.filter((r) => r.target === target);
|
|
382073
|
+
}
|
|
382074
|
+
/** Get records for a specific actor. */
|
|
382075
|
+
listByActor(actor) {
|
|
382076
|
+
return this.records.filter((r) => r.actor === actor);
|
|
382077
|
+
}
|
|
382078
|
+
/** Get records in a time range. */
|
|
382079
|
+
listInRange(fromAt, toAt) {
|
|
382080
|
+
return this.records.filter((r) => r.at >= fromAt && r.at <= toAt);
|
|
382081
|
+
}
|
|
382082
|
+
/** Verify the hash chain integrity. Returns { valid: true } or { valid: false, brokenAt: seq }. */
|
|
382083
|
+
verify() {
|
|
382084
|
+
if (this.records.length === 0) return { valid: true };
|
|
382085
|
+
for (let i = 0; i < this.records.length; i++) {
|
|
382086
|
+
const record2 = this.records[i];
|
|
382087
|
+
if (record2.seq !== i + 1) {
|
|
382088
|
+
return { valid: false, brokenAt: record2.seq, detail: `seq mismatch: expected ${i + 1}, got ${record2.seq}` };
|
|
382089
|
+
}
|
|
382090
|
+
const expectedContentHash = computeRecordHash(
|
|
382091
|
+
record2.kind,
|
|
382092
|
+
record2.actor,
|
|
382093
|
+
record2.target,
|
|
382094
|
+
record2.summary,
|
|
382095
|
+
record2.detail,
|
|
382096
|
+
record2.at,
|
|
382097
|
+
record2.seq
|
|
382098
|
+
);
|
|
382099
|
+
const expectedHash = createHash("sha256").update(expectedContentHash + record2.prevHash).digest("hex");
|
|
382100
|
+
if (record2.hash !== expectedHash) {
|
|
382101
|
+
return { valid: false, brokenAt: record2.seq, detail: `hash mismatch at seq ${record2.seq}` };
|
|
382102
|
+
}
|
|
382103
|
+
const expectedPrevHash = i === 0 ? GENESIS_HASH : this.records[i - 1].hash;
|
|
382104
|
+
if (record2.prevHash !== expectedPrevHash) {
|
|
382105
|
+
return { valid: false, brokenAt: record2.seq, detail: `prevHash mismatch at seq ${record2.seq}` };
|
|
382106
|
+
}
|
|
382107
|
+
}
|
|
382108
|
+
return { valid: true };
|
|
382109
|
+
}
|
|
382110
|
+
/** Get the current chain tip (the latest record's hash). */
|
|
382111
|
+
tip() {
|
|
382112
|
+
if (this.records.length === 0) return GENESIS_HASH;
|
|
382113
|
+
return this.records[this.records.length - 1].hash;
|
|
382114
|
+
}
|
|
382115
|
+
/** Get the total number of records. */
|
|
382116
|
+
size() {
|
|
382117
|
+
return this.records.length;
|
|
382118
|
+
}
|
|
382119
|
+
/** Export all records as JSON (for persistence). */
|
|
382120
|
+
export() {
|
|
382121
|
+
return JSON.stringify(this.records, null, 2);
|
|
382122
|
+
}
|
|
382123
|
+
/** Import records from JSON (for recovery). Verifies the chain on import. */
|
|
382124
|
+
import(json3) {
|
|
382125
|
+
let parsed;
|
|
382126
|
+
try {
|
|
382127
|
+
parsed = JSON.parse(json3);
|
|
382128
|
+
} catch {
|
|
382129
|
+
return { imported: 0, valid: false, detail: "invalid JSON" };
|
|
382130
|
+
}
|
|
382131
|
+
if (!Array.isArray(parsed)) {
|
|
382132
|
+
return { imported: 0, valid: false, detail: "not an array" };
|
|
382133
|
+
}
|
|
382134
|
+
const tempLedger = new _AuditLedger({ hashFn: this.hashFn, now: this.now });
|
|
382135
|
+
tempLedger.records = parsed;
|
|
382136
|
+
const result = tempLedger.verify();
|
|
382137
|
+
if (!result.valid) {
|
|
382138
|
+
return { imported: 0, valid: false, detail: result.detail };
|
|
382139
|
+
}
|
|
382140
|
+
this.records = parsed;
|
|
382141
|
+
return { imported: parsed.length, valid: true };
|
|
382142
|
+
}
|
|
382143
|
+
};
|
|
382144
|
+
|
|
382145
|
+
// ../../packages/backend/src/services/audit/evidenceSealer.ts
|
|
382146
|
+
import { createHash as createHash2 } from "crypto";
|
|
382147
|
+
function computeMerkleRoot(hashes) {
|
|
382148
|
+
if (hashes.length === 0) return "0".repeat(64);
|
|
382149
|
+
if (hashes.length === 1) return hashes[0];
|
|
382150
|
+
const nextLevel = [];
|
|
382151
|
+
for (let i = 0; i < hashes.length; i += 2) {
|
|
382152
|
+
const left = hashes[i];
|
|
382153
|
+
const right = i + 1 < hashes.length ? hashes[i + 1] : left;
|
|
382154
|
+
nextLevel.push(createHash2("sha256").update(left + right).digest("hex"));
|
|
382155
|
+
}
|
|
382156
|
+
return computeMerkleRoot(nextLevel);
|
|
382157
|
+
}
|
|
382158
|
+
function verifyMerkleRoot(hashes, expectedRoot) {
|
|
382159
|
+
return computeMerkleRoot(hashes) === expectedRoot;
|
|
382160
|
+
}
|
|
382161
|
+
var EvidenceSealer = class {
|
|
382162
|
+
bundles = [];
|
|
382163
|
+
now;
|
|
382164
|
+
constructor(deps = {}) {
|
|
382165
|
+
this.now = deps.now ?? (() => Date.now());
|
|
382166
|
+
}
|
|
382167
|
+
/** Seal the current state of the audit ledger into an evidence bundle. */
|
|
382168
|
+
seal(ledger, sealId) {
|
|
382169
|
+
const records = ledger.list();
|
|
382170
|
+
const recordHashes = records.map((r) => r.hash);
|
|
382171
|
+
const merkleRoot = computeMerkleRoot(recordHashes);
|
|
382172
|
+
const bundle = {
|
|
382173
|
+
merkleRoot,
|
|
382174
|
+
recordCount: records.length,
|
|
382175
|
+
fromAt: records.length > 0 ? records[0].at : 0,
|
|
382176
|
+
toAt: records.length > 0 ? records[records.length - 1].at : 0,
|
|
382177
|
+
tipHash: ledger.tip(),
|
|
382178
|
+
recordHashes,
|
|
382179
|
+
sealedAt: this.now(),
|
|
382180
|
+
sealId: sealId ?? `seal-${Date.now().toString(36)}`
|
|
382181
|
+
};
|
|
382182
|
+
this.bundles.push(bundle);
|
|
382183
|
+
return bundle;
|
|
382184
|
+
}
|
|
382185
|
+
/** Verify a sealed bundle against a set of records. */
|
|
382186
|
+
verify(bundle, records) {
|
|
382187
|
+
if (records.length !== bundle.recordCount) {
|
|
382188
|
+
return { valid: false, detail: `record count mismatch: expected ${bundle.recordCount}, got ${records.length}` };
|
|
382189
|
+
}
|
|
382190
|
+
if (records.length > 0 && records[records.length - 1].hash !== bundle.tipHash) {
|
|
382191
|
+
return { valid: false, detail: `tip hash mismatch` };
|
|
382192
|
+
}
|
|
382193
|
+
const hashes = records.map((r) => r.hash);
|
|
382194
|
+
if (!verifyMerkleRoot(hashes, bundle.merkleRoot)) {
|
|
382195
|
+
return { valid: false, detail: `Merkle root mismatch` };
|
|
382196
|
+
}
|
|
382197
|
+
return { valid: true };
|
|
382198
|
+
}
|
|
382199
|
+
/** Get all sealed bundles. */
|
|
382200
|
+
listBundles() {
|
|
382201
|
+
return [...this.bundles];
|
|
382202
|
+
}
|
|
382203
|
+
/** Get the latest sealed bundle. */
|
|
382204
|
+
latest() {
|
|
382205
|
+
return this.bundles.length > 0 ? this.bundles[this.bundles.length - 1] : void 0;
|
|
382206
|
+
}
|
|
382207
|
+
};
|
|
382208
|
+
|
|
382014
382209
|
// ../../packages/backend/src/services/observability.ts
|
|
382015
382210
|
function createObservability(deps) {
|
|
382016
382211
|
const log = deps.onLog ?? (() => {
|
|
@@ -382087,6 +382282,8 @@ function createObservability(deps) {
|
|
|
382087
382282
|
const aperfService = new AperfService({
|
|
382088
382283
|
execSsh: async () => ""
|
|
382089
382284
|
});
|
|
382285
|
+
const auditLedger = new AuditLedger({});
|
|
382286
|
+
const evidenceSealer = new EvidenceSealer({});
|
|
382090
382287
|
const pluginScanRoot = (process.env.GYBACKEND_DATA_DIR ?? "./.gybackend-data") + "/plugins";
|
|
382091
382288
|
const pluginRegistry = new PluginRegistry({
|
|
382092
382289
|
scanRoots: [pluginScanRoot, "./plugins"],
|
|
@@ -382139,6 +382336,7 @@ function createObservability(deps) {
|
|
|
382139
382336
|
dagu: { parseDaguYaml, parseDaguWorkflow, daguExecutionPlan },
|
|
382140
382337
|
notify: { slackChannel, teamsChannel, smtpChannel, telegramChannel },
|
|
382141
382338
|
aperf: { service: aperfService, toMetricPoint: aperfSummaryToMetricPoint },
|
|
382339
|
+
audit: { ledger: auditLedger, sealer: evidenceSealer },
|
|
382142
382340
|
pluginRegistry
|
|
382143
382341
|
};
|
|
382144
382342
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rterm-backend",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.2",
|
|
4
4
|
"description": "RTerm headless backend — run RTerm-as-a-service (AI agent, SSH/WinRM/Serial/local terminals, fleet orchestration, advanced automation with event-driven triggers + NATS event mesh, scheduled automation, SRE observability, Netdata integration, AWS APerf performance deep-dive, plugin system) and drive it over a WebSocket JSON-RPC gateway. No desktop UI required.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|