@eyeauras/insight-cli 0.0.0-stage → 0.2.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.
Files changed (174) hide show
  1. package/.cli-package-build.json +1 -0
  2. package/LICENSE +21 -0
  3. package/README.md +60 -2
  4. package/SKILL.md +58 -0
  5. package/dist/src/administration-results.d.ts +1 -0
  6. package/dist/src/administration-results.js +84 -0
  7. package/dist/src/administration.d.ts +14 -0
  8. package/dist/src/administration.js +134 -0
  9. package/dist/src/archive-contract.d.ts +6 -0
  10. package/dist/src/archive-contract.js +108 -0
  11. package/dist/src/archive.d.ts +12 -0
  12. package/dist/src/archive.js +116 -0
  13. package/dist/src/arguments.d.ts +14 -0
  14. package/dist/src/arguments.js +62 -0
  15. package/dist/src/artifacts.d.ts +17 -0
  16. package/dist/src/artifacts.js +115 -0
  17. package/dist/src/bin.d.ts +2 -0
  18. package/dist/src/bin.js +4 -0
  19. package/dist/src/cli.d.ts +10 -0
  20. package/dist/src/cli.js +419 -0
  21. package/dist/src/comment-effects.d.ts +3 -0
  22. package/dist/src/comment-effects.js +55 -0
  23. package/dist/src/contexts.d.ts +115 -0
  24. package/dist/src/contexts.js +1078 -0
  25. package/dist/src/coordinator.d.ts +41 -0
  26. package/dist/src/coordinator.js +160 -0
  27. package/dist/src/cross-results.d.ts +14 -0
  28. package/dist/src/cross-results.js +71 -0
  29. package/dist/src/cross.d.ts +3 -0
  30. package/dist/src/cross.js +43 -0
  31. package/dist/src/data-effects.d.ts +3 -0
  32. package/dist/src/data-effects.js +103 -0
  33. package/dist/src/debugger-results.d.ts +4 -0
  34. package/dist/src/debugger-results.js +151 -0
  35. package/dist/src/debugger-supervisor.d.ts +1 -0
  36. package/dist/src/debugger-supervisor.js +136 -0
  37. package/dist/src/debugger.d.ts +21 -0
  38. package/dist/src/debugger.js +267 -0
  39. package/dist/src/documentation-effects.d.ts +5 -0
  40. package/dist/src/documentation-effects.js +56 -0
  41. package/dist/src/extensions.d.ts +64 -0
  42. package/dist/src/extensions.js +289 -0
  43. package/dist/src/function-effects.d.ts +4 -0
  44. package/dist/src/function-effects.js +112 -0
  45. package/dist/src/ghidra.d.ts +50 -0
  46. package/dist/src/ghidra.js +468 -0
  47. package/dist/src/index.d.ts +2 -0
  48. package/dist/src/index.js +1 -0
  49. package/dist/src/initial-model.d.ts +6 -0
  50. package/dist/src/initial-model.js +85 -0
  51. package/dist/src/json.d.ts +2 -0
  52. package/dist/src/json.js +8 -0
  53. package/dist/src/lifecycle.d.ts +49 -0
  54. package/dist/src/lifecycle.js +154 -0
  55. package/dist/src/memory-inputs.d.ts +8 -0
  56. package/dist/src/memory-inputs.js +56 -0
  57. package/dist/src/merge.d.ts +29 -0
  58. package/dist/src/merge.js +66 -0
  59. package/dist/src/model.d.ts +74 -0
  60. package/dist/src/model.js +67 -0
  61. package/dist/src/mutation-effects.d.ts +4 -0
  62. package/dist/src/mutation-effects.js +321 -0
  63. package/dist/src/mutation-inputs.d.ts +2 -0
  64. package/dist/src/mutation-inputs.js +71 -0
  65. package/dist/src/mutations.d.ts +4 -0
  66. package/dist/src/mutations.js +53 -0
  67. package/dist/src/native-inputs.d.ts +28 -0
  68. package/dist/src/native-inputs.js +182 -0
  69. package/dist/src/native-queries.d.ts +2 -0
  70. package/dist/src/native-queries.js +29 -0
  71. package/dist/src/projects.d.ts +31 -0
  72. package/dist/src/projects.js +301 -0
  73. package/dist/src/prototype-cli.d.ts +8 -0
  74. package/dist/src/prototype-cli.js +155 -0
  75. package/dist/src/queries.d.ts +5 -0
  76. package/dist/src/queries.js +84 -0
  77. package/dist/src/query-policy.d.ts +6 -0
  78. package/dist/src/query-policy.js +72 -0
  79. package/dist/src/query-results.d.ts +2 -0
  80. package/dist/src/query-results.js +224 -0
  81. package/dist/src/research.d.ts +184 -0
  82. package/dist/src/research.js +808 -0
  83. package/dist/src/runtime-archives.d.ts +5 -0
  84. package/dist/src/runtime-archives.js +127 -0
  85. package/dist/src/runtime-catalog.d.ts +18 -0
  86. package/dist/src/runtime-catalog.js +27 -0
  87. package/dist/src/runtime-manager.d.ts +101 -0
  88. package/dist/src/runtime-manager.js +444 -0
  89. package/dist/src/runtime-selection.d.ts +13 -0
  90. package/dist/src/runtime-selection.js +25 -0
  91. package/dist/src/saved-materials.d.ts +12 -0
  92. package/dist/src/saved-materials.js +196 -0
  93. package/dist/src/scripts.d.ts +19 -0
  94. package/dist/src/scripts.js +79 -0
  95. package/dist/src/sdk-contracts.d.ts +13 -0
  96. package/dist/src/sdk-contracts.js +37 -0
  97. package/dist/src/sdk-files.d.ts +29 -0
  98. package/dist/src/sdk-files.js +249 -0
  99. package/dist/src/sdk.d.ts +4 -0
  100. package/dist/src/sdk.js +125 -0
  101. package/dist/src/server.d.ts +46 -0
  102. package/dist/src/server.js +460 -0
  103. package/dist/src/store.d.ts +33 -0
  104. package/dist/src/store.js +178 -0
  105. package/dist/src/sync-policy.d.ts +22 -0
  106. package/dist/src/sync-policy.js +123 -0
  107. package/dist/src/transport.d.ts +23 -0
  108. package/dist/src/transport.js +135 -0
  109. package/dist/src/ui-results.d.ts +2 -0
  110. package/dist/src/ui-results.js +80 -0
  111. package/dist/src/ui.d.ts +17 -0
  112. package/dist/src/ui.js +88 -0
  113. package/dist/src/utility.d.ts +14 -0
  114. package/dist/src/utility.js +43 -0
  115. package/dist/src/version-tracking.d.ts +57 -0
  116. package/dist/src/version-tracking.js +200 -0
  117. package/dist/src/workspace.d.ts +199 -0
  118. package/dist/src/workspace.js +564 -0
  119. package/package.json +47 -4
  120. package/references/InsightRepositoryProbe.java +55 -0
  121. package/references/administration.md +34 -0
  122. package/references/archive.md +32 -0
  123. package/references/artifacts.md +47 -0
  124. package/references/debugger.md +40 -0
  125. package/references/extensions-and-sync.md +124 -0
  126. package/references/managed-runtime.md +116 -0
  127. package/references/mutations.json +1424 -0
  128. package/references/native/InsightApplyDocumentation.java +48 -0
  129. package/references/native/InsightArchivePayloads.java +36 -0
  130. package/references/native/InsightBlockTransfer.java +57 -0
  131. package/references/native/InsightEmulation.java +93 -0
  132. package/references/native/InsightExtensions.java +36 -0
  133. package/references/native/InsightFunctionDatabases.java +98 -0
  134. package/references/native/InsightGuiMain.java +32 -0
  135. package/references/native/InsightHeadlessMain.java +10 -0
  136. package/references/native/InsightInitialImport.java +42 -0
  137. package/references/native/InsightInitialJournal.java +48 -0
  138. package/references/native/InsightInitialPublication.java +142 -0
  139. package/references/native/InsightKeytool.java +16 -0
  140. package/references/native/InsightNativeReceipt.java +31 -0
  141. package/references/native/InsightPlugins.java +34 -0
  142. package/references/native/InsightProjectFiles.java +92 -0
  143. package/references/native/InsightPublicationRecovery.java +63 -0
  144. package/references/native/InsightPythonWheels.java +31 -0
  145. package/references/native/InsightResearchQueries.java +188 -0
  146. package/references/native/InsightRuntimeDoctor.java +33 -0
  147. package/references/native/InsightSdkFiles.java +142 -0
  148. package/references/native/InsightSdkScript.java +31 -0
  149. package/references/native/InsightSdkTools.java +338 -0
  150. package/references/native/InsightSdkTypes.java +132 -0
  151. package/references/native/InsightServerAdministration.java +82 -0
  152. package/references/native/InsightServerInit.java +62 -0
  153. package/references/native/InsightServerMain.java +43 -0
  154. package/references/native/InsightServerProbe.java +29 -0
  155. package/references/native/InsightSnapshotFields.java +68 -0
  156. package/references/native/InsightSnapshotInspect.java +189 -0
  157. package/references/native/InsightSnapshotPublisher.java +123 -0
  158. package/references/native/InsightStartDebugger.java +28 -0
  159. package/references/native/InsightStopDebugger.java +20 -0
  160. package/references/native/InsightTraceInspect.java +24 -0
  161. package/references/native/InsightTraceState.java +130 -0
  162. package/references/native/InsightTraceTools.java +114 -0
  163. package/references/native/InsightVersionTracking.java +91 -0
  164. package/references/native-workflows.md +112 -0
  165. package/references/queries.json +1761 -0
  166. package/references/query-semantics.md +96 -0
  167. package/references/recovery.md +53 -0
  168. package/references/research.md +101 -0
  169. package/references/setup.md +49 -0
  170. package/runtime/GhidraMCP-7.0.0.jar +0 -0
  171. package/runtime/GhidraMCP-LICENSE +202 -0
  172. package/runtime/GhidraMCP-NOTICE +10 -0
  173. package/runtime/provenance.json +11 -0
  174. package/skills/insight-re/SKILL.md +108 -0
package/dist/src/ui.js ADDED
@@ -0,0 +1,88 @@
1
+ import { command, Permission } from '@eyeauras/cli-factory';
2
+ import { scope, address, number } from './research.js';
3
+ import { id, text, object, fail } from './model.js';
4
+ import { selectedOptions } from './arguments.js';
5
+ export class UserInterface {
6
+ workspace;
7
+ research;
8
+ backend;
9
+ constructor(workspace, research, backend) {
10
+ this.workspace = workspace;
11
+ this.research = research;
12
+ this.backend = backend;
13
+ }
14
+ async read(selected, operation, facet = 'all') {
15
+ return this.research.ordered(selected, async () => {
16
+ const backend = await this.backend();
17
+ await backend.open(selected);
18
+ if (operation === 'tools' || operation === 'plugins_list')
19
+ await backend.mode(selected, 'gui');
20
+ if (!backend.ui)
21
+ fail('configuration', 'Selected backend lacks the UI adapter.');
22
+ const value = await backend.ui(selected, operation, {});
23
+ return { ...selected, value: operation === 'cursor' && facet !== 'all' ? { [facet]: value[facet], ...(value[facet] === null ? { reason: value[facet + '_unavailable'] } : {}) } : value };
24
+ });
25
+ }
26
+ async change(selected, operation, p, callerId) {
27
+ const reserved = this.workspace.reserveRequest(selected, 'ghidra.ui.' + operation, p, callerId);
28
+ return this.research.ordered(selected, async () => {
29
+ const request = this.workspace.request(reserved.request.id);
30
+ if (reserved.replay && request.state !== 'accepted') {
31
+ if (request.state === 'applied')
32
+ return request.result;
33
+ fail('unknown', 'UI effect is retained and is not repeated.', request.result);
34
+ }
35
+ const backend = await this.backend();
36
+ await backend.open(selected);
37
+ await backend.mode(selected, 'gui');
38
+ if (!backend.ui)
39
+ fail('configuration', 'Selected backend lacks the UI adapter.');
40
+ const context = this.workspace.context(selected);
41
+ this.workspace.startRequest(request.id, { kind: 'ui', operation, generation: context.generation });
42
+ try {
43
+ const value = await backend.ui(selected, operation, p);
44
+ if (operation === 'goto') {
45
+ const cursor = await backend.ui(selected, 'cursor', {}), location = object(cursor.address);
46
+ if (location.session !== selected.session || location.target !== selected.target || address(location.address) !== address(p.address))
47
+ fail('partial', 'Cursor did not reach the requested context and address.');
48
+ }
49
+ const plugin = operation === 'plugins_set', snapshot = plugin ? await backend.saved(selected) : undefined;
50
+ const complexChanges = plugin ? [...context.complexChanges, request.id] : context.complexChanges;
51
+ const result = { ...selected, state: 'applied', value, ...(plugin ? { sharedState: 'pending', pendingChanges: context.scalars.length + complexChanges.length } : {}) };
52
+ this.workspace.completeSavedRequest(selected, request.id, context.generation, plugin ? { workingSnapshot: snapshot, complexChanges } : {}, result);
53
+ return result;
54
+ }
55
+ catch (error) {
56
+ this.workspace.recordRequest(request.id, 'unknown', { ...selected, state: 'unknown', operation });
57
+ throw error;
58
+ }
59
+ });
60
+ }
61
+ }
62
+ export function uiCommands(service) {
63
+ const request = { flags: '--request-id <id>', description: 'Retrieve the recorded UI result without repeating it' };
64
+ return [command('ui', 'Inspect and control the selected context interface', [
65
+ command('cursor', 'Read the focused address, function, selection and program', ({ options: o }, c) => {
66
+ const type = text(o.type);
67
+ if (!['all', 'address', 'function', 'selection', 'program'].includes(type))
68
+ fail('invalid', 'Cursor type must be all, address, function, selection or program.');
69
+ return service(c).read(scope(o), 'cursor', type);
70
+ }, { permission: Permission.ReadOnly, options: [...selectedOptions, { flags: '--type <facet>', description: 'Cursor facet or all', defaultValue: 'all' }] }),
71
+ command('tools', 'Read tools in the owned GUI context', ({ options: o }, c) => service(c).read(scope(o), 'tools'), { permission: Permission.ReadOnly, options: selectedOptions }),
72
+ command('goto', 'Navigate the owned browser and verify the cursor', ({ options: o }, c) => service(c).change(scope(o), 'goto', { address: address(o.address) }, o.requestId === undefined ? undefined : id(o.requestId)), { permission: Permission.Update, options: [...selectedOptions, { flags: '--address <address>', description: 'Explicit address', required: true }, request] }),
73
+ command('prompt-policy', 'Temporarily control the owned GUI prompt policy', ({ options: o }, c) => {
74
+ const action = text(o.action);
75
+ if (!['status', 'enable', 'disable'].includes(action))
76
+ fail('invalid', 'Prompt policy action must be status, enable or disable.');
77
+ return service(c).change(scope(o), 'prompt_policy', { action, reason: text(o.reason, 512), seconds: number(o.seconds, 300, 1) }, o.requestId === undefined ? undefined : id(o.requestId));
78
+ }, { permission: Permission.Update, options: [...selectedOptions, { flags: '--action <action>', description: 'status, enable or disable', defaultValue: 'status' },
79
+ { flags: '--reason <text>', description: 'Recorded policy reason', defaultValue: 'automation' }, { flags: '--seconds <count>', description: 'Requested duration (1–300 seconds)', defaultValue: 120 }, request] }),
80
+ ])];
81
+ }
82
+ export function pluginCommands(service) {
83
+ return [command('plugins', 'Configure plugins from enabled managed modules in an owned GUI', [
84
+ command('list', 'Read enabled managed plugins in this GUI tool', ({ options: o }, c) => service(c).read(scope(o), 'plugins_list'), { permission: Permission.ReadOnly, options: selectedOptions }),
85
+ command('set', 'Enable or disable one installed managed plugin with immediate readback', ({ options: o }, c) => { if (o.enabled !== 'true' && o.enabled !== 'false')
86
+ fail('invalid', 'enabled must be true or false.'); return service(c).change(scope(o), 'plugins_set', { plugin: text(o.plugin, 512), enabled: o.enabled === 'true' }, o.requestId === undefined ? undefined : id(o.requestId)); }, { permission: 'Destructive', options: [...selectedOptions, { flags: '--plugin <class>', description: 'Managed plugin ID from extensions capabilities', required: true }, { flags: '--enabled <boolean>', description: 'true or false', required: true }, { flags: '--request-id <id>', description: 'Retrieve the original plugin outcome without replay' }] })
87
+ ])];
88
+ }
@@ -0,0 +1,14 @@
1
+ import { type CommandDefinition } from '@eyeauras/cli-factory';
2
+ /** Equivalent of the pinned unsigned 64-bit number conversion, without a tool process. */
3
+ export declare function convertNumber(input: unknown, size?: unknown): {
4
+ input: string;
5
+ input_type: string;
6
+ size: number;
7
+ decimal_unsigned: string;
8
+ decimal_signed: string;
9
+ hexadecimal: string;
10
+ binary: string;
11
+ octal: string;
12
+ hex_padded: string;
13
+ };
14
+ export declare function utilityCommands(): CommandDefinition[];
@@ -0,0 +1,43 @@
1
+ import { command, Permission } from '@eyeauras/cli-factory';
2
+ import { fail, text } from './model.js';
3
+ /** Equivalent of the pinned unsigned 64-bit number conversion, without a tool process. */
4
+ export function convertNumber(input, size = 4) {
5
+ const source = text(input, 128), width = Number(size);
6
+ if (!Number.isSafeInteger(width) || width < 1 || width > 8)
7
+ fail('invalid', 'Size must be 1–8 bytes.');
8
+ let value, inputType;
9
+ try {
10
+ if (/^0[xX][a-fA-F0-9]+$/.test(source)) {
11
+ value = BigInt(source);
12
+ inputType = 'hexadecimal';
13
+ }
14
+ else if (/^0[bB][01]+$/.test(source)) {
15
+ value = BigInt(source);
16
+ inputType = 'binary';
17
+ }
18
+ else if (/^0[0-7]+$/.test(source) && source.length > 1) {
19
+ value = BigInt('0o' + source);
20
+ inputType = 'octal';
21
+ }
22
+ else if (/^\+?\d+$/.test(source)) {
23
+ value = BigInt(source);
24
+ inputType = 'decimal';
25
+ }
26
+ else
27
+ throw Error();
28
+ if (value < 0n || value > 0xffffffffffffffffn)
29
+ throw Error();
30
+ }
31
+ catch {
32
+ fail('invalid', 'Number must be an unsigned 64-bit decimal, hexadecimal, binary or octal integer.');
33
+ }
34
+ const bits = BigInt(width * 8), masked = value & ((1n << bits) - 1n), signed = masked >= (1n << (bits - 1n)) ? masked - (1n << bits) : masked, hex = masked.toString(16).toUpperCase();
35
+ return { input: source, input_type: inputType, size: width, decimal_unsigned: masked.toString(), decimal_signed: signed.toString(),
36
+ hexadecimal: '0x' + hex, binary: '0b' + masked.toString(2), octal: '0' + masked.toString(8), hex_padded: '0x' + hex.padStart(width * 2, '0') };
37
+ }
38
+ export function utilityCommands() {
39
+ return [command('utility', 'Stateless research utilities', [
40
+ command('convert-number', 'Convert an unsigned 64-bit integer at an explicit byte width', ({ options: o }) => convertNumber(o.text, o.size), { permission: Permission.ReadOnly, options: [{ flags: '--text <number>', description: 'Decimal, 0x hexadecimal, 0b binary or leading-zero octal', required: true },
41
+ { flags: '--size <bytes>', description: 'Output width, 1–8 bytes', defaultValue: 4 }] })
42
+ ])];
43
+ }
@@ -0,0 +1,57 @@
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
+ /** A native VT object belongs to an existing research session; it is not another agent session. */
5
+ export declare class VersionTracking {
6
+ private readonly workspace;
7
+ private readonly research;
8
+ private readonly backend;
9
+ private readonly tasks;
10
+ constructor(workspace: Workspace, research: Research, backend: () => Promise<ResearchBackend>, tasks: Map<string, Promise<unknown>>);
11
+ private ordered;
12
+ private root;
13
+ private owner;
14
+ private response;
15
+ private native;
16
+ list(selected: Scope, offset: number, limit: number): Promise<{
17
+ items: never[];
18
+ complete: boolean;
19
+ session: string;
20
+ target: string;
21
+ } | {
22
+ nextOffset?: number | undefined;
23
+ items: {
24
+ tracking: string;
25
+ source: Scope;
26
+ destination: Scope;
27
+ sourceSnapshotSha256: string;
28
+ destinationSnapshotSha256: string;
29
+ }[];
30
+ visited: number;
31
+ complete: boolean;
32
+ session: string;
33
+ target: string;
34
+ }>;
35
+ private decisionResult;
36
+ create(selected: Scope, source: Scope, callerId?: string): Promise<unknown>;
37
+ read(selected: Scope, tracking: string, operation: 'matches' | 'markup', p: Record<string, unknown>): Promise<{
38
+ tracking: string;
39
+ source: {
40
+ snapshotSha256: string;
41
+ session: string;
42
+ target: string;
43
+ };
44
+ destination: {
45
+ snapshotSha256: string;
46
+ session: string;
47
+ target: string;
48
+ };
49
+ sourceContextChanged: boolean;
50
+ destinationContextChanged: boolean;
51
+ value: unknown;
52
+ session: string;
53
+ target: string;
54
+ }>;
55
+ decide(selected: Scope, tracking: string, p: Record<string, unknown>, callerId?: string): Promise<unknown>;
56
+ }
57
+ export declare function trackingCommands(service: (c: CommandContext) => VersionTracking): CommandDefinition[];
@@ -0,0 +1,200 @@
1
+ import { command, Permission } from '@eyeauras/cli-factory';
2
+ import { mkdir, readFile, open, rename, realpath, lstat, readdir } from 'node:fs/promises';
3
+ import { join, resolve } from 'node:path';
4
+ import { scope, address, number } from './research.js';
5
+ import { id, text, object, fail, stable } from './model.js';
6
+ import { selectedOptions } from './arguments.js';
7
+ import { randomUUID } from 'node:crypto';
8
+ async function persist(path, value) { const temp = path + '.new-' + randomUUID(), file = await open(temp, 'wx', 0o600); try {
9
+ await file.writeFile(stable(value));
10
+ await file.sync();
11
+ }
12
+ finally {
13
+ await file.close();
14
+ } await rename(temp, path); }
15
+ /** A native VT object belongs to an existing research session; it is not another agent session. */
16
+ export class VersionTracking {
17
+ workspace;
18
+ research;
19
+ backend;
20
+ tasks;
21
+ constructor(workspace, research, backend, tasks) {
22
+ this.workspace = workspace;
23
+ this.research = research;
24
+ this.backend = backend;
25
+ this.tasks = tasks;
26
+ }
27
+ async ordered(tracking, action) { const key = 'vt:' + tracking, previous = this.tasks.get(key), task = (previous ? previous.catch(() => { }) : Promise.resolve()).then(action); this.tasks.set(key, task); try {
28
+ return await task;
29
+ }
30
+ finally {
31
+ if (this.tasks.get(key) === task)
32
+ this.tasks.delete(key);
33
+ } }
34
+ async root(tracking) {
35
+ id(tracking);
36
+ if (!/^vt_a_[a-zA-Z0-9-]+$/.test(tracking))
37
+ fail('invalid', 'Invalid Version Tracking object ID.');
38
+ const root = join(this.workspace.directory, 'version-tracking', tracking);
39
+ if (await realpath(root) !== resolve(root) || (await lstat(root)).isSymbolicLink())
40
+ fail('binding', 'VT object left its owned storage.');
41
+ return root;
42
+ }
43
+ async owner(selected, tracking) {
44
+ const root = await this.root(tracking), owner = object(JSON.parse(await readFile(join(root, 'owner.json'), 'utf8')));
45
+ if (owner.schema !== 1 || owner.tracking !== tracking || owner.profile !== this.workspace.profile || owner.destination.session !== selected.session || owner.destination.target !== selected.target)
46
+ fail('binding', 'VT object belongs to another research scope.');
47
+ await this.workspace.artifacts.verify(owner.sourceSnapshot);
48
+ await this.workspace.artifacts.verify(owner.destinationSnapshot);
49
+ return { root, owner };
50
+ }
51
+ response(owner, value) {
52
+ return { ...owner.destination, tracking: owner.tracking, source: { ...owner.source, snapshotSha256: owner.sourceSnapshot.sha256 }, destination: { ...owner.destination, snapshotSha256: owner.destinationSnapshot.sha256 },
53
+ sourceContextChanged: this.workspace.context(owner.source).generation !== owner.sourceGeneration, destinationContextChanged: this.workspace.context(owner.destination).generation !== owner.destinationGeneration, value };
54
+ }
55
+ async native(operation, root, p) { const backend = await this.backend(); if (!backend.sdkFile)
56
+ fail('configuration', 'Native VT is unavailable.'); return backend.sdkFile('vt.' + operation, { ...p, root }); }
57
+ async list(selected, offset, limit) {
58
+ this.workspace.context(selected);
59
+ const parent = join(this.workspace.directory, 'version-tracking');
60
+ let entries;
61
+ try {
62
+ entries = await readdir(parent, { withFileTypes: true });
63
+ }
64
+ catch (error) {
65
+ if (error.code === 'ENOENT')
66
+ return { ...selected, items: [], complete: true };
67
+ throw error;
68
+ }
69
+ entries.sort((a, b) => a.name.localeCompare(b.name));
70
+ const items = [];
71
+ let index = offset, visited = 0;
72
+ for (; index < entries.length && items.length < limit && visited < 1000; index++, visited++) {
73
+ const entry = entries[index];
74
+ if (!entry.isDirectory() || !/^vt_a_[a-zA-Z0-9-]+$/.test(entry.name))
75
+ fail('binding', 'VT inventory contains an unexpected storage entry.');
76
+ const root = await this.root(entry.name);
77
+ let owner;
78
+ try {
79
+ owner = JSON.parse(await readFile(join(root, 'owner.json'), 'utf8'));
80
+ }
81
+ catch (error) {
82
+ if (error.code === 'ENOENT')
83
+ continue;
84
+ fail('binding', 'VT owner record is malformed; retained storage requires inspection.');
85
+ }
86
+ if (owner.profile !== this.workspace.profile || owner.destination.session !== selected.session || owner.destination.target !== selected.target)
87
+ continue;
88
+ items.push({ tracking: entry.name, source: owner.source, destination: owner.destination, sourceSnapshotSha256: owner.sourceSnapshot.sha256, destinationSnapshotSha256: owner.destinationSnapshot.sha256 });
89
+ }
90
+ return { ...selected, items, visited, complete: index === entries.length, ...(index < entries.length ? { nextOffset: index } : {}) };
91
+ }
92
+ decisionResult(request, owner, value) {
93
+ const result = this.response(owner, value);
94
+ if (object(value).state === 'rejected') {
95
+ this.workspace.administrationState(request, 'failed', result);
96
+ fail('rejected', 'Native association constraints rejected this decision; its transaction was rolled back.', result);
97
+ }
98
+ this.workspace.administrationState(request, 'applied', result);
99
+ return result;
100
+ }
101
+ async create(selected, source, callerId) {
102
+ const reserved = this.workspace.reserveAdministration('sdk.vt.create', { ...selected, source }, callerId), tracking = 'vt_' + reserved.request.id;
103
+ return this.research.paired(selected, source, () => this.ordered(tracking, async () => {
104
+ const request = this.workspace.administration(reserved.request.id);
105
+ if (reserved.replay && request.state !== 'accepted') {
106
+ if (request.state === 'applied')
107
+ return request.result;
108
+ const { root, owner } = await this.owner(selected, tracking);
109
+ let receipt;
110
+ try {
111
+ receipt = object(JSON.parse(await readFile(join(root, 'created.json'), 'utf8')));
112
+ }
113
+ catch {
114
+ fail('unknown', 'VT creation is retained without a completed receipt; it is not repeated.');
115
+ }
116
+ if (receipt.request !== request.id || receipt.operation !== 'create')
117
+ fail('binding', 'VT creation receipt differs.');
118
+ const result = this.response(owner, receipt.value);
119
+ this.workspace.administrationState(request.id, 'applied', result);
120
+ return result;
121
+ }
122
+ const backend = await this.backend();
123
+ await backend.open(selected);
124
+ await backend.open(source);
125
+ const sourceSnapshot = await backend.saved(source), destinationSnapshot = await backend.saved(selected), root = join(this.workspace.directory, 'version-tracking', tracking);
126
+ await mkdir(root, { recursive: true, mode: 0o700 });
127
+ if (await realpath(root) !== resolve(root))
128
+ fail('binding', 'VT storage moved.');
129
+ const owner = { schema: 1, tracking, profile: this.workspace.profile, source, destination: selected, sourceSnapshot, destinationSnapshot,
130
+ sourceGeneration: this.workspace.context(source).generation, destinationGeneration: this.workspace.context(selected).generation };
131
+ await persist(join(root, 'owner.json'), owner);
132
+ this.workspace.administrationState(request.id, 'running', { tracking });
133
+ try {
134
+ const value = await this.native('create', root, { request_id: request.id, source_snapshot: await this.workspace.artifacts.verify(sourceSnapshot), destination_snapshot: await this.workspace.artifacts.verify(destinationSnapshot), receipt: join(root, 'created.json') });
135
+ const result = this.response(owner, value);
136
+ this.workspace.administrationState(request.id, 'applied', result);
137
+ return result;
138
+ }
139
+ catch (error) {
140
+ this.workspace.administrationState(request.id, 'unknown', { ...selected, tracking, state: 'unknown' });
141
+ throw error;
142
+ }
143
+ }));
144
+ }
145
+ async read(selected, tracking, operation, p) { return this.ordered(tracking, async () => { const { root, owner } = await this.owner(selected, tracking); return this.response(owner, await this.native(operation, root, p)); }); }
146
+ async decide(selected, tracking, p, callerId) {
147
+ const reserved = this.workspace.reserveAdministration('sdk.vt.decide', { ...selected, tracking, ...p }, callerId);
148
+ return this.ordered(tracking, async () => {
149
+ const request = this.workspace.administration(reserved.request.id), { root, owner } = await this.owner(selected, tracking), receipts = join(root, 'decisions');
150
+ await mkdir(receipts, { recursive: true, mode: 0o700 });
151
+ const receiptPath = join(receipts, request.id + '.json');
152
+ if (reserved.replay && request.state !== 'accepted') {
153
+ if (request.state === 'applied')
154
+ return request.result;
155
+ if (request.state === 'failed')
156
+ fail('rejected', 'The native association decision was rejected and is not repeated.', request.result);
157
+ let receipt;
158
+ try {
159
+ receipt = object(JSON.parse(await readFile(receiptPath, 'utf8')));
160
+ }
161
+ catch {
162
+ fail('unknown', 'VT decision is uncertain and is not repeated.');
163
+ }
164
+ if (receipt.request !== request.id || receipt.operation !== 'decide')
165
+ fail('binding', 'VT decision receipt differs.');
166
+ return this.decisionResult(request.id, owner, receipt.value);
167
+ }
168
+ if (this.workspace.unresolvedSessionAdministration(selected.session, selected.target).some(earlier => earlier.id !== request.id && object(object(earlier.intent).parameters).tracking === tracking))
169
+ fail('blocked', 'An earlier decision on this VT object is unresolved.');
170
+ this.workspace.administrationState(request.id, 'running', { ...selected, tracking });
171
+ try {
172
+ const value = await this.native('decide', root, { ...p, request_id: request.id, receipt: receiptPath });
173
+ return this.decisionResult(request.id, owner, value);
174
+ }
175
+ catch (error) {
176
+ if (this.workspace.administration(request.id).state !== 'failed')
177
+ this.workspace.administrationState(request.id, 'unknown', { ...selected, tracking, state: 'unknown' });
178
+ throw error;
179
+ }
180
+ });
181
+ }
182
+ }
183
+ export function trackingCommands(service) {
184
+ const tracking = { flags: '--tracking <id>', description: 'Native VT object returned by create', required: true }, request = { flags: '--request-id <id>', description: 'Retrieve a retained native decision without repeating it' }, association = [{ flags: '--source-address <address>', description: 'Source address in this VT snapshot', required: true }, { flags: '--destination-address <address>', description: 'Destination address in this VT snapshot', required: true }];
185
+ return [command('version-tracking', 'Native source/destination match sets, association decisions and markup inventory', [
186
+ command('list', 'List VT objects owned by this destination research scope, including retained creation stages', ({ options: o }, c) => service(c).list(scope(o), number(o.offset, 1000000), number(o.limit, 1000, 1)), { permission: Permission.ReadOnly, options: [...selectedOptions, { flags: '--limit <count>', description: 'Maximum returned owned VT objects', defaultValue: 100 }, { flags: '--offset <count>', description: 'Inventory offset; use nextOffset after a partial scan', defaultValue: 0 }] }),
187
+ command('create', 'Correlate immutable source/destination snapshots in a persistent owned native VT project', ({ options: o }, c) => {
188
+ const selected = scope(o), source = { session: o.sourceSession === undefined ? selected.session : id(o.sourceSession), target: id(o.sourceTarget) };
189
+ return service(c).create(selected, source, o.requestId === undefined ? undefined : id(o.requestId));
190
+ }, { permission: Permission.Update, options: [...selectedOptions, { flags: '--source-target <id>', description: 'Source immutable target', required: true }, { flags: '--source-session <id>', description: 'Source research session (default destination session)' }, request] }),
191
+ command('matches', 'Read native match sets and correlator-specific scores from the saved VT object', ({ options: o }, c) => service(c).read(scope(o), id(o.tracking), 'matches', { limit: number(o.limit, 1000, 1), offset: number(o.offset, 100000) }), { permission: Permission.ReadOnly, options: [...selectedOptions, tracking, { flags: '--limit <count>', description: 'Match page size 1–1000', defaultValue: 100 }, { flags: '--offset <count>', description: 'Page offset', defaultValue: 0 }] }),
192
+ command('markup', 'Inspect native markup values for one association before agent-directed edits', ({ options: o }, c) => service(c).read(scope(o), id(o.tracking), 'markup', { source_address: address(o.sourceAddress), destination_address: address(o.destinationAddress) }), { permission: Permission.ReadOnly, options: [...selectedOptions, tracking, ...association] }),
193
+ command('decide', 'Explicitly accept/reject/reset one native association; native one-to-one constraints apply', ({ options: o }, c) => {
194
+ const decision = text(o.decision);
195
+ if (!['accepted', 'rejected', 'available'].includes(decision))
196
+ fail('invalid', 'Decision must be accepted, rejected or available.');
197
+ return service(c).decide(scope(o), id(o.tracking), { source_address: address(o.sourceAddress), destination_address: address(o.destinationAddress), decision }, o.requestId === undefined ? undefined : id(o.requestId));
198
+ }, { permission: Permission.Update, options: [...selectedOptions, tracking, ...association, { flags: '--decision <state>', description: 'accepted, rejected or available', required: true }, request] }),
199
+ ])];
200
+ }
@@ -0,0 +1,199 @@
1
+ import { Artifacts, type Artifact } from './artifacts.js';
2
+ export interface ResearchTarget {
3
+ id: string;
4
+ name: string;
5
+ binarySha256: string;
6
+ size: number;
7
+ input: Artifact;
8
+ createdAt: string;
9
+ }
10
+ export interface ResearchSession {
11
+ id: string;
12
+ createdAt: string;
13
+ }
14
+ export interface GhidraLoadSettings {
15
+ autoAnalyze: boolean;
16
+ language?: string;
17
+ compilerSpec?: string;
18
+ imageBase?: string;
19
+ loader?: string;
20
+ loaderOptions?: Record<string, string | number | boolean>;
21
+ }
22
+ export interface GhidraModel {
23
+ target: string;
24
+ settings: GhidraLoadSettings;
25
+ state: 'opening' | 'ready' | 'unknown';
26
+ sharedRevision?: number;
27
+ sharedSnapshot?: Artifact;
28
+ }
29
+ export interface Scope {
30
+ session: string;
31
+ target: string;
32
+ }
33
+ export interface ScalarChange {
34
+ address: string;
35
+ field: 'function.name' | 'comment.eol' | 'comment.pre' | 'comment.post' | 'comment.plate' | 'comment.repeatable';
36
+ original: string | null;
37
+ local: string | null;
38
+ /** The field ceased to exist in a retained complex program result. Never a scalar setter. */
39
+ removed?: true;
40
+ dependency?: string;
41
+ conflict?: {
42
+ shared: string | null;
43
+ };
44
+ }
45
+ export interface SyncPolicy {
46
+ publish: 'manual' | 'after-edit' | 'after-save';
47
+ refresh: 'manual' | 'before-edit';
48
+ }
49
+ export interface GhidraContext extends Scope {
50
+ /** Internal storage key, never a caller's backend selector. */
51
+ id: string;
52
+ tool: 'ghidra';
53
+ state: 'opening' | 'ready' | 'stopped' | 'needs_recovery';
54
+ generation: number;
55
+ baseRevision?: number;
56
+ baseSnapshot?: Artifact;
57
+ workingSnapshot?: Artifact;
58
+ viewEpoch?: number;
59
+ scalars: ScalarChange[];
60
+ complexChanges: string[];
61
+ debugger?: DebuggerState;
62
+ syncPolicy?: SyncPolicy;
63
+ }
64
+ export interface SavedTrace {
65
+ id: string;
66
+ name: string;
67
+ snapshot: Artifact;
68
+ snap: string;
69
+ modules: number;
70
+ threads: number;
71
+ connected: boolean;
72
+ thread?: string;
73
+ }
74
+ export interface DebuggerState {
75
+ traces: SavedTrace[];
76
+ active?: string;
77
+ connected: boolean;
78
+ }
79
+ export type RequestState = 'accepted' | 'running' | 'applied' | 'failed' | 'unknown';
80
+ export interface AdministrationRequest {
81
+ id: string;
82
+ operation: string;
83
+ intent: unknown;
84
+ state: RequestState;
85
+ callerId?: string;
86
+ result?: unknown;
87
+ createdAt: string;
88
+ updatedAt: string;
89
+ }
90
+ export interface ResearchRequest {
91
+ id: string;
92
+ callerId?: string;
93
+ operation: string;
94
+ intent: unknown;
95
+ context: string;
96
+ state: RequestState;
97
+ result?: unknown;
98
+ execution?: unknown;
99
+ createdAt: string;
100
+ updatedAt: string;
101
+ }
102
+ export interface Publication extends Scope {
103
+ id: string;
104
+ request: string;
105
+ state: 'preparing' | 'commit_intent' | 'unknown' | 'verified' | 'conflict';
106
+ expectedRevision: number;
107
+ snapshot: Artifact;
108
+ generation: number;
109
+ scalars: ScalarChange[];
110
+ complex: boolean;
111
+ complexChanges?: string[];
112
+ publishedRevision?: number;
113
+ detail?: unknown;
114
+ }
115
+ export interface OpenReservation {
116
+ context: GhidraContext;
117
+ request: ResearchRequest;
118
+ replay: boolean;
119
+ }
120
+ type ContextPatch = Partial<Pick<GhidraContext, 'state' | 'baseRevision' | 'baseSnapshot' | 'workingSnapshot' | 'viewEpoch' | 'scalars' | 'complexChanges' | 'debugger' | 'syncPolicy'>>;
121
+ /** Profile root state, deliberately separate from the earlier coordinator.sqlite journals. */
122
+ export declare class Workspace {
123
+ readonly profile: string;
124
+ readonly directory: string;
125
+ readonly artifacts: Artifacts;
126
+ private readonly db;
127
+ private namespace;
128
+ constructor(directory: string, profile: string);
129
+ close(): void;
130
+ private transaction;
131
+ private rememberArtifact;
132
+ private knownSnapshot;
133
+ captureSnapshot(path: string): Promise<Artifact>;
134
+ importTarget(path: string): Promise<ResearchTarget>;
135
+ target(target: string): ResearchTarget;
136
+ targets(limit?: number, offset?: number): ResearchTarget[];
137
+ session(session: string): ResearchSession;
138
+ sessions(limit?: number, offset?: number): ResearchSession[];
139
+ model(target: string): GhidraModel;
140
+ context(scope: Scope): GhidraContext;
141
+ contexts(session: string, limit?: number, offset?: number): GhidraContext[];
142
+ private existingRequest;
143
+ private insertRequest;
144
+ reserveOpen(target: string, session: string | 'new', settings: GhidraLoadSettings, callerId?: string, mode?: 'headless' | 'gui'): OpenReservation;
145
+ reserveRequest(scope: Scope, operation: string, parameters: unknown, callerId?: string): {
146
+ request: ResearchRequest;
147
+ replay: boolean;
148
+ };
149
+ request(request: string): ResearchRequest;
150
+ reserveAdministration(operation: string, parameters: unknown, callerId?: string): {
151
+ request: AdministrationRequest;
152
+ replay: boolean;
153
+ };
154
+ administrationByCaller(callerId: string): AdministrationRequest | undefined;
155
+ requestByCaller(callerId: string): ResearchRequest | undefined;
156
+ administration(request: string): AdministrationRequest;
157
+ unresolvedAdministration(): AdministrationRequest[];
158
+ unresolvedSessionAdministration(session: string, target?: string): AdministrationRequest[];
159
+ unresolvedServer(): AdministrationRequest[];
160
+ unresolvedProject(project: string): AdministrationRequest[];
161
+ administrationState(request: string, state: Exclude<RequestState, 'accepted'>, result?: unknown): AdministrationRequest;
162
+ unresolved(scope: Scope): ResearchRequest[];
163
+ startRequest(request: string, execution?: unknown): ResearchRequest;
164
+ recordRequest(request: string, state: 'applied' | 'failed' | 'unknown', result: unknown): ResearchRequest;
165
+ private writeRequest;
166
+ establishModel(target: string, revision: number, snapshot: Artifact): GhidraModel;
167
+ beginModelInitialization(target: string): GhidraModel;
168
+ observeShared(target: string, revision: number, snapshot: Artifact): GhidraModel;
169
+ publication(publication: string): Publication;
170
+ activePublication(target: string): Publication | undefined;
171
+ beginPublication(scope: Scope, request: string, expectedRevision: number, snapshot: Artifact): Publication;
172
+ publicationIntent(publication: string): Publication;
173
+ unknownPublication(publication: string, detail: unknown): {
174
+ state: "unknown";
175
+ detail: unknown;
176
+ id: string;
177
+ request: string;
178
+ expectedRevision: number;
179
+ snapshot: Artifact;
180
+ generation: number;
181
+ scalars: ScalarChange[];
182
+ complex: boolean;
183
+ complexChanges?: string[];
184
+ publishedRevision?: number;
185
+ session: string;
186
+ target: string;
187
+ };
188
+ finishPublication(publication: string, publishedRevision: number, result: unknown): Publication;
189
+ rejectPublication(publication: string, result: unknown): Publication;
190
+ publishedSince(target: string, revision: number): Publication[];
191
+ saveContext(scope: Scope, expectedGeneration: number, patch: ContextPatch): GhidraContext;
192
+ /** Snapshot and successful request acknowledgment enter the same durable transaction. */
193
+ completeSavedRequest(scope: Scope, request: string, expectedGeneration: number, patch: ContextPatch, result: unknown): {
194
+ context: GhidraContext;
195
+ request: ResearchRequest;
196
+ };
197
+ private writeContext;
198
+ }
199
+ export {};