@denisixnpm/agent-rdp 0.7.12 → 0.7.13

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 CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.7.13](https://github.com/denisix/agent-rdp/compare/agent-rdp-v0.7.12...agent-rdp-v0.7.13) (2026-09-02)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * stop the daemon dying 90s after spawn, plus run/file-pull fixes from the 0.7.11 field report ([c98b42f](https://github.com/denisix/agent-rdp/commit/c98b42fad3807d6c431884783cd849a036512ddb))
9
+
3
10
  ## [0.7.12](https://github.com/denisix/agent-rdp/compare/agent-rdp-v0.7.11...agent-rdp-v0.7.12) (2026-09-02)
4
11
 
5
12
 
package/README.md CHANGED
@@ -212,12 +212,19 @@ corrupted transfer fails loudly instead of leaving a plausible-looking file.
212
212
  ```bash
213
213
  agent-rdp file push ./report.xlsx "C:\\Users\\Admin\\report.xlsx"
214
214
  agent-rdp file pull "C:\\Users\\Admin\\export.csv" ./export.csv
215
+ agent-rdp file pull "C:\\out\\result.json" ./result.json --max-age 120 # stale_file if older
215
216
  ```
216
217
 
217
218
  Transfers are byte-exact, which also makes this the reliable way to place a
218
219
  script with non-ASCII content on the remote — writing one through the clipboard
219
220
  or `Add-Content` re-encodes it and mangles anything outside ASCII. Limit: 128MB.
220
221
 
222
+ `pull` reports the remote file's last-write time (`Modified: … (Ns ago by the
223
+ remote clock)`; `modified`/`modified_unix`/`age_secs` in JSON), so a result
224
+ file can be told from yesterday's without reading it; `--max-age <secs>`
225
+ refuses a stale one outright. The age is computed from the remote machine's
226
+ own clock, so it is right even when the two hosts disagree on the time.
227
+
221
228
  ### Locate (OCR)
222
229
 
223
230
  Finds text on screen with [ocrs](https://github.com/robertknight/ocrs). Use it
@@ -344,25 +351,48 @@ retry that reuses the key — after `indeterminate`, an IPC timeout or
344
351
  per agent process (last 64 results) and empty after a reconnect, so after
345
352
  `connect` verify the side effect instead of retrying blindly.
346
353
 
347
- **`run` exit codes mean something.** The child runs with
348
- `$ErrorActionPreference='Stop'`, so a cmdlet that fails non-terminatingly —
349
- `Add-Content` to a locked file, `Set-Content` to a bad path — exits 1 with the
350
- error on stderr instead of reporting success for a write that never happened.
351
- A script that wants continue-on-error sets `$ErrorActionPreference='Continue'`
352
- on its first line. Do not redirect `*>` into a file the script itself writes:
353
- the child holds it open and the script's own writes fail with "being used by
354
- another process" `run --wait` captures output without a file, and
355
- `run --stream` + `run-poll` covers long jobs.
354
+ **`run` exit codes mean something, and errors arrive as text.** The child
355
+ runs with `$ErrorActionPreference='Stop'` inside a `try/catch` the agent adds:
356
+ a cmdlet that fails non-terminatingly `Add-Content` to a locked file,
357
+ `Set-Content` to a bad path exits 1, and stderr carries the whole exception
358
+ chain as plain text (`ERROR: <type>: <message>`, then `caused by …` for each
359
+ inner exception, the failing line, and the script stack trace). The inner
360
+ exceptions are where a COM error's real message lives a 1C failure that
361
+ used to surface as a bare `NullReferenceException` now shows the COM text
362
+ under it. Native commands keep their own exit code. Scripts that start with
363
+ `param(...)` or `using` get the prelude but no wrapper (those must be a
364
+ script's first statements). A script that wants continue-on-error sets
365
+ `$ErrorActionPreference='Continue'` on its first line; note that with `Stop`,
366
+ a native command's stderr merged via `2>&1` is an error. Do not redirect `*>`
367
+ into a file the script itself writes: the child holds it open and the script's
368
+ own writes fail with "being used by another process" — `run --wait` captures
369
+ output without a file.
370
+
371
+ **Detached launches report an immediate death.** `run` without `--wait`
372
+ returns the pid; if the process has already exited ~250ms later the reply
373
+ also carries `early_exit: true` and its `exit_code` — the "it said it started
374
+ but nothing happened" case made visible at launch time.
375
+
376
+ **Do not `Stop-Process -Name powershell` from a script.** The automation agent
377
+ is a `powershell.exe` process, and so are `run --stream` children; that kills
378
+ them all (`automate restart` brings the agent back, streamed pids are gone).
379
+ Children see the agent's pid as `$env:AGENT_RDP_AGENT_PID`:
380
+ `Get-Process powershell | Where-Object { $_.Id -notin $PID, [int]$env:AGENT_RDP_AGENT_PID } | Stop-Process`.
356
381
 
357
382
  **Long-running commands** get the time they ask for: the transport deadline,
358
383
  the CLI socket timeout and the watchdog all extend to cover
359
384
  `--process-timeout`/`--timeout`. But the agent handles one command at a time,
360
385
  so a long `run --wait` blocks every other `automate` call — past about a
361
- minute, prefer `run --stream` plus `run-poll`. Streamed output is captured to
362
- files on the remote side, so nothing is lost if the process exits between
363
- polls; a finished process stays pollable for 10 minutes and repeat polls return
364
- `exited: true` with empty chunks. (If you redirect inside the command instead,
365
- note that Windows PowerShell 5.1's `>` writes UTF-16LE.)
386
+ minute, prefer `run --stream` plus `run-poll` that is the detached mode:
387
+ `run-poll <pid> --follow [--follow-timeout <ms>]` keeps polling until the
388
+ process exits, printing output as it arrives and the exit code at the end, so
389
+ one command collects everything (a single plain poll before the child has
390
+ flushed prints nothing, which looks like lost output). Streamed output is
391
+ captured to files on the remote side, so nothing is lost if the process exits
392
+ between polls; a finished process stays pollable for 10 minutes and repeat
393
+ polls return `exited: true` with empty chunks. `--wait --stream` waits (the
394
+ stream flag is ignored, with a note). (If you redirect inside the command
395
+ instead, note that Windows PowerShell 5.1's `>` writes UTF-16LE.)
366
396
 
367
397
  **`daemon_not_running` vs `daemon_unresponsive`.** The first means no daemon
368
398
  process exists — reconnect, and `<session>/daemon.log` says why it exited. The
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * Error codes for structured error handling.
3
3
  */
4
- export type ErrorCode = "not_connected" | "already_connected" | "connection_failed" | "authentication_failed" | "timeout" | "invalid_request" | "not_supported" | "internal_error" | "session_not_found" | "ipc_error" | "daemon_not_running" | "daemon_unresponsive" | "daemon_version_mismatch" | "clipboard_error" | "drive_error" | "automation_not_enabled" | "automation_error" | "automation_indeterminate" | "element_not_found" | "stale_ref" | "command_failed";
4
+ export type ErrorCode = "not_connected" | "already_connected" | "connection_failed" | "authentication_failed" | "timeout" | "invalid_request" | "not_supported" | "internal_error" | "session_not_found" | "ipc_error" | "daemon_not_running" | "daemon_unresponsive" | "daemon_version_mismatch" | "stale_file" | "clipboard_error" | "drive_error" | "automation_not_enabled" | "automation_error" | "automation_indeterminate" | "element_not_found" | "stale_ref" | "command_failed";
5
5
  //# sourceMappingURL=ErrorCode.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ErrorCode.d.ts","sourceRoot":"","sources":["../../src/generated/ErrorCode.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,eAAe,GAAG,mBAAmB,GAAG,mBAAmB,GAAG,uBAAuB,GAAG,SAAS,GAAG,iBAAiB,GAAG,eAAe,GAAG,gBAAgB,GAAG,mBAAmB,GAAG,WAAW,GAAG,oBAAoB,GAAG,qBAAqB,GAAG,yBAAyB,GAAG,iBAAiB,GAAG,aAAa,GAAG,wBAAwB,GAAG,kBAAkB,GAAG,0BAA0B,GAAG,mBAAmB,GAAG,WAAW,GAAG,gBAAgB,CAAC"}
1
+ {"version":3,"file":"ErrorCode.d.ts","sourceRoot":"","sources":["../../src/generated/ErrorCode.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,eAAe,GAAG,mBAAmB,GAAG,mBAAmB,GAAG,uBAAuB,GAAG,SAAS,GAAG,iBAAiB,GAAG,eAAe,GAAG,gBAAgB,GAAG,mBAAmB,GAAG,WAAW,GAAG,oBAAoB,GAAG,qBAAqB,GAAG,yBAAyB,GAAG,YAAY,GAAG,iBAAiB,GAAG,aAAa,GAAG,wBAAwB,GAAG,kBAAkB,GAAG,0BAA0B,GAAG,mBAAmB,GAAG,WAAW,GAAG,gBAAgB,CAAC"}
@@ -10,5 +10,11 @@ export type FilePullRequest = {
10
10
  * Destination path on this machine.
11
11
  */
12
12
  local_path: string;
13
+ /**
14
+ * Refuse (with `stale_file`) if the remote file was last written more
15
+ * than this many seconds ago, by the remote machine's clock. Nothing is
16
+ * transferred in that case.
17
+ */
18
+ max_age_secs?: number;
13
19
  };
14
20
  //# sourceMappingURL=FilePullRequest.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FilePullRequest.d.ts","sourceRoot":"","sources":["../../src/generated/FilePullRequest.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC9B;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;CAAE,CAAC"}
1
+ {"version":3,"file":"FilePullRequest.d.ts","sourceRoot":"","sources":["../../src/generated/FilePullRequest.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC9B;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CAAE,CAAC"}
@@ -18,5 +18,20 @@ export type FileTransferResult = {
18
18
  * Number of chunks the transfer was split into.
19
19
  */
20
20
  chunks: number;
21
+ /**
22
+ * Pull only: the remote file's last-write time (RFC 3339, UTC).
23
+ */
24
+ modified?: string;
25
+ /**
26
+ * Pull only: the same as Unix seconds.
27
+ */
28
+ modified_unix?: number;
29
+ /**
30
+ * Pull only: seconds between the remote file's last write and the
31
+ * remote machine's clock at stat time - the freshness signal that tells
32
+ * a just-written result from yesterday's, without depending on the two
33
+ * machines' clocks agreeing.
34
+ */
35
+ age_secs?: number;
21
36
  };
22
37
  //# sourceMappingURL=FileTransferResult.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FileTransferResult.d.ts","sourceRoot":"","sources":["../../src/generated/FileTransferResult.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG;IACjC;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;CAAE,CAAC"}
1
+ {"version":3,"file":"FileTransferResult.d.ts","sourceRoot":"","sources":["../../src/generated/FileTransferResult.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG;IACjC;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CAAE,CAAC"}
@@ -24,5 +24,12 @@ export type RunResult = {
24
24
  * again.
25
25
  */
26
26
  replayed: boolean;
27
+ /**
28
+ * Detached launch only (`wait: false`): the process had already exited
29
+ * when the agent checked ~250ms after starting it, and `exit_code`
30
+ * carries its status. A script that fails before its first real
31
+ * statement is otherwise indistinguishable from one that is running.
32
+ */
33
+ early_exit: boolean;
27
34
  };
28
35
  //# sourceMappingURL=RunResult.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"RunResult.d.ts","sourceRoot":"","sources":["../../src/generated/RunResult.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG;IACxB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,QAAQ,EAAE,OAAO,CAAC;CAAE,CAAC"}
1
+ {"version":3,"file":"RunResult.d.ts","sourceRoot":"","sources":["../../src/generated/RunResult.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG;IACxB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,QAAQ,EAAE,OAAO,CAAC;IAClB;;;;;OAKG;IACH,UAAU,EAAE,OAAO,CAAC;CAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@denisixnpm/agent-rdp",
3
- "version": "0.7.12",
3
+ "version": "0.7.13",
4
4
  "description": "CLI tool for AI agents to control Windows Remote Desktop sessions",
5
5
  "repository": {
6
6
  "type": "git",
@@ -48,11 +48,11 @@
48
48
  },
49
49
  "homepage": "https://github.com/denisix/agent-rdp#readme",
50
50
  "optionalDependencies": {
51
- "@denisixnpm/agent-rdp-darwin-arm64": "^0.7.12",
52
- "@denisixnpm/agent-rdp-darwin-x64": "^0.7.12",
53
- "@denisixnpm/agent-rdp-linux-x64": "^0.7.12",
54
- "@denisixnpm/agent-rdp-linux-arm64": "^0.7.12",
55
- "@denisixnpm/agent-rdp-win32-x64": "^0.7.12",
56
- "@denisixnpm/agent-rdp-win32-arm64": "^0.7.12"
51
+ "@denisixnpm/agent-rdp-darwin-arm64": "^0.7.13",
52
+ "@denisixnpm/agent-rdp-darwin-x64": "^0.7.13",
53
+ "@denisixnpm/agent-rdp-linux-x64": "^0.7.13",
54
+ "@denisixnpm/agent-rdp-linux-arm64": "^0.7.13",
55
+ "@denisixnpm/agent-rdp-win32-x64": "^0.7.13",
56
+ "@denisixnpm/agent-rdp-win32-arm64": "^0.7.13"
57
57
  }
58
58
  }