diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..2b67800 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,80 @@ +name: CI + +on: + push: + branches: [dev, main] + pull_request: + branches: [dev, main] + +permissions: + contents: read + +env: + GOFLAGS: -buildvcs=false + GOWORK: "off" + GOPROXY: "direct" + GOSUMDB: "off" + +jobs: + test: + name: Test + Coverage + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + with: + fetch-depth: 0 + - uses: actions/setup-go@v6 + with: + go-version: '1.26' + - name: Test with coverage + working-directory: go + run: go test -race -coverprofile=coverage.out -covermode=atomic -count=1 ./... + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: go/coverage.out + flags: unittests + fail_ci_if_error: false + + lint: + name: golangci-lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-go@v6 + with: + go-version: '1.26' + - uses: golangci/golangci-lint-action@v9 + with: + version: latest + working-directory: go + args: --timeout=5m --tests=false + + sonarcloud: + name: SonarCloud + runs-on: ubuntu-latest + needs: test + steps: + - uses: actions/checkout@v5 + with: + fetch-depth: 0 + - uses: actions/setup-go@v6 + with: + go-version: '1.26' + - name: Test for coverage + working-directory: go + run: go test -coverprofile=coverage.out -covermode=atomic -count=1 ./... + - name: SonarCloud Scan + uses: SonarSource/sonarqube-scan-action@v6 + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + with: + args: > + -Dsonar.organization=dappcore + -Dsonar.projectKey=dappcore_go-process + -Dsonar.sources=go + -Dsonar.exclusions=**/vendor/**,**/third_party/**,**/.tmp/**,**/*_test.go + -Dsonar.tests=go + -Dsonar.test.inclusions=**/*_test.go + -Dsonar.go.coverage.reportPaths=go/coverage.out diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..f71254f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "external/go"] + path = external/go + url = https://github.com/dappcore/go.git + branch = dev diff --git a/.woodpecker.yml b/.woodpecker.yml index 107f0e6..60358ee 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -14,7 +14,7 @@ steps: GOFLAGS: -buildvcs=false GOWORK: "off" commands: - - golangci-lint run --timeout=5m ./... + - cd go && golangci-lint run --timeout=5m ./... - name: go-test image: golang:1.26-alpine @@ -25,7 +25,7 @@ steps: CGO_ENABLED: "1" commands: - apk add --no-cache git build-base - - go test -race -coverprofile=coverage.out -covermode=atomic -count=1 ./... + - cd go && go test -race -coverprofile=coverage.out -covermode=atomic -count=1 ./... - name: sonar image: sonarsource/sonar-scanner-cli:latest depends_on: [go-test] diff --git a/CLAUDE.md b/CLAUDE.md index 217a6fc..121bc2b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,39 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview -`dappco.re/go/core/process` is the process management framework for CoreGO. It handles process execution (spawn, monitor, stream, kill), daemon lifecycle (PID files, health checks, graceful shutdown, registry), and pipeline orchestration (parallel, sequential, or DAG-ordered multi-process runs). All process events broadcast via Core IPC actions. +`dappco.re/go/process` is the process management framework for CoreGO. It handles process execution (spawn, monitor, stream, kill), daemon lifecycle (PID files, health checks, graceful shutdown, registry), and pipeline orchestration (parallel, sequential, or DAG-ordered multi-process runs). All process events are broadcast via Core IPC actions. + +## Repo Layout + +```text +core/go-process/ +├── go/ ← primary Go module root (dappco.re/go/process) +│ ├── go.mod ← kept at module root to preserve import path +│ ├── go.sum +│ ├── *.go +│ ├── exec/ +│ ├── pkg/ +│ ├── tests/ +│ ├── README.md ← symlink to repo root README.md +│ ├── CLAUDE.md ← symlink to repo root CLAUDE.md +│ ├── AGENTS.md ← symlink to repo root AGENTS.md +│ └── docs/ ← symlink to repo root docs/ +├── ui/ ← TypeScript frontend UI (not part of Go module) +├── docs/ ← shared process docs/spec references +├── specs/ ← process specs YAML (cross-language) +├── .woodpecker.yml +├── sonar-project.properties +└── other cross-language repository files +``` + +## Go Resolution Modes + +| Mode | When | What runs | +|------|------|-----------| +| **CI module mode** | CI and scripted verification | `cd go && GOWORK=off` with pinned module cache and short mode test/vet runs. This is the reproducible dependency mode. | +| **Local contributor mode** | day-to-day development | `cd go && go test ./...` from the Go module root. No `go.work` is present in this repo. | + +Prefer running module tooling from `go/` so commands resolve module paths consistently. ## Commands diff --git a/README.md b/README.md index ad819d4..fade6c9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,21 @@ + + # go-process +> Process orchestration — daemons, runners, registry, pidfiles, IPC bus + +[](https://github.com/dappcore/go-process/actions/workflows/ci.yml) +[](https://sonarcloud.io/dashboard?id=dappcore_go-process) +[](https://codecov.io/gh/dappcore/go-process) +[](https://sonarcloud.io/dashboard?id=dappcore_go-process) +[](https://sonarcloud.io/dashboard?id=dappcore_go-process) +[](https://sonarcloud.io/dashboard?id=dappcore_go-process) +[](https://sonarcloud.io/dashboard?id=dappcore_go-process) +[](https://sonarcloud.io/dashboard?id=dappcore_go-process) +[](https://pkg.go.dev/dappco.re/go/go-process) +[](https://eupl.eu/1.2/en/) + + `go-process` is the process-management service for Core applications. It wraps external command execution, lifecycle tracking, output capture, daemon PID files, health endpoints, and REST/WebSocket provider integration around the `dappco.re/go` diff --git a/external/go b/external/go new file mode 160000 index 0000000..d661b70 --- /dev/null +++ b/external/go @@ -0,0 +1 @@ +Subproject commit d661b703e16183b3cbab101de189f688888a1174 diff --git a/go.work b/go.work new file mode 100644 index 0000000..3d4907c --- /dev/null +++ b/go.work @@ -0,0 +1,9 @@ +go 1.26.2 + +// Workspace mode for development: pulls fresh code from external/ submodules. +// CI uses GOWORK=off to fall back to go/go.mod tags (reproducible). + +use ( + ./go + ./external/go +) diff --git a/go/AGENTS.md b/go/AGENTS.md new file mode 120000 index 0000000..be77ac8 --- /dev/null +++ b/go/AGENTS.md @@ -0,0 +1 @@ +../AGENTS.md \ No newline at end of file diff --git a/go/CLAUDE.md b/go/CLAUDE.md new file mode 120000 index 0000000..949a29f --- /dev/null +++ b/go/CLAUDE.md @@ -0,0 +1 @@ +../CLAUDE.md \ No newline at end of file diff --git a/go/README.md b/go/README.md new file mode 120000 index 0000000..32d46ee --- /dev/null +++ b/go/README.md @@ -0,0 +1 @@ +../README.md \ No newline at end of file diff --git a/actions.go b/go/actions.go similarity index 100% rename from actions.go rename to go/actions.go diff --git a/actions_example_test.go b/go/actions_example_test.go similarity index 100% rename from actions_example_test.go rename to go/actions_example_test.go diff --git a/actions_test.go b/go/actions_test.go similarity index 100% rename from actions_test.go rename to go/actions_test.go diff --git a/buffer.go b/go/buffer.go similarity index 100% rename from buffer.go rename to go/buffer.go diff --git a/buffer_example_test.go b/go/buffer_example_test.go similarity index 100% rename from buffer_example_test.go rename to go/buffer_example_test.go diff --git a/buffer_test.go b/go/buffer_test.go similarity index 100% rename from buffer_test.go rename to go/buffer_test.go diff --git a/daemon.go b/go/daemon.go similarity index 100% rename from daemon.go rename to go/daemon.go diff --git a/daemon_example_test.go b/go/daemon_example_test.go similarity index 100% rename from daemon_example_test.go rename to go/daemon_example_test.go diff --git a/daemon_test.go b/go/daemon_test.go similarity index 100% rename from daemon_test.go rename to go/daemon_test.go diff --git a/go/docs b/go/docs new file mode 120000 index 0000000..a9594bf --- /dev/null +++ b/go/docs @@ -0,0 +1 @@ +../docs \ No newline at end of file diff --git a/errors.go b/go/errors.go similarity index 100% rename from errors.go rename to go/errors.go diff --git a/errors_example_test.go b/go/errors_example_test.go similarity index 100% rename from errors_example_test.go rename to go/errors_example_test.go diff --git a/errors_test.go b/go/errors_test.go similarity index 100% rename from errors_test.go rename to go/errors_test.go diff --git a/exec/doc.go b/go/exec/doc.go similarity index 100% rename from exec/doc.go rename to go/exec/doc.go diff --git a/exec/exec.go b/go/exec/exec.go similarity index 100% rename from exec/exec.go rename to go/exec/exec.go diff --git a/exec/exec_example_test.go b/go/exec/exec_example_test.go similarity index 100% rename from exec/exec_example_test.go rename to go/exec/exec_example_test.go diff --git a/exec/exec_test.go b/go/exec/exec_test.go similarity index 100% rename from exec/exec_test.go rename to go/exec/exec_test.go diff --git a/exec/logger.go b/go/exec/logger.go similarity index 100% rename from exec/logger.go rename to go/exec/logger.go diff --git a/exec/logger_example_test.go b/go/exec/logger_example_test.go similarity index 100% rename from exec/logger_example_test.go rename to go/exec/logger_example_test.go diff --git a/exec/logger_test.go b/go/exec/logger_test.go similarity index 100% rename from exec/logger_test.go rename to go/exec/logger_test.go diff --git a/go.mod b/go/go.mod similarity index 100% rename from go.mod rename to go/go.mod diff --git a/go.sum b/go/go.sum similarity index 100% rename from go.sum rename to go/go.sum diff --git a/health.go b/go/health.go similarity index 100% rename from health.go rename to go/health.go diff --git a/health_example_test.go b/go/health_example_test.go similarity index 100% rename from health_example_test.go rename to go/health_example_test.go diff --git a/health_test.go b/go/health_test.go similarity index 100% rename from health_test.go rename to go/health_test.go diff --git a/os_exec_link.go b/go/os_exec_link.go similarity index 100% rename from os_exec_link.go rename to go/os_exec_link.go diff --git a/pidfile.go b/go/pidfile.go similarity index 100% rename from pidfile.go rename to go/pidfile.go diff --git a/pidfile_example_test.go b/go/pidfile_example_test.go similarity index 100% rename from pidfile_example_test.go rename to go/pidfile_example_test.go diff --git a/pidfile_test.go b/go/pidfile_test.go similarity index 100% rename from pidfile_test.go rename to go/pidfile_test.go diff --git a/pidfile_unix.go b/go/pidfile_unix.go similarity index 100% rename from pidfile_unix.go rename to go/pidfile_unix.go diff --git a/pidfile_windows.go b/go/pidfile_windows.go similarity index 100% rename from pidfile_windows.go rename to go/pidfile_windows.go diff --git a/pkg/api/embed.go b/go/pkg/api/embed.go similarity index 100% rename from pkg/api/embed.go rename to go/pkg/api/embed.go diff --git a/pkg/api/provider.go b/go/pkg/api/provider.go similarity index 98% rename from pkg/api/provider.go rename to go/pkg/api/provider.go index a61797f..0c422f5 100644 --- a/pkg/api/provider.go +++ b/go/pkg/api/provider.go @@ -308,17 +308,7 @@ func (p *ProcessProvider) runProcess(c *gin.Context) { return } - result := p.service.RunWithOptions(c.Request.Context(), process.RunOptions{ - Command: req.Command, - Args: req.Args, - Dir: req.Dir, - Env: req.Env, - DisableCapture: req.DisableCapture, - Detach: req.Detach, - Timeout: req.Timeout, - GracePeriod: req.GracePeriod, - KillGroup: req.KillGroup, - }) + result := p.service.RunWithOptions(c.Request.Context(), process.RunOptions(req)) if !result.OK { c.JSON(http.StatusInternalServerError, failWithDetails("run_failed", result.Error(), map[string]any{ "output": "", @@ -660,11 +650,6 @@ func PIDAlive(pid int) bool { } // intParam parses a URL param as int, returning 0 on failure. -func intParam(c *gin.Context, name string) int { - v, _ := strconv.Atoi(c.Param(name)) - return v -} - func pidFromString(value string) (int, bool) { pid, err := strconv.Atoi(core.Trim(value)) if err != nil || pid <= 0 { diff --git a/pkg/api/provider_example_test.go b/go/pkg/api/provider_example_test.go similarity index 100% rename from pkg/api/provider_example_test.go rename to go/pkg/api/provider_example_test.go diff --git a/pkg/api/provider_test.go b/go/pkg/api/provider_test.go similarity index 100% rename from pkg/api/provider_test.go rename to go/pkg/api/provider_test.go diff --git a/pkg/api/response.go b/go/pkg/api/response.go similarity index 100% rename from pkg/api/response.go rename to go/pkg/api/response.go diff --git a/pkg/api/test_helpers_test.go b/go/pkg/api/test_helpers_test.go similarity index 100% rename from pkg/api/test_helpers_test.go rename to go/pkg/api/test_helpers_test.go diff --git a/pkg/api/websocket.go b/go/pkg/api/websocket.go similarity index 96% rename from pkg/api/websocket.go rename to go/pkg/api/websocket.go index d20ea1c..b3eba65 100644 --- a/pkg/api/websocket.go +++ b/go/pkg/api/websocket.go @@ -20,12 +20,6 @@ type messageType string const ( // typeEvent indicates a generic event. typeEvent messageType = "event" - // typeError indicates an error message. - typeError messageType = "error" - // typeSubscribe requests subscription to a channel. - typeSubscribe messageType = "subscribe" - // typeUnsubscribe requests unsubscription from a channel. - typeUnsubscribe messageType = "unsubscribe" ) // hubMessage is the standard WebSocket event payload emitted by the provider. diff --git a/process.go b/go/process.go similarity index 100% rename from process.go rename to go/process.go diff --git a/process_example_test.go b/go/process_example_test.go similarity index 100% rename from process_example_test.go rename to go/process_example_test.go diff --git a/process_global.go b/go/process_global.go similarity index 100% rename from process_global.go rename to go/process_global.go diff --git a/process_global_example_test.go b/go/process_global_example_test.go similarity index 100% rename from process_global_example_test.go rename to go/process_global_example_test.go diff --git a/process_global_test.go b/go/process_global_test.go similarity index 100% rename from process_global_test.go rename to go/process_global_test.go diff --git a/process_test.go b/go/process_test.go similarity index 100% rename from process_test.go rename to go/process_test.go diff --git a/program.go b/go/program.go similarity index 100% rename from program.go rename to go/program.go diff --git a/program_example_test.go b/go/program_example_test.go similarity index 100% rename from program_example_test.go rename to go/program_example_test.go diff --git a/program_test.go b/go/program_test.go similarity index 100% rename from program_test.go rename to go/program_test.go diff --git a/registry.go b/go/registry.go similarity index 100% rename from registry.go rename to go/registry.go diff --git a/registry_example_test.go b/go/registry_example_test.go similarity index 100% rename from registry_example_test.go rename to go/registry_example_test.go diff --git a/registry_test.go b/go/registry_test.go similarity index 100% rename from registry_test.go rename to go/registry_test.go diff --git a/runner.go b/go/runner.go similarity index 100% rename from runner.go rename to go/runner.go diff --git a/runner_example_test.go b/go/runner_example_test.go similarity index 100% rename from runner_example_test.go rename to go/runner_example_test.go diff --git a/runner_test.go b/go/runner_test.go similarity index 100% rename from runner_test.go rename to go/runner_test.go diff --git a/service.go b/go/service.go similarity index 98% rename from service.go rename to go/service.go index f97eef7..26a84db 100644 --- a/service.go +++ b/go/service.go @@ -43,7 +43,7 @@ func (s *Service) coreApp() *core.Core { if s == nil || s.ServiceRuntime == nil { return nil } - return s.ServiceRuntime.Core() + return s.Core() } // Options configures the process service. @@ -822,22 +822,12 @@ func (s *Service) handleTask(c *core.Core, task core.Message) core.Result { } proc := result.Value.(*Process) return core.Ok(proc.Info()) - case TaskProcessRun: - result := s.RunWithOptions(c.Context(), RunOptions{ - Command: m.Command, - Args: m.Args, - Dir: m.Dir, - Env: m.Env, - DisableCapture: m.DisableCapture, - Detach: m.Detach, - Timeout: m.Timeout, - GracePeriod: m.GracePeriod, - KillGroup: m.KillGroup, - }) - if !result.OK { - return result - } - return core.Ok(result.Value) + case TaskProcessRun: + result := s.RunWithOptions(c.Context(), RunOptions(m)) + if !result.OK { + return result + } + return core.Ok(result.Value) case TaskProcessKill: switch { case m.ID != "": diff --git a/service_example_test.go b/go/service_example_test.go similarity index 100% rename from service_example_test.go rename to go/service_example_test.go diff --git a/service_test.go b/go/service_test.go similarity index 100% rename from service_test.go rename to go/service_test.go diff --git a/test_helpers_test.go b/go/test_helpers_test.go similarity index 100% rename from test_helpers_test.go rename to go/test_helpers_test.go diff --git a/tests/cli/process/Taskfile.yaml b/go/tests/cli/process/Taskfile.yaml similarity index 100% rename from tests/cli/process/Taskfile.yaml rename to go/tests/cli/process/Taskfile.yaml diff --git a/types.go b/go/types.go similarity index 100% rename from types.go rename to go/types.go diff --git a/types_example_test.go b/go/types_example_test.go similarity index 100% rename from types_example_test.go rename to go/types_example_test.go diff --git a/types_test.go b/go/types_test.go similarity index 100% rename from types_test.go rename to go/types_test.go diff --git a/pkg/api/ui/dist/core-process.js b/pkg/api/ui/dist/core-process.js deleted file mode 100644 index b9711a6..0000000 --- a/pkg/api/ui/dist/core-process.js +++ /dev/null @@ -1,2145 +0,0 @@ -/** - * @license - * Copyright 2019 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const V = globalThis, ie = V.ShadowRoot && (V.ShadyCSS === void 0 || V.ShadyCSS.nativeShadow) && "adoptedStyleSheets" in Document.prototype && "replace" in CSSStyleSheet.prototype, re = Symbol(), le = /* @__PURE__ */ new WeakMap(); -let ve = class { - constructor(e, t, i) { - if (this._$cssResult$ = !0, i !== re) throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead."); - this.cssText = e, this.t = t; - } - get styleSheet() { - let e = this.o; - const t = this.t; - if (ie && e === void 0) { - const i = t !== void 0 && t.length === 1; - i && (e = le.get(t)), e === void 0 && ((this.o = e = new CSSStyleSheet()).replaceSync(this.cssText), i && le.set(t, e)); - } - return e; - } - toString() { - return this.cssText; - } -}; -const ke = (s) => new ve(typeof s == "string" ? s : s + "", void 0, re), F = (s, ...e) => { - const t = s.length === 1 ? s[0] : e.reduce((i, r, n) => i + ((o) => { - if (o._$cssResult$ === !0) return o.cssText; - if (typeof o == "number") return o; - throw Error("Value passed to 'css' function must be a 'css' function result: " + o + ". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security."); - })(r) + s[n + 1], s[0]); - return new ve(t, s, re); -}, Ae = (s, e) => { - if (ie) s.adoptedStyleSheets = e.map((t) => t instanceof CSSStyleSheet ? t : t.styleSheet); - else for (const t of e) { - const i = document.createElement("style"), r = V.litNonce; - r !== void 0 && i.setAttribute("nonce", r), i.textContent = t.cssText, s.appendChild(i); - } -}, ce = ie ? (s) => s : (s) => s instanceof CSSStyleSheet ? ((e) => { - let t = ""; - for (const i of e.cssRules) t += i.cssText; - return ke(t); -})(s) : s; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const { is: Pe, defineProperty: Ce, getOwnPropertyDescriptor: Ee, getOwnPropertyNames: Ue, getOwnPropertySymbols: Oe, getPrototypeOf: Te } = Object, k = globalThis, de = k.trustedTypes, ze = de ? de.emptyScript : "", Y = k.reactiveElementPolyfillSupport, j = (s, e) => s, Z = { toAttribute(s, e) { - switch (e) { - case Boolean: - s = s ? ze : null; - break; - case Object: - case Array: - s = s == null ? s : JSON.stringify(s); - } - return s; -}, fromAttribute(s, e) { - let t = s; - switch (e) { - case Boolean: - t = s !== null; - break; - case Number: - t = s === null ? null : Number(s); - break; - case Object: - case Array: - try { - t = JSON.parse(s); - } catch { - t = null; - } - } - return t; -} }, oe = (s, e) => !Pe(s, e), he = { attribute: !0, type: String, converter: Z, reflect: !1, useDefault: !1, hasChanged: oe }; -Symbol.metadata ?? (Symbol.metadata = Symbol("metadata")), k.litPropertyMetadata ?? (k.litPropertyMetadata = /* @__PURE__ */ new WeakMap()); -let z = class extends HTMLElement { - static addInitializer(e) { - this._$Ei(), (this.l ?? (this.l = [])).push(e); - } - static get observedAttributes() { - return this.finalize(), this._$Eh && [...this._$Eh.keys()]; - } - static createProperty(e, t = he) { - if (t.state && (t.attribute = !1), this._$Ei(), this.prototype.hasOwnProperty(e) && ((t = Object.create(t)).wrapped = !0), this.elementProperties.set(e, t), !t.noAccessor) { - const i = Symbol(), r = this.getPropertyDescriptor(e, i, t); - r !== void 0 && Ce(this.prototype, e, r); - } - } - static getPropertyDescriptor(e, t, i) { - const { get: r, set: n } = Ee(this.prototype, e) ?? { get() { - return this[t]; - }, set(o) { - this[t] = o; - } }; - return { get: r, set(o) { - const l = r == null ? void 0 : r.call(this); - n == null || n.call(this, o), this.requestUpdate(e, l, i); - }, configurable: !0, enumerable: !0 }; - } - static getPropertyOptions(e) { - return this.elementProperties.get(e) ?? he; - } - static _$Ei() { - if (this.hasOwnProperty(j("elementProperties"))) return; - const e = Te(this); - e.finalize(), e.l !== void 0 && (this.l = [...e.l]), this.elementProperties = new Map(e.elementProperties); - } - static finalize() { - if (this.hasOwnProperty(j("finalized"))) return; - if (this.finalized = !0, this._$Ei(), this.hasOwnProperty(j("properties"))) { - const t = this.properties, i = [...Ue(t), ...Oe(t)]; - for (const r of i) this.createProperty(r, t[r]); - } - const e = this[Symbol.metadata]; - if (e !== null) { - const t = litPropertyMetadata.get(e); - if (t !== void 0) for (const [i, r] of t) this.elementProperties.set(i, r); - } - this._$Eh = /* @__PURE__ */ new Map(); - for (const [t, i] of this.elementProperties) { - const r = this._$Eu(t, i); - r !== void 0 && this._$Eh.set(r, t); - } - this.elementStyles = this.finalizeStyles(this.styles); - } - static finalizeStyles(e) { - const t = []; - if (Array.isArray(e)) { - const i = new Set(e.flat(1 / 0).reverse()); - for (const r of i) t.unshift(ce(r)); - } else e !== void 0 && t.push(ce(e)); - return t; - } - static _$Eu(e, t) { - const i = t.attribute; - return i === !1 ? void 0 : typeof i == "string" ? i : typeof e == "string" ? e.toLowerCase() : void 0; - } - constructor() { - super(), this._$Ep = void 0, this.isUpdatePending = !1, this.hasUpdated = !1, this._$Em = null, this._$Ev(); - } - _$Ev() { - var e; - this._$ES = new Promise((t) => this.enableUpdating = t), this._$AL = /* @__PURE__ */ new Map(), this._$E_(), this.requestUpdate(), (e = this.constructor.l) == null || e.forEach((t) => t(this)); - } - addController(e) { - var t; - (this._$EO ?? (this._$EO = /* @__PURE__ */ new Set())).add(e), this.renderRoot !== void 0 && this.isConnected && ((t = e.hostConnected) == null || t.call(e)); - } - removeController(e) { - var t; - (t = this._$EO) == null || t.delete(e); - } - _$E_() { - const e = /* @__PURE__ */ new Map(), t = this.constructor.elementProperties; - for (const i of t.keys()) this.hasOwnProperty(i) && (e.set(i, this[i]), delete this[i]); - e.size > 0 && (this._$Ep = e); - } - createRenderRoot() { - const e = this.shadowRoot ?? this.attachShadow(this.constructor.shadowRootOptions); - return Ae(e, this.constructor.elementStyles), e; - } - connectedCallback() { - var e; - this.renderRoot ?? (this.renderRoot = this.createRenderRoot()), this.enableUpdating(!0), (e = this._$EO) == null || e.forEach((t) => { - var i; - return (i = t.hostConnected) == null ? void 0 : i.call(t); - }); - } - enableUpdating(e) { - } - disconnectedCallback() { - var e; - (e = this._$EO) == null || e.forEach((t) => { - var i; - return (i = t.hostDisconnected) == null ? void 0 : i.call(t); - }); - } - attributeChangedCallback(e, t, i) { - this._$AK(e, i); - } - _$ET(e, t) { - var n; - const i = this.constructor.elementProperties.get(e), r = this.constructor._$Eu(e, i); - if (r !== void 0 && i.reflect === !0) { - const o = (((n = i.converter) == null ? void 0 : n.toAttribute) !== void 0 ? i.converter : Z).toAttribute(t, i.type); - this._$Em = e, o == null ? this.removeAttribute(r) : this.setAttribute(r, o), this._$Em = null; - } - } - _$AK(e, t) { - var n, o; - const i = this.constructor, r = i._$Eh.get(e); - if (r !== void 0 && this._$Em !== r) { - const l = i.getPropertyOptions(r), a = typeof l.converter == "function" ? { fromAttribute: l.converter } : ((n = l.converter) == null ? void 0 : n.fromAttribute) !== void 0 ? l.converter : Z; - this._$Em = r; - const p = a.fromAttribute(t, l.type); - this[r] = p ?? ((o = this._$Ej) == null ? void 0 : o.get(r)) ?? p, this._$Em = null; - } - } - requestUpdate(e, t, i, r = !1, n) { - var o; - if (e !== void 0) { - const l = this.constructor; - if (r === !1 && (n = this[e]), i ?? (i = l.getPropertyOptions(e)), !((i.hasChanged ?? oe)(n, t) || i.useDefault && i.reflect && n === ((o = this._$Ej) == null ? void 0 : o.get(e)) && !this.hasAttribute(l._$Eu(e, i)))) return; - this.C(e, t, i); - } - this.isUpdatePending === !1 && (this._$ES = this._$EP()); - } - C(e, t, { useDefault: i, reflect: r, wrapped: n }, o) { - i && !(this._$Ej ?? (this._$Ej = /* @__PURE__ */ new Map())).has(e) && (this._$Ej.set(e, o ?? t ?? this[e]), n !== !0 || o !== void 0) || (this._$AL.has(e) || (this.hasUpdated || i || (t = void 0), this._$AL.set(e, t)), r === !0 && this._$Em !== e && (this._$Eq ?? (this._$Eq = /* @__PURE__ */ new Set())).add(e)); - } - async _$EP() { - this.isUpdatePending = !0; - try { - await this._$ES; - } catch (t) { - Promise.reject(t); - } - const e = this.scheduleUpdate(); - return e != null && await e, !this.isUpdatePending; - } - scheduleUpdate() { - return this.performUpdate(); - } - performUpdate() { - var i; - if (!this.isUpdatePending) return; - if (!this.hasUpdated) { - if (this.renderRoot ?? (this.renderRoot = this.createRenderRoot()), this._$Ep) { - for (const [n, o] of this._$Ep) this[n] = o; - this._$Ep = void 0; - } - const r = this.constructor.elementProperties; - if (r.size > 0) for (const [n, o] of r) { - const { wrapped: l } = o, a = this[n]; - l !== !0 || this._$AL.has(n) || a === void 0 || this.C(n, void 0, o, a); - } - } - let e = !1; - const t = this._$AL; - try { - e = this.shouldUpdate(t), e ? (this.willUpdate(t), (i = this._$EO) == null || i.forEach((r) => { - var n; - return (n = r.hostUpdate) == null ? void 0 : n.call(r); - }), this.update(t)) : this._$EM(); - } catch (r) { - throw e = !1, this._$EM(), r; - } - e && this._$AE(t); - } - willUpdate(e) { - } - _$AE(e) { - var t; - (t = this._$EO) == null || t.forEach((i) => { - var r; - return (r = i.hostUpdated) == null ? void 0 : r.call(i); - }), this.hasUpdated || (this.hasUpdated = !0, this.firstUpdated(e)), this.updated(e); - } - _$EM() { - this._$AL = /* @__PURE__ */ new Map(), this.isUpdatePending = !1; - } - get updateComplete() { - return this.getUpdateComplete(); - } - getUpdateComplete() { - return this._$ES; - } - shouldUpdate(e) { - return !0; - } - update(e) { - this._$Eq && (this._$Eq = this._$Eq.forEach((t) => this._$ET(t, this[t]))), this._$EM(); - } - updated(e) { - } - firstUpdated(e) { - } -}; -z.elementStyles = [], z.shadowRootOptions = { mode: "open" }, z[j("elementProperties")] = /* @__PURE__ */ new Map(), z[j("finalized")] = /* @__PURE__ */ new Map(), Y == null || Y({ ReactiveElement: z }), (k.reactiveElementVersions ?? (k.reactiveElementVersions = [])).push("2.1.2"); -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const N = globalThis, pe = (s) => s, G = N.trustedTypes, ue = G ? G.createPolicy("lit-html", { createHTML: (s) => s }) : void 0, we = "$lit$", S = `lit$${Math.random().toFixed(9).slice(2)}$`, _e = "?" + S, De = `<${_e}>`, O = document, I = () => O.createComment(""), q = (s) => s === null || typeof s != "object" && typeof s != "function", ne = Array.isArray, Me = (s) => ne(s) || typeof (s == null ? void 0 : s[Symbol.iterator]) == "function", ee = "[ \\t\\n\\f\\r]", H = /<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g, me = /-->/g, fe = />/g, C = RegExp(`>|${ee}(?:([^\\s"'>=/]+)(${ee}*=${ee}*(?:[^ \\t\\n\\f\\r"'\`<>=]|("|')|))|$)`, "g"), ge = /'/g, be = /"/g, xe = /^(?:script|style|textarea|title)$/i, Re = (s) => (e, ...t) => ({ _$litType$: s, strings: e, values: t }), c = Re(1), D = Symbol.for("lit-noChange"), d = Symbol.for("lit-nothing"), $e = /* @__PURE__ */ new WeakMap(), E = O.createTreeWalker(O, 129); -function Se(s, e) { - if (!ne(s) || !s.hasOwnProperty("raw")) throw Error("invalid template strings array"); - return ue !== void 0 ? ue.createHTML(e) : e; -} -const He = (s, e) => { - const t = s.length - 1, i = []; - let r, n = e === 2 ? "" : e === 3 ? "" : "")), i]; -}; -class L { - constructor({ strings: e, _$litType$: t }, i) { - let r; - this.parts = []; - let n = 0, o = 0; - const l = e.length - 1, a = this.parts, [p, m] = He(e, t); - if (this.el = L.createElement(p, i), E.currentNode = this.el.content, t === 2 || t === 3) { - const h = this.el.content.firstChild; - h.replaceWith(...h.childNodes); - } - for (; (r = E.nextNode()) !== null && a.length < l; ) { - if (r.nodeType === 1) { - if (r.hasAttributes()) for (const h of r.getAttributeNames()) if (h.endsWith(we)) { - const y = m[o++], x = r.getAttribute(h).split(S), J = /([.?@])?(.*)/.exec(y); - a.push({ type: 1, index: n, name: J[2], strings: x, ctor: J[1] === "." ? Ne : J[1] === "?" ? Ie : J[1] === "@" ? qe : Q }), r.removeAttribute(h); - } else h.startsWith(S) && (a.push({ type: 6, index: n }), r.removeAttribute(h)); - if (xe.test(r.tagName)) { - const h = r.textContent.split(S), y = h.length - 1; - if (y > 0) { - r.textContent = G ? G.emptyScript : ""; - for (let x = 0; x < y; x++) r.append(h[x], I()), E.nextNode(), a.push({ type: 2, index: ++n }); - r.append(h[y], I()); - } - } - } else if (r.nodeType === 8) if (r.data === _e) a.push({ type: 2, index: n }); - else { - let h = -1; - for (; (h = r.data.indexOf(S, h + 1)) !== -1; ) a.push({ type: 7, index: n }), h += S.length - 1; - } - n++; - } - } - static createElement(e, t) { - const i = O.createElement("template"); - return i.innerHTML = e, i; - } -} -function M(s, e, t = s, i) { - var o, l; - if (e === D) return e; - let r = i !== void 0 ? (o = t._$Co) == null ? void 0 : o[i] : t._$Cl; - const n = q(e) ? void 0 : e._$litDirective$; - return (r == null ? void 0 : r.constructor) !== n && ((l = r == null ? void 0 : r._$AO) == null || l.call(r, !1), n === void 0 ? r = void 0 : (r = new n(s), r._$AT(s, t, i)), i !== void 0 ? (t._$Co ?? (t._$Co = []))[i] = r : t._$Cl = r), r !== void 0 && (e = M(s, r._$AS(s, e.values), r, i)), e; -} -class je { - constructor(e, t) { - this._$AV = [], this._$AN = void 0, this._$AD = e, this._$AM = t; - } - get parentNode() { - return this._$AM.parentNode; - } - get _$AU() { - return this._$AM._$AU; - } - u(e) { - const { el: { content: t }, parts: i } = this._$AD, r = ((e == null ? void 0 : e.creationScope) ?? O).importNode(t, !0); - E.currentNode = r; - let n = E.nextNode(), o = 0, l = 0, a = i[0]; - for (; a !== void 0; ) { - if (o === a.index) { - let p; - a.type === 2 ? p = new W(n, n.nextSibling, this, e) : a.type === 1 ? p = new a.ctor(n, a.name, a.strings, this, e) : a.type === 6 && (p = new Le(n, this, e)), this._$AV.push(p), a = i[++l]; - } - o !== (a == null ? void 0 : a.index) && (n = E.nextNode(), o++); - } - return E.currentNode = O, r; - } - p(e) { - let t = 0; - for (const i of this._$AV) i !== void 0 && (i.strings !== void 0 ? (i._$AI(e, i, t), t += i.strings.length - 2) : i._$AI(e[t])), t++; - } -} -class W { - get _$AU() { - var e; - return ((e = this._$AM) == null ? void 0 : e._$AU) ?? this._$Cv; - } - constructor(e, t, i, r) { - this.type = 2, this._$AH = d, this._$AN = void 0, this._$AA = e, this._$AB = t, this._$AM = i, this.options = r, this._$Cv = (r == null ? void 0 : r.isConnected) ?? !0; - } - get parentNode() { - let e = this._$AA.parentNode; - const t = this._$AM; - return t !== void 0 && (e == null ? void 0 : e.nodeType) === 11 && (e = t.parentNode), e; - } - get startNode() { - return this._$AA; - } - get endNode() { - return this._$AB; - } - _$AI(e, t = this) { - e = M(this, e, t), q(e) ? e === d || e == null || e === "" ? (this._$AH !== d && this._$AR(), this._$AH = d) : e !== this._$AH && e !== D && this._(e) : e._$litType$ !== void 0 ? this.$(e) : e.nodeType !== void 0 ? this.T(e) : Me(e) ? this.k(e) : this._(e); - } - O(e) { - return this._$AA.parentNode.insertBefore(e, this._$AB); - } - T(e) { - this._$AH !== e && (this._$AR(), this._$AH = this.O(e)); - } - _(e) { - this._$AH !== d && q(this._$AH) ? this._$AA.nextSibling.data = e : this.T(O.createTextNode(e)), this._$AH = e; - } - $(e) { - var n; - const { values: t, _$litType$: i } = e, r = typeof i == "number" ? this._$AC(e) : (i.el === void 0 && (i.el = L.createElement(Se(i.h, i.h[0]), this.options)), i); - if (((n = this._$AH) == null ? void 0 : n._$AD) === r) this._$AH.p(t); - else { - const o = new je(r, this), l = o.u(this.options); - o.p(t), this.T(l), this._$AH = o; - } - } - _$AC(e) { - let t = $e.get(e.strings); - return t === void 0 && $e.set(e.strings, t = new L(e)), t; - } - k(e) { - ne(this._$AH) || (this._$AH = [], this._$AR()); - const t = this._$AH; - let i, r = 0; - for (const n of e) r === t.length ? t.push(i = new W(this.O(I()), this.O(I()), this, this.options)) : i = t[r], i._$AI(n), r++; - r < t.length && (this._$AR(i && i._$AB.nextSibling, r), t.length = r); - } - _$AR(e = this._$AA.nextSibling, t) { - var i; - for ((i = this._$AP) == null ? void 0 : i.call(this, !1, !0, t); e !== this._$AB; ) { - const r = pe(e).nextSibling; - pe(e).remove(), e = r; - } - } - setConnected(e) { - var t; - this._$AM === void 0 && (this._$Cv = e, (t = this._$AP) == null || t.call(this, e)); - } -} -class Q { - get tagName() { - return this.element.tagName; - } - get _$AU() { - return this._$AM._$AU; - } - constructor(e, t, i, r, n) { - this.type = 1, this._$AH = d, this._$AN = void 0, this.element = e, this.name = t, this._$AM = r, this.options = n, i.length > 2 || i[0] !== "" || i[1] !== "" ? (this._$AH = Array(i.length - 1).fill(new String()), this.strings = i) : this._$AH = d; - } - _$AI(e, t = this, i, r) { - const n = this.strings; - let o = !1; - if (n === void 0) e = M(this, e, t, 0), o = !q(e) || e !== this._$AH && e !== D, o && (this._$AH = e); - else { - const l = e; - let a, p; - for (e = n[0], a = 0; a < n.length - 1; a++) p = M(this, l[i + a], t, a), p === D && (p = this._$AH[a]), o || (o = !q(p) || p !== this._$AH[a]), p === d ? e = d : e !== d && (e += (p ?? "") + n[a + 1]), this._$AH[a] = p; - } - o && !r && this.j(e); - } - j(e) { - e === d ? this.element.removeAttribute(this.name) : this.element.setAttribute(this.name, e ?? ""); - } -} -class Ne extends Q { - constructor() { - super(...arguments), this.type = 3; - } - j(e) { - this.element[this.name] = e === d ? void 0 : e; - } -} -class Ie extends Q { - constructor() { - super(...arguments), this.type = 4; - } - j(e) { - this.element.toggleAttribute(this.name, !!e && e !== d); - } -} -class qe extends Q { - constructor(e, t, i, r, n) { - super(e, t, i, r, n), this.type = 5; - } - _$AI(e, t = this) { - if ((e = M(this, e, t, 0) ?? d) === D) return; - const i = this._$AH, r = e === d && i !== d || e.capture !== i.capture || e.once !== i.once || e.passive !== i.passive, n = e !== d && (i === d || r); - r && this.element.removeEventListener(this.name, this, i), n && this.element.addEventListener(this.name, this, e), this._$AH = e; - } - handleEvent(e) { - var t; - typeof this._$AH == "function" ? this._$AH.call(((t = this.options) == null ? void 0 : t.host) ?? this.element, e) : this._$AH.handleEvent(e); - } -} -class Le { - constructor(e, t, i) { - this.element = e, this.type = 6, this._$AN = void 0, this._$AM = t, this.options = i; - } - get _$AU() { - return this._$AM._$AU; - } - _$AI(e) { - M(this, e); - } -} -const te = N.litHtmlPolyfillSupport; -te == null || te(L, W), (N.litHtmlVersions ?? (N.litHtmlVersions = [])).push("3.3.2"); -const Be = (s, e, t) => { - const i = (t == null ? void 0 : t.renderBefore) ?? e; - let r = i._$litPart$; - if (r === void 0) { - const n = (t == null ? void 0 : t.renderBefore) ?? null; - i._$litPart$ = r = new W(e.insertBefore(I(), n), n, void 0, t ?? {}); - } - return r._$AI(s), r; -}; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const U = globalThis; -class v extends z { - constructor() { - super(...arguments), this.renderOptions = { host: this }, this._$Do = void 0; - } - createRenderRoot() { - var t; - const e = super.createRenderRoot(); - return (t = this.renderOptions).renderBefore ?? (t.renderBefore = e.firstChild), e; - } - update(e) { - const t = this.render(); - this.hasUpdated || (this.renderOptions.isConnected = this.isConnected), super.update(e), this._$Do = Be(t, this.renderRoot, this.renderOptions); - } - connectedCallback() { - var e; - super.connectedCallback(), (e = this._$Do) == null || e.setConnected(!0); - } - disconnectedCallback() { - var e; - super.disconnectedCallback(), (e = this._$Do) == null || e.setConnected(!1); - } - render() { - return D; - } -} -var ye; -v._$litElement$ = !0, v.finalized = !0, (ye = U.litElementHydrateSupport) == null || ye.call(U, { LitElement: v }); -const se = U.litElementPolyfillSupport; -se == null || se({ LitElement: v }); -(U.litElementVersions ?? (U.litElementVersions = [])).push("4.2.2"); -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const K = (s) => (e, t) => { - t !== void 0 ? t.addInitializer(() => { - customElements.define(s, e); - }) : customElements.define(s, e); -}; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const Fe = { attribute: !0, type: String, converter: Z, reflect: !1, hasChanged: oe }, We = (s = Fe, e, t) => { - const { kind: i, metadata: r } = t; - let n = globalThis.litPropertyMetadata.get(r); - if (n === void 0 && globalThis.litPropertyMetadata.set(r, n = /* @__PURE__ */ new Map()), i === "setter" && ((s = Object.create(s)).wrapped = !0), n.set(t.name, s), i === "accessor") { - const { name: o } = t; - return { set(l) { - const a = e.get.call(this); - e.set.call(this, l), this.requestUpdate(o, a, s, !0, l); - }, init(l) { - return l !== void 0 && this.C(o, void 0, s, l), l; - } }; - } - if (i === "setter") { - const { name: o } = t; - return function(l) { - const a = this[o]; - e.call(this, l), this.requestUpdate(o, a, s, !0, l); - }; - } - throw Error("Unsupported decorator location: " + i); -}; -function f(s) { - return (e, t) => typeof t == "object" ? We(s, e, t) : ((i, r, n) => { - const o = r.hasOwnProperty(n); - return r.constructor.createProperty(n, i), o ? Object.getOwnPropertyDescriptor(r, n) : void 0; - })(s, e, t); -} -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -function u(s) { - return f({ ...s, state: !0, attribute: !1 }); -} -function ae(s, e) { - const t = new WebSocket(s); - return t.onmessage = (i) => { - var r, n, o, l; - try { - const a = JSON.parse(i.data); - ((n = (r = a.type) == null ? void 0 : r.startsWith) != null && n.call(r, "process.") || (l = (o = a.channel) == null ? void 0 : o.startsWith) != null && l.call(o, "process.")) && e(a); - } catch { - } - }, t; -} -class B { - constructor(e = "") { - this.baseUrl = e; - } - get base() { - return `${this.baseUrl}/api/process`; - } - async request(e, t) { - var n; - const r = await (await fetch(`${this.base}${e}`, t)).json(); - if (!r.success) - throw new Error(((n = r.error) == null ? void 0 : n.message) ?? "Request failed"); - return r.data; - } - /** List all alive daemons from the registry. */ - listDaemons() { - return this.request("/daemons"); - } - /** Get a single daemon entry by code and daemon name. */ - getDaemon(e, t) { - return this.request(`/daemons/${e}/${t}`); - } - /** Stop a daemon (SIGTERM + unregister). */ - stopDaemon(e, t) { - return this.request(`/daemons/${e}/${t}/stop`, { - method: "POST" - }); - } - /** Check daemon health endpoint. */ - healthCheck(e, t) { - return this.request(`/daemons/${e}/${t}/health`); - } - /** List all managed processes. */ - listProcesses(e = !1) { - const t = e ? "?runningOnly=true" : ""; - return this.request(`/processes${t}`); - } - /** Get a single managed process by ID. */ - getProcess(e) { - return this.request(`/processes/${e}`); - } - /** Get the captured stdout/stderr for a managed process by ID. */ - getProcessOutput(e) { - return this.request(`/processes/${e}/output`); - } - /** Start a managed process asynchronously. */ - startProcess(e) { - return this.request("/processes", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(e) - }); - } - /** Run a managed process synchronously and return its combined output. */ - runProcess(e) { - return this.request("/processes/run", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(e) - }); - } - /** Wait for a managed process to exit and return its final snapshot. */ - waitProcess(e) { - return this.request(`/processes/${e}/wait`, { - method: "POST" - }); - } - /** Write input to a managed process stdin pipe. */ - inputProcess(e, t) { - return this.request(`/processes/${e}/input`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ input: t }) - }); - } - /** Close a managed process stdin pipe. */ - closeProcessStdin(e) { - return this.request(`/processes/${e}/close-stdin`, { - method: "POST" - }); - } - /** Kill a managed process by ID. */ - killProcess(e) { - return this.request(`/processes/${e}/kill`, { - method: "POST" - }); - } - /** Send a signal to a managed process by ID. */ - signalProcess(e, t) { - return this.request(`/processes/${e}/signal`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ signal: String(t) }) - }); - } - /** Run a process pipeline using the configured runner. */ - runPipeline(e, t) { - return this.request("/pipelines/run", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ mode: e, specs: t }) - }); - } -} -var Ke = Object.defineProperty, Je = Object.getOwnPropertyDescriptor, A = (s, e, t, i) => { - for (var r = i > 1 ? void 0 : i ? Je(e, t) : e, n = s.length - 1, o; n >= 0; n--) - (o = s[n]) && (r = (i ? o(e, t, r) : o(r)) || r); - return i && r && Ke(e, t, r), r; -}; -let b = class extends v { - constructor() { - super(...arguments), this.apiUrl = "", this.daemons = [], this.loading = !0, this.error = "", this.stopping = /* @__PURE__ */ new Set(), this.checking = /* @__PURE__ */ new Set(), this.healthResults = /* @__PURE__ */ new Map(); - } - connectedCallback() { - super.connectedCallback(), this.api = new B(this.apiUrl), this.loadDaemons(); - } - async loadDaemons() { - this.loading = !0, this.error = ""; - try { - this.daemons = await this.api.listDaemons(); - } catch (s) { - this.error = s.message ?? "Failed to load daemons"; - } finally { - this.loading = !1; - } - } - daemonKey(s) { - return `${s.code}/${s.daemon}`; - } - async handleStop(s) { - const e = this.daemonKey(s); - this.stopping = /* @__PURE__ */ new Set([...this.stopping, e]); - try { - await this.api.stopDaemon(s.code, s.daemon), this.dispatchEvent( - new CustomEvent("daemon-stopped", { - detail: { code: s.code, daemon: s.daemon }, - bubbles: !0 - }) - ), await this.loadDaemons(); - } catch (t) { - this.error = t.message ?? "Failed to stop daemon"; - } finally { - const t = new Set(this.stopping); - t.delete(e), this.stopping = t; - } - } - async handleHealthCheck(s) { - const e = this.daemonKey(s); - this.checking = /* @__PURE__ */ new Set([...this.checking, e]); - try { - const t = await this.api.healthCheck(s.code, s.daemon), i = new Map(this.healthResults); - i.set(e, t), this.healthResults = i; - } catch (t) { - this.error = t.message ?? "Health check failed"; - } finally { - const t = new Set(this.checking); - t.delete(e), this.checking = t; - } - } - formatDate(s) { - try { - return new Date(s).toLocaleDateString("en-GB", { - day: "numeric", - month: "short", - year: "numeric", - hour: "2-digit", - minute: "2-digit" - }); - } catch { - return s; - } - } - renderHealthBadge(s) { - const e = this.daemonKey(s); - if (this.checking.has(e)) - return c`Checking\u2026`; - const t = this.healthResults.get(e); - return t ? c` - ${t.healthy ? "Healthy" : "Unhealthy"} - ` : s.health ? c`Unchecked` : c`No health endpoint`; - } - render() { - return this.loading ? c`
result property.
-