@eyeauras/insight-cli 0.0.0-stage → 0.1.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 -0
- package/LICENSE +21 -0
- package/README.md +54 -2
- package/SKILL.md +55 -0
- package/dist/src/administration-results.d.ts +1 -0
- package/dist/src/administration-results.js +84 -0
- package/dist/src/administration.d.ts +14 -0
- package/dist/src/administration.js +120 -0
- package/dist/src/archive-contract.d.ts +6 -0
- package/dist/src/archive-contract.js +108 -0
- package/dist/src/archive.d.ts +12 -0
- package/dist/src/archive.js +116 -0
- package/dist/src/arguments.d.ts +14 -0
- package/dist/src/arguments.js +62 -0
- package/dist/src/artifacts.d.ts +17 -0
- package/dist/src/artifacts.js +115 -0
- package/dist/src/bin.d.ts +2 -0
- package/dist/src/bin.js +4 -0
- package/dist/src/cli.d.ts +10 -0
- package/dist/src/cli.js +387 -0
- package/dist/src/comment-effects.d.ts +3 -0
- package/dist/src/comment-effects.js +55 -0
- package/dist/src/contexts.d.ts +115 -0
- package/dist/src/contexts.js +1068 -0
- package/dist/src/coordinator.d.ts +41 -0
- package/dist/src/coordinator.js +160 -0
- package/dist/src/cross-results.d.ts +14 -0
- package/dist/src/cross-results.js +71 -0
- package/dist/src/cross.d.ts +3 -0
- package/dist/src/cross.js +43 -0
- package/dist/src/data-effects.d.ts +3 -0
- package/dist/src/data-effects.js +103 -0
- package/dist/src/debugger-results.d.ts +4 -0
- package/dist/src/debugger-results.js +151 -0
- package/dist/src/debugger-supervisor.d.ts +1 -0
- package/dist/src/debugger-supervisor.js +136 -0
- package/dist/src/debugger.d.ts +21 -0
- package/dist/src/debugger.js +267 -0
- package/dist/src/documentation-effects.d.ts +5 -0
- package/dist/src/documentation-effects.js +56 -0
- package/dist/src/extensions.d.ts +64 -0
- package/dist/src/extensions.js +289 -0
- package/dist/src/function-effects.d.ts +4 -0
- package/dist/src/function-effects.js +112 -0
- package/dist/src/ghidra.d.ts +50 -0
- package/dist/src/ghidra.js +468 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +1 -0
- package/dist/src/initial-model.d.ts +6 -0
- package/dist/src/initial-model.js +85 -0
- package/dist/src/json.d.ts +2 -0
- package/dist/src/json.js +8 -0
- package/dist/src/lifecycle.d.ts +49 -0
- package/dist/src/lifecycle.js +148 -0
- package/dist/src/memory-inputs.d.ts +8 -0
- package/dist/src/memory-inputs.js +56 -0
- package/dist/src/merge.d.ts +29 -0
- package/dist/src/merge.js +66 -0
- package/dist/src/model.d.ts +74 -0
- package/dist/src/model.js +67 -0
- package/dist/src/mutation-effects.d.ts +4 -0
- package/dist/src/mutation-effects.js +321 -0
- package/dist/src/mutation-inputs.d.ts +2 -0
- package/dist/src/mutation-inputs.js +71 -0
- package/dist/src/mutations.d.ts +4 -0
- package/dist/src/mutations.js +53 -0
- package/dist/src/native-inputs.d.ts +28 -0
- package/dist/src/native-inputs.js +171 -0
- package/dist/src/native-queries.d.ts +2 -0
- package/dist/src/native-queries.js +29 -0
- package/dist/src/projects.d.ts +31 -0
- package/dist/src/projects.js +301 -0
- package/dist/src/prototype-cli.d.ts +8 -0
- package/dist/src/prototype-cli.js +155 -0
- package/dist/src/queries.d.ts +5 -0
- package/dist/src/queries.js +84 -0
- package/dist/src/query-policy.d.ts +6 -0
- package/dist/src/query-policy.js +72 -0
- package/dist/src/query-results.d.ts +2 -0
- package/dist/src/query-results.js +224 -0
- package/dist/src/research.d.ts +184 -0
- package/dist/src/research.js +808 -0
- package/dist/src/saved-materials.d.ts +12 -0
- package/dist/src/saved-materials.js +196 -0
- package/dist/src/scripts.d.ts +19 -0
- package/dist/src/scripts.js +79 -0
- package/dist/src/sdk-contracts.d.ts +13 -0
- package/dist/src/sdk-contracts.js +37 -0
- package/dist/src/sdk-files.d.ts +29 -0
- package/dist/src/sdk-files.js +249 -0
- package/dist/src/sdk.d.ts +4 -0
- package/dist/src/sdk.js +125 -0
- package/dist/src/server.d.ts +44 -0
- package/dist/src/server.js +455 -0
- package/dist/src/store.d.ts +33 -0
- package/dist/src/store.js +178 -0
- package/dist/src/sync-policy.d.ts +22 -0
- package/dist/src/sync-policy.js +123 -0
- package/dist/src/transport.d.ts +23 -0
- package/dist/src/transport.js +135 -0
- package/dist/src/ui-results.d.ts +2 -0
- package/dist/src/ui-results.js +80 -0
- package/dist/src/ui.d.ts +17 -0
- package/dist/src/ui.js +88 -0
- package/dist/src/utility.d.ts +14 -0
- package/dist/src/utility.js +43 -0
- package/dist/src/version-tracking.d.ts +57 -0
- package/dist/src/version-tracking.js +200 -0
- package/dist/src/workspace.d.ts +199 -0
- package/dist/src/workspace.js +564 -0
- package/package.json +45 -4
- package/references/InsightRepositoryProbe.java +55 -0
- package/references/administration.md +33 -0
- package/references/archive.md +32 -0
- package/references/artifacts.md +47 -0
- package/references/debugger.md +40 -0
- package/references/extensions-and-sync.md +124 -0
- package/references/mutations.json +1424 -0
- package/references/native/InsightApplyDocumentation.java +48 -0
- package/references/native/InsightArchivePayloads.java +36 -0
- package/references/native/InsightBlockTransfer.java +57 -0
- package/references/native/InsightEmulation.java +93 -0
- package/references/native/InsightExtensions.java +36 -0
- package/references/native/InsightFunctionDatabases.java +98 -0
- package/references/native/InsightGuiMain.java +32 -0
- package/references/native/InsightHeadlessMain.java +10 -0
- package/references/native/InsightInitialImport.java +42 -0
- package/references/native/InsightInitialJournal.java +48 -0
- package/references/native/InsightInitialPublication.java +142 -0
- package/references/native/InsightKeytool.java +16 -0
- package/references/native/InsightNativeReceipt.java +31 -0
- package/references/native/InsightPlugins.java +34 -0
- package/references/native/InsightProjectFiles.java +92 -0
- package/references/native/InsightPublicationRecovery.java +63 -0
- package/references/native/InsightPythonWheels.java +31 -0
- package/references/native/InsightResearchQueries.java +188 -0
- package/references/native/InsightSdkFiles.java +142 -0
- package/references/native/InsightSdkScript.java +31 -0
- package/references/native/InsightSdkTools.java +338 -0
- package/references/native/InsightSdkTypes.java +132 -0
- package/references/native/InsightServerAdministration.java +82 -0
- package/references/native/InsightServerInit.java +62 -0
- package/references/native/InsightServerMain.java +43 -0
- package/references/native/InsightServerProbe.java +29 -0
- package/references/native/InsightSnapshotFields.java +68 -0
- package/references/native/InsightSnapshotInspect.java +189 -0
- package/references/native/InsightSnapshotPublisher.java +123 -0
- package/references/native/InsightStartDebugger.java +28 -0
- package/references/native/InsightStopDebugger.java +20 -0
- package/references/native/InsightTraceInspect.java +24 -0
- package/references/native/InsightTraceState.java +130 -0
- package/references/native/InsightTraceTools.java +114 -0
- package/references/native/InsightVersionTracking.java +91 -0
- package/references/native-workflows.md +112 -0
- package/references/queries.json +1761 -0
- package/references/query-semantics.md +96 -0
- package/references/recovery.md +53 -0
- package/references/research.md +101 -0
- package/references/setup.md +47 -0
- package/runtime/GhidraMCP-7.0.0.jar +0 -0
- package/runtime/GhidraMCP-LICENSE +202 -0
- package/runtime/GhidraMCP-NOTICE +10 -0
- package/runtime/provenance.json +11 -0
- package/skills/insight-re/SKILL.md +108 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/** Internal LLDB child owner. It has no public command or independent product IPC host. */
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { readFile, writeFile, mkdir, open, rename, realpath } from 'node:fs/promises';
|
|
4
|
+
import { join, resolve, delimiter } from 'node:path';
|
|
5
|
+
import { randomUUID } from 'node:crypto';
|
|
6
|
+
import { HttpBridge } from './transport.js';
|
|
7
|
+
async function main() {
|
|
8
|
+
const root = await realpath(process.argv[2]), binding = JSON.parse(await readFile(join(root, 'process.json'), 'utf8'));
|
|
9
|
+
if (root !== resolve(root) || binding.root !== root || binding.mode !== 'gui' || !Number.isSafeInteger(binding.pid))
|
|
10
|
+
throw Error('Invalid GUI owner');
|
|
11
|
+
const token = process.env.GHIDRA_MCP_AUTH_TOKEN;
|
|
12
|
+
if (!token)
|
|
13
|
+
throw Error('Missing GUI authority');
|
|
14
|
+
const url = new URL(binding.endpoint);
|
|
15
|
+
if (url.hostname !== '127.0.0.1' || url.protocol !== 'http:' || !url.port)
|
|
16
|
+
throw Error('Invalid GUI endpoint');
|
|
17
|
+
const info = await new HttpBridge(binding.endpoint, fetch, undefined, token, 5000).instance();
|
|
18
|
+
if (info.pid !== binding.pid || info.tcp_port !== Number(url.port) || !String(info.project_path ?? '').startsWith(root + '/'))
|
|
19
|
+
throw Error('GUI identity changed');
|
|
20
|
+
const launch = JSON.parse(await readFile(join(root, 'debugger-run/current.json'), 'utf8'));
|
|
21
|
+
if (launch.context !== binding.context || launch.target !== binding.target || !String(launch.executable).startsWith(root + '/debugger-run/' + launch.request + '/'))
|
|
22
|
+
throw Error('Launch identity changed');
|
|
23
|
+
const directory = join(root, 'debugger-supervisors');
|
|
24
|
+
await mkdir(directory, { recursive: true, mode: 0o700 });
|
|
25
|
+
if (await realpath(directory) !== directory)
|
|
26
|
+
throw Error('Supervisor directory changed');
|
|
27
|
+
const id = randomUUID(), path = join(directory, id + '.json'), resultPath = join(directory, id + '.result.json');
|
|
28
|
+
const receipt = { schema: 1, id, root, guiPid: binding.pid, ownerToken: binding.tokenSecret, context: binding.context, target: binding.target, supervisorPid: process.pid, childPid: 0, state: 'starting', inferiorTerminated: false };
|
|
29
|
+
async function saved() { const temporary = path + '.new', file = await open(temporary, 'w', 0o600); try {
|
|
30
|
+
await file.writeFile(JSON.stringify(receipt));
|
|
31
|
+
await file.sync();
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
await file.close();
|
|
35
|
+
} await rename(temporary, path); const parent = await open(directory, 'r'); try {
|
|
36
|
+
await parent.sync();
|
|
37
|
+
}
|
|
38
|
+
finally {
|
|
39
|
+
await parent.close();
|
|
40
|
+
} }
|
|
41
|
+
await saved();
|
|
42
|
+
const environment = { ...process.env };
|
|
43
|
+
delete environment.GHIDRA_MCP_AUTH_TOKEN;
|
|
44
|
+
delete environment.GHIDRA_SERVER_PASSWORD;
|
|
45
|
+
// Ghidra's launcher prepends its source tree to PYTHONPATH. Restore the owned wheel
|
|
46
|
+
// copy first, including the pinned SBProcess compatibility adapter, before LLDB imports it.
|
|
47
|
+
const python = await realpath(environment.INSIGHT_DEBUGGER_PYTHON ?? '');
|
|
48
|
+
if (!python.startsWith(root + '/debug-python-') || python !== environment.INSIGHT_DEBUGGER_PYTHON)
|
|
49
|
+
throw Error('Owned debugger Python identity changed');
|
|
50
|
+
environment.PYTHONPATH = python + delimiter + (environment.PYTHONPATH ?? '');
|
|
51
|
+
delete environment.INSIGHT_DEBUGGER_PYTHON;
|
|
52
|
+
let closing = false, finished = false;
|
|
53
|
+
process.once('SIGHUP', close);
|
|
54
|
+
process.once('SIGTERM', close);
|
|
55
|
+
// A separate session keeps the GUI terminal's SIGHUP from bypassing our orderly kill.
|
|
56
|
+
const child = spawn('/usr/bin/lldb', ['--no-lldbinit', '-o', 'settings set auto-confirm true', ...process.argv.slice(3)], { env: environment, detached: true, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
57
|
+
const exited = new Promise((ok, bad) => { child.once('error', bad); child.once('exit', () => { finished = true; ok(); }); });
|
|
58
|
+
void exited.catch(() => { });
|
|
59
|
+
receipt.childPid = child.pid ?? 0;
|
|
60
|
+
receipt.state = 'running';
|
|
61
|
+
let diagnostics = '';
|
|
62
|
+
const capture = (chunk) => { diagnostics = (diagnostics + chunk.toString()).slice(-65536); };
|
|
63
|
+
child.stdout.on('data', capture);
|
|
64
|
+
child.stderr.on('data', capture);
|
|
65
|
+
process.stdout.on('error', () => { });
|
|
66
|
+
process.stderr.on('error', () => { });
|
|
67
|
+
child.stdin.on('error', () => { });
|
|
68
|
+
child.stdout.pipe(process.stdout, { end: false });
|
|
69
|
+
child.stderr.pipe(process.stderr, { end: false });
|
|
70
|
+
process.stdin.on('data', data => { if (!closing)
|
|
71
|
+
child.stdin.write(data); });
|
|
72
|
+
const code = [
|
|
73
|
+
'import lldb,json,pathlib,os',
|
|
74
|
+
'p=lldb.debugger.GetSelectedTarget().GetProcess()',
|
|
75
|
+
'pid=p.GetProcessID() if p.IsValid() else 0',
|
|
76
|
+
'state=p.GetState() if p.IsValid() else lldb.eStateInvalid',
|
|
77
|
+
'done=state in (lldb.eStateInvalid,lldb.eStateUnloaded,lldb.eStateExited)',
|
|
78
|
+
'error=None',
|
|
79
|
+
'if not done:',
|
|
80
|
+
' e=p.Kill()',
|
|
81
|
+
' done=e.Success() and p.GetState()==lldb.eStateExited',
|
|
82
|
+
' error=None if done else str(e)',
|
|
83
|
+
'path=pathlib.Path(' + JSON.stringify(resultPath) + ')',
|
|
84
|
+
'path.write_text(json.dumps({"id":' + JSON.stringify(id) + ',"pid":pid,"inferiorTerminated":done,"error":error}))',
|
|
85
|
+
'os.chmod(str(path),0o600)',
|
|
86
|
+
].join('\n');
|
|
87
|
+
function close() {
|
|
88
|
+
if (closing || finished)
|
|
89
|
+
return;
|
|
90
|
+
closing = true;
|
|
91
|
+
process.stdin.pause();
|
|
92
|
+
child.stdout.unpipe(process.stdout);
|
|
93
|
+
child.stderr.unpipe(process.stderr);
|
|
94
|
+
child.stdout.resume();
|
|
95
|
+
child.stderr.resume();
|
|
96
|
+
const commands = 'script exec(' + JSON.stringify(code) + ')\nquit\n';
|
|
97
|
+
child.stdin.write(commands);
|
|
98
|
+
setTimeout(() => { if (!finished) {
|
|
99
|
+
child.kill('SIGINT');
|
|
100
|
+
child.stdin.write(commands);
|
|
101
|
+
} }, 3000).unref();
|
|
102
|
+
}
|
|
103
|
+
process.once('SIGINT', () => { if (!closing)
|
|
104
|
+
child.kill('SIGINT'); });
|
|
105
|
+
process.stdin.once('end', close);
|
|
106
|
+
process.stdin.once('error', close);
|
|
107
|
+
const monitor = setInterval(() => { try {
|
|
108
|
+
process.kill(binding.pid, 0);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
if (error.code === 'ESRCH')
|
|
112
|
+
close();
|
|
113
|
+
} }, 250);
|
|
114
|
+
try {
|
|
115
|
+
await saved();
|
|
116
|
+
await exited;
|
|
117
|
+
let result;
|
|
118
|
+
try {
|
|
119
|
+
result = JSON.parse(await readFile(resultPath, 'utf8'));
|
|
120
|
+
}
|
|
121
|
+
catch { }
|
|
122
|
+
receipt.inferiorTerminated = result?.id === id && result.inferiorTerminated === true;
|
|
123
|
+
receipt.state = receipt.inferiorTerminated ? 'closed' : 'failed';
|
|
124
|
+
Object.assign(receipt, { exitCode: child.exitCode, signalCode: child.signalCode });
|
|
125
|
+
if (!receipt.inferiorTerminated)
|
|
126
|
+
await writeFile(join(directory, id + '.diagnostic.log'), diagnostics, { mode: 0o600 });
|
|
127
|
+
await saved();
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
clearInterval(monitor);
|
|
131
|
+
process.stdin.pause();
|
|
132
|
+
}
|
|
133
|
+
// Keep a failed child outcome observable; never claim that killing LLDB proves inferior exit.
|
|
134
|
+
process.exitCode = receipt.inferiorTerminated ? 0 : 1;
|
|
135
|
+
}
|
|
136
|
+
await main().catch(() => { process.stderr.write('Owned debugger supervision failed; its context is retained.\n'); process.exitCode = 1; });
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type CommandContext, type CommandDefinition } from '@eyeauras/cli-factory';
|
|
2
|
+
import { Workspace, type Scope } from './workspace.js';
|
|
3
|
+
import { Research, type ResearchBackend } from './research.js';
|
|
4
|
+
export declare function debuggerWriteInputs(operation: string, p: Record<string, unknown>): void;
|
|
5
|
+
export declare class Debugger {
|
|
6
|
+
private readonly workspace;
|
|
7
|
+
private readonly research;
|
|
8
|
+
private readonly backend;
|
|
9
|
+
constructor(workspace: Workspace, research: Research, backend: () => Promise<ResearchBackend>);
|
|
10
|
+
private prepared;
|
|
11
|
+
read(selected: Scope, operation: string, p: Record<string, unknown>, trace?: string): Promise<{
|
|
12
|
+
connectionState: string;
|
|
13
|
+
pendingChanges: number;
|
|
14
|
+
value: unknown;
|
|
15
|
+
trace?: string | undefined;
|
|
16
|
+
session: string;
|
|
17
|
+
target: string;
|
|
18
|
+
}>;
|
|
19
|
+
change(selected: Scope, operation: string, p: Record<string, unknown>, callerId?: string, trace?: string): Promise<unknown>;
|
|
20
|
+
}
|
|
21
|
+
export declare function debuggerCommands(service: (c: CommandContext) => Debugger): CommandDefinition[];
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { command, Permission } from '@eyeauras/cli-factory';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { scope, address, number } from './research.js';
|
|
4
|
+
import { id, text, object, fail } from './model.js';
|
|
5
|
+
import { selectedOptions } from './arguments.js';
|
|
6
|
+
import { debuggerResult, debuggerProgramMetadata as metadata } from './debugger-results.js';
|
|
7
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
8
|
+
import { randomUUID } from 'node:crypto';
|
|
9
|
+
const lists = ['launch_offers', 'modules', 'list_breakpoints', 'stack_trace'];
|
|
10
|
+
const liveWrites = new Set(['write_memory', 'write_register']);
|
|
11
|
+
function traceInteger(value) {
|
|
12
|
+
const encoded = text(String(value), 32);
|
|
13
|
+
if (!/^-?\d+$/.test(encoded))
|
|
14
|
+
fail('invalid', 'Trace positions require exact signed decimal integers.');
|
|
15
|
+
const n = BigInt(encoded);
|
|
16
|
+
if (n < -(1n << 63n) || n >= (1n << 63n))
|
|
17
|
+
fail('invalid', 'Trace position exceeds signed 64-bit range.');
|
|
18
|
+
return n.toString();
|
|
19
|
+
}
|
|
20
|
+
export function debuggerWriteInputs(operation, p) {
|
|
21
|
+
if (operation === 'write_memory') {
|
|
22
|
+
for (const key of ['expect_hex', 'bytes_hex'])
|
|
23
|
+
if (typeof p[key] !== 'string' || !/^(?:[a-f0-9]{2}){1,4096}$/i.test(p[key]))
|
|
24
|
+
fail('invalid', 'Live memory requires 1–4096 complete hex bytes.');
|
|
25
|
+
if (String(p.expect_hex).length !== String(p.bytes_hex).length)
|
|
26
|
+
fail('invalid', 'Live memory expected/replacement lengths must match.');
|
|
27
|
+
}
|
|
28
|
+
if (operation === 'write_register')
|
|
29
|
+
for (const key of ['expect_value', 'value'])
|
|
30
|
+
if (!/^(?:0x)?[a-f0-9]{1,128}$/i.test(String(p[key])))
|
|
31
|
+
fail('invalid', 'Register values must be exact unsigned hex integers.');
|
|
32
|
+
}
|
|
33
|
+
export class Debugger {
|
|
34
|
+
workspace;
|
|
35
|
+
research;
|
|
36
|
+
backend;
|
|
37
|
+
constructor(workspace, research, backend) {
|
|
38
|
+
this.workspace = workspace;
|
|
39
|
+
this.research = research;
|
|
40
|
+
this.backend = backend;
|
|
41
|
+
}
|
|
42
|
+
async prepared(selected, trace, reading = false) {
|
|
43
|
+
const backend = await this.backend(), unresolved = this.workspace.unresolved(selected);
|
|
44
|
+
if (!backend.debuggerCall || !backend.debuggerStart || !backend.debuggerSaved)
|
|
45
|
+
fail('configuration', 'Selected backend lacks debugger support.');
|
|
46
|
+
if (unresolved.length) {
|
|
47
|
+
if (!reading || unresolved.some(request => object(request.execution ?? {}).kind !== 'debugger'))
|
|
48
|
+
fail('blocked', 'Selected context retains an unresolved operation.');
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
await backend.open(selected);
|
|
52
|
+
await backend.mode(selected, 'gui');
|
|
53
|
+
await backend.debuggerStart(selected);
|
|
54
|
+
}
|
|
55
|
+
if (trace) {
|
|
56
|
+
if (!backend.debuggerSelect || !this.workspace.context(selected).debugger?.traces.some(item => item.id === trace))
|
|
57
|
+
fail('notFound', 'Trace is not saved in the selected context.');
|
|
58
|
+
await backend.debuggerSelect(selected, trace);
|
|
59
|
+
}
|
|
60
|
+
return backend;
|
|
61
|
+
}
|
|
62
|
+
async read(selected, operation, p, trace) {
|
|
63
|
+
return this.research.ordered(selected, async () => {
|
|
64
|
+
const backend = await this.prepared(selected, trace, true);
|
|
65
|
+
let value;
|
|
66
|
+
if (operation !== 'traces')
|
|
67
|
+
value = debuggerResult(operation, p, await backend.debuggerCall(selected, operation, Object.fromEntries(Object.entries(p).filter(([key]) => !['limit', 'offset'].includes(key)))));
|
|
68
|
+
const saved = await backend.debuggerSaved(selected), current = this.workspace.context(selected), snapshot = await backend.saved(selected);
|
|
69
|
+
const changed = current.workingSnapshot && await metadata(backend, current.workingSnapshot) !== await metadata(backend, snapshot);
|
|
70
|
+
const complexChanges = changed ? [...current.complexChanges, 'debugger-metadata-' + randomUUID()] : current.complexChanges;
|
|
71
|
+
this.workspace.saveContext(selected, current.generation, { debugger: saved, workingSnapshot: snapshot, complexChanges });
|
|
72
|
+
if (operation === 'traces')
|
|
73
|
+
value = saved.traces.map(row => ({ trace: row.id, name: this.workspace.target(selected.target).name, current: row.id === saved.active, snap: row.snap, module_count: row.modules, thread_count: row.threads, connected: row.connected }));
|
|
74
|
+
if (operation === 'traces' || lists.includes(operation)) {
|
|
75
|
+
const rows = value, offset = Number(p.offset ?? 0), limit = Number(p.limit ?? 50);
|
|
76
|
+
value = { items: rows.slice(offset, offset + limit), total: rows.length, offset, limit, truncated: offset + limit < rows.length };
|
|
77
|
+
}
|
|
78
|
+
return { ...selected, ...(saved.active ? { trace: saved.active } : {}), connectionState: saved.connected ? 'connected' : saved.traces.length ? 'disconnected' : 'idle', pendingChanges: current.scalars.length + complexChanges.length, value };
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
async change(selected, operation, p, callerId, trace) {
|
|
82
|
+
const reserved = this.workspace.reserveRequest(selected, 'ghidra.debugger.' + operation, { ...p, ...(trace ? { trace } : {}) }, callerId);
|
|
83
|
+
return this.research.ordered(selected, async () => {
|
|
84
|
+
const request = this.workspace.request(reserved.request.id);
|
|
85
|
+
if (reserved.replay && request.state !== 'accepted') {
|
|
86
|
+
if (request.state === 'applied')
|
|
87
|
+
return request.result;
|
|
88
|
+
const result = object(request.result ?? {});
|
|
89
|
+
if (request.state === 'failed' && result.reason === 'disconnected')
|
|
90
|
+
fail('disconnected', 'Selected trace has no live target.', result);
|
|
91
|
+
if (request.state === 'failed' && result.reason === 'busy')
|
|
92
|
+
fail('busy', 'This context already has a live debug target; use another session for another process.', result);
|
|
93
|
+
if (request.state === 'failed' && result.reason === 'native_precondition')
|
|
94
|
+
fail('rejected', 'The native live precondition failed; the target write was not started.', result);
|
|
95
|
+
fail('unknown', 'Debugger action is retained and is not repeated.', request.result);
|
|
96
|
+
}
|
|
97
|
+
const backend = await this.prepared(selected, trace), context = this.workspace.context(selected);
|
|
98
|
+
if (operation === 'launch' && (await backend.debuggerSaved(selected)).connected) {
|
|
99
|
+
const result = { ...selected, state: 'not_started', reason: 'busy' };
|
|
100
|
+
this.workspace.recordRequest(request.id, 'failed', result);
|
|
101
|
+
fail('busy', 'This context already has a live debug target; use another session for another process.', result);
|
|
102
|
+
}
|
|
103
|
+
const before = debuggerResult('status', {}, await backend.debuggerCall(selected, 'status'));
|
|
104
|
+
if (operation !== 'launch' && object(before).target_connected !== true) {
|
|
105
|
+
const result = { ...selected, state: 'not_started', reason: 'disconnected' };
|
|
106
|
+
this.workspace.recordRequest(request.id, 'failed', result);
|
|
107
|
+
fail('disconnected', 'Selected trace has no live target.', result);
|
|
108
|
+
}
|
|
109
|
+
const beforeMetadata = context.workingSnapshot ? await metadata(backend, context.workingSnapshot) : undefined;
|
|
110
|
+
this.workspace.startRequest(request.id, { kind: 'debugger', operation, generation: context.generation, before });
|
|
111
|
+
let saved, savedSnapshot, metadataChanged = false;
|
|
112
|
+
try {
|
|
113
|
+
let raw;
|
|
114
|
+
if (operation === 'launch') {
|
|
115
|
+
if (!backend.debuggerLaunch)
|
|
116
|
+
fail('configuration', 'Owned target staging is unavailable.');
|
|
117
|
+
raw = await backend.debuggerLaunch(selected, p, request.id);
|
|
118
|
+
}
|
|
119
|
+
else
|
|
120
|
+
raw = await backend.debuggerCall(selected, operation, p);
|
|
121
|
+
if (liveWrites.has(operation) && object(raw).status === 'rejected' && object(raw).target_effect_started === false) {
|
|
122
|
+
const result = { ...selected, state: 'not_started', reason: 'native_precondition', value: raw };
|
|
123
|
+
this.workspace.recordRequest(request.id, 'failed', result);
|
|
124
|
+
fail('rejected', 'Native live preconditions rejected the write before target dispatch.', result);
|
|
125
|
+
}
|
|
126
|
+
const value = debuggerResult(operation, p, raw);
|
|
127
|
+
if (['resume', 'interrupt', 'step'].includes(operation)) {
|
|
128
|
+
const wanted = operation === 'resume' ? 'RUNNING' : 'STOPPED';
|
|
129
|
+
let observed = false;
|
|
130
|
+
for (let attempt = 0; attempt < 40; attempt++) {
|
|
131
|
+
const status = object(debuggerResult('status', {}, await backend.debuggerCall(selected, 'status')));
|
|
132
|
+
if (status.execution_state === wanted || operation === 'resume' && status.execution_state === 'STOPPED' && BigInt(String(status.snap ?? 0)) > BigInt(String(object(before).snap ?? 0))) {
|
|
133
|
+
observed = true;
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
await delay(100);
|
|
137
|
+
}
|
|
138
|
+
if (!observed)
|
|
139
|
+
fail('partial', 'Debugger execution state did not settle after the action.');
|
|
140
|
+
}
|
|
141
|
+
if (operation === 'set_breakpoint' || operation === 'remove_breakpoint') {
|
|
142
|
+
let observed = false;
|
|
143
|
+
for (let attempt = 0; attempt < 50; attempt++) {
|
|
144
|
+
const rows = debuggerResult('list_breakpoints', {}, await backend.debuggerCall(selected, 'list_breakpoints'));
|
|
145
|
+
if (rows.some(row => address(row.address) === address(p.address) && Number(row.trace_location_count) > 0) === (operation === 'set_breakpoint')) {
|
|
146
|
+
observed = true;
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
await delay(100);
|
|
150
|
+
}
|
|
151
|
+
if (!observed)
|
|
152
|
+
fail('partial', 'Breakpoint inventory did not verify the action.');
|
|
153
|
+
}
|
|
154
|
+
saved = await backend.debuggerSaved(selected);
|
|
155
|
+
savedSnapshot = await backend.saved(selected);
|
|
156
|
+
metadataChanged = beforeMetadata !== undefined && beforeMetadata !== await metadata(backend, savedSnapshot);
|
|
157
|
+
const complexChanges = metadataChanged ? [...context.complexChanges, request.id] : context.complexChanges;
|
|
158
|
+
const result = { ...selected, state: 'applied', ...(saved.active ? { trace: saved.active } : {}), pendingChanges: context.scalars.length + complexChanges.length, sharedState: context.scalars.length || complexChanges.length ? 'pending' : 'shared', value };
|
|
159
|
+
this.workspace.completeSavedRequest(selected, request.id, context.generation, { debugger: saved, workingSnapshot: savedSnapshot, complexChanges }, result);
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
if (this.workspace.request(request.id).state === 'failed')
|
|
164
|
+
throw error;
|
|
165
|
+
try {
|
|
166
|
+
saved ??= await backend.debuggerSaved(selected);
|
|
167
|
+
}
|
|
168
|
+
catch { }
|
|
169
|
+
try {
|
|
170
|
+
savedSnapshot ??= await backend.saved(selected);
|
|
171
|
+
metadataChanged = beforeMetadata !== undefined && beforeMetadata !== await metadata(backend, savedSnapshot);
|
|
172
|
+
}
|
|
173
|
+
catch { }
|
|
174
|
+
this.workspace.recordRequest(request.id, 'unknown', { ...selected, state: 'unknown', operation, ...(saved ? { savedDebugger: saved } : {}), ...(savedSnapshot ? { savedSnapshot, metadataChanged } : {}) });
|
|
175
|
+
throw error;
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
export function debuggerCommands(service) {
|
|
181
|
+
const leaves = [], request = { flags: '--request-id <id>', description: 'Retrieve the outcome without repeating execution' }, trace = { flags: '--trace <id>', description: 'Select a saved trace within this context' };
|
|
182
|
+
const operations = ['launch', 'launch_offers', 'status', 'traces', 'resume', 'interrupt', 'step', 'set_breakpoint', 'remove_breakpoint', 'list_breakpoints', 'registers', 'read_memory', 'stack_trace', 'modules', 'static_to_dynamic', 'dynamic_to_static', 'write_memory', 'write_register', 'timeline', 'objects', 'object_values', 'schedule'];
|
|
183
|
+
for (const operation of operations) {
|
|
184
|
+
const changing = ['launch', 'resume', 'interrupt', 'step', 'set_breakpoint', 'remove_breakpoint'].includes(operation) || liveWrites.has(operation), at = ['set_breakpoint', 'remove_breakpoint', 'read_memory', 'static_to_dynamic', 'dynamic_to_static', 'write_memory'].includes(operation), paged = operation === 'traces' || lists.includes(operation);
|
|
185
|
+
const options = [...selectedOptions, ...(operation === 'launch' ? [] : [trace]), ...(at ? [{ flags: '--address <address>', description: 'Explicit static or trace address', required: true }] : []),
|
|
186
|
+
...(operation === 'read_memory' ? [{ flags: '--size <bytes>', description: 'Read 1–4096 bytes', defaultValue: 64 }] : []), ...(operation === 'stack_trace' ? [{ flags: '--depth <count>', description: 'Maximum frames 1–1000', defaultValue: 20 }] : []),
|
|
187
|
+
...(operation === 'write_memory' ? [{ flags: '--expect-hex <hex>', description: 'Exact current bytes before stopped-target write', required: true }, { flags: '--bytes-hex <hex>', description: 'Equal-length replacement bytes, at most 4096', required: true }] : []),
|
|
188
|
+
...(operation === 'write_register' ? [{ flags: '--register <name>', description: 'Exact target platform register', required: true }, { flags: '--expect-value <hex>', description: 'Exact current unsigned hex value', required: true }, { flags: '--value <hex>', description: 'Replacement unsigned hex value within native register width', required: true }] : []),
|
|
189
|
+
...(['timeline', 'objects'].includes(operation) ? [{ flags: '--limit <count>', description: 'Maximum returned rows 1–1000', defaultValue: 100 }] : []),
|
|
190
|
+
...(operation === 'timeline' ? [{ flags: '--before-snap <integer>', description: 'Latest included snapshot key; result gives next-before-snap' }] : []),
|
|
191
|
+
...(operation === 'objects' ? [{ flags: '--path-prefix <text>', description: 'Literal canonical path prefix; @target hides the owned staging root', defaultValue: '' }, { flags: '--max-scan <count>', description: 'Maximum visited objects 1–200000', defaultValue: 10000 }] : []),
|
|
192
|
+
...(['objects', 'object_values'].includes(operation) ? [{ flags: '--snap <integer>', description: 'Snapshot key (default selected time)' }] : []),
|
|
193
|
+
...(operation === 'object_values' ? [{ flags: '--object-id <integer>', description: 'Object ID returned by objects; scoped to the selected trace', required: true }, { flags: '--key <name>', description: 'Exact native attribute/element key', required: true },
|
|
194
|
+
{ flags: '--from-snap <integer>', description: 'Start of a bounded history range' }, { flags: '--to-snap <integer>', description: 'End of a range of at most 1000 snaps' }] : []),
|
|
195
|
+
...(operation === 'schedule' ? [{ flags: '--schedule <time>', description: 'Parse and validate a native time schedule without executing it', required: true }] : []),
|
|
196
|
+
...(paged ? [{ flags: '--limit <count>', description: 'Page size 1–100', defaultValue: 50 }, { flags: '--offset <count>', description: 'Page offset', defaultValue: 0 }] : []),
|
|
197
|
+
...(operation === 'step' ? [{ flags: '--kind <kind>', description: 'into, over or out', defaultValue: 'into' }] : []),
|
|
198
|
+
...(operation === 'launch' ? [{ flags: '--args <text>', description: 'Target command-line arguments', defaultValue: '' }, { flags: '--cwd <directory>', description: 'Target working directory (default owned run folder)' },
|
|
199
|
+
{ flags: '--offer <name>', description: 'Launch offer title or configuration name', defaultValue: process.platform === 'win32' ? 'dbgeng' : process.platform === 'darwin' ? 'lldb' : 'gdb' }, { flags: '--timeout-seconds <count>', description: 'Launch wait 1–300 seconds', defaultValue: 60 }] : []), ...(changing ? [request] : [])];
|
|
200
|
+
leaves.push(command(operation.replaceAll('_', '-'), 'Use the selected context debugger', ({ options: o }, c) => {
|
|
201
|
+
const p = {};
|
|
202
|
+
if (at)
|
|
203
|
+
p.address = address(o.address);
|
|
204
|
+
if (paged) {
|
|
205
|
+
p.limit = number(o.limit, 100, 1);
|
|
206
|
+
p.offset = number(o.offset, 2147483647);
|
|
207
|
+
}
|
|
208
|
+
if (operation === 'read_memory')
|
|
209
|
+
p.size = number(o.size, 4096, 1);
|
|
210
|
+
if (operation === 'stack_trace')
|
|
211
|
+
p.depth = number(o.depth, 1000, 1);
|
|
212
|
+
if (operation === 'write_memory') {
|
|
213
|
+
p.expect_hex = text(o.expectHex, 8192);
|
|
214
|
+
p.bytes_hex = text(o.bytesHex, 8192);
|
|
215
|
+
}
|
|
216
|
+
if (operation === 'write_register') {
|
|
217
|
+
p.register = text(o.register, 128);
|
|
218
|
+
p.expect_value = text(o.expectValue, 130);
|
|
219
|
+
p.value = text(o.value, 130);
|
|
220
|
+
}
|
|
221
|
+
if (liveWrites.has(operation))
|
|
222
|
+
debuggerWriteInputs(operation, p);
|
|
223
|
+
for (const [key, publicKey] of [['snap', 'snap'], ['before_snap', 'beforeSnap'], ['from_snap', 'fromSnap'], ['to_snap', 'toSnap']])
|
|
224
|
+
if (o[publicKey] !== undefined)
|
|
225
|
+
p[key] = traceInteger(o[publicKey]);
|
|
226
|
+
if (['timeline', 'objects'].includes(operation))
|
|
227
|
+
p.limit = number(o.limit, 1000, 1);
|
|
228
|
+
if (operation === 'objects') {
|
|
229
|
+
const prefix = typeof o.pathPrefix === 'string' ? o.pathPrefix : '';
|
|
230
|
+
if (prefix.length > 4096 || prefix.includes('\0'))
|
|
231
|
+
fail('invalid', 'Trace path prefix exceeds its text bound.');
|
|
232
|
+
p.path_prefix = prefix;
|
|
233
|
+
p.max_scan = number(o.maxScan, 200000, 1);
|
|
234
|
+
}
|
|
235
|
+
if (operation === 'object_values') {
|
|
236
|
+
p.object_id = traceInteger(o.objectId);
|
|
237
|
+
if (BigInt(String(p.object_id)) < 0n)
|
|
238
|
+
fail('invalid', 'Object ID must be nonnegative.');
|
|
239
|
+
p.key = text(o.key, 1024);
|
|
240
|
+
if (p.from_snap !== undefined && p.to_snap !== undefined && (BigInt(String(p.to_snap)) < BigInt(String(p.from_snap)) || BigInt(String(p.to_snap)) - BigInt(String(p.from_snap)) > 999n))
|
|
241
|
+
fail('invalid', 'Trace value history is bounded to 1000 snaps.');
|
|
242
|
+
}
|
|
243
|
+
if (operation === 'schedule')
|
|
244
|
+
p.schedule = text(o.schedule, 4096);
|
|
245
|
+
if (operation === 'step') {
|
|
246
|
+
p.kind = text(o.kind);
|
|
247
|
+
if (!['into', 'over', 'out'].includes(String(p.kind)))
|
|
248
|
+
fail('invalid', 'Step kind must be into, over or out.');
|
|
249
|
+
}
|
|
250
|
+
if (operation === 'launch') {
|
|
251
|
+
if (typeof o.args !== 'string' || o.args.length > 8192 || o.args.includes('\0'))
|
|
252
|
+
fail('invalid', 'Arguments must be bounded text.');
|
|
253
|
+
p.args = o.args;
|
|
254
|
+
p.offer = text(o.offer, 512);
|
|
255
|
+
p.timeout_seconds = number(o.timeoutSeconds, 300, 1);
|
|
256
|
+
if (o.cwd) {
|
|
257
|
+
p.cwd = resolve(c.cwd, text(o.cwd, 4096));
|
|
258
|
+
if (/[\r\n\0]/.test(String(p.cwd)))
|
|
259
|
+
fail('invalid', 'Debugger working directory must be a single filesystem path.');
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
const selected = scope(o), selectedTrace = o.trace === undefined ? undefined : id(o.trace);
|
|
263
|
+
return changing ? service(c).change(selected, operation, p, o.requestId === undefined ? undefined : id(o.requestId), selectedTrace) : service(c).read(selected, operation, p, selectedTrace);
|
|
264
|
+
}, { permission: operation === 'remove_breakpoint' || liveWrites.has(operation) ? 'Destructive' : changing ? Permission.Update : Permission.ReadOnly, options }));
|
|
265
|
+
}
|
|
266
|
+
return [command('debugger', 'Debug the selected immutable target in an owned GUI', leaves)];
|
|
267
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ResearchBackend } from './research.js';
|
|
2
|
+
import type { Artifact } from './artifacts.js';
|
|
3
|
+
export declare function verifyDocumentation(backend: ResearchBackend, snapshot: Artifact, p: Record<string, unknown>): Promise<{
|
|
4
|
+
function: Record<string, unknown>;
|
|
5
|
+
}>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { address } from './research.js';
|
|
2
|
+
import { object, fail } from './model.js';
|
|
3
|
+
const equal = (actual, expected, label) => { if (actual !== expected)
|
|
4
|
+
fail('partial', `Saved documentation ${label} differs from the requested effect.`); };
|
|
5
|
+
function offset(at, value) {
|
|
6
|
+
const normalized = address(at), parts = normalized.split(':'), hex = parts.pop(), distance = BigInt(String(value));
|
|
7
|
+
if (distance < 0n || distance > 0x7fffffffffffffffn)
|
|
8
|
+
fail('invalid', 'Documentation offsets must be nonnegative signed 64-bit integers.');
|
|
9
|
+
return (parts.length ? parts[0] + ':' : '') + (BigInt('0x' + hex) + distance).toString(16);
|
|
10
|
+
}
|
|
11
|
+
export async function verifyDocumentation(backend, snapshot, p) {
|
|
12
|
+
if (!backend.inspect)
|
|
13
|
+
fail('configuration', 'Documentation requires saved-state inspection.');
|
|
14
|
+
const document = object(p.json_body), at = address(document.target_address), inspect = (kind, parameters) => backend.inspect(snapshot, { kind, ...parameters });
|
|
15
|
+
const value = await inspect('function', { address: at });
|
|
16
|
+
equal(value.exists, true, 'function presence');
|
|
17
|
+
if (document.function_name)
|
|
18
|
+
equal(value.name, document.function_name, 'function name');
|
|
19
|
+
if (document.calling_convention)
|
|
20
|
+
equal(value.callingConvention, document.calling_convention, 'calling convention');
|
|
21
|
+
const checkType = async (actual, name) => { const wanted = await inspect('resolve_type', { name }); equal(wanted.exists, true, 'type presence'); equal(actual, object(wanted.type).name, 'type'); };
|
|
22
|
+
if (document.return_type)
|
|
23
|
+
await checkType(value.returnType, document.return_type);
|
|
24
|
+
if (document.plate_comment) {
|
|
25
|
+
const comments = await inspect('comments', { address: at });
|
|
26
|
+
equal(comments.plate, document.plate_comment, 'plate comment');
|
|
27
|
+
}
|
|
28
|
+
const requested = document.parameters ?? [];
|
|
29
|
+
if (!Array.isArray(requested) || !Array.isArray(value.parameters))
|
|
30
|
+
fail('partial', 'Documentation parameter inventory is absent.');
|
|
31
|
+
for (const raw of requested) {
|
|
32
|
+
const parameter = object(raw), ordinal = Number(parameter.ordinal);
|
|
33
|
+
if (!Number.isSafeInteger(ordinal) || ordinal < 0 || ordinal >= value.parameters.length)
|
|
34
|
+
fail('partial', 'Documentation parameter ordinal is absent.');
|
|
35
|
+
const saved = object(value.parameters[ordinal]);
|
|
36
|
+
if (parameter.name)
|
|
37
|
+
equal(saved.name, parameter.name, 'parameter name');
|
|
38
|
+
if (parameter.type)
|
|
39
|
+
await checkType(saved.type, parameter.type);
|
|
40
|
+
}
|
|
41
|
+
if (document.comments !== undefined && !Array.isArray(document.comments) || document.labels !== undefined && !Array.isArray(document.labels))
|
|
42
|
+
fail('invalid', 'Documentation comments and labels must be arrays.');
|
|
43
|
+
for (const raw of document.comments ?? []) {
|
|
44
|
+
const comment = object(raw), saved = await inspect('comments', { address: offset(at, comment.relative_offset) });
|
|
45
|
+
if (comment.eol_comment)
|
|
46
|
+
equal(saved.eol, comment.eol_comment, 'EOL comment');
|
|
47
|
+
if (comment.pre_comment)
|
|
48
|
+
equal(saved.pre, comment.pre_comment, 'PRE comment');
|
|
49
|
+
}
|
|
50
|
+
for (const raw of document.labels ?? []) {
|
|
51
|
+
const label = object(raw), saved = await inspect('symbols', { address: offset(at, label.relative_offset) });
|
|
52
|
+
if (!Array.isArray(saved.symbols) || !saved.symbols.some(symbol => object(symbol).name === label.name))
|
|
53
|
+
fail('partial', 'Documented label is absent from saved state.');
|
|
54
|
+
}
|
|
55
|
+
return { function: value };
|
|
56
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { type CommandDefinition } from '@eyeauras/cli-factory';
|
|
2
|
+
interface ModuleFile {
|
|
3
|
+
name: string;
|
|
4
|
+
sha256: string;
|
|
5
|
+
size: number;
|
|
6
|
+
}
|
|
7
|
+
interface Extension {
|
|
8
|
+
name: string;
|
|
9
|
+
version: string;
|
|
10
|
+
sha256: string;
|
|
11
|
+
files: ModuleFile[];
|
|
12
|
+
enabled: boolean;
|
|
13
|
+
}
|
|
14
|
+
interface ExtensionState {
|
|
15
|
+
schema: 1;
|
|
16
|
+
modules: Extension[];
|
|
17
|
+
}
|
|
18
|
+
export declare function extensionState(directory: string): ExtensionState;
|
|
19
|
+
export declare function inspectExtension(directory: string): Promise<Extension>;
|
|
20
|
+
export declare function extensionDirectory(directory: string, module: Extension): string;
|
|
21
|
+
/** Verify immutable owned copies; a changed input never joins an existing JVM's classpath. */
|
|
22
|
+
export declare function managedExtensions(directory?: string): Promise<{
|
|
23
|
+
root: string;
|
|
24
|
+
name: string;
|
|
25
|
+
version: string;
|
|
26
|
+
sha256: string;
|
|
27
|
+
files: ModuleFile[];
|
|
28
|
+
enabled: boolean;
|
|
29
|
+
}[]>;
|
|
30
|
+
export declare class Extensions {
|
|
31
|
+
readonly directory: string;
|
|
32
|
+
constructor(directory: string);
|
|
33
|
+
list(): {
|
|
34
|
+
items: {
|
|
35
|
+
fileCount: number;
|
|
36
|
+
name: string;
|
|
37
|
+
version: string;
|
|
38
|
+
sha256: string;
|
|
39
|
+
enabled: boolean;
|
|
40
|
+
}[];
|
|
41
|
+
runtimeBoundary: string;
|
|
42
|
+
};
|
|
43
|
+
private write;
|
|
44
|
+
private ordered;
|
|
45
|
+
install(directory: string, expected: string): Promise<{
|
|
46
|
+
state: string;
|
|
47
|
+
name: string;
|
|
48
|
+
sha256: string;
|
|
49
|
+
enabled: boolean;
|
|
50
|
+
restartRequired: boolean;
|
|
51
|
+
}>;
|
|
52
|
+
private consume;
|
|
53
|
+
enable(name: string, enabled: boolean): Promise<{
|
|
54
|
+
state: string;
|
|
55
|
+
name: string;
|
|
56
|
+
sha256: string;
|
|
57
|
+
restartRequired: boolean;
|
|
58
|
+
}>;
|
|
59
|
+
private change;
|
|
60
|
+
}
|
|
61
|
+
export declare function extensionCommands(service: (c: import('@eyeauras/cli-factory').CommandContext) => Extensions): CommandDefinition[];
|
|
62
|
+
export declare const extensionJvmArguments: (roots: string[]) => string[];
|
|
63
|
+
export declare function extensionInventoryCommands(backend: (c: import('@eyeauras/cli-factory').CommandContext) => () => Promise<import('./research.js').ResearchBackend>): CommandDefinition[];
|
|
64
|
+
export {};
|