gdharness 0.12.17 → 0.13.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.
Files changed (36) hide show
  1. package/build/cli.js +174 -56
  2. package/build/godot/addons/auto_reload/auto_reload.gd +9 -2
  3. package/build/godot/addons/gdharness_editor/bridge_client.gd +11 -5
  4. package/build/godot/addons/gdharness_editor/plugin.gd +17 -4
  5. package/build/godot/addons/gdharness_editor/reading.gd +64 -0
  6. package/build/godot/addons/gdharness_editor/reading.gd.uid +1 -0
  7. package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +30 -12
  8. package/build/godot/addons/gdharness_editor/tools/class_tools.gd +5 -2
  9. package/build/godot/addons/gdharness_editor/tools/play_tools.gd +3 -1
  10. package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +50 -27
  11. package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +92 -42
  12. package/build/godot/addons/gdharness_runtime/reading.gd +64 -0
  13. package/build/godot/addons/gdharness_runtime/reading.gd.uid +1 -0
  14. package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +24 -10
  15. package/build/godot/addons/gdharness_runtime/runtime_capture.gd +7 -5
  16. package/build/godot/addons/gdharness_runtime/runtime_input.gd +24 -22
  17. package/build/godot/addons/gdharness_runtime/runtime_queries.gd +16 -13
  18. package/build/godot/addons/gdharness_runtime/runtime_values.gd +24 -8
  19. package/build/godot/addons/gdharness_runtime/runtime_waits.gd +7 -4
  20. package/build/godot/operations/audio_buses.gd +7 -6
  21. package/build/godot/operations/class_cache.gd +14 -9
  22. package/build/godot/operations/classdb_queries.gd +4 -3
  23. package/build/godot/operations/dependencies.gd +19 -16
  24. package/build/godot/operations/file_walk.gd +4 -2
  25. package/build/godot/operations/gdscript_analysis.gd +2 -3
  26. package/build/godot/operations/gdscript_authoring.gd +20 -8
  27. package/build/godot/operations/godot_operations.gd +7 -2
  28. package/build/godot/operations/import_pipeline.gd +5 -4
  29. package/build/godot/operations/input_actions.gd +13 -9
  30. package/build/godot/operations/patterns.gd +22 -0
  31. package/build/godot/operations/plugins.gd +10 -6
  32. package/build/godot/operations/project_config.gd +41 -3
  33. package/build/godot/operations/reading.gd +66 -0
  34. package/build/godot/operations/serialisation.gd +41 -9
  35. package/build/index.js +147 -53
  36. package/package.json +1 -1
package/build/index.js CHANGED
@@ -10138,7 +10138,7 @@ function defectReport(where, error, godotVersion) {
10138
10138
  }
10139
10139
 
10140
10140
  // src/server.ts
10141
- import { execFile as execFile3, spawn } from "node:child_process";
10141
+ import { execFile as execFile4, spawn } from "node:child_process";
10142
10142
  import { randomUUID as randomUUID2 } from "node:crypto";
10143
10143
  import {
10144
10144
  closeSync,
@@ -10157,7 +10157,7 @@ import { tmpdir as tmpdir4 } from "node:os";
10157
10157
  import { basename as basename2, dirname as dirname4, join as join10, normalize as normalize2 } from "node:path";
10158
10158
  import { setTimeout as delay3 } from "node:timers/promises";
10159
10159
  import { fileURLToPath as fileURLToPath2 } from "node:url";
10160
- import { promisify as promisify3 } from "node:util";
10160
+ import { promisify as promisify4 } from "node:util";
10161
10161
 
10162
10162
  // node_modules/zod/v3/helpers/util.js
10163
10163
  var util;
@@ -25450,6 +25450,9 @@ function announced(line) {
25450
25450
  return { severity: match[2] === "WARNING" ? "warning" : "error", text: match[3] ?? "" };
25451
25451
  }
25452
25452
  var STREAMS = ["stdout", "stderr", "transcript"];
25453
+ function forAnswer(entries) {
25454
+ return entries.map((entry) => entry.detail.length === 0 ? { index: entry.index, severity: entry.severity, source: entry.source, text: entry.text } : entry);
25455
+ }
25453
25456
 
25454
25457
  class GameLog {
25455
25458
  entries = [];
@@ -26356,6 +26359,40 @@ async function runOperation(engine, operation, params, projectPath) {
26356
26359
  return { ok: true, payload, messages: problems(stderr) };
26357
26360
  }
26358
26361
 
26362
+ // src/headless-operations.ts
26363
+ var HEADLESS_OPERATIONS = dictionary({
26364
+ project_settings: dictionary({
26365
+ get: "get_project_setting",
26366
+ set: "set_project_setting",
26367
+ add_autoload: "add_autoload",
26368
+ remove_autoload: "remove_autoload",
26369
+ set_main_scene: "set_main_scene",
26370
+ add_input_action: "add_input_action",
26371
+ enable_plugin: "enable_plugin",
26372
+ disable_plugin: "disable_plugin",
26373
+ add_audio_bus: "create_audio_bus",
26374
+ set_audio_bus_effect: "set_audio_bus_effect",
26375
+ set_audio_bus_volume: "set_audio_bus_volume"
26376
+ }),
26377
+ project_import: dictionary({
26378
+ status: "get_import_status",
26379
+ options: "get_import_options",
26380
+ set_options: "set_import_options",
26381
+ reimport: "reimport_resource",
26382
+ uid: "get_uid",
26383
+ refresh_uids: "resave_resources",
26384
+ refresh_classes: "refresh_class_cache"
26385
+ }),
26386
+ project_export: dictionary({ list: "list_export_presets" }),
26387
+ script_edit: dictionary({ create: "create_script", modify: "modify_script" }),
26388
+ script_info: dictionary({ structure: "get_script_info" }),
26389
+ editor_classes: dictionary({
26390
+ query: "query_classes",
26391
+ info: "query_class_info",
26392
+ inheritance: "inspect_inheritance"
26393
+ })
26394
+ });
26395
+
26359
26396
  // src/issues.ts
26360
26397
  import { createHash as createHash2 } from "node:crypto";
26361
26398
  import process5 from "node:process";
@@ -27212,6 +27249,43 @@ async function handleLSPTool(client, toolName, args) {
27212
27249
  }
27213
27250
  }
27214
27251
 
27252
+ // src/process-time.ts
27253
+ import { execFile as execFile3 } from "node:child_process";
27254
+ import { promisify as promisify3 } from "node:util";
27255
+ var run3 = promisify3(execFile3);
27256
+ var ASK_TIMEOUT_MS = 2000;
27257
+ async function cpuSecondsOf(pid) {
27258
+ try {
27259
+ return process.platform === "win32" ? await askWindows(pid) : await askPosix(pid);
27260
+ } catch {
27261
+ return;
27262
+ }
27263
+ }
27264
+ async function askWindows(pid) {
27265
+ const { stdout } = await run3("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", `(Get-Process -Id ${pid}).CPU`], { timeout: ASK_TIMEOUT_MS, windowsHide: true });
27266
+ const seconds = Number(stdout.trim().replace(",", "."));
27267
+ return Number.isFinite(seconds) ? seconds : undefined;
27268
+ }
27269
+ async function askPosix(pid) {
27270
+ const { stdout } = await run3("ps", ["-o", "time=", "-p", String(pid)], { timeout: ASK_TIMEOUT_MS });
27271
+ return secondsFromClock(stdout.trim());
27272
+ }
27273
+ function secondsFromClock(printed) {
27274
+ if (printed === "") {
27275
+ return;
27276
+ }
27277
+ const dashed = printed.includes("-");
27278
+ const days = dashed ? printed.slice(0, printed.indexOf("-")) : null;
27279
+ const clock = dashed ? printed.slice(printed.indexOf("-") + 1) : printed;
27280
+ const parts = clock.split(":").map((part) => Number(part));
27281
+ if (parts.length === 0 || parts.some((part) => !Number.isFinite(part))) {
27282
+ return;
27283
+ }
27284
+ const seconds = parts.reduce((total, part) => total * 60 + part, 0);
27285
+ const extra = days === null ? 0 : Number(days) * 86400;
27286
+ return Number.isFinite(extra) ? seconds + extra : undefined;
27287
+ }
27288
+
27215
27289
  // src/project-scan.ts
27216
27290
  import { existsSync as existsSync5, readdirSync as readdirSync3, readFileSync as readFileSync5 } from "node:fs";
27217
27291
  import { join as join6 } from "node:path";
@@ -27562,6 +27636,11 @@ function readPositiveNumber(params, key) {
27562
27636
  const number = typeof value === "string" && value.trim() !== "" ? Number(value) : value;
27563
27637
  return typeof number === "number" && Number.isFinite(number) && number > 0 ? number : undefined;
27564
27638
  }
27639
+ function readNonNegativeNumber(params, key) {
27640
+ const value = params[key];
27641
+ const number = typeof value === "string" && value.trim() !== "" ? Number(value) : value;
27642
+ return typeof number === "number" && Number.isFinite(number) && number >= 0 ? number : undefined;
27643
+ }
27565
27644
  function readBoolean(params, key) {
27566
27645
  const value = params[key];
27567
27646
  if (typeof value === "boolean") {
@@ -28205,6 +28284,11 @@ var TOOL_SPECS = [
28205
28284
  parameters: {
28206
28285
  projectPath: PROJECT_PATH,
28207
28286
  setting: { type: "string", description: 'Setting path, such as "display/window/size/viewport_width".' },
28287
+ prefix: {
28288
+ type: "string",
28289
+ ops: ["get"],
28290
+ description: 'get: answer with every setting whose name starts with this, and the type the engine registers for each, instead of one setting by name. "debug/gdscript/warnings/" answers the whole family. The type matters: a family of levels can hold a setting that is a bool, and writing a level over it looks like it worked.'
28291
+ },
28208
28292
  value: {
28209
28293
  blank: true,
28210
28294
  description: 'The value to write. Engine types may be tagged, {"_type": "Vector2", "x": 1, "y": 2}. "" writes an empty string.'
@@ -28226,7 +28310,7 @@ var TOOL_SPECS = [
28226
28310
  },
28227
28311
  requires: ["projectPath"],
28228
28312
  operations: {
28229
- get: { summary: "read one setting", requires: ["setting"] },
28313
+ get: { summary: "read one setting, or every setting under prefix", requires: [] },
28230
28314
  set: { summary: "write one setting", requires: ["setting", "value"] },
28231
28315
  add_autoload: { summary: "register an autoload singleton", requires: ["name", "path"] },
28232
28316
  remove_autoload: { summary: "unregister an autoload", requires: ["name"] },
@@ -28633,7 +28717,7 @@ var TOOL_SPECS = [
28633
28717
  runtimeWaitMs: {
28634
28718
  type: "number",
28635
28719
  ops: ["start"],
28636
- description: "start: how long to wait for the game to announce its runtime before answering. Default 5000. Worth raising for a project that takes longer than that to reach its first frame, which is what leaves runtime listening false on a game whose runtime arrives a moment later."
28720
+ description: "start: how long to wait for the game to announce its runtime before answering. Default 5000. Worth raising for a project that takes longer than that to reach its first frame, which is what leaves runtime listening false on a game whose runtime arrives a moment later. 0 does not wait at all, which is the one to pass for a scene that announces nothing by construction, such as a bench that prints and quits."
28637
28721
  },
28638
28722
  frames: {
28639
28723
  type: "number",
@@ -28642,15 +28726,16 @@ var TOOL_SPECS = [
28642
28726
  },
28643
28727
  timeoutMs: {
28644
28728
  type: "number",
28645
- ops: ["check"],
28646
- description: "check: how long to give the boot before it is called hung. Default 60000."
28729
+ ops: ["check", "wait"],
28730
+ description: "check: how long to give the boot before it is called hung. Default 60000. wait: how long to wait for the run to end before answering anyway. Default 600000."
28647
28731
  }
28648
28732
  },
28649
28733
  requires: ["projectPath"],
28650
28734
  operations: {
28651
28735
  start: { summary: "run the project until it quits or is stopped", requires: [] },
28652
28736
  stop: { summary: "end the run and answer with what it printed last", requires: [] },
28653
- check: { summary: "boot headless, quit after a few frames, and report the verdict", requires: [] }
28737
+ check: { summary: "boot headless, quit after a few frames, and report the verdict", requires: [] },
28738
+ wait: { summary: "wait for the run to end, then answer as editor_output does", requires: [] }
28654
28739
  },
28655
28740
  defaultOperation: "start"
28656
28741
  },
@@ -28667,6 +28752,10 @@ var TOOL_SPECS = [
28667
28752
  type: "boolean",
28668
28753
  description: "Only entries printed since the previous editor_output. Default false."
28669
28754
  },
28755
+ cpu: {
28756
+ type: "boolean",
28757
+ description: "Also answer with cpuSeconds, the processor time the run has used. Default false, because asking costs a subprocess. Worth it when elapsedMs is climbing and nothing is being printed: processor time standing still is a run that has stopped doing anything, rather than one that is slow."
28758
+ },
28670
28759
  contains: {
28671
28760
  type: "string",
28672
28761
  description: "Only entries mentioning this text, matched against everything the run has printed rather than against the entries this answer would otherwise carry: a line is found however much was printed after it."
@@ -29092,7 +29181,7 @@ function buildToolDefinitions() {
29092
29181
  // src/server.ts
29093
29182
  var UPDATE_NOTICE_EVERY = 500;
29094
29183
  var FEEDBACK_NOTICE_EVERY = 250;
29095
- var run3 = promisify3(execFile3);
29184
+ var run4 = promisify4(execFile4);
29096
29185
  var __dirname2 = dirname4(fileURLToPath2(import.meta.url));
29097
29186
  var EDITOR_RESTART_TIMEOUT_MS = 90000;
29098
29187
  var SLOWEST_FRAME_RATE = 20;
@@ -29198,11 +29287,20 @@ function wrongTypes(spec, args) {
29198
29287
  }
29199
29288
  var BRIDGE_RETRY_MS = 2000;
29200
29289
  var SUCCESSOR_CHECK_MS = 1e4;
29290
+ var WAIT_FOR_RUN_MS = 600000;
29291
+ var RUN_POLL_MS = 250;
29201
29292
  var PATH_SOLUTIONS = [
29202
29293
  'Give the path relative to the project, such as "scenes/main.tscn" or "res://scenes/main.tscn"',
29203
29294
  "Point projectPath at the project the file belongs to"
29204
29295
  ];
29205
- var PROJECT_FILE_ARGUMENTS = ["scenePath", "scriptPath", "resourcePath", "newPath", "path", "script"];
29296
+ var PROJECT_FILE_ARGUMENTS = [
29297
+ "scenePath",
29298
+ "scriptPath",
29299
+ "resourcePath",
29300
+ "newPath",
29301
+ "path",
29302
+ "script"
29303
+ ];
29206
29304
  function escapingPropertyValue(properties) {
29207
29305
  if (typeof properties !== "object" || properties === null) {
29208
29306
  return;
@@ -29222,38 +29320,6 @@ var DEBUG_STATE_CALLS = dictionary({
29222
29320
  output: "dap_get_output",
29223
29321
  variables: "dap_get_variables"
29224
29322
  });
29225
- var HEADLESS_OPERATIONS = dictionary({
29226
- project_settings: dictionary({
29227
- get: "get_project_setting",
29228
- set: "set_project_setting",
29229
- add_autoload: "add_autoload",
29230
- remove_autoload: "remove_autoload",
29231
- set_main_scene: "set_main_scene",
29232
- add_input_action: "add_input_action",
29233
- enable_plugin: "enable_plugin",
29234
- disable_plugin: "disable_plugin",
29235
- add_audio_bus: "create_audio_bus",
29236
- set_audio_bus_effect: "set_audio_bus_effect",
29237
- set_audio_bus_volume: "set_audio_bus_volume"
29238
- }),
29239
- project_import: dictionary({
29240
- status: "get_import_status",
29241
- options: "get_import_options",
29242
- set_options: "set_import_options",
29243
- reimport: "reimport_resource",
29244
- uid: "get_uid",
29245
- refresh_uids: "resave_resources",
29246
- refresh_classes: "refresh_class_cache"
29247
- }),
29248
- project_export: dictionary({ list: "list_export_presets" }),
29249
- script_edit: dictionary({ create: "create_script", modify: "modify_script" }),
29250
- script_info: dictionary({ structure: "get_script_info" }),
29251
- editor_classes: dictionary({
29252
- query: "query_classes",
29253
- info: "query_class_info",
29254
- inheritance: "inspect_inheritance"
29255
- })
29256
- });
29257
29323
  var PROJECT_INFO_SECTIONS = dictionary({
29258
29324
  autoloads: { operation: "list_autoloads", params: () => ({}) },
29259
29325
  plugins: { operation: "list_plugins", params: () => ({}) },
@@ -29786,9 +29852,15 @@ class GodotServer {
29786
29852
  case "editor_launch":
29787
29853
  return op === "restart" ? await this.handleRestartEditor() : await this.handleLaunchEditor(args);
29788
29854
  case "editor_run":
29789
- return op === "stop" ? await this.handleStopProject() : await this.handleRunProject(args, op);
29855
+ if (op === "stop") {
29856
+ return await this.handleStopProject();
29857
+ }
29858
+ if (op === "wait") {
29859
+ return await this.handleWaitForRun(args);
29860
+ }
29861
+ return await this.handleRunProject(args, op);
29790
29862
  case "editor_output":
29791
- return this.handleGetDebugOutput(args);
29863
+ return await this.handleGetDebugOutput(args);
29792
29864
  case "editor_status":
29793
29865
  return await this.handleEditorStatus();
29794
29866
  case "editor_rescan":
@@ -30159,7 +30231,7 @@ class GodotServer {
30159
30231
  const log = new GameLog;
30160
30232
  let exitCode = 0;
30161
30233
  try {
30162
- const { stdout, stderr } = await run3(engine.value, exportArgs, { timeout: 300000 });
30234
+ const { stdout, stderr } = await run4(engine.value, exportArgs, { timeout: 300000 });
30163
30235
  log.append("stdout", stdout);
30164
30236
  log.append("stderr", stderr);
30165
30237
  } catch (error) {
@@ -30183,7 +30255,7 @@ class GodotServer {
30183
30255
  exitCode,
30184
30256
  errors: log.count("error"),
30185
30257
  warnings: log.count("warning"),
30186
- entries: problems.entries
30258
+ entries: forAnswer(problems.entries)
30187
30259
  };
30188
30260
  if (!verdict.exported) {
30189
30261
  return {
@@ -30300,7 +30372,7 @@ class GodotServer {
30300
30372
  exitCode,
30301
30373
  hung,
30302
30374
  arguments: cmdArgs,
30303
- entries: printed.slice(0, 60)
30375
+ entries: forAnswer(printed.slice(0, 60))
30304
30376
  }, null, 2)
30305
30377
  }
30306
30378
  ],
@@ -30354,7 +30426,7 @@ class GodotServer {
30354
30426
  }
30355
30427
  async godotVersion(godotPath) {
30356
30428
  try {
30357
- const { stdout } = await run3(godotPath, ["--version"], { timeout: 1e4 });
30429
+ const { stdout } = await run4(godotPath, ["--version"], { timeout: 1e4 });
30358
30430
  return stdout.trim();
30359
30431
  } catch (error) {
30360
30432
  this.logDebug(`Godot did not answer --version: ${errorMessage(error)}`);
@@ -30649,7 +30721,7 @@ class GodotServer {
30649
30721
  platform: process.platform,
30650
30722
  variables: process.env
30651
30723
  });
30652
- const runtimeWaitMs = readPositiveNumber(args, "runtimeWaitMs") ?? ANNOUNCE_BUDGET_MS;
30724
+ const runtimeWaitMs = readNonNegativeNumber(args, "runtimeWaitMs") ?? ANNOUNCE_BUDGET_MS;
30653
30725
  const editorWouldPlay = !headless || editorPlaysHeadless(project.value.file);
30654
30726
  if (this.godotBridge.isConnected() && editorWouldPlay && given.value.length === 0) {
30655
30727
  return await this.playThroughEditor(sceneArgument, refreshed.value, project.value.path, alreadyPlaying, runtimeWaitMs, ended);
@@ -30971,10 +31043,24 @@ class GodotServer {
30971
31043
  frames,
30972
31044
  errors,
30973
31045
  warnings,
30974
- entries: boot.log.select({ severity: "warning", sinceLastCall: false, limit: 200 }).entries
31046
+ entries: forAnswer(boot.log.select({ severity: "warning", sinceLastCall: false, limit: 200 }).entries)
30975
31047
  });
30976
31048
  }
30977
- handleGetDebugOutput(args) {
31049
+ async handleWaitForRun(args) {
31050
+ const run = this.currentRun();
31051
+ if (!run) {
31052
+ return this.createErrorResponse(this.nothingOfOursIsRunning());
31053
+ }
31054
+ const budgetMs = readPositiveNumber(args, "timeoutMs") ?? WAIT_FOR_RUN_MS;
31055
+ const until = Date.now() + budgetMs;
31056
+ while (stillRunning(run) && Date.now() < until) {
31057
+ this.drainEditorOutput(run);
31058
+ this.drainTranscript(run);
31059
+ await delay3(RUN_POLL_MS);
31060
+ }
31061
+ return await this.handleGetDebugOutput(args, stillRunning(run) ? budgetMs : undefined);
31062
+ }
31063
+ async handleGetDebugOutput(args, waitedMs) {
30978
31064
  const run = this.currentRun();
30979
31065
  if (!run) {
30980
31066
  return this.createErrorResponse(this.nothingOfOursIsRunning());
@@ -30993,14 +31079,19 @@ class GodotServer {
30993
31079
  limit: readPositiveNumber(args, "limit") ?? 200
30994
31080
  });
30995
31081
  const halt = run.throughEditor ? this.dapClient?.whereItStopped() ?? null : null;
31082
+ const wanted = readBoolean(args, "cpu") ?? false;
31083
+ const cpuSeconds = wanted && run.pid !== null && stillRunning(run) ? await cpuSecondsOf(run.pid) : undefined;
30996
31084
  const notes = [];
31085
+ if (waitedMs !== undefined) {
31086
+ notes.push(`The wait of ${waitedMs}ms ran out and the run is still going, so this is what it had printed by then rather than everything it will print. editor_run wait again to keep waiting.`);
31087
+ }
30997
31088
  if (run.endedUnwatched === true) {
30998
31089
  notes.push("This run outlived the server that started it and is over now, so its exit code was never collected. Everything it printed is below, read back from its transcript.");
30999
31090
  }
31000
31091
  if (typeof run.endedHere === "string") {
31001
31092
  notes.push(`This run was ended here, by ${run.endedHere}.`);
31002
31093
  } else if (!stillRunning(run) && !run.throughEditor && run.exitCode !== null) {
31003
- notes.push("Nothing here ended this run: it stopped on its own or something outside this server stopped it.");
31094
+ notes.push(run.exitCode === 0 ? "This run quit on its own, cleanly: exit code 0." : "Nothing here ended this run: it stopped on its own or something outside this server stopped it.");
31004
31095
  }
31005
31096
  if (run.transcript !== null && selected.omitted > 0) {
31006
31097
  notes.push(`Everything this run has printed is in ${run.transcript}, uncapped and still being written. Read it for output, not for whether the run is alive: running above is asked of the operating system, and a transcript that stops growing is a run between prints.`);
@@ -31018,9 +31109,12 @@ class GodotServer {
31018
31109
  endedBy: stillRunning(run) ? undefined : run.endedHere ?? null,
31019
31110
  project: !run.throughEditor && run.process === null ? run.projectPath ?? undefined : undefined,
31020
31111
  transcript: run.transcript ?? undefined,
31112
+ startedAt: new Date(run.startedAt).toISOString(),
31113
+ elapsedMs: Date.now() - run.startedAt,
31114
+ cpuSeconds,
31021
31115
  note: notes.length > 0 ? notes.join(" ") : undefined,
31022
31116
  omitted: selected.omitted,
31023
- entries: selected.entries
31117
+ entries: forAnswer(selected.entries)
31024
31118
  });
31025
31119
  }
31026
31120
  async handleStopProject() {
@@ -31040,7 +31134,7 @@ class GodotServer {
31040
31134
  errors: stopped.log.count("error"),
31041
31135
  warnings: stopped.log.count("warning"),
31042
31136
  clean: stopped.log.count("error") === 0,
31043
- entries: stopped.log.select({ severity: "warning", sinceLastCall: false, limit: 200 }).entries
31137
+ entries: forAnswer(stopped.log.select({ severity: "warning", sinceLastCall: false, limit: 200 }).entries)
31044
31138
  });
31045
31139
  }
31046
31140
  async handleRescanFilesystem(args) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdharness",
3
- "version": "0.12.17",
3
+ "version": "0.13.0",
4
4
  "mcpName": "io.github.Aureliolo/gdharness",
5
5
  "description": "A harness for driving a Godot 4 project from an agent: editor addons, a runtime bridge into the running game, an MCP server in front of them, and a CLI that installs and diagnoses the Godot side.",
6
6
  "type": "module",