@termwright/probe-tview 0.3.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.
@@ -45,7 +45,7 @@ import (
45
45
  // from the hand-written adapter so a session can be told apart in diagnostics.
46
46
  const (
47
47
  probeName = "termwright-probe-tview"
48
- probeVersion = "0.3.0"
48
+ probeVersion = "0.3.1"
49
49
  )
50
50
 
51
51
  // termwrightProbeState is nil for an uninstrumented run, which is every run
package/dist/index.d.ts CHANGED
@@ -42,7 +42,7 @@ declare function recognize(frame: ProbeFrame, options: RecognizeOptions): Semant
42
42
  /** Capability-driven, add-only compiler injection for tview. */
43
43
  declare const FRAMEWORK = "github.com/rivo/tview";
44
44
  declare const CLIENT_MODULE = "github.com/gorce-ai/termwright/clients/go";
45
- declare const PROBE_VERSION = "0.3.0";
45
+ declare const PROBE_VERSION = "0.3.1";
46
46
  interface PrepareOptions {
47
47
  readonly moduleDir: string;
48
48
  /** Advisory expectation; runtime capability compilation is authoritative. */
package/dist/index.js CHANGED
@@ -123,7 +123,7 @@ import {
123
123
  var run = promisify(execFile);
124
124
  var FRAMEWORK = "github.com/rivo/tview";
125
125
  var CLIENT_MODULE = "github.com/gorce-ai/termwright/clients/go";
126
- var PROBE_VERSION = "0.3.0";
126
+ var PROBE_VERSION = "0.3.1";
127
127
  var TCELL_FRAMEWORK = "github.com/gdamore/tcell/v2";
128
128
  async function prepareInstrumentedBuild(options) {
129
129
  const env = options.env ?? process.env;
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 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.0';\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":[]}
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@termwright/probe-tview",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
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": {
@@ -23,12 +23,12 @@
23
23
  "assets"
24
24
  ],
25
25
  "dependencies": {
26
- "@termwright/protocol": "0.3.0",
27
- "@termwright/probe-go": "0.3.0"
26
+ "@termwright/probe-go": "0.3.1",
27
+ "@termwright/protocol": "0.3.1"
28
28
  },
29
29
  "devDependencies": {
30
- "@termwright/resource-broker": "0.3.0",
31
- "@termwright/driver": "0.3.0"
30
+ "@termwright/resource-broker": "0.3.1",
31
+ "@termwright/driver": "0.3.1"
32
32
  },
33
33
  "scripts": {
34
34
  "build": "tsup src/index.ts --format esm --dts --sourcemap --clean",