godot-cli 0.37.0 → 0.39.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/CHANGELOG.md +99 -0
- package/README.md +139 -16
- package/dist/cli.d.ts +7 -0
- package/dist/cli.js +7 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/check.d.ts +2 -0
- package/dist/commands/check.js +48 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/close.d.ts +3 -0
- package/dist/commands/close.js +82 -27
- package/dist/commands/close.js.map +1 -1
- package/dist/commands/commands.d.ts +3 -0
- package/dist/commands/commands.js +28 -0
- package/dist/commands/commands.js.map +1 -0
- package/dist/commands/exec.js +43 -28
- package/dist/commands/exec.js.map +1 -1
- package/dist/commands/export.d.ts +2 -0
- package/dist/commands/export.js +45 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/headless-common.d.ts +29 -0
- package/dist/commands/headless-common.js +69 -0
- package/dist/commands/headless-common.js.map +1 -0
- package/dist/commands/import.d.ts +2 -0
- package/dist/commands/import.js +32 -0
- package/dist/commands/import.js.map +1 -0
- package/dist/commands/open.js +38 -6
- package/dist/commands/open.js.map +1 -1
- package/dist/commands/rql.d.ts +2 -0
- package/dist/commands/rql.js +81 -0
- package/dist/commands/rql.js.map +1 -0
- package/dist/commands/run-script.d.ts +2 -0
- package/dist/commands/run-script.js +33 -0
- package/dist/commands/run-script.js.map +1 -0
- package/dist/commands/run-system-tool.js +0 -1
- package/dist/commands/run-system-tool.js.map +1 -1
- package/dist/commands/run-tool-builder.d.ts +38 -4
- package/dist/commands/run-tool-builder.js +179 -56
- package/dist/commands/run-tool-builder.js.map +1 -1
- package/dist/commands/run-tool.js +0 -1
- package/dist/commands/run-tool.js.map +1 -1
- package/dist/commands/setup-skills.js +3 -2
- package/dist/commands/setup-skills.js.map +1 -1
- package/dist/commands/status.js +25 -19
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/test.d.ts +5 -0
- package/dist/commands/test.js +111 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/commands/tools.d.ts +2 -0
- package/dist/commands/tools.js +120 -0
- package/dist/commands/tools.js.map +1 -0
- package/dist/commands/update.js +1 -1
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/wait-for-ready.js +18 -20
- package/dist/commands/wait-for-ready.js.map +1 -1
- package/dist/index.js +48 -3
- package/dist/index.js.map +1 -1
- package/dist/lib/build.d.ts +1 -1
- package/dist/lib/build.js +21 -4
- package/dist/lib/build.js.map +1 -1
- package/dist/lib/check.d.ts +44 -0
- package/dist/lib/check.js +191 -0
- package/dist/lib/check.js.map +1 -0
- package/dist/lib/cli-guide.d.ts +26 -0
- package/dist/lib/cli-guide.js +51 -0
- package/dist/lib/cli-guide.js.map +1 -0
- package/dist/lib/exec.d.ts +19 -0
- package/dist/lib/exec.js +61 -10
- package/dist/lib/exec.js.map +1 -1
- package/dist/lib/export-project.d.ts +41 -0
- package/dist/lib/export-project.js +159 -0
- package/dist/lib/export-project.js.map +1 -0
- package/dist/lib/godot-log.d.ts +38 -0
- package/dist/lib/godot-log.js +145 -0
- package/dist/lib/godot-log.js.map +1 -0
- package/dist/lib/godot-run.d.ts +34 -0
- package/dist/lib/godot-run.js +113 -0
- package/dist/lib/godot-run.js.map +1 -0
- package/dist/lib/headless.d.ts +82 -0
- package/dist/lib/headless.js +117 -0
- package/dist/lib/headless.js.map +1 -0
- package/dist/lib/import-project.d.ts +9 -0
- package/dist/lib/import-project.js +15 -0
- package/dist/lib/import-project.js.map +1 -0
- package/dist/lib/junit.d.ts +36 -0
- package/dist/lib/junit.js +146 -0
- package/dist/lib/junit.js.map +1 -0
- package/dist/lib/list-tools.d.ts +51 -0
- package/dist/lib/list-tools.js +147 -0
- package/dist/lib/list-tools.js.map +1 -0
- package/dist/lib/open.js +3 -1
- package/dist/lib/open.js.map +1 -1
- package/dist/lib/progress.d.ts +1 -7
- package/dist/lib/progress.js +2 -15
- package/dist/lib/progress.js.map +1 -1
- package/dist/lib/rql.d.ts +32 -0
- package/dist/lib/rql.js +105 -0
- package/dist/lib/rql.js.map +1 -0
- package/dist/lib/run-script.d.ts +23 -0
- package/dist/lib/run-script.js +43 -0
- package/dist/lib/run-script.js.map +1 -0
- package/dist/lib/run-tests.d.ts +50 -0
- package/dist/lib/run-tests.js +181 -0
- package/dist/lib/run-tests.js.map +1 -0
- package/dist/lib/run-tool.js +6 -22
- package/dist/lib/run-tool.js.map +1 -1
- package/dist/lib/session.d.ts +8 -1
- package/dist/lib/session.js +38 -5
- package/dist/lib/session.js.map +1 -1
- package/dist/lib/setup-skills.d.ts +8 -13
- package/dist/lib/setup-skills.js +27 -24
- package/dist/lib/setup-skills.js.map +1 -1
- package/dist/lib/types.d.ts +7 -0
- package/dist/lib/update-project.js +1 -1
- package/dist/lib/update-project.js.map +1 -1
- package/dist/lib.d.ts +16 -0
- package/dist/lib.js +10 -0
- package/dist/lib.js.map +1 -1
- package/dist/utils/addon-deps.js +2 -2
- package/dist/utils/agents.d.ts +2 -6
- package/dist/utils/agents.js.map +1 -1
- package/dist/utils/connection.d.ts +59 -0
- package/dist/utils/connection.js +11 -10
- package/dist/utils/connection.js.map +1 -1
- package/dist/utils/csproj-deps.js +1 -1
- package/dist/utils/csproj-deps.js.map +1 -1
- package/dist/utils/editor-connection.d.ts +74 -0
- package/dist/utils/editor-connection.js +225 -0
- package/dist/utils/editor-connection.js.map +1 -0
- package/dist/utils/editor-mcp-server.d.ts +32 -0
- package/dist/utils/editor-mcp-server.js +134 -0
- package/dist/utils/editor-mcp-server.js.map +1 -0
- package/dist/utils/exit-codes.d.ts +2 -0
- package/dist/utils/exit-codes.js +4 -0
- package/dist/utils/exit-codes.js.map +1 -0
- package/dist/utils/godot-process.d.ts +5 -0
- package/dist/utils/godot-process.js +16 -0
- package/dist/utils/godot-process.js.map +1 -1
- package/dist/utils/godot-shutdown.d.ts +3 -1
- package/dist/utils/godot-shutdown.js +4 -2
- package/dist/utils/godot-shutdown.js.map +1 -1
- package/dist/utils/input.d.ts +3 -3
- package/dist/utils/input.js +22 -35
- package/dist/utils/input.js.map +1 -1
- package/dist/utils/output.d.ts +45 -0
- package/dist/utils/output.js +101 -0
- package/dist/utils/output.js.map +1 -0
- package/dist/utils/server-source.d.ts +1 -1
- package/dist/utils/server-source.js +1 -1
- package/dist/utils/skill-guides.d.ts +6 -9
- package/dist/utils/skill-guides.js +100 -43
- package/dist/utils/skill-guides.js.map +1 -1
- package/dist/utils/skills.d.ts +1 -14
- package/dist/utils/skills.js +0 -89
- package/dist/utils/skills.js.map +1 -1
- package/dist/utils/ui.d.ts +6 -1
- package/dist/utils/ui.js +72 -58
- package/dist/utils/ui.js.map +1 -1
- package/dist/utils/update-check.d.ts +6 -17
- package/dist/utils/update-check.js +10 -77
- package/dist/utils/update-check.js.map +1 -1
- package/package.json +2 -2
- package/dist/utils/json-parse.d.ts +0 -25
- package/dist/utils/json-parse.js +0 -83
- package/dist/utils/json-parse.js.map +0 -1
- package/dist/utils/semver.d.ts +0 -4
- package/dist/utils/semver.js +0 -25
- package/dist/utils/semver.js.map +0 -1
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// `exportProject`: `godot --headless --export-release|--export-debug|--export-pack <preset> <output>`. Godot can exit 0
|
|
2
|
+
// without writing anything, so the export templates are checked first and the output file afterwards. Library-safe.
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import * as os from 'os';
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import { spawnSync } from 'child_process';
|
|
7
|
+
import { parseGodotLog } from './godot-log.js';
|
|
8
|
+
import { engineRan, headlessFailure, stoppedRun, notStarted, remainingMs, prepareHeadless, runGodotStep, } from './headless.js';
|
|
9
|
+
/** Log errors that mean the export itself failed (other engine errors, e.g. a plugin's runtime noise, do not). */
|
|
10
|
+
const EXPORT_ERROR_RE = /export|solution file|publish|template|cannot save|failed to (?:save|write|open|copy)/i;
|
|
11
|
+
/** The `[preset.N]` sections of export_presets.cfg. */
|
|
12
|
+
export function readExportPresets(projectPath) {
|
|
13
|
+
let text;
|
|
14
|
+
try {
|
|
15
|
+
text = fs.readFileSync(path.join(projectPath, 'export_presets.cfg'), 'utf-8');
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
const presets = [];
|
|
21
|
+
let current;
|
|
22
|
+
let inOptions = false;
|
|
23
|
+
for (const raw of text.split(/\r?\n/)) {
|
|
24
|
+
const line = raw.trim();
|
|
25
|
+
const section = /^\[preset\.(\d+)(\.options)?\]$/.exec(line);
|
|
26
|
+
if (section) {
|
|
27
|
+
inOptions = section[2] !== undefined;
|
|
28
|
+
if (!inOptions) {
|
|
29
|
+
current = { name: '', platform: '', customTemplateDebug: '', customTemplateRelease: '' };
|
|
30
|
+
presets.push(current);
|
|
31
|
+
}
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (line.startsWith('[')) {
|
|
35
|
+
inOptions = false;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const kv = /^([\w/]+)\s*=\s*"(.*)"$/.exec(line);
|
|
39
|
+
if (!kv || !current)
|
|
40
|
+
continue;
|
|
41
|
+
const [, key, value] = kv;
|
|
42
|
+
if (!inOptions && key === 'name')
|
|
43
|
+
current.name = value;
|
|
44
|
+
else if (!inOptions && key === 'platform')
|
|
45
|
+
current.platform = value;
|
|
46
|
+
else if (inOptions && key === 'custom_template/debug')
|
|
47
|
+
current.customTemplateDebug = value;
|
|
48
|
+
else if (inOptions && key === 'custom_template/release')
|
|
49
|
+
current.customTemplateRelease = value;
|
|
50
|
+
}
|
|
51
|
+
return presets;
|
|
52
|
+
}
|
|
53
|
+
/** Export templates folder name for a `--version` string: `4.5.1.stable.mono.official.f62fdbde1` → `4.5.1.stable.mono`. */
|
|
54
|
+
export function templatesVersionDir(version) {
|
|
55
|
+
const parts = version.trim().split('.');
|
|
56
|
+
const status = parts.findIndex((p) => /^(stable|dev\d*|alpha\d*|beta\d*|rc\d*)$/.test(p));
|
|
57
|
+
if (status < 0)
|
|
58
|
+
return version.trim();
|
|
59
|
+
return parts.slice(0, status + 1 + (parts[status + 1] === 'mono' ? 1 : 0)).join('.');
|
|
60
|
+
}
|
|
61
|
+
/** Godot's export templates root for this OS (or the self-contained `editor_data` next to the binary). */
|
|
62
|
+
export function templatesRoot(binary, platform = process.platform) {
|
|
63
|
+
const binDir = path.dirname(binary);
|
|
64
|
+
if (fs.existsSync(path.join(binDir, '._sc_')) || fs.existsSync(path.join(binDir, '_sc_'))) {
|
|
65
|
+
return path.join(binDir, 'editor_data', 'export_templates');
|
|
66
|
+
}
|
|
67
|
+
const home = os.homedir();
|
|
68
|
+
if (platform === 'win32')
|
|
69
|
+
return path.join(process.env.APPDATA ?? path.join(home, 'AppData', 'Roaming'), 'Godot', 'export_templates');
|
|
70
|
+
if (platform === 'darwin')
|
|
71
|
+
return path.join(home, 'Library', 'Application Support', 'Godot', 'export_templates');
|
|
72
|
+
return path.join(process.env.XDG_DATA_HOME ?? path.join(home, '.local', 'share'), 'godot', 'export_templates');
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The editor's version: `--version` (stdout; a GUI build may print nothing to a pipe), else the official file name
|
|
76
|
+
* (`Godot_v4.5.1-stable_mono_win64.exe` → `4.5.1.stable.mono`).
|
|
77
|
+
*/
|
|
78
|
+
export function godotVersion(binary, timeoutMs) {
|
|
79
|
+
const run = spawnSync(binary, ['--version'], { encoding: 'utf-8', timeout: Math.max(1, Math.min(30000, timeoutMs)), windowsHide: true });
|
|
80
|
+
const line = (run.stdout ?? '').split(/\r?\n/).map((l) => l.trim()).find((l) => /^\d+\.\d+/.test(l));
|
|
81
|
+
return line ?? versionFromFileName(path.basename(binary));
|
|
82
|
+
}
|
|
83
|
+
export function versionFromFileName(name) {
|
|
84
|
+
const m = /^godot_v(\d+\.\d+(?:\.\d+)?)-(stable|dev\d*|alpha\d*|beta\d*|rc\d*)(_mono)?/i.exec(name);
|
|
85
|
+
return m ? `${m[1]}.${m[2]}${m[3] ? '.mono' : ''}` : undefined;
|
|
86
|
+
}
|
|
87
|
+
export async function exportProject(options) {
|
|
88
|
+
const ctx = prepareHeadless(options);
|
|
89
|
+
if (ctx.kind === 'failure')
|
|
90
|
+
return ctx;
|
|
91
|
+
const mode = options.mode ?? 'release';
|
|
92
|
+
const output = path.resolve(options.output);
|
|
93
|
+
const report = { projectPath: ctx.projectPath, preset: options.preset, mode, output, steps: ctx.steps, diagnostics: [] };
|
|
94
|
+
const fail = (failure) => ({ ...failure, ...report });
|
|
95
|
+
const presets = readExportPresets(ctx.projectPath);
|
|
96
|
+
const preset = presets.find((p) => p.name === options.preset);
|
|
97
|
+
if (!preset) {
|
|
98
|
+
const known = presets.map((p) => `"${p.name}"`).join(', ');
|
|
99
|
+
return fail(headlessFailure('not-found', `No export preset named "${options.preset}" in export_presets.cfg${known ? ` (presets: ${known})` : ' (the file is missing or has no presets)'}.`));
|
|
100
|
+
}
|
|
101
|
+
// A .pck/.zip pack needs no templates; a preset with its own template file does not use the installed ones.
|
|
102
|
+
const custom = mode === 'debug' ? preset.customTemplateDebug : preset.customTemplateRelease;
|
|
103
|
+
if (mode !== 'pack' && custom === '') {
|
|
104
|
+
const version = options.godotVersion ?? godotVersion(ctx.binary, remainingMs(ctx));
|
|
105
|
+
if (!version)
|
|
106
|
+
return fail(headlessFailure('export-failed', `Could not read the Godot version (${ctx.binary} --version).`));
|
|
107
|
+
const versionDir = templatesVersionDir(version);
|
|
108
|
+
const dir = path.join(templatesRoot(ctx.binary), versionDir);
|
|
109
|
+
report.templatesDir = dir;
|
|
110
|
+
let installed = false;
|
|
111
|
+
try {
|
|
112
|
+
installed = fs.readdirSync(dir).length > 0;
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
installed = false;
|
|
116
|
+
}
|
|
117
|
+
if (!installed) {
|
|
118
|
+
return fail(headlessFailure('not-found', `Export templates for Godot ${versionDir} are not installed (expected in ${dir}). Install them from the editor (Editor > Manage Export Templates) or download the .tpz from godotengine.org.`));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
else if (custom !== '' && mode !== 'pack' && !fs.existsSync(custom)) {
|
|
122
|
+
return fail(headlessFailure('not-found', `The preset's custom ${mode} template does not exist: ${custom}`));
|
|
123
|
+
}
|
|
124
|
+
fs.mkdirSync(path.dirname(output), { recursive: true });
|
|
125
|
+
const startedAt = Date.now();
|
|
126
|
+
options.onProgress?.(`Exporting "${options.preset}" (${mode}) to ${output}...`);
|
|
127
|
+
const run = await runGodotStep(ctx, 'export', [`--export-${mode}`, options.preset, output]);
|
|
128
|
+
report.diagnostics = parseGodotLog(run.output).filter((d) => d.severity === 'error');
|
|
129
|
+
const stopped = stoppedRun(run, 'The export');
|
|
130
|
+
if (stopped)
|
|
131
|
+
return fail(stopped);
|
|
132
|
+
if (!engineRan(run))
|
|
133
|
+
return fail(notStarted('export-failed', run));
|
|
134
|
+
let stat;
|
|
135
|
+
try {
|
|
136
|
+
stat = fs.statSync(output);
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
stat = undefined;
|
|
140
|
+
}
|
|
141
|
+
// Allow for coarse file-system timestamps when deciding the file is this run's.
|
|
142
|
+
// A macOS export is an .app folder.
|
|
143
|
+
if (!stat || !(stat.isFile() || stat.isDirectory()) || (stat.isFile() && stat.size === 0) || stat.mtimeMs < startedAt - 2000) {
|
|
144
|
+
const detail = report.diagnostics[0]?.message;
|
|
145
|
+
return fail(headlessFailure('export-failed', `The export produced no output file at ${output} (exit ${run.exitCode})${detail ? `: ${detail}` : ''}.`));
|
|
146
|
+
}
|
|
147
|
+
if (stat.isFile())
|
|
148
|
+
report.sizeBytes = stat.size;
|
|
149
|
+
// Godot writes the executable and the .pck even when a step failed (e.g. the C# publish: no .sln) and exits 0.
|
|
150
|
+
const exportErrors = report.diagnostics.filter((d) => (d.source === 'gdscript' && d.phase === 'load') || EXPORT_ERROR_RE.test(d.message));
|
|
151
|
+
if (exportErrors.length > 0) {
|
|
152
|
+
report.diagnostics = exportErrors;
|
|
153
|
+
return fail(headlessFailure('export-failed', `The export logged ${exportErrors.length} error(s): ${exportErrors[0].message}`));
|
|
154
|
+
}
|
|
155
|
+
if (run.exitCode !== 0)
|
|
156
|
+
return fail(headlessFailure('export-failed', `Godot exited with code ${run.exitCode} during the export.`));
|
|
157
|
+
return { kind: 'success', success: true, ...report };
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=export-project.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export-project.js","sourceRoot":"","sources":["../../src/lib/export-project.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export type DiagnosticSeverity = 'error' | 'warning';
|
|
2
|
+
/** `gdscript`: GDScript parse/load errors; `csharp`: compiler diagnostics; `engine`: any other engine line. */
|
|
3
|
+
export type DiagnosticSource = 'gdscript' | 'csharp' | 'engine';
|
|
4
|
+
export interface Diagnostic {
|
|
5
|
+
severity: DiagnosticSeverity;
|
|
6
|
+
source: DiagnosticSource;
|
|
7
|
+
message: string;
|
|
8
|
+
/** `res://…` for Godot lines, the file path MSBuild printed for C#. */
|
|
9
|
+
file?: string;
|
|
10
|
+
line?: number;
|
|
11
|
+
column?: number;
|
|
12
|
+
/** Compiler code, e.g. `CS1002`. */
|
|
13
|
+
code?: string;
|
|
14
|
+
/** `load`: the script did not parse/compile/load; `runtime`: it loaded and then reported an error. */
|
|
15
|
+
phase?: 'load' | 'runtime';
|
|
16
|
+
}
|
|
17
|
+
/** The first line every Godot run prints: proof the engine started at all. */
|
|
18
|
+
export declare const GODOT_BANNER_RE: RegExp;
|
|
19
|
+
/** Printed by the Godot-MCP addon when its EditorPlugin entered the editor. */
|
|
20
|
+
export declare const ADDON_LOADED_MARKER = "[Godot-MCP] plugin loaded";
|
|
21
|
+
/** Marker prefix of the scripts godot-cli runs inside Godot (`check`). */
|
|
22
|
+
export declare const CLI_MARKER = "[godot-cli] ";
|
|
23
|
+
export declare function stripAnsi(text: string): string;
|
|
24
|
+
/**
|
|
25
|
+
* Diagnostics in a Godot log: `SCRIPT ERROR:` (GDScript parse/compile, with `res://file:line` from the following
|
|
26
|
+
* `at:` line; "Warning treated as error" rows become warnings), `ERROR:` / `WARNING:` lines. Engine errors that mean a
|
|
27
|
+
* script or addon failed to load are `gdscript` errors; other engine errors are `engine` and do not fail a run.
|
|
28
|
+
*/
|
|
29
|
+
export declare function parseGodotLog(text: string): Diagnostic[];
|
|
30
|
+
/**
|
|
31
|
+
* Diagnostics in `dotnet build` output: `file(line,col): error CS1002: … [proj]` and unlocated
|
|
32
|
+
* `MSBUILD : error MSB1009: …`. MSBuild repeats each one in its summary; duplicates are dropped.
|
|
33
|
+
*/
|
|
34
|
+
export declare function parseMsbuildOutput(text: string): Diagnostic[];
|
|
35
|
+
/** `file:line: error: message` (C#: `file(line,col): error CS0000: message`). */
|
|
36
|
+
export declare function formatDiagnostic(d: Diagnostic): string;
|
|
37
|
+
/** The lines of `text` that start with the godot-cli marker, without it. */
|
|
38
|
+
export declare function cliMarkerLines(text: string): string[];
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// Godot / MSBuild log parsing. Godot's headless exit code is not a verdict (a GDScript parse error during --import, or an
|
|
2
|
+
// addon that fails to load, still exits 0), so the headless commands judge a run from its log: error lines, markers
|
|
3
|
+
// the run must print, and the report files it writes. Pure functions, no I/O.
|
|
4
|
+
/** The first line every Godot run prints: proof the engine started at all. */
|
|
5
|
+
export const GODOT_BANNER_RE = /^Godot Engine v\d+\.\d+/m;
|
|
6
|
+
/** Printed by the Godot-MCP addon when its EditorPlugin entered the editor. */
|
|
7
|
+
export const ADDON_LOADED_MARKER = '[Godot-MCP] plugin loaded';
|
|
8
|
+
/** Marker prefix of the scripts godot-cli runs inside Godot (`check`). */
|
|
9
|
+
export const CLI_MARKER = '[godot-cli] ';
|
|
10
|
+
// eslint-disable-next-line no-control-regex
|
|
11
|
+
const ANSI_RE = /\x1B\[[0-9;]*[A-Za-z]/g;
|
|
12
|
+
export function stripAnsi(text) {
|
|
13
|
+
return text.replace(ANSI_RE, '');
|
|
14
|
+
}
|
|
15
|
+
const HEADER_RE = /^(USER SCRIPT ERROR|USER ERROR|USER WARNING|SCRIPT ERROR|SCRIPT WARNING|ERROR|WARNING): (.*)$/;
|
|
16
|
+
const AT_RE = /^\s+at: .*?\(((?:res|user):\/\/[^():]+|[A-Za-z]:[\\/][^():]+|\/[^():]+):(\d+)\)\s*$/;
|
|
17
|
+
const WARNING_AS_ERROR = '(Warning treated as error.)';
|
|
18
|
+
const FAILED_LOAD_RE = /Failed to load script "([^"]+)"/;
|
|
19
|
+
/** Engine errors that mean a script or an addon did not load (the rest are runtime noise). */
|
|
20
|
+
const LOAD_FAILURE_RE = /Failed to load script|Unable to load addon script|Cannot load (?:C#|GDScript)|Failed to instantiate|Could not find type|Unable to load \.NET|\.NET: Failed/i;
|
|
21
|
+
/**
|
|
22
|
+
* Diagnostics in a Godot log: `SCRIPT ERROR:` (GDScript parse/compile, with `res://file:line` from the following
|
|
23
|
+
* `at:` line; "Warning treated as error" rows become warnings), `ERROR:` / `WARNING:` lines. Engine errors that mean a
|
|
24
|
+
* script or addon failed to load are `gdscript` errors; other engine errors are `engine` and do not fail a run.
|
|
25
|
+
*/
|
|
26
|
+
export function parseGodotLog(text) {
|
|
27
|
+
const lines = stripAnsi(text).split(/\r?\n/);
|
|
28
|
+
const diagnostics = [];
|
|
29
|
+
// Files a SCRIPT ERROR row already reported (with its line): their "Failed to load script" follow-up is dropped.
|
|
30
|
+
const reportedFiles = new Set();
|
|
31
|
+
for (let i = 0; i < lines.length; i++) {
|
|
32
|
+
const header = HEADER_RE.exec(lines[i]);
|
|
33
|
+
if (!header)
|
|
34
|
+
continue;
|
|
35
|
+
const kind = header[1];
|
|
36
|
+
let message = header[2].trim();
|
|
37
|
+
let file;
|
|
38
|
+
let line;
|
|
39
|
+
const at = i + 1 < lines.length ? AT_RE.exec(lines[i + 1]) : null;
|
|
40
|
+
if (at) {
|
|
41
|
+
file = at[1];
|
|
42
|
+
line = Number(at[2]);
|
|
43
|
+
}
|
|
44
|
+
if (kind === 'SCRIPT ERROR' || kind === 'USER SCRIPT ERROR') {
|
|
45
|
+
const asWarning = message.endsWith(WARNING_AS_ERROR);
|
|
46
|
+
if (asWarning)
|
|
47
|
+
message = message.slice(0, -WARNING_AS_ERROR.length).trim();
|
|
48
|
+
const loadError = /^(Parse|Compile) Error: /.test(message);
|
|
49
|
+
message = message.replace(/^(Parse|Compile) Error: /, '');
|
|
50
|
+
if (file && loadError)
|
|
51
|
+
reportedFiles.add(file);
|
|
52
|
+
diagnostics.push({
|
|
53
|
+
severity: asWarning ? 'warning' : 'error',
|
|
54
|
+
source: 'gdscript',
|
|
55
|
+
message,
|
|
56
|
+
...(file ? { file } : {}),
|
|
57
|
+
...(line ? { line } : {}),
|
|
58
|
+
phase: loadError ? 'load' : 'runtime',
|
|
59
|
+
});
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (kind.endsWith('WARNING')) {
|
|
63
|
+
diagnostics.push({ severity: 'warning', source: isScriptFile(file) && file.endsWith('.gd') ? 'gdscript' : 'engine', message, ...(file ? { file } : {}), ...(line ? { line } : {}) });
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
// ERROR / USER ERROR
|
|
67
|
+
const failedLoad = FAILED_LOAD_RE.exec(message);
|
|
68
|
+
if (failedLoad) {
|
|
69
|
+
// Already reported with its line by the SCRIPT ERROR above it.
|
|
70
|
+
if (reportedFiles.has(failedLoad[1]))
|
|
71
|
+
continue;
|
|
72
|
+
diagnostics.push({ severity: 'error', source: 'gdscript', message, file: failedLoad[1], phase: 'load' });
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const loadFailure = LOAD_FAILURE_RE.test(message);
|
|
76
|
+
const scriptLevel = loadFailure || (file !== undefined && file.endsWith('.gd'));
|
|
77
|
+
diagnostics.push({
|
|
78
|
+
severity: 'error',
|
|
79
|
+
source: scriptLevel ? 'gdscript' : 'engine',
|
|
80
|
+
message,
|
|
81
|
+
...(file ? { file } : {}),
|
|
82
|
+
...(line ? { line } : {}),
|
|
83
|
+
...(scriptLevel ? { phase: loadFailure ? 'load' : 'runtime' } : {}),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return diagnostics;
|
|
87
|
+
}
|
|
88
|
+
function isScriptFile(file) {
|
|
89
|
+
return file !== undefined && /\.(gd|cs)$/i.test(file);
|
|
90
|
+
}
|
|
91
|
+
const MSBUILD_LOCATED_RE = /^\s*(.+?)\((\d+)(?:,(\d+))?(?:,\d+,\d+)?\)\s*:\s*(error|warning)\s+([A-Z]+\d+)\s*:\s*(.*?)(?:\s+\[[^\]]+\])?\s*$/;
|
|
92
|
+
const MSBUILD_UNLOCATED_RE = /^\s*(.+?)\s*:\s*(error|warning)\s+([A-Z]+\d+)\s*:\s*(.*?)(?:\s+\[[^\]]+\])?\s*$/;
|
|
93
|
+
/**
|
|
94
|
+
* Diagnostics in `dotnet build` output: `file(line,col): error CS1002: … [proj]` and unlocated
|
|
95
|
+
* `MSBUILD : error MSB1009: …`. MSBuild repeats each one in its summary; duplicates are dropped.
|
|
96
|
+
*/
|
|
97
|
+
export function parseMsbuildOutput(text) {
|
|
98
|
+
const seen = new Set();
|
|
99
|
+
const diagnostics = [];
|
|
100
|
+
for (const raw of stripAnsi(text).split(/\r?\n/)) {
|
|
101
|
+
let d;
|
|
102
|
+
const located = MSBUILD_LOCATED_RE.exec(raw);
|
|
103
|
+
if (located) {
|
|
104
|
+
d = {
|
|
105
|
+
severity: located[4],
|
|
106
|
+
source: 'csharp',
|
|
107
|
+
message: located[6],
|
|
108
|
+
file: located[1].trim(),
|
|
109
|
+
line: Number(located[2]),
|
|
110
|
+
...(located[3] ? { column: Number(located[3]) } : {}),
|
|
111
|
+
code: located[5],
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
const unlocated = MSBUILD_UNLOCATED_RE.exec(raw);
|
|
116
|
+
if (unlocated)
|
|
117
|
+
d = { severity: unlocated[2], source: 'csharp', message: unlocated[4], code: unlocated[3] };
|
|
118
|
+
}
|
|
119
|
+
if (!d)
|
|
120
|
+
continue;
|
|
121
|
+
const key = `${d.severity}|${d.file ?? ''}|${d.line ?? ''}|${d.column ?? ''}|${d.code}|${d.message}`;
|
|
122
|
+
if (seen.has(key))
|
|
123
|
+
continue;
|
|
124
|
+
seen.add(key);
|
|
125
|
+
diagnostics.push(d);
|
|
126
|
+
}
|
|
127
|
+
return diagnostics;
|
|
128
|
+
}
|
|
129
|
+
/** `file:line: error: message` (C#: `file(line,col): error CS0000: message`). */
|
|
130
|
+
export function formatDiagnostic(d) {
|
|
131
|
+
if (d.source === 'csharp' && d.file) {
|
|
132
|
+
const loc = d.line !== undefined ? `(${d.line}${d.column !== undefined ? `,${d.column}` : ''})` : '';
|
|
133
|
+
return `${d.file}${loc}: ${d.severity}${d.code ? ` ${d.code}` : ''}: ${d.message}`;
|
|
134
|
+
}
|
|
135
|
+
const loc = d.file ? `${d.file}${d.line !== undefined ? `:${d.line}` : ''}: ` : '';
|
|
136
|
+
return `${loc}${d.severity}${d.code ? ` ${d.code}` : ''}: ${d.message}`;
|
|
137
|
+
}
|
|
138
|
+
/** The lines of `text` that start with the godot-cli marker, without it. */
|
|
139
|
+
export function cliMarkerLines(text) {
|
|
140
|
+
return stripAnsi(text)
|
|
141
|
+
.split(/\r?\n/)
|
|
142
|
+
.filter((l) => l.startsWith(CLI_MARKER))
|
|
143
|
+
.map((l) => l.slice(CLI_MARKER.length).trim());
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=godot-log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"godot-log.js","sourceRoot":"","sources":["../../src/lib/godot-log.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
export interface GodotRunOptions {
|
|
3
|
+
binary: string;
|
|
4
|
+
args: string[];
|
|
5
|
+
cwd: string;
|
|
6
|
+
timeoutMs: number;
|
|
7
|
+
/** Extra environment (on top of `process.env` and the .NET workaround every editor launch gets). */
|
|
8
|
+
env?: Record<string, string>;
|
|
9
|
+
/**
|
|
10
|
+
* Also pass `--log-file <logFile>`; its content is used when the process printed no Godot banner to stdout
|
|
11
|
+
* (a GUI-subsystem binary on Windows may print nothing to a pipe).
|
|
12
|
+
*/
|
|
13
|
+
logFile?: string;
|
|
14
|
+
/** Kill the tree and report `interrupted` on SIGINT / SIGTERM (CLI use; a library host keeps its own handling). */
|
|
15
|
+
killOnSignal?: boolean;
|
|
16
|
+
/** Each output line as it arrives (ANSI stripped). */
|
|
17
|
+
onLine?: (line: string) => void;
|
|
18
|
+
/** Tests. */
|
|
19
|
+
spawnImpl?: typeof spawn;
|
|
20
|
+
killTreeImpl?: (pid: number) => void | Promise<unknown>;
|
|
21
|
+
}
|
|
22
|
+
export interface GodotRunResult {
|
|
23
|
+
/** Process exit code; null when killed or it could not start. */
|
|
24
|
+
exitCode: number | null;
|
|
25
|
+
timedOut: boolean;
|
|
26
|
+
/** Combined stdout + stderr (or the log file), ANSI stripped. */
|
|
27
|
+
output: string;
|
|
28
|
+
durationMs: number;
|
|
29
|
+
/** The process could not be started. */
|
|
30
|
+
spawnError?: string;
|
|
31
|
+
/** This CLI got SIGINT / SIGTERM while the process ran; its tree was killed. */
|
|
32
|
+
interrupted?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export declare function runGodot(options: GodotRunOptions): Promise<GodotRunResult>;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// One headless Godot process with a hard timeout: output captured, the whole process tree killed on timeout (test
|
|
2
|
+
// runners and imports are known to hang). Library-safe: no console output, no `process.exit`, never throws.
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import { spawn } from 'child_process';
|
|
6
|
+
import { killProcessTree } from '@baizor/gamedev-cli-core';
|
|
7
|
+
import { editorLaunchEnv } from '../utils/godot-editor.js';
|
|
8
|
+
import { GODOT_BANNER_RE, stripAnsi } from './godot-log.js';
|
|
9
|
+
export function runGodot(options) {
|
|
10
|
+
const started = Date.now();
|
|
11
|
+
// Before the args: anything after a `--` belongs to the script (OS.get_cmdline_user_args()).
|
|
12
|
+
const args = options.logFile ? ['--log-file', options.logFile, ...options.args] : options.args;
|
|
13
|
+
if (options.logFile) {
|
|
14
|
+
try {
|
|
15
|
+
fs.mkdirSync(path.dirname(options.logFile), { recursive: true });
|
|
16
|
+
fs.rmSync(options.logFile, { force: true });
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// the log file is only a fallback
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return new Promise((resolve) => {
|
|
23
|
+
let output = '';
|
|
24
|
+
let pending = '';
|
|
25
|
+
let settled = false;
|
|
26
|
+
let timedOut = false;
|
|
27
|
+
let interrupted = false;
|
|
28
|
+
let detachSignals = () => undefined;
|
|
29
|
+
const finish = (exitCode, spawnError) => {
|
|
30
|
+
if (settled)
|
|
31
|
+
return;
|
|
32
|
+
settled = true;
|
|
33
|
+
clearTimeout(timer);
|
|
34
|
+
detachSignals();
|
|
35
|
+
if (pending)
|
|
36
|
+
options.onLine?.(stripAnsi(pending));
|
|
37
|
+
let text = stripAnsi(output);
|
|
38
|
+
if (!GODOT_BANNER_RE.test(text) && options.logFile) {
|
|
39
|
+
try {
|
|
40
|
+
const fromFile = stripAnsi(fs.readFileSync(options.logFile, 'utf-8'));
|
|
41
|
+
if (fromFile.trim().length > 0)
|
|
42
|
+
text = fromFile;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// no log file either
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
resolve({ exitCode, timedOut, output: text, durationMs: Date.now() - started, ...(spawnError ? { spawnError } : {}), ...(interrupted ? { interrupted } : {}) });
|
|
49
|
+
};
|
|
50
|
+
let child;
|
|
51
|
+
try {
|
|
52
|
+
child = (options.spawnImpl ?? spawn)(options.binary, args, {
|
|
53
|
+
cwd: options.cwd,
|
|
54
|
+
env: editorLaunchEnv(options.env),
|
|
55
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
56
|
+
windowsHide: true,
|
|
57
|
+
// POSIX: own process group so a timeout kills the whole tree.
|
|
58
|
+
detached: process.platform !== 'win32',
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
resolve({ exitCode: null, timedOut: false, output: '', durationMs: Date.now() - started, spawnError: err instanceof Error ? err.message : String(err) });
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const killTree = () => {
|
|
66
|
+
const killed = child.pid !== undefined ? (options.killTreeImpl ?? killProcessTree)(child.pid) : undefined;
|
|
67
|
+
// A killed tree closes its pipes; resolve anyway should a grandchild keep them open — counted from when the
|
|
68
|
+
// kill is done, so the caller never exits while taskkill still runs.
|
|
69
|
+
void Promise.resolve(killed)
|
|
70
|
+
.catch(() => undefined)
|
|
71
|
+
.then(() => setTimeout(() => finish(null), 5000).unref());
|
|
72
|
+
};
|
|
73
|
+
const timer = setTimeout(() => {
|
|
74
|
+
timedOut = true;
|
|
75
|
+
killTree();
|
|
76
|
+
}, options.timeoutMs);
|
|
77
|
+
// Ctrl+C / SIGTERM: the POSIX child runs in its own process group and never sees the signal, and on Windows
|
|
78
|
+
// Godot's children survive it; kill the tree and let the caller report the interruption.
|
|
79
|
+
const onSignal = () => {
|
|
80
|
+
interrupted = true;
|
|
81
|
+
killTree();
|
|
82
|
+
};
|
|
83
|
+
if (options.killOnSignal) {
|
|
84
|
+
process.once('SIGINT', onSignal);
|
|
85
|
+
process.once('SIGTERM', onSignal);
|
|
86
|
+
detachSignals = () => {
|
|
87
|
+
process.removeListener('SIGINT', onSignal);
|
|
88
|
+
process.removeListener('SIGTERM', onSignal);
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
const onData = (chunk) => {
|
|
92
|
+
const text = chunk.toString();
|
|
93
|
+
output += text;
|
|
94
|
+
if (!options.onLine)
|
|
95
|
+
return;
|
|
96
|
+
pending += text;
|
|
97
|
+
const parts = pending.split(/\r?\n/);
|
|
98
|
+
pending = parts.pop() ?? '';
|
|
99
|
+
for (const line of parts)
|
|
100
|
+
options.onLine(stripAnsi(line));
|
|
101
|
+
};
|
|
102
|
+
child.stdout?.on('data', onData);
|
|
103
|
+
child.stderr?.on('data', onData);
|
|
104
|
+
child.on('error', (err) => finish(null, err.message));
|
|
105
|
+
child.on('close', (code) => finish(timedOut || interrupted ? null : code));
|
|
106
|
+
// A grandchild that inherited the pipes (an MSBuild node, the C# compiler server) can keep them open long after
|
|
107
|
+
// Godot exited: give the output 2 s to drain, then finish on the exit itself.
|
|
108
|
+
child.on('exit', (code) => {
|
|
109
|
+
setTimeout(() => finish(timedOut || interrupted ? null : code), 2000).unref();
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=godot-run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"godot-run.js","sourceRoot":"","sources":["../../src/lib/godot-run.ts"],"names":[],"mappings":"AAAA,kHAAkH;AAClH,4GAA4G;AAE5G,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAoC5D,MAAM,UAAU,QAAQ,CAAC,OAAwB;IAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC3B,6FAA6F;IAC7F,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,YAAY,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC;IAC/F,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,IAAI,CAAC;YACH,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACjE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,CAAC;QAAC,MAAM,CAAC;YACP,kCAAkC;QACpC,CAAC;IACH,CAAC;IAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,OAAO,GAAG,EAAE,CAAC;QACjB,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,IAAI,WAAW,GAAG,KAAK,CAAC;QACxB,IAAI,aAAa,GAAG,GAAS,EAAE,CAAC,SAAS,CAAC;QAC1C,MAAM,MAAM,GAAG,CAAC,QAAuB,EAAE,UAAmB,EAAQ,EAAE;YACpE,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,aAAa,EAAE,CAAC;YAChB,IAAI,OAAO;gBAAE,OAAO,CAAC,MAAM,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;YAClD,IAAI,IAAI,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;YAC7B,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;gBACnD,IAAI,CAAC;oBACH,MAAM,QAAQ,GAAG,SAAS,CAAC,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;oBACtE,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;wBAAE,IAAI,GAAG,QAAQ,CAAC;gBAClD,CAAC;gBAAC,MAAM,CAAC;oBACP,qBAAqB;gBACvB,CAAC;YACH,CAAC;YACD,OAAO,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAClK,CAAC,CAAC;QAEF,IAAI,KAA+B,CAAC;QACpC,IAAI,CAAC;YACH,KAAK,GAAG,CAAC,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE;gBACzD,GAAG,EAAE,OAAO,CAAC,GAAG;gBAChB,GAAG,EAAE,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC;gBACjC,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;gBACjC,WAAW,EAAE,IAAI;gBACjB,8DAA8D;gBAC9D,QAAQ,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO;aACvC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACzJ,OAAO;QACT,CAAC;QAED,MAAM,QAAQ,GAAG,GAAS,EAAE;YAC1B,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,IAAI,eAAe,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAC1G,4GAA4G;YAC5G,qEAAqE;YACrE,KAAK,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC;iBACzB,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC;iBACtB,IAAI,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAK,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;QAC/D,CAAC,CAAC;QACF,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,QAAQ,GAAG,IAAI,CAAC;YAChB,QAAQ,EAAE,CAAC;QACb,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;QACtB,4GAA4G;QAC5G,yFAAyF;QACzF,MAAM,QAAQ,GAAG,GAAS,EAAE;YAC1B,WAAW,GAAG,IAAI,CAAC;YACnB,QAAQ,EAAE,CAAC;QACb,CAAC,CAAC;QACF,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;YACzB,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YACjC,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;YAClC,aAAa,GAAG,GAAG,EAAE;gBACnB,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;gBAC3C,OAAO,CAAC,cAAc,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;YAC9C,CAAC,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,CAAC,KAAsB,EAAQ,EAAE;YAC9C,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;YAC9B,MAAM,IAAI,IAAI,CAAC;YACf,IAAI,CAAC,OAAO,CAAC,MAAM;gBAAE,OAAO;YAC5B,OAAO,IAAI,IAAI,CAAC;YAChB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACrC,OAAO,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;YAC5B,KAAK,MAAM,IAAI,IAAI,KAAK;gBAAE,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;QAC5D,CAAC,CAAC;QACF,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACjC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACjC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;QACtD,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,IAAI,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3E,gHAAgH;QAChH,8EAA8E;QAC9E,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YACxB,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,IAAI,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,IAAK,CAAC,CAAC,KAAK,EAAE,CAAC;QACjF,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { type Diagnostic } from './godot-log.js';
|
|
2
|
+
import { type GodotRunOptions, type GodotRunResult } from './godot-run.js';
|
|
3
|
+
export type HeadlessFailureReason = 'invalid-options' | 'not-found' | 'project-open' | 'timeout' | 'script-errors' | 'compilation-failed' | 'import-failed' | 'export-failed' | 'tests-failed' | 'test-run-error' | 'interrupted';
|
|
4
|
+
/** One Godot / dotnet process a headless command ran. */
|
|
5
|
+
export interface HeadlessStep {
|
|
6
|
+
name: string;
|
|
7
|
+
command: string[];
|
|
8
|
+
exitCode: number | null;
|
|
9
|
+
timedOut: boolean;
|
|
10
|
+
durationMs: number;
|
|
11
|
+
}
|
|
12
|
+
export interface HeadlessBaseOptions {
|
|
13
|
+
projectPath: string;
|
|
14
|
+
/** Explicit Godot executable (else GODOT_BIN / PATH / common install locations). */
|
|
15
|
+
editorPath?: string;
|
|
16
|
+
/** Total budget for the whole command, in ms. */
|
|
17
|
+
timeoutMs: number;
|
|
18
|
+
/** Run even when an editor has the project open. */
|
|
19
|
+
allowOpenProject?: boolean;
|
|
20
|
+
onProgress?: (message: string) => void;
|
|
21
|
+
/** Tests. */
|
|
22
|
+
deps?: Partial<HeadlessDeps>;
|
|
23
|
+
}
|
|
24
|
+
export interface HeadlessDeps {
|
|
25
|
+
runGodot: (options: GodotRunOptions) => Promise<GodotRunResult>;
|
|
26
|
+
findBinary: (editorPath?: string) => string | null;
|
|
27
|
+
findOpenEditor: (projectPath: string) => {
|
|
28
|
+
pid: number;
|
|
29
|
+
} | null;
|
|
30
|
+
}
|
|
31
|
+
export declare const defaultHeadlessDeps: HeadlessDeps;
|
|
32
|
+
export interface HeadlessFailure {
|
|
33
|
+
kind: 'failure';
|
|
34
|
+
success: false;
|
|
35
|
+
reason: HeadlessFailureReason;
|
|
36
|
+
message: string;
|
|
37
|
+
}
|
|
38
|
+
export declare function headlessFailure(reason: HeadlessFailureReason, message: string): HeadlessFailure;
|
|
39
|
+
/** A headless command's result: its report on success, as much of it as exists on failure. */
|
|
40
|
+
export type HeadlessResult<R> = ({
|
|
41
|
+
kind: 'success';
|
|
42
|
+
success: true;
|
|
43
|
+
} & R) | (HeadlessFailure & Partial<R>);
|
|
44
|
+
/** The failure of a run that was stopped (SIGINT/SIGTERM, or the timeout), else undefined. */
|
|
45
|
+
export declare function stoppedRun(run: GodotRunResult, what: string): HeadlessFailure | undefined;
|
|
46
|
+
/** The failure of a run that never printed Godot's banner. */
|
|
47
|
+
export declare function notStarted(reason: HeadlessFailureReason, run: GodotRunResult): HeadlessFailure;
|
|
48
|
+
export interface HeadlessContext {
|
|
49
|
+
kind: 'context';
|
|
50
|
+
projectPath: string;
|
|
51
|
+
binary: string;
|
|
52
|
+
deps: HeadlessDeps;
|
|
53
|
+
/** `<project>/.godot/godot-cli`: scratch scripts and logs (ignored by Godot's file system and by VCS). */
|
|
54
|
+
workDir: string;
|
|
55
|
+
deadline: number;
|
|
56
|
+
steps: HeadlessStep[];
|
|
57
|
+
}
|
|
58
|
+
/** The scratch folder of the headless commands inside a project. */
|
|
59
|
+
export declare function cliWorkDir(projectPath: string): string;
|
|
60
|
+
/**
|
|
61
|
+
* Validate the project, find the binary and refuse when an editor has the project open (an `--import` against an
|
|
62
|
+
* open project fights the editor over `.godot/`).
|
|
63
|
+
*/
|
|
64
|
+
export declare function prepareHeadless(options: HeadlessBaseOptions): HeadlessContext | HeadlessFailure;
|
|
65
|
+
export declare function remainingMs(ctx: HeadlessContext): number;
|
|
66
|
+
/** Run one headless Godot step in the project within the remaining budget and record it. */
|
|
67
|
+
export declare function runGodotStep(ctx: HeadlessContext, name: string, args: string[], extra?: Pick<GodotRunOptions, 'env' | 'onLine'>): Promise<GodotRunResult>;
|
|
68
|
+
/** Whether the run printed Godot's start banner: without it nothing ran and the log proves nothing. */
|
|
69
|
+
export declare function engineRan(result: GodotRunResult): boolean;
|
|
70
|
+
export interface ImportStepResult {
|
|
71
|
+
result: GodotRunResult;
|
|
72
|
+
diagnostics: Diagnostic[];
|
|
73
|
+
/** Set when the step decides the command's verdict. */
|
|
74
|
+
failure?: HeadlessFailure;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* `--import`: materialise `.godot/` (imported resources, the global class cache every later step needs). Fails on a
|
|
78
|
+
* timeout, an engine that never started, script errors or `Error importing` lines; the exit code is not trusted.
|
|
79
|
+
*/
|
|
80
|
+
export declare function runImportStep(ctx: HeadlessContext, onLine?: (line: string) => void): Promise<ImportStepResult>;
|
|
81
|
+
/** Write a GDScript the CLI runs with `-s` into the work dir; returns its `res://` path. */
|
|
82
|
+
export declare function writeCliScript(ctx: HeadlessContext, name: string, source: string): string;
|