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
package/dist/lib/progress.js
CHANGED
|
@@ -1,18 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* swallowing any exception the callback throws. A broken onProgress handler
|
|
4
|
-
* must never abort the underlying library operation.
|
|
5
|
-
*/
|
|
6
|
-
export function emitProgress(onProgress, event) {
|
|
7
|
-
if (!onProgress)
|
|
8
|
-
return;
|
|
9
|
-
try {
|
|
10
|
-
onProgress(event);
|
|
11
|
-
}
|
|
12
|
-
catch {
|
|
13
|
-
// Intentionally ignored — see doc comment.
|
|
14
|
-
}
|
|
15
|
-
}
|
|
1
|
+
// Forward a progress event to the caller's optional callback, swallowing anything it throws: cli-core's.
|
|
2
|
+
export { emitProgress } from '@baizor/gamedev-cli-core';
|
|
16
3
|
/** ` (PID: n)` when the pid is known, else nothing: a log line never says "PID: unknown". */
|
|
17
4
|
export function pidSuffix(pid) {
|
|
18
5
|
return pid !== undefined ? ` (PID: ${pid})` : '';
|
package/dist/lib/progress.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"progress.js","sourceRoot":"","sources":["../../src/lib/progress.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"progress.js","sourceRoot":"","sources":["../../src/lib/progress.ts"],"names":[],"mappings":"AAAA,yGAAyG;AACzG,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAExD,6FAA6F;AAC7F,MAAM,UAAU,SAAS,CAAC,GAAuB;IAC/C,OAAO,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;AACnD,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { RQL_MUTATE_TOOL, RQL_QUERY_TOOL, RQL_SCHEMA_TOOL, decodeQueryFile, isRqlMutation, rqlStages, stripToolName } from '@baizor/gamedev-cli-core';
|
|
2
|
+
export { RQL_MUTATE_TOOL, RQL_QUERY_TOOL, RQL_SCHEMA_TOOL, decodeQueryFile, isRqlMutation, rqlStages, stripToolName };
|
|
3
|
+
export interface RqlCallOptions {
|
|
4
|
+
/** `--schema [type]`: true without a type. */
|
|
5
|
+
schema?: string | boolean;
|
|
6
|
+
dryRun?: boolean;
|
|
7
|
+
maxTargets?: number;
|
|
8
|
+
}
|
|
9
|
+
/** The rql-* tool and its input for a command-line query, or why the command line is wrong. */
|
|
10
|
+
export declare function rqlToolCall(query: string | undefined, options: RqlCallOptions): {
|
|
11
|
+
toolName: string;
|
|
12
|
+
input: Record<string, unknown>;
|
|
13
|
+
} | {
|
|
14
|
+
usage: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* A hint for a failed RQL tool (`toolName` rql-*) whose error is a familiar slip (an operator spelled as in another
|
|
18
|
+
* language), else `undefined`.
|
|
19
|
+
*/
|
|
20
|
+
export declare function rqlErrorHint(toolName: string, message: string): string | undefined;
|
|
21
|
+
/** A query that saves files: a scene save, a new scene (saved at its path), a resource edit or creation. */
|
|
22
|
+
export declare function rqlMaySave(query: string): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* The scene / resource files of the project (dot folders such as `.godot` and `addons/` left out) modified at or after
|
|
25
|
+
* `sinceMs`, as res:// paths, sorted — what a save call actually wrote.
|
|
26
|
+
*/
|
|
27
|
+
export declare function resourcesWrittenSince(projectDir: string, sinceMs: number): string[];
|
|
28
|
+
/**
|
|
29
|
+
* The line a change prints: the files it wrote (the first `limit`, then how many more). When it wrote none: that, for a
|
|
30
|
+
* save (`maySave`); nothing for any other change (scene edits stay unsaved until SaveScene()).
|
|
31
|
+
*/
|
|
32
|
+
export declare function saveReceipt(written: readonly string[], maySave: boolean, limit?: number): string | undefined;
|
package/dist/lib/rql.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// `rql` — RQL text straight from the command line: which rql-* tool a query goes to, hints for the RQL slips agents
|
|
2
|
+
// make most, and which files a save wrote. Never prints, never exits.
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import { RQL_MUTATE_TOOL, RQL_QUERY_TOOL, RQL_SCHEMA_TOOL, decodeQueryFile, isRqlMutation, rqlStages, stripToolName } from '@baizor/gamedev-cli-core';
|
|
6
|
+
export { RQL_MUTATE_TOOL, RQL_QUERY_TOOL, RQL_SCHEMA_TOOL, decodeQueryFile, isRqlMutation, rqlStages, stripToolName };
|
|
7
|
+
/** The rql-* tool and its input for a command-line query, or why the command line is wrong. */
|
|
8
|
+
export function rqlToolCall(query, options) {
|
|
9
|
+
const { schema, dryRun, maxTargets } = options;
|
|
10
|
+
if (schema !== undefined) {
|
|
11
|
+
// `rql --schema 'nodes | count'` hands the query to --schema as its value.
|
|
12
|
+
if (query !== undefined || (typeof schema === 'string' && /[|\s]/.test(schema.trim()))) {
|
|
13
|
+
return { usage: 'Pass either a query or --schema [type] (a type name), not both.' };
|
|
14
|
+
}
|
|
15
|
+
return { toolName: RQL_SCHEMA_TOOL, input: typeof schema === 'string' ? { type: schema } : {} };
|
|
16
|
+
}
|
|
17
|
+
if (query === undefined || query.trim() === '') {
|
|
18
|
+
return { usage: 'No query given: godot-cli rql \'nodes | where name starts-with "Tree" | count\' (recipes: rql --help)' };
|
|
19
|
+
}
|
|
20
|
+
if (!isRqlMutation(query)) {
|
|
21
|
+
if (dryRun || maxTargets !== undefined)
|
|
22
|
+
return { usage: '--dry-run and --max-targets are for a query ending in set / call.' };
|
|
23
|
+
return { toolName: RQL_QUERY_TOOL, input: { query } };
|
|
24
|
+
}
|
|
25
|
+
return { toolName: RQL_MUTATE_TOOL, input: { query, ...(dryRun ? { dry_run: true } : {}), ...(maxTargets !== undefined ? { max_targets: maxTargets } : {}) } };
|
|
26
|
+
}
|
|
27
|
+
/** Operators agents bring from other languages, with RQL's spelling. */
|
|
28
|
+
const OPERATOR_HINTS = [
|
|
29
|
+
[/^startswith$/i, 'RQL spells it starts-with: where name starts-with "Tree"'],
|
|
30
|
+
[/^endswith$/i, 'RQL spells it ends-with: where name ends-with "_lod0"'],
|
|
31
|
+
[/^(like|ilike)$/i, 'RQL has no like: use starts-with / ends-with / contains, or matches "regex"'],
|
|
32
|
+
[/^(includes|has)$/i, 'RQL spells it contains: where name contains "Tree"'],
|
|
33
|
+
[/^(isnull|is_null)$/i, 'RQL spells it x is null / x is not null'],
|
|
34
|
+
[/^(notin|not_in)$/i, 'RQL spells it not (x in [a, b])'],
|
|
35
|
+
[/^(regex|regexp|match|rlike)$/i, 'RQL spells it matches "regex": where name matches "^Tree[0-9]+$"'],
|
|
36
|
+
];
|
|
37
|
+
/**
|
|
38
|
+
* A hint for a failed RQL tool (`toolName` rql-*) whose error is a familiar slip (an operator spelled as in another
|
|
39
|
+
* language), else `undefined`.
|
|
40
|
+
*/
|
|
41
|
+
export function rqlErrorHint(toolName, message) {
|
|
42
|
+
if (!toolName.startsWith('rql-'))
|
|
43
|
+
return undefined;
|
|
44
|
+
const token = /unexpected '([^']+)'|unknown method '([^']+)'/.exec(message);
|
|
45
|
+
const word = token?.[1] ?? token?.[2];
|
|
46
|
+
if (word) {
|
|
47
|
+
for (const [pattern, hint] of OPERATOR_HINTS)
|
|
48
|
+
if (pattern.test(word))
|
|
49
|
+
return hint;
|
|
50
|
+
}
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
/** A query that saves files: a scene save, a new scene (saved at its path), a resource edit or creation. */
|
|
54
|
+
export function rqlMaySave(query) {
|
|
55
|
+
// Outside string literals and `identifiers`: where name == "SaveScene(" saves nothing.
|
|
56
|
+
const code = query.replace(/"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|`[^`]*`/g, '""');
|
|
57
|
+
return /\b(SaveScenes?|SaveAllScenes|NewScene|CreateResource|Save)\s*\(|godot\.resources?\(/.test(code);
|
|
58
|
+
}
|
|
59
|
+
/** The Godot files a save writes: scenes and resources. */
|
|
60
|
+
const SAVED_FILE = /\.(tscn|scn|tres|res)$/i;
|
|
61
|
+
/**
|
|
62
|
+
* The scene / resource files of the project (dot folders such as `.godot` and `addons/` left out) modified at or after
|
|
63
|
+
* `sinceMs`, as res:// paths, sorted — what a save call actually wrote.
|
|
64
|
+
*/
|
|
65
|
+
export function resourcesWrittenSince(projectDir, sinceMs) {
|
|
66
|
+
const found = [];
|
|
67
|
+
const walk = (dir) => {
|
|
68
|
+
let entries;
|
|
69
|
+
try {
|
|
70
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
for (const entry of entries) {
|
|
76
|
+
const full = path.join(dir, entry.name);
|
|
77
|
+
if (entry.isDirectory()) {
|
|
78
|
+
if (!entry.name.startsWith('.') && !(dir === projectDir && entry.name === 'addons'))
|
|
79
|
+
walk(full);
|
|
80
|
+
}
|
|
81
|
+
else if (SAVED_FILE.test(entry.name)) {
|
|
82
|
+
try {
|
|
83
|
+
if (fs.statSync(full).mtimeMs >= sinceMs)
|
|
84
|
+
found.push(`res://${path.relative(projectDir, full).split(path.sep).join('/')}`);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
/* removed meanwhile */
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
walk(projectDir);
|
|
93
|
+
return found.sort();
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* The line a change prints: the files it wrote (the first `limit`, then how many more). When it wrote none: that, for a
|
|
97
|
+
* save (`maySave`); nothing for any other change (scene edits stay unsaved until SaveScene()).
|
|
98
|
+
*/
|
|
99
|
+
export function saveReceipt(written, maySave, limit = 20) {
|
|
100
|
+
if (written.length === 0)
|
|
101
|
+
return maySave ? 'No scene or resource file of the project changed on disk.' : undefined;
|
|
102
|
+
const more = written.length > limit ? `, +${written.length - limit} more` : '';
|
|
103
|
+
return `Saved to disk: ${written.slice(0, limit).join(', ')}${more}`;
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=rql.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rql.js","sourceRoot":"","sources":["../../src/lib/rql.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type Diagnostic } from './godot-log.js';
|
|
2
|
+
import { type HeadlessBaseOptions, type HeadlessResult, type HeadlessStep } from './headless.js';
|
|
3
|
+
export interface RunScriptOptions extends HeadlessBaseOptions {
|
|
4
|
+
/** `res://…` or a file path (absolute or relative to the current directory). */
|
|
5
|
+
script: string;
|
|
6
|
+
/** Passed after `--` (`OS.get_cmdline_user_args()`). */
|
|
7
|
+
scriptArgs?: string[];
|
|
8
|
+
/** Each output line as it arrives. */
|
|
9
|
+
onLine?: (line: string) => void;
|
|
10
|
+
}
|
|
11
|
+
export interface RunScriptReport {
|
|
12
|
+
projectPath: string;
|
|
13
|
+
script: string;
|
|
14
|
+
/** The code the script passed to `quit()`. */
|
|
15
|
+
scriptExitCode: number | null;
|
|
16
|
+
output: string;
|
|
17
|
+
steps: HeadlessStep[];
|
|
18
|
+
diagnostics: Diagnostic[];
|
|
19
|
+
}
|
|
20
|
+
export type RunScriptResult = HeadlessResult<RunScriptReport>;
|
|
21
|
+
/** `res://` path of a script inside the project, the absolute path otherwise (Godot loads both). */
|
|
22
|
+
export declare function scriptPathForGodot(projectPath: string, script: string, cwd?: string): string;
|
|
23
|
+
export declare function runScript(options: RunScriptOptions): Promise<RunScriptResult>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// `runScript`: a one-shot SceneTree / MainLoop GDScript (`godot --headless --path <project> -s <file>`) with a hard
|
|
2
|
+
// timeout. The verdict is the exit code the script passed to `quit(code)`, checked against the log: a script that
|
|
3
|
+
// failed to parse makes Godot exit 0 without running anything. Library-safe.
|
|
4
|
+
import * as fs from 'fs';
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import { parseGodotLog } from './godot-log.js';
|
|
7
|
+
import { engineRan, headlessFailure, stoppedRun, notStarted, prepareHeadless, runGodotStep, } from './headless.js';
|
|
8
|
+
/** `res://` path of a script inside the project, the absolute path otherwise (Godot loads both). */
|
|
9
|
+
export function scriptPathForGodot(projectPath, script, cwd = process.cwd()) {
|
|
10
|
+
if (script.startsWith('res://'))
|
|
11
|
+
return script;
|
|
12
|
+
const abs = path.resolve(cwd, script);
|
|
13
|
+
const rel = path.relative(projectPath, abs);
|
|
14
|
+
if (!rel.startsWith('..') && !path.isAbsolute(rel))
|
|
15
|
+
return `res://${rel.split(path.sep).join('/')}`;
|
|
16
|
+
return abs.split(path.sep).join('/');
|
|
17
|
+
}
|
|
18
|
+
export async function runScript(options) {
|
|
19
|
+
const ctx = prepareHeadless(options);
|
|
20
|
+
if (ctx.kind === 'failure')
|
|
21
|
+
return ctx;
|
|
22
|
+
const script = scriptPathForGodot(ctx.projectPath, options.script);
|
|
23
|
+
const onDisk = script.startsWith('res://') ? path.join(ctx.projectPath, script.slice('res://'.length)) : script;
|
|
24
|
+
if (!fs.existsSync(onDisk))
|
|
25
|
+
return headlessFailure('not-found', `Script not found: ${onDisk}`);
|
|
26
|
+
const args = ['-s', script, ...(options.scriptArgs && options.scriptArgs.length > 0 ? ['--', ...options.scriptArgs] : [])];
|
|
27
|
+
const run = await runGodotStep(ctx, 'run-script', args, options.onLine ? { onLine: options.onLine } : {});
|
|
28
|
+
const diagnostics = parseGodotLog(run.output).filter((d) => d.source !== 'engine');
|
|
29
|
+
const report = { projectPath: ctx.projectPath, script, scriptExitCode: run.exitCode, output: run.output, steps: ctx.steps, diagnostics };
|
|
30
|
+
const fail = (failure) => ({ ...failure, ...report });
|
|
31
|
+
const stopped = stoppedRun(run, 'The script');
|
|
32
|
+
if (stopped)
|
|
33
|
+
return fail(stopped);
|
|
34
|
+
if (!engineRan(run))
|
|
35
|
+
return fail(notStarted('script-errors', run));
|
|
36
|
+
const loadErrors = diagnostics.filter((d) => d.severity === 'error' && d.phase === 'load');
|
|
37
|
+
if (loadErrors.length > 0)
|
|
38
|
+
return fail(headlessFailure('script-errors', `The script failed to load (${loadErrors.length} error(s)).`));
|
|
39
|
+
if (run.exitCode !== 0)
|
|
40
|
+
return fail(headlessFailure('script-errors', `The script exited with code ${run.exitCode}.`));
|
|
41
|
+
return { kind: 'success', success: true, ...report };
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=run-script.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-script.js","sourceRoot":"","sources":["../../src/lib/run-script.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type Diagnostic } from './godot-log.js';
|
|
2
|
+
import { type HeadlessBaseOptions, type HeadlessResult, type HeadlessStep } from './headless.js';
|
|
3
|
+
import { type JUnitReport } from './junit.js';
|
|
4
|
+
export type TestRunner = 'gut' | 'gdunit4';
|
|
5
|
+
export declare const TEST_RUNNERS: Record<TestRunner, {
|
|
6
|
+
label: string;
|
|
7
|
+
script: string;
|
|
8
|
+
install: string;
|
|
9
|
+
}>;
|
|
10
|
+
export interface RunTestsOptions extends HeadlessBaseOptions {
|
|
11
|
+
/** Default: the one installed (GUT first when both are). */
|
|
12
|
+
runner?: TestRunner;
|
|
13
|
+
/** Test directories (res:// or project-relative). Default: GUT's .gutconfig.json, else res://test and/or res://tests. */
|
|
14
|
+
dirs?: string[];
|
|
15
|
+
/** Only test scripts whose file name contains this text. */
|
|
16
|
+
filter?: string;
|
|
17
|
+
/** Skip the `--import` step (the project was imported already). */
|
|
18
|
+
skipImport?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface TestsReport {
|
|
21
|
+
projectPath: string;
|
|
22
|
+
runner: TestRunner;
|
|
23
|
+
steps: HeadlessStep[];
|
|
24
|
+
/** The JUnit XML file the runner wrote. */
|
|
25
|
+
junitFile?: string;
|
|
26
|
+
report?: JUnitReport;
|
|
27
|
+
/** Test scripts that failed to load (they never reach the report). */
|
|
28
|
+
loadErrors: Diagnostic[];
|
|
29
|
+
}
|
|
30
|
+
export type RunTestsResult = HeadlessResult<TestsReport>;
|
|
31
|
+
/** The test runners installed in a project. */
|
|
32
|
+
export declare function detectTestRunners(projectPath: string): TestRunner[];
|
|
33
|
+
/** Default test dirs: res://test and/or res://tests when they exist, else res://. */
|
|
34
|
+
export declare function defaultTestDirs(projectPath: string): string[];
|
|
35
|
+
/** The `.gd` files under `dirs` whose name contains `filter` (for gdUnit4, which has no file filter of its own). */
|
|
36
|
+
export declare function findTestScripts(projectPath: string, dirs: string[], filter: string): string[];
|
|
37
|
+
/** The runner's command line (after `--headless --path <project>`). */
|
|
38
|
+
export declare function runnerArgs(runner: TestRunner, options: {
|
|
39
|
+
projectPath: string;
|
|
40
|
+
dirs?: string[];
|
|
41
|
+
filter?: string;
|
|
42
|
+
junitFile: string;
|
|
43
|
+
reportDir: string;
|
|
44
|
+
}): string[];
|
|
45
|
+
/**
|
|
46
|
+
* The newest file named like `nameRe` under `root`, `maxDepth` folders deep (gdUnit4: `<dir>/report_<n>/results.xml`;
|
|
47
|
+
* GUT: `junit.xml` or `junit_<timestamp>.xml` directly in the run folder).
|
|
48
|
+
*/
|
|
49
|
+
export declare function newestReport(root: string, nameRe: RegExp, maxDepth?: number): string | undefined;
|
|
50
|
+
export declare function runTests(options: RunTestsOptions): Promise<RunTestsResult>;
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
// `runTests`: run the project's GUT or gdUnit4 suite headless with a hard timeout (both runners are known to hang),
|
|
2
|
+
// and judge it from the JUnit XML report cross-checked with the log: a test script that failed to load never shows up
|
|
3
|
+
// in the report, so it must not pass. Library-safe.
|
|
4
|
+
import * as fs from 'fs';
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import { parseGodotLog } from './godot-log.js';
|
|
7
|
+
import { engineRan, headlessFailure, stoppedRun, notStarted, prepareHeadless, runGodotStep, runImportStep, } from './headless.js';
|
|
8
|
+
import { parseJUnit } from './junit.js';
|
|
9
|
+
import { isGodotProjectRoot } from '../utils/project-godot.js';
|
|
10
|
+
export const TEST_RUNNERS = {
|
|
11
|
+
gut: {
|
|
12
|
+
label: 'GUT',
|
|
13
|
+
script: 'addons/gut/gut_cmdln.gd',
|
|
14
|
+
install: 'GUT: https://github.com/bitwes/Gut (Asset Library "GUT - Godot Unit Testing"), copied to res://addons/gut/',
|
|
15
|
+
},
|
|
16
|
+
gdunit4: {
|
|
17
|
+
label: 'gdUnit4',
|
|
18
|
+
script: 'addons/gdUnit4/bin/GdUnitCmdTool.gd',
|
|
19
|
+
install: 'gdUnit4: https://github.com/godot-gdunit-labs/gdUnit4 (Asset Library "gdUnit4"), copied to res://addons/gdUnit4/',
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
/** The test runners installed in a project. */
|
|
23
|
+
export function detectTestRunners(projectPath) {
|
|
24
|
+
return Object.keys(TEST_RUNNERS).filter((r) => fs.existsSync(path.join(projectPath, TEST_RUNNERS[r].script)));
|
|
25
|
+
}
|
|
26
|
+
function toResPath(projectPath, dir) {
|
|
27
|
+
if (dir.startsWith('res://'))
|
|
28
|
+
return dir.replace(/\/+$/, '') || 'res://';
|
|
29
|
+
const rel = path.relative(projectPath, path.resolve(projectPath, dir)).split(path.sep).join('/');
|
|
30
|
+
return rel === '' ? 'res://' : `res://${rel}`;
|
|
31
|
+
}
|
|
32
|
+
function resToFs(projectPath, res) {
|
|
33
|
+
return path.join(projectPath, res.replace(/^res:\/\//, ''));
|
|
34
|
+
}
|
|
35
|
+
/** Default test dirs: res://test and/or res://tests when they exist, else res://. */
|
|
36
|
+
export function defaultTestDirs(projectPath) {
|
|
37
|
+
const found = ['res://test', 'res://tests'].filter((d) => fs.existsSync(resToFs(projectPath, d)));
|
|
38
|
+
return found.length > 0 ? found : ['res://'];
|
|
39
|
+
}
|
|
40
|
+
/** The `.gd` files under `dirs` whose name contains `filter` (for gdUnit4, which has no file filter of its own). */
|
|
41
|
+
export function findTestScripts(projectPath, dirs, filter) {
|
|
42
|
+
const out = [];
|
|
43
|
+
const walk = (res) => {
|
|
44
|
+
let entries;
|
|
45
|
+
try {
|
|
46
|
+
entries = fs.readdirSync(resToFs(projectPath, res), { withFileTypes: true });
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
for (const e of entries) {
|
|
52
|
+
if (e.name.startsWith('.') || (res === 'res://' && e.name === 'addons'))
|
|
53
|
+
continue;
|
|
54
|
+
const child = res === 'res://' ? `res://${e.name}` : `${res}/${e.name}`;
|
|
55
|
+
if (e.isDirectory())
|
|
56
|
+
walk(child);
|
|
57
|
+
else if (e.name.endsWith('.gd') && e.name.includes(filter))
|
|
58
|
+
out.push(child);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
for (const d of dirs)
|
|
62
|
+
walk(d);
|
|
63
|
+
return out.sort();
|
|
64
|
+
}
|
|
65
|
+
/** The runner's command line (after `--headless --path <project>`). */
|
|
66
|
+
export function runnerArgs(runner, options) {
|
|
67
|
+
if (runner === 'gut') {
|
|
68
|
+
const hasConfig = fs.existsSync(path.join(options.projectPath, '.gutconfig.json'));
|
|
69
|
+
const dirs = options.dirs ?? (hasConfig ? [] : defaultTestDirs(options.projectPath));
|
|
70
|
+
return [
|
|
71
|
+
'-s', `res://${TEST_RUNNERS.gut.script}`,
|
|
72
|
+
'-gexit',
|
|
73
|
+
'-ginclude_subdirs',
|
|
74
|
+
...(dirs.length > 0 ? [`-gdir=${dirs.join(',')}`] : []),
|
|
75
|
+
...(options.filter ? [`-gselect=${options.filter}`] : []),
|
|
76
|
+
`-gjunit_xml_file=${options.junitFile}`,
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
const dirs = options.dirs ?? defaultTestDirs(options.projectPath);
|
|
80
|
+
const targets = options.filter ? findTestScripts(options.projectPath, dirs, options.filter) : dirs;
|
|
81
|
+
return [
|
|
82
|
+
'-s', `res://${TEST_RUNNERS.gdunit4.script}`,
|
|
83
|
+
'--ignoreHeadlessMode',
|
|
84
|
+
// gdUnit4 stops at the first failure unless told to continue.
|
|
85
|
+
'-c',
|
|
86
|
+
...targets.flatMap((t) => ['-a', t]),
|
|
87
|
+
'-rd', options.reportDir,
|
|
88
|
+
];
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The newest file named like `nameRe` under `root`, `maxDepth` folders deep (gdUnit4: `<dir>/report_<n>/results.xml`;
|
|
92
|
+
* GUT: `junit.xml` or `junit_<timestamp>.xml` directly in the run folder).
|
|
93
|
+
*/
|
|
94
|
+
export function newestReport(root, nameRe, maxDepth = 0) {
|
|
95
|
+
let best;
|
|
96
|
+
const walk = (dir, depth) => {
|
|
97
|
+
let entries;
|
|
98
|
+
try {
|
|
99
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
for (const e of entries) {
|
|
105
|
+
const full = path.join(dir, e.name);
|
|
106
|
+
if (e.isDirectory() && depth < maxDepth)
|
|
107
|
+
walk(full, depth + 1);
|
|
108
|
+
else if (e.isFile() && nameRe.test(e.name)) {
|
|
109
|
+
const mtime = fs.statSync(full).mtimeMs;
|
|
110
|
+
if (!best || mtime > best.mtime)
|
|
111
|
+
best = { file: full, mtime };
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
walk(root, 0);
|
|
116
|
+
return best?.file;
|
|
117
|
+
}
|
|
118
|
+
export async function runTests(options) {
|
|
119
|
+
const projectPath = path.resolve(options.projectPath);
|
|
120
|
+
// Before prepareHeadless: a missing runner is reported without looking for a binary or an open editor.
|
|
121
|
+
const installed = isGodotProjectRoot(projectPath) ? detectTestRunners(projectPath) : [];
|
|
122
|
+
const runner = options.runner ?? installed[0];
|
|
123
|
+
if (!runner || !installed.includes(runner)) {
|
|
124
|
+
const wanted = options.runner ? [options.runner] : Object.keys(TEST_RUNNERS);
|
|
125
|
+
return headlessFailure('not-found', `No ${options.runner ? TEST_RUNNERS[options.runner].label : 'test runner (GUT or gdUnit4)'} installed in ${projectPath}. Install one:\n` +
|
|
126
|
+
wanted.map((r) => ` ${TEST_RUNNERS[r].install}`).join('\n'));
|
|
127
|
+
}
|
|
128
|
+
const ctx = prepareHeadless(options);
|
|
129
|
+
if (ctx.kind === 'failure')
|
|
130
|
+
return ctx;
|
|
131
|
+
const progress = options.onProgress ?? (() => undefined);
|
|
132
|
+
const dirs = options.dirs?.map((d) => toResPath(ctx.projectPath, d));
|
|
133
|
+
// One report folder, cleared per run (the previous run's reports would otherwise pile up in .godot/).
|
|
134
|
+
const runDir = path.join(ctx.workDir, 'test');
|
|
135
|
+
fs.rmSync(runDir, { recursive: true, force: true });
|
|
136
|
+
fs.mkdirSync(runDir, { recursive: true });
|
|
137
|
+
const junitFile = path.join(runDir, 'junit.xml');
|
|
138
|
+
const reportDir = path.join(runDir, 'gdunit-reports');
|
|
139
|
+
const report = { projectPath: ctx.projectPath, runner, steps: ctx.steps, loadErrors: [] };
|
|
140
|
+
const fail = (failure) => ({ ...failure, ...report });
|
|
141
|
+
if (!options.skipImport) {
|
|
142
|
+
progress('Importing the project (godot --headless --import)...');
|
|
143
|
+
const imported = await runImportStep(ctx);
|
|
144
|
+
if (imported.failure?.reason === 'timeout' || imported.failure?.reason === 'interrupted')
|
|
145
|
+
return fail(imported.failure);
|
|
146
|
+
}
|
|
147
|
+
progress(`Running ${TEST_RUNNERS[runner].label} tests...`);
|
|
148
|
+
const run = await runGodotStep(ctx, 'test', runnerArgs(runner, { projectPath: ctx.projectPath, ...(dirs ? { dirs } : {}), ...(options.filter ? { filter: options.filter } : {}), junitFile, reportDir }));
|
|
149
|
+
report.loadErrors = parseGodotLog(run.output).filter((d) => d.severity === 'error' && d.source === 'gdscript' && d.phase === 'load' && !d.file?.startsWith('res://addons/'));
|
|
150
|
+
const stopped = stoppedRun(run, `The ${TEST_RUNNERS[runner].label} run`);
|
|
151
|
+
if (stopped)
|
|
152
|
+
return fail(stopped);
|
|
153
|
+
if (!engineRan(run))
|
|
154
|
+
return fail(notStarted('test-run-error', run));
|
|
155
|
+
// GUT renames the report to junit_<timestamp>.xml when junit_xml_timestamp is on (.gutconfig.json; GUT reads any
|
|
156
|
+
// -gjunit_xml_timestamp value as true): take the newest junit*.xml in this run's folder, which starts empty.
|
|
157
|
+
const xmlFile = runner === 'gut' ? newestReport(runDir, /^junit.*\.xml$/) : newestReport(reportDir, /^results\.xml$/, 3);
|
|
158
|
+
if (!xmlFile || !fs.existsSync(xmlFile)) {
|
|
159
|
+
return fail(headlessFailure('test-run-error', `${TEST_RUNNERS[runner].label} wrote no JUnit report (exit ${run.exitCode}): no verdict.`));
|
|
160
|
+
}
|
|
161
|
+
report.junitFile = xmlFile;
|
|
162
|
+
try {
|
|
163
|
+
report.report = parseJUnit(fs.readFileSync(xmlFile, 'utf-8'));
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
return fail(headlessFailure('test-run-error', `Unreadable JUnit report ${xmlFile}: ${err instanceof Error ? err.message : String(err)}`));
|
|
167
|
+
}
|
|
168
|
+
const { failures, errors, tests, suiteFailures } = report.report;
|
|
169
|
+
if (failures + errors > 0)
|
|
170
|
+
return fail(headlessFailure('tests-failed', `${failures + errors} of ${tests} test(s) failed.`));
|
|
171
|
+
// A suite-level setup/teardown failure leaves every case passing.
|
|
172
|
+
if (suiteFailures > 0)
|
|
173
|
+
return fail(headlessFailure('tests-failed', `${suiteFailures} suite-level failure(s) (setup/teardown) although no test case failed.`));
|
|
174
|
+
if (report.loadErrors.length > 0) {
|
|
175
|
+
return fail(headlessFailure('test-run-error', `${report.loadErrors.length} test script(s) failed to load, so their tests never ran.`));
|
|
176
|
+
}
|
|
177
|
+
if (tests === 0)
|
|
178
|
+
return fail(headlessFailure('test-run-error', 'No tests ran: no verdict.'));
|
|
179
|
+
return { kind: 'success', success: true, ...report };
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=run-tests.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-tests.js","sourceRoot":"","sources":["../../src/lib/run-tests.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/run-tool.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// - No commander, no spinners, no process.exit, no console output.
|
|
5
5
|
// - Errors are returned in `{ kind: 'failure', success: false, ... }`,
|
|
6
6
|
// never thrown past the public boundary.
|
|
7
|
+
import { networkFailureReason } from '@baizor/gamedev-cli-core';
|
|
7
8
|
const DEFAULT_TIMEOUT_MS = 60000;
|
|
8
9
|
/**
|
|
9
10
|
* Invoke a regular MCP tool over the Godot plugin's HTTP API.
|
|
@@ -57,7 +58,8 @@ async function invokeTool(routePrefix, opts) {
|
|
|
57
58
|
body: body.json,
|
|
58
59
|
signal: controller.signal,
|
|
59
60
|
});
|
|
60
|
-
|
|
61
|
+
// A connection dropped mid-body throws here and is classified as such, never read as an empty answer.
|
|
62
|
+
const text = await response.text();
|
|
61
63
|
const data = parseJsonOrText(text);
|
|
62
64
|
if (!response.ok) {
|
|
63
65
|
return makeFailure({
|
|
@@ -138,14 +140,6 @@ function serializeInput(input) {
|
|
|
138
140
|
};
|
|
139
141
|
}
|
|
140
142
|
}
|
|
141
|
-
async function safeReadText(response) {
|
|
142
|
-
try {
|
|
143
|
-
return await response.text();
|
|
144
|
-
}
|
|
145
|
-
catch {
|
|
146
|
-
return '';
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
143
|
function parseJsonOrText(text) {
|
|
150
144
|
if (text.length === 0)
|
|
151
145
|
return undefined;
|
|
@@ -156,11 +150,6 @@ function parseJsonOrText(text) {
|
|
|
156
150
|
return text;
|
|
157
151
|
}
|
|
158
152
|
}
|
|
159
|
-
function getCause(err) {
|
|
160
|
-
if (!(err instanceof Error) || !('cause' in err))
|
|
161
|
-
return undefined;
|
|
162
|
-
return err.cause;
|
|
163
|
-
}
|
|
164
153
|
function classifyFetchError(err, endpoint, timeoutMs) {
|
|
165
154
|
if (err instanceof Error && err.name === 'AbortError') {
|
|
166
155
|
return makeFailure({
|
|
@@ -171,14 +160,9 @@ function classifyFetchError(err, endpoint, timeoutMs) {
|
|
|
171
160
|
});
|
|
172
161
|
}
|
|
173
162
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
reason = 'connection-refused';
|
|
178
|
-
else if (causeCode === 'ECONNRESET')
|
|
179
|
-
reason = 'connection-reset';
|
|
180
|
-
else if (causeCode === 'ENOTFOUND' || causeCode === 'EAI_AGAIN')
|
|
181
|
-
reason = 'network-error';
|
|
163
|
+
// The transport cause through the error's cause chain (shared with the other engine CLIs): a socket the server
|
|
164
|
+
// closed before or during the answer is `connection-reset` (exit 7), not `unknown`.
|
|
165
|
+
const reason = networkFailureReason(err) ?? 'unknown';
|
|
182
166
|
return makeFailure({
|
|
183
167
|
endpoint,
|
|
184
168
|
reason,
|
package/dist/lib/run-tool.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-tool.js","sourceRoot":"","sources":["../../src/lib/run-tool.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,EAAE;AACF,yDAAyD;AACzD,mEAAmE;AACnE,uEAAuE;AACvE,2CAA2C;
|
|
1
|
+
{"version":3,"file":"run-tool.js","sourceRoot":"","sources":["../../src/lib/run-tool.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/session.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import { EditorSession, type EditorSessionOptions, type EngineAdapter, type EnsureResult, type SessionProcessOps } from '@baizor/gamedev-cli-core';
|
|
1
|
+
import { EditorSession, type EditorSessionErrorReason, type EditorSessionOptions, type EngineAdapter, type EnsureResult, type SessionProcessOps } from '@baizor/gamedev-cli-core';
|
|
2
2
|
import type { BuildProjectResult } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Where a started editor's log is (Godot's own `user://logs/godot.log`) and the script/load errors in it, for a start
|
|
5
|
+
* that never became ready.
|
|
6
|
+
*/
|
|
7
|
+
export declare function startupLogHint(projectPath: string): string;
|
|
3
8
|
/** The plugin's system tool that saves and quits a CLI-started editor (a windowless one gets no WM_CLOSE). */
|
|
4
9
|
export declare const SESSION_CLOSE_TOOL = "session-close";
|
|
5
10
|
export interface GodotSessionOptions {
|
|
@@ -67,6 +72,8 @@ export type EnsureEditorResult = {
|
|
|
67
72
|
* `launch-failed`: no editor could be started; `not-ready`: its server never answered.
|
|
68
73
|
*/
|
|
69
74
|
reason: 'invalid-options' | 'build-failed' | 'launch-failed' | 'not-ready';
|
|
75
|
+
/** The editor session's own reason, when it failed (`lock-busy`: another CLI call holds the start lock). */
|
|
76
|
+
sessionReason?: EditorSessionErrorReason;
|
|
70
77
|
message: string;
|
|
71
78
|
error?: Error;
|
|
72
79
|
};
|