forgeos 0.1.0-alpha.64 → 0.1.0-alpha.65

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +10 -0
  3. package/docs/agent-fabric.md +174 -10
  4. package/package.json +1 -1
  5. package/src/forge/_generated/releaseManifest.json +1 -1
  6. package/src/forge/_generated/releaseManifest.ts +3 -3
  7. package/src/forge/agent-fabric/index.ts +4 -0
  8. package/src/forge/agent-fabric/local-adaptive-digest-worker.mjs +26 -0
  9. package/src/forge/agent-fabric/local-adaptive-harness.ts +343 -0
  10. package/src/forge/agent-fabric/local-adaptive-service.ts +398 -0
  11. package/src/forge/agent-fabric/local-adaptive-worker.ts +146 -0
  12. package/src/forge/agent-fabric/local-approval-window.ts +49 -7
  13. package/src/forge/agent-fabric/local-coding-worker.ts +107 -12
  14. package/src/forge/agent-fabric/local-effect-broker.ts +348 -0
  15. package/src/forge/agent-fabric/local-evolution-approval.ts +81 -0
  16. package/src/forge/agent-fabric/local-evolution-profile.ts +87 -0
  17. package/src/forge/agent-fabric/local-evolution-registry.ts +427 -0
  18. package/src/forge/agent-fabric/local-evolution-service.ts +200 -0
  19. package/src/forge/agent-fabric/local-intelligence.ts +281 -0
  20. package/src/forge/agent-fabric/local-task-contract.ts +63 -2
  21. package/src/forge/agent-fabric/local-task-inbox.ts +346 -2
  22. package/src/forge/agent-fabric/local-task-server.ts +49 -7
  23. package/src/forge/agent-fabric/local-task-service.ts +516 -28
  24. package/src/forge/agent-fabric/local-verification.ts +551 -0
  25. package/src/forge/agent-fabric/p0b-model-adapter.ts +37 -7
  26. package/src/forge/agent-memory/bridge.ts +156 -53
  27. package/src/forge/agent-memory/mcp.ts +16 -4
  28. package/src/forge/agent-memory/sources/codex-hook-runner.mjs +65 -3
  29. package/src/forge/cli/adaptive.ts +51 -0
  30. package/src/forge/cli/changed.ts +13 -32
  31. package/src/forge/cli/commands.ts +22 -4
  32. package/src/forge/cli/evolution.ts +40 -0
  33. package/src/forge/cli/fabric.ts +57 -9
  34. package/src/forge/cli/main.ts +17 -0
  35. package/src/forge/cli/parse.ts +45 -5
  36. package/src/forge/cli/studio.ts +141 -77
  37. package/src/forge/dev-console/cycle.ts +6 -1
  38. package/src/forge/review/index.ts +6 -2
  39. package/src/forge/version.ts +1 -1
  40. package/src/forge/workspace/change-summary.ts +37 -7
  41. package/src/forge/workspace/git-summary.ts +27 -25
package/AGENTS.md CHANGED
@@ -1,4 +1,4 @@
1
- // @forge-generated generator=0.1.0-alpha.64 input=5bea3de626e4a7db3a5bbe226a820619b19a6e1347922b0a4f97fedf6654c489 content=721818a6f9a664aa898092d4aa5cd68cbbd8ad6e150c7d3ad3ef785a2e3fcf53
1
+ // @forge-generated generator=0.1.0-alpha.65 input=dbf2c962fa9cf8e2fa9dc988c6cf3a4e462e8f7dd759f143418b9538a08f4a85 content=721818a6f9a664aa898092d4aa5cd68cbbd8ad6e150c7d3ad3ef785a2e3fcf53
2
2
  # AGENTS.md
3
3
 
4
4
  <!-- forge-generated:start -->
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # forgeos
2
2
 
3
+ ## 0.1.0-alpha.65
4
+
5
+ ### Patch Changes
6
+
7
+ - [#61](https://github.com/Stahldavid/forge/pull/61) [`6fb76d7`](https://github.com/Stahldavid/forge/commit/6fb76d734257d96d1d366528848801cbd3698395) Thanks [@Stahldavid](https://github.com/Stahldavid)! - Harden the single-owner Agent Fabric CLI and MCP pilot with durable patch and
8
+ verification receipts, owner-controlled source-grounded memory, guarded local
9
+ effects, a fixed two-worker harness, and a local Evolution Registry. Improve
10
+ Codex hook ingestion, Studio startup, release propagation checks, and targeted
11
+ CI coverage.
12
+
3
13
  ## 0.1.0-alpha.64
4
14
 
5
15
  ### Minor Changes
@@ -14,15 +14,71 @@ control journal, browser-based owner review, an isolated Ollama coding worker,
14
14
  and MCP proposal/status tools backed by a local owner process. It does not make
15
15
  a production persistence or security claim. Its scope and remaining gates are
16
16
  in [`P0B_B_LOCAL_CODING_SCOPE.md`](./architecture/agent-fabric/P0B_B_LOCAL_CODING_SCOPE.md).
17
+ The [single-owner acceptance matrix](./architecture/agent-fabric/LOCAL_SINGLE_OWNER_ACCEPTANCE.md)
18
+ separates the current local implementation from its remaining release and human
19
+ acceptance gates.
20
+ Owner-selected local memory, fixed two-process data workers, and a standalone
21
+ Evolution Registry have separate narrow workflows below. These do not grant
22
+ the Ollama coding worker new tools or executable extensions.
23
+
24
+ `LocalAdaptiveHarness.run()` is a fixed local demonstration of two permitted
25
+ Node processes (`inventory` and `constraints`) followed by an authoritative
26
+ join. Each child receives only bounded text on stdin, an empty environment,
27
+ and a one second wall limit. The coordinator validates each digest against its
28
+ own input before committing the P0a result; cancellation, timeout, or an
29
+ invalid report leaves the join blocked. This trusted data worker is not an
30
+ arbitrary coding agent or an OS security sandbox.
31
+
32
+ The single-PC CLI wraps that harness with a local owner decision and durable
33
+ readback. From the repository root, create a JSON file with exactly two fields,
34
+ for example `{"inventory":"src/a.ts","constraints":"read only"}`. Each field
35
+ is data of at most 256 UTF-8 bytes. Then run:
36
+
37
+ ```bash
38
+ node bin/forge.mjs fabric adaptive-propose --file input.json --json
39
+ node bin/forge.mjs fabric adaptive-review <run-id> --json
40
+ node bin/forge.mjs fabric adaptive-run <run-id> --json
41
+ node bin/forge.mjs fabric adaptive-status <run-id> --json
42
+ ```
43
+
44
+ To narrow the two data fields through an owner-selected Evolution profile,
45
+ pass `--channel canary` or `--channel stable` to `adaptive-propose` after that
46
+ channel has a selected `local-adaptive-input-profile` version. The proposal
47
+ binds the immutable version ID before review. The owner window displays it,
48
+ and `adaptive-run` checks that the same version remains selected and loadable
49
+ before issuing permits. A changed or revoked selection blocks the run. The
50
+ profile validates labels and lengths only; it does not provide code, tools,
51
+ instructions, or worker behavior.
52
+ Profile decisions and an adaptive run share a local process lock, so a
53
+ promotion or revocation cannot race between profile readback and worker dispatch.
54
+
55
+ `adaptive-review` opens a loopback browser window showing both exact inputs,
56
+ the bound profile version when present, and their proposal digest. The approval
57
+ expires after five minutes and permits one run. The
58
+ CLI commits the owner authorization, fixed plan, child grants, and both P0a
59
+ permits to a separate local PGlite journal before starting either process.
60
+ The result record and authoritative join can be read after closing and
61
+ reopening the CLI. If the process dies after committing the join but before
62
+ saving the result record, status still reports the authoritative join but may
63
+ omit process IDs and child details. A run cannot be repeated; a crash after
64
+ dispatch is shown as uncertain unless the durable journal contains the join.
65
+ Cancellation, failed workers, or missing results never authorize a join.
66
+ Local records and the owner verifier key live under `.forge/local/agent-fabric`.
67
+ The local owner lock prevents concurrent mutating CLI invocations; after a
68
+ crash, inspect `adaptive-status` before any manual lock recovery. This is a
69
+ single-PC workflow, not a production security or multi-host claim.
17
70
 
18
71
  The following remain explicitly deferred and must not be inferred from architecture notes, historical handoffs, or local experiments:
19
72
 
20
- - model-selected tools, plugins or child delegation beyond P0b-A;
73
+ - model-selected tools, plugins or child delegation (the local data workers use
74
+ fixed code-owned child permits only);
21
75
  - PGlite-backed production persistence/outbox integration for Agent Fabric;
22
- - consequential-effect brokers and reconciliation against real systems;
76
+ - general consequential-effect brokers and arbitrary external-system effects
77
+ (the local pilot has fixed patch and Docker verification receipts only);
23
78
  - recovery epochs and integrity-unknown recovery;
24
- - adaptive model routing/harness compilation beyond the P0a contracts;
25
- - plugin promotion, persistent memory, and governed self-evolution;
79
+ - adaptive model routing and general harness compilation beyond the fixed
80
+ two-process data workflow;
81
+ - executable plugin promotion, shared production memory, and autonomous self-evolution;
26
82
  - production deployment or production security claims.
27
83
 
28
84
  ## P0a scope
@@ -208,45 +264,153 @@ That public entry point exposes the hardened Conductor and hardened replay funct
208
264
  ## Local coding pilot (experimental)
209
265
 
210
266
  The framework checkout also exposes a bounded single-owner CLI path. Run these commands
211
- from the root of a trusted Git repository with Ollama running and `qwen3:0.6b`
267
+ from the root of a trusted Git repository with Ollama running and `qwen2.5-coder:3b`
212
268
  installed. This pilot uses no hosted API key or Codex model turn.
213
269
 
214
270
  ```text
215
271
  node bin/forge.mjs fabric capabilities --json
216
272
  node bin/forge.mjs fabric propose --file task.json --json
273
+ node bin/forge.mjs fabric memory-add --file note.json --json
274
+ node bin/forge.mjs fabric memory-list --file paths.json --json
275
+ node bin/forge.mjs fabric memory-delete <memory-id> --json
217
276
  node bin/forge.mjs fabric status <task-id> --json
277
+ node bin/forge.mjs fabric evidence <task-id> --json
218
278
  node bin/forge.mjs fabric review <task-id> --json
219
279
  node bin/forge.mjs fabric run <task-id> --json
280
+ node bin/forge.mjs fabric cancel <task-id> --json
281
+ node bin/forge.mjs fabric reconcile <task-id> --json
282
+ node bin/forge.mjs fabric verify <task-id> --json
220
283
  node bin/forge.mjs fabric review-result <task-id> --json
221
284
  node bin/forge.mjs fabric serve --json
222
285
  ```
223
286
 
224
287
  `task.json` is an untrusted proposal. Its required fields are `schemaVersion: 1`,
225
288
  `repositoryId`, the full `baseCommit`, `goal`, `acceptanceCriteria`, `nonObjectives`,
226
- `sourcePaths`, `writablePaths`, `requestedModelTargetId: "target:ollama:local"`, and
289
+ `sourcePaths`, `writablePaths`, `requestedModelTargetId: "target:ollama:local"`,
290
+ `requestedModelId: "qwen2.5-coder:3b"`, and
227
291
  `limits` with `maximumAttempts`, `maximumWallClockMs`, `maximumOutputTokens`,
228
292
  `maximumContextBytes`, `maximumPatchBytes`, and a Unix millisecond `expiresAt`.
229
293
  `review` opens a local browser window showing the exact proposal and digest; `run`
230
294
  consumes one approved model attempt and writes a diff in an isolated Git worktree.
295
+ The model ID is part of that digest and appears in the owner review. Existing
296
+ tasks approved before model pinning cannot start a new model call; submit a fresh
297
+ proposal. Their saved outcomes and patches remain available for readback, while
298
+ the exact model for a legacy outcome is reported as unknown.
299
+ The service captures the allowlisted tracked source files at the exact current
300
+ HEAD when proposing and rechecks them before spending the approved model
301
+ attempt. Changed source content or HEAD blocks a stale attempt.
302
+ `cancel` revokes an unused owner approval so `run` cannot start it, including after
303
+ the owner restarts. During an active model call, it requests abort from the
304
+ local adapter. The returned `model_uncertain` state does not prove the provider
305
+ stopped; a dispatched attempt cannot be retried. Cancellation does not stop
306
+ patch materialization or Docker verification that has already started. Start
307
+ `fabric serve` before `run` when you need a second CLI process to cancel an
308
+ in-flight call; a one-shot `run` has no cross-process abort endpoint.
231
309
  `review-result` shows the recorded diff for a separate owner decision. Acceptance
232
310
  records a decision only; it does not alter the original checkout or merge code.
233
311
 
312
+ Private memory is opt in and local to this checkout. `memory-add` reads a JSON
313
+ file such as `{ "sourcePaths": ["src/example.ts"], "text": "Owner note",
314
+ "retentionMs": 86400000 }`; `memory-list` reads a JSON file containing only
315
+ `sourcePaths`. Both require an unchanged tracked source snapshot. Notes are
316
+ bounded to 2 KiB, retained for at most 30 days, and stored under
317
+ `.forge/local/agent-fabric`. `memory-delete` removes a note by its returned ID.
318
+ To select notes for a coding task, add `"memoryIds": ["memory:<id>"]` to
319
+ `task.json` using returned full IDs. The proposal digest binds those IDs; the
320
+ owner review displays their text and provenance. Missing, expired, deleted, or
321
+ source-stale notes block approval or execution. Selected notes consume the
322
+ existing context byte budget and are labeled `untrusted_memory` in the model
323
+ context. MCP task tools cannot add, list, or delete private memory.
324
+
325
+ An optional `verification` field binds an immutable local Docker image ID and
326
+ two to four bounded command descriptors into the proposal digest. The first
327
+ descriptor is `{ "kind": "git-diff-check", "timeoutMs": 5000 }`; the remaining
328
+ descriptors are `{ "kind": "node-test-file", "path": "pass.test.mjs",
329
+ "timeoutMs": 20000 }`. The owner sees these exact commands and image in the
330
+ approval window. The image ID must match the locally installed `node:22`
331
+ image with a `node@sha256` registry digest; the service rejects a proposal
332
+ pointing to another local image and rechecks the tag before execution.
333
+ Each Node test file must exist in the pinned commit and be outside the task's
334
+ writable paths, so the model cannot replace the test that judges its patch.
335
+ After `run` produces a patch, `verify` checks deterministic test paths,
336
+ mount encoding, Docker context, and the pinned image before recording a durable
337
+ intent. A dispatch barrier is recorded before container execution. The owner
338
+ can clear only an intent that has no dispatch barrier; a potentially started
339
+ container cannot be retried automatically. `git diff --check` runs with external
340
+ diff and filesystem monitor helpers disabled on the host; Node tests run inside
341
+ Docker Desktop without network, with an
342
+ immutable already-installed image, read-only checkout, nonroot user and resource
343
+ limits. Accepting a patch with an approved verification profile requires all
344
+ checks to pass; a failed or uncertain result can still be rejected by the owner.
345
+
234
346
  The local store lives under `.forge/local/agent-fabric` and has one PGlite process
235
347
  owner. Start `forge fabric serve` to keep that owner running while separate CLI
236
348
  and MCP clients connect through a loopback endpoint. The endpoint token stays in
237
- the local repository store and is not printed. The MCP tools `fabric_propose` and
238
- `fabric_status` use that same owner; they cannot approve, run, or accept a task.
349
+ the local repository store and is not printed. The MCP tools `fabric_propose`,
350
+ `fabric_status`, and `fabric_evidence` use that same owner; they cannot approve,
351
+ run, or accept a task. `fabric_evidence` returns a digest-bound provenance summary
352
+ without raw model text or diff content.
239
353
  Without a running owner, CLI commands open the store for a single operation and
240
354
  MCP task tools report that the owner is unavailable. A crashed model attempt with
241
355
  a committed permit and no outcome remains
242
356
  uncertain; a repeated `run` does not spend another attempt. A committed model result
243
- can be materialized after restart. The existing MCP server reports the boundary
357
+ can be materialized after restart if no patch-effect intent was issued. Patch
358
+ materialization records a durable intent before creating the isolated checkout.
359
+ A crash after that intent reports `patch_uncertain`; `run` will not reapply it.
360
+ `fabric reconcile` reads the checkout and diff artifact against the committed
361
+ model result and records a receipt only when they match exactly. It never
362
+ creates or rewrites the patch. The existing MCP server reports the boundary
244
363
  through `fabric_capabilities`. The popup is
245
364
  a cooperative same-account interaction, so it is not a security boundary against
246
365
  an agent with unrestricted shell or UI control. The model receives only approved
247
366
  source files and cannot run shell commands. The Git worktree confines patch
248
- materialization, but it is not a container or VM sandbox.
367
+ materialization; optional Node verification is container isolated. This pilot
368
+ does not yet broker arbitrary consequential effects or attest to sandbox escape
369
+ resistance against a hostile local administrator.
249
370
 
250
371
  Maintainers can run `bun scripts/agent-fabric-local-smoke.ts` for an opt-in real
251
372
  Ollama fixture. That script injects a synthetic test approval and confirms a diff;
252
373
  it does not prove the human popup flow or coding quality on real projects.
374
+ `FORGE_FABRIC_DOCKER_SMOKE=1 bun test tests/agent-fabric/local-task-service.test.ts`
375
+ exercises the service, approved verification, Docker Desktop, durable readback,
376
+ and acceptance with a synthetic approval callback.
377
+
378
+ ### Local Evolution Registry (single owner)
379
+
380
+ The local extension workflow pins a candidate's bytes before evaluation. A
381
+ manifest is a repository file with exactly these fields:
382
+
383
+ ```json
384
+ {"schemaVersion":1,"extensionKey":"sample","artifactPath":"extensions/sample.js"}
385
+ ```
386
+
387
+ The artifact and manifest must be regular files inside the repository. The
388
+ artifact is limited to 1 MiB and the manifest to 16 KiB. Forge copies both to
389
+ content addressed files under `.forge/local/agent-fabric/evolution/`; edits to
390
+ the source files after registration do not change the registered version.
391
+
392
+ ```bash
393
+ node bin/forge.mjs evolution register --manifest extensions/sample.json --json
394
+ node bin/forge.mjs evolution evaluate extension:sha256:<digest> --json
395
+ node bin/forge.mjs evolution status extension:sha256:<digest> --json
396
+ node bin/forge.mjs evolution review canary extension:sha256:<digest> --json
397
+ node bin/forge.mjs evolution review promote extension:sha256:<digest> --json
398
+ node bin/forge.mjs evolution load sample --channel stable --json
399
+ node bin/forge.mjs evolution review rollback extension:sha256:<older-digest> --json
400
+ node bin/forge.mjs evolution review revoke extension:sha256:<digest> --json
401
+ ```
402
+
403
+ Evaluation is a fixed local suite that checks stored artifact integrity,
404
+ stored manifest integrity, and the manifest contract. It does not execute the
405
+ candidate. Each version evaluates once; a failed or interrupted evaluation
406
+ requires a new candidate version. Canary, promotion, rollback, and revocation
407
+ open a loopback owner review window. Rejection or timeout leaves selection
408
+ unchanged. Rollback can select only a previously stable version that still has
409
+ a passing evaluation. Revocation clears selections and blocks future loading.
410
+
411
+ The `load` command reports verified metadata. Local runtime callers can use
412
+ `LocalEvolutionService.loadSelected` to obtain bytes after the same channel,
413
+ evaluation, revocation, and digest checks. This registry does not import or
414
+ execute those bytes or grant them side effects. Its review window is a
415
+ cooperative human checkpoint; same-account shell or browser automation is
416
+ outside its protection boundary. No hosted model or API key is used.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forgeos",
3
- "version": "0.1.0-alpha.64",
3
+ "version": "0.1.0-alpha.65",
4
4
  "description": "Agent-native application framework and compiler for building Forge apps without a mandatory dashboard.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1 +1 @@
1
- {"defaultProvider":"local","diagnostics":[],"env":{"deployEnv":"FORGE_DEPLOY_ENV","deployId":"FORGE_DEPLOY_ID","publicReleaseId":"NEXT_PUBLIC_FORGE_RELEASE_ID","releaseId":"FORGE_RELEASE_ID"},"gitSha":"unknown","optionalProviders":["local","sentry-compatible","sentry","glitchtip","bugsink","otel","custom"],"packageName":"forgeos","packageVersion":"0.1.0-alpha.64","releaseId":"forgeos@0.1.0-alpha.64+unknown","schemaVersion":"0.1.0"}
1
+ {"defaultProvider":"local","diagnostics":[],"env":{"deployEnv":"FORGE_DEPLOY_ENV","deployId":"FORGE_DEPLOY_ID","publicReleaseId":"NEXT_PUBLIC_FORGE_RELEASE_ID","releaseId":"FORGE_RELEASE_ID"},"gitSha":"unknown","optionalProviders":["local","sentry-compatible","sentry","glitchtip","bugsink","otel","custom"],"packageName":"forgeos","packageVersion":"0.1.0-alpha.65","releaseId":"forgeos@0.1.0-alpha.65+unknown","schemaVersion":"0.1.0"}
@@ -1,4 +1,4 @@
1
- // @forge-generated generator=0.1.0-alpha.64 input=5bea3de626e4a7db3a5bbe226a820619b19a6e1347922b0a4f97fedf6654c489 content=805acd8e59d13986e509830825226246b9aa99c68f254c7879db5648cec66b9f
1
+ // @forge-generated generator=0.1.0-alpha.65 input=dbf2c962fa9cf8e2fa9dc988c6cf3a4e462e8f7dd759f143418b9538a08f4a85 content=f04ed7266bc22851fbf9c2570af65d25c3718c8ab8133af3bce480d49f6df516
2
2
  export const releaseManifest = {
3
3
  "defaultProvider": "local",
4
4
  "diagnostics": [],
@@ -19,7 +19,7 @@ export const releaseManifest = {
19
19
  "custom"
20
20
  ],
21
21
  "packageName": "forgeos",
22
- "packageVersion": "0.1.0-alpha.64",
23
- "releaseId": "forgeos@0.1.0-alpha.64+unknown",
22
+ "packageVersion": "0.1.0-alpha.65",
23
+ "releaseId": "forgeos@0.1.0-alpha.65+unknown",
24
24
  "schemaVersion": "0.1.0"
25
25
  } as const;
@@ -8,6 +8,10 @@ export type {
8
8
  } from "./hardened-conductor.ts";
9
9
  export * from "./errors.ts";
10
10
  export * from "./journal.ts";
11
+ export { LocalEvolutionRegistry } from "./local-evolution-registry.ts";
12
+ export type { ExtensionCandidate, ExtensionVersion, FixedEvaluationSuite, EvaluationRecord,
13
+ EvolutionChannel, EvolutionDecisionAction, EvolutionVersionStatus, EvolutionOwnerVerifier } from "./local-evolution-registry.ts";
14
+ export { LocalEvolutionService, LOCAL_EVOLUTION_SUITE } from "./local-evolution-service.ts";
11
15
  export * from "./p0a.ts";
12
16
  export * from "./p0b-model-adapter.ts";
13
17
  export * from "./planning.ts";
@@ -0,0 +1,26 @@
1
+ // Fixed data-only worker. This file deliberately has no Forge imports, tools,
2
+ // filesystem access, or network calls. The coordinator verifies its output.
3
+ import { createHash } from "node:crypto";
4
+
5
+ let input = "";
6
+ process.stdin.setEncoding("utf8");
7
+ process.stdin.on("data", (chunk) => {
8
+ input += chunk;
9
+ if (Buffer.byteLength(input, "utf8") > 1_024) process.exit(2);
10
+ });
11
+ process.stdin.on("end", () => {
12
+ try {
13
+ const message = JSON.parse(input);
14
+ if (!message || Object.keys(message).sort().join(",") !== "input,role" ||
15
+ !["inventory", "constraints"].includes(message.role) ||
16
+ typeof message.input !== "string" || message.input.length > 256) {
17
+ process.exitCode = 2;
18
+ return;
19
+ }
20
+ const canonical = JSON.stringify({ input: message.input, role: message.role });
21
+ const resultDigest = `sha256:${createHash("sha256").update(canonical).digest("hex")}`;
22
+ process.stdout.write(JSON.stringify({ pid: process.pid, role: message.role, resultDigest }));
23
+ } catch {
24
+ process.exitCode = 2;
25
+ }
26
+ });