happy2 0.0.11 → 0.0.12
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/README.md +10 -0
- package/package.json +1 -1
- package/packages/happy2-server/PLUGINS.md +106 -34
- package/packages/happy2-server/README.md +11 -0
- package/packages/happy2-server/dist/daemon.d.ts +39 -0
- package/packages/happy2-server/dist/daemon.d.ts.map +1 -0
- package/packages/happy2-server/dist/daemon.js +302 -0
- package/packages/happy2-server/dist/daemon.js.map +1 -0
- package/packages/happy2-server/dist/daemon.test.d.ts +2 -0
- package/packages/happy2-server/dist/daemon.test.d.ts.map +1 -0
- package/packages/happy2-server/dist/daemon.test.js +245 -0
- package/packages/happy2-server/dist/daemon.test.js.map +1 -0
- package/packages/happy2-server/dist/index.d.ts +1 -1
- package/packages/happy2-server/dist/index.d.ts.map +1 -1
- package/packages/happy2-server/dist/index.js +1 -1
- package/packages/happy2-server/dist/index.js.map +1 -1
- package/packages/happy2-server/dist/modules/auth/tokens.d.ts +7 -0
- package/packages/happy2-server/dist/modules/auth/tokens.d.ts.map +1 -1
- package/packages/happy2-server/dist/modules/auth/tokens.js +33 -0
- package/packages/happy2-server/dist/modules/auth/tokens.js.map +1 -1
- package/packages/happy2-server/dist/modules/auth/tokens.test.d.ts +2 -0
- package/packages/happy2-server/dist/modules/auth/tokens.test.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/auth/tokens.test.js +28 -0
- package/packages/happy2-server/dist/modules/auth/tokens.test.js.map +1 -0
- package/packages/happy2-server/dist/modules/config/defaults.d.ts.map +1 -1
- package/packages/happy2-server/dist/modules/config/defaults.js +2 -0
- package/packages/happy2-server/dist/modules/config/defaults.js.map +1 -1
- package/packages/happy2-server/dist/modules/config/loader.d.ts.map +1 -1
- package/packages/happy2-server/dist/modules/config/loader.js +5 -0
- package/packages/happy2-server/dist/modules/config/loader.js.map +1 -1
- package/packages/happy2-server/dist/modules/config/loader.test.js +15 -0
- package/packages/happy2-server/dist/modules/config/loader.test.js.map +1 -1
- package/packages/happy2-server/dist/modules/config/type.d.ts +2 -0
- package/packages/happy2-server/dist/modules/config/type.d.ts.map +1 -1
- package/packages/happy2-server/dist/modules/plugin/catalog.d.ts.map +1 -1
- package/packages/happy2-server/dist/modules/plugin/catalog.js +45 -4
- package/packages/happy2-server/dist/modules/plugin/catalog.js.map +1 -1
- package/packages/happy2-server/dist/modules/plugin/catalog.test.js +33 -0
- package/packages/happy2-server/dist/modules/plugin/catalog.test.js.map +1 -1
- package/packages/happy2-server/dist/modules/plugin/impl/asPlugin.d.ts.map +1 -1
- package/packages/happy2-server/dist/modules/plugin/impl/asPlugin.js +14 -1
- package/packages/happy2-server/dist/modules/plugin/impl/asPlugin.js.map +1 -1
- package/packages/happy2-server/dist/modules/plugin/impl/effectiveContainer.d.ts +3 -0
- package/packages/happy2-server/dist/modules/plugin/impl/effectiveContainer.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/plugin/impl/effectiveContainer.js +12 -0
- package/packages/happy2-server/dist/modules/plugin/impl/effectiveContainer.js.map +1 -0
- package/packages/happy2-server/dist/modules/plugin/impl/installedManifest.d.ts.map +1 -1
- package/packages/happy2-server/dist/modules/plugin/impl/installedManifest.js +11 -0
- package/packages/happy2-server/dist/modules/plugin/impl/installedManifest.js.map +1 -1
- package/packages/happy2-server/dist/modules/plugin/pluginCatalogList.d.ts.map +1 -1
- package/packages/happy2-server/dist/modules/plugin/pluginCatalogList.js +15 -1
- package/packages/happy2-server/dist/modules/plugin/pluginCatalogList.js.map +1 -1
- package/packages/happy2-server/dist/modules/plugin/pluginContainerInstanceAuthorize.d.ts +10 -0
- package/packages/happy2-server/dist/modules/plugin/pluginContainerInstanceAuthorize.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/plugin/pluginContainerInstanceAuthorize.js +20 -0
- package/packages/happy2-server/dist/modules/plugin/pluginContainerInstanceAuthorize.js.map +1 -0
- package/packages/happy2-server/dist/modules/plugin/pluginContainerInstanceInvalidate.d.ts +12 -0
- package/packages/happy2-server/dist/modules/plugin/pluginContainerInstanceInvalidate.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/plugin/pluginContainerInstanceInvalidate.js +44 -0
- package/packages/happy2-server/dist/modules/plugin/pluginContainerInstanceInvalidate.js.map +1 -0
- package/packages/happy2-server/dist/modules/plugin/pluginInstall.d.ts.map +1 -1
- package/packages/happy2-server/dist/modules/plugin/pluginInstall.js +10 -5
- package/packages/happy2-server/dist/modules/plugin/pluginInstall.js.map +1 -1
- package/packages/happy2-server/dist/modules/plugin/pluginInstallationGetRuntimeConfiguration.d.ts.map +1 -1
- package/packages/happy2-server/dist/modules/plugin/pluginInstallationGetRuntimeConfiguration.js +18 -7
- package/packages/happy2-server/dist/modules/plugin/pluginInstallationGetRuntimeConfiguration.js.map +1 -1
- package/packages/happy2-server/dist/modules/plugin/pluginInstallationListForHost.d.ts +14 -0
- package/packages/happy2-server/dist/modules/plugin/pluginInstallationListForHost.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/plugin/pluginInstallationListForHost.js +20 -0
- package/packages/happy2-server/dist/modules/plugin/pluginInstallationListForHost.js.map +1 -0
- package/packages/happy2-server/dist/modules/plugin/pluginInstallationUpdateStatus.d.ts +1 -0
- package/packages/happy2-server/dist/modules/plugin/pluginInstallationUpdateStatus.d.ts.map +1 -1
- package/packages/happy2-server/dist/modules/plugin/pluginInstallationUpdateStatus.js +1 -0
- package/packages/happy2-server/dist/modules/plugin/pluginInstallationUpdateStatus.js.map +1 -1
- package/packages/happy2-server/dist/modules/plugin/pluginMcpToolsList.d.ts +11 -0
- package/packages/happy2-server/dist/modules/plugin/pluginMcpToolsList.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/plugin/pluginMcpToolsList.js +43 -0
- package/packages/happy2-server/dist/modules/plugin/pluginMcpToolsList.js.map +1 -0
- package/packages/happy2-server/dist/modules/plugin/pluginMcpToolsListReady.d.ts +15 -0
- package/packages/happy2-server/dist/modules/plugin/pluginMcpToolsListReady.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/plugin/pluginMcpToolsListReady.js +37 -0
- package/packages/happy2-server/dist/modules/plugin/pluginMcpToolsListReady.js.map +1 -0
- package/packages/happy2-server/dist/modules/plugin/pluginMcpToolsReplace.d.ts +16 -0
- package/packages/happy2-server/dist/modules/plugin/pluginMcpToolsReplace.d.ts.map +1 -0
- package/packages/happy2-server/dist/modules/plugin/pluginMcpToolsReplace.js +71 -0
- package/packages/happy2-server/dist/modules/plugin/pluginMcpToolsReplace.js.map +1 -0
- package/packages/happy2-server/dist/modules/plugin/runtime.d.ts +19 -0
- package/packages/happy2-server/dist/modules/plugin/runtime.d.ts.map +1 -1
- package/packages/happy2-server/dist/modules/plugin/runtime.js +87 -2
- package/packages/happy2-server/dist/modules/plugin/runtime.js.map +1 -1
- package/packages/happy2-server/dist/modules/plugin/service.d.ts +12 -4
- package/packages/happy2-server/dist/modules/plugin/service.d.ts.map +1 -1
- package/packages/happy2-server/dist/modules/plugin/service.js +216 -170
- package/packages/happy2-server/dist/modules/plugin/service.js.map +1 -1
- package/packages/happy2-server/dist/modules/plugin/types.d.ts +40 -3
- package/packages/happy2-server/dist/modules/plugin/types.d.ts.map +1 -1
- package/packages/happy2-server/dist/modules/plugin/types.js +1 -0
- package/packages/happy2-server/dist/modules/plugin/types.js.map +1 -1
- package/packages/happy2-server/dist/modules/sandbox/index.d.ts +1 -1
- package/packages/happy2-server/dist/modules/sandbox/index.d.ts.map +1 -1
- package/packages/happy2-server/dist/modules/sandbox/localOciSandboxProvider.d.ts +8 -1
- package/packages/happy2-server/dist/modules/sandbox/localOciSandboxProvider.d.ts.map +1 -1
- package/packages/happy2-server/dist/modules/sandbox/localOciSandboxProvider.js +101 -1
- package/packages/happy2-server/dist/modules/sandbox/localOciSandboxProvider.js.map +1 -1
- package/packages/happy2-server/dist/modules/sandbox/localOciSandboxProvider.test.js +78 -2
- package/packages/happy2-server/dist/modules/sandbox/localOciSandboxProvider.test.js.map +1 -1
- package/packages/happy2-server/dist/modules/sandbox/types.d.ts +16 -0
- package/packages/happy2-server/dist/modules/sandbox/types.d.ts.map +1 -1
- package/packages/happy2-server/dist/modules/schema.d.ts +394 -0
- package/packages/happy2-server/dist/modules/schema.d.ts.map +1 -1
- package/packages/happy2-server/dist/modules/schema.js +15 -0
- package/packages/happy2-server/dist/modules/schema.js.map +1 -1
- package/packages/happy2-server/dist/routes/pluginHost.d.ts +6 -0
- package/packages/happy2-server/dist/routes/pluginHost.d.ts.map +1 -0
- package/packages/happy2-server/dist/routes/pluginHost.js +37 -0
- package/packages/happy2-server/dist/routes/pluginHost.js.map +1 -0
- package/packages/happy2-server/dist/routes/plugins.d.ts.map +1 -1
- package/packages/happy2-server/dist/routes/plugins.js +12 -0
- package/packages/happy2-server/dist/routes/plugins.js.map +1 -1
- package/packages/happy2-server/dist/runner.js +20 -1
- package/packages/happy2-server/dist/runner.js.map +1 -1
- package/packages/happy2-server/dist/server.d.ts +2 -0
- package/packages/happy2-server/dist/server.d.ts.map +1 -1
- package/packages/happy2-server/dist/server.js +20 -1
- package/packages/happy2-server/dist/server.js.map +1 -1
- package/packages/happy2-server/dist/systemService.d.ts.map +1 -1
- package/packages/happy2-server/dist/systemService.js +16 -1
- package/packages/happy2-server/dist/systemService.js.map +1 -1
- package/packages/happy2-server/dist/systemService.test.js +15 -1
- package/packages/happy2-server/dist/systemService.test.js.map +1 -1
- package/packages/happy2-server/drizzle/0022_plugin_container_runtime_tools.sql +16 -0
- package/packages/happy2-server/drizzle/meta/_journal.json +7 -0
- package/packages/happy2-server/happy2.example.toml +8 -0
package/README.md
CHANGED
|
@@ -49,6 +49,16 @@ Open [http://127.0.0.1:3000](http://127.0.0.1:3000) in your browser. Happy (2)
|
|
|
49
49
|
stores its database, files, generated secrets, agent workspaces, and private Rig
|
|
50
50
|
runtime under `.happy2` in the directory where you start it.
|
|
51
51
|
|
|
52
|
+
Run it in the background without installing an operating-system service with:
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
npx happy2 daemon start
|
|
56
|
+
npx happy2 daemon stop
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The daemon stores `happy2.pid` and `happy2.log` under `./.happy2`. Stopping it
|
|
60
|
+
terminates the daemon process tree and removes the PID file.
|
|
61
|
+
|
|
52
62
|
Keep Happy (2) running across reboots with:
|
|
53
63
|
|
|
54
64
|
```sh
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Server plugins
|
|
2
2
|
|
|
3
|
-
Happy (2) plugins package Agent Skills,
|
|
3
|
+
Happy (2) plugins package Agent Skills, a persistent container command, an MCP
|
|
4
|
+
server, or any useful combination of those pieces. This first
|
|
4
5
|
server-side implementation installs only packages bundled with the server. It
|
|
5
6
|
already keeps catalog discovery, durable system plugins, immutable package/image
|
|
6
7
|
snapshots, and independent runtime installations separate so a later remote
|
|
@@ -10,25 +11,30 @@ Plugin management is system-wide and administrator-only. The first installation
|
|
|
10
11
|
of a catalog package creates one durable system-plugin record and one immutable
|
|
11
12
|
package snapshot; later installations reuse that plugin identity and snapshot.
|
|
12
13
|
Every installation has its own CUID2, variables, selected image, lifecycle, and
|
|
13
|
-
dedicated container when it
|
|
14
|
+
dedicated container when it has a local runtime. Remote MCP configuration is
|
|
14
15
|
persisted and health-checked independently per installation. Ready MCP
|
|
15
|
-
installations expose their tools to Rig as
|
|
16
|
-
agent submission. Happy executes each durable call against the originating
|
|
16
|
+
installations expose their durably cached tools to Rig as external functions on
|
|
17
|
+
every agent submission. Happy executes each durable call against the originating
|
|
17
18
|
installation and resolves the result back into the paused Rig run. This feature
|
|
18
19
|
does not yet inject installed skills, and it does not yet implement upgrade,
|
|
19
20
|
uninstall, marketplace download, or OAuth flows.
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
22
|
+
MCP tools are discovered during each runtime activation and atomically replace
|
|
23
|
+
that installation's SQLite cache before it becomes ready. Local discovery runs
|
|
24
|
+
after every container creation, and remote discovery runs on every server
|
|
25
|
+
restart. Rig function discovery reads only this cache rather than opening every
|
|
26
|
+
MCP server again per submission. Each discovery page is bounded to 15
|
|
27
|
+
seconds, and one MCP tool execution is bounded to 30 seconds so a stalled plugin
|
|
28
|
+
cannot indefinitely block turn submission or Rig's global event consumer. Before
|
|
29
|
+
execution, Happy claims a 45-second database lease keyed by Rig session and call
|
|
30
|
+
ID. Other server instances wait for that lease or replay its completed result
|
|
31
|
+
instead of concurrently invoking the same tool. The first terminal outcome,
|
|
32
|
+
including an MCP error or timeout, is persisted and reused for every later event
|
|
33
|
+
delivery; Happy does not automatically retry an ambiguous failure because the
|
|
34
|
+
first request may already have produced an external side effect. A process crash
|
|
35
|
+
after the MCP side effect but before the outcome is committed can still cause one
|
|
36
|
+
lease-expiry replay, so plugin tools with external side effects should themselves
|
|
37
|
+
be idempotent.
|
|
32
38
|
|
|
33
39
|
Rig sessions use Full access so durable external functions are executable without
|
|
34
40
|
an unresolved permission prompt. Agent code still runs inside Happy's dedicated,
|
|
@@ -91,7 +97,13 @@ available.
|
|
|
91
97
|
"description": "Region used for project queries.",
|
|
92
98
|
"kind": "text"
|
|
93
99
|
}
|
|
94
|
-
]
|
|
100
|
+
],
|
|
101
|
+
"container": {
|
|
102
|
+
"dockerfile": "container/Dockerfile",
|
|
103
|
+
"command": "/plugin/bin/indexer",
|
|
104
|
+
"args": ["--watch"],
|
|
105
|
+
"permissions": ["plugins:list"]
|
|
106
|
+
}
|
|
95
107
|
}
|
|
96
108
|
```
|
|
97
109
|
|
|
@@ -101,10 +113,18 @@ The durable system plugin and every installation receive separate CUID2 IDs.
|
|
|
101
113
|
Variable keys are environment variable names. Every declared variable is
|
|
102
114
|
required for each installation. Secret values are encrypted with AES-256-GCM
|
|
103
115
|
and are never returned by catalog or installation reads; text values are stored
|
|
104
|
-
as ordinary configuration. Both kinds are supplied to
|
|
105
|
-
environment variables.
|
|
116
|
+
as ordinary configuration. Both kinds are supplied to configured local
|
|
117
|
+
processes as environment variables.
|
|
118
|
+
|
|
119
|
+
A package must contain at least one skill, `container`, or `mcp` definition.
|
|
120
|
+
`container.command` is optional when the same container exposes a stdio MCP;
|
|
121
|
+
otherwise it is required. A command and stdio MCP run alongside each other in
|
|
122
|
+
the same dedicated installation container. Container variables are supplied to
|
|
123
|
+
each configured process, not persisted in the image or container definition.
|
|
106
124
|
|
|
107
|
-
|
|
125
|
+
`container.permissions` is an exact allowlist of host API capabilities. The
|
|
126
|
+
currently supported permission is `plugins:list`. Unknown and duplicate
|
|
127
|
+
permissions are rejected when the package is loaded.
|
|
108
128
|
|
|
109
129
|
The bundled `hello` package is the minimal skill-plus-MCP example. It declares no
|
|
110
130
|
variables or MCP authentication, so an administrator can install it with an
|
|
@@ -118,10 +138,12 @@ container.
|
|
|
118
138
|
"mcp": {
|
|
119
139
|
"type": "stdio",
|
|
120
140
|
"command": "/plugin/bin/project-mcp",
|
|
121
|
-
"args": ["--stdio"]
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
141
|
+
"args": ["--stdio"]
|
|
142
|
+
},
|
|
143
|
+
"container": {
|
|
144
|
+
"dockerfile": "container/Dockerfile",
|
|
145
|
+
"args": [],
|
|
146
|
+
"permissions": []
|
|
125
147
|
}
|
|
126
148
|
}
|
|
127
149
|
```
|
|
@@ -146,22 +168,29 @@ The fixed `HOME`, `TMPDIR`, and working directory are `/tmp`, so tools that
|
|
|
146
168
|
need a cache can still run without making the image root writable.
|
|
147
169
|
|
|
148
170
|
Each installation container stays alive as that installation's plugin runtime.
|
|
149
|
-
|
|
171
|
+
The optional persistent command is started detached once for each container
|
|
172
|
+
incarnation and monitored through a PID marker in the container's ephemeral
|
|
173
|
+
`/run`. Server restart recovery adopts and resumes monitoring that same command
|
|
174
|
+
without double-starting it. Each HTTP MCP session starts
|
|
150
175
|
the configured command with `docker exec`/`podman exec`; variables reach that
|
|
151
176
|
process through Docker/Podman's environment-copy option. Values are never placed
|
|
152
177
|
in command arguments, Happy's process environment, the long-lived container
|
|
153
178
|
definition, or the image build; they exist only in the short-lived OCI CLI child
|
|
154
|
-
and the MCP process. Variables that could alter the OCI client itself, such as
|
|
179
|
+
and the target persistent-command or MCP process. Variables that could alter the OCI client itself, such as
|
|
155
180
|
`DOCKER_*`, proxy, loader, or executable-path settings, are rejected. Happy
|
|
156
181
|
transparently bridges newline-delimited stdio JSON-RPC to MCP Streamable HTTP, so
|
|
157
182
|
the plugin itself does not need an HTTP server.
|
|
158
183
|
|
|
159
184
|
## Stdio MCP using a selected container image
|
|
160
185
|
|
|
161
|
-
Omit `
|
|
186
|
+
Omit `container.dockerfile` when the plugin does not bundle its own Dockerfile:
|
|
162
187
|
|
|
163
188
|
```json
|
|
164
189
|
{
|
|
190
|
+
"container": {
|
|
191
|
+
"args": [],
|
|
192
|
+
"permissions": []
|
|
193
|
+
},
|
|
165
194
|
"mcp": {
|
|
166
195
|
"type": "stdio",
|
|
167
196
|
"command": "npx",
|
|
@@ -220,8 +249,8 @@ transport used for SSRF-safe address pinning.
|
|
|
220
249
|
The request body may be omitted when the manifest declares no variables and
|
|
221
250
|
does not require a selected container image.
|
|
222
251
|
|
|
223
|
-
`containerImageId` is required
|
|
224
|
-
|
|
252
|
+
`containerImageId` is required for every local container manifest without a
|
|
253
|
+
bundled Dockerfile, and it is rejected in every other case. Unknown, missing, empty, or
|
|
225
254
|
oversized variable values are rejected. The endpoint returns HTTP 202 after the
|
|
226
255
|
durable system plugin (created once), immutable package/image snapshot, new
|
|
227
256
|
installation, variables, audit entry, initial state, and sync event are durable.
|
|
@@ -240,9 +269,10 @@ An installation has one of these durable health states:
|
|
|
240
269
|
- `preparing`: copying/reconciling package and image/container state.
|
|
241
270
|
- `starting`: the container exists or remote endpoint is selected and MCP
|
|
242
271
|
initialization/health checking is in progress.
|
|
243
|
-
- `ready`:
|
|
244
|
-
|
|
245
|
-
|
|
272
|
+
- `ready`: a container-only command survived its startup probe; or a local or
|
|
273
|
+
remote MCP server completed initialization, ping, and durable tool discovery.
|
|
274
|
+
A skills-only plugin becomes ready immediately
|
|
275
|
+
after its durable install.
|
|
246
276
|
- `broken_configuration`: stored variables, selected image state, manifest
|
|
247
277
|
material, or resolved headers cannot form a valid runtime configuration.
|
|
248
278
|
- `failed`: package integrity, image build, container creation, process startup,
|
|
@@ -253,8 +283,10 @@ Every transition updates `plugin_installations`, appends a `plugin.*` sync
|
|
|
253
283
|
event, and publishes the normal server SSE hint with the `plugins` area. Clients
|
|
254
284
|
must reconcile the durable catalog after a hint; the event itself is not state.
|
|
255
285
|
On server restart, every installation is reconciled again. Its package path and
|
|
256
|
-
SHA-256 digest are revalidated first;
|
|
257
|
-
|
|
286
|
+
SHA-256 digest are revalidated first; a running local container with matching
|
|
287
|
+
installation and incarnation labels is adopted, otherwise it is recreated from
|
|
288
|
+
the installed snapshot. Adopted persistent commands resume liveness monitoring.
|
|
289
|
+
Remote endpoints are rechecked.
|
|
258
290
|
|
|
259
291
|
## Read and MCP endpoints
|
|
260
292
|
|
|
@@ -277,6 +309,44 @@ the installed snapshot, and remote endpoints are rechecked.
|
|
|
277
309
|
Streamable HTTP bridge for one ready local stdio installation. It follows MCP
|
|
278
310
|
session semantics via `Mcp-Session-Id`. Happy’s existing bearer session is
|
|
279
311
|
required; there is no plugin-specific OAuth exchange.
|
|
312
|
+
- `GET /v0/admin/pluginInstallations/:installationId/mcpTools` returns the last
|
|
313
|
+
successfully synchronized MCP tool schemas from SQLite. It never contacts the
|
|
314
|
+
MCP server. Tool discovery is replaced atomically on every runtime activation,
|
|
315
|
+
including each server restart.
|
|
316
|
+
- `GET /plugins` on the dedicated plugin host listener is the first
|
|
317
|
+
capability-scoped host API. It is deliberately absent from the product API
|
|
318
|
+
listener. A
|
|
319
|
+
container may call it only when its manifest grants `plugins:list` and it
|
|
320
|
+
presents the incarnation token supplied as `HAPPY2_PLUGIN_API_TOKEN`.
|
|
321
|
+
|
|
322
|
+
Container processes receive `HAPPY2_PLUGIN_API_URL` and
|
|
323
|
+
`HAPPY2_PLUGIN_API_TOKEN`. The URL is always
|
|
324
|
+
`http://happy2.host.internal:<plugins.host_api_port>`; the hardened container
|
|
325
|
+
adds `happy2.host.internal:host-gateway`, while the capability-only listener
|
|
326
|
+
binds `plugins.host_api_host` on that fixed port. HTTP/TCP and the OCI
|
|
327
|
+
`host-gateway` mapping work across Docker and Podman on macOS and Linux.
|
|
328
|
+
The cross-platform default bind is `0.0.0.0` because a loopback bind is not
|
|
329
|
+
reachable through Docker Desktop's or Podman's host gateway. Operators who
|
|
330
|
+
expose the host to an untrusted LAN should firewall
|
|
331
|
+
`plugins.host_api_port`; it is a container capability endpoint, not a public
|
|
332
|
+
service.
|
|
333
|
+
|
|
334
|
+
The token is an RS256 capability containing the installation ID, a random CUID2
|
|
335
|
+
container-incarnation ID, and the exact manifest permissions. Token bytes are
|
|
336
|
+
never stored. The incarnation ID is stored in `plugin_installations` and also
|
|
337
|
+
attached to the OCI container as `dev.happy2.plugin-instance`. On each request,
|
|
338
|
+
Happy verifies the signature, matches the incarnation against the ready database
|
|
339
|
+
row, and confirms that the correspondingly labelled container is running. A
|
|
340
|
+
stopped, missing, or replaced container therefore receives 403. A surviving
|
|
341
|
+
container and its token remain valid after a server restart; startup adopts the
|
|
342
|
+
matching container and refreshes its MCP tool cache instead of recreating it.
|
|
343
|
+
The token intentionally has no time expiration: its lifetime is exactly the
|
|
344
|
+
database-and-OCI incarnation lifetime, allowing a command to survive arbitrarily
|
|
345
|
+
many server restarts without persisting or rotating token bytes. Killing,
|
|
346
|
+
replacing, failing, or removing that incarnation immediately makes the token
|
|
347
|
+
unauthorized.
|
|
348
|
+
The capability is not a user session and the dedicated listener exposes no
|
|
349
|
+
ordinary `/v0` APIs.
|
|
280
350
|
|
|
281
351
|
The bridge allows at most 128 simultaneous sessions server-wide and 16 per
|
|
282
352
|
authenticated user. Idle sessions close after 15 minutes; inbound requests and
|
|
@@ -297,7 +367,9 @@ thumbhash, and SHA-256 checksum.
|
|
|
297
367
|
|
|
298
368
|
`plugin_installations` records a separate CUID2 and foreign key to `plugins`,
|
|
299
369
|
plus that instance's container/image choice, lifecycle state, error detail,
|
|
300
|
-
installer, and timestamps. `
|
|
370
|
+
installer, tool-sync timestamp, and timestamps. `plugin_mcp_tools` contains the
|
|
371
|
+
last complete MCP discovery keyed by installation and tool name.
|
|
372
|
+
`plugin_installation_variables` records each declared
|
|
301
373
|
value for one installation; secret rows contain authenticated ciphertext rather
|
|
302
374
|
than plaintext. No installation uses `shortName` as identity.
|
|
303
375
|
|
|
@@ -18,6 +18,10 @@ npx happy2
|
|
|
18
18
|
npx happy2 service start --config ./happy2.toml
|
|
19
19
|
npx happy2 service stop
|
|
20
20
|
|
|
21
|
+
# Start or stop it as a background process in the current directory
|
|
22
|
+
npx happy2 daemon start --config ./happy2.toml
|
|
23
|
+
npx happy2 daemon stop
|
|
24
|
+
|
|
21
25
|
# Development, with reload and no configuration file:
|
|
22
26
|
pnpm dev:server
|
|
23
27
|
|
|
@@ -62,6 +66,13 @@ reinstallation. `happy2 service stop` directly stops and removes the macOS Launc
|
|
|
62
66
|
while on Linux it prints the `sudo` commands that stop, disable, and remove the
|
|
63
67
|
systemd unit.
|
|
64
68
|
|
|
69
|
+
`happy2 daemon start` starts the all-in-one app in a detached process group and
|
|
70
|
+
returns immediately. It writes `.happy2/happy2.pid` and appends output to
|
|
71
|
+
`.happy2/happy2.log` in the current directory. `happy2 daemon stop` sends the
|
|
72
|
+
whole process group a graceful termination signal, force-stops it if necessary,
|
|
73
|
+
and removes the PID file. Unlike `service start`, daemon mode does not arrange
|
|
74
|
+
automatic startup after a login or reboot.
|
|
75
|
+
|
|
65
76
|
The service preserves the installation-time working directory, `PATH`, and
|
|
66
77
|
optional `RIG_HOME`. When `--config` is supplied, its path is made
|
|
67
78
|
absolute before installation. Put service secrets and SMTP environment values
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export interface DaemonHost {
|
|
2
|
+
cwd: string;
|
|
3
|
+
environment: NodeJS.ProcessEnv;
|
|
4
|
+
executablePath: string;
|
|
5
|
+
executableArguments: readonly string[];
|
|
6
|
+
scriptPath: string;
|
|
7
|
+
directoryCreate(path: string): Promise<void>;
|
|
8
|
+
fileCreate(path: string, contents: string): Promise<boolean>;
|
|
9
|
+
fileExists(path: string): Promise<boolean>;
|
|
10
|
+
fileRead(path: string): Promise<string>;
|
|
11
|
+
fileRemove(path: string): Promise<void>;
|
|
12
|
+
processStart(input: {
|
|
13
|
+
arguments_: readonly string[];
|
|
14
|
+
cwd: string;
|
|
15
|
+
environment: NodeJS.ProcessEnv;
|
|
16
|
+
executablePath: string;
|
|
17
|
+
logPath: string;
|
|
18
|
+
pidPath: string;
|
|
19
|
+
}): Promise<number>;
|
|
20
|
+
processAlive(pid: number): boolean;
|
|
21
|
+
processTreeAlive(pid: number): boolean;
|
|
22
|
+
processTreeSignal(pid: number, signal: NodeJS.Signals): void;
|
|
23
|
+
wait(milliseconds: number): Promise<void>;
|
|
24
|
+
log(message: string): void;
|
|
25
|
+
}
|
|
26
|
+
export type ParsedDaemonCommand = {
|
|
27
|
+
action: "help" | "invalid" | "stop";
|
|
28
|
+
} | {
|
|
29
|
+
action: "start";
|
|
30
|
+
configPath?: string;
|
|
31
|
+
};
|
|
32
|
+
export declare function parseDaemonCommand(arguments_: readonly string[]): ParsedDaemonCommand;
|
|
33
|
+
export declare function daemonStart(options: {
|
|
34
|
+
configPath?: string;
|
|
35
|
+
}, host?: DaemonHost): Promise<void>;
|
|
36
|
+
export declare function daemonStop(host?: DaemonHost): Promise<void>;
|
|
37
|
+
export declare function daemonUsage(): string;
|
|
38
|
+
export declare function createDaemonHost(): DaemonHost;
|
|
39
|
+
//# sourceMappingURL=daemon.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../sources/daemon.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,UAAU;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC,UAAU,CAAC;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7D,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACxC,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,YAAY,CAAC,KAAK,EAAE;QAChB,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;QAC9B,GAAG,EAAE,MAAM,CAAC;QACZ,WAAW,EAAE,MAAM,CAAC,UAAU,CAAC;QAC/B,cAAc,EAAE,MAAM,CAAC;QACvB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACpB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACnC,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvC,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;IAC7D,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,MAAM,mBAAmB,GACzB;IAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAAA;CAAE,GACvC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/C,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,GAAG,mBAAmB,CAgCrF;AAED,wBAAsB,WAAW,CAC7B,OAAO,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,EAChC,IAAI,GAAE,UAA+B,GACtC,OAAO,CAAC,IAAI,CAAC,CAyCf;AAED,wBAAsB,UAAU,CAAC,IAAI,GAAE,UAA+B,GAAG,OAAO,CAAC,IAAI,CAAC,CA+BrF;AAED,wBAAgB,WAAW,IAAI,MAAM,CAQpC;AA4DD,wBAAgB,gBAAgB,IAAI,UAAU,CAuG7C"}
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { close, open, writeFileSync } from "node:fs";
|
|
3
|
+
import { access, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
4
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
5
|
+
import { parseArgs } from "node:util";
|
|
6
|
+
const daemonDirectoryName = ".happy2";
|
|
7
|
+
const daemonPidFileName = "happy2.pid";
|
|
8
|
+
const daemonLogFileName = "happy2.log";
|
|
9
|
+
const gracefulStopTimeoutMs = 10_000;
|
|
10
|
+
const forcedStopTimeoutMs = 2_000;
|
|
11
|
+
const processExitPollMs = 50;
|
|
12
|
+
export function parseDaemonCommand(arguments_) {
|
|
13
|
+
let positionals;
|
|
14
|
+
let values;
|
|
15
|
+
try {
|
|
16
|
+
const parsed = parseArgs({
|
|
17
|
+
args: [...arguments_],
|
|
18
|
+
allowPositionals: true,
|
|
19
|
+
options: {
|
|
20
|
+
config: { type: "string" },
|
|
21
|
+
help: { type: "boolean", short: "h" },
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
positionals = parsed.positionals;
|
|
25
|
+
values = parsed.values;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return { action: "invalid" };
|
|
29
|
+
}
|
|
30
|
+
const action = positionals[0];
|
|
31
|
+
if ((values.help || action === "help") &&
|
|
32
|
+
positionals.length <= 1 &&
|
|
33
|
+
values.config === undefined) {
|
|
34
|
+
return { action: "help" };
|
|
35
|
+
}
|
|
36
|
+
if (action === "start" && positionals.length === 1) {
|
|
37
|
+
return { action: "start", configPath: values.config };
|
|
38
|
+
}
|
|
39
|
+
if (action === "stop" && positionals.length === 1 && values.config === undefined) {
|
|
40
|
+
return { action: "stop" };
|
|
41
|
+
}
|
|
42
|
+
return { action: "invalid" };
|
|
43
|
+
}
|
|
44
|
+
export async function daemonStart(options, host = createDaemonHost()) {
|
|
45
|
+
const paths = daemonPaths(host.cwd);
|
|
46
|
+
await host.directoryCreate(paths.directory);
|
|
47
|
+
const existing = await pidFileRead(paths.pid, host);
|
|
48
|
+
if (existing.kind === "starting" && host.processAlive(existing.pid)) {
|
|
49
|
+
throw new Error(`Happy (2) daemon is already starting as process ${existing.pid}.`);
|
|
50
|
+
}
|
|
51
|
+
if (existing.kind === "pid" && host.processTreeAlive(existing.pid)) {
|
|
52
|
+
throw new Error(`Happy (2) is already running as daemon process ${existing.pid}.`);
|
|
53
|
+
}
|
|
54
|
+
if (existing.kind !== "missing") {
|
|
55
|
+
await host.fileRemove(paths.pid);
|
|
56
|
+
}
|
|
57
|
+
const configPath = await configPathResolve(options.configPath, host);
|
|
58
|
+
if (!(await host.fileCreate(paths.pid, `starting:${process.pid}\n`))) {
|
|
59
|
+
throw new Error("Happy (2) daemon is already starting.");
|
|
60
|
+
}
|
|
61
|
+
const childArguments = [
|
|
62
|
+
...host.executableArguments,
|
|
63
|
+
host.scriptPath,
|
|
64
|
+
...(configPath ? ["--config", configPath] : []),
|
|
65
|
+
];
|
|
66
|
+
let pid;
|
|
67
|
+
try {
|
|
68
|
+
pid = await host.processStart({
|
|
69
|
+
arguments_: childArguments,
|
|
70
|
+
cwd: host.cwd,
|
|
71
|
+
environment: host.environment,
|
|
72
|
+
executablePath: host.executablePath,
|
|
73
|
+
logPath: paths.log,
|
|
74
|
+
pidPath: paths.pid,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
if (pid !== undefined)
|
|
79
|
+
signalIfAlive(pid, "SIGKILL", host);
|
|
80
|
+
await host.fileRemove(paths.pid);
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
host.log(`Happy (2) daemon started as process ${pid}.`);
|
|
84
|
+
host.log(`PID file: ${paths.pid}`);
|
|
85
|
+
host.log(`Logs: ${paths.log}`);
|
|
86
|
+
}
|
|
87
|
+
export async function daemonStop(host = createDaemonHost()) {
|
|
88
|
+
const { pid: pidPath } = daemonPaths(host.cwd);
|
|
89
|
+
const state = await pidFileRead(pidPath, host);
|
|
90
|
+
if (state.kind === "starting" && host.processAlive(state.pid)) {
|
|
91
|
+
throw new Error(`Happy (2) daemon is still starting as process ${state.pid}.`);
|
|
92
|
+
}
|
|
93
|
+
if (state.kind === "missing" || state.kind === "invalid" || state.kind === "starting") {
|
|
94
|
+
if (state.kind !== "missing")
|
|
95
|
+
await host.fileRemove(pidPath);
|
|
96
|
+
host.log("Happy (2) daemon is not running.");
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const { pid } = state;
|
|
100
|
+
if (!host.processTreeAlive(pid)) {
|
|
101
|
+
await host.fileRemove(pidPath);
|
|
102
|
+
host.log(`Removed stale Happy (2) daemon PID file for process ${pid}.`);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
host.processTreeSignal(pid, "SIGTERM");
|
|
107
|
+
if (!(await processExitWait(pid, gracefulStopTimeoutMs, host))) {
|
|
108
|
+
host.processTreeSignal(pid, "SIGKILL");
|
|
109
|
+
if (!(await processExitWait(pid, forcedStopTimeoutMs, host))) {
|
|
110
|
+
throw new Error(`Happy (2) daemon process ${pid} did not stop.`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
if (host.processTreeAlive(pid))
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
await host.fileRemove(pidPath);
|
|
119
|
+
host.log(`Happy (2) daemon process ${pid} was stopped.`);
|
|
120
|
+
}
|
|
121
|
+
export function daemonUsage() {
|
|
122
|
+
return [
|
|
123
|
+
"Usage:",
|
|
124
|
+
" happy2 daemon start [--config /path/to/happy2.toml]",
|
|
125
|
+
" happy2 daemon stop",
|
|
126
|
+
"",
|
|
127
|
+
"The daemon runs in the background. Its PID and logs are stored under ./.happy2.",
|
|
128
|
+
].join("\n");
|
|
129
|
+
}
|
|
130
|
+
function daemonPaths(cwd) {
|
|
131
|
+
const directory = join(cwd, daemonDirectoryName);
|
|
132
|
+
return {
|
|
133
|
+
directory,
|
|
134
|
+
log: join(directory, daemonLogFileName),
|
|
135
|
+
pid: join(directory, daemonPidFileName),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
async function configPathResolve(configPath, host) {
|
|
139
|
+
if (!configPath)
|
|
140
|
+
return undefined;
|
|
141
|
+
const absolute = isAbsolute(configPath) ? configPath : resolve(host.cwd, configPath);
|
|
142
|
+
if (!(await host.fileExists(absolute))) {
|
|
143
|
+
throw new Error(`Happy (2) config does not exist: ${absolute}`);
|
|
144
|
+
}
|
|
145
|
+
return absolute;
|
|
146
|
+
}
|
|
147
|
+
async function pidFileRead(path, host) {
|
|
148
|
+
if (!(await host.fileExists(path)))
|
|
149
|
+
return { kind: "missing" };
|
|
150
|
+
const contents = (await host.fileRead(path)).trim();
|
|
151
|
+
const starting = /^starting:([1-9]\d*)$/.exec(contents);
|
|
152
|
+
if (starting) {
|
|
153
|
+
const pid = Number(starting[1]);
|
|
154
|
+
return Number.isSafeInteger(pid) ? { kind: "starting", pid } : { kind: "invalid" };
|
|
155
|
+
}
|
|
156
|
+
if (!/^[1-9]\d*$/.test(contents))
|
|
157
|
+
return { kind: "invalid" };
|
|
158
|
+
const pid = Number(contents);
|
|
159
|
+
return Number.isSafeInteger(pid) ? { kind: "pid", pid } : { kind: "invalid" };
|
|
160
|
+
}
|
|
161
|
+
async function processExitWait(pid, timeoutMs, host) {
|
|
162
|
+
const attempts = Math.ceil(timeoutMs / processExitPollMs);
|
|
163
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
164
|
+
if (!host.processTreeAlive(pid))
|
|
165
|
+
return true;
|
|
166
|
+
await host.wait(processExitPollMs);
|
|
167
|
+
}
|
|
168
|
+
return !host.processTreeAlive(pid);
|
|
169
|
+
}
|
|
170
|
+
function signalIfAlive(pid, signal, host) {
|
|
171
|
+
if (!host.processTreeAlive(pid))
|
|
172
|
+
return;
|
|
173
|
+
try {
|
|
174
|
+
host.processTreeSignal(pid, signal);
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
// Preserve the original file-write error from daemonStart.
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
export function createDaemonHost() {
|
|
181
|
+
return {
|
|
182
|
+
cwd: process.cwd(),
|
|
183
|
+
environment: process.env,
|
|
184
|
+
executablePath: process.execPath,
|
|
185
|
+
executableArguments: process.execArgv,
|
|
186
|
+
scriptPath: resolve(process.argv[1] ?? ""),
|
|
187
|
+
async directoryCreate(path) {
|
|
188
|
+
await mkdir(path, { mode: 0o700, recursive: true });
|
|
189
|
+
},
|
|
190
|
+
async fileCreate(path, contents) {
|
|
191
|
+
try {
|
|
192
|
+
await writeFile(path, contents, { encoding: "utf8", flag: "wx", mode: 0o600 });
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
if (error.code === "EEXIST")
|
|
197
|
+
return false;
|
|
198
|
+
throw error;
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
async fileExists(path) {
|
|
202
|
+
try {
|
|
203
|
+
await access(path);
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
async fileRead(path) {
|
|
211
|
+
return await readFile(path, "utf8");
|
|
212
|
+
},
|
|
213
|
+
async fileRemove(path) {
|
|
214
|
+
await rm(path, { force: true });
|
|
215
|
+
},
|
|
216
|
+
processAlive(pid) {
|
|
217
|
+
try {
|
|
218
|
+
process.kill(pid, 0);
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
return error.code === "EPERM";
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
processTreeAlive(pid) {
|
|
226
|
+
try {
|
|
227
|
+
process.kill(-pid, 0);
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
return error.code === "EPERM";
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
async processStart(input) {
|
|
235
|
+
const descriptor = await new Promise((resolveDescriptor, reject) => {
|
|
236
|
+
open(input.logPath, "a", 0o600, (error, openedDescriptor) => {
|
|
237
|
+
if (error)
|
|
238
|
+
reject(error);
|
|
239
|
+
else
|
|
240
|
+
resolveDescriptor(openedDescriptor);
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
try {
|
|
244
|
+
const child = spawn(input.executablePath, [...input.arguments_], {
|
|
245
|
+
cwd: input.cwd,
|
|
246
|
+
detached: true,
|
|
247
|
+
env: input.environment,
|
|
248
|
+
stdio: ["ignore", descriptor, descriptor],
|
|
249
|
+
});
|
|
250
|
+
const spawned = new Promise((resolveSpawn, reject) => {
|
|
251
|
+
child.once("error", reject);
|
|
252
|
+
child.once("spawn", resolveSpawn);
|
|
253
|
+
});
|
|
254
|
+
if (child.pid === undefined) {
|
|
255
|
+
await spawned;
|
|
256
|
+
throw new Error("Happy (2) daemon did not start.");
|
|
257
|
+
}
|
|
258
|
+
try {
|
|
259
|
+
writeFileSync(input.pidPath, `${child.pid}\n`, {
|
|
260
|
+
encoding: "utf8",
|
|
261
|
+
flag: "w",
|
|
262
|
+
mode: 0o600,
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
catch (error) {
|
|
266
|
+
signalProcessGroupIfAlive(child.pid, "SIGKILL");
|
|
267
|
+
throw error;
|
|
268
|
+
}
|
|
269
|
+
try {
|
|
270
|
+
await spawned;
|
|
271
|
+
}
|
|
272
|
+
catch (error) {
|
|
273
|
+
signalProcessGroupIfAlive(child.pid, "SIGKILL");
|
|
274
|
+
throw error;
|
|
275
|
+
}
|
|
276
|
+
child.unref();
|
|
277
|
+
return child.pid;
|
|
278
|
+
}
|
|
279
|
+
finally {
|
|
280
|
+
close(descriptor, () => { });
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
processTreeSignal(pid, signal) {
|
|
284
|
+
process.kill(-pid, signal);
|
|
285
|
+
},
|
|
286
|
+
async wait(milliseconds) {
|
|
287
|
+
await new Promise((resolveWait) => setTimeout(resolveWait, milliseconds));
|
|
288
|
+
},
|
|
289
|
+
log(message) {
|
|
290
|
+
console.log(message);
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
function signalProcessGroupIfAlive(pid, signal) {
|
|
295
|
+
try {
|
|
296
|
+
process.kill(-pid, signal);
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
// The process group may already have exited after a failed spawn.
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
//# sourceMappingURL=daemon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../sources/daemon.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,mBAAmB,GAAG,SAAS,CAAC;AACtC,MAAM,iBAAiB,GAAG,YAAY,CAAC;AACvC,MAAM,iBAAiB,GAAG,YAAY,CAAC;AACvC,MAAM,qBAAqB,GAAG,MAAM,CAAC;AACrC,MAAM,mBAAmB,GAAG,KAAK,CAAC;AAClC,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAgC7B,MAAM,UAAU,kBAAkB,CAAC,UAA6B;IAC5D,IAAI,WAAqB,CAAC;IAC1B,IAAI,MAA2C,CAAC;IAChD,IAAI,CAAC;QACD,MAAM,MAAM,GAAG,SAAS,CAAC;YACrB,IAAI,EAAE,CAAC,GAAG,UAAU,CAAC;YACrB,gBAAgB,EAAE,IAAI;YACtB,OAAO,EAAE;gBACL,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC1B,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE;aACxC;SACJ,CAAC,CAAC;QACH,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;QACjC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IACjC,CAAC;IACD,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;IAC9B,IACI,CAAC,MAAM,CAAC,IAAI,IAAI,MAAM,KAAK,MAAM,CAAC;QAClC,WAAW,CAAC,MAAM,IAAI,CAAC;QACvB,MAAM,CAAC,MAAM,KAAK,SAAS,EAC7B,CAAC;QACC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAC9B,CAAC;IACD,IAAI,MAAM,KAAK,OAAO,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjD,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;IAC1D,CAAC;IACD,IAAI,MAAM,KAAK,MAAM,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC/E,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAC9B,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AACjC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC7B,OAAgC,EAChC,IAAI,GAAe,gBAAgB,EAAE;IAErC,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpC,MAAM,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAC5C,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACpD,IAAI,QAAQ,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAClE,MAAM,IAAI,KAAK,CAAC,mDAAmD,QAAQ,CAAC,GAAG,GAAG,CAAC,CAAC;IACxF,CAAC;IACD,IAAI,QAAQ,CAAC,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACjE,MAAM,IAAI,KAAK,CAAC,kDAAkD,QAAQ,CAAC,GAAG,GAAG,CAAC,CAAC;IACvF,CAAC;IACD,IAAI,QAAQ,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC9B,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACrC,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,iBAAiB,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IACrE,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,EAAE,YAAY,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC;QACnE,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC7D,CAAC;IACD,MAAM,cAAc,GAAG;QACnB,GAAG,IAAI,CAAC,mBAAmB;QAC3B,IAAI,CAAC,UAAU;QACf,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KAClD,CAAC;IACF,IAAI,GAAuB,CAAC;IAC5B,IAAI,CAAC;QACD,GAAG,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC;YAC1B,UAAU,EAAE,cAAc;YAC1B,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,cAAc,EAAE,IAAI,CAAC,cAAc;YACnC,OAAO,EAAE,KAAK,CAAC,GAAG;YAClB,OAAO,EAAE,KAAK,CAAC,GAAG;SACrB,CAAC,CAAC;IACP,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,IAAI,GAAG,KAAK,SAAS;YAAE,aAAa,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;QAC3D,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACjC,MAAM,KAAK,CAAC;IAChB,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,uCAAuC,GAAG,GAAG,CAAC,CAAC;IACxD,IAAI,CAAC,GAAG,CAAC,aAAa,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;IACnC,IAAI,CAAC,GAAG,CAAC,SAAS,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAI,GAAe,gBAAgB,EAAE;IAClE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/C,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC/C,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5D,MAAM,IAAI,KAAK,CAAC,iDAAiD,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC;IACnF,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QACpF,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;YAAE,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAC7D,IAAI,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;QAC7C,OAAO;IACX,CAAC;IACD,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC;IACtB,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAC/B,IAAI,CAAC,GAAG,CAAC,uDAAuD,GAAG,GAAG,CAAC,CAAC;QACxE,OAAO;IACX,CAAC;IAED,IAAI,CAAC;QACD,IAAI,CAAC,iBAAiB,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QACvC,IAAI,CAAC,CAAC,MAAM,eAAe,CAAC,GAAG,EAAE,qBAAqB,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC;YAC7D,IAAI,CAAC,iBAAiB,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;YACvC,IAAI,CAAC,CAAC,MAAM,eAAe,CAAC,GAAG,EAAE,mBAAmB,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC;gBAC3D,MAAM,IAAI,KAAK,CAAC,4BAA4B,GAAG,gBAAgB,CAAC,CAAC;YACrE,CAAC;QACL,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,IAAI,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC;YAAE,MAAM,KAAK,CAAC;IAChD,CAAC;IACD,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAC/B,IAAI,CAAC,GAAG,CAAC,4BAA4B,GAAG,eAAe,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,WAAW;IACvB,OAAO;QACH,QAAQ;QACR,uDAAuD;QACvD,sBAAsB;QACtB,EAAE;QACF,iFAAiF;KACpF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACjB,CAAC;AAED,SAAS,WAAW,CAAC,GAAW;IAC5B,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,mBAAmB,CAAC,CAAC;IACjD,OAAO;QACH,SAAS;QACT,GAAG,EAAE,IAAI,CAAC,SAAS,EAAE,iBAAiB,CAAC;QACvC,GAAG,EAAE,IAAI,CAAC,SAAS,EAAE,iBAAiB,CAAC;KAC1C,CAAC;AACN,CAAC;AAED,KAAK,UAAU,iBAAiB,CAC5B,UAA8B,EAC9B,IAAgB;IAEhB,IAAI,CAAC,UAAU;QAAE,OAAO,SAAS,CAAC;IAClC,MAAM,QAAQ,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IACrF,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CAAC,oCAAoC,QAAQ,EAAE,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,QAAQ,CAAC;AACpB,CAAC;AAQD,KAAK,UAAU,WAAW,CAAC,IAAY,EAAE,IAAgB;IACrD,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IAC/D,MAAM,QAAQ,GAAG,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACpD,MAAM,QAAQ,GAAG,uBAAuB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxD,IAAI,QAAQ,EAAE,CAAC;QACX,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QAChC,OAAO,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IACvF,CAAC;IACD,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IAC7D,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC7B,OAAO,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;AAClF,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,GAAW,EAAE,SAAiB,EAAE,IAAgB;IAC3E,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,iBAAiB,CAAC,CAAC;IAC1D,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,QAAQ,EAAE,OAAO,IAAI,CAAC,EAAE,CAAC;QACrD,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;QAC7C,MAAM,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC;AACvC,CAAC;AAED,SAAS,aAAa,CAAC,GAAW,EAAE,MAAsB,EAAE,IAAgB;IACxE,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC;QAAE,OAAO;IACxC,IAAI,CAAC;QACD,IAAI,CAAC,iBAAiB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACL,2DAA2D;IAC/D,CAAC;AACL,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC5B,OAAO;QACH,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE;QAClB,WAAW,EAAE,OAAO,CAAC,GAAG;QACxB,cAAc,EAAE,OAAO,CAAC,QAAQ;QAChC,mBAAmB,EAAE,OAAO,CAAC,QAAQ;QACrC,UAAU,EAAE,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC1C,KAAK,CAAC,eAAe,CAAC,IAAI;YACtB,MAAM,KAAK,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACxD,CAAC;QACD,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,QAAQ;YAC3B,IAAI,CAAC;gBACD,MAAM,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;gBAC/E,OAAO,IAAI,CAAC;YAChB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACb,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;oBAAE,OAAO,KAAK,CAAC;gBACrE,MAAM,KAAK,CAAC;YAChB,CAAC;QACL,CAAC;QACD,KAAK,CAAC,UAAU,CAAC,IAAI;YACjB,IAAI,CAAC;gBACD,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;gBACnB,OAAO,IAAI,CAAC;YAChB,CAAC;YAAC,MAAM,CAAC;gBACL,OAAO,KAAK,CAAC;YACjB,CAAC;QACL,CAAC;QACD,KAAK,CAAC,QAAQ,CAAC,IAAI;YACf,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACxC,CAAC;QACD,KAAK,CAAC,UAAU,CAAC,IAAI;YACjB,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACpC,CAAC;QACD,YAAY,CAAC,GAAG;YACZ,IAAI,CAAC;gBACD,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;gBACrB,OAAO,IAAI,CAAC;YAChB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACb,OAAQ,KAA+B,CAAC,IAAI,KAAK,OAAO,CAAC;YAC7D,CAAC;QACL,CAAC;QACD,gBAAgB,CAAC,GAAG;YAChB,IAAI,CAAC;gBACD,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;gBACtB,OAAO,IAAI,CAAC;YAChB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACb,OAAQ,KAA+B,CAAC,IAAI,KAAK,OAAO,CAAC;YAC7D,CAAC;QACL,CAAC;QACD,KAAK,CAAC,YAAY,CAAC,KAAK;YACpB,MAAM,UAAU,GAAG,MAAM,IAAI,OAAO,CAAS,CAAC,iBAAiB,EAAE,MAAM,EAAE,EAAE;gBACvE,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,KAAK,EAAE,gBAAgB,EAAE,EAAE;oBACxD,IAAI,KAAK;wBAAE,MAAM,CAAC,KAAK,CAAC,CAAC;;wBACpB,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;gBAC7C,CAAC,CAAC,CAAC;YACP,CAAC,CAAC,CAAC;YACH,IAAI,CAAC;gBACD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC,GAAG,KAAK,CAAC,UAAU,CAAC,EAAE;oBAC7D,GAAG,EAAE,KAAK,CAAC,GAAG;oBACd,QAAQ,EAAE,IAAI;oBACd,GAAG,EAAE,KAAK,CAAC,WAAW;oBACtB,KAAK,EAAE,CAAC,QAAQ,EAAE,UAAU,EAAE,UAAU,CAAC;iBAC5C,CAAC,CAAC;gBACH,MAAM,OAAO,GAAG,IAAI,OAAO,CAAO,CAAC,YAAY,EAAE,MAAM,EAAE,EAAE;oBACvD,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;oBAC5B,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;gBACtC,CAAC,CAAC,CAAC;gBACH,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;oBAC1B,MAAM,OAAO,CAAC;oBACd,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;gBACvD,CAAC;gBACD,IAAI,CAAC;oBACD,aAAa,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC,GAAG,IAAI,EAAE;wBAC3C,QAAQ,EAAE,MAAM;wBAChB,IAAI,EAAE,GAAG;wBACT,IAAI,EAAE,KAAK;qBACd,CAAC,CAAC;gBACP,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACb,yBAAyB,CAAC,KAAK,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;oBAChD,MAAM,KAAK,CAAC;gBAChB,CAAC;gBACD,IAAI,CAAC;oBACD,MAAM,OAAO,CAAC;gBAClB,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACb,yBAAyB,CAAC,KAAK,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;oBAChD,MAAM,KAAK,CAAC;gBAChB,CAAC;gBACD,KAAK,CAAC,KAAK,EAAE,CAAC;gBACd,OAAO,KAAK,CAAC,GAAG,CAAC;YACrB,CAAC;oBAAS,CAAC;gBACP,KAAK,CAAC,UAAU,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YAChC,CAAC;QACL,CAAC;QACD,iBAAiB,CAAC,GAAG,EAAE,MAAM;YACzB,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAC/B,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,YAAY;YACnB,MAAM,IAAI,OAAO,CAAO,CAAC,WAAW,EAAE,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC,CAAC;QACpF,CAAC;QACD,GAAG,CAAC,OAAO;YACP,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACzB,CAAC;KACJ,CAAC;AACN,CAAC;AAED,SAAS,yBAAyB,CAAC,GAAW,EAAE,MAAsB;IAClE,IAAI,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACL,kEAAkE;IACtE,CAAC;AACL,CAAC"}
|