Commit Graph

534 Commits

Author SHA1 Message Date
Peter Steinberger d512d1b182 fix(gateway): preserve trusted CA certificates when reinstalling services (#117944) 2026-08-02 03:19:00 -07:00
Peter Steinberger f620c19a93 fix(gateway): bound service reads in status (#117636)
* fix(gateway): bound status service reads

* fix(gateway): preserve service probe errors
2026-08-01 14:38:40 -07:00
Masato Hoshino 29577fb03b fix(daemon): keep backslashes and quotes intact in generated systemd units (#117375)
* fix(daemon): keep backslashes and quotes intact in generated systemd units

systemdEscapeArg escaped only pairs of backslashes and rendered a quote as
two backslashes plus a quote, so any value holding a lone backslash or a
quote did not survive the readers in this module -- or systemd itself. The
installed Linux service received a different value than the operator
configured, with nothing reporting the difference.

parseSystemdEnvAssignment also carried a private copy of the shared
unquoting loop whose escape branch compared one character against a
two-character literal, so it never unescaped anything. Service inspection
reads through that path, so status and doctor reported the escaped form.

Align the writer with serializeSystemdEnvironmentFileValue and route the
reader through the shared splitter its three siblings already use. Adds
round-trip tables mirroring the Windows cmd sibling.

* fix(daemon): align systemd round-trip parser with current main

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-08-01 12:04:05 -05:00
Peter Steinberger ae1ab269eb fix(systemd): parse all inline environment assignments (#117484) 2026-08-01 09:09:51 -07:00
Sasan 6938f7dddb fix: allow gateway service commands for named profiles (#116314)
* fix: gateway service commands refuse a named profile or relocated OPENCLAW_HOME

- Resolve the default install identity against the canonical state directory
  for the active OpenClaw home and profile instead of the unprofiled OS
  account default.
- `--profile <name>` / `--dev` project `.openclaw-<profile>` state and config
  paths, so every named profile was classified as isolated state and refused
  `install`, `start`, `stop`, `restart`, `uninstall`, Doctor service repair,
  and self-update service handling.
- `OPENCLAW_HOME` relocates all OpenClaw path defaults and is documented for
  running as a dedicated service user; a relocated home is now an install
  identity. `HOME` alone still is not.
- An `OPENCLAW_STATE_DIR` or `OPENCLAW_CONFIG_PATH` pointing outside those
  canonical paths is still treated as isolated state.
- Recovery guidance in the refusal message now names the paths that must match.

Verified: focused vitest shards for the changed suites plus the daemon, CLI,
and doctor suites that consume the identity check; tsgo core and core-test
lanes; oxlint; docs format, MDX, link, and map checks.

* fix(gateway): keep relocated homes isolated

* fix(config): validate service profile identity

* fix(daemon): enforce named-profile service ownership

* fix(update): reject drifted service selectors before probes

* test(windows): prove scheduled task lifecycle

* test(windows): harden scheduled task proof cleanup

* test(windows): bind lifecycle proof to checkout

* test(windows): normalize cleanup exit status

* test(windows): verify effective task privilege

* test(windows): protect scheduled task proof roots

* test(windows): prove listener-owned task lifecycle

* test(windows): fix scheduled task proof contracts

* test(windows): remove redundant mock coercions

* test(windows): measure fallback before task probes

* test(windows): prove scheduled task process origin

* fix(gateway): preserve unmanaged restart fallback

* test(gateway): cover denied restart ownership

* test(gateway): keep restart helper types private

* test(gateway): classify lifecycle helpers as test code

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-08-01 11:28:39 +08:00
Peter Steinberger 08c47c29bb fix(daemon): report platform service removal failures (#116819)
Co-authored-by: Peter Steinberger <steipete@macos.shared>
2026-07-31 05:33:39 -07:00
Peter Steinberger deee9ed3cb fix(daemon): verify service starts and installations (#116815)
Co-authored-by: Peter Steinberger <steipete@macos.shared>
2026-07-31 05:32:06 -07:00
Ben Badejo 847dcad4a4 fix(gateway): avoid false port-busy reports behind Tailscale Serve (#116579)
* fix(gateway): scope lifecycle port probes to bind host

* fix(gateway): honor configured bind in lifecycle probes

* fix(gateway): preserve restart health call compatibility

* fix(gateway): scope daemon port ownership

---------

Co-authored-by: Benjamin Badejo <ben@benbadejo.com>
Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
2026-07-30 21:49:25 -07:00
Vincent Koc b015925bc3 test(core): remove dead and duplicate coverage (#116641)
* test(markdown): make code table style coverage assertive

* test(core): remove duplicate regression coverage
2026-07-31 11:48:46 +08:00
Miorbnli 5e1e70e423 fix(systemd): clear start-limit latch before gateway start (#116178)
* fix(systemd): clear start-limit latch before gateway start

systemd latches a unit into failed/start-limit-hit after a crash loop,
then refuses systemctl start until reset-failed clears the latch. The
reset-failed step only ran for the restart action, so the natural
recovery command `openclaw gateway start` failed on a crash-looped
gateway while `openclaw gateway restart` recovered it.

Run reset-failed before both start and restart (stop is unchanged),
mirroring launchd's startLaunchAgent which calls launchctl enable before
kickstart. reset-failed is idempotent and a no-op on a healthy unit.

* test(systemd): cover start-limit latch clearing on system-scope start

Mirrors the existing system-scope restart test: when startSystemdService
targets a system unit as root, it must emit reset-failed before start so
a crash-looped gateway recovers via `openclaw gateway start` too.

* test(systemd): assert healthy start tolerates reset-failed no-op

reset-failed is idempotent on a non-failed unit (systemd returns success
without changing unit state), so prepending it to start cannot regress a
healthy gateway. Covers the merge-risk concern that every start now
depends on an extra reset-failed call.

* test(systemd): cover reset failure and stop controls

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-30 12:47:56 +08:00
Vincent Koc d5e3c68632 fix(daemon): block conflicting systemd gateway owners (#116162) 2026-07-30 09:53:38 +08:00
Ayaan Zaidi d8074d7ca2 fix(daemon): require system-scope ownership before removing the user unit
Doctor's dueling-scope repair removed the user-scope gateway unit on file
presence alone. A system unit file that is merely staged, disabled, failed, or
uncheckable would then cost the operator their only working gateway, and the
default confirm value is true so non-interactive repair amplifies it.

Gate the removal on isSystemUnitActiveOrEnabled: the system unit must be the
live or boot-configured supervisor. Otherwise doctor explains the ambiguity and
prints both fix directions instead of deleting anything. An erroring probe
fails closed for the same reason.

Applies the ClawSweeper rank-up move on #91221.
2026-07-30 08:52:05 +09:00
北京刻熵科技有限责任公司 dcfd6a535d fix(daemon): only treat same-canonical-name user+system units as dueling
Address review: a marker-owned system unit with a *different* name (e.g. an
intentional rescue-bot gateway on the same host) must not be classified as a
duplicate of the canonical user unit, or doctor could remove a legitimate user
gateway. Dueling now requires user.unitName === system.unitName; the direct
canonical system path still matches the real #79375 case. Adds a regression
test for "user canonical + custom marker-owned system" -> kind:"user".
2026-07-30 08:52:05 +09:00
北京刻熵科技有限责任公司 9308557f72 fix(daemon): detect and resolve dueling user+system systemd gateway units
After upgrading on Linux, a user-scope unit
(~/.config/systemd/user/openclaw-gateway.service) and a system-scope unit
(/etc/systemd/system/openclaw-gateway.service) can both exist and both try to
manage the gateway. They bind the same port and each instance's stale-process
detection SIGTERMs the other, producing an endless restart cascade (#79375).

The detector `findInstalledSystemdGatewayScope` checked the user path first and
returned early, so it could never surface that both scopes coexist.

This adds, in three layers:

1. Detection: `findSystemdGatewayInstallation` — a discriminated union
   (none/user/system/dueling) that reports every installed scope without
   early-returning. `findInstalledSystemdGatewayScope` is refactored to delegate
   to it while preserving the exact user-first preference its four lifecycle
   callers rely on (no behavior change for stop/restart/is-enabled/runtime).
   Adds `uninstallUserSystemdGatewayUnit` (removes only the $HOME user unit, no
   root needed) and the pure `formatDuelingScopesWarning` helper.

2. Doctor: `maybeResolveDuelingSystemdGatewayScopes` detects the dueling state
   and, after the existing confirm/policy gate, removes the redundant user-scope
   unit while keeping the root-installed system unit authoritative. Declining or
   an externally-managed policy falls back to the existing cleanup hints.

3. Startup guard: in service mode, when a stale-kill actually happened, log a
   targeted remediation pointing at `openclaw doctor --fix` instead of letting
   the loop look like routine stale cleanup. Diagnostic only — the kill decision
   is unchanged.
2026-07-30 08:52:05 +09:00
Peter Steinberger e80fe942c8 fix(gateway): stop start-time repair from retargeting managed services (#115935) 2026-07-29 13:41:19 -04:00
Harjoth Khara 895b691c55 fix(daemon): refuse duplicate launchd gateway managers (#97285)
* fix(daemon): block duplicate launchd owners

Co-authored-by: Harjoth Khara <harjoth.khara@gmail.com>

* fix(ci): satisfy launchd docs and lint gates

* fix(ci): remove unused launchd exports

* docs: refresh gateway map

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-29 13:31:22 -04:00
Peter Steinberger 383f8947c1 fix: doctor skips host services for isolated state (#115922)
* fix(doctor): isolate host service management

* fix(doctor): clarify service isolation recovery

* test(doctor): isolate service identity fixtures

* test(daemon): keep lifecycle fixtures lint-clean

* test(daemon): isolate install identity fixtures
2026-07-29 11:09:56 -04:00
Vincent Koc f6e874835c fix(gateway): stop managed crash loops for pending agent DB migrations (#115631)
* fix(gateway): recover managed services after agent DB migration

* test(infra): use managed temp directory cleanup
2026-07-29 19:21:46 +08:00
Peter Steinberger aa1e8683d8 fix(doctor): keep cleanup hints scoped to detected services (#115559) 2026-07-29 01:08:19 -04:00
WhatsSkiLL 62129e3a68 fix(windows): make scheduled task stop locale-independent (#114434)
Co-authored-by: IWhatsskill <284122573+IWhatsskill@users.noreply.github.com>
2026-07-28 01:33:31 +08:00
Peter Steinberger 269bc5c89e fix(cli): preserve machine-readable stdout (#113654)
Co-authored-by: 1052326311 <65798732+1052326311@users.noreply.github.com>
2026-07-27 05:44:16 -04:00
Peter Steinberger b34c19632f refactor(daemon): split Windows task scheduler integration (#113783)
* refactor(daemon): split Windows task scheduler integration

* refactor(daemon): keep split helpers private

* refactor(daemon): remove superseded schtasks script module
2026-07-27 02:55:23 -04:00
Dallin Romney ffec7731a4 fix: report unavailable system Node versions accurately (#114325) 2026-07-27 14:06:27 +08:00
Peter Steinberger cb099f5193 refactor(daemon): split Windows scheduled task service (#113934)
* refactor(daemon): split scheduled task service

* refactor(daemon): narrow scheduled task internals
2026-07-25 18:03:32 -07:00
Vincent Koc adf78cefbd refactor(daemon): centralize environment source lookup (#113283) 2026-07-24 16:45:03 +08:00
RickLin 328fa5b964 fix(gateway): actively relaunch launchd restarts (#104637)
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-21 08:58:18 -07:00
shawnduggan f53e9f6e1c fix: allow macOS gateway restart while LaunchAgent owns its port (#89096)
* fix(daemon): allow launchd restart while gateway port is busy

* fix(daemon): verify launchd restart port ownership

* fix(macos): refresh launchd restart ownership guard

Protect the managed LaunchAgent PID, re-read after cleanup, and reject mixed or unattributed port ownership before service mutation.

Co-authored-by: Shawn Duggan <shawn.duggan@gmail.com>

* fix(macos): refresh launchd pid during stale cleanup

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-21 02:49:28 -07:00
Alix-007 3c10513c72 fix(doctor): bound legacy launchd cleanup (#109116)
* fix(doctor): bound legacy launchd cleanup

Bound both legacy launchctl cleanup attempts to five seconds while keeping the existing bootout-then-unload order. Only move the legacy plist after a successful command or an explicit already-unloaded result; leave it in place when timeout or another failure prevents confirmation so a later doctor run can retry. Report filesystem cleanup failures as skipped instead of claiming removal.

* fix(doctor): verify launchd cleanup state before moving plist

Poll a bounded launchctl print postcondition after legacy bootout and unload. Treat loaded, unknown, and timed-out probes as unconfirmed so doctor leaves the plist available for retry.

* test(doctor): clarify launchd cleanup postcondition

* fix(doctor): reject timed-out launchd probes

Preserve timeout evidence from the command runner, including sanitized timeout messages, so partial not-loaded output cannot authorize plist removal.

* fix(doctor): classify no-output launchd timeouts

* test(doctor): trim launchd cleanup cases

Co-authored-by: Alix-007 <li.long15@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-20 20:36:04 -07:00
Peter Steinberger c45e3dd315 fix(gateway): avoid macOS startup freeze during worker reconciliation (#111533)
* fix(gateway): warm system CA before worker reconciliation

* fix(gateway): tolerate denied worker warmup
2026-07-19 14:39:25 -07:00
Peter Steinberger 359859d343 fix(gateway): keep first outbound TLS off the main event loop on macOS (system-CA/trustd) (#111473) 2026-07-19 09:01:09 -07:00
Peter Steinberger fab3cdf1d8 refactor(gateway): simplify lifecycle mutation contracts (#110583)
* refactor(gateway): simplify lifecycle mutation contracts

* refactor(cli): unexport internal emitDaemonActionMessage
2026-07-18 11:09:21 +01:00
cxbAsDev 94e1cc9a4e fix(daemon): distinguish shared and embedded SQLite in system Node warning (#107990) 2026-07-18 10:02:28 +01:00
Peter Steinberger 6416baef83 fix(gateway): audit managed service starts (#110497)
* fix(gateway): audit managed service starts

* chore: drop changelog edit (release-only per repo policy)

* test(gateway): align start routing mocks
2026-07-18 08:26:45 +01:00
Peter Steinberger 8992f92dab feat(gateway): idempotent start, non-interactive stop guard, unified lifecycle audit (#110323)
* feat: guard gateway lifecycle mutations

* fix: harden gateway lifecycle mutation guards

* fix: harden gateway lifecycle audit safety

* fix(cli): repair gateway lifecycle checks

* fix(cli): satisfy lifecycle CI gates
2026-07-18 07:01:38 +01:00
MatthewSynthia c23dc147b8 fix: launchd reload handoff strands gateway when restart races the drain window (#110213)
* fix(daemon): reload handoff outwaits gateway drain before re-bootstrapping

The launchd reload handoff waited only 15 x 0.2s (~3s) after bootout for the
label to unload, but the booted-out gateway keeps the label registered until
its drain-before-exit window (up to 300s) completes. Bootstrap then failed
with EIO mid-drain, and the kickstart -k fallback cannot succeed on a
booted-out label, leaving the LaunchAgent deregistered and the gateway down
until a manual bootstrap.

Extend the post-bootout wait to cover the full restart-deferral budget plus
margin (315 x 1s, derived from DEFAULT_RESTART_DEFERRAL_TIMEOUT_MS), and
replace the single dead kickstart fallback with a bootstrap retry loop that
only falls back to kickstart -k while the label is actually registered, so
the handoff never exits with the service deregistered.

Closes #110137

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(daemon): scale reload bootout wait to the effective drain budget

A config-raised gateway.reload.deferralTimeoutMs would outlast the fixed
default-derived wait and reopen the stranded-LaunchAgent race. Thread the
effective deferral timeout from restartLaunchAgent into the handoff and derive
the reload bootout wait from it; unbounded (<=0) configs keep the finite
default wait, with the bootstrap retry loop covering the overshoot.

Related: #110137

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(daemon): derive reload wait from launchd ExitTimeOut and keep failed bootstrap status

Review follow-up: the bootout SIGTERM path is bounded by the plist's
ExitTimeOut, not the gateway's restart-deferral config, so derive the reload
bootout wait from LAUNCH_AGENT_EXIT_TIMEOUT_SECONDS plus margin and drop the
deferralTimeoutMs threading. Capture a failed bootstrap's status in the else
branch: after a completed if with a false condition $? is 0, so exhausted
retries logged 'restart done' and exit 0 while the LaunchAgent stayed
deregistered. Adds an execution-level retry-exhaustion test that runs the
generated script against an always-failing launchctl stub.

Related: #110137

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(daemon): exercise delayed launchd reload handoff

Run the generated reload handoff through delayed-stop and exhausted-bootstrap
paths, and clarify that the wait covers launchd's ExitTimeOut stop window.

Co-authored-by: MatthewSynthia <299972631+MatthewSynthia@users.noreply.github.com>

* test(daemon): narrow generated handoff script

Fail clearly when the spawn arguments omit the generated script and pass a
narrowed string to the execution helper.

Co-authored-by: MatthewSynthia <299972631+MatthewSynthia@users.noreply.github.com>

* fix(daemon): retry bootstrap after launchd kickstart race

Continue the bootstrap retry loop when a label disappears between the
registration check and kickstart, and cover the race by executing the generated
handoff script.

Co-authored-by: MatthewSynthia <299972631+MatthewSynthia@users.noreply.github.com>

* test(daemon): make handoff no-wait sentinel explicit

Co-authored-by: MatthewSynthia <299972631+MatthewSynthia@users.noreply.github.com>

---------

Co-authored-by: MatthewSynthia <299972631+MatthewSynthia@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 06:44:02 +01:00
Peter Steinberger da69daeb72 feat(onboarding): recommend plugins and skills from installed apps (#109668)
* feat(onboarding): recommend plugins and skills from installed apps

Scan installed macOS apps during classic onboarding (TCC-free), gather
candidates from official catalogs + ClawHub search, let the configured
model pick genuine matches, and offer an opt-in multiselect install step.
Adds a device.apps node-host command (default-off sharing, Android-parity
envelope) so remote gateways can request a paired Mac's inventory, and a
wizard.appRecommendations kill switch. Custom setup-inference completions
no longer inherit the 32-token verification-probe output cap.

* feat(onboarding): recommend apps in guided flow

* fix(onboarding): harden app recommendations against ClawHub self-promotion

Third-party ClawHub skills are never pre-selected regardless of model tier
(publisher-controlled listing text reaches the matcher prompt and could
promote itself); their labels now say they install third-party code.
Installed-app scans follow symlinked .app bundles. Matcher output stays
bounded by the resolved model's own maxTokens budget (documented invariant).

* fix(onboarding): key official catalog candidates by resolved plugin id

Real catalog entries are package manifests without a top-level id; keying the
candidate map and channel/provider classification by entry.id collapsed the
whole official catalog into one undefined-keyed entry, so no official plugin
or channel was ever recommended. Regression test runs against the bundled
catalogs.

* fix(onboarding): satisfy lint, types, deadcode, and migration gates

Split the guided-onboarding test into a self-contained custodian suite to stay
under max-lines. Narrow app-recommendation exports (drop dead node-payload
normalizer, unexport internal types/helpers, route candidate tests through the
public API), replace map-spread with a helper, unexport device.apps result
types, add installedAppsSharing to node-host migration expectations, cast the
wizard multiselect mock, and regenerate the docs map.

* test(onboarding): register new live test in the shard classifier
2026-07-17 14:07:59 +01:00
Yiğit ERDOĞAN e228a3ca3a fix: gateway.cmd launcher garbles non-ASCII profile paths on non-CJK Windows locales (#108967)
* fix(daemon): encode Windows cmd launcher to the console OEM code page

* fix(infra): unexport resolveWindowsSystemEncoding

The launcher encoder now resolves the boot-time OEM code page via
resolveWindowsOemEncoding, leaving resolveWindowsSystemEncoding with no
external importers; the dead-export ratchet (knip all-exports) fails CI
on exported-but-module-local functions.

* test(windows): cover OEM launcher encoding resolver

* fix(windows): declare launcher OEM code page

* fix(windows): fail closed on unsupported OEM pages

* fix(windows): reject CP1258 precomposition drift

* fix(windows): guard CP864 launcher syntax

* test(windows): pin OEM launcher code page

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-17 00:16:59 -07:00
xydt-tanshanshan a3e526a9d0 fix(daemon): prevent gateway OOM crash loops under sustained load (#96250)
* [AI] fix(daemon): set default --max-old-space-size=8192 in gateway service env

Generate a managed NODE_OPTIONS with --max-old-space-size=8192 for the
gateway systemd service, discarding ambient host NODE_OPTIONS to prevent
--require/--inspect persistence.  8192 MB is chosen as a safe default
that prevents the common OOM crash-loop with the default Node ~4 GB heap
under sustained load.

Related to #96203

* fix(daemon): adapt gateway heap limit to host memory

* fix(daemon): keep gateway heap helpers internal

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 20:12:12 -07:00
pacoa 14dc557cee fix: use effective daemon port flag (#109294)
* fix: use effective daemon port flag

* fix(daemon): unify effective port parsing

Co-authored-by: pacoa <29181120+pacoa-kdbg@users.noreply.github.com>

* fix(daemon): skip consumed port values

Co-authored-by: pacoa <29181120+pacoa-kdbg@users.noreply.github.com>

* style(daemon): format effective port handling

Co-authored-by: pacoa <29181120+pacoa-kdbg@users.noreply.github.com>

---------

Co-authored-by: Paco Avelar <[email protected]>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 17:11:09 -07:00
Alix-007 8f1c6df748 fix(daemon): bound Node binary lookup (#109239)
* fix(daemon): bound Node binary lookup

* docs(changelog): note bounded daemon Node lookup

Co-authored-by: Alix-007 <li.long15@xydigit.com>

* docs(changelog): keep release notes out of contributor PR

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 12:32:08 -07:00
thomas.szbay b06fe2a673 fix(kill-tree): verify process group leader before using group kill to prevent gateway SIGTERM (#76259) (#94697)
* fix(kill-tree): verify process group leader before group kill to prevent gateway SIGTERM (#76259)

- Add isProcessGroupLeader() to killProcessTree/signalProcessTree: ps -p <pid> -o pgid= primary check with /proc/<pid>/stat fallback on Linux. Group kill only when the PID is its own process group leader; non-leaders fall back to single-pid kill, preventing accidental gateway SIGTERM when a non-detached child shares the gateway's process group.
- Propagate detached: true to all detached-spawn cleanup callers (exec-termination, agent-bundle LSP, mcp-stdio, bash, supervisor pty, agent-core nodejs) so detached group cleanup survives leader exit.
- Gateway/daemon cleanup paths (schtasks, restart-health) keep the leader-checked default (detached omitted).

Closes #76259

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor(process): tighten process-group ownership checks

* refactor(daemon): split restart diagnostics

* refactor(daemon): isolate restart health types

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 12:30:51 -07:00
Shakker 244f4965dc feat: add external gateway supervision policy 2026-07-16 17:01:39 +01:00
Alix-007 2d0f0d0ec0 fix(daemon): bound Node runtime probes (#109127) 2026-07-16 09:00:06 -07:00
Abner Shang 5ad0d4d6c0 fix: avoid false restart failure during startup migrations (#105544)
* fix: wait for startup migrations on restart

* fix: honor restart readiness caller context

* fix: preserve restart service identity

* fix: refresh readiness context after repair

* fix: bound migration restart wait by wall clock

Co-authored-by: Abner Shang <abner.shang@gmail.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 02:04:49 -07:00
Peter Steinberger 8959ac8b17 refactor(windows): remove launcher encoding test seam (#108750)
* refactor(windows): remove launcher encoding test hook

* test(windows): widen launcher fixture buffer type
2026-07-16 01:13:21 -07:00
Jason ccec0224fa fix(daemon): gateway fails to launch on Windows when the profile path contains CJK characters (#107751)
* fix(daemon): write Windows gateway launchers in encodings wscript/cmd can decode

gateway.vbs and gateway.cmd were written as UTF-8 without BOM, but
wscript.exe only reads .vbs as ANSI or UTF-16 LE with BOM and cmd.exe
reads .cmd in the console OEM code page, so installs under CJK profile
paths failed with "file not found" (#107416).

Write .vbs as UTF-16 LE with BOM, write non-ASCII .cmd content in the
system code page when it matches the console page (CJK/Thai locales),
and BOM-sniff plus code-page-fallback on read so launchers from older
installs keep parsing and migrate on refresh. The hidden .vbs launch
path originates from #95480, which addressed console visibility only.

* refactor(daemon): drop unused WindowsLauncherScriptFormat export

The type is only referenced by encodeWindowsLauncherScript's format
parameter within the module, so the export tripped check-deadcode-exports.
Keep it module-local.

* fix(daemon): mark code-page cmd launchers with their encoding for deterministic readback

Prepend an ASCII '@rem openclaw-launcher-encoding=<label>' line to code-page
.cmd launchers and decode by that marker instead of sniffing UTF-8. Some GBK
byte sequences are valid UTF-8 (隆 = C2 A1 reads as ¡), so the old sniff
silently corrupted readback and rejected valid paths; the marker makes decode
deterministic and drops the code-page probe (a PowerShell spawn) from the
frequent readScheduledTaskCommand poll path.

Also fix the representability guard for euc-kr: Node ICU decodes euc-kr as
KS X 1001 only, but Windows code page 949 is cp949/UHC, so the TextDecoder
cross-check false-rejected ~8,800 UHC extension syllables (똠 = 8C 63) that
iconv encodes and cmd.exe reads fine. Verify euc-kr via iconv's own cp949
round-trip; keep TextDecoder for the other five labels.

* fix(infra): write Windows restart helper scripts through the launcher encoder

The update-time restart helper wrote its temp .cmd as raw UTF-8 while
embedding the restart-log path, task name, and task script path, so a CJK
profile path or task name broke the same way as the gateway launchers
(#107416). Route the write through encodeWindowsLauncherScript: ASCII content
stays byte-identical UTF-8, CJK content gets the marked code-page encoding, and
an unrepresentable task name now fails the restart attempt cleanly instead of
writing a script cmd.exe would misread.

* chore(deps): minimize pnpm-lock delta for the iconv-lite promotion

Reset pnpm-lock.yaml to origin/main and re-add only the iconv-lite root
importer entry, dropping the unrelated @types/node peer-context flips and
audio-decode deprecation metadata that a mismatched-toolchain regeneration had
pulled in. The diff vs main is now the three-line importer entry only; the
version already resolves in main's tree via express -> body-parser/raw-body.

* refactor(windows): centralize launcher encoding

Co-authored-by: Jason Yao <wsyjh8@gmail.com>

* style(windows): format launcher encoding test

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Peter Steinberger <peter@steipete.me>
2026-07-16 00:42:56 -07:00
Peter Steinberger f81f9d8570 chore: enforce max-lines suppression ratchet (#107315)
* ci: enforce max-lines suppression ratchet

* chore: prune stale max-lines suppression

* fix: close max-lines ratchet enforcement gaps

* fix: harden max-lines ratchet checks

* fix(ci): satisfy max-lines ratchet checks

* style: format max-lines declarations

* fix(ci): match oxlint suppression grammar

* test: isolate max-lines git fixtures

* chore: prune resolved max-lines debt

* test: skip newline path fixture on Windows

* fix: harden max-lines suppression ratchet

* chore: refresh max-lines baseline

* fix: close max-lines ratchet bypasses

* fix: derive ratchet base from PR merge tree

* fix: support older Git in staged ratchet

* fix: align max-lines declarations and baseline

* chore: refresh max-lines baseline for current main

* fix: exclude generated wizard locales from max-lines

* chore: prune resolved max-lines debt
2026-07-14 09:27:02 -07:00
Vincent Koc 63c7c21bdf refactor(core): privatize new internal helpers (#107096) 2026-07-14 12:02:46 +08:00
Peter Steinberger fe715a1b88 refactor(daemon): trim internal exports (#107075) 2026-07-13 20:43:08 -07:00
Peter Steinberger d1684f48a3 refactor: delete dead infra and config exports (#106019)
* refactor: delete dead infra and config exports

* refactor: preserve live infra and config contracts

* refactor(config): remove obsolete file-store lifecycle APIs

* refactor(infra): finish current-main dead export cleanup
2026-07-13 12:00:47 -07:00