@eyeauras/insight-cli 0.3.2 → 0.4.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/.cli-package-build.json +1 -1
- package/SKILL.md +4 -1
- package/dist/src/arguments.js +9 -4
- package/dist/src/cli.js +45 -37
- package/dist/src/code-references.d.ts +48 -0
- package/dist/src/code-references.js +154 -0
- package/dist/src/context-diagnostics.d.ts +25 -0
- package/dist/src/context-diagnostics.js +82 -0
- package/dist/src/contexts.d.ts +13 -1
- package/dist/src/contexts.js +38 -9
- package/dist/src/function-boundaries.d.ts +9 -0
- package/dist/src/function-boundaries.js +68 -0
- package/dist/src/function-entries.d.ts +33 -0
- package/dist/src/function-entries.js +84 -0
- package/dist/src/function-inspection.d.ts +4 -2
- package/dist/src/function-inspection.js +94 -59
- package/dist/src/function-results.d.ts +3 -1
- package/dist/src/function-results.js +9 -3
- package/dist/src/inline-script.d.ts +15 -0
- package/dist/src/inline-script.js +62 -0
- package/dist/src/internal-timing.d.ts +3 -0
- package/dist/src/internal-timing.js +52 -0
- package/dist/src/lifecycle.js +14 -4
- package/dist/src/native-inputs.d.ts +1 -1
- package/dist/src/native-inputs.js +1 -1
- package/dist/src/native-queries.js +1 -1
- package/dist/src/persistent-client.d.ts +30 -0
- package/dist/src/persistent-client.js +125 -0
- package/dist/src/queries.js +4 -1
- package/dist/src/query-policy.js +9 -1
- package/dist/src/research-view.d.ts +6 -0
- package/dist/src/research-view.js +9 -0
- package/dist/src/research.d.ts +38 -0
- package/dist/src/research.js +78 -8
- package/dist/src/string-anchors.d.ts +43 -0
- package/dist/src/string-anchors.js +176 -0
- package/dist/src/sync-policy.js +3 -0
- package/dist/src/timed-backend.d.ts +3 -0
- package/dist/src/timed-backend.js +16 -0
- package/dist/src/transport.d.ts +1 -0
- package/dist/src/transport.js +4 -0
- package/dist/src/workspace.js +5 -2
- package/package.json +12 -1
- package/references/native/InsightAnchorScan.java +135 -0
- package/references/native/InsightCodeReferences.java +129 -0
- package/references/native/InsightFunctionBoundaries.java +56 -0
- package/references/native/InsightInlineExecutor.java +143 -0
- package/references/native/InsightInlineScript.java +14 -0
- package/references/native/InsightPeRanges.java +92 -0
- package/references/native/InsightResearchQueries.java +22 -1
- package/references/native/InsightResearchView.java +69 -0
- package/references/native/InsightStringAnchors.java +49 -0
- package/references/persistent-client.mjs +125 -0
- package/references/persistent.md +50 -0
- package/references/query-semantics.md +12 -0
- package/references/recovery.md +36 -0
- package/references/research.md +87 -0
- package/references/setup.md +1 -1
- package/references/sparse-navigation.md +173 -0
- package/runtime/GhidraMCP-7.0.0.jar +0 -0
- package/runtime/GhidraMCP-NOTICE +5 -0
- package/runtime/provenance.json +17 -3
- package/runtime/research-view.provenance.json +28 -0
- package/runtime/toolbox.json +10 -3
- package/skill-bundle/insight-artifacts/SKILL.md +1 -1
- package/skill-bundle/insight-artifacts/references/recovery.md +36 -0
- package/skill-bundle/insight-binary/SKILL.md +1 -1
- package/skill-bundle/insight-capture/SKILL.md +1 -1
- package/skill-bundle/insight-cli/SKILL.md +4 -1
- package/skill-bundle/insight-cli/references/persistent-client.mjs +125 -0
- package/skill-bundle/insight-cli/references/persistent.md +50 -0
- package/skill-bundle/insight-cli/references/recovery.md +36 -0
- package/skill-bundle/insight-cli/references/setup.md +1 -1
- package/skill-bundle/insight-die/SKILL.md +1 -1
- package/skill-bundle/insight-dnspy/SKILL.md +1 -1
- package/skill-bundle/insight-dotnet-dump/SKILL.md +1 -1
- package/skill-bundle/insight-dotnet-stack/SKILL.md +1 -1
- package/skill-bundle/insight-dotnet-trace/SKILL.md +1 -1
- package/skill-bundle/insight-frida/SKILL.md +1 -1
- package/skill-bundle/insight-ghidra/SKILL.md +14 -1
- package/skill-bundle/insight-ghidra/references/persistent-client.mjs +125 -0
- package/skill-bundle/insight-ghidra/references/persistent.md +50 -0
- package/skill-bundle/insight-ghidra/references/query-semantics.md +12 -0
- package/skill-bundle/insight-ghidra/references/recovery.md +36 -0
- package/skill-bundle/insight-ghidra/references/research.md +87 -0
- package/skill-bundle/insight-ghidra/references/setup.md +1 -1
- package/skill-bundle/insight-ghidra/references/sparse-navigation.md +173 -0
- package/skill-bundle/insight-ilspy/SKILL.md +1 -1
- package/skill-bundle/insight-interop/SKILL.md +1 -1
- package/skill-bundle/insight-lldb/SKILL.md +1 -1
- package/skill-bundle/insight-llvm/SKILL.md +1 -1
- package/skill-bundle/insight-managed-heap/SKILL.md +1 -1
- package/skill-bundle/insight-managed-metadata/SKILL.md +1 -1
- package/skill-bundle/insight-mitmproxy/SKILL.md +1 -1
- package/skill-bundle/insight-overview/SKILL.md +1 -1
- package/skill-bundle/insight-procmon/SKILL.md +1 -1
- package/skill-bundle/insight-re/SKILL.md +4 -1
- package/skill-bundle/insight-re/references/persistent-client.mjs +125 -0
- package/skill-bundle/insight-re/references/persistent.md +50 -0
- package/skill-bundle/insight-re/references/recovery.md +36 -0
- package/skill-bundle/insight-sevenzip/SKILL.md +1 -1
- package/skill-bundle/insight-strace/SKILL.md +1 -1
- package/skill-bundle/insight-tshark/SKILL.md +1 -1
- package/skill-bundle/insight-windbg/SKILL.md +1 -1
- package/skill-bundle/manifest.json +1 -1
- package/skills/insight-artifacts/SKILL.md +1 -1
- package/skills/insight-artifacts/references/recovery.md +36 -0
- package/skills/insight-binary/SKILL.md +1 -1
- package/skills/insight-capture/SKILL.md +1 -1
- package/skills/insight-die/SKILL.md +1 -1
- package/skills/insight-dnspy/SKILL.md +1 -1
- package/skills/insight-dotnet-dump/SKILL.md +1 -1
- package/skills/insight-dotnet-stack/SKILL.md +1 -1
- package/skills/insight-dotnet-trace/SKILL.md +1 -1
- package/skills/insight-frida/SKILL.md +1 -1
- package/skills/insight-ghidra/SKILL.md +14 -1
- package/skills/insight-ghidra/references/persistent-client.mjs +125 -0
- package/skills/insight-ghidra/references/persistent.md +50 -0
- package/skills/insight-ghidra/references/query-semantics.md +12 -0
- package/skills/insight-ghidra/references/recovery.md +36 -0
- package/skills/insight-ghidra/references/research.md +87 -0
- package/skills/insight-ghidra/references/setup.md +1 -1
- package/skills/insight-ghidra/references/sparse-navigation.md +173 -0
- package/skills/insight-ilspy/SKILL.md +1 -1
- package/skills/insight-interop/SKILL.md +1 -1
- package/skills/insight-lldb/SKILL.md +1 -1
- package/skills/insight-llvm/SKILL.md +1 -1
- package/skills/insight-managed-heap/SKILL.md +1 -1
- package/skills/insight-managed-metadata/SKILL.md +1 -1
- package/skills/insight-mitmproxy/SKILL.md +1 -1
- package/skills/insight-overview/SKILL.md +1 -1
- package/skills/insight-procmon/SKILL.md +1 -1
- package/skills/insight-re/SKILL.md +4 -1
- package/skills/insight-re/references/persistent-client.mjs +125 -0
- package/skills/insight-re/references/persistent.md +50 -0
- package/skills/insight-re/references/recovery.md +36 -0
- package/skills/insight-sevenzip/SKILL.md +1 -1
- package/skills/insight-strace/SKILL.md +1 -1
- package/skills/insight-tshark/SKILL.md +1 -1
- package/skills/insight-windbg/SKILL.md +1 -1
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": 1,
|
|
3
|
+
"upstreamSha256": "7084e2fda16dc779dd05398e48821ff3f9ace4d32757ea143b257b39689e5fbd",
|
|
4
|
+
"upstreamGitArtifact": "6e4bfc9:skills/ghidra-coordinator/runtime/GhidraMCP-7.0.0.jar",
|
|
5
|
+
"sha256": "f4a9272e911466ed774c8cf73bc1adf1daef6009d895fda57aa0b28522fcdf32",
|
|
6
|
+
"inputs": {
|
|
7
|
+
"skills/ghidra-coordinator/references/native/InsightResearchView.java": "78bd25f8a3d7a1bc640938d8497cd581c2c178915ae6003b19dceda507c34df2",
|
|
8
|
+
"skills/ghidra-coordinator/references/native/InsightStringAnchors.java": "e496ec2a28a57eb1447b11e3d0ec6d918f2d543857643b0e6c9f6c2a905b7f9d",
|
|
9
|
+
"skills/ghidra-coordinator/references/native/InsightAnchorScan.java": "2197d6dedd133d229a58876f8d164faaf178902646dea7bb4540197bacfcf105",
|
|
10
|
+
"skills/ghidra-coordinator/references/native/InsightFunctionBoundaries.java": "61eab3948186597ad22396eb029d61d2ddeb89a77d3969ea41ead55a38deb651",
|
|
11
|
+
"skills/ghidra-coordinator/references/native/InsightPeRanges.java": "2e82a7b2e908f29f6e68f6491549981cc709012186096a60ce10d5b15f29712b",
|
|
12
|
+
"scripts/native/InstallResearchView.java": "bf0a3e54f789eb14319cc14ce89d8fb78459be500fe53576eddb4b9d02c02f12",
|
|
13
|
+
"scripts/build-research-view-bridge.mjs": "490c6b3628be07da5afb3d7ae9efc6d216d139bf779ec38aa32233b2b563cfb8"
|
|
14
|
+
},
|
|
15
|
+
"asm": {
|
|
16
|
+
"version": "9.7.1",
|
|
17
|
+
"sha256": "8cadd43ac5eb6d09de05faecca38b917a040bb9139c7edeb4cc81c740b713281"
|
|
18
|
+
},
|
|
19
|
+
"jdk": "openjdk 21.0.11 2026-04-21\nOpenJDK Runtime Environment Homebrew (build 21.0.11)\nOpenJDK 64-Bit Server VM Homebrew (build 21.0.11, mixed mode, sharing)",
|
|
20
|
+
"change": "Add annotated ReadOnly services for research-view, indexed function entries raw string anchors and PE runtime boundaries to the pinned AnnotationScanner constructor; all other upstream class bytes unchanged except ZIP container metadata",
|
|
21
|
+
"endpoint": "/insight/research-view/v1",
|
|
22
|
+
"endpoints": [
|
|
23
|
+
"/insight/research-view/v1",
|
|
24
|
+
"/insight/function-entries/v1",
|
|
25
|
+
"/insight/string-anchors/v1",
|
|
26
|
+
"/insight/function-boundaries/v1"
|
|
27
|
+
]
|
|
28
|
+
}
|
package/runtime/toolbox.json
CHANGED
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
"resources": {
|
|
9
9
|
"references/agent-execution.md": "references/agent-execution.md",
|
|
10
10
|
"references/storage.md": "references/storage.md",
|
|
11
|
-
"references/recovery.md": "references/recovery.md"
|
|
11
|
+
"references/recovery.md": "references/recovery.md",
|
|
12
|
+
"references/persistent.md": "references/persistent.md",
|
|
13
|
+
"references/persistent-client.mjs": "dist/src/persistent-client.js"
|
|
12
14
|
}
|
|
13
15
|
},
|
|
14
16
|
{
|
|
@@ -19,7 +21,9 @@
|
|
|
19
21
|
"references/recovery.md": "references/recovery.md",
|
|
20
22
|
"references/artifacts.md": "references/artifacts.md",
|
|
21
23
|
"references/agent-execution.md": "references/agent-execution.md",
|
|
22
|
-
"references/storage.md": "references/storage.md"
|
|
24
|
+
"references/storage.md": "references/storage.md",
|
|
25
|
+
"references/persistent.md": "references/persistent.md",
|
|
26
|
+
"references/persistent-client.mjs": "dist/src/persistent-client.js"
|
|
23
27
|
}
|
|
24
28
|
},
|
|
25
29
|
{
|
|
@@ -29,6 +33,7 @@
|
|
|
29
33
|
"resources": {
|
|
30
34
|
"references/setup.md": "references/setup.md",
|
|
31
35
|
"references/research.md": "references/research.md",
|
|
36
|
+
"references/sparse-navigation.md": "references/sparse-navigation.md",
|
|
32
37
|
"references/query-semantics.md": "references/query-semantics.md",
|
|
33
38
|
"references/native-workflows.md": "references/native-workflows.md",
|
|
34
39
|
"references/recovery.md": "references/recovery.md",
|
|
@@ -39,7 +44,9 @@
|
|
|
39
44
|
"references/extensions-and-sync.md": "references/extensions-and-sync.md",
|
|
40
45
|
"references/artifacts.md": "references/artifacts.md",
|
|
41
46
|
"references/agent-execution.md": "references/agent-execution.md",
|
|
42
|
-
"references/storage.md": "references/storage.md"
|
|
47
|
+
"references/storage.md": "references/storage.md",
|
|
48
|
+
"references/persistent.md": "references/persistent.md",
|
|
49
|
+
"references/persistent-client.mjs": "dist/src/persistent-client.js"
|
|
43
50
|
}
|
|
44
51
|
},
|
|
45
52
|
{
|
|
@@ -3,7 +3,7 @@ name: insight-artifacts
|
|
|
3
3
|
description: Retain a completed reverse-engineering evidence file or directory as an ordinary checkpoint using insight-cli artifacts save. Use after Ghidra or any external tool; preserve existing project formats and source of truth.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Insight Artifacts 0.
|
|
6
|
+
# Insight Artifacts 0.4.0
|
|
7
7
|
|
|
8
8
|
If local help/status reports AppData or IPC access denied, retry that **read-only probe**
|
|
9
9
|
with the agent host's normal permission escalation, then use that route for later authorized
|
|
@@ -24,11 +24,47 @@ establishes that the old owned bootstrap worker has ended, then uses an authorit
|
|
|
24
24
|
probe. An absent canonical item permits private preparation; an existing first version requires
|
|
25
25
|
independent readback. The old project, intent and original session/target remain preserved.
|
|
26
26
|
|
|
27
|
+
## Interrupted save or close
|
|
28
|
+
|
|
29
|
+
After restoring profile storage, inspect the selected session and target with
|
|
30
|
+
`sessions status <session> --target <target>`. Retry the original save/close with the
|
|
31
|
+
same `--request-id`. If the original command had no caller ID, use the generated
|
|
32
|
+
`unresolvedRequests[].id` from status. This retrieves the original intent instead of
|
|
33
|
+
creating another close. A verified stopped worker lets close report `state: closed`
|
|
34
|
+
and retain the acknowledged private snapshot and pending changes; it sends no new save,
|
|
35
|
+
open, close or publication. Reopen the same session/target afterward.
|
|
36
|
+
|
|
37
|
+
If release cannot be proved, the command still fails with `coordinator.unknown`
|
|
38
|
+
and gives scoped status/process inspection commands. An authenticated live worker is not
|
|
39
|
+
closed again automatically. Retain its project and journals. After inspecting its retained
|
|
40
|
+
work, an operator may terminate that exact owned headless worker through
|
|
41
|
+
`tools processes kill <process>` with Destructive permission, accepting loss of any
|
|
42
|
+
unacknowledged in-memory work. Do not kill by guessed PID/port or stop the shared server.
|
|
43
|
+
Retrieve the same close request after that worker has ended. An unavailable endpoint or
|
|
44
|
+
changed process receipt is not proof of release; preserve the binding for investigation.
|
|
45
|
+
|
|
27
46
|
If a response was lost, inspect `sessions status <session> --target <target>`. Never infer
|
|
28
47
|
`applied` from a connection failure. A changing command's optional request ID binds one effective
|
|
29
48
|
intent inside the profile. Repeating the same ID/input retrieves its recorded outcome or
|
|
30
49
|
unresolved state; changing input under that ID fails. Without an ID, calls are new intentions.
|
|
31
50
|
|
|
51
|
+
For Java `scripts run-inline`, a versioned native executor records compilation, non-initializing
|
|
52
|
+
class resolution, initialization, construction and framework execution separately. A correlated
|
|
53
|
+
receipt proving `userCode: not_started` and `entrypointInvoked: false` before initialization
|
|
54
|
+
produces `state: rejected` with a terminal failed request. The context remains available and
|
|
55
|
+
its prior saved/pending work is unchanged. The CLI exits nonzero; the error result retains
|
|
56
|
+
bounded compiler/load diagnostics. Repeating that request ID retrieves the same rejection;
|
|
57
|
+
corrected source requires a new ID. Each attempt has isolated source/classes under profile
|
|
58
|
+
analysis storage, with annotation processing disabled and no reuse of older inline classes.
|
|
59
|
+
|
|
60
|
+
Initialization or constructor failure can have effects even when `run()` was never called.
|
|
61
|
+
These failures, framework/runtime failures and missing/malformed/lost acknowledgements remain
|
|
62
|
+
unknown. During framework execution `entrypointInvoked: null` means actual `run()` entry was
|
|
63
|
+
not independently observed. Console text cannot establish non-execution. This is not a sandbox
|
|
64
|
+
for adversarial same-JVM Java, and existing unknown script attempts are not reclassified from
|
|
65
|
+
their exception strings or an unchanged program snapshot. File/Python scripts retain their
|
|
66
|
+
existing conservative recovery semantics.
|
|
67
|
+
|
|
32
68
|
An unknown or partial effect retains its request intent, snapshots and native stage. Open the
|
|
33
69
|
same tuple to reconcile supported outcomes. A committed merge is verified against its recorded
|
|
34
70
|
server version and complete saved buffers through independent read authority; it is never
|
|
@@ -3,7 +3,7 @@ name: insight-binary
|
|
|
3
3
|
description: Query structured PE, ELF and thin Mach-O headers, sections, imports and exports, or make an explicitly guarded new PE subsystem copy through insight-cli binary. Use for scoped binary structure, linkage and copy-only header edits without target execution.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Insight Binary 0.
|
|
6
|
+
# Insight Binary 0.4.0
|
|
7
7
|
|
|
8
8
|
If local help/status reports AppData or IPC access denied, retry that **read-only probe**
|
|
9
9
|
with the agent host's normal permission escalation, then use that route for later authorized
|
|
@@ -3,7 +3,7 @@ name: insight-capture
|
|
|
3
3
|
description: Capture one explicitly authorized native program run, exact stdout/stderr and declared file snapshots through insight-cli capture, then inspect or compare retained reports without replay. Use for controlled input/output research, preserving original files and interrupted or unknown outcomes.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Insight Capture 0.
|
|
6
|
+
# Insight Capture 0.4.0
|
|
7
7
|
|
|
8
8
|
If local help/status reports AppData or IPC access denied, retry that **read-only probe**
|
|
9
9
|
with the agent host's normal permission escalation, then use that route for later authorized
|
|
@@ -3,7 +3,7 @@ name: insight-cli
|
|
|
3
3
|
description: Choose and prepare reverse-engineering capabilities from the insight-cli toolbox for binary, managed-code and runtime investigations. Use to discover the appropriate tool or its packaged skill before installing native runtimes.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# insight-cli 0.
|
|
6
|
+
# insight-cli 0.4.0
|
|
7
7
|
|
|
8
8
|
If local help/status reports AppData or IPC access denied, retry that **read-only probe**
|
|
9
9
|
with the agent host's normal permission escalation, then use that route for later authorized
|
|
@@ -15,6 +15,9 @@ request/status recovery, never blind retry.
|
|
|
15
15
|
Read the project's RE instructions and existing findings first. They own tool choices,
|
|
16
16
|
evidence layout, formats and source of truth. These skills fill the remaining gaps.
|
|
17
17
|
|
|
18
|
+
For repeated research calls, retain one [persistent CLI client](references/persistent.md)
|
|
19
|
+
in the agent's long-lived runtime. Reuse it across calls to avoid repeated frontend startup.
|
|
20
|
+
|
|
18
21
|
1. Run `insight-cli tools list --json` to discover the capabilities shipped in this version.
|
|
19
22
|
The catalog works before profile configuration, downloads or IPC startup. A downloaded
|
|
20
23
|
runtime is not needed to discover what a tool does.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { StringDecoder } from 'node:string_decoder';
|
|
3
|
+
export class InsightCommandError extends Error {
|
|
4
|
+
rpc;
|
|
5
|
+
constructor(rpc) {
|
|
6
|
+
super(rpc.message);
|
|
7
|
+
this.rpc = rpc;
|
|
8
|
+
this.name = 'InsightCommandError';
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export class InsightTransportError extends Error {
|
|
12
|
+
dispatched;
|
|
13
|
+
constructor(message, dispatched) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.dispatched = dispatched;
|
|
16
|
+
this.name = 'InsightTransportError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** A thin client of Factory's existing stream, with no reconnect or command replay. */
|
|
20
|
+
export async function connectInsight(options) {
|
|
21
|
+
const child = spawn(options.executable, [...(options.arguments ?? []), '--json-rpc'], { cwd: options.cwd, env: options.env, stdio: ['pipe', 'pipe', 'pipe'], shell: false });
|
|
22
|
+
const maximum = 4 * 1024 ** 2, decoder = new StringDecoder('utf8');
|
|
23
|
+
let buffer = '', bytes = 0, sequence = 0, accepting = true, failed = false, ended = false;
|
|
24
|
+
let closePromise, tail = Promise.resolve();
|
|
25
|
+
const pending = new Map();
|
|
26
|
+
// Drain diagnostics without retaining arbitrary stderr, paths, tokens or unbounded output.
|
|
27
|
+
child.stderr.resume();
|
|
28
|
+
const fail = (message) => {
|
|
29
|
+
if (failed)
|
|
30
|
+
return;
|
|
31
|
+
failed = true;
|
|
32
|
+
accepting = false;
|
|
33
|
+
for (const p of pending.values())
|
|
34
|
+
p.reject(new InsightTransportError(message, true));
|
|
35
|
+
pending.clear();
|
|
36
|
+
child.stdin.destroy();
|
|
37
|
+
child.stdout.destroy();
|
|
38
|
+
};
|
|
39
|
+
child.on('error', () => fail('Persistent CLI process failed.'));
|
|
40
|
+
child.stdout.on('error', () => fail('Persistent CLI output was lost.'));
|
|
41
|
+
child.stderr.on('error', () => fail('Persistent CLI diagnostic stream was lost.'));
|
|
42
|
+
child.stdin.on('error', () => fail('Persistent CLI input was lost; inspect request/status before retrying a changing command.'));
|
|
43
|
+
const exited = new Promise((resolve, reject) => {
|
|
44
|
+
child.once('close', (code, signal) => {
|
|
45
|
+
ended = true;
|
|
46
|
+
if (code !== 0 || signal || pending.size || buffer.trim()) {
|
|
47
|
+
fail('Persistent CLI closed without a complete acknowledged response.');
|
|
48
|
+
reject(new InsightTransportError('Persistent CLI transport ended unexpectedly.', false));
|
|
49
|
+
}
|
|
50
|
+
else if (failed)
|
|
51
|
+
reject(new InsightTransportError('Persistent CLI transport failed.', false));
|
|
52
|
+
else
|
|
53
|
+
resolve();
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
void exited.catch(() => { });
|
|
57
|
+
child.stdout.on('data', (chunk) => {
|
|
58
|
+
if (failed)
|
|
59
|
+
return;
|
|
60
|
+
buffer += decoder.write(chunk);
|
|
61
|
+
bytes += chunk.length;
|
|
62
|
+
if (bytes > maximum) {
|
|
63
|
+
fail('Persistent CLI response exceeded the bounded transport limit.');
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
for (;;) {
|
|
67
|
+
const newline = buffer.indexOf('\n');
|
|
68
|
+
if (newline < 0)
|
|
69
|
+
break;
|
|
70
|
+
const line = buffer.slice(0, newline);
|
|
71
|
+
buffer = buffer.slice(newline + 1);
|
|
72
|
+
bytes = Buffer.byteLength(buffer);
|
|
73
|
+
if (!line.trim())
|
|
74
|
+
continue;
|
|
75
|
+
try {
|
|
76
|
+
const r = JSON.parse(line);
|
|
77
|
+
if (!r || r.jsonrpc !== '2.0' || typeof r.id !== 'number' || !pending.has(r.id) || Object.hasOwn(r, 'result') === Object.hasOwn(r, 'error'))
|
|
78
|
+
throw Error();
|
|
79
|
+
const p = pending.get(r.id);
|
|
80
|
+
if (Object.hasOwn(r, 'error') && (!r.error || typeof r.error !== 'object' || typeof r.error.code !== 'number' || !Number.isInteger(r.error.code) || typeof r.error.message !== 'string'))
|
|
81
|
+
throw Error();
|
|
82
|
+
pending.delete(r.id);
|
|
83
|
+
if (r.error)
|
|
84
|
+
p.reject(new InsightCommandError(r.error));
|
|
85
|
+
else
|
|
86
|
+
p.resolve(r.result);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
fail('Persistent CLI returned a malformed or uncorrelated response.');
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
await new Promise((resolve, reject) => { child.once('spawn', resolve); child.once('error', () => reject(new InsightTransportError('Could not start persistent CLI.', false))); });
|
|
95
|
+
return {
|
|
96
|
+
execute(argv) {
|
|
97
|
+
if (!Array.isArray(argv) || argv.length > 512 || argv.some(x => typeof x !== 'string' || x.includes('\0')) || Buffer.byteLength(JSON.stringify(argv)) > 1024 ** 2)
|
|
98
|
+
return Promise.reject(new TypeError('argv must be a bounded array of strings without NUL.'));
|
|
99
|
+
if (!accepting || failed || ended)
|
|
100
|
+
return Promise.reject(new InsightTransportError('Persistent CLI connection is closed.', false));
|
|
101
|
+
const args = [...argv];
|
|
102
|
+
const request = tail.then(() => {
|
|
103
|
+
if (failed || ended)
|
|
104
|
+
throw new InsightTransportError('Persistent CLI connection was lost before dispatch.', false);
|
|
105
|
+
const id = ++sequence;
|
|
106
|
+
return new Promise((resolve, reject) => {
|
|
107
|
+
pending.set(id, { resolve, reject });
|
|
108
|
+
try {
|
|
109
|
+
child.stdin.write(JSON.stringify({ jsonrpc: '2.0', id, method: 'cli.execute', params: { argv: args } }) + '\n');
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
fail('Persistent CLI input was lost; inspect request/status before retrying a changing command.');
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
tail = request.catch(() => { });
|
|
117
|
+
return request;
|
|
118
|
+
},
|
|
119
|
+
close() {
|
|
120
|
+
accepting = false;
|
|
121
|
+
return closePromise ??= tail.then(async () => { if (!failed && !ended)
|
|
122
|
+
child.stdin.end(); await exited; });
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Persistent research calls
|
|
2
|
+
|
|
3
|
+
For a sequence of CLI research calls, keep one `insight-cli --json-rpc` frontend alive in the
|
|
4
|
+
agent's long-lived JavaScript runtime. The Factory owner and Ghidra processes have their own
|
|
5
|
+
lifetime; retaining only the owner still makes a new frontend validate its installed build.
|
|
6
|
+
|
|
7
|
+
The package exports a small stdio client, independent of the facade's eager imports:
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
import {connectInsight, InsightCommandError, InsightTransportError}
|
|
11
|
+
from '@eyeauras/insight-cli/persistent';
|
|
12
|
+
|
|
13
|
+
const cli = await connectInsight({executable: 'insight-cli', cwd: projectDirectory});
|
|
14
|
+
const result = await cli.execute([
|
|
15
|
+
'ghidra', 'functions', 'inspect', '--session', session, '--target', target,
|
|
16
|
+
'--function', 'main', '--fields', 'entry_point', '--include-call-context', 'false'
|
|
17
|
+
]);
|
|
18
|
+
// Keep cli for subsequent calls in this investigation.
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
When package-name resolution is unavailable in the agent host, dynamically import the
|
|
22
|
+
[`persistent-client.mjs`](persistent-client.mjs) beside this reference by its absolute file URL.
|
|
23
|
+
It is generated from the same canonical package module. In a persistent REPL retain `cli` as a
|
|
24
|
+
binding across tool invocations. A new Node process per query does not retain this connection.
|
|
25
|
+
For an explicit installed bin, use `{executable: process.execPath, arguments: [installedBin], cwd}`.
|
|
26
|
+
`env` optionally supplies the child environment. There is no shell or separate profile/config.
|
|
27
|
+
Use existing `--profile` argv flags when selecting a profile explicitly.
|
|
28
|
+
|
|
29
|
+
`execute(argv)` returns the CLI result unchanged, without a JSON-RPC envelope. Calls are queued
|
|
30
|
+
in admission order. Native work can be long: await the original promise through the agent host's
|
|
31
|
+
existing wait facility. This V1 client deliberately has no per-request timeout or automatic
|
|
32
|
+
reconnect/retry. Factory permissions, command validation, IDs and native budgets remain authoritative.
|
|
33
|
+
|
|
34
|
+
* `InsightCommandError.rpc` preserves the JSON-RPC error code, message and data, including typed
|
|
35
|
+
CLI error results. A command failure leaves a healthy connection usable for the next command.
|
|
36
|
+
* `InsightTransportError.dispatched === true` means the request was handed to the stream and its
|
|
37
|
+
acknowledgment is unavailable; it does not prove execution. Changing commands require their
|
|
38
|
+
existing request-ID/status recovery. The client never resends them.
|
|
39
|
+
* `dispatched === false` means this client did not send that request, including queued requests
|
|
40
|
+
after a transport failure. Reconnection is an explicit new connection, not an implicit replay.
|
|
41
|
+
|
|
42
|
+
The client bounds each response line to four MiB, validates IDs and envelopes, and fails the
|
|
43
|
+
connection on malformed, uncorrelated or oversized replies. It drains stderr without retaining
|
|
44
|
+
arbitrary native diagnostics in memory. Read command-level diagnostics from their typed results
|
|
45
|
+
and the existing profile diagnostic locations.
|
|
46
|
+
|
|
47
|
+
Finish owned Ghidra contexts with the normal scoped commands, following the project's shared
|
|
48
|
+
server policy. Then `await cli.close()` drains already admitted requests and ends this stdio
|
|
49
|
+
connection. It does not issue `ipc-server stop`, close another agent's context or stop a server.
|
|
50
|
+
Do not call close while expecting to admit more requests.
|
|
@@ -24,11 +24,47 @@ establishes that the old owned bootstrap worker has ended, then uses an authorit
|
|
|
24
24
|
probe. An absent canonical item permits private preparation; an existing first version requires
|
|
25
25
|
independent readback. The old project, intent and original session/target remain preserved.
|
|
26
26
|
|
|
27
|
+
## Interrupted save or close
|
|
28
|
+
|
|
29
|
+
After restoring profile storage, inspect the selected session and target with
|
|
30
|
+
`sessions status <session> --target <target>`. Retry the original save/close with the
|
|
31
|
+
same `--request-id`. If the original command had no caller ID, use the generated
|
|
32
|
+
`unresolvedRequests[].id` from status. This retrieves the original intent instead of
|
|
33
|
+
creating another close. A verified stopped worker lets close report `state: closed`
|
|
34
|
+
and retain the acknowledged private snapshot and pending changes; it sends no new save,
|
|
35
|
+
open, close or publication. Reopen the same session/target afterward.
|
|
36
|
+
|
|
37
|
+
If release cannot be proved, the command still fails with `coordinator.unknown`
|
|
38
|
+
and gives scoped status/process inspection commands. An authenticated live worker is not
|
|
39
|
+
closed again automatically. Retain its project and journals. After inspecting its retained
|
|
40
|
+
work, an operator may terminate that exact owned headless worker through
|
|
41
|
+
`tools processes kill <process>` with Destructive permission, accepting loss of any
|
|
42
|
+
unacknowledged in-memory work. Do not kill by guessed PID/port or stop the shared server.
|
|
43
|
+
Retrieve the same close request after that worker has ended. An unavailable endpoint or
|
|
44
|
+
changed process receipt is not proof of release; preserve the binding for investigation.
|
|
45
|
+
|
|
27
46
|
If a response was lost, inspect `sessions status <session> --target <target>`. Never infer
|
|
28
47
|
`applied` from a connection failure. A changing command's optional request ID binds one effective
|
|
29
48
|
intent inside the profile. Repeating the same ID/input retrieves its recorded outcome or
|
|
30
49
|
unresolved state; changing input under that ID fails. Without an ID, calls are new intentions.
|
|
31
50
|
|
|
51
|
+
For Java `scripts run-inline`, a versioned native executor records compilation, non-initializing
|
|
52
|
+
class resolution, initialization, construction and framework execution separately. A correlated
|
|
53
|
+
receipt proving `userCode: not_started` and `entrypointInvoked: false` before initialization
|
|
54
|
+
produces `state: rejected` with a terminal failed request. The context remains available and
|
|
55
|
+
its prior saved/pending work is unchanged. The CLI exits nonzero; the error result retains
|
|
56
|
+
bounded compiler/load diagnostics. Repeating that request ID retrieves the same rejection;
|
|
57
|
+
corrected source requires a new ID. Each attempt has isolated source/classes under profile
|
|
58
|
+
analysis storage, with annotation processing disabled and no reuse of older inline classes.
|
|
59
|
+
|
|
60
|
+
Initialization or constructor failure can have effects even when `run()` was never called.
|
|
61
|
+
These failures, framework/runtime failures and missing/malformed/lost acknowledgements remain
|
|
62
|
+
unknown. During framework execution `entrypointInvoked: null` means actual `run()` entry was
|
|
63
|
+
not independently observed. Console text cannot establish non-execution. This is not a sandbox
|
|
64
|
+
for adversarial same-JVM Java, and existing unknown script attempts are not reclassified from
|
|
65
|
+
their exception strings or an unchanged program snapshot. File/Python scripts retain their
|
|
66
|
+
existing conservative recovery semantics.
|
|
67
|
+
|
|
32
68
|
An unknown or partial effect retains its request intent, snapshots and native stage. Open the
|
|
33
69
|
same tuple to reconcile supported outcomes. A committed merge is verified against its recorded
|
|
34
70
|
server version and complete saved buffers through independent read authority; it is never
|
|
@@ -11,7 +11,7 @@ external executable paths, status/doctor and safe removal. Existing Factory prof
|
|
|
11
11
|
The package contains the verified GhidraMCP 7.0.0 bridge, its Apache-2.0 license/notice and
|
|
12
12
|
artifact provenance. Its pinned functionality catalog is from source
|
|
13
13
|
`57e37a8bf388b1279cff46378f85be1554548f61`, with SHA256
|
|
14
|
-
`
|
|
14
|
+
`f4a9272e911466ed774c8cf73bc1adf1daef6009d895fda57aa0b28522fcdf32`.
|
|
15
15
|
`mcpJar` optionally selects another copy of those exact verified bytes. Runtime startup needs no
|
|
16
16
|
bridge/Factory source checkout or Python MCP bridge. Packaged native helper sources compile
|
|
17
17
|
against the installed Ghidra/JDK.
|
|
@@ -3,7 +3,7 @@ name: insight-die
|
|
|
3
3
|
description: Classify unknown executable or container inputs by format, architecture and compiler/runtime/packer signatures with Detect It Easy before choosing managed or native analysis. Use insight-cli die for static triage and preserve scan uncertainty.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Insight Detect It Easy 0.
|
|
6
|
+
# Insight Detect It Easy 0.4.0
|
|
7
7
|
|
|
8
8
|
If local help/status reports AppData or IPC access denied, retry that **read-only probe**
|
|
9
9
|
with the agent host's normal permission escalation, then use that route for later authorized
|
|
@@ -3,7 +3,7 @@ name: insight-dnspy
|
|
|
3
3
|
description: Debug a managed Windows EXE at an IL breakpoint and read primitive arguments, or change one expected IL Int32 constant in a new assembly copy through insight-cli dnspy. Use when C# decompilation needs observed execution or a controlled binary edit.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Insight dnSpyEx 0.
|
|
6
|
+
# Insight dnSpyEx 0.4.0
|
|
7
7
|
|
|
8
8
|
If local help/status reports AppData or IPC access denied, retry that **read-only probe**
|
|
9
9
|
with the agent host's normal permission escalation, then use that route for later authorized
|
|
@@ -3,7 +3,7 @@ name: insight-dotnet-dump
|
|
|
3
3
|
description: Capture a managed heap dump or inspect compatible .NET dumps with SOS to recover waiting stacks, objects, fields and retention evidence.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Insight .NET Dump 0.
|
|
6
|
+
# Insight .NET Dump 0.4.0
|
|
7
7
|
|
|
8
8
|
If local help/status reports AppData or IPC access denied, retry that **read-only probe**
|
|
9
9
|
with the agent host's normal permission escalation, then use that route for later authorized
|
|
@@ -3,7 +3,7 @@ name: insight-dotnet-stack
|
|
|
3
3
|
description: Inspect live managed thread stacks to identify active or waiting work in an explicitly selected .NET process without taking a dump.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Insight .NET Stack 0.
|
|
6
|
+
# Insight .NET Stack 0.4.0
|
|
7
7
|
|
|
8
8
|
If local help/status reports AppData or IPC access denied, retry that **read-only probe**
|
|
9
9
|
with the agent host's normal permission escalation, then use that route for later authorized
|
|
@@ -3,7 +3,7 @@ name: insight-dotnet-trace
|
|
|
3
3
|
description: Collect and inspect bounded .NET EventPipe traces to identify sampled thread work, waits or runtime activity; distinguish wall-clock samples from CPU consumption.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Insight .NET Trace 0.
|
|
6
|
+
# Insight .NET Trace 0.4.0
|
|
7
7
|
|
|
8
8
|
If local help/status reports AppData or IPC access denied, retry that **read-only probe**
|
|
9
9
|
with the agent host's normal permission escalation, then use that route for later authorized
|
|
@@ -3,7 +3,7 @@ name: insight-frida
|
|
|
3
3
|
description: Observe native call arguments/results or inspect runtime modules and exports in an explicitly selected authorized process with bounded Frida scripts. Use insight-cli frida to corroborate static findings with actual execution on macOS ARM64.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Insight Frida 0.
|
|
6
|
+
# Insight Frida 0.4.0
|
|
7
7
|
|
|
8
8
|
If local help/status reports AppData or IPC access denied, retry that **read-only probe**
|
|
9
9
|
with the agent host's normal permission escalation, then use that route for later authorized
|
|
@@ -3,7 +3,7 @@ name: insight-ghidra
|
|
|
3
3
|
description: Inspect or edit native binaries with Ghidra through insight-cli; use isolated contexts, multi-agent merge/refresh, server, GUI or debugger workflows. Choose this for static native behavior and references, not C# recovery from managed IL.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Insight Ghidra 0.
|
|
6
|
+
# Insight Ghidra 0.4.0
|
|
7
7
|
|
|
8
8
|
If local help/status reports AppData or IPC access denied, retry that **read-only probe**
|
|
9
9
|
with the agent host's normal permission escalation, then use that route for later authorized
|
|
@@ -17,6 +17,9 @@ session/cell ID, await that same execution with the host's wait/poll facility un
|
|
|
17
17
|
status and read its stdout/stderr. An empty first chunk is not a lost CLI response. Do not
|
|
18
18
|
start another open or repeat a CPU mutation while the original command is still running.
|
|
19
19
|
|
|
20
|
+
For research loops, use the packaged [persistent client](references/persistent.md) and keep
|
|
21
|
+
one connection across queries. It uses the existing CLI transport and preserves typed errors;
|
|
22
|
+
lost mutation replies still require request/status recovery.
|
|
20
23
|
|
|
21
24
|
New targets import **without automatic analysis**. Reuse existing selectors/projects before
|
|
22
25
|
opening another copy; `ghidra open --session <session> --target <target>` preserves its bound
|
|
@@ -68,6 +71,13 @@ ordinary saved copy and its receipt. Neither operation requires a Ghidra install
|
|
|
68
71
|
function addresses, then inspect/decompile. Decompilation defaults to a 16000-character
|
|
69
72
|
window; page with `--limit-chars`/`--offset-chars` and retain `textSha256`. Native
|
|
70
73
|
truncation remains explicit even at the end of the observed text. See [research](references/research.md).
|
|
74
|
+
If strings/xrefs are empty or function bodies are one byte, run `analysis navigation`
|
|
75
|
+
and follow [sparse navigation](references/sparse-navigation.md). Try compact `functions inspect`
|
|
76
|
+
at known entries before writing reconstruction scripts. `force-decompile` refreshes an
|
|
77
|
+
existing entry; `decode` handles known bytes without an entry. Neither repairs stored xrefs.
|
|
78
|
+
`analysis code-references` scans explicit RIP/CALL byte forms for exact targets with bounded
|
|
79
|
+
coverage and a stateless cursor. Its decoded candidates retain boundary uncertainty; see the
|
|
80
|
+
sparse-navigation reference before treating a candidate as an actual code reference.
|
|
71
81
|
4. Rename functions or set/clear comments through ordinary commands. An `applied` result means
|
|
72
82
|
verified saved private work. Other sessions keep their own views; local edits remain unpublished in the default manual mode.
|
|
73
83
|
5. By default, publish explicitly with `ghidra merge`; receive safe shared changes with `ghidra refresh`.
|
|
@@ -76,6 +86,9 @@ ordinary saved copy and its receipt. Neither operation requires a Ghidra install
|
|
|
76
86
|
6. Continue with `ghidra open --session <id> --target <id>`. For uncertain outcomes inspect
|
|
77
87
|
`sessions status <id> --target <id>` and [recovery](references/recovery.md). Supplying a stable
|
|
78
88
|
optional `--request-id` lets a repeated changing intent retrieve its outcome without rerunning.
|
|
89
|
+
Java `scripts run-inline` returns bounded compile/load diagnostics. Only proven rejection
|
|
90
|
+
before user-code initialization leaves the context available; constructor/static initializer
|
|
91
|
+
failures and lost responses remain unknown. See the recovery reference for execution fields.
|
|
79
92
|
7. Finish by closing each context you own with scoped `ghidra close`, preserving private work.
|
|
80
93
|
If this profile's local server belongs exclusively to your investigation and all its contexts
|
|
81
94
|
are closed, use `ghidra server stop`, then verify `ghidra status` has no remaining processes.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { StringDecoder } from 'node:string_decoder';
|
|
3
|
+
export class InsightCommandError extends Error {
|
|
4
|
+
rpc;
|
|
5
|
+
constructor(rpc) {
|
|
6
|
+
super(rpc.message);
|
|
7
|
+
this.rpc = rpc;
|
|
8
|
+
this.name = 'InsightCommandError';
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export class InsightTransportError extends Error {
|
|
12
|
+
dispatched;
|
|
13
|
+
constructor(message, dispatched) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.dispatched = dispatched;
|
|
16
|
+
this.name = 'InsightTransportError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** A thin client of Factory's existing stream, with no reconnect or command replay. */
|
|
20
|
+
export async function connectInsight(options) {
|
|
21
|
+
const child = spawn(options.executable, [...(options.arguments ?? []), '--json-rpc'], { cwd: options.cwd, env: options.env, stdio: ['pipe', 'pipe', 'pipe'], shell: false });
|
|
22
|
+
const maximum = 4 * 1024 ** 2, decoder = new StringDecoder('utf8');
|
|
23
|
+
let buffer = '', bytes = 0, sequence = 0, accepting = true, failed = false, ended = false;
|
|
24
|
+
let closePromise, tail = Promise.resolve();
|
|
25
|
+
const pending = new Map();
|
|
26
|
+
// Drain diagnostics without retaining arbitrary stderr, paths, tokens or unbounded output.
|
|
27
|
+
child.stderr.resume();
|
|
28
|
+
const fail = (message) => {
|
|
29
|
+
if (failed)
|
|
30
|
+
return;
|
|
31
|
+
failed = true;
|
|
32
|
+
accepting = false;
|
|
33
|
+
for (const p of pending.values())
|
|
34
|
+
p.reject(new InsightTransportError(message, true));
|
|
35
|
+
pending.clear();
|
|
36
|
+
child.stdin.destroy();
|
|
37
|
+
child.stdout.destroy();
|
|
38
|
+
};
|
|
39
|
+
child.on('error', () => fail('Persistent CLI process failed.'));
|
|
40
|
+
child.stdout.on('error', () => fail('Persistent CLI output was lost.'));
|
|
41
|
+
child.stderr.on('error', () => fail('Persistent CLI diagnostic stream was lost.'));
|
|
42
|
+
child.stdin.on('error', () => fail('Persistent CLI input was lost; inspect request/status before retrying a changing command.'));
|
|
43
|
+
const exited = new Promise((resolve, reject) => {
|
|
44
|
+
child.once('close', (code, signal) => {
|
|
45
|
+
ended = true;
|
|
46
|
+
if (code !== 0 || signal || pending.size || buffer.trim()) {
|
|
47
|
+
fail('Persistent CLI closed without a complete acknowledged response.');
|
|
48
|
+
reject(new InsightTransportError('Persistent CLI transport ended unexpectedly.', false));
|
|
49
|
+
}
|
|
50
|
+
else if (failed)
|
|
51
|
+
reject(new InsightTransportError('Persistent CLI transport failed.', false));
|
|
52
|
+
else
|
|
53
|
+
resolve();
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
void exited.catch(() => { });
|
|
57
|
+
child.stdout.on('data', (chunk) => {
|
|
58
|
+
if (failed)
|
|
59
|
+
return;
|
|
60
|
+
buffer += decoder.write(chunk);
|
|
61
|
+
bytes += chunk.length;
|
|
62
|
+
if (bytes > maximum) {
|
|
63
|
+
fail('Persistent CLI response exceeded the bounded transport limit.');
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
for (;;) {
|
|
67
|
+
const newline = buffer.indexOf('\n');
|
|
68
|
+
if (newline < 0)
|
|
69
|
+
break;
|
|
70
|
+
const line = buffer.slice(0, newline);
|
|
71
|
+
buffer = buffer.slice(newline + 1);
|
|
72
|
+
bytes = Buffer.byteLength(buffer);
|
|
73
|
+
if (!line.trim())
|
|
74
|
+
continue;
|
|
75
|
+
try {
|
|
76
|
+
const r = JSON.parse(line);
|
|
77
|
+
if (!r || r.jsonrpc !== '2.0' || typeof r.id !== 'number' || !pending.has(r.id) || Object.hasOwn(r, 'result') === Object.hasOwn(r, 'error'))
|
|
78
|
+
throw Error();
|
|
79
|
+
const p = pending.get(r.id);
|
|
80
|
+
if (Object.hasOwn(r, 'error') && (!r.error || typeof r.error !== 'object' || typeof r.error.code !== 'number' || !Number.isInteger(r.error.code) || typeof r.error.message !== 'string'))
|
|
81
|
+
throw Error();
|
|
82
|
+
pending.delete(r.id);
|
|
83
|
+
if (r.error)
|
|
84
|
+
p.reject(new InsightCommandError(r.error));
|
|
85
|
+
else
|
|
86
|
+
p.resolve(r.result);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
fail('Persistent CLI returned a malformed or uncorrelated response.');
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
await new Promise((resolve, reject) => { child.once('spawn', resolve); child.once('error', () => reject(new InsightTransportError('Could not start persistent CLI.', false))); });
|
|
95
|
+
return {
|
|
96
|
+
execute(argv) {
|
|
97
|
+
if (!Array.isArray(argv) || argv.length > 512 || argv.some(x => typeof x !== 'string' || x.includes('\0')) || Buffer.byteLength(JSON.stringify(argv)) > 1024 ** 2)
|
|
98
|
+
return Promise.reject(new TypeError('argv must be a bounded array of strings without NUL.'));
|
|
99
|
+
if (!accepting || failed || ended)
|
|
100
|
+
return Promise.reject(new InsightTransportError('Persistent CLI connection is closed.', false));
|
|
101
|
+
const args = [...argv];
|
|
102
|
+
const request = tail.then(() => {
|
|
103
|
+
if (failed || ended)
|
|
104
|
+
throw new InsightTransportError('Persistent CLI connection was lost before dispatch.', false);
|
|
105
|
+
const id = ++sequence;
|
|
106
|
+
return new Promise((resolve, reject) => {
|
|
107
|
+
pending.set(id, { resolve, reject });
|
|
108
|
+
try {
|
|
109
|
+
child.stdin.write(JSON.stringify({ jsonrpc: '2.0', id, method: 'cli.execute', params: { argv: args } }) + '\n');
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
fail('Persistent CLI input was lost; inspect request/status before retrying a changing command.');
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
tail = request.catch(() => { });
|
|
117
|
+
return request;
|
|
118
|
+
},
|
|
119
|
+
close() {
|
|
120
|
+
accepting = false;
|
|
121
|
+
return closePromise ??= tail.then(async () => { if (!failed && !ended)
|
|
122
|
+
child.stdin.end(); await exited; });
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}
|