agentsmesh 0.19.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +133 -0
- package/README.md +30 -1
- package/dist/canonical.js +12 -3
- package/dist/canonical.js.map +1 -1
- package/dist/cli.js +163 -160
- package/dist/engine.js +27 -3
- package/dist/engine.js.map +1 -1
- package/dist/index.js +27 -3
- package/dist/index.js.map +1 -1
- package/dist/targets.js +7 -2
- package/dist/targets.js.map +1 -1
- package/package.json +1 -1
- package/schemas/installs.json +7 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,138 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.20.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 6739c63: feat(refresh): add `agentsmesh refresh` to re-fetch and re-apply installed packs
|
|
8
|
+
|
|
9
|
+
A new top-level CLI command and MCP tool for keeping installed packs in
|
|
10
|
+
sync with their declared sources. Branch pins re-resolve to the current
|
|
11
|
+
tip; tag pins re-resolve in case the tag moved; SHA pins stay put. Per-pack
|
|
12
|
+
atomic via the existing `materializePack` swap — a failure or interruption
|
|
13
|
+
leaves the affected pack at its pre-refresh state.
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
agentsmesh refresh # refresh every installed pack
|
|
17
|
+
agentsmesh refresh my-pack,other-pack # refresh just these
|
|
18
|
+
agentsmesh refresh --dry-run # preview without writing
|
|
19
|
+
agentsmesh refresh --force # skip the drift consent prompt
|
|
20
|
+
agentsmesh refresh --json # JSON output (implies --force)
|
|
21
|
+
agentsmesh refresh --global # global scope
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**MCP parity:** new `mcp__agentsmesh__refresh` tool with the same
|
|
25
|
+
`{ names?, dry_run?, global? }` input shape MCP install/uninstall use.
|
|
26
|
+
`force: true` is implicit (no TTY). Errors map to two new codes —
|
|
27
|
+
`REFRESH_RESOLVE_FAILED` and `REFRESH_APPLY_FAILED` — plus the existing
|
|
28
|
+
`LOCK_HELD` and `IO_ERROR`.
|
|
29
|
+
|
|
30
|
+
**Drift handling:** modified pack files trigger a consolidated consent
|
|
31
|
+
prompt with a 5-minute timeout (default no). `--force` bypasses the prompt
|
|
32
|
+
and overwrites local edits. The prompt is collapsed across packs so a bulk
|
|
33
|
+
refresh asks once, not N times.
|
|
34
|
+
|
|
35
|
+
**Schema additions** (`installs.yaml`, all optional, backwards-compatible):
|
|
36
|
+
- `original_ref?: string` — the user's original ref expression (e.g.
|
|
37
|
+
`main`, `v1.2.3`) captured at install time. Used by refresh to
|
|
38
|
+
re-resolve branch pins. Absent on installs predating this release;
|
|
39
|
+
refresh becomes a deterministic no-op for those rows.
|
|
40
|
+
- `refreshed_at?: string` — ISO-8601 timestamp of the last successful
|
|
41
|
+
refresh. Surfaces in `installs list` under the "LAST TOUCHED" column
|
|
42
|
+
(falls back to `installed_at` when absent).
|
|
43
|
+
|
|
44
|
+
**Behavior changes that could affect existing consumers:**
|
|
45
|
+
- `installs.yaml` rows written by this release include `original_ref`.
|
|
46
|
+
Pre-existing rows continue to parse and behave identically.
|
|
47
|
+
- `--json` on `agentsmesh refresh` implies `--force` (CI/MCP can't
|
|
48
|
+
answer the consent prompt). Documented on the website CLI reference.
|
|
49
|
+
- `installs list` column header was "INSTALLED AT", now "LAST TOUCHED",
|
|
50
|
+
showing `refreshed_at` when present and `installed_at` otherwise.
|
|
51
|
+
|
|
52
|
+
**Architecture notes:**
|
|
53
|
+
- `installAsPack` gains an optional `forceFreshMaterialize` flag,
|
|
54
|
+
threaded through five layers (`install-flags → run-install →
|
|
55
|
+
run-install-locked → single-pack-install → run-install-execute →
|
|
56
|
+
installAsPack`). Default is false; install's existing flow is
|
|
57
|
+
untouched. Refresh sets the flag to bypass the
|
|
58
|
+
merge-into-existing-pack branch and force atomic replacement via
|
|
59
|
+
`materializePack`.
|
|
60
|
+
- Source-URL parsing is now shared between install and refresh via the
|
|
61
|
+
new pure `parseSourceUrl` helper (`src/install/source/parse-source-url.ts`).
|
|
62
|
+
|
|
63
|
+
**Refresh does NOT switch refs.** To move a pack to a different ref,
|
|
64
|
+
re-run `agentsmesh install <source>@<new-ref>` — install silently
|
|
65
|
+
overwrites an existing pack of the same name.
|
|
66
|
+
|
|
67
|
+
**Refresh vs `install --sync`** are orthogonal. `--sync` replays missing
|
|
68
|
+
installs from `installs.yaml` (fresh clone). `refresh` updates existing
|
|
69
|
+
installs against their declared sources.
|
|
70
|
+
|
|
71
|
+
Verified end-to-end against 64 community packs from the install
|
|
72
|
+
compatibility log spanning Anthropic skill-packs, marketplaces (`--all`),
|
|
73
|
+
canonical mixed packs, flat collections, root SKILL.md, root CLAUDE.md,
|
|
74
|
+
manual `--path`/`--as`/`--target` combinations, and packs using `pick`
|
|
75
|
+
selectors. Full unit/integration/e2e suite green (7700+ tests).
|
|
76
|
+
|
|
77
|
+
## 0.19.1
|
|
78
|
+
|
|
79
|
+
### Patch Changes
|
|
80
|
+
|
|
81
|
+
- 041b9c5: fix(security): plug input/path/proto-pollution holes in plugin, install, MCP, and config
|
|
82
|
+
|
|
83
|
+
Closes a batch of security audit findings (2 HIGH + 5 MEDIUM):
|
|
84
|
+
- **Plugin source containment** (`src/plugins/load-plugin.ts`) — local plugin
|
|
85
|
+
sources are now resolved with `realpath` and rejected when they escape
|
|
86
|
+
`projectRoot`. A hostile `agentsmesh.yaml` with
|
|
87
|
+
`plugins[].source: "../../tmp/evil.js"` no longer reaches dynamic
|
|
88
|
+
`import()`. Bare npm specifiers continue to resolve through
|
|
89
|
+
`node_modules/<source>`. Both sides are canonicalized so macOS
|
|
90
|
+
`/tmp -> /private/tmp` (and other platform-level symlinks) do not
|
|
91
|
+
produce false positives.
|
|
92
|
+
- **Prototype pollution denylist** (`src/config/core/loader.ts`) —
|
|
93
|
+
`deepMergeObjects` over `agentsmesh.local.yaml` now skips `__proto__`,
|
|
94
|
+
`constructor`, and `prototype` keys. Defense-in-depth: the `yaml` v2
|
|
95
|
+
parser already strips `__proto__`, but this pins the invariant against
|
|
96
|
+
future parser swaps.
|
|
97
|
+
- **Install manifest name validation** (`src/install/core/install-manifest.ts`) —
|
|
98
|
+
`installManifestEntrySchema.name` now refuses path separators, NUL,
|
|
99
|
+
and `.`/`..` segments. A poisoned `installs.yaml` entry can no longer
|
|
100
|
+
drive `rm -rf` outside `.agentsmesh/packs/` at uninstall time.
|
|
101
|
+
- **`git+http://` allowlist** (`src/config/remote/remote-source.ts`) —
|
|
102
|
+
rejected by default; opt-in via `AGENTSMESH_ALLOW_INSECURE_GIT=1` for
|
|
103
|
+
closed-network development. `https://`, `ssh://`, and `file://` are
|
|
104
|
+
unchanged. Closes a MITM window before SHA pinning resolves.
|
|
105
|
+
- **MCP `cwd` / `description` refinement** (`src/mcp/schemas.ts`) — `cwd`
|
|
106
|
+
rejects `..` segments (POSIX + Windows separators), NUL, and newlines;
|
|
107
|
+
`description` rejects NUL and newlines. MCP clients can no longer
|
|
108
|
+
plant a structurally-escaping working directory that downstream agents
|
|
109
|
+
consume via `spawn(command, args, { cwd })`.
|
|
110
|
+
- **Global path redaction in MCP errors** (`src/mcp/errors.ts`) —
|
|
111
|
+
`redactAbsolutePaths` now strips paths anywhere in the string, catching
|
|
112
|
+
embedded paths in stack frames (`at Foo (/Users/...)`) and quoted
|
|
113
|
+
paths in Node errors (`ENOENT, open '/Users/...'`) the prior
|
|
114
|
+
whitespace-anchored regex missed.
|
|
115
|
+
- **`copyDir` symlink hardening** (`src/utils/filesystem/fs-traverse.ts`) —
|
|
116
|
+
`copyDir` now uses `lstat` and skips symlinks. A symlink in the source
|
|
117
|
+
tree pointing outside its root can no longer have its target's bytes
|
|
118
|
+
exfiltrated into the destination (and into any redistributed pack
|
|
119
|
+
built on top of it).
|
|
120
|
+
|
|
121
|
+
Behavioral changes that could affect existing consumers:
|
|
122
|
+
- `git+http://...` extends/installs require `AGENTSMESH_ALLOW_INSECURE_GIT=1`.
|
|
123
|
+
- MCP server entries with `cwd: "../foo"` no longer parse — rewrite as a
|
|
124
|
+
POSIX-relative path without `..` segments.
|
|
125
|
+
- Plugin `source:` entries pointing outside the project tree no longer
|
|
126
|
+
load. The standard `node_modules/<plugin>` and project-local layouts
|
|
127
|
+
are unaffected.
|
|
128
|
+
- A poisoned `installs.yaml` entry whose `name` contains separators or
|
|
129
|
+
`..` is now dropped at parse time (the rest of the manifest survives).
|
|
130
|
+
- A `agentsmesh.local.yaml` payload at `__proto__`, `constructor`, or
|
|
131
|
+
`prototype` keys is silently dropped instead of merged.
|
|
132
|
+
|
|
133
|
+
Branch coverage > 95% on every touched file; full unit/integration suite
|
|
134
|
+
(7596 tests) and plugin e2e suite (57 tests) green.
|
|
135
|
+
|
|
3
136
|
## 0.19.0
|
|
4
137
|
|
|
5
138
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -235,6 +235,7 @@ agentsmesh matrix [--global] [--targets <csv>] [--verbose]
|
|
|
235
235
|
agentsmesh install <source> [--sync] [--path <dir>] [--target <id>] [--as <kind>] [--name <id>] [--extends] [--all] [--dry-run] [--global] [--force]
|
|
236
236
|
agentsmesh uninstall <name>[,<name>...] [--all] [--keep-pack] [--keep-generated] [--dry-run] [--global] [--force]
|
|
237
237
|
agentsmesh installs list [--global]
|
|
238
|
+
agentsmesh refresh [<name>[,<name>...]] [--dry-run] [--force] [--json] [--global]
|
|
238
239
|
agentsmesh plugin add|list|remove|info [--version <v>] [--id <id>]
|
|
239
240
|
agentsmesh target scaffold <id> [--name <displayName>] [--force]
|
|
240
241
|
```
|
|
@@ -314,6 +315,34 @@ agentsmesh uninstall <name> --dry-run # preview; no writes
|
|
|
314
315
|
|
|
315
316
|
Each install writes `.agentsmesh-install-manifest.json` next to the pack with per-file sha256 hashes; uninstall compares current contents against that manifest and prompts before deleting locally-modified files. `--force` accepts the documented defaults (bulk = accept all, broken-link = leave-with-warnings, modified = delete-anyway). `.agentsmesh/.install.lock` serialises install/uninstall so concurrent runs on the same project fail fast rather than racing on disk.
|
|
316
317
|
|
|
318
|
+
### Refreshing packs
|
|
319
|
+
|
|
320
|
+
`agentsmesh refresh` re-fetches every installed pack from its recorded source/ref
|
|
321
|
+
and re-applies it. Branch pins (`@main`) advance to the current tip; tag pins
|
|
322
|
+
re-resolve in case the tag moved; SHA pins stay put (re-fetch with the same content).
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
agentsmesh refresh # refresh every installed pack
|
|
326
|
+
agentsmesh refresh my-pack,other-pack # refresh just these
|
|
327
|
+
agentsmesh refresh --dry-run # preview without writing
|
|
328
|
+
agentsmesh refresh --force # skip the drift prompt
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Each pack is refreshed atomically — a failure or interruption leaves the
|
|
332
|
+
affected pack at its pre-refresh state. Local edits to pack files trigger
|
|
333
|
+
a consolidated consent prompt (5-minute timeout) unless `--force` is set.
|
|
334
|
+
|
|
335
|
+
**refresh does NOT switch refs.** To move a pack to a different ref, just install
|
|
336
|
+
with the new ref — install silently overwrites an existing pack of the same name:
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
agentsmesh install github:org/repo@v2.0.0
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
**refresh vs `install --sync`.** `--sync` replays missing installs from
|
|
343
|
+
`installs.yaml` (e.g. after a fresh clone). `refresh` updates existing
|
|
344
|
+
installs against their declared sources. They are orthogonal.
|
|
345
|
+
|
|
317
346
|
### What to commit and what to gitignore
|
|
318
347
|
|
|
319
348
|
`agentsmesh init` writes a `.gitignore` that follows the recommended convention. The defaults are deliberate:
|
|
@@ -457,7 +486,7 @@ See the [full feature matrix docs](https://samplexbro.github.io/agentsmesh/refer
|
|
|
457
486
|
|
|
458
487
|
- **[Getting Started](https://samplexbro.github.io/agentsmesh/getting-started/installation/)** — installation, quick start
|
|
459
488
|
- **[Canonical Config](https://samplexbro.github.io/agentsmesh/canonical-config/)** — rules, commands, agents, skills, MCP, hooks, ignore, permissions
|
|
460
|
-
- **[CLI Reference](https://samplexbro.github.io/agentsmesh/cli/)** — `init`, `generate`, `import`, `convert`, `install`, `uninstall`, `installs`, `diff`, `lint`, `watch`, `check`, `merge`, `matrix`, `plugin`, `target`
|
|
489
|
+
- **[CLI Reference](https://samplexbro.github.io/agentsmesh/cli/)** — `init`, `generate`, `import`, `convert`, `install`, `uninstall`, `installs`, `refresh`, `diff`, `lint`, `watch`, `check`, `merge`, `matrix`, `plugin`, `target`
|
|
461
490
|
- **[Configuration](https://samplexbro.github.io/agentsmesh/configuration/agentsmesh-yaml/)** — `agentsmesh.yaml`, local overrides, extends, collaboration, conversions
|
|
462
491
|
- **[Guides](https://samplexbro.github.io/agentsmesh/guides/existing-project/)** — adopting in existing projects · multi-tool teams · sharing config · CI drift detection · community packs · **building plugins**
|
|
463
492
|
- **[Reference](https://samplexbro.github.io/agentsmesh/reference/generation-pipeline/)** — supported tools matrix · generation pipeline · managed embedding
|
package/dist/canonical.js
CHANGED
|
@@ -1023,7 +1023,7 @@ ${legacy}`, "");
|
|
|
1023
1023
|
}
|
|
1024
1024
|
return result.trim();
|
|
1025
1025
|
}
|
|
1026
|
-
var ROOT_INSTRUCTION_BODY_V1, ROOT_INSTRUCTION_BODY_V2, ROOT_INSTRUCTION_BODY_V3, ROOT_INSTRUCTION_BODY_V4, ROOT_INSTRUCTION_BODY_V5, ROOT_INSTRUCTION_BODY_V6, ROOT_INSTRUCTION_BODY_V7, ROOT_INSTRUCTION_BODY, LEGACY_AGENTSMESH_ROOT_INSTRUCTION_PARAGRAPH, LEGACY_AGENTSMESH_ROOT_INSTRUCTION_SECTION, AGENTSMESH_CONTRACT_WITH_V1_BODY, AGENTSMESH_CONTRACT_WITH_V2_BODY, AGENTSMESH_CONTRACT_WITH_V3_BODY, AGENTSMESH_CONTRACT_WITH_V4_BODY, AGENTSMESH_CONTRACT_WITH_V5_BODY, AGENTSMESH_CONTRACT_WITH_V6_BODY, AGENTSMESH_CONTRACT_WITH_V7_BODY, AGENTSMESH_ROOT_INSTRUCTION_PARAGRAPH, LEGACY_FORMS;
|
|
1026
|
+
var ROOT_INSTRUCTION_BODY_V1, ROOT_INSTRUCTION_BODY_V2, ROOT_INSTRUCTION_BODY_V3, ROOT_INSTRUCTION_BODY_V4, ROOT_INSTRUCTION_BODY_V5, ROOT_INSTRUCTION_BODY_V6, ROOT_INSTRUCTION_BODY_V7, ROOT_INSTRUCTION_BODY_V8, ROOT_INSTRUCTION_BODY, LEGACY_AGENTSMESH_ROOT_INSTRUCTION_PARAGRAPH, LEGACY_AGENTSMESH_ROOT_INSTRUCTION_SECTION, AGENTSMESH_CONTRACT_WITH_V1_BODY, AGENTSMESH_CONTRACT_WITH_V2_BODY, AGENTSMESH_CONTRACT_WITH_V3_BODY, AGENTSMESH_CONTRACT_WITH_V4_BODY, AGENTSMESH_CONTRACT_WITH_V5_BODY, AGENTSMESH_CONTRACT_WITH_V6_BODY, AGENTSMESH_CONTRACT_WITH_V7_BODY, AGENTSMESH_CONTRACT_WITH_V8_BODY, AGENTSMESH_ROOT_INSTRUCTION_PARAGRAPH, LEGACY_FORMS;
|
|
1027
1027
|
var init_root_instruction_paragraph = __esm({
|
|
1028
1028
|
"src/targets/projection/root-instruction-paragraph.ts"() {
|
|
1029
1029
|
init_managed_blocks();
|
|
@@ -1034,7 +1034,8 @@ var init_root_instruction_paragraph = __esm({
|
|
|
1034
1034
|
ROOT_INSTRUCTION_BODY_V5 = "Use Claude-style Markdown in `.agentsmesh`: `agents/*.md`, `commands/*.md`, and `skills/*/SKILL.md`; keep rules in `rules/*.md`, hooks in `hooks.yaml`, MCP in `mcp.json`, permissions in `permissions.yaml`, and ignore patterns in `ignore`, then run `agentsmesh generate`.";
|
|
1035
1035
|
ROOT_INSTRUCTION_BODY_V6 = "Create canonical files in `.agentsmesh`: `rules/_root.md` and `rules/*.md` are Markdown rules; `commands/*.md`, `agents/*.md`, and `skills/*/SKILL.md` plus supporting files use Claude-style frontmatter Markdown; `mcp.json` is MCP JSON; `hooks.yaml` and `permissions.yaml` are YAML; `ignore` is gitignore-style text. Then run `agentsmesh generate`.";
|
|
1036
1036
|
ROOT_INSTRUCTION_BODY_V7 = "`.agentsmesh` is the only folder you edit or add these files in: `rules/_root.md` and `rules/*.md` are Markdown rules; `commands/*.md`, `agents/*.md`, and `skills/*/SKILL.md` plus supporting files use Claude-style frontmatter Markdown; `mcp.json` is MCP JSON; `hooks.yaml` and `permissions.yaml` are YAML; `ignore` is gitignore-style text. Do not edit generated tool files; run `agentsmesh generate`.";
|
|
1037
|
-
|
|
1037
|
+
ROOT_INSTRUCTION_BODY_V8 = "`agentsmesh.yaml` selects targets/features (`agentsmesh.local.yaml` overrides locally), and `.agentsmesh` is the only place to add or edit canonical items: `rules/_root.md`, `rules/*.md`, `commands/*.md`, `agents/*.md`, `skills/*/SKILL.md` plus supporting files, `mcp.json`, `hooks.yaml`, `permissions.yaml`, and `ignore`; if missing run `agentsmesh init`, use `agentsmesh import --from <tool>` for native configs, `agentsmesh install <source>` or `install --sync` for reusable packs, then run `agentsmesh generate`. Use `diff`, `lint`, `check`, `watch`, `matrix`, and `merge` as needed; never edit generated tool files.";
|
|
1038
|
+
ROOT_INSTRUCTION_BODY = "`agentsmesh.yaml` selects targets/features (`agentsmesh.local.yaml` overrides locally), and `.agentsmesh` is the only place to add or edit canonical items: `rules/_root.md`, `rules/*.md`, `commands/*.md`, `agents/*.md`, `skills/*/SKILL.md` plus supporting files, `mcp.json`, `hooks.yaml`, `permissions.yaml`, and `ignore`; if missing run `agentsmesh init`, use `agentsmesh import --from <tool>` for native configs, `agentsmesh install <source>` or `install --sync` for reusable packs, then run `agentsmesh generate`. Use `diff`, `lint`, `check`, `watch`, `matrix`, `merge`, and `refresh` as needed; never edit generated tool files.";
|
|
1038
1039
|
LEGACY_AGENTSMESH_ROOT_INSTRUCTION_PARAGRAPH = ROOT_INSTRUCTION_BODY_V1;
|
|
1039
1040
|
LEGACY_AGENTSMESH_ROOT_INSTRUCTION_SECTION = `## Project-Specific Rules
|
|
1040
1041
|
|
|
@@ -1060,12 +1061,16 @@ ${ROOT_INSTRUCTION_BODY_V6}`;
|
|
|
1060
1061
|
AGENTSMESH_CONTRACT_WITH_V7_BODY = `## AgentsMesh Generation Contract
|
|
1061
1062
|
|
|
1062
1063
|
${ROOT_INSTRUCTION_BODY_V7}`;
|
|
1064
|
+
AGENTSMESH_CONTRACT_WITH_V8_BODY = `## AgentsMesh Generation Contract
|
|
1065
|
+
|
|
1066
|
+
${ROOT_INSTRUCTION_BODY_V8}`;
|
|
1063
1067
|
AGENTSMESH_ROOT_INSTRUCTION_PARAGRAPH = `${ROOT_CONTRACT_START}
|
|
1064
1068
|
## AgentsMesh Generation Contract
|
|
1065
1069
|
|
|
1066
1070
|
${ROOT_INSTRUCTION_BODY}
|
|
1067
1071
|
${ROOT_CONTRACT_END}`;
|
|
1068
1072
|
LEGACY_FORMS = [
|
|
1073
|
+
AGENTSMESH_CONTRACT_WITH_V8_BODY,
|
|
1069
1074
|
AGENTSMESH_CONTRACT_WITH_V7_BODY,
|
|
1070
1075
|
AGENTSMESH_CONTRACT_WITH_V6_BODY,
|
|
1071
1076
|
AGENTSMESH_CONTRACT_WITH_V5_BODY,
|
|
@@ -19044,7 +19049,9 @@ function parseGitSource(source) {
|
|
|
19044
19049
|
} catch {
|
|
19045
19050
|
return null;
|
|
19046
19051
|
}
|
|
19047
|
-
|
|
19052
|
+
const allowInsecure = process.env.AGENTSMESH_ALLOW_INSECURE_GIT === "1" || process.env.AGENTSMESH_ALLOW_INSECURE_GIT === "true";
|
|
19053
|
+
const allowedProtocols = allowInsecure ? ["https:", "http:", "ssh:", "file:"] : ["https:", "ssh:", "file:"];
|
|
19054
|
+
if (!allowedProtocols.includes(parsedUrl.protocol)) {
|
|
19048
19055
|
return null;
|
|
19049
19056
|
}
|
|
19050
19057
|
return { url, ref };
|
|
@@ -20984,10 +20991,12 @@ async function loadConfig(configPath) {
|
|
|
20984
20991
|
}
|
|
20985
20992
|
return result.data;
|
|
20986
20993
|
}
|
|
20994
|
+
var PROTOTYPE_POLLUTION_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
20987
20995
|
function deepMergeObjects(base, overrides2) {
|
|
20988
20996
|
const result = { ...base };
|
|
20989
20997
|
for (const [k, v] of Object.entries(overrides2)) {
|
|
20990
20998
|
if (v === null || v === void 0) continue;
|
|
20999
|
+
if (PROTOTYPE_POLLUTION_KEYS.has(k)) continue;
|
|
20991
21000
|
const baseVal = result[k];
|
|
20992
21001
|
if (typeof v === "object" && !Array.isArray(v) && v !== null && typeof baseVal === "object" && baseVal !== null && !Array.isArray(baseVal)) {
|
|
20993
21002
|
result[k] = deepMergeObjects(
|