@termwright/probe-tview 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.
- package/LICENSE +21 -0
- package/README.md +116 -0
- package/dist/index.d.ts +88 -0
- package/dist/index.js +219 -0
- package/dist/index.js.map +1 -0
- package/package.json +37 -0
- package/upstream-patches/tview/v0.42.0/add/termwright_probe.go +876 -0
- package/upstream-patches/tview/v0.42.0/add/termwright_probe_test.go +428 -0
- package/upstream-patches/tview/v0.42.0/manifest.json +42 -0
- package/upstream-patches/tview/v0.42.0/patches/application.go.patch +14 -0
- package/upstream-patches/tview/v0.42.0/patches/go.mod.patch +15 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 gorce-ai
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# @termwright/probe-tview
|
|
2
|
+
|
|
3
|
+
Semantics from a [tview](https://github.com/rivo/tview) application that
|
|
4
|
+
**imports nothing of ours**.
|
|
5
|
+
|
|
6
|
+
The application is built through an ephemeral Go workspace that redirects
|
|
7
|
+
`github.com/rivo/tview` to an instrumented copy. Nothing is written into the
|
|
8
|
+
project: its `go.mod`, its `go.sum` and any `go.work` of its own come out of the
|
|
9
|
+
build byte-identical.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npm install --save-dev @termwright/probe-tview
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Requires the Go toolchain and `git` (which the toolchain needs anyway). Node >= 22.
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
One call prepares the build; the launcher owns everything else.
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import {prepareInstrumentedBuild} from '@termwright/probe-tview';
|
|
25
|
+
|
|
26
|
+
const build = await prepareInstrumentedBuild({moduleDir: 'path/to/app'});
|
|
27
|
+
|
|
28
|
+
// build.env carries GOWORK; the project's own files are untouched.
|
|
29
|
+
await execFile('go', ['build', '-o', 'app-binary', '.'], {cwd: 'path/to/app', env: build.env});
|
|
30
|
+
|
|
31
|
+
await launchTerminal({command: ['./app-binary']});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The framework version is read from the module, the instrumented copy is cached,
|
|
35
|
+
and a second call with the same inputs reuses it.
|
|
36
|
+
|
|
37
|
+
## What it gives you
|
|
38
|
+
|
|
39
|
+
Being inside the package is the point. A `tview.Grid` exposes no accessor for
|
|
40
|
+
its children at all, so an out-of-package adapter has to be handed a callback;
|
|
41
|
+
here it is a field read that also carries whether the item was drawn. A widget
|
|
42
|
+
on a `Pages` page that is not shown reports as **hidden** rather than going
|
|
43
|
+
missing, so a test can tell "not on screen" from "not there".
|
|
44
|
+
|
|
45
|
+
Identity is the primitive's pointer: tview retains its widget tree, so the same
|
|
46
|
+
`*Button` is the same button across frames. The handshake therefore reports
|
|
47
|
+
`identityKind: 'stable'` and only the probe capabilities it earns:
|
|
48
|
+
`stable-identity` and `annotations`. Its `frameworkVersion` is the exact
|
|
49
|
+
version selected by the verified patch set, not the Go runtime version.
|
|
50
|
+
|
|
51
|
+
## Describing what the probe cannot see
|
|
52
|
+
|
|
53
|
+
Zero-config means the probe reads facts. It cannot read intent — which button
|
|
54
|
+
is the destructive one, which list is the inbox, what "overdue" means here. For
|
|
55
|
+
that, and only for that, an application may import
|
|
56
|
+
`github.com/gorce-ai/termwright/clients/go/annotate`:
|
|
57
|
+
|
|
58
|
+
```go
|
|
59
|
+
import (
|
|
60
|
+
"github.com/gorce-ai/termwright/clients/go/annotate"
|
|
61
|
+
"github.com/gorce-ai/termwright/clients/go/protocol"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
annotate.Tag(label, annotate.Semantics{Key: "unread-label"})
|
|
65
|
+
annotate.Tag(unreadBadge, annotate.Semantics{
|
|
66
|
+
Role: "status", Name: "Unread messages", TestID: "unread-badge",
|
|
67
|
+
Actions: []protocol.Action{protocol.ActionFocus, protocol.ActionActivate},
|
|
68
|
+
LabelledBy: []annotate.SemanticKey{"unread-label"},
|
|
69
|
+
})
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The probe merges this with what it observed: the wording is the author's, the
|
|
73
|
+
bounds and the focus stay the probe's. `Semantics` has no field for bounds,
|
|
74
|
+
focus, visibility, value, rendered text or framework state — not by convention
|
|
75
|
+
but structurally, so an annotation cannot go stale against the screen. Actions
|
|
76
|
+
come from the protocol's closed descriptive set and never install callbacks.
|
|
77
|
+
Tagging retains nothing; the entry is released with the widget.
|
|
78
|
+
|
|
79
|
+
`LabelledBy` and `DescribedBy` use framework-neutral `SemanticKey` strings, so
|
|
80
|
+
one annotation does not retain its targets. The probe resolves them after the
|
|
81
|
+
whole tree has been walked. Missing or duplicate keys are omitted instead of
|
|
82
|
+
becoming dangling or arbitrary node references. Pointer identity remains the
|
|
83
|
+
stable node id; a key is only the relation target for tview. Primary framework
|
|
84
|
+
provenance is reported in `p`, with recognizer and annotation exceptions in
|
|
85
|
+
`px`.
|
|
86
|
+
|
|
87
|
+
This is the one import that makes an application no longer zero-config, which
|
|
88
|
+
is why it is optional and why the two example fixtures in this package are kept
|
|
89
|
+
apart.
|
|
90
|
+
|
|
91
|
+
## Dormant without instrumentation
|
|
92
|
+
|
|
93
|
+
Without `TERMWRIGHT_ENDPOINT` and `TERMWRIGHT_TOKEN` the instrumented copy opens
|
|
94
|
+
no socket, writes no marker and renders exactly what upstream renders. That is
|
|
95
|
+
measured, not asserted: the test suite builds the same application twice, once
|
|
96
|
+
against untouched tview and once against the copy, and requires the two screens
|
|
97
|
+
to be byte-identical.
|
|
98
|
+
|
|
99
|
+
## When it refuses
|
|
100
|
+
|
|
101
|
+
- `-mod=vendor` in `GOFLAGS` is reported by name rather than overridden;
|
|
102
|
+
workspace mode is incompatible with it, and overriding would change what
|
|
103
|
+
compiles.
|
|
104
|
+
- A framework version with no patch set is named as such — "this is not
|
|
105
|
+
tview v0.42.0" — instead of failing somewhere inside a diff.
|
|
106
|
+
|
|
107
|
+
## Development
|
|
108
|
+
|
|
109
|
+
```sh
|
|
110
|
+
pnpm build && pnpm typecheck && pnpm test
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
The suites that need Go or a pseudo-terminal skip themselves where either is
|
|
114
|
+
missing, and say so in a test named for it. `TERMWRIGHT_SKIP_GO=1` and
|
|
115
|
+
`TERMWRIGHT_SKIP_PTY=1` force it. Implementation notes, including the traps
|
|
116
|
+
that cost time, are in [`NOTES.md`](NOTES.md).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
export * from '@termwright/probe-go';
|
|
2
|
+
import { ProbeFrame, SemanticSnapshot, SemanticRole } from '@termwright/protocol';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* IR → semantic tree, as a pure function.
|
|
6
|
+
*
|
|
7
|
+
* The Go probe normalises inside the copy, because that is where the private
|
|
8
|
+
* state is. This module is the same normalisation expressed over Probe IR, and
|
|
9
|
+
* it exists for two reasons that are worth stating, since duplicated logic is
|
|
10
|
+
* usually a smell:
|
|
11
|
+
*
|
|
12
|
+
* 1. **It is testable without a process.** The B tier of the campaign's test
|
|
13
|
+
* plan feeds IR in and inspects the tree that comes out — no build, no
|
|
14
|
+
* toolchain, no pseudo-terminal, and every edge case reachable in one line
|
|
15
|
+
* instead of being coaxed out of a running application.
|
|
16
|
+
* 2. **It is the contract the Go side is held to.** The recorded IR of a real
|
|
17
|
+
* frame goes through here, and the result must match what the probe
|
|
18
|
+
* published. Two implementations that disagree describe the same
|
|
19
|
+
* application differently, which is the failure mode the convergence round
|
|
20
|
+
* existed to prevent.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/** What a recognizer needs to know about the frame it is given. */
|
|
24
|
+
interface RecognizeOptions {
|
|
25
|
+
readonly sessionId: string;
|
|
26
|
+
readonly revision: number;
|
|
27
|
+
readonly columns: number;
|
|
28
|
+
readonly rows: number;
|
|
29
|
+
}
|
|
30
|
+
/** Resolves one object's role from its framework type. */
|
|
31
|
+
declare function roleFor(frameworkType: string): SemanticRole;
|
|
32
|
+
/**
|
|
33
|
+
* Normalises one observed frame into a semantic snapshot.
|
|
34
|
+
*
|
|
35
|
+
* Facts are carried across, never invented. An object without geometry stays
|
|
36
|
+
* without bounds; a state the probe did not report stays absent, because
|
|
37
|
+
* "the probe did not say" and "the widget is not focused" are different
|
|
38
|
+
* claims and only one of them is safe to make.
|
|
39
|
+
*/
|
|
40
|
+
declare function recognize(frame: ProbeFrame, options: RecognizeOptions): SemanticSnapshot;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The one call a launcher makes.
|
|
44
|
+
*
|
|
45
|
+
* Everything else in this package is a step: read the workspace, materialise a
|
|
46
|
+
* copy, patch it, key the cache, write the file. A user should not assemble
|
|
47
|
+
* those, and neither should a test — the assembly order is where the mistakes
|
|
48
|
+
* live, and there is exactly one correct one.
|
|
49
|
+
*/
|
|
50
|
+
/** Module path of the framework this probe instruments. */
|
|
51
|
+
declare const FRAMEWORK = "github.com/rivo/tview";
|
|
52
|
+
/** Module path of the protocol client the injected probe imports. */
|
|
53
|
+
declare const CLIENT_MODULE = "github.com/gorce-ai/termwright/clients/go";
|
|
54
|
+
/** Version of this probe; part of the cache key, so a new patch set invalidates copies. */
|
|
55
|
+
declare const PROBE_VERSION = "0.1.0";
|
|
56
|
+
interface PrepareOptions {
|
|
57
|
+
/** Directory of the Go module to build. */
|
|
58
|
+
readonly moduleDir: string;
|
|
59
|
+
/** Framework version to instrument, e.g. `v0.42.0`. */
|
|
60
|
+
readonly frameworkVersion?: string;
|
|
61
|
+
/** Where the protocol client lives on disk. Defaults to the copy shipped here. */
|
|
62
|
+
readonly clientDir?: string;
|
|
63
|
+
/** Where the generated workspace is written. Defaults to inside the copy's cache entry. */
|
|
64
|
+
readonly workspaceFile?: string;
|
|
65
|
+
/** Environment the build will run with; checked, never mutated. */
|
|
66
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
67
|
+
}
|
|
68
|
+
interface PreparedBuild {
|
|
69
|
+
/** Hand this to the build as `GOWORK`. */
|
|
70
|
+
readonly workspaceFile: string;
|
|
71
|
+
/** The instrumented copy, for a canary check or for diagnosis. */
|
|
72
|
+
readonly copyDir: string;
|
|
73
|
+
/** Environment to build with: the caller's, plus GOWORK. */
|
|
74
|
+
readonly env: NodeJS.ProcessEnv;
|
|
75
|
+
/** True when the copy was built during this call rather than reused. */
|
|
76
|
+
readonly built: boolean;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Makes a build of `moduleDir` compile against the instrumented framework.
|
|
80
|
+
*
|
|
81
|
+
* Does not build anything and does not spawn the application: a launcher owns
|
|
82
|
+
* that. This returns what the build needs and nothing else, which keeps the
|
|
83
|
+
* package testable and keeps the decision about *how* to run the user's build
|
|
84
|
+
* where it belongs.
|
|
85
|
+
*/
|
|
86
|
+
declare function prepareInstrumentedBuild(options: PrepareOptions): Promise<PreparedBuild>;
|
|
87
|
+
|
|
88
|
+
export { CLIENT_MODULE, FRAMEWORK, PROBE_VERSION, type PrepareOptions, type PreparedBuild, type RecognizeOptions, prepareInstrumentedBuild, recognize, roleFor };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
export * from "@termwright/probe-go";
|
|
3
|
+
|
|
4
|
+
// src/recognizer.ts
|
|
5
|
+
var ROLE_BY_TYPE = {
|
|
6
|
+
Button: "button",
|
|
7
|
+
Checkbox: "checkbox",
|
|
8
|
+
InputField: "textbox",
|
|
9
|
+
TextArea: "textbox",
|
|
10
|
+
DropDown: "list",
|
|
11
|
+
List: "list",
|
|
12
|
+
TreeView: "list",
|
|
13
|
+
Table: "table",
|
|
14
|
+
TextView: "text",
|
|
15
|
+
Modal: "dialog",
|
|
16
|
+
Form: "region",
|
|
17
|
+
Flex: "region",
|
|
18
|
+
Grid: "region",
|
|
19
|
+
Pages: "region",
|
|
20
|
+
Frame: "region",
|
|
21
|
+
Box: "region"
|
|
22
|
+
};
|
|
23
|
+
function roleFor(frameworkType) {
|
|
24
|
+
return ROLE_BY_TYPE[frameworkType] ?? "generic";
|
|
25
|
+
}
|
|
26
|
+
function recognize(frame, options) {
|
|
27
|
+
const nodes = [];
|
|
28
|
+
const rootIds = [];
|
|
29
|
+
for (const object of frame.objects) {
|
|
30
|
+
const node = recognizeObject(object);
|
|
31
|
+
nodes.push(node);
|
|
32
|
+
if (object.parent === void 0) rootIds.push(object.identity.value);
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
v: 1,
|
|
36
|
+
sessionId: options.sessionId,
|
|
37
|
+
revision: options.revision,
|
|
38
|
+
columns: options.columns,
|
|
39
|
+
rows: options.rows,
|
|
40
|
+
rootIds,
|
|
41
|
+
nodes
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function recognizeObject(object) {
|
|
45
|
+
const role = roleFor(object.frameworkType);
|
|
46
|
+
const state = recognizeState(object);
|
|
47
|
+
return {
|
|
48
|
+
id: object.identity.value,
|
|
49
|
+
role,
|
|
50
|
+
// The framework's own name survives on every node, not only generic ones:
|
|
51
|
+
// it is what lets a reader tell a Flex-shaped region from a Form-shaped
|
|
52
|
+
// one after both became `region`.
|
|
53
|
+
frameworkType: object.frameworkType,
|
|
54
|
+
name: object.annotations?.name ?? object.text ?? "",
|
|
55
|
+
...object.parent === void 0 ? {} : { parentId: object.parent },
|
|
56
|
+
// intendedRect is where the widget was drawn. visibleRect is absent for
|
|
57
|
+
// tview, which computes no clip, so nothing here fabricates one.
|
|
58
|
+
...object.geometry?.intendedRect === void 0 ? {} : { bounds: object.geometry.intendedRect },
|
|
59
|
+
...object.state?.value === void 0 ? {} : { value: object.state.value },
|
|
60
|
+
...state === void 0 ? {} : { state }
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function recognizeState(object) {
|
|
64
|
+
const observed = object.state;
|
|
65
|
+
if (observed === void 0) return void 0;
|
|
66
|
+
const state = {};
|
|
67
|
+
if (observed.focused !== void 0) state["focused"] = observed.focused;
|
|
68
|
+
if (observed.disabled !== void 0) state["disabled"] = observed.disabled;
|
|
69
|
+
if (observed.checked !== void 0) state["checked"] = observed.checked;
|
|
70
|
+
if (observed.expanded !== void 0) state["expanded"] = observed.expanded;
|
|
71
|
+
if (observed.readonly !== void 0) state["readonly"] = observed.readonly;
|
|
72
|
+
if (observed.displayed === false) state["hidden"] = true;
|
|
73
|
+
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
|
+
return Object.keys(state).length === 0 ? void 0 : state;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/launch.ts
|
|
84
|
+
import { execFile } from "child_process";
|
|
85
|
+
import { existsSync } from "fs";
|
|
86
|
+
import { join } from "path";
|
|
87
|
+
import { fileURLToPath } from "url";
|
|
88
|
+
import { promisify } from "util";
|
|
89
|
+
import {
|
|
90
|
+
applyPatchSet,
|
|
91
|
+
assertNoVendorMode,
|
|
92
|
+
copyDir,
|
|
93
|
+
digestPatchSet,
|
|
94
|
+
ensureUpstreamModule,
|
|
95
|
+
isComplete,
|
|
96
|
+
markComplete,
|
|
97
|
+
materializeUpstream,
|
|
98
|
+
prepareCopyDir,
|
|
99
|
+
readManifest,
|
|
100
|
+
readWorkspace,
|
|
101
|
+
writeProvenance,
|
|
102
|
+
writeWorkspace
|
|
103
|
+
} from "@termwright/probe-go";
|
|
104
|
+
var run = promisify(execFile);
|
|
105
|
+
var FRAMEWORK = "github.com/rivo/tview";
|
|
106
|
+
var CLIENT_MODULE = "github.com/gorce-ai/termwright/clients/go";
|
|
107
|
+
var PROBE_VERSION = "0.1.0";
|
|
108
|
+
async function prepareInstrumentedBuild(options) {
|
|
109
|
+
const env = options.env ?? process.env;
|
|
110
|
+
assertNoVendorMode(env);
|
|
111
|
+
const frameworkVersion = options.frameworkVersion ?? await detectFrameworkVersion(options.moduleDir, env);
|
|
112
|
+
const patchSetDir = patchSetFor(frameworkVersion);
|
|
113
|
+
const manifest = await readManifest(patchSetDir);
|
|
114
|
+
const key = {
|
|
115
|
+
framework: FRAMEWORK,
|
|
116
|
+
frameworkVersion,
|
|
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);
|
|
129
|
+
}
|
|
130
|
+
const workspaceFile = await writeWorkspace(options.workspaceFile ?? join(copy, "..", "generated.work"), {
|
|
131
|
+
moduleDir: options.moduleDir,
|
|
132
|
+
inherited: await readWorkspace(options.moduleDir),
|
|
133
|
+
suppliedUses: await clientWorkspaceUses(options, env),
|
|
134
|
+
replaces: [
|
|
135
|
+
{ from: FRAMEWORK, to: copy },
|
|
136
|
+
...await clientVersionReplacements(options, env)
|
|
137
|
+
]
|
|
138
|
+
});
|
|
139
|
+
return { workspaceFile, copyDir: copy, env: { ...env, GOWORK: workspaceFile }, built };
|
|
140
|
+
}
|
|
141
|
+
async function clientWorkspaceUses(options, env) {
|
|
142
|
+
if (await modulePath(options.moduleDir, env) === CLIENT_MODULE) return [];
|
|
143
|
+
return [{ dir: options.clientDir ?? await defaultClientDir(env), module: CLIENT_MODULE }];
|
|
144
|
+
}
|
|
145
|
+
async function clientVersionReplacements(options, env) {
|
|
146
|
+
if (await modulePath(options.moduleDir, env) === CLIENT_MODULE) return [];
|
|
147
|
+
const to = options.clientDir ?? await defaultClientDir(env);
|
|
148
|
+
const versions = /* @__PURE__ */ new Set(["v0.0.0"]);
|
|
149
|
+
try {
|
|
150
|
+
const { stdout } = await run("go", ["list", "-m", "-f", "{{.Version}}", CLIENT_MODULE], {
|
|
151
|
+
cwd: options.moduleDir,
|
|
152
|
+
env: { ...env, GOWORK: "off" }
|
|
153
|
+
});
|
|
154
|
+
if (stdout.trim() !== "") versions.add(stdout.trim());
|
|
155
|
+
} catch {
|
|
156
|
+
}
|
|
157
|
+
return [...versions].map((version) => ({ from: CLIENT_MODULE, to, version }));
|
|
158
|
+
}
|
|
159
|
+
async function detectFrameworkVersion(moduleDir, env) {
|
|
160
|
+
const { stdout } = await run("go", ["list", "-m", "-f", "{{.Version}}", FRAMEWORK], {
|
|
161
|
+
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
|
+
env
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
function packageRoot() {
|
|
209
|
+
return join(fileURLToPath(new URL(".", import.meta.url)), "..");
|
|
210
|
+
}
|
|
211
|
+
export {
|
|
212
|
+
CLIENT_MODULE,
|
|
213
|
+
FRAMEWORK,
|
|
214
|
+
PROBE_VERSION,
|
|
215
|
+
prepareInstrumentedBuild,
|
|
216
|
+
recognize,
|
|
217
|
+
roleFor
|
|
218
|
+
};
|
|
219
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@termwright/probe-tview",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "zero-config tview probe: an ephemeral Go workspace redirects the build to an instrumented copy, with no change to the project",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/Gorce-AI/termwright.git",
|
|
9
|
+
"directory": "packages/probe-tview"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": ">=22"
|
|
14
|
+
},
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"import": "./dist/index.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"upstream-patches"
|
|
24
|
+
],
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@termwright/protocol": "0.2.0",
|
|
27
|
+
"@termwright/probe-go": "0.2.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@termwright/driver": "0.2.0"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsup src/index.ts --format esm --dts --sourcemap --clean",
|
|
34
|
+
"typecheck": "tsc --noEmit",
|
|
35
|
+
"test": "vitest run"
|
|
36
|
+
}
|
|
37
|
+
}
|