@dsh-electron/dsh-plugin-multi-root-workspace 0.1.14 → 0.1.15

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 (61) hide show
  1. package/CHANGELOG.en.md +20 -1
  2. package/CHANGELOG.md +20 -1
  3. package/README.en.md +24 -7
  4. package/README.md +24 -7
  5. package/cordis.patch.yml +5 -1
  6. package/lib/{llm-message-fYORacvh.js → agent-instructions-BXuKhOzr.js} +1 -84
  7. package/lib/auto-review.js +2 -2
  8. package/lib/client.js +208 -26
  9. package/lib/client.js.map +1 -1
  10. package/lib/{command-Dcz6nULo.js → command-y5tfcAeC.js} +21 -10
  11. package/lib/command.js +1 -1
  12. package/lib/compat-CTJbpCxu.js +267 -0
  13. package/lib/compat.js +1 -1
  14. package/lib/{dialects-Dr4rDR-H.js → dialects-CdlG3RF9.js} +1 -1
  15. package/lib/{compat-veDqCOoa.js → dsh-version-DCwF7REk.js} +2 -264
  16. package/lib/index.js +10 -8
  17. package/lib/{instructions-KsUwUFE0.js → instructions-Ck9wp4J4.js} +695 -26
  18. package/lib/instructions.js +1 -1
  19. package/lib/llm-message-CepyzEBQ.js +84 -0
  20. package/lib/lsp.js +3 -3
  21. package/lib/{metadata-DgqSdVX_.js → metadata-99jgViHQ.js} +1 -1
  22. package/lib/{method-wrapper-CFY3p3j2.js → method-wrapper-D4oUCsEF.js} +22 -2
  23. package/lib/{native-win32-CRNXr11U.js → native-win32-C5oMtt7f.js} +1 -1
  24. package/lib/ops-compatibility.js +58 -0
  25. package/lib/ops-settings-UN2oOKEp.js +40 -0
  26. package/lib/ops-tools-k4JYaOrM.js +152 -0
  27. package/lib/process-fence.js +100 -24
  28. package/lib/{protocol-CnR1KTXy.js → protocol-CwDnFEyK.js} +8 -4
  29. package/lib/{registry-DR_sMuvS.js → registry-DyDNDSJZ.js} +235 -79
  30. package/lib/{registry-lease-win32-DDO-c7ig.js → registry-lease-win32-r4puac9m.js} +1 -1
  31. package/lib/registry.js +2 -2
  32. package/lib/{review-context-NDpKeMCc.js → review-context-B-oHkU6a.js} +24 -7
  33. package/lib/review-context.js +1 -1
  34. package/lib/{roots-Ca_W1S9n.js → roots-CmaWxRVy.js} +112 -22
  35. package/lib/{sandbox-C-vCJCp3.js → sandbox-CZElTNPz.js} +1 -1
  36. package/lib/sandbox.js +1 -1
  37. package/lib/scope.js +72 -9
  38. package/lib/skills-utyuvmwr.js +104 -0
  39. package/lib/skills.js +4 -105
  40. package/lib/{tool-context-CpgIFcCB.js → tool-context-CCeY3mrK.js} +1 -1
  41. package/lib/types/authority/protocol.d.ts +5 -1
  42. package/lib/types/client/locales.d.ts +28 -0
  43. package/lib/types/client/ops-settings.d.ts +6 -0
  44. package/lib/types/client/skills-settings.d.ts +3 -2
  45. package/lib/types/command.d.ts +3 -3
  46. package/lib/types/compat/instruction-activity.d.ts +32 -1
  47. package/lib/types/compat/ops-confirmation.d.ts +17 -0
  48. package/lib/types/compat/ops-settings.d.ts +11 -0
  49. package/lib/types/compat/ops-tools.d.ts +25 -0
  50. package/lib/types/compat/skills-client.d.ts +1 -0
  51. package/lib/types/compat/tool-context.d.ts +4 -0
  52. package/lib/types/compat.d.ts +2 -2
  53. package/lib/types/instructions.d.ts +6 -1
  54. package/lib/types/ops-compatibility.d.ts +34 -0
  55. package/lib/types/registry.d.ts +67 -8
  56. package/lib/types/review-context.d.ts +2 -0
  57. package/lib/types/roots.d.ts +114 -20
  58. package/lib/types/scope.d.ts +28 -2
  59. package/lib/workspace-files.js +3 -3
  60. package/lib/{workspace-routing-YPxaQ4V3.js → workspace-routing-D_qwMUh-.js} +4 -4
  61. package/package.json +5 -1
@@ -14,15 +14,21 @@
14
14
  * misconfiguration, not a path to guess at.
15
15
  * 2. `missing` — the directory does not exist.
16
16
  * 3. `not-a-directory` — it exists but is not a directory.
17
- * 4. `equals-primary` — the canonical candidate IS the session's workspace
17
+ * 4. `protected-path` — the canonical candidate equals, lies inside, or
18
+ * contains a protected DSH directory (the DSH home, the registry's
19
+ * physical storage medium, or the lease directory, supplied through
20
+ * {@link RootCandidateContext.protectedPaths}): those hold the store,
21
+ * the lease, profiles and user credentials, so no access level may
22
+ * register them. Applies to every mode; no remote exemption exists.
23
+ * 5. `equals-primary` — the canonical candidate IS the session's workspace
18
24
  * root, which is already granted by the upstream policy.
19
- * 5. `primary-overlap` — the candidate lies under the workspace root, or
25
+ * 6. `primary-overlap` — the candidate lies under the workspace root, or
20
26
  * contains it. The union grants nothing new, and the displayed list would
21
27
  * stop matching the enforced root set.
22
- * 6. `duplicate` — another registered root canonicalizes to the same path.
23
- * 7. `nested` — the candidate lies under a registered root, or contains one.
24
- * Same reasoning as rule 5, applied to the additional roots.
25
- * 8. `unsupported-path` — on Windows, a network or device path (`\\server`,
28
+ * 7. `duplicate` — another registered root canonicalizes to the same path.
29
+ * 8. `nested` — the candidate lies under a registered root, or contains one.
30
+ * Same reasoning as rule 6, applied to the additional roots.
31
+ * 9. `unsupported-path` — on Windows, a network or device path (`\\server`,
26
32
  * `\\?\…`, `\\.\…`): the ACL confinement cannot be shown to cover it, so it
27
33
  * is rejected lexically before any filesystem touch.
28
34
  *
@@ -67,6 +73,23 @@ export declare function additionalRootId(value: string): AdditionalRootId;
67
73
  * `read-only` session; it does not widen anything beyond its own directory.
68
74
  */
69
75
  export type RootAccess = 'inherit' | 'read-write' | 'read-only';
76
+ /**
77
+ * Which protected DSH directory one boundary belongs to: the harness home
78
+ * (`$DSH_HOME`), the registry's physical storage medium, or the directory the
79
+ * store-wide lease occupies.
80
+ */
81
+ export type ProtectedPathKind = 'dsh-home' | 'registry-medium' | 'registry-lease';
82
+ /**
83
+ * One directory no additional root may equal, lie inside, or contain. `path`
84
+ * is an absolute spelling — the configured one, the current canonical one, or
85
+ * a canonical value pinned the first time the boundary resolved — and is
86
+ * compared with {@link samePath}/{@link isCanonicallyUnder} exactly like
87
+ * every other boundary in this module.
88
+ */
89
+ export interface ProtectedPath {
90
+ readonly kind: ProtectedPathKind;
91
+ readonly path: string;
92
+ }
70
93
  /** One registered additional root exactly as it is persisted. */
71
94
  export interface RegisteredRoot {
72
95
  /** Stable registry identity. */
@@ -139,13 +162,20 @@ export interface RootStatus extends RegisteredRoot {
139
162
  *
140
163
  * `'membership-conflict'` means the record passed every structural rule —
141
164
  * including an intact, absolute recorded grant — but its recorded boundary
142
- * overlaps another record's. Neither membership may be granted while the
143
- * conflict stands, yet the boundary remains a trustworthy negative
144
- * restriction, so {@link reservedRootBoundary} keeps it reserved and the
145
- * registry publishes the root as a withheld, deny-only entry. Every other
146
- * `invalid` verdict left the grant unverifiable, so it contributes nothing.
165
+ * overlaps another record's. `'protected-path'` means that same intact
166
+ * grant overlaps a protected DSH directory (the home, the storage medium,
167
+ * or the lease directory) — typically a store written before the boundary
168
+ * existed or on another layout. `'primary-overlap'` means the intact grant
169
+ * equals or overlaps the CURRENT canonical primary root — e.g. a primary
170
+ * spelling retargeted onto (or above) a read-only root — which withholds
171
+ * membership but must never drop the deny. All three reasons mean the same
172
+ * thing: the boundary is intact and trustworthy, so
173
+ * {@link reservedRootBoundary} keeps it reserved and the registry
174
+ * publishes the root as a withheld, deny-only entry — only the membership
175
+ * is withheld. Every other `invalid` verdict left the grant unverifiable,
176
+ * so it contributes nothing.
147
177
  */
148
- readonly invalidReason?: 'membership-conflict';
178
+ readonly invalidReason?: 'membership-conflict' | 'protected-path' | 'primary-overlap';
149
179
  }
150
180
  /** One status together with the 1-based ordinal the surfaces accept for it. */
151
181
  export interface IndexedRootStatus {
@@ -172,7 +202,7 @@ export type RootRef = {
172
202
  readonly addedAt: string;
173
203
  };
174
204
  /** Every way root handling can fail, as a stable code the surfaces localize. */
175
- export type RootValidationCode = 'not-absolute' | 'missing' | 'not-a-directory' | 'equals-primary' | 'primary-overlap' | 'duplicate' | 'nested' | 'invalid-alias' | 'invalid-alias-id' | 'not-found' | 'invalid-ref' | 'storage-unavailable' | 'registry-contended' | 'root-limit-reached' | 'config-invalid' | 'reveal-unavailable' | 'session-not-found' | 'unsupported-path' | 'root-unavailable';
205
+ export type RootValidationCode = 'not-absolute' | 'missing' | 'not-a-directory' | 'equals-primary' | 'primary-overlap' | 'duplicate' | 'nested' | 'invalid-alias' | 'invalid-alias-id' | 'not-found' | 'invalid-ref' | 'storage-unavailable' | 'registry-contended' | 'root-limit-reached' | 'config-invalid' | 'reveal-unavailable' | 'session-not-found' | 'unsupported-path' | 'root-unavailable' | 'protected-path' | 'protected-boundary-unknown' | 'revive-target-changed';
176
206
  /** A rejected root operation; `code` is the stable contract, `message` the fallback prose. */
177
207
  export declare class RootValidationError extends Error {
178
208
  readonly code: RootValidationCode;
@@ -200,6 +230,21 @@ export declare class RootValidationError extends Error {
200
230
  * @returns the expanded path (still unvalidated).
201
231
  */
202
232
  export declare function expandRootInput(raw: string, home?: string): string;
233
+ /**
234
+ * The Registry/scope lookup key of one primary root: the UPSTREAM spelling,
235
+ * normalized lexically by `path.resolve` and case-folded on Windows — never
236
+ * re-resolved. Upstream `dsh-sandbox-policy` sets `policy.workspaceRoot` to
237
+ * `session.header.cwd` verbatim, so the spelling IS the identity: a symlinked
238
+ * workspace and its realpath target are different sessions and different key
239
+ * spaces. Re-resolving here would let a replaced workspace directory
240
+ * silently re-key the record set onto another identity — the exact failure
241
+ * the redirect rules exist to prevent. This is the ONLY function allowed to
242
+ * turn a primary root into a Registry key; path geometry keeps using
243
+ * {@link canonicalRoot}.
244
+ */
245
+ export declare function primaryKey(path: string, platform?: NodeJS.Platform): string;
246
+ /** Whether two canonical paths name the same directory on this platform. */
247
+ export declare function samePath(left: string, right: string): boolean;
203
248
  /**
204
249
  * Whether `candidate` is `root` itself or lies beneath it. Both sides must
205
250
  * already be canonical: symlinks are resolved by then, so the lexical test is
@@ -242,6 +287,15 @@ export interface RootCandidateContext {
242
287
  * revived exempts only itself.
243
288
  */
244
289
  readonly existing: readonly string[];
290
+ /**
291
+ * The protected DSH directories — the home, the registry's storage medium,
292
+ * and the lease directory — that no candidate may equal, lie inside, or
293
+ * contain, at any access level and under any mode. Derived from the trusted
294
+ * host configuration by the registry; absent means "no protected set was
295
+ * provided" and is never a substitute for the boundary being unprovable
296
+ * (the caller refuses that case itself with `protected-boundary-unknown`).
297
+ */
298
+ readonly protectedPaths?: readonly ProtectedPath[];
245
299
  }
246
300
  /**
247
301
  * Judge one candidate root and return its canonical spelling.
@@ -251,6 +305,16 @@ export interface RootCandidateContext {
251
305
  * @throws {RootValidationError} with the first rule that failed.
252
306
  */
253
307
  export declare function validateRootCandidate(raw: string, context: RootCandidateContext): string;
308
+ /**
309
+ * The first protected directory `candidate` overlaps — equals, lies inside,
310
+ * or contains. Both sides must already be canonical (or absolute boundary
311
+ * spellings): the comparison is the same lexical three-way test the other
312
+ * boundary rules use, so a symlink alias can never route around it.
313
+ * @param candidate - the canonical candidate or recorded grant to test.
314
+ * @param protectedPaths - the protected DSH directories.
315
+ * @returns the overlapping protected path, or `undefined`.
316
+ */
317
+ export declare function protectedOverlap(candidate: string, protectedPaths: readonly ProtectedPath[]): ProtectedPath | undefined;
254
318
  /**
255
319
  * Re-judge every stored record of one primary root — the startup and refresh
256
320
  * path, which must never throw: a registration that stopped being usable is
@@ -262,26 +326,56 @@ export declare function validateRootCandidate(raw: string, context: RootCandidat
262
326
  * @param primaryRoot - the session workspace root the records belong to.
263
327
  * @param records - the stored records, in registry order.
264
328
  * @param platform - the platform the rules run for; defaults to `process.platform`.
329
+ * @param protectedPaths - the protected DSH directories a stored grant may not
330
+ * overlap; a record that does is reported `invalid` with
331
+ * `invalidReason: 'protected-path'` — withheld, never deleted, never re-granted.
265
332
  * @returns one status per record, in the same order.
266
333
  */
267
- export declare function classifyStoredRoots(primaryRoot: string, records: readonly RegisteredRoot[], platform?: NodeJS.Platform): RootStatus[];
334
+ export declare function classifyStoredRoots(primaryRoot: string, records: readonly RegisteredRoot[], platform?: NodeJS.Platform, protectedPaths?: readonly ProtectedPath[]): RootStatus[];
335
+ /**
336
+ * The single recorded grant a record or status carries — `undefined` unless
337
+ * EXACTLY one grant field holds a non-empty string. Shared by the conflict
338
+ * pass and {@link reservedRootBoundary}; the stored decoder is the source of
339
+ * the access level, so this never cross-checks the field against `access`.
340
+ */
341
+ export declare function singleGrantOf(record: {
342
+ readonly recordedPath?: string;
343
+ readonly readOnlyRecordedPath?: string;
344
+ readonly readWriteRecordedPath?: string;
345
+ }): string | undefined;
268
346
  /**
269
347
  * The canonical roots a status list grants, in registry order.
270
348
  * @param statuses - the statuses to project.
271
349
  * @returns the canonical paths of every `available` root.
272
350
  */
273
351
  export declare function availableRoots(statuses: readonly RootStatus[]): string[];
352
+ /**
353
+ * Apply the protected-path verdict to an already-classified list: a status
354
+ * whose recorded grant overlaps a protected directory becomes `invalid` with
355
+ * `invalidReason: 'protected-path'` — the same verdict the pass-one stored
356
+ * check produces, here applied by a consumer that classified elsewhere (a
357
+ * shared-registry follower re-judging an owner snapshot against ITS OWN
358
+ * protected set, which may differ from the owner's). Statuses already
359
+ * `invalid` keep their own verdict — a structural failure is never healed
360
+ * into a trustworthy boundary.
361
+ * @param statuses - the classified statuses to re-mark.
362
+ * @param protectedPaths - the caller's own protected DSH directories.
363
+ * @returns a new list with the verdict applied.
364
+ */
365
+ export declare function markProtectedPaths(statuses: readonly RootStatus[], protectedPaths: readonly ProtectedPath[]): RootStatus[];
274
366
  /**
275
367
  * The recorded boundary one status still RESERVES: the grant it was
276
368
  * registered for, which a new or revived registration may not overlap merely
277
369
  * because the root is currently unusable.
278
370
  *
279
- * `available`, `missing`, `redirected`, and membership-conflict statuses all
280
- * carry an intact, trustworthy recorded grant; every other `invalid` verdict
281
- * means the grant could not be parsed at all, so it reserves — and denies —
282
- * nothing. This is not an independent authorization table: it derives the
283
- * boundary from the status list {@link classifyStoredRoots} produced, with
284
- * the same grant-extraction semantics, so its answer can never disagree with
371
+ * `available`, `missing`, `redirected`, and the trustworthy-boundary
372
+ * `invalid` verdicts (`membership-conflict`, `protected-path`, and
373
+ * `primary-overlap`) all carry
374
+ * an intact recorded grant; every other `invalid` verdict means the grant
375
+ * could not be parsed at all, so it reserves — and denies — nothing. This is
376
+ * not an independent authorization table: it derives the boundary from the
377
+ * status list {@link classifyStoredRoots} produced, with the same
378
+ * grant-extraction semantics, so its answer can never disagree with
285
379
  * classification.
286
380
  * @param status - one classified registration.
287
381
  * @returns the absolute recorded boundary, or `undefined` when there is none.
@@ -17,7 +17,8 @@
17
17
  */
18
18
  import { Context, Service } from '@deepseek-ai/cordis';
19
19
  import type { SandboxExecutionPolicy, SandboxMode } from '@deepseek-ai/dsh-sandbox';
20
- import type { RootAccess } from './roots.ts';
20
+ import { type RootAccess } from './roots.ts';
21
+ import type { OpsProtectionState } from './compat/ops-tools.ts';
21
22
  declare module '@deepseek-ai/cordis' {
22
23
  interface Context {
23
24
  multiRootScope: MultiRootScopeService;
@@ -210,6 +211,7 @@ export declare class MultiRootScopeService extends Service {
210
211
  private readonly listeners;
211
212
  /** Enclosing {@link MultiRootScopeService.batch} depth; inner calls nest. */
212
213
  private batchDepth;
214
+ readonly opsProtection: OpsProtectionState;
213
215
  /** True when a batched write coalesced its notification into the flush. */
214
216
  private batchDirty;
215
217
  private admissionProof;
@@ -233,6 +235,13 @@ export declare class MultiRootScopeService extends Service {
233
235
  * treated as evidence.
234
236
  */
235
237
  private readonly dshHome;
238
+ /**
239
+ * Whether the row carried a `dshHome` value at all — even an invalid one.
240
+ * The registry's protected-boundary set must tell "no home configured"
241
+ * (absent: nothing to protect) from "a home was supplied and rejected"
242
+ * (invalid: the boundary cannot be proven).
243
+ */
244
+ private readonly dshHomeSupplied;
236
245
  /** Observe registry publications; consumers still resolve authority from scopeOf(). */
237
246
  subscribe(listener: () => void): () => void;
238
247
  /**
@@ -257,6 +266,21 @@ export declare class MultiRootScopeService extends Service {
257
266
  toolCaps(): readonly string[];
258
267
  /** Whether malformed toolAccess caps every call, including unknown tools. */
259
268
  get toolAccessInvalid(): boolean;
269
+ /**
270
+ * The validated `dshHome` row config, classified for the registry's
271
+ * protected-boundary set: `absent` when the row carried no value (nothing
272
+ * to protect), `invalid` when it carried a value the constructor rejected
273
+ * (the boundary cannot be proven), `configured` with the accepted absolute
274
+ * path. Read-only — this changes no scope or `protectedHome` semantics.
275
+ */
276
+ get dshHomeConfig(): {
277
+ readonly kind: 'absent';
278
+ } | {
279
+ readonly kind: 'invalid';
280
+ } | {
281
+ readonly kind: 'configured';
282
+ readonly path: string;
283
+ };
260
284
  constructor(ctx: Context, config?: Config);
261
285
  /**
262
286
  * Resolve the scope of one confined call.
@@ -275,6 +299,7 @@ export declare class MultiRootScopeService extends Service {
275
299
  resolve(policy: SandboxExecutionPolicy): FilesystemScope;
276
300
  /** Read-only pending-dispatch projection. Does not mint an ALS execution grant. */
277
301
  previewForTool(policy: SandboxExecutionPolicy, toolName: string): FilesystemScope;
302
+ withToolCap<T>(name: string, fn: () => T): T;
278
303
  /** Execution and review use exactly the same parent-narrowing rule. */
279
304
  private capForTool;
280
305
  private project;
@@ -324,7 +349,8 @@ export declare class MultiRootScopeService extends Service {
324
349
  * M1 uses this from tests and smoke scripts only; M3 feeds it from plugin
325
350
  * storage. It is a plain setter so the transition swaps the data source
326
351
  * without touching any provider.
327
- * @param primaryRoot - canonical primary root to register roots under.
352
+ * @param primaryRoot - primary root the roots belong to; the Registry key
353
+ * is its upstream spelling via {@link primaryKey}, never re-resolved.
328
354
  * @param roots - the additional roots, in display order.
329
355
  */
330
356
  setAdditionalRoots(primaryRoot: string, roots: readonly AdditionalWorkspaceRoot[]): void;
@@ -1,7 +1,7 @@
1
- import { t as wrapMethod } from "./method-wrapper-CFY3p3j2.js";
1
+ import { t as wrapMethod } from "./method-wrapper-D4oUCsEF.js";
2
2
  import { c as withRegistryRead } from "./registry-operation-C2ZV3Jhw.js";
3
- import { l as isCanonicallyUnder } from "./roots-Ca_W1S9n.js";
4
- import { t as routeWorkspacePath } from "./workspace-routing-YPxaQ4V3.js";
3
+ import { l as isCanonicallyUnder } from "./roots-CmaWxRVy.js";
4
+ import { t as routeWorkspacePath } from "./workspace-routing-D_qwMUh-.js";
5
5
  import { posix, resolve, win32 } from "node:path";
6
6
  //#region src/compat/workspace-files.ts
7
7
  function adaptPathWatch(service, wrap) {
@@ -4,7 +4,7 @@ import { canonicalPath } from "@deepseek-ai/dsh-sandbox";
4
4
  async function routeWorkspacePath(ctx, primaryRoot, path, signal, cwd = primaryRoot) {
5
5
  signal?.throwIfAborted();
6
6
  const primary = canonicalPath(primaryRoot);
7
- const roots = [primary, ...ctx.multiRootScope.scopeOf(primary)];
7
+ const roots = [primary, ...ctx.multiRootScope.scopeOf(primaryRoot)];
8
8
  const target = await ctx.fs.resolve(path, {
9
9
  cwd,
10
10
  ...signal === void 0 ? {} : { signal }
@@ -12,11 +12,11 @@ async function routeWorkspacePath(ctx, primaryRoot, path, signal, cwd = primaryR
12
12
  for (const root of roots) {
13
13
  const rootTarget = await ctx.fs.resolve(root, signal === void 0 ? {} : { signal });
14
14
  if (!ctx.fs.contains(rootTarget, target)) continue;
15
- if (root !== primary && !ctx.multiRootScope.scopeOf(primary).includes(root)) return void 0;
15
+ if (root !== primary && !ctx.multiRootScope.scopeOf(primaryRoot).includes(root)) return void 0;
16
16
  signal?.throwIfAborted();
17
17
  return {
18
- primaryRoot: primary,
19
- workspaceRoot: root,
18
+ primaryRoot,
19
+ workspaceRoot: root === primary ? primaryRoot : root,
20
20
  target,
21
21
  absolutePath: ctx.fs.processPath(target)
22
22
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dsh-electron/dsh-plugin-multi-root-workspace",
3
3
  "description": "Multi-root Workspace for DeepSeek Harness: adds N additional roots to the workspace sandbox scope as an out-of-tree bundle",
4
- "version": "0.1.14",
4
+ "version": "0.1.15",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/types/index.d.ts",
@@ -75,6 +75,10 @@
75
75
  "./skills": {
76
76
  "types": "./lib/types/skills.d.ts",
77
77
  "default": "./lib/skills.js"
78
+ },
79
+ "./ops-compatibility": {
80
+ "types": "./lib/types/ops-compatibility.d.ts",
81
+ "default": "./lib/ops-compatibility.js"
78
82
  }
79
83
  },
80
84
  "files": [