procsi 0.4.1 → 0.6.0
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/cli/commands/on.d.ts.map +1 -1
- package/dist/cli/commands/on.js +1 -0
- package/dist/cli/commands/on.js.map +1 -1
- package/dist/cli/commands/request.d.ts.map +1 -1
- package/dist/cli/commands/request.js +97 -2
- package/dist/cli/commands/request.js.map +1 -1
- package/dist/cli/commands/requests.d.ts +25 -0
- package/dist/cli/commands/requests.d.ts.map +1 -1
- package/dist/cli/commands/requests.js +35 -4
- package/dist/cli/commands/requests.js.map +1 -1
- package/dist/cli/formatters/detail.d.ts.map +1 -1
- package/dist/cli/formatters/detail.js +6 -0
- package/dist/cli/formatters/detail.js.map +1 -1
- package/dist/cli/formatters/table.d.ts.map +1 -1
- package/dist/cli/formatters/table.js +11 -1
- package/dist/cli/formatters/table.js.map +1 -1
- package/dist/cli/tui/App.d.ts.map +1 -1
- package/dist/cli/tui/App.js +81 -28
- package/dist/cli/tui/App.js.map +1 -1
- package/dist/cli/tui/components/AccordionPanel.d.ts.map +1 -1
- package/dist/cli/tui/components/AccordionPanel.js +2 -1
- package/dist/cli/tui/components/AccordionPanel.js.map +1 -1
- package/dist/cli/tui/components/FilterBar.d.ts +10 -2
- package/dist/cli/tui/components/FilterBar.d.ts.map +1 -1
- package/dist/cli/tui/components/FilterBar.js +100 -12
- package/dist/cli/tui/components/FilterBar.js.map +1 -1
- package/dist/cli/tui/components/HelpModal.d.ts +6 -2
- package/dist/cli/tui/components/HelpModal.d.ts.map +1 -1
- package/dist/cli/tui/components/HelpModal.js +19 -46
- package/dist/cli/tui/components/HelpModal.js.map +1 -1
- package/dist/cli/tui/components/RequestListItem.d.ts +8 -0
- package/dist/cli/tui/components/RequestListItem.d.ts.map +1 -1
- package/dist/cli/tui/components/RequestListItem.js +14 -2
- package/dist/cli/tui/components/RequestListItem.js.map +1 -1
- package/dist/cli/tui/components/StatusBar.d.ts +2 -9
- package/dist/cli/tui/components/StatusBar.d.ts.map +1 -1
- package/dist/cli/tui/components/StatusBar.js +7 -13
- package/dist/cli/tui/components/StatusBar.js.map +1 -1
- package/dist/cli/tui/hooks/useExport.d.ts +10 -2
- package/dist/cli/tui/hooks/useExport.d.ts.map +1 -1
- package/dist/cli/tui/hooks/useExport.js +33 -8
- package/dist/cli/tui/hooks/useExport.js.map +1 -1
- package/dist/cli/tui/hooks/useRequests.d.ts +4 -1
- package/dist/cli/tui/hooks/useRequests.d.ts.map +1 -1
- package/dist/cli/tui/hooks/useRequests.js +36 -9
- package/dist/cli/tui/hooks/useRequests.js.map +1 -1
- package/dist/cli/tui/hooks/useStdoutDimensions.d.ts +4 -0
- package/dist/cli/tui/hooks/useStdoutDimensions.d.ts.map +1 -1
- package/dist/cli/tui/hooks/useStdoutDimensions.js +20 -10
- package/dist/cli/tui/hooks/useStdoutDimensions.js.map +1 -1
- package/dist/cli/tui/utils/curl.d.ts.map +1 -1
- package/dist/cli/tui/utils/curl.js +6 -13
- package/dist/cli/tui/utils/curl.js.map +1 -1
- package/dist/cli/tui/utils/export-shared.d.ts +9 -0
- package/dist/cli/tui/utils/export-shared.d.ts.map +1 -0
- package/dist/cli/tui/utils/export-shared.js +15 -0
- package/dist/cli/tui/utils/export-shared.js.map +1 -0
- package/dist/cli/tui/utils/fetch.d.ts +9 -0
- package/dist/cli/tui/utils/fetch.d.ts.map +1 -0
- package/dist/cli/tui/utils/fetch.js +77 -0
- package/dist/cli/tui/utils/fetch.js.map +1 -0
- package/dist/cli/tui/utils/filters.d.ts.map +1 -1
- package/dist/cli/tui/utils/filters.js +1 -0
- package/dist/cli/tui/utils/filters.js.map +1 -1
- package/dist/cli/tui/utils/httpie.d.ts +9 -0
- package/dist/cli/tui/utils/httpie.d.ts.map +1 -0
- package/dist/cli/tui/utils/httpie.js +78 -0
- package/dist/cli/tui/utils/httpie.js.map +1 -0
- package/dist/cli/tui/utils/python-requests.d.ts +19 -0
- package/dist/cli/tui/utils/python-requests.d.ts.map +1 -0
- package/dist/cli/tui/utils/python-requests.js +101 -0
- package/dist/cli/tui/utils/python-requests.js.map +1 -0
- package/dist/cli/tui/utils/terminal-size.d.ts +20 -0
- package/dist/cli/tui/utils/terminal-size.d.ts.map +1 -0
- package/dist/cli/tui/utils/terminal-size.js +100 -0
- package/dist/cli/tui/utils/terminal-size.js.map +1 -0
- package/dist/daemon/control.d.ts +3 -0
- package/dist/daemon/control.d.ts.map +1 -1
- package/dist/daemon/control.js +199 -6
- package/dist/daemon/control.js.map +1 -1
- package/dist/daemon/index.js +7 -0
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/procsi-client.d.ts.map +1 -1
- package/dist/daemon/procsi-client.js +1 -0
- package/dist/daemon/procsi-client.js.map +1 -1
- package/dist/daemon/proxy.d.ts +3 -0
- package/dist/daemon/proxy.d.ts.map +1 -1
- package/dist/daemon/proxy.js +19 -6
- package/dist/daemon/proxy.js.map +1 -1
- package/dist/daemon/replay-tracker.d.ts +11 -0
- package/dist/daemon/replay-tracker.d.ts.map +1 -0
- package/dist/daemon/replay-tracker.js +47 -0
- package/dist/daemon/replay-tracker.js.map +1 -0
- package/dist/daemon/replay.d.ts +20 -0
- package/dist/daemon/replay.d.ts.map +1 -0
- package/dist/daemon/replay.js +113 -0
- package/dist/daemon/replay.js.map +1 -0
- package/dist/daemon/storage.d.ts +16 -2
- package/dist/daemon/storage.d.ts.map +1 -1
- package/dist/daemon/storage.js +104 -7
- package/dist/daemon/storage.js.map +1 -1
- package/dist/mcp/server.d.ts +3 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +183 -4
- package/dist/mcp/server.js.map +1 -1
- package/dist/shared/body-search.d.ts +28 -0
- package/dist/shared/body-search.d.ts.map +1 -0
- package/dist/shared/body-search.js +64 -0
- package/dist/shared/body-search.js.map +1 -0
- package/dist/shared/constants.d.ts +2 -0
- package/dist/shared/constants.d.ts.map +1 -1
- package/dist/shared/constants.js +2 -0
- package/dist/shared/constants.js.map +1 -1
- package/dist/shared/control-client.d.ts +23 -2
- package/dist/shared/control-client.d.ts.map +1 -1
- package/dist/shared/control-client.js +15 -2
- package/dist/shared/control-client.js.map +1 -1
- package/dist/shared/regex-filter.d.ts +35 -0
- package/dist/shared/regex-filter.d.ts.map +1 -0
- package/dist/shared/regex-filter.js +86 -0
- package/dist/shared/regex-filter.js.map +1 -0
- package/dist/shared/types.d.ts +13 -0
- package/dist/shared/types.d.ts.map +1 -1
- package/package.json +2 -1
- package/skills/procsi/SKILL.md +9 -4
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate HTTPie commands from captured requests.
|
|
3
|
+
*/
|
|
4
|
+
import { EXCLUDED_HEADERS } from "./export-shared.js";
|
|
5
|
+
import { isJsonContent } from "./content-type.js";
|
|
6
|
+
/**
|
|
7
|
+
* Escape a string for use in a shell single-quoted context.
|
|
8
|
+
* Same approach as curl: end the quote, insert an escaped single quote, reopen.
|
|
9
|
+
* Null bytes are stripped to prevent shell truncation.
|
|
10
|
+
*/
|
|
11
|
+
function shellEscape(str) {
|
|
12
|
+
return str.replace(/\0/g, "").replace(/'/g, "'\"'\"'");
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Generate an HTTPie command from a captured request.
|
|
16
|
+
*/
|
|
17
|
+
export function generateHttpie(request) {
|
|
18
|
+
const parts = ["http"];
|
|
19
|
+
// HTTPie uses the method as the first argument (defaults to GET without body, POST with body)
|
|
20
|
+
// Always include the method for clarity
|
|
21
|
+
parts.push(request.method);
|
|
22
|
+
// Add URL
|
|
23
|
+
parts.push(`'${shellEscape(request.url)}'`);
|
|
24
|
+
// Add headers using Name:Value syntax
|
|
25
|
+
for (const [name, value] of Object.entries(request.requestHeaders)) {
|
|
26
|
+
if (EXCLUDED_HEADERS.has(name.toLowerCase())) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
parts.push(`'${shellEscape(name)}:${shellEscape(value)}'`);
|
|
30
|
+
}
|
|
31
|
+
// Add body if present and method is not GET/HEAD
|
|
32
|
+
if (request.requestBody &&
|
|
33
|
+
request.requestBody.length > 0 &&
|
|
34
|
+
request.method !== "GET" &&
|
|
35
|
+
request.method !== "HEAD") {
|
|
36
|
+
const bodyStr = request.requestBody.toString("utf-8");
|
|
37
|
+
const contentType = request.requestHeaders["content-type"];
|
|
38
|
+
if (isJsonContent(contentType)) {
|
|
39
|
+
try {
|
|
40
|
+
const parsed = JSON.parse(bodyStr);
|
|
41
|
+
// Only use key=value / key:=value syntax for flat objects
|
|
42
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
43
|
+
const entries = Object.entries(parsed);
|
|
44
|
+
const allFlat = entries.every(([, v]) => typeof v === "string" || typeof v === "number" || typeof v === "boolean" || v === null);
|
|
45
|
+
if (allFlat) {
|
|
46
|
+
for (const [key, val] of entries) {
|
|
47
|
+
if (typeof val === "string") {
|
|
48
|
+
// key=value for strings
|
|
49
|
+
parts.push(`'${shellEscape(key)}=${shellEscape(val)}'`);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
// key:=value for non-strings (numbers, booleans, null)
|
|
53
|
+
parts.push(`'${shellEscape(key)}:=${JSON.stringify(val)}'`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
// Non-flat object — use --raw
|
|
59
|
+
parts.push(`--raw='${shellEscape(bodyStr)}'`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
// Non-object JSON (array, primitive) — use --raw
|
|
64
|
+
parts.push(`--raw='${shellEscape(bodyStr)}'`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// Invalid JSON — use --raw
|
|
69
|
+
parts.push(`--raw='${shellEscape(bodyStr)}'`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
parts.push(`--raw='${shellEscape(bodyStr)}'`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return parts.join(" \\\n ");
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=httpie.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"httpie.js","sourceRoot":"","sources":["../../../../src/cli/tui/utils/httpie.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate Python requests library calls from captured requests.
|
|
3
|
+
*/
|
|
4
|
+
import type { CapturedRequest } from "../../../shared/types.js";
|
|
5
|
+
/**
|
|
6
|
+
* Escape a string for use inside a Python single-quoted string literal.
|
|
7
|
+
* Null bytes are stripped to prevent interpreter issues.
|
|
8
|
+
*/
|
|
9
|
+
export declare function escapePythonString(str: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Convert a JavaScript value to its Python repr equivalent.
|
|
12
|
+
* Handles strings, numbers, booleans, null, arrays, and objects.
|
|
13
|
+
*/
|
|
14
|
+
export declare function pythonRepr(value: unknown): string;
|
|
15
|
+
/**
|
|
16
|
+
* Generate a Python requests call from a captured request.
|
|
17
|
+
*/
|
|
18
|
+
export declare function generatePythonRequests(request: CapturedRequest): string;
|
|
19
|
+
//# sourceMappingURL=python-requests.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"python-requests.d.ts","sourceRoot":"","sources":["../../../../src/cli/tui/utils/python-requests.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAIhE;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAQtD;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CA0BjD;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CAwDvE"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate Python requests library calls from captured requests.
|
|
3
|
+
*/
|
|
4
|
+
import { EXCLUDED_HEADERS } from "./export-shared.js";
|
|
5
|
+
import { isJsonContent } from "./content-type.js";
|
|
6
|
+
/**
|
|
7
|
+
* Escape a string for use inside a Python single-quoted string literal.
|
|
8
|
+
* Null bytes are stripped to prevent interpreter issues.
|
|
9
|
+
*/
|
|
10
|
+
export function escapePythonString(str) {
|
|
11
|
+
return str
|
|
12
|
+
.replace(/\0/g, "")
|
|
13
|
+
.replace(/\\/g, "\\\\")
|
|
14
|
+
.replace(/'/g, "\\'")
|
|
15
|
+
.replace(/\n/g, "\\n")
|
|
16
|
+
.replace(/\r/g, "\\r")
|
|
17
|
+
.replace(/\t/g, "\\t");
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Convert a JavaScript value to its Python repr equivalent.
|
|
21
|
+
* Handles strings, numbers, booleans, null, arrays, and objects.
|
|
22
|
+
*/
|
|
23
|
+
export function pythonRepr(value) {
|
|
24
|
+
if (value === null || value === undefined) {
|
|
25
|
+
return "None";
|
|
26
|
+
}
|
|
27
|
+
if (value === true) {
|
|
28
|
+
return "True";
|
|
29
|
+
}
|
|
30
|
+
if (value === false) {
|
|
31
|
+
return "False";
|
|
32
|
+
}
|
|
33
|
+
if (typeof value === "number") {
|
|
34
|
+
return String(value);
|
|
35
|
+
}
|
|
36
|
+
if (typeof value === "string") {
|
|
37
|
+
return `'${escapePythonString(value)}'`;
|
|
38
|
+
}
|
|
39
|
+
if (Array.isArray(value)) {
|
|
40
|
+
const items = value.map((item) => pythonRepr(item));
|
|
41
|
+
return `[${items.join(", ")}]`;
|
|
42
|
+
}
|
|
43
|
+
if (typeof value === "object") {
|
|
44
|
+
const entries = Object.entries(value);
|
|
45
|
+
const pairs = entries.map(([k, v]) => `${pythonRepr(k)}: ${pythonRepr(v)}`);
|
|
46
|
+
return `{${pairs.join(", ")}}`;
|
|
47
|
+
}
|
|
48
|
+
return String(value);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Generate a Python requests call from a captured request.
|
|
52
|
+
*/
|
|
53
|
+
export function generatePythonRequests(request) {
|
|
54
|
+
const lines = ["import requests", ""];
|
|
55
|
+
const method = request.method.toLowerCase();
|
|
56
|
+
const args = [`'${escapePythonString(request.url)}'`];
|
|
57
|
+
// Collect headers (excluding automatic ones)
|
|
58
|
+
const headers = [];
|
|
59
|
+
const contentType = request.requestHeaders["content-type"];
|
|
60
|
+
const usingJsonKwarg = isJsonContent(contentType);
|
|
61
|
+
for (const [name, value] of Object.entries(request.requestHeaders)) {
|
|
62
|
+
if (EXCLUDED_HEADERS.has(name.toLowerCase())) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
// When using json= kwarg, requests sets content-type automatically
|
|
66
|
+
if (usingJsonKwarg && name.toLowerCase() === "content-type") {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
headers.push([name, value]);
|
|
70
|
+
}
|
|
71
|
+
if (headers.length > 0) {
|
|
72
|
+
const headerPairs = headers
|
|
73
|
+
.map(([k, v]) => ` '${escapePythonString(k)}': '${escapePythonString(v)}'`)
|
|
74
|
+
.join(",\n");
|
|
75
|
+
args.push(`headers={\n${headerPairs}\n}`);
|
|
76
|
+
}
|
|
77
|
+
// Add body if present and method is not GET/HEAD
|
|
78
|
+
if (request.requestBody &&
|
|
79
|
+
request.requestBody.length > 0 &&
|
|
80
|
+
request.method !== "GET" &&
|
|
81
|
+
request.method !== "HEAD") {
|
|
82
|
+
const bodyStr = request.requestBody.toString("utf-8");
|
|
83
|
+
if (usingJsonKwarg) {
|
|
84
|
+
try {
|
|
85
|
+
const parsed = JSON.parse(bodyStr);
|
|
86
|
+
args.push(`json=${pythonRepr(parsed)}`);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// Invalid JSON — fall back to data kwarg
|
|
90
|
+
args.push(`data='${escapePythonString(bodyStr)}'`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
args.push(`data='${escapePythonString(bodyStr)}'`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const argsStr = args.length === 1 ? args[0] : `\n ${args.join(",\n ")}\n`;
|
|
98
|
+
lines.push(`response = requests.${method}(${argsStr})`);
|
|
99
|
+
return lines.join("\n");
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=python-requests.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"python-requests.js","sourceRoot":"","sources":["../../../../src/cli/tui/utils/python-requests.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
interface StdoutLike {
|
|
2
|
+
columns?: number;
|
|
3
|
+
rows?: number;
|
|
4
|
+
getWindowSize?: () => [number, number];
|
|
5
|
+
emit?: (event: string) => boolean;
|
|
6
|
+
}
|
|
7
|
+
type ReadFallbackDimensions = () => [number, number] | undefined;
|
|
8
|
+
/**
|
|
9
|
+
* Synchronise stdout.columns/rows and emit a resize event so Ink recalculates
|
|
10
|
+
* layout using current terminal dimensions.
|
|
11
|
+
*
|
|
12
|
+
* Primary source: stdout.getWindowSize().
|
|
13
|
+
* Fallback sources: `stty size` against /dev/tty, then COLUMNS/LINES env vars
|
|
14
|
+
* (for terminals where Node's width/height sources lag until later interaction).
|
|
15
|
+
*
|
|
16
|
+
* Returns true when valid dimensions were applied.
|
|
17
|
+
*/
|
|
18
|
+
export declare function syncStdoutDimensions(stdout: StdoutLike, readFallbackDimensions?: ReadFallbackDimensions): boolean;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=terminal-size.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-size.d.ts","sourceRoot":"","sources":["../../../../src/cli/tui/utils/terminal-size.ts"],"names":[],"mappings":"AAGA,UAAU,UAAU;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;CACnC;AAED,KAAK,sBAAsB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;AA2EjE;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,UAAU,EAClB,sBAAsB,GAAE,sBAA+C,GACtE,OAAO,CA4BT"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { closeSync, openSync } from "node:fs";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
function isPositiveInteger(value) {
|
|
4
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
5
|
+
}
|
|
6
|
+
function hasValidDimensions(dimensions) {
|
|
7
|
+
if (!dimensions)
|
|
8
|
+
return false;
|
|
9
|
+
const [columns, rows] = dimensions;
|
|
10
|
+
return isPositiveInteger(columns) && isPositiveInteger(rows);
|
|
11
|
+
}
|
|
12
|
+
function readDimensionsFromWindowSize(stdout) {
|
|
13
|
+
if (typeof stdout.getWindowSize !== "function") {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
return stdout.getWindowSize();
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function readDimensionsFromEnv() {
|
|
24
|
+
const columns = Number(process.env["COLUMNS"]);
|
|
25
|
+
const rows = Number(process.env["LINES"]);
|
|
26
|
+
if (!isPositiveInteger(columns) || !isPositiveInteger(rows)) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
return [columns, rows];
|
|
30
|
+
}
|
|
31
|
+
function readDimensionsFromStty() {
|
|
32
|
+
if (process.platform === "win32") {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
let ttyFd;
|
|
36
|
+
try {
|
|
37
|
+
ttyFd = openSync("/dev/tty", "r");
|
|
38
|
+
const result = spawnSync("stty", ["size"], {
|
|
39
|
+
stdio: [ttyFd, "pipe", "ignore"],
|
|
40
|
+
encoding: "utf-8",
|
|
41
|
+
});
|
|
42
|
+
if (result.status !== 0 || !result.stdout) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
const output = result.stdout.trim();
|
|
46
|
+
const parts = output.split(/\s+/);
|
|
47
|
+
if (parts.length !== 2) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
const rows = Number(parts[0]);
|
|
51
|
+
const columns = Number(parts[1]);
|
|
52
|
+
if (!isPositiveInteger(columns) || !isPositiveInteger(rows)) {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
return [columns, rows];
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
if (ttyFd !== undefined) {
|
|
62
|
+
closeSync(ttyFd);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Synchronise stdout.columns/rows and emit a resize event so Ink recalculates
|
|
68
|
+
* layout using current terminal dimensions.
|
|
69
|
+
*
|
|
70
|
+
* Primary source: stdout.getWindowSize().
|
|
71
|
+
* Fallback sources: `stty size` against /dev/tty, then COLUMNS/LINES env vars
|
|
72
|
+
* (for terminals where Node's width/height sources lag until later interaction).
|
|
73
|
+
*
|
|
74
|
+
* Returns true when valid dimensions were applied.
|
|
75
|
+
*/
|
|
76
|
+
export function syncStdoutDimensions(stdout, readFallbackDimensions = readDimensionsFromStty) {
|
|
77
|
+
const fromFallback = readFallbackDimensions();
|
|
78
|
+
const fromEnv = readDimensionsFromEnv();
|
|
79
|
+
const fromWindowSize = readDimensionsFromWindowSize(stdout);
|
|
80
|
+
// Prefer /dev/tty + stty when available: in some terminals this reports
|
|
81
|
+
// the correct startup size earlier than Node's tty stream values.
|
|
82
|
+
const dimensions = hasValidDimensions(fromFallback)
|
|
83
|
+
? fromFallback
|
|
84
|
+
: hasValidDimensions(fromEnv)
|
|
85
|
+
? fromEnv
|
|
86
|
+
: fromWindowSize;
|
|
87
|
+
if (!hasValidDimensions(dimensions)) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
const [columns, rows] = dimensions;
|
|
91
|
+
stdout.columns = columns;
|
|
92
|
+
stdout.rows = rows;
|
|
93
|
+
process.env["COLUMNS"] = String(columns);
|
|
94
|
+
process.env["LINES"] = String(rows);
|
|
95
|
+
if (typeof stdout.emit === "function") {
|
|
96
|
+
stdout.emit("resize");
|
|
97
|
+
}
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=terminal-size.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-size.js","sourceRoot":"","sources":["../../../../src/cli/tui/utils/terminal-size.ts"],"names":[],"mappings":"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"}
|
package/dist/daemon/control.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { RequestRepository } from "./storage.js";
|
|
2
2
|
import type { InterceptorLoader } from "./interceptor-loader.js";
|
|
3
|
+
import type { ReplayTracker } from "./replay-tracker.js";
|
|
3
4
|
import type { InterceptorEventLog } from "./interceptor-event-log.js";
|
|
4
5
|
import { type LogLevel } from "../shared/logger.js";
|
|
5
6
|
export { ControlClient } from "../shared/control-client.js";
|
|
@@ -12,6 +13,8 @@ export interface ControlServerOptions {
|
|
|
12
13
|
logLevel?: LogLevel;
|
|
13
14
|
interceptorLoader?: InterceptorLoader;
|
|
14
15
|
interceptorEventLog?: InterceptorEventLog;
|
|
16
|
+
replayTracker?: ReplayTracker;
|
|
17
|
+
caCertPem?: string;
|
|
15
18
|
}
|
|
16
19
|
export interface ControlServer {
|
|
17
20
|
close: () => Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control.d.ts","sourceRoot":"","sources":["../../src/daemon/control.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"control.d.ts","sourceRoot":"","sources":["../../src/daemon/control.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAiBzD,OAAO,KAAK,EAAE,mBAAmB,EAAe,MAAM,4BAA4B,CAAC;AAMnF,OAAO,EAAgB,KAAK,QAAQ,EAAe,MAAM,qBAAqB,CAAC;AAI/E,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAI5D,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAC1C,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAySD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CAoXhF"}
|
package/dist/daemon/control.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import * as net from "node:net";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
|
+
import { randomBytes } from "node:crypto";
|
|
4
|
+
import { buildReplayHeaders, replayViaProxy } from "./replay.js";
|
|
3
5
|
import { MAX_BUFFER_SIZE, } from "../shared/control-client.js";
|
|
4
6
|
import { createLogger } from "../shared/logger.js";
|
|
5
7
|
import { resolveProcessName } from "../shared/process-name.js";
|
|
8
|
+
import { parseBodySearchTarget } from "../shared/body-search.js";
|
|
6
9
|
export { ControlClient } from "../shared/control-client.js";
|
|
10
|
+
const REPLAY_TOKEN_BYTES = 16;
|
|
7
11
|
/**
|
|
8
12
|
* Runtime type guard for incoming control messages.
|
|
9
13
|
*/
|
|
@@ -31,6 +35,97 @@ function requireString(params, key) {
|
|
|
31
35
|
}
|
|
32
36
|
return value;
|
|
33
37
|
}
|
|
38
|
+
function optionalStringRecord(params, key) {
|
|
39
|
+
const value = params[key];
|
|
40
|
+
if (value === undefined)
|
|
41
|
+
return undefined;
|
|
42
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
43
|
+
throw new Error(`Invalid ${key} parameter: expected object of string values`);
|
|
44
|
+
}
|
|
45
|
+
const result = {};
|
|
46
|
+
for (const [entryKey, entryValue] of Object.entries(value)) {
|
|
47
|
+
if (typeof entryValue !== "string") {
|
|
48
|
+
throw new Error(`Invalid ${key}.${entryKey}: expected string value`);
|
|
49
|
+
}
|
|
50
|
+
result[entryKey] = entryValue;
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
function optionalStringArray(params, key) {
|
|
55
|
+
const value = params[key];
|
|
56
|
+
if (value === undefined)
|
|
57
|
+
return undefined;
|
|
58
|
+
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
|
|
59
|
+
throw new Error(`Invalid ${key} parameter: expected array of strings`);
|
|
60
|
+
}
|
|
61
|
+
return value;
|
|
62
|
+
}
|
|
63
|
+
function optionalReplayInitiator(params, key) {
|
|
64
|
+
const value = params[key];
|
|
65
|
+
if (value === undefined)
|
|
66
|
+
return undefined;
|
|
67
|
+
if (value === "mcp" || value === "tui" || value === "cli") {
|
|
68
|
+
return value;
|
|
69
|
+
}
|
|
70
|
+
throw new Error(`Invalid ${key} parameter: expected "mcp", "tui", or "cli"`);
|
|
71
|
+
}
|
|
72
|
+
function optionalBodySearchTarget(params, key) {
|
|
73
|
+
const value = params[key];
|
|
74
|
+
if (value === undefined)
|
|
75
|
+
return undefined;
|
|
76
|
+
if (typeof value !== "string") {
|
|
77
|
+
throw new Error(`Invalid ${key} parameter: expected string`);
|
|
78
|
+
}
|
|
79
|
+
const target = parseBodySearchTarget(value);
|
|
80
|
+
if (!target) {
|
|
81
|
+
throw new Error(`Invalid ${key} parameter: "${value}". Use request, response, or both.`);
|
|
82
|
+
}
|
|
83
|
+
return target;
|
|
84
|
+
}
|
|
85
|
+
function optionalQueryTarget(params, key) {
|
|
86
|
+
const value = params[key];
|
|
87
|
+
if (value === undefined)
|
|
88
|
+
return undefined;
|
|
89
|
+
if (value === "request" || value === "response" || value === "both") {
|
|
90
|
+
return value;
|
|
91
|
+
}
|
|
92
|
+
throw new Error(`Invalid ${key} parameter: "${String(value)}". Use "request", "response", or "both".`);
|
|
93
|
+
}
|
|
94
|
+
const VALID_INTERCEPTOR_EVENT_LEVELS = new Set(["info", "warn", "error"]);
|
|
95
|
+
function optionalInterceptorEventLevel(params, key) {
|
|
96
|
+
const value = params[key];
|
|
97
|
+
if (value === undefined)
|
|
98
|
+
return undefined;
|
|
99
|
+
if (typeof value === "string" && VALID_INTERCEPTOR_EVENT_LEVELS.has(value)) {
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
throw new Error(`Invalid ${key} parameter: "${String(value)}". Use "info", "warn", or "error".`);
|
|
103
|
+
}
|
|
104
|
+
const VALID_INTERCEPTOR_EVENT_TYPES = new Set([
|
|
105
|
+
"matched",
|
|
106
|
+
"mocked",
|
|
107
|
+
"modified",
|
|
108
|
+
"observed",
|
|
109
|
+
"match_error",
|
|
110
|
+
"match_timeout",
|
|
111
|
+
"handler_error",
|
|
112
|
+
"handler_timeout",
|
|
113
|
+
"invalid_response",
|
|
114
|
+
"forward_after_complete",
|
|
115
|
+
"load_error",
|
|
116
|
+
"loaded",
|
|
117
|
+
"reload",
|
|
118
|
+
"user_log",
|
|
119
|
+
]);
|
|
120
|
+
function optionalInterceptorEventType(params, key) {
|
|
121
|
+
const value = params[key];
|
|
122
|
+
if (value === undefined)
|
|
123
|
+
return undefined;
|
|
124
|
+
if (typeof value === "string" && VALID_INTERCEPTOR_EVENT_TYPES.has(value)) {
|
|
125
|
+
return value;
|
|
126
|
+
}
|
|
127
|
+
throw new Error(`Invalid ${key} parameter: "${String(value)}". Expected a valid interceptor event type.`);
|
|
128
|
+
}
|
|
34
129
|
/**
|
|
35
130
|
* Validate an optional RequestFilter from untyped control message params.
|
|
36
131
|
*/
|
|
@@ -52,6 +147,12 @@ function optionalFilter(params) {
|
|
|
52
147
|
if (typeof f["search"] === "string") {
|
|
53
148
|
result.search = f["search"];
|
|
54
149
|
}
|
|
150
|
+
if (typeof f["regex"] === "string") {
|
|
151
|
+
result.regex = f["regex"];
|
|
152
|
+
}
|
|
153
|
+
if (typeof f["regexFlags"] === "string") {
|
|
154
|
+
result.regexFlags = f["regexFlags"];
|
|
155
|
+
}
|
|
55
156
|
if (typeof f["host"] === "string") {
|
|
56
157
|
result.host = f["host"];
|
|
57
158
|
}
|
|
@@ -86,11 +187,38 @@ function optionalFilter(params) {
|
|
|
86
187
|
}
|
|
87
188
|
return Object.keys(result).length > 0 ? result : undefined;
|
|
88
189
|
}
|
|
190
|
+
function parseOptionalReplayBody(params) {
|
|
191
|
+
const bodyText = optionalString(params, "body");
|
|
192
|
+
const bodyBase64 = optionalString(params, "bodyBase64");
|
|
193
|
+
if (bodyText !== undefined && bodyBase64 !== undefined) {
|
|
194
|
+
throw new Error('Provide either "body" or "bodyBase64", not both.');
|
|
195
|
+
}
|
|
196
|
+
if (bodyBase64 !== undefined) {
|
|
197
|
+
const BASE64_PATTERN = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
|
|
198
|
+
if (!BASE64_PATTERN.test(bodyBase64)) {
|
|
199
|
+
throw new Error("Invalid bodyBase64 parameter: expected valid base64 content");
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
body: Buffer.from(bodyBase64, "base64"),
|
|
203
|
+
hasExplicitBody: true,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
if (bodyText !== undefined) {
|
|
207
|
+
return {
|
|
208
|
+
body: Buffer.from(bodyText, "utf-8"),
|
|
209
|
+
hasExplicitBody: true,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
return { hasExplicitBody: false };
|
|
213
|
+
}
|
|
214
|
+
function createReplayToken() {
|
|
215
|
+
return randomBytes(REPLAY_TOKEN_BYTES).toString("hex");
|
|
216
|
+
}
|
|
89
217
|
/**
|
|
90
218
|
* Create a Unix socket control server for daemon communication.
|
|
91
219
|
*/
|
|
92
220
|
export function createControlServer(options) {
|
|
93
|
-
const { socketPath, storage, proxyPort, version, projectRoot, logLevel, interceptorLoader, interceptorEventLog, } = options;
|
|
221
|
+
const { socketPath, storage, proxyPort, version, projectRoot, logLevel, interceptorLoader, interceptorEventLog, replayTracker, caCertPem, } = options;
|
|
94
222
|
// Create logger if projectRoot is provided
|
|
95
223
|
const logger = projectRoot
|
|
96
224
|
? createLogger("control", projectRoot, logLevel)
|
|
@@ -101,7 +229,7 @@ export function createControlServer(options) {
|
|
|
101
229
|
}
|
|
102
230
|
const handlers = {
|
|
103
231
|
status: () => {
|
|
104
|
-
const
|
|
232
|
+
const sessionCount = storage.countSessions();
|
|
105
233
|
const requestCount = storage.countRequests();
|
|
106
234
|
const interceptorCount = interceptorLoader
|
|
107
235
|
? interceptorLoader.getInterceptors().length
|
|
@@ -109,7 +237,7 @@ export function createControlServer(options) {
|
|
|
109
237
|
return {
|
|
110
238
|
running: true,
|
|
111
239
|
proxyPort,
|
|
112
|
-
sessionCount
|
|
240
|
+
sessionCount,
|
|
113
241
|
requestCount,
|
|
114
242
|
version,
|
|
115
243
|
interceptorCount,
|
|
@@ -162,8 +290,10 @@ export function createControlServer(options) {
|
|
|
162
290
|
},
|
|
163
291
|
searchBodies: (params) => {
|
|
164
292
|
const query = requireString(params, "query");
|
|
293
|
+
const target = optionalBodySearchTarget(params, "target");
|
|
165
294
|
return storage.searchBodies({
|
|
166
295
|
query,
|
|
296
|
+
target,
|
|
167
297
|
limit: optionalNumber(params, "limit"),
|
|
168
298
|
offset: optionalNumber(params, "offset"),
|
|
169
299
|
filter: optionalFilter(params),
|
|
@@ -171,7 +301,7 @@ export function createControlServer(options) {
|
|
|
171
301
|
},
|
|
172
302
|
queryJsonBodies: (params) => {
|
|
173
303
|
const jsonPath = requireString(params, "jsonPath");
|
|
174
|
-
const target =
|
|
304
|
+
const target = optionalQueryTarget(params, "target");
|
|
175
305
|
return storage.queryJsonBodies({
|
|
176
306
|
jsonPath,
|
|
177
307
|
value: optionalString(params, "value"),
|
|
@@ -185,6 +315,69 @@ export function createControlServer(options) {
|
|
|
185
315
|
storage.clearRequests();
|
|
186
316
|
return { success: true };
|
|
187
317
|
},
|
|
318
|
+
replayRequest: async (params) => {
|
|
319
|
+
if (!replayTracker) {
|
|
320
|
+
throw new Error("Replay is not available: replay tracker not initialised");
|
|
321
|
+
}
|
|
322
|
+
const id = requireString(params, "id");
|
|
323
|
+
const original = storage.getRequest(id);
|
|
324
|
+
if (!original) {
|
|
325
|
+
throw new Error(`Request not found: ${id}`);
|
|
326
|
+
}
|
|
327
|
+
const method = optionalString(params, "method") ?? original.method;
|
|
328
|
+
const url = optionalString(params, "url") ?? original.url;
|
|
329
|
+
const setHeaders = optionalStringRecord(params, "setHeaders");
|
|
330
|
+
const removeHeaders = optionalStringArray(params, "removeHeaders");
|
|
331
|
+
const timeoutMs = optionalNumber(params, "timeoutMs");
|
|
332
|
+
const replayInitiator = optionalReplayInitiator(params, "initiator") ?? "mcp";
|
|
333
|
+
try {
|
|
334
|
+
// Validate URL early for clearer errors.
|
|
335
|
+
new URL(url);
|
|
336
|
+
}
|
|
337
|
+
catch {
|
|
338
|
+
throw new Error(`Invalid URL for replay: ${url}`);
|
|
339
|
+
}
|
|
340
|
+
const replayBody = parseOptionalReplayBody(params);
|
|
341
|
+
const body = replayBody.hasExplicitBody ? replayBody.body : original.requestBody;
|
|
342
|
+
const replayToken = createReplayToken();
|
|
343
|
+
replayTracker.register(replayToken, original.id, replayInitiator);
|
|
344
|
+
const headers = buildReplayHeaders({
|
|
345
|
+
baseHeaders: original.requestHeaders,
|
|
346
|
+
setHeaders,
|
|
347
|
+
removeHeaders,
|
|
348
|
+
replayToken,
|
|
349
|
+
});
|
|
350
|
+
try {
|
|
351
|
+
const replayStart = Date.now();
|
|
352
|
+
await replayViaProxy({
|
|
353
|
+
proxyPort,
|
|
354
|
+
method,
|
|
355
|
+
url,
|
|
356
|
+
headers,
|
|
357
|
+
body,
|
|
358
|
+
timeoutMs,
|
|
359
|
+
caCertPem,
|
|
360
|
+
});
|
|
361
|
+
const recent = storage.listRequestsSummary({ limit: 50 });
|
|
362
|
+
const replayed = recent.find((request) => request.replayedFromId === original.id &&
|
|
363
|
+
request.replayInitiator === replayInitiator &&
|
|
364
|
+
request.timestamp >= replayStart);
|
|
365
|
+
if (!replayed) {
|
|
366
|
+
throw new Error("Replay request completed but could not locate captured replay result");
|
|
367
|
+
}
|
|
368
|
+
return { requestId: replayed.id };
|
|
369
|
+
}
|
|
370
|
+
catch (err) {
|
|
371
|
+
logger?.error("Replay request failed", {
|
|
372
|
+
originalRequestId: id,
|
|
373
|
+
method,
|
|
374
|
+
url,
|
|
375
|
+
replayInitiator,
|
|
376
|
+
error: err instanceof Error ? err.message : "Unknown error",
|
|
377
|
+
});
|
|
378
|
+
throw err;
|
|
379
|
+
}
|
|
380
|
+
},
|
|
188
381
|
saveRequest: (params) => {
|
|
189
382
|
const id = requireString(params, "id");
|
|
190
383
|
const success = storage.bookmarkRequest(id);
|
|
@@ -229,9 +422,9 @@ export function createControlServer(options) {
|
|
|
229
422
|
}
|
|
230
423
|
const afterSeq = optionalNumber(params, "afterSeq") ?? 0;
|
|
231
424
|
const limit = optionalNumber(params, "limit");
|
|
232
|
-
const level =
|
|
425
|
+
const level = optionalInterceptorEventLevel(params, "level");
|
|
233
426
|
const interceptor = optionalString(params, "interceptor");
|
|
234
|
-
const type =
|
|
427
|
+
const type = optionalInterceptorEventType(params, "type");
|
|
235
428
|
const events = afterSeq > 0
|
|
236
429
|
? interceptorEventLog.since(afterSeq, { limit, level, interceptor, type })
|
|
237
430
|
: interceptorEventLog.latest(limit ?? 100);
|