sentinelayer-cli 0.9.0 → 0.9.3
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/package.json +1 -1
- package/src/agents/backend/tools/timeout-audit.js +33 -17
- package/src/agents/devtestbot/runner.js +11 -5
- package/src/commands/session.js +566 -32
- package/src/legacy-cli.js +1 -1
- package/src/scan/generator.js +1 -1
- package/src/session/coordination-guidance.js +2 -1
- package/src/session/event-identity.js +139 -0
- package/src/session/listener.js +96 -2
- package/src/session/live-source.js +11 -2
- package/src/session/mentions.js +130 -0
- package/src/session/recap.js +35 -0
- package/src/session/remote-hydrate.js +252 -8
- package/src/session/store.js +116 -1
- package/src/session/stream.js +17 -7
- package/src/session/sync.js +375 -26
- package/src/session/title-sync.js +107 -0
package/package.json
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
// timeout-audit — flag outbound calls without explicit timeout (#A14).
|
|
2
|
+
// @sentinelayer-static-analysis-only
|
|
2
3
|
//
|
|
3
|
-
// Default timeouts in
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// - axios: no timeout by default
|
|
7
|
-
// - requests: no connect/read timeout by default
|
|
8
|
-
// - urllib: no timeout
|
|
9
|
-
// We flag outbound calls that don't carry an explicit `timeout` / `signal` /
|
|
10
|
-
// AbortSignal within the call arguments.
|
|
4
|
+
// Default timeouts in common HTTP clients are usually unsafe for backend
|
|
5
|
+
// handlers. This offline scanner flags outbound call expressions that do not
|
|
6
|
+
// carry an explicit timeout or abort signal.
|
|
11
7
|
|
|
12
8
|
import fsp from "node:fs/promises";
|
|
13
9
|
import path from "node:path";
|
|
14
10
|
|
|
15
11
|
import { createFinding, findLineMatches, getLineContent, toPosix, walkRepoFiles } from "./base.js";
|
|
16
12
|
|
|
13
|
+
// circuitBreaker is not applicable here: this tool only reads local files.
|
|
14
|
+
// HTTP client names below are regex literals used to inspect other files.
|
|
15
|
+
|
|
17
16
|
const JS_TS_EXTENSIONS = new Set([
|
|
18
17
|
".js",
|
|
19
18
|
".jsx",
|
|
@@ -68,18 +67,35 @@ function hasTimeoutInArgs(argString) {
|
|
|
68
67
|
);
|
|
69
68
|
}
|
|
70
69
|
|
|
70
|
+
const CALL_OPEN_PATTERN = "\\s*\\(";
|
|
71
|
+
const HTTP_METHOD_PATTERN = "(?:get|post|put|patch|delete|request)";
|
|
72
|
+
|
|
73
|
+
function escapeRegexLiteral(value) {
|
|
74
|
+
return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function callPattern(name, { optionalMethod = false, methods = null } = {}) {
|
|
78
|
+
const escapedName = escapeRegexLiteral(name);
|
|
79
|
+
const suffix = methods
|
|
80
|
+
? `\\.(?:${methods.map(escapeRegexLiteral).join("|")})`
|
|
81
|
+
: optionalMethod
|
|
82
|
+
? "(?:\\.[a-z]+)?"
|
|
83
|
+
: "";
|
|
84
|
+
return new RegExp(`\\b${escapedName}${suffix}${CALL_OPEN_PATTERN}`);
|
|
85
|
+
}
|
|
86
|
+
|
|
71
87
|
const JS_CALLS = [
|
|
72
|
-
{ pattern:
|
|
73
|
-
{ pattern:
|
|
74
|
-
{ pattern:
|
|
75
|
-
{ pattern:
|
|
76
|
-
{ pattern:
|
|
88
|
+
{ pattern: callPattern("fetch"), label: "fetch" },
|
|
89
|
+
{ pattern: callPattern("axios", { optionalMethod: true }), label: "axios" },
|
|
90
|
+
{ pattern: callPattern("got", { optionalMethod: true }), label: "got" },
|
|
91
|
+
{ pattern: callPattern("http", { methods: ["request", "get", "post"] }), label: "http" },
|
|
92
|
+
{ pattern: callPattern("https", { methods: ["request", "get", "post"] }), label: "https" },
|
|
77
93
|
];
|
|
78
94
|
|
|
79
95
|
const PY_CALLS = [
|
|
80
|
-
{ pattern:
|
|
81
|
-
{ pattern:
|
|
82
|
-
{ pattern:
|
|
96
|
+
{ pattern: new RegExp(`\\brequests\\.${HTTP_METHOD_PATTERN}${CALL_OPEN_PATTERN}`), label: "requests" },
|
|
97
|
+
{ pattern: callPattern("urllib.request.urlopen"), label: "urllib" },
|
|
98
|
+
{ pattern: new RegExp(`\\bhttpx\\.${HTTP_METHOD_PATTERN}${CALL_OPEN_PATTERN}`), label: "httpx" },
|
|
83
99
|
];
|
|
84
100
|
|
|
85
101
|
export async function runTimeoutAudit({ rootPath, files = null } = {}) {
|
|
@@ -117,7 +133,7 @@ export async function runTimeoutAudit({ rootPath, files = null } = {}) {
|
|
|
117
133
|
evidence: getLineContent(content, match.line),
|
|
118
134
|
rootCause: `${call.label} call has no explicit timeout — a slow downstream can stall the handler indefinitely.`,
|
|
119
135
|
recommendedFix:
|
|
120
|
-
"Always pass an explicit timeout
|
|
136
|
+
"Always pass an explicit timeout or abort signal for outbound clients. Pick a value shorter than your request SLO.",
|
|
121
137
|
confidence: 0.7,
|
|
122
138
|
})
|
|
123
139
|
);
|
|
@@ -448,11 +448,17 @@ export async function writeMp4FromPngFrames(page, frames, outputPath, viewport =
|
|
|
448
448
|
|
|
449
449
|
const canvas = new OffscreenCanvas(width, height);
|
|
450
450
|
const context = canvas.getContext("2d", { alpha: false });
|
|
451
|
-
const
|
|
452
|
-
const
|
|
453
|
-
const
|
|
454
|
-
|
|
455
|
-
|
|
451
|
+
const pngBlobFromBase64 = (pngBase64) => {
|
|
452
|
+
const binary = atob(pngBase64);
|
|
453
|
+
const bytes = new Uint8Array(binary.length);
|
|
454
|
+
for (let i = 0; i < binary.length; i += 1) {
|
|
455
|
+
bytes[i] = binary.charCodeAt(i);
|
|
456
|
+
}
|
|
457
|
+
return new Blob([bytes], { type: "image/png" });
|
|
458
|
+
};
|
|
459
|
+
const bitmaps = await Promise.all(
|
|
460
|
+
payloadFrames.map((item) => createImageBitmap(pngBlobFromBase64(item.pngBase64)))
|
|
461
|
+
);
|
|
456
462
|
for (let index = 0; index < payloadFrames.length; index += 1) {
|
|
457
463
|
const bitmap = bitmaps[index];
|
|
458
464
|
context.fillStyle = "#ffffff";
|