cyclops-clientdevbridge-cli 0.1.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/CHANGELOG.md +41 -0
- package/LICENSE +21 -0
- package/README.md +218 -0
- package/dist/artifacts.d.ts +56 -0
- package/dist/artifacts.d.ts.map +1 -0
- package/dist/artifacts.js +58 -0
- package/dist/artifacts.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +348 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/compare.d.ts +27 -0
- package/dist/commands/compare.d.ts.map +1 -0
- package/dist/commands/compare.js +209 -0
- package/dist/commands/compare.js.map +1 -0
- package/dist/commands/context.d.ts +25 -0
- package/dist/commands/context.d.ts.map +1 -0
- package/dist/commands/context.js +36 -0
- package/dist/commands/context.js.map +1 -0
- package/dist/commands/doctor.d.ts +31 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +234 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/hotswap.d.ts +7 -0
- package/dist/commands/hotswap.d.ts.map +1 -0
- package/dist/commands/hotswap.js +76 -0
- package/dist/commands/hotswap.js.map +1 -0
- package/dist/commands/input.d.ts +53 -0
- package/dist/commands/input.d.ts.map +1 -0
- package/dist/commands/input.js +204 -0
- package/dist/commands/input.js.map +1 -0
- package/dist/commands/inspect.d.ts +19 -0
- package/dist/commands/inspect.d.ts.map +1 -0
- package/dist/commands/inspect.js +90 -0
- package/dist/commands/inspect.js.map +1 -0
- package/dist/commands/lifecycle.d.ts +29 -0
- package/dist/commands/lifecycle.d.ts.map +1 -0
- package/dist/commands/lifecycle.js +201 -0
- package/dist/commands/lifecycle.js.map +1 -0
- package/dist/commands/snapshot.d.ts +37 -0
- package/dist/commands/snapshot.d.ts.map +1 -0
- package/dist/commands/snapshot.js +133 -0
- package/dist/commands/snapshot.js.map +1 -0
- package/dist/commands/world.d.ts +27 -0
- package/dist/commands/world.d.ts.map +1 -0
- package/dist/commands/world.js +203 -0
- package/dist/commands/world.js.map +1 -0
- package/dist/detect.d.ts +58 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/detect.js +185 -0
- package/dist/detect.js.map +1 -0
- package/dist/errors.d.ts +24 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +37 -0
- package/dist/errors.js.map +1 -0
- package/dist/hotswap.d.ts +58 -0
- package/dist/hotswap.d.ts.map +1 -0
- package/dist/hotswap.js +284 -0
- package/dist/hotswap.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/initscript.d.ts +49 -0
- package/dist/initscript.d.ts.map +1 -0
- package/dist/initscript.js +202 -0
- package/dist/initscript.js.map +1 -0
- package/dist/java.d.ts +47 -0
- package/dist/java.d.ts.map +1 -0
- package/dist/java.js +114 -0
- package/dist/java.js.map +1 -0
- package/dist/launcher.d.ts +104 -0
- package/dist/launcher.d.ts.map +1 -0
- package/dist/launcher.js +609 -0
- package/dist/launcher.js.map +1 -0
- package/dist/loopback.d.ts +17 -0
- package/dist/loopback.d.ts.map +1 -0
- package/dist/loopback.js +19 -0
- package/dist/loopback.js.map +1 -0
- package/dist/output.d.ts +21 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +41 -0
- package/dist/output.js.map +1 -0
- package/dist/paths.d.ts +23 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +47 -0
- package/dist/paths.js.map +1 -0
- package/dist/protocol/client.d.ts +35 -0
- package/dist/protocol/client.d.ts.map +1 -0
- package/dist/protocol/client.js +170 -0
- package/dist/protocol/client.js.map +1 -0
- package/dist/protocol/types.d.ts +114 -0
- package/dist/protocol/types.d.ts.map +1 -0
- package/dist/protocol/types.js +38 -0
- package/dist/protocol/types.js.map +1 -0
- package/dist/session.d.ts +77 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +79 -0
- package/dist/session.js.map +1 -0
- package/dist/snapshot/model.d.ts +327 -0
- package/dist/snapshot/model.d.ts.map +1 -0
- package/dist/snapshot/model.js +91 -0
- package/dist/snapshot/model.js.map +1 -0
- package/dist/snapshot/outline.d.ts +35 -0
- package/dist/snapshot/outline.d.ts.map +1 -0
- package/dist/snapshot/outline.js +135 -0
- package/dist/snapshot/outline.js.map +1 -0
- package/dist/xvfb.d.ts +30 -0
- package/dist/xvfb.d.ts.map +1 -0
- package/dist/xvfb.js +66 -0
- package/dist/xvfb.js.map +1 -0
- package/package.json +66 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { centre, flatten, label, simpleType } from './model.js';
|
|
2
|
+
/**
|
|
3
|
+
* Renders a snapshot as one line per widget, indented by depth.
|
|
4
|
+
*
|
|
5
|
+
* This is the format an agent actually reads, so it is optimised for scanning: the type and label
|
|
6
|
+
* first, then the geometry it would need to click, then only the flags that are *not* the default.
|
|
7
|
+
* A line looks like:
|
|
8
|
+
*
|
|
9
|
+
* ```
|
|
10
|
+
* Button "Apply" @(312,208 60x20) disabled /root/children[3]
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* The trailing path is what `click --widget` consumes, so an outline line can be copied verbatim.
|
|
14
|
+
*/
|
|
15
|
+
export function formatOutline(snapshot, options = {}) {
|
|
16
|
+
const lines = [];
|
|
17
|
+
if (snapshot.screenClass === null) {
|
|
18
|
+
lines.push('No screen is open.');
|
|
19
|
+
lines.push(`window ${snapshot.pixelWidth}x${snapshot.pixelHeight}px, gui ${snapshot.guiWidth}x${snapshot.guiHeight} @ scale ${snapshot.guiScale}`);
|
|
20
|
+
return lines.join('\n');
|
|
21
|
+
}
|
|
22
|
+
lines.push(`${simpleType({ type: snapshot.screenClass })} "${snapshot.title ?? ''}"`);
|
|
23
|
+
lines.push(`gui ${snapshot.guiWidth}x${snapshot.guiHeight} @ scale ${snapshot.guiScale}` +
|
|
24
|
+
`, window ${snapshot.pixelWidth}x${snapshot.pixelHeight}px` +
|
|
25
|
+
`, mouse at ${snapshot.mouse.join(',')}`);
|
|
26
|
+
if (snapshot.container !== null) {
|
|
27
|
+
const container = snapshot.container;
|
|
28
|
+
const filled = container.slots.filter((slot) => slot.item !== null).length;
|
|
29
|
+
lines.push(`container ${simpleType({ type: container.menuClass })}` +
|
|
30
|
+
` at (${container.leftPos},${container.topPos}) ${container.imageWidth}x${container.imageHeight}` +
|
|
31
|
+
`, ${container.slots.length} slots (${filled} filled)`);
|
|
32
|
+
for (const slot of container.slots) {
|
|
33
|
+
if (slot.item === null && !slot.hovered) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
lines.push(` ${formatSlot(slot)}`);
|
|
37
|
+
}
|
|
38
|
+
if (container.carried.item !== null) {
|
|
39
|
+
lines.push(` carried: ${container.carried.item} x${container.carried.count}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const nodes = flatten(snapshot.root);
|
|
43
|
+
// The root node is the screen itself, already summarised above.
|
|
44
|
+
for (const { node, depth } of nodes.slice(1)) {
|
|
45
|
+
if (!node.visible && options.includeHidden !== true) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
lines.push(`${' '.repeat(depth)}${formatNode(node)}`);
|
|
49
|
+
}
|
|
50
|
+
if (nodes.length <= 1 && snapshot.container === null) {
|
|
51
|
+
lines.push('(this screen has no widgets)');
|
|
52
|
+
}
|
|
53
|
+
if (snapshot.truncated) {
|
|
54
|
+
lines.push('# truncated: the widget tree hit the depth or node limit');
|
|
55
|
+
}
|
|
56
|
+
return lines.join('\n');
|
|
57
|
+
}
|
|
58
|
+
export function formatSlot(slot) {
|
|
59
|
+
const item = slot.item === null ? '(empty)' : `${slot.item} x${slot.count}`;
|
|
60
|
+
const flags = slot.hovered ? ' hovered' : '';
|
|
61
|
+
return `slot ${String(slot.index).padStart(2)} ${item} @(${slot.x},${slot.y})${flags}`;
|
|
62
|
+
}
|
|
63
|
+
export function formatNode(node) {
|
|
64
|
+
const parts = [simpleType(node)];
|
|
65
|
+
const text = label(node);
|
|
66
|
+
if (text.length > 0) {
|
|
67
|
+
parts.push(`"${text}"`);
|
|
68
|
+
}
|
|
69
|
+
if (node.bounds !== null) {
|
|
70
|
+
parts.push(`@(${node.bounds.x},${node.bounds.y} ${node.bounds.w}x${node.bounds.h})`);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
parts.push('@(unknown)');
|
|
74
|
+
}
|
|
75
|
+
if (node.value !== null && node.value !== undefined) {
|
|
76
|
+
parts.push(`= ${formatValue(node.value)}`);
|
|
77
|
+
}
|
|
78
|
+
// Only the states that differ from the default are worth a reader's attention.
|
|
79
|
+
if (!node.active) {
|
|
80
|
+
parts.push('disabled');
|
|
81
|
+
}
|
|
82
|
+
if (!node.visible) {
|
|
83
|
+
parts.push('hidden');
|
|
84
|
+
}
|
|
85
|
+
if (node.focused) {
|
|
86
|
+
parts.push('focused');
|
|
87
|
+
}
|
|
88
|
+
if (node.hovered) {
|
|
89
|
+
parts.push('hovered');
|
|
90
|
+
}
|
|
91
|
+
const tooltip = node.extra['tooltip'];
|
|
92
|
+
if (Array.isArray(tooltip) && tooltip.length > 0) {
|
|
93
|
+
parts.push(`tooltip="${tooltip.join(' / ')}"`);
|
|
94
|
+
}
|
|
95
|
+
parts.push(` ${node.path}`);
|
|
96
|
+
return parts.join(' ');
|
|
97
|
+
}
|
|
98
|
+
function formatValue(value) {
|
|
99
|
+
if (typeof value === 'string') {
|
|
100
|
+
return `"${value}"`;
|
|
101
|
+
}
|
|
102
|
+
if (typeof value === 'number') {
|
|
103
|
+
return Number.isInteger(value) ? String(value) : value.toFixed(3);
|
|
104
|
+
}
|
|
105
|
+
return JSON.stringify(value);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Finds nodes by label, by path, or by type.
|
|
109
|
+
*
|
|
110
|
+
* An exact path match wins outright, then an exact label match, then a case-insensitive
|
|
111
|
+
* substring match — so `click --widget Apply` picks the "Apply" button even on a screen that
|
|
112
|
+
* also has an "Apply to all" one.
|
|
113
|
+
*/
|
|
114
|
+
export function findNodes(snapshot, query, typeFilter) {
|
|
115
|
+
const all = flatten(snapshot.root).map(({ node }) => node);
|
|
116
|
+
const candidates = typeFilter === undefined
|
|
117
|
+
? all
|
|
118
|
+
: all.filter((node) => simpleType(node).toLowerCase().includes(typeFilter.toLowerCase()));
|
|
119
|
+
const byPath = candidates.filter((node) => node.path === query);
|
|
120
|
+
if (byPath.length > 0) {
|
|
121
|
+
return byPath.map(toMatch);
|
|
122
|
+
}
|
|
123
|
+
const lowered = query.toLowerCase();
|
|
124
|
+
const exact = candidates.filter((node) => label(node).toLowerCase() === lowered);
|
|
125
|
+
if (exact.length > 0) {
|
|
126
|
+
return exact.map(toMatch);
|
|
127
|
+
}
|
|
128
|
+
return candidates
|
|
129
|
+
.filter((node) => label(node).toLowerCase().includes(lowered) || simpleType(node).toLowerCase() === lowered)
|
|
130
|
+
.map(toMatch);
|
|
131
|
+
}
|
|
132
|
+
function toMatch(node) {
|
|
133
|
+
return { node, centre: centre(node) };
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=outline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outline.js","sourceRoot":"","sources":["../../src/snapshot/outline.ts"],"names":[],"mappings":"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"}
|
package/dist/xvfb.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface DisplayPlan {
|
|
2
|
+
/** The command to run, which may be `xvfb-run` wrapping the real one. */
|
|
3
|
+
readonly command: string;
|
|
4
|
+
readonly prefixArgs: readonly string[];
|
|
5
|
+
readonly env: Readonly<Record<string, string>>;
|
|
6
|
+
readonly description: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function commandExists(command: string): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Decides how to give the client a display.
|
|
11
|
+
*
|
|
12
|
+
* "Headless" here means a real client on a virtual display, not a client without rendering:
|
|
13
|
+
* on Linux without `$DISPLAY` that is Xvfb plus Mesa's llvmpipe software rasteriser. Everywhere
|
|
14
|
+
* else, and with `--headed`, the client just opens a window. Nothing else about the CLI's
|
|
15
|
+
* behaviour differs between the two.
|
|
16
|
+
*/
|
|
17
|
+
export declare function planDisplay(command: string, options: {
|
|
18
|
+
headed: boolean;
|
|
19
|
+
width: number;
|
|
20
|
+
height: number;
|
|
21
|
+
}): DisplayPlan;
|
|
22
|
+
/**
|
|
23
|
+
* The pids of every Xvfb server currently running.
|
|
24
|
+
*
|
|
25
|
+
* `xvfb-run` picks a free display itself and never says which, so the only way to know what it
|
|
26
|
+
* started is to look before and after. Anything that appears in between is what this launch is
|
|
27
|
+
* responsible for cleaning up.
|
|
28
|
+
*/
|
|
29
|
+
export declare function xvfbPids(): number[];
|
|
30
|
+
//# sourceMappingURL=xvfb.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xvfb.d.ts","sourceRoot":"","sources":["../src/xvfb.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,WAAW;IAC1B,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC/C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAOtD;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC1D,WAAW,CA+Bb;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,IAAI,MAAM,EAAE,CAUnC"}
|
package/dist/xvfb.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
export function commandExists(command) {
|
|
3
|
+
try {
|
|
4
|
+
execFileSync(process.platform === 'win32' ? 'where' : 'which', [command], { stdio: 'ignore' });
|
|
5
|
+
return true;
|
|
6
|
+
}
|
|
7
|
+
catch {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Decides how to give the client a display.
|
|
13
|
+
*
|
|
14
|
+
* "Headless" here means a real client on a virtual display, not a client without rendering:
|
|
15
|
+
* on Linux without `$DISPLAY` that is Xvfb plus Mesa's llvmpipe software rasteriser. Everywhere
|
|
16
|
+
* else, and with `--headed`, the client just opens a window. Nothing else about the CLI's
|
|
17
|
+
* behaviour differs between the two.
|
|
18
|
+
*/
|
|
19
|
+
export function planDisplay(command, options) {
|
|
20
|
+
const hasDisplay = (process.env['DISPLAY'] ?? '').length > 0;
|
|
21
|
+
if (options.headed || hasDisplay || process.platform !== 'linux') {
|
|
22
|
+
return {
|
|
23
|
+
command,
|
|
24
|
+
prefixArgs: [],
|
|
25
|
+
env: {},
|
|
26
|
+
description: options.headed
|
|
27
|
+
? 'headed (--headed)'
|
|
28
|
+
: hasDisplay
|
|
29
|
+
? `existing display ${process.env['DISPLAY']}`
|
|
30
|
+
: `native window on ${process.platform}`,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (!commandExists('xvfb-run')) {
|
|
34
|
+
throw new Error('No $DISPLAY is set and xvfb-run is not installed, so the client has nowhere to render.\n' +
|
|
35
|
+
'Install it with: sudo apt-get install -y xvfb libgl1-mesa-dri\n' +
|
|
36
|
+
'Or pass --headed if you do have a display available.');
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
command: 'xvfb-run',
|
|
40
|
+
// A generous virtual screen: the client window is smaller, but resizing beyond the
|
|
41
|
+
// screen (window.resize) would otherwise silently clamp.
|
|
42
|
+
prefixArgs: ['-a', '-s', `-screen 0 ${Math.max(options.width, 1280)}x${Math.max(options.height, 800)}x24`],
|
|
43
|
+
env: { LIBGL_ALWAYS_SOFTWARE: '1' },
|
|
44
|
+
description: 'xvfb-run with Mesa llvmpipe',
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The pids of every Xvfb server currently running.
|
|
49
|
+
*
|
|
50
|
+
* `xvfb-run` picks a free display itself and never says which, so the only way to know what it
|
|
51
|
+
* started is to look before and after. Anything that appears in between is what this launch is
|
|
52
|
+
* responsible for cleaning up.
|
|
53
|
+
*/
|
|
54
|
+
export function xvfbPids() {
|
|
55
|
+
try {
|
|
56
|
+
return execFileSync('pgrep', ['-x', 'Xvfb'], { encoding: 'utf8' })
|
|
57
|
+
.split('\n')
|
|
58
|
+
.map((line) => Number(line.trim()))
|
|
59
|
+
.filter((pid) => Number.isInteger(pid) && pid > 0);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// pgrep exits non-zero when nothing matches, and is absent on platforms that never use Xvfb.
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=xvfb.js.map
|
package/dist/xvfb.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xvfb.js","sourceRoot":"","sources":["../src/xvfb.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAUlD,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,IAAI,CAAC;QACH,YAAY,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC/F,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CACzB,OAAe,EACf,OAA2D;IAE3D,MAAM,UAAU,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;IAC7D,IAAI,OAAO,CAAC,MAAM,IAAI,UAAU,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjE,OAAO;YACL,OAAO;YACP,UAAU,EAAE,EAAE;YACd,GAAG,EAAE,EAAE;YACP,WAAW,EAAE,OAAO,CAAC,MAAM;gBACzB,CAAC,CAAC,mBAAmB;gBACrB,CAAC,CAAC,UAAU;oBACV,CAAC,CAAC,oBAAoB,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE;oBAC9C,CAAC,CAAC,oBAAoB,OAAO,CAAC,QAAQ,EAAE;SAC7C,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CACb,0FAA0F;YACxF,iEAAiE;YACjE,sDAAsD,CACzD,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO,EAAE,UAAU;QACnB,mFAAmF;QACnF,yDAAyD;QACzD,UAAU,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,aAAa,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC;QAC1G,GAAG,EAAE,EAAE,qBAAqB,EAAE,GAAG,EAAE;QACnC,WAAW,EAAE,6BAA6B;KAC3C,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ;IACtB,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;aAC/D,KAAK,CAAC,IAAI,CAAC;aACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;aAClC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,6FAA6F;QAC7F,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cyclops-clientdevbridge-cli",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "CLI that launches and drives a Minecraft dev client through the ClientDevBridge mod, for coding agents.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"minecraft",
|
|
7
|
+
"modding",
|
|
8
|
+
"cyclopsmc",
|
|
9
|
+
"agent",
|
|
10
|
+
"claude-code",
|
|
11
|
+
"testing"
|
|
12
|
+
],
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"author": "rubensworks (aka kroeser)",
|
|
15
|
+
"homepage": "https://github.com/CyclopsMC/clientdevbridge-cli",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/CyclopsMC/clientdevbridge-cli.git"
|
|
19
|
+
},
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/CyclopsMC/clientdevbridge-cli/issues"
|
|
22
|
+
},
|
|
23
|
+
"type": "module",
|
|
24
|
+
"bin": {
|
|
25
|
+
"clientdevbridge": "dist/cli.js"
|
|
26
|
+
},
|
|
27
|
+
"main": "dist/index.js",
|
|
28
|
+
"types": "dist/index.d.ts",
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"README.md",
|
|
32
|
+
"CHANGELOG.md",
|
|
33
|
+
"LICENSE"
|
|
34
|
+
],
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=20"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsc -p tsconfig.json",
|
|
40
|
+
"prepublishOnly": "npm run build",
|
|
41
|
+
"version": "manual-git-changelog onversion",
|
|
42
|
+
"test": "vitest run",
|
|
43
|
+
"test:watch": "vitest",
|
|
44
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
45
|
+
"lint": "eslint src test --ext .ts",
|
|
46
|
+
"lint:fix": "eslint src test --ext .ts --fix"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"commander": "^12.1.0",
|
|
50
|
+
"pixelmatch": "^6.0.0",
|
|
51
|
+
"pngjs": "^7.0.0",
|
|
52
|
+
"ws": "^8.18.0",
|
|
53
|
+
"zod": "^3.23.8"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/node": "^20.14.0",
|
|
57
|
+
"@types/pngjs": "^6.0.5",
|
|
58
|
+
"@types/ws": "^8.5.12",
|
|
59
|
+
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
|
60
|
+
"@typescript-eslint/parser": "^7.18.0",
|
|
61
|
+
"eslint": "^8.57.0",
|
|
62
|
+
"manual-git-changelog": "^1.0.2",
|
|
63
|
+
"typescript": "^5.5.4",
|
|
64
|
+
"vitest": "^2.0.5"
|
|
65
|
+
}
|
|
66
|
+
}
|