@termwright/probe-tview 0.2.0 → 0.3.1
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/README.md +99 -89
- package/assets/tcell_marker_windows.go.txt +80 -0
- package/assets/tview_probe.go.txt +1368 -0
- package/assets/tview_probe_test.go.txt +483 -0
- package/dist/index.d.ts +16 -32
- package/dist/index.js +118 -116
- package/dist/index.js.map +1 -1
- package/package.json +9 -8
- package/upstream-patches/tview/v0.42.0/add/termwright_probe.go +0 -876
- package/upstream-patches/tview/v0.42.0/add/termwright_probe_test.go +0 -428
- package/upstream-patches/tview/v0.42.0/manifest.json +0 -42
- package/upstream-patches/tview/v0.42.0/patches/application.go.patch +0 -14
- package/upstream-patches/tview/v0.42.0/patches/go.mod.patch +0 -15
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
export * from "@termwright/probe-go";
|
|
3
3
|
|
|
4
4
|
// src/recognizer.ts
|
|
5
|
+
import { evidence } from "@termwright/protocol";
|
|
5
6
|
var ROLE_BY_TYPE = {
|
|
6
7
|
Button: "button",
|
|
7
8
|
Checkbox: "checkbox",
|
|
@@ -32,13 +33,23 @@ function recognize(frame, options) {
|
|
|
32
33
|
if (object.parent === void 0) rootIds.push(object.identity.value);
|
|
33
34
|
}
|
|
34
35
|
return {
|
|
35
|
-
v:
|
|
36
|
+
v: 2,
|
|
36
37
|
sessionId: options.sessionId,
|
|
37
38
|
revision: options.revision,
|
|
38
39
|
columns: options.columns,
|
|
39
40
|
rows: options.rows,
|
|
40
41
|
rootIds,
|
|
41
|
-
nodes
|
|
42
|
+
nodes,
|
|
43
|
+
coordinateSpace: {
|
|
44
|
+
status: "known",
|
|
45
|
+
value: "viewport-cells",
|
|
46
|
+
evidence: evidence("framework", "instrumented", "authoritative", "tview")
|
|
47
|
+
},
|
|
48
|
+
hitGrid: {
|
|
49
|
+
status: "unsupported",
|
|
50
|
+
capability: "pointer-hit-grid",
|
|
51
|
+
reason: "framework-unobservable"
|
|
52
|
+
}
|
|
42
53
|
};
|
|
43
54
|
}
|
|
44
55
|
function recognizeObject(object) {
|
|
@@ -53,10 +64,35 @@ function recognizeObject(object) {
|
|
|
53
64
|
frameworkType: object.frameworkType,
|
|
54
65
|
name: object.annotations?.name ?? object.text ?? "",
|
|
55
66
|
...object.parent === void 0 ? {} : { parentId: object.parent },
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
67
|
+
geometry: {
|
|
68
|
+
displayed: object.state?.displayed === void 0 ? { status: "unsupported", capability: "displayed", reason: "framework-unobservable" } : {
|
|
69
|
+
status: "known",
|
|
70
|
+
value: object.state.displayed,
|
|
71
|
+
evidence: evidence("framework", "instrumented", "authoritative", "tview")
|
|
72
|
+
},
|
|
73
|
+
intendedRect: object.geometry?.intendedRect === void 0 ? {
|
|
74
|
+
status: "unsupported",
|
|
75
|
+
capability: "intended-geometry",
|
|
76
|
+
reason: "framework-unobservable"
|
|
77
|
+
} : {
|
|
78
|
+
status: "known",
|
|
79
|
+
value: object.geometry.intendedRect,
|
|
80
|
+
evidence: evidence("framework", "instrumented", "authoritative", "tview")
|
|
81
|
+
},
|
|
82
|
+
visibleRect: {
|
|
83
|
+
status: "unsupported",
|
|
84
|
+
capability: "visible-rect",
|
|
85
|
+
reason: "framework-unobservable"
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
...object.state?.value === void 0 ? {} : {
|
|
89
|
+
value: {
|
|
90
|
+
status: "known",
|
|
91
|
+
value: object.state.value,
|
|
92
|
+
sensitivity: object.state?.valueSensitivity ?? "sensitive",
|
|
93
|
+
evidence: evidence("framework", "instrumented", "authoritative", "tview")
|
|
94
|
+
}
|
|
95
|
+
},
|
|
60
96
|
...state === void 0 ? {} : { state }
|
|
61
97
|
};
|
|
62
98
|
}
|
|
@@ -71,139 +107,105 @@ function recognizeState(object) {
|
|
|
71
107
|
if (observed.readonly !== void 0) state["readonly"] = observed.readonly;
|
|
72
108
|
if (observed.displayed === false) state["hidden"] = true;
|
|
73
109
|
if (observed.selectedIndex !== void 0) state["positionInSet"] = observed.selectedIndex + 1;
|
|
74
|
-
if (observed.scroll !== void 0 && observed.scroll.row >= 0) {
|
|
75
|
-
state["scrollOffset"] = observed.scroll.row;
|
|
76
|
-
}
|
|
77
|
-
if (observed.scrollExtent !== void 0 && observed.scrollExtent.rows >= 0) {
|
|
78
|
-
state["scrollExtent"] = observed.scrollExtent.rows;
|
|
79
|
-
}
|
|
80
110
|
return Object.keys(state).length === 0 ? void 0 : state;
|
|
81
111
|
}
|
|
82
112
|
|
|
83
113
|
// src/launch.ts
|
|
84
114
|
import { execFile } from "child_process";
|
|
85
|
-
import {
|
|
115
|
+
import { readFile, realpath } from "fs/promises";
|
|
86
116
|
import { join } from "path";
|
|
87
117
|
import { fileURLToPath } from "url";
|
|
88
118
|
import { promisify } from "util";
|
|
89
119
|
import {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
copyDir,
|
|
93
|
-
digestPatchSet,
|
|
94
|
-
ensureUpstreamModule,
|
|
95
|
-
isComplete,
|
|
96
|
-
markComplete,
|
|
97
|
-
materializeUpstream,
|
|
98
|
-
prepareCopyDir,
|
|
99
|
-
readManifest,
|
|
100
|
-
readWorkspace,
|
|
101
|
-
writeProvenance,
|
|
102
|
-
writeWorkspace
|
|
120
|
+
digestGoToolExecSource,
|
|
121
|
+
prepareGoToolExec
|
|
103
122
|
} from "@termwright/probe-go";
|
|
104
123
|
var run = promisify(execFile);
|
|
105
124
|
var FRAMEWORK = "github.com/rivo/tview";
|
|
106
125
|
var CLIENT_MODULE = "github.com/gorce-ai/termwright/clients/go";
|
|
107
|
-
var PROBE_VERSION = "0.1
|
|
126
|
+
var PROBE_VERSION = "0.3.1";
|
|
127
|
+
var TCELL_FRAMEWORK = "github.com/gdamore/tcell/v2";
|
|
108
128
|
async function prepareInstrumentedBuild(options) {
|
|
109
129
|
const env = options.env ?? process.env;
|
|
110
|
-
|
|
111
|
-
const
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
probeVersion: PROBE_VERSION,
|
|
118
|
-
toolchain: await toolchain(env),
|
|
119
|
-
patchDigest: await digestPatchSet(patchSetDir)
|
|
120
|
-
};
|
|
121
|
-
const copy = copyDir(key, env);
|
|
122
|
-
const built = !await isComplete(copy);
|
|
123
|
-
if (built) {
|
|
124
|
-
await prepareCopyDir(copy);
|
|
125
|
-
await materializeUpstream(await upstreamDir(frameworkVersion, env), copy);
|
|
126
|
-
await applyPatchSet(copy, patchSetDir);
|
|
127
|
-
await writeProvenance(copy, manifest);
|
|
128
|
-
await markComplete(copy, key);
|
|
130
|
+
const moduleDir = await realpath(options.moduleDir);
|
|
131
|
+
const buildEnv = { ...env, PWD: moduleDir };
|
|
132
|
+
const frameworkVersion = await moduleVersion(moduleDir, FRAMEWORK, buildEnv);
|
|
133
|
+
if (options.frameworkVersion !== void 0 && options.frameworkVersion !== frameworkVersion) {
|
|
134
|
+
throw new Error(
|
|
135
|
+
`@termwright/probe-tview expected ${FRAMEWORK} ${options.frameworkVersion}, but the application resolves ${frameworkVersion || "no version"}`
|
|
136
|
+
);
|
|
129
137
|
}
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
]
|
|
138
|
+
const goos = (await run("go", ["env", "GOOS"], { cwd: moduleDir, env: buildEnv })).stdout.trim();
|
|
139
|
+
const units = await compilationUnits(goos);
|
|
140
|
+
const prepared = await prepareGoToolExec({
|
|
141
|
+
moduleDir,
|
|
142
|
+
outputDir: options.outputDir ?? join(moduleDir, ".termwright", "go-toolexec"),
|
|
143
|
+
units,
|
|
144
|
+
env: buildEnv
|
|
138
145
|
});
|
|
139
|
-
return {
|
|
146
|
+
return {
|
|
147
|
+
...prepared,
|
|
148
|
+
moduleDir,
|
|
149
|
+
frameworkVersion,
|
|
150
|
+
sourceDigests: units.map((unit) => unit.sourceDigest)
|
|
151
|
+
};
|
|
140
152
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
return [{ dir: options.clientDir ?? await defaultClientDir(env), module: CLIENT_MODULE }];
|
|
153
|
+
function compilerUnitTargetsForPlatform(goos) {
|
|
154
|
+
return goos === "windows" ? ["zz_termwright_probe.go", "zz_termwright_marker.go"] : ["zz_termwright_probe.go"];
|
|
144
155
|
}
|
|
145
|
-
async function
|
|
146
|
-
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
156
|
+
async function compilationUnits(goos) {
|
|
157
|
+
const root = packageRoot();
|
|
158
|
+
const declarations = [
|
|
159
|
+
{
|
|
160
|
+
packagePath: FRAMEWORK,
|
|
161
|
+
targetFile: "zz_termwright_probe.go",
|
|
162
|
+
sourceFile: join(root, "assets", "tview_probe.go.txt"),
|
|
163
|
+
imports: [
|
|
164
|
+
"errors",
|
|
165
|
+
"reflect",
|
|
166
|
+
"runtime/debug",
|
|
167
|
+
"sort",
|
|
168
|
+
"strconv",
|
|
169
|
+
"strings",
|
|
170
|
+
"sync",
|
|
171
|
+
"sync/atomic",
|
|
172
|
+
"github.com/gdamore/tcell/v2",
|
|
173
|
+
"github.com/gorce-ai/termwright/clients/go/annotate",
|
|
174
|
+
"github.com/gorce-ai/termwright/clients/go/evidence",
|
|
175
|
+
"github.com/gorce-ai/termwright/clients/go/probehost",
|
|
176
|
+
"github.com/gorce-ai/termwright/clients/go/protocol"
|
|
177
|
+
]
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
packagePath: TCELL_FRAMEWORK,
|
|
181
|
+
targetFile: "zz_termwright_marker.go",
|
|
182
|
+
sourceFile: join(root, "assets", "tcell_marker_windows.go.txt"),
|
|
183
|
+
stripWindowsConstraint: true,
|
|
184
|
+
imports: ["errors", "io", "syscall", "unicode/utf16", "unsafe"]
|
|
185
|
+
}
|
|
186
|
+
].filter(({ targetFile }) => compilerUnitTargetsForPlatform(goos).includes(targetFile));
|
|
187
|
+
return Promise.all(
|
|
188
|
+
declarations.map(
|
|
189
|
+
async ({ packagePath, targetFile, sourceFile, stripWindowsConstraint, imports }) => {
|
|
190
|
+
const asset = await readFile(sourceFile, "utf8");
|
|
191
|
+
const source = stripWindowsConstraint ? asset.replace(/^\/\/go:build windows\n\n/u, "") : asset;
|
|
192
|
+
return {
|
|
193
|
+
packagePath,
|
|
194
|
+
targetFile,
|
|
195
|
+
source,
|
|
196
|
+
sourceDigest: digestGoToolExecSource(source),
|
|
197
|
+
...imports === void 0 ? {} : { imports }
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
)
|
|
201
|
+
);
|
|
158
202
|
}
|
|
159
|
-
async function
|
|
160
|
-
const
|
|
203
|
+
async function moduleVersion(moduleDir, framework, env) {
|
|
204
|
+
const result = await run("go", ["list", "-m", "-f={{.Version}}", framework], {
|
|
161
205
|
cwd: moduleDir,
|
|
162
|
-
// Without this a workspace already in effect could report a replaced
|
|
163
|
-
// version, and the patch set would be chosen for the wrong source.
|
|
164
|
-
env: { ...env, GOWORK: "off" }
|
|
165
|
-
});
|
|
166
|
-
return stdout.trim();
|
|
167
|
-
}
|
|
168
|
-
async function modulePath(moduleDir, env) {
|
|
169
|
-
const { stdout } = await run("go", ["list", "-m", "-f", "{{.Path}}"], {
|
|
170
|
-
cwd: moduleDir,
|
|
171
|
-
env: { ...env, GOWORK: "off" }
|
|
172
|
-
});
|
|
173
|
-
return stdout.trim();
|
|
174
|
-
}
|
|
175
|
-
async function upstreamDir(version, env) {
|
|
176
|
-
return ensureUpstreamModule({
|
|
177
|
-
module: FRAMEWORK,
|
|
178
|
-
version,
|
|
179
|
-
cachePath: ["github.com", "rivo", `tview@${version}`],
|
|
180
|
-
env
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
async function toolchain(env) {
|
|
184
|
-
const { stdout } = await run("go", ["version"], { env });
|
|
185
|
-
return stdout.trim();
|
|
186
|
-
}
|
|
187
|
-
function moduleCachePath(module, version) {
|
|
188
|
-
const parts = module.split("/");
|
|
189
|
-
const last = parts.pop();
|
|
190
|
-
return last === void 0 ? [] : [...parts, `${last}@${version}`];
|
|
191
|
-
}
|
|
192
|
-
function patchSetFor(version) {
|
|
193
|
-
return join(packageRoot(), "upstream-patches", "tview", version);
|
|
194
|
-
}
|
|
195
|
-
async function defaultClientDir(env) {
|
|
196
|
-
const vendored = join(packageRoot(), "go-client");
|
|
197
|
-
if (existsSync(join(vendored, "go.mod"))) return vendored;
|
|
198
|
-
const inRepo = join(packageRoot(), "..", "..", "clients", "go");
|
|
199
|
-
if (existsSync(join(inRepo, "go.mod"))) return inRepo;
|
|
200
|
-
const version = `v${PROBE_VERSION}`;
|
|
201
|
-
return ensureUpstreamModule({
|
|
202
|
-
module: CLIENT_MODULE,
|
|
203
|
-
version,
|
|
204
|
-
cachePath: moduleCachePath(CLIENT_MODULE, version),
|
|
205
206
|
env
|
|
206
207
|
});
|
|
208
|
+
return result.stdout.trim();
|
|
207
209
|
}
|
|
208
210
|
function packageRoot() {
|
|
209
211
|
return join(fileURLToPath(new URL(".", import.meta.url)), "..");
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/recognizer.ts","../src/launch.ts"],"sourcesContent":["/**\n * `@termwright/probe-tview` — semantics from a tview application that imports\n * nothing of ours.\n *\n * The application is built through an ephemeral Go workspace that redirects\n * `github.com/rivo/tview` to an instrumented copy. The project's `go.mod`,\n * `go.sum` and any workspace of its own are never touched.\n *\n * @packageDocumentation\n */\n\n\n\n\n// The Go machinery is shared with every other copy-based probe.\nexport * from '@termwright/probe-go';\n\nexport { recognize, roleFor, type RecognizeOptions } from './recognizer.js';\n\nexport {\n prepareInstrumentedBuild,\n CLIENT_MODULE,\n FRAMEWORK,\n PROBE_VERSION,\n type PrepareOptions,\n type PreparedBuild,\n} from './launch.js';\n","/**\n * IR → semantic tree, as a pure function.\n *\n * The Go probe normalises inside the copy, because that is where the private\n * state is. This module is the same normalisation expressed over Probe IR, and\n * it exists for two reasons that are worth stating, since duplicated logic is\n * usually a smell:\n *\n * 1. **It is testable without a process.** The B tier of the campaign's test\n * plan feeds IR in and inspects the tree that comes out — no build, no\n * toolchain, no pseudo-terminal, and every edge case reachable in one line\n * instead of being coaxed out of a running application.\n * 2. **It is the contract the Go side is held to.** The recorded IR of a real\n * frame goes through here, and the result must match what the probe\n * published. Two implementations that disagree describe the same\n * application differently, which is the failure mode the convergence round\n * existed to prevent.\n */\n\nimport type { ProbeFrame, ProbeObject } from '@termwright/protocol';\nimport type { SemanticNode, SemanticRole, SemanticSnapshot } from '@termwright/protocol';\n\n/**\n * tview widget type → role.\n *\n * Keyed on the framework's own type name, which the probe reports for every\n * object. Anything absent from this table is deliberately *not* a failure: it\n * becomes `generic` and keeps its `frameworkType`, so a widget added by a\n * future tview release is still addressable rather than dropped.\n */\nconst ROLE_BY_TYPE: Readonly<Record<string, SemanticRole>> = {\n Button: 'button',\n Checkbox: 'checkbox',\n InputField: 'textbox',\n TextArea: 'textbox',\n DropDown: 'list',\n List: 'list',\n TreeView: 'list',\n Table: 'table',\n TextView: 'text',\n Modal: 'dialog',\n Form: 'region',\n Flex: 'region',\n Grid: 'region',\n Pages: 'region',\n Frame: 'region',\n Box: 'region',\n};\n\n/** What a recognizer needs to know about the frame it is given. */\nexport interface RecognizeOptions {\n readonly sessionId: string;\n readonly revision: number;\n readonly columns: number;\n readonly rows: number;\n}\n\n/** Resolves one object's role from its framework type. */\nexport function roleFor(frameworkType: string): SemanticRole {\n return ROLE_BY_TYPE[frameworkType] ?? 'generic';\n}\n\n/**\n * Normalises one observed frame into a semantic snapshot.\n *\n * Facts are carried across, never invented. An object without geometry stays\n * without bounds; a state the probe did not report stays absent, because\n * \"the probe did not say\" and \"the widget is not focused\" are different\n * claims and only one of them is safe to make.\n */\nexport function recognize(frame: ProbeFrame, options: RecognizeOptions): SemanticSnapshot {\n const nodes: SemanticNode[] = [];\n const rootIds: string[] = [];\n\n for (const object of frame.objects) {\n const node = recognizeObject(object);\n nodes.push(node);\n if (object.parent === undefined) rootIds.push(object.identity.value);\n }\n\n return {\n v: 1,\n sessionId: options.sessionId,\n revision: options.revision,\n columns: options.columns,\n rows: options.rows,\n rootIds,\n nodes,\n };\n}\n\nfunction recognizeObject(object: ProbeObject): SemanticNode {\n const role = roleFor(object.frameworkType);\n const state = recognizeState(object);\n\n return {\n id: object.identity.value,\n role,\n // The framework's own name survives on every node, not only generic ones:\n // it is what lets a reader tell a Flex-shaped region from a Form-shaped\n // one after both became `region`.\n frameworkType: object.frameworkType,\n name: object.annotations?.name ?? object.text ?? '',\n ...(object.parent === undefined ? {} : { parentId: object.parent }),\n // intendedRect is where the widget was drawn. visibleRect is absent for\n // tview, which computes no clip, so nothing here fabricates one.\n ...(object.geometry?.intendedRect === undefined\n ? {}\n : { bounds: object.geometry.intendedRect }),\n ...(object.state?.value === undefined ? {} : { value: object.state.value }),\n ...(state === undefined ? {} : { state }),\n } as SemanticNode;\n}\n\nfunction recognizeState(object: ProbeObject): SemanticNode['state'] {\n const observed = object.state;\n if (observed === undefined) return undefined;\n\n const state: Record<string, unknown> = {};\n if (observed.focused !== undefined) state['focused'] = observed.focused;\n if (observed.disabled !== undefined) state['disabled'] = observed.disabled;\n if (observed.checked !== undefined) state['checked'] = observed.checked;\n if (observed.expanded !== undefined) state['expanded'] = observed.expanded;\n if (observed.readonly !== undefined) state['readonly'] = observed.readonly;\n // `displayed: false` is the probe's way of saying the framework hid it; the\n // wire calls that `hidden`, and the inversion is the whole translation.\n if (observed.displayed === false) state['hidden'] = true;\n if (observed.selectedIndex !== undefined) state['positionInSet'] = observed.selectedIndex + 1;\n // A negative scroll offset is a pre-layout artefact, not a position. The Go\n // probe drops it at the source; this is the same rule, restated where a test\n // can reach it in one line.\n if (observed.scroll !== undefined && observed.scroll.row >= 0) {\n state['scrollOffset'] = observed.scroll.row;\n }\n if (observed.scrollExtent !== undefined && observed.scrollExtent.rows >= 0) {\n state['scrollExtent'] = observed.scrollExtent.rows;\n }\n\n return Object.keys(state).length === 0 ? undefined : (state as SemanticNode['state']);\n}\n","/**\n * The one call a launcher makes.\n *\n * Everything else in this package is a step: read the workspace, materialise a\n * copy, patch it, key the cache, write the file. A user should not assemble\n * those, and neither should a test — the assembly order is where the mistakes\n * live, and there is exactly one correct one.\n */\n\nimport { execFile } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { promisify } from 'node:util';\nimport {\n applyPatchSet,\n assertNoVendorMode,\n copyDir,\n digestPatchSet,\n ensureUpstreamModule,\n isComplete,\n markComplete,\n materializeUpstream,\n prepareCopyDir,\n readManifest,\n readWorkspace,\n writeProvenance,\n writeWorkspace,\n type CopyKeyInput,\n} from '@termwright/probe-go';\n\nconst run = promisify(execFile);\n\n/** Module path of the framework this probe instruments. */\nexport const FRAMEWORK = 'github.com/rivo/tview';\n\n/** Module path of the protocol client the injected probe imports. */\nexport const CLIENT_MODULE = 'github.com/gorce-ai/termwright/clients/go';\n\n/** Version of this probe; part of the cache key, so a new patch set invalidates copies. */\nexport const PROBE_VERSION = '0.1.0';\n\nexport interface PrepareOptions {\n /** Directory of the Go module to build. */\n readonly moduleDir: string;\n /** Framework version to instrument, e.g. `v0.42.0`. */\n readonly frameworkVersion?: string;\n /** Where the protocol client lives on disk. Defaults to the copy shipped here. */\n readonly clientDir?: string;\n /** Where the generated workspace is written. Defaults to inside the copy's cache entry. */\n readonly workspaceFile?: string;\n /** Environment the build will run with; checked, never mutated. */\n readonly env?: NodeJS.ProcessEnv;\n}\n\nexport interface PreparedBuild {\n /** Hand this to the build as `GOWORK`. */\n readonly workspaceFile: string;\n /** The instrumented copy, for a canary check or for diagnosis. */\n readonly copyDir: string;\n /** Environment to build with: the caller's, plus GOWORK. */\n readonly env: NodeJS.ProcessEnv;\n /** True when the copy was built during this call rather than reused. */\n readonly built: boolean;\n}\n\n/**\n * Makes a build of `moduleDir` compile against the instrumented framework.\n *\n * Does not build anything and does not spawn the application: a launcher owns\n * that. This returns what the build needs and nothing else, which keeps the\n * package testable and keeps the decision about *how* to run the user's build\n * where it belongs.\n */\nexport async function prepareInstrumentedBuild(\n options: PrepareOptions,\n): Promise<PreparedBuild> {\n const env = options.env ?? process.env;\n // Refused rather than overridden: forcing workspace mode over a vendored\n // build would change what compiles, behind the user's back.\n assertNoVendorMode(env);\n\n const frameworkVersion = options.frameworkVersion ?? (await detectFrameworkVersion(options.moduleDir, env));\n const patchSetDir = patchSetFor(frameworkVersion);\n const manifest = await readManifest(patchSetDir);\n\n const key: CopyKeyInput = {\n framework: FRAMEWORK,\n frameworkVersion,\n probeVersion: PROBE_VERSION,\n toolchain: await toolchain(env),\n patchDigest: await digestPatchSet(patchSetDir),\n };\n\n const copy = copyDir(key, env);\n const built = !(await isComplete(copy));\n if (built) {\n await prepareCopyDir(copy);\n await materializeUpstream(await upstreamDir(frameworkVersion, env), copy);\n await applyPatchSet(copy, patchSetDir);\n await writeProvenance(copy, manifest);\n // Last, so an interrupted build is rebuilt rather than compiled.\n await markComplete(copy, key);\n }\n\n const workspaceFile = await writeWorkspace(options.workspaceFile ?? join(copy, '..', 'generated.work'), {\n moduleDir: options.moduleDir,\n inherited: await readWorkspace(options.moduleDir),\n suppliedUses: await clientWorkspaceUses(options, env),\n replaces: [\n { from: FRAMEWORK, to: copy },\n ...(await clientVersionReplacements(options, env)),\n ],\n });\n\n return { workspaceFile, copyDir: copy, env: { ...env, GOWORK: workspaceFile }, built };\n}\n\nasync function clientWorkspaceUses(\n options: PrepareOptions,\n env: NodeJS.ProcessEnv,\n): Promise<{ dir: string; module: string }[]> {\n if ((await modulePath(options.moduleDir, env)) === CLIENT_MODULE) return [];\n return [{ dir: options.clientDir ?? (await defaultClientDir(env)), module: CLIENT_MODULE }];\n}\n\nasync function clientVersionReplacements(\n options: PrepareOptions,\n env: NodeJS.ProcessEnv,\n): Promise<{ from: string; to: string; version: string }[]> {\n if ((await modulePath(options.moduleDir, env)) === CLIENT_MODULE) return [];\n const to = options.clientDir ?? (await defaultClientDir(env));\n const versions = new Set(['v0.0.0']);\n try {\n const { stdout } = await run('go', ['list', '-m', '-f', '{{.Version}}', CLIENT_MODULE], {\n cwd: options.moduleDir,\n env: { ...env, GOWORK: 'off' },\n });\n if (stdout.trim() !== '') versions.add(stdout.trim());\n } catch {\n // A project need not import annotations itself. The injected copy still\n // carries v0.0.0, which is the only version that must be redirected then.\n }\n return [...versions].map((version) => ({ from: CLIENT_MODULE, to, version }));\n}\n\n/** Reads the framework version the module actually resolves to. */\nasync function detectFrameworkVersion(moduleDir: string, env: NodeJS.ProcessEnv): Promise<string> {\n const { stdout } = await run('go', ['list', '-m', '-f', '{{.Version}}', FRAMEWORK], {\n cwd: moduleDir,\n // Without this a workspace already in effect could report a replaced\n // version, and the patch set would be chosen for the wrong source.\n env: { ...env, GOWORK: 'off' },\n });\n return stdout.trim();\n}\n\nasync function modulePath(moduleDir: string, env: NodeJS.ProcessEnv): Promise<string> {\n const { stdout } = await run('go', ['list', '-m', '-f', '{{.Path}}'], {\n cwd: moduleDir,\n env: { ...env, GOWORK: 'off' },\n });\n return stdout.trim();\n}\n\n/** Locates the pristine module, fetching it when the cache is cold. */\nasync function upstreamDir(version: string, env: NodeJS.ProcessEnv): Promise<string> {\n return ensureUpstreamModule({\n module: FRAMEWORK,\n version,\n cachePath: ['github.com', 'rivo', `tview@${version}`],\n env,\n });\n}\n\nasync function toolchain(env: NodeJS.ProcessEnv): Promise<string> {\n const { stdout } = await run('go', ['version'], { env });\n return stdout.trim();\n}\n\n/** Directory layout used by Go for the lowercase client module path. */\nfunction moduleCachePath(module: string, version: string): readonly string[] {\n const parts = module.split('/');\n const last = parts.pop();\n return last === undefined ? [] : [...parts, `${last}@${version}`];\n}\n\nfunction patchSetFor(version: string): string {\n return join(packageRoot(), 'upstream-patches', 'tview', version);\n}\n\n/**\n * Where the protocol client lives.\n *\n * A package may vendor it as `go-client/`; this repository uses the workspace\n * source so client edits are picked up without a copy. A published standalone\n * probe resolves the matching tagged Go module into GOMODCACHE, the same\n * release strategy as probe-charm.\n */\nasync function defaultClientDir(env: NodeJS.ProcessEnv): Promise<string> {\n const vendored = join(packageRoot(), 'go-client');\n if (existsSync(join(vendored, 'go.mod'))) return vendored;\n\n const inRepo = join(packageRoot(), '..', '..', 'clients', 'go');\n if (existsSync(join(inRepo, 'go.mod'))) return inRepo;\n\n // Published probes and the Go client are released from one versioned\n // commit. Outside the monorepo, materialise that exact module version in the\n // normal Go cache instead of requiring a second manually supplied path.\n const version = `v${PROBE_VERSION}`;\n return ensureUpstreamModule({\n module: CLIENT_MODULE,\n version,\n cachePath: moduleCachePath(CLIENT_MODULE, version),\n env,\n });\n}\n\nfunction packageRoot(): string {\n return join(fileURLToPath(new URL('.', import.meta.url)), '..');\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/recognizer.ts","../src/launch.ts"],"sourcesContent":["/**\n * `@termwright/probe-tview` — semantics from a tview application with one\n * public `tviewprobe.Attach` lifecycle call.\n *\n * Go's official `-toolexec` seam adds an owned compilation unit to tview's\n * package namespace. No upstream file is copied or edited, including in\n * vendor mode.\n *\n * @packageDocumentation\n */\n\n// The Go compiler machinery is shared with every add-only Go probe.\nexport * from '@termwright/probe-go';\n\nexport { recognize, roleFor, type RecognizeOptions } from './recognizer.js';\n\nexport {\n prepareInstrumentedBuild,\n CLIENT_MODULE,\n FRAMEWORK,\n PROBE_VERSION,\n type PrepareOptions,\n type PreparedBuild,\n} from './launch.js';\n","/**\n * IR → semantic tree, as a pure function.\n *\n * The Go probe normalises inside the copy, because that is where the private\n * state is. This module is the same normalisation expressed over Probe IR, and\n * it exists for two reasons that are worth stating, since duplicated logic is\n * usually a smell:\n *\n * 1. **It is testable without a process.** The B tier of the campaign's test\n * plan feeds IR in and inspects the tree that comes out — no build, no\n * toolchain, no pseudo-terminal, and every edge case reachable in one line\n * instead of being coaxed out of a running application.\n * 2. **It is the contract the Go side is held to.** The recorded IR of a real\n * frame goes through here, and the result must match what the probe\n * published. Two implementations that disagree describe the same\n * application differently, which is the failure mode the convergence round\n * existed to prevent.\n */\n\nimport { evidence, type ProbeFrame, type ProbeObject } from '@termwright/protocol';\nimport type { SemanticNode, SemanticRole, SemanticSnapshot } from '@termwright/protocol';\n\n/**\n * tview widget type → role.\n *\n * Keyed on the framework's own type name, which the probe reports for every\n * object. Anything absent from this table is deliberately *not* a failure: it\n * becomes `generic` and keeps its `frameworkType`, so a widget added by a\n * future tview release is still addressable rather than dropped.\n */\nconst ROLE_BY_TYPE: Readonly<Record<string, SemanticRole>> = {\n Button: 'button',\n Checkbox: 'checkbox',\n InputField: 'textbox',\n TextArea: 'textbox',\n DropDown: 'list',\n List: 'list',\n TreeView: 'list',\n Table: 'table',\n TextView: 'text',\n Modal: 'dialog',\n Form: 'region',\n Flex: 'region',\n Grid: 'region',\n Pages: 'region',\n Frame: 'region',\n Box: 'region',\n};\n\n/** What a recognizer needs to know about the frame it is given. */\nexport interface RecognizeOptions {\n readonly sessionId: string;\n readonly revision: number;\n readonly columns: number;\n readonly rows: number;\n}\n\n/** Resolves one object's role from its framework type. */\nexport function roleFor(frameworkType: string): SemanticRole {\n return ROLE_BY_TYPE[frameworkType] ?? 'generic';\n}\n\n/**\n * Normalises one observed frame into a semantic snapshot.\n *\n * Facts are carried across, never invented. An object without geometry stays\n * without bounds; a state the probe did not report stays absent, because\n * \"the probe did not say\" and \"the widget is not focused\" are different\n * claims and only one of them is safe to make.\n */\nexport function recognize(frame: ProbeFrame, options: RecognizeOptions): SemanticSnapshot {\n const nodes: SemanticNode[] = [];\n const rootIds: string[] = [];\n\n for (const object of frame.objects) {\n const node = recognizeObject(object);\n nodes.push(node);\n if (object.parent === undefined) rootIds.push(object.identity.value);\n }\n\n return {\n v: 2,\n sessionId: options.sessionId,\n revision: options.revision,\n columns: options.columns,\n rows: options.rows,\n rootIds,\n nodes,\n coordinateSpace: {\n status: 'known',\n value: 'viewport-cells',\n evidence: evidence('framework', 'instrumented', 'authoritative', 'tview'),\n },\n hitGrid: {\n status: 'unsupported',\n capability: 'pointer-hit-grid',\n reason: 'framework-unobservable',\n },\n };\n}\n\nfunction recognizeObject(object: ProbeObject): SemanticNode {\n const role = roleFor(object.frameworkType);\n const state = recognizeState(object);\n\n return {\n id: object.identity.value,\n role,\n // The framework's own name survives on every node, not only generic ones:\n // it is what lets a reader tell a Flex-shaped region from a Form-shaped\n // one after both became `region`.\n frameworkType: object.frameworkType,\n name: object.annotations?.name ?? object.text ?? '',\n ...(object.parent === undefined ? {} : { parentId: object.parent }),\n geometry: {\n displayed:\n object.state?.displayed === undefined\n ? { status: 'unsupported', capability: 'displayed', reason: 'framework-unobservable' }\n : {\n status: 'known',\n value: object.state.displayed,\n evidence: evidence('framework', 'instrumented', 'authoritative', 'tview'),\n },\n intendedRect:\n object.geometry?.intendedRect === undefined\n ? {\n status: 'unsupported',\n capability: 'intended-geometry',\n reason: 'framework-unobservable',\n }\n : {\n status: 'known',\n value: object.geometry.intendedRect,\n evidence: evidence('framework', 'instrumented', 'authoritative', 'tview'),\n },\n visibleRect: {\n status: 'unsupported',\n capability: 'visible-rect',\n reason: 'framework-unobservable',\n },\n },\n ...(object.state?.value === undefined\n ? {}\n : {\n value: {\n status: 'known' as const,\n value: object.state.value,\n sensitivity: object.state?.valueSensitivity ?? 'sensitive',\n evidence: evidence('framework', 'instrumented', 'authoritative', 'tview'),\n },\n }),\n ...(state === undefined ? {} : { state }),\n } as SemanticNode;\n}\n\nfunction recognizeState(object: ProbeObject): SemanticNode['state'] {\n const observed = object.state;\n if (observed === undefined) return undefined;\n\n const state: Record<string, unknown> = {};\n if (observed.focused !== undefined) state['focused'] = observed.focused;\n if (observed.disabled !== undefined) state['disabled'] = observed.disabled;\n if (observed.checked !== undefined) state['checked'] = observed.checked;\n if (observed.expanded !== undefined) state['expanded'] = observed.expanded;\n if (observed.readonly !== undefined) state['readonly'] = observed.readonly;\n // `displayed: false` is the probe's way of saying the framework hid it; the\n // wire calls that `hidden`, and the inversion is the whole translation.\n if (observed.displayed === false) state['hidden'] = true;\n if (observed.selectedIndex !== undefined) state['positionInSet'] = observed.selectedIndex + 1;\n return Object.keys(state).length === 0 ? undefined : (state as SemanticNode['state']);\n}\n","/** Capability-driven, add-only compiler injection for tview. */\n\nimport { execFile } from 'node:child_process';\nimport { readFile, realpath } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { promisify } from 'node:util';\nimport {\n digestGoToolExecSource,\n prepareGoToolExec,\n type GoToolExecUnit,\n} from '@termwright/probe-go';\n\nconst run = promisify(execFile);\n\nexport const FRAMEWORK = 'github.com/rivo/tview';\nexport const CLIENT_MODULE = 'github.com/gorce-ai/termwright/clients/go';\nexport const PROBE_VERSION = '0.3.1';\nconst TCELL_FRAMEWORK = 'github.com/gdamore/tcell/v2';\n\nexport interface PrepareOptions {\n readonly moduleDir: string;\n /** Advisory expectation; runtime capability compilation is authoritative. */\n readonly frameworkVersion?: string;\n readonly outputDir?: string;\n readonly env?: NodeJS.ProcessEnv;\n}\n\nexport interface PreparedBuild {\n /** Canonical module directory that must be used as the build cwd. */\n readonly moduleDir: string;\n /** Insert after `go build` or `go test`. */\n readonly goArgs: readonly [string, string];\n readonly env: NodeJS.ProcessEnv;\n readonly wrapperFile: string;\n readonly configDigest: string;\n readonly frameworkVersion: string;\n readonly sourceDigests: readonly string[];\n}\n\n/**\n * Prepares one official Go `-toolexec` wrapper. It adds compilation units to\n * package namespaces selected by TOOLEXEC_IMPORTPATH and therefore works for\n * module-cache, workspace, replacement, and vendored dependency layouts.\n * No upstream path is copied, patched, or hashed.\n */\nexport async function prepareInstrumentedBuild(options: PrepareOptions): Promise<PreparedBuild> {\n const env = options.env ?? process.env;\n const moduleDir = await realpath(options.moduleDir);\n const buildEnv = { ...env, PWD: moduleDir };\n const frameworkVersion = await moduleVersion(moduleDir, FRAMEWORK, buildEnv);\n if (options.frameworkVersion !== undefined && options.frameworkVersion !== frameworkVersion) {\n throw new Error(\n `@termwright/probe-tview expected ${FRAMEWORK} ${options.frameworkVersion}, ` +\n `but the application resolves ${frameworkVersion || 'no version'}`,\n );\n }\n\n const goos = (await run('go', ['env', 'GOOS'], { cwd: moduleDir, env: buildEnv })).stdout.trim();\n const units = await compilationUnits(goos);\n const prepared = await prepareGoToolExec({\n moduleDir,\n outputDir: options.outputDir ?? join(moduleDir, '.termwright', 'go-toolexec'),\n units,\n env: buildEnv,\n });\n return {\n ...prepared,\n moduleDir,\n frameworkVersion,\n sourceDigests: units.map((unit) => unit.sourceDigest),\n };\n}\n\nexport function compilerUnitTargetsForPlatform(goos: string): readonly string[] {\n return goos === 'windows'\n ? ['zz_termwright_probe.go', 'zz_termwright_marker.go']\n : ['zz_termwright_probe.go'];\n}\n\nasync function compilationUnits(goos: string): Promise<readonly GoToolExecUnit[]> {\n const root = packageRoot();\n const declarations: {\n packagePath: string;\n targetFile: string;\n sourceFile: string;\n stripWindowsConstraint?: boolean;\n imports?: readonly string[];\n }[] = [\n {\n packagePath: FRAMEWORK,\n targetFile: 'zz_termwright_probe.go',\n sourceFile: join(root, 'assets', 'tview_probe.go.txt'),\n imports: [\n 'errors',\n 'reflect',\n 'runtime/debug',\n 'sort',\n 'strconv',\n 'strings',\n 'sync',\n 'sync/atomic',\n 'github.com/gdamore/tcell/v2',\n 'github.com/gorce-ai/termwright/clients/go/annotate',\n 'github.com/gorce-ai/termwright/clients/go/evidence',\n 'github.com/gorce-ai/termwright/clients/go/probehost',\n 'github.com/gorce-ai/termwright/clients/go/protocol',\n ],\n },\n {\n packagePath: TCELL_FRAMEWORK,\n targetFile: 'zz_termwright_marker.go',\n sourceFile: join(root, 'assets', 'tcell_marker_windows.go.txt'),\n stripWindowsConstraint: true,\n imports: ['errors', 'io', 'syscall', 'unicode/utf16', 'unsafe'],\n },\n ].filter(({ targetFile }) => compilerUnitTargetsForPlatform(goos).includes(targetFile));\n return Promise.all(\n declarations.map(\n async ({ packagePath, targetFile, sourceFile, stripWindowsConstraint, imports }) => {\n const asset = await readFile(sourceFile, 'utf8');\n const source = stripWindowsConstraint\n ? asset.replace(/^\\/\\/go:build windows\\n\\n/u, '')\n : asset;\n return {\n packagePath,\n targetFile,\n source,\n sourceDigest: digestGoToolExecSource(source),\n ...(imports === undefined ? {} : { imports }),\n };\n },\n ),\n );\n}\n\nasync function moduleVersion(\n moduleDir: string,\n framework: string,\n env: NodeJS.ProcessEnv,\n): Promise<string> {\n const result = await run('go', ['list', '-m', '-f={{.Version}}', framework], {\n cwd: moduleDir,\n env,\n });\n return result.stdout.trim();\n}\n\nfunction packageRoot(): string {\n return join(fileURLToPath(new URL('.', import.meta.url)), '..');\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@termwright/probe-tview",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"description": "composable tview probe: one public Attach call plus add-only Go compiler injection, with no upstream mutation",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"type": "module",
|
|
12
12
|
"engines": {
|
|
13
|
-
"node": "
|
|
13
|
+
"node": "^22.0.0 || ^24.0.0"
|
|
14
14
|
},
|
|
15
15
|
"exports": {
|
|
16
16
|
".": {
|
|
@@ -20,18 +20,19 @@
|
|
|
20
20
|
},
|
|
21
21
|
"files": [
|
|
22
22
|
"dist",
|
|
23
|
-
"
|
|
23
|
+
"assets"
|
|
24
24
|
],
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@termwright/
|
|
27
|
-
"@termwright/
|
|
26
|
+
"@termwright/probe-go": "0.3.1",
|
|
27
|
+
"@termwright/protocol": "0.3.1"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@termwright/
|
|
30
|
+
"@termwright/resource-broker": "0.3.1",
|
|
31
|
+
"@termwright/driver": "0.3.1"
|
|
31
32
|
},
|
|
32
33
|
"scripts": {
|
|
33
34
|
"build": "tsup src/index.ts --format esm --dts --sourcemap --clean",
|
|
34
35
|
"typecheck": "tsc --noEmit",
|
|
35
|
-
"test": "
|
|
36
|
+
"test": "pnpm --dir ../.. test -- -- --run packages/probe-tview"
|
|
36
37
|
}
|
|
37
38
|
}
|