@deepseek-ai/dsh-app-boot 0.1.5-rc.2 → 0.1.6-alpha.2

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.
@@ -5,18 +5,22 @@
5
5
  * config expressions, and drive the Cordis Loader against a leaf `cordis.yml` until the tree settles.
6
6
  * @module @deepseek-ai/dsh-app-boot
7
7
  */
8
- import { Context } from '@deepseek-ai/cordis';
8
+ import { Context, type FiberState } from '@deepseek-ai/cordis';
9
9
  import { type Entry } from '@deepseek-ai/cordis-plugin-loader';
10
10
  import { type PatchOptions } from '@deepseek-ai/cordis-plugin-include';
11
11
  import { dshHomePath } from '@deepseek-ai/dsh-home-paths';
12
12
  import { type LaunchEnvironmentSnapshot } from '@deepseek-ai/dsh-launch-environment';
13
+ export { readProfilePatches, resolveTelemetryPatch, type ProfileContext, type ProfilePnpmInvocation } from './profile-context.ts';
14
+ export { sanitizeProfile } from './profile-sanitize.ts';
15
+ export { readProfilePlugins, reconcileProfilePlugins, writeProfileBundles, type ProfilePluginLocation, type ProfilePluginDependency, type ProfilePluginInventory, type ProfilePluginReconciliation, } from './profile-plugins.ts';
13
16
  declare module '@deepseek-ai/cordis' {
14
17
  interface Context {
15
18
  /** Harness-home path resolver available to Loader `!!js` config expressions. */
16
19
  dshHomePath?: typeof dshHomePath;
17
20
  }
18
21
  }
19
- export { composeEntries, DEFAULT_PROFILE_BUNDLES, DEFAULT_PROFILE_PATCH_RELOAD, healProfilesModuleFallback, initProfile, loadProfile, loadProfileDirectory, PROFILE_PATCH_FILENAME, PROFILE_TEMPLATES, PROFILES_DIR, readProfileManifest, resolveBundleDir, resolveProfileDir, writeProfileManifest, type Profile, type ProfileLayer, type ProfileManifest, type ProfileModuleFallbackOptions, type ProfileTemplate, } from './profile.ts';
22
+ export { composeEntries, createProfileResolutionGeneration, DEFAULT_PROFILE_BUNDLES, OPTIONAL_BUNDLES, healProfilesModuleFallback, healIsolatedProfileModuleFallback, unlinkProfileModuleFallback, initProfile, loadProfile, loadProfileDirectory, PROFILE_PATCH_FILENAME, PROFILE_TEMPLATES, PROFILES_DIR, readProfileManifest, resolveBundleDir, resolveProfileDir, writeProfileManifest, type Profile, type ProfileLayer, type ProfileManifest, type ProfileModuleFallbackOptions, type ProfileResolutionEntry, type ProfileResolutionGeneration, type ProfileResolutionMode, type ProfileTemplate, } from './profile.ts';
23
+ export { PluginPackages, type PluginPackage, type PluginPackagesConfig, } from './profile-resolution/service.ts';
20
24
  /**
21
25
  * Resolve the config to boot. Replay swaps a `cordis.yml` basename for
22
26
  * `cordis.snapshot.yml` in the same directory; every other mode keeps the path.
@@ -47,29 +51,14 @@ export declare function loadEnv(binName: string, dir?: string, warn?: (line: str
47
51
  * @throws when either file declares a bootstrap-only variable, except {@link HOME_LAYER_PROXY_NAMES} in the Harness-home file.
48
52
  */
49
53
  export declare function loadLayeredEnv(binName: string, cwd?: string, warn?: (line: string) => void): LaunchEnvironmentSnapshot;
50
- /** Options for live user patch-layer reconciliation. */
51
- export interface UserPatchWatchOptions {
52
- /** Diagnostic prefix used by {@link loadOptionalPatches}. */
53
- binName: string;
54
- /** Absolute path of the watched patch file (a profile's `cordis.patch.yml`). */
55
- filename: string;
56
- /**
57
- * Compose the full patch list for a fresh user-layer generation —
58
- * the same composition the app booted with, so a reload can interleave the
59
- * new user patches between app-owned layers (bundle layers below,
60
- * overlays above). Identity when omitted: the user layer
61
- * is the whole patch list.
62
- */
63
- compose?: (userPatches: PatchOptions[]) => PatchOptions[];
64
- }
65
- /**
66
- * Watch the user patch layer through Cordis HMR and transactionally reapply it to the boot include.
67
- * @param ctx - settled app context containing the root Include and an active HMR service.
68
- * @param options - diagnostic, file, and patch-composition inputs.
69
- * @returns an asynchronous disposer after the exact-path watcher is ready.
70
- * @throws when HMR or the root Include is absent, watcher setup fails, or initial path resolution fails.
54
+ /** Apply one complete patch generation and wait for Loader activation diagnostics.
55
+ * @param ctx Booted root context.
56
+ * @param patches Complete ordered patch list.
57
+ * @param binName Diagnostic prefix.
58
+ * @param requiredIds Explicit enablement targets whose existing failures also reject reconciliation.
59
+ * @returns Diagnostics for unchanged pre-existing inactive entries; new or changed failures reject.
71
60
  */
72
- export declare function watchUserPatches(ctx: Context, options: UserPatchWatchOptions): Promise<() => Promise<void>>;
61
+ export declare function reconcileProfilePatches(ctx: Context, patches: PatchOptions[], binName: string, requiredIds?: readonly string[]): Promise<string[]>;
73
62
  /**
74
63
  * Load an optional patch-list file: a top-level YAML array of loader patch
75
64
  * entries (`@deepseek-ai/cordis-plugin-include`'s `PatchOptions`): id-targeted config
@@ -140,7 +129,7 @@ export declare function renderConfigDump(binName: string, absoluteConfigPath: st
140
129
  * names; relative names continue to resolve beside the configuration file.
141
130
  * @returns the created root Include entry, or `undefined` when a surface
142
131
  * disposed the whole tree (taking the Loader service with it) while the
143
- * transactional create was still settling entry lifecycle.
132
+ * entry creation was in flight.
144
133
  */
145
134
  export declare function mountRootInclude(ctx: Context, absoluteConfigPath: string, patches?: readonly PatchOptions[], bareModuleBaseUrl?: string): Promise<Entry | undefined>;
146
135
  /**
@@ -168,7 +157,7 @@ export declare const FAIL_LOUD_RELEASE_TIMEOUT_MS = 2000;
168
157
  /**
169
158
  * Install before boot to turn a late unhandled plugin-init rejection into one
170
159
  * labelled stderr diagnostic and `exit(1)`. A rejection already included by
171
- * {@link assertEntriesActivated} is ignored during its process checkpoint;
160
+ * {@link auditStartupEntries} is ignored during its process checkpoint;
172
161
  * every other rejection remains fatal. Stdout remains untouched for ACP; the
173
162
  * returned function removes the handler.
174
163
  *
@@ -195,27 +184,65 @@ export declare const FAIL_LOUD_RELEASE_TIMEOUT_MS = 2000;
195
184
  * @returns the uninstaller that removes the rejection handler.
196
185
  */
197
186
  export declare function installFailLoud(binName: string, proc?: FailLoudProcess, release?: () => Promise<void> | void): () => void;
187
+ interface InactiveEntry {
188
+ /** Loader entry used to identify the bootstrap Include and required ids. */
189
+ entry: Entry;
190
+ /** Activation errors and missing services remain distinct for presentation. */
191
+ outcome: {
192
+ kind: 'failed';
193
+ error: unknown;
194
+ phase?: string;
195
+ } | {
196
+ kind: 'pending';
197
+ missing: string[];
198
+ };
199
+ }
200
+ /** Inactive plugin metadata without retaining its Context or Fiber. */
201
+ interface StartupEntryDiagnostic {
202
+ id: string;
203
+ module: string;
204
+ required: boolean;
205
+ fiberState: FiberState | undefined;
206
+ outcome: InactiveEntry['outcome'];
207
+ }
208
+ /** Startup warning or error arguments, including import errors with no Fiber. */
209
+ interface StartupLogRecord {
210
+ ts: number;
211
+ name: string;
212
+ type: string;
213
+ args: readonly unknown[];
214
+ }
215
+ /** Startup audit failure with non-enumerable metadata and original failures as its cause. */
216
+ export declare class StartupError extends Error {
217
+ readonly entries: readonly StartupEntryDiagnostic[];
218
+ /** Root configuration and startup logs, attached by boot after disposal. */
219
+ startup?: {
220
+ configurationPath: string;
221
+ messages: readonly StartupLogRecord[];
222
+ };
223
+ /**
224
+ * @param message - concise terminal diagnostic.
225
+ * @param entries - inactive plugin metadata and original failure values.
226
+ */
227
+ constructor(message: string, entries: readonly StartupEntryDiagnostic[]);
228
+ }
198
229
  /**
199
- * After the tree settles, reject entries with no fiber and name every plugin
200
- * whose module failed to resolve. Disabled entries are the only valid
201
- * fiber-less state.
202
- * @param ctx - the settled context whose loader entries to audit.
203
- * @param binName - the diagnostic prefix on the thrown error.
204
- */
205
- export declare function assertEntriesLoaded(ctx: Context, binName: string): void;
206
- /**
207
- * Reject a settled Loader tree when an enabled entry failed or remains inactive.
208
- * Plugin failures include the original thrown stack; pending entries name their
209
- * unresolved services because no plugin error exists for that state. Active
210
- * entries require no further wait; only failed fibers are awaited to recover
211
- * their private rejection reason.
230
+ * Apply DSH startup policy to a settled Loader tree.
231
+ *
232
+ * Inactive entries from the global required list reject startup. Other
233
+ * inactive entries join that failure diagnostic, or produce one warning when
234
+ * no required entry failed and leave successful siblings running.
235
+ * Required ids absent from the tree, and disabled required entries, are ignored.
236
+ * A throwing disabled expression is an entry failure, not a disabled entry.
237
+ * The bootstrap Include must activate so unreadable or invalid root config is fatal.
212
238
  * @param ctx - the settled context whose Loader entries to audit.
213
- * @param binName - the diagnostic prefix on the thrown error.
214
- * @returns nothing when every enabled entry is active.
215
- * @throws after one process rejection checkpoint when an entry failed to
216
- * import, rejected during activation, or did not become active.
239
+ * @param binName - the prefix on startup diagnostics.
240
+ * @param warn - sink for optional-entry warnings.
241
+ * @returns after optional warnings if required startup checks pass.
242
+ * @throws {@link StartupError} when the bootstrap Include or a required entry is inactive or its disabled expression throws;
243
+ * its message includes optional failures too.
217
244
  */
218
- export declare function assertEntriesActivated(ctx: Context, binName: string): Promise<void>;
245
+ export declare function auditStartupEntries(ctx: Context, binName: string, warn?: (line: string) => void): Promise<void>;
219
246
  /**
220
247
  * Boot the Loader against `absoluteConfigPath` and return only after the whole
221
248
  * tree settles. Relative entry names resolve against the config directory;
@@ -224,12 +251,11 @@ export declare function assertEntriesActivated(ctx: Context, binName: string): P
224
251
  * is statically imported and mounted as the `cordis:include` builtin, loading
225
252
  * through the ambient module pipeline (vite/tsx/plain ESM). The package build
226
253
  * embeds Include while leaving Loader external, so the built include tree and
227
- * host share one Loader peer. Loader
228
- * settlement rejects startup failures, which `boot` wraps after disposing the
229
- * partial context; a missing fiber or never-activating entry is rejected by
230
- * the final audit, {@link assertEntriesActivated}, which rethrows a plugin's
231
- * init rejection with its original stack; later unhandled rejections remain
232
- * covered by {@link installFailLoud}. Built bins need the Loader's native
254
+ * host share one Loader peer. Loader settlement drains entry work without
255
+ * rejecting the whole tree. The final {@link auditStartupEntries} call rejects
256
+ * failures in the global required list and warns about other failed, missing,
257
+ * and pending entries while successful siblings remain active. Later unhandled
258
+ * rejections remain covered by {@link installFailLoud}. Built bins need the Loader's native
233
259
  * helper for bare plugin specifiers; relative specifiers do not.
234
260
  * @param binName - the diagnostic prefix for load-failure errors.
235
261
  * @param absoluteConfigPath - the config to include; must already be absolute
@@ -240,11 +266,13 @@ export declare function assertEntriesActivated(ctx: Context, binName: string): P
240
266
  * @param bareModuleBaseUrl - optional installed-host base for bare package
241
267
  * names; use it when the host, rather than the configuration project, owns the
242
268
  * complete plugin set.
243
- * @returns the root context once every entry has started, or as soon as a
269
+ * @returns the root context after the initial startup audit, or as soon as a
244
270
  * surface disposed the tree while startup was still in flight.
245
- * @throws a labelled error after disposing the partial context — `host
271
+ * @throws {@link StartupError} for an inactive required entry, including all inactive plugins in its message;
272
+ * otherwise a labelled error after disposing the partial context — `host
246
273
  * preparation failed` when `prepare` threw before any config-tree entry
247
- * mounted, `plugin tree failed to load` afterwards.
274
+ * mounted, `plugin tree failed to load` afterwards. Cyclic causes terminate
275
+ * diagnostic traversal without replacing the original cause.
248
276
  */
249
277
  export declare function boot(binName: string, absoluteConfigPath: string, patches?: PatchOptions[], prepare?: (ctx: Context) => Promise<void> | void, bareModuleBaseUrl?: string): Promise<Context>;
250
278
  /** Prompt-section name for the harness-source location line an app bin adds after boot. */
@@ -0,0 +1,51 @@
1
+ import { type Profile } from './profile.ts';
2
+ import type { PatchOptions } from '@deepseek-ai/cordis-plugin-include';
3
+ /** Application-owned package manager executable; environment applies only to package operations. */
4
+ export interface ProfilePnpmInvocation {
5
+ readonly command: string;
6
+ readonly args: readonly string[];
7
+ readonly env: Readonly<Record<string, string>>;
8
+ }
9
+ /** Current profile facts; scheduling and mutation belong to their callers. */
10
+ export interface ProfileContext {
11
+ readonly name: string;
12
+ /** Packaged applications supply their bundled runtime instead of a PATH executable. */
13
+ readonly packageManager?: ProfilePnpmInvocation;
14
+ readonly dir: string;
15
+ readonly patchPath: string;
16
+ readonly installAnchor: string;
17
+ readonly cwd: string;
18
+ readonly home: string;
19
+ /** Bundle packages used to start this process, before any persisted edits. */
20
+ readonly startedBundles: readonly string[];
21
+ /** Parsed command-line overlays, applied above profile and home patches. */
22
+ readonly overlays: readonly PatchOptions[];
23
+ /** Launch-time DSH_TELEMETRY_DISABLED value; any non-empty value opts out. */
24
+ readonly telemetryDisabledEnv: string | undefined;
25
+ }
26
+ declare module '@deepseek-ai/cordis' {
27
+ interface Context {
28
+ /** Present only in a profile launched by dsh. */
29
+ profileContext: ProfileContext;
30
+ }
31
+ }
32
+ /**
33
+ * Resolve the telemetry opt-out switch into its boot patch. ANY non-empty
34
+ * value (including `'0'`/`'false'`) disables: a privacy switch prefers
35
+ * off-by-mistake over on-by-mistake. A composition without the telemetry row
36
+ * exports nothing, so the switch is then trivially satisfied and no patch is
37
+ * generated — custom profiles need not mount telemetry to run with the
38
+ * switch set.
39
+ * @param disabledEnv - the raw `DSH_TELEMETRY_DISABLED` value (`undefined` when unset).
40
+ * @param hasRow - whether the composition carries the telemetry row.
41
+ * @returns the disable patch, or `undefined` when no hard-disable patch is required.
42
+ */
43
+ export declare function resolveTelemetryPatch(disabledEnv: string | undefined, hasRow: boolean): PatchOptions | undefined;
44
+ /** Read current bundle and user layers with the launch-time overlays.
45
+ * @param binName Diagnostic prefix for malformed or missing configuration.
46
+ * @param context Data supplied by the profile launcher.
47
+ * @param initialProfile Already loaded startup profile; omitted reads the current files.
48
+ * @returns Detached ordered patches; this function does not update the Loader.
49
+ */
50
+ export declare function readProfilePatches(binName: string, context: ProfileContext, initialProfile?: Profile): PatchOptions[];
51
+ //# sourceMappingURL=profile-context.d.ts.map
@@ -0,0 +1,60 @@
1
+ /** Installed profile dependencies and their bundle activation after package-manager operations. */
2
+ import { type ProfileManifest } from './profile.ts';
3
+ /** Profile directory and installation used by the shared bundle resolver. */
4
+ export interface ProfilePluginLocation {
5
+ /** Diagnostic prefix for profile manifest failures. */
6
+ readonly binName: string;
7
+ /** Profile package directory managed by pnpm. */
8
+ readonly profileDir: string;
9
+ /** Absolute package.json path of the owning dsh installation. */
10
+ readonly installAnchor: string;
11
+ }
12
+ /** One dependency under its package-manager key, including npm aliases. */
13
+ export interface ProfilePluginDependency {
14
+ /** Dependency key used in package.json and the bundle list. */
15
+ readonly name: string;
16
+ /** Installed version, or the dependency spec when installed metadata is unavailable. */
17
+ readonly version: string;
18
+ /** Whether the package selected by the bundle resolver declares a patch. */
19
+ readonly bundle: boolean;
20
+ /** Whether the dependency appears in the profile's active bundle list. */
21
+ readonly enabled: boolean;
22
+ }
23
+ /** Profile manifest and dependencies in manifest order. */
24
+ export interface ProfilePluginInventory {
25
+ readonly manifest: ProfileManifest;
26
+ readonly dependencies: readonly ProfilePluginDependency[];
27
+ }
28
+ /** Result of reconciling bundle activation after a successful package operation. */
29
+ export interface ProfilePluginReconciliation {
30
+ readonly plugins: ProfilePluginInventory;
31
+ /** Newly added ordinary dependencies, for optional caller-owned diagnostics. */
32
+ readonly addedPlainDependencies: readonly string[];
33
+ }
34
+ /**
35
+ * Read installed versions and bundle declarations without requiring loadable plugin code.
36
+ * Missing or unreadable installed metadata leaves the dependency visible for repair or removal.
37
+ * @param location - profile and installation resolution inputs.
38
+ * @returns dependency records in package.json order and the profile manifest.
39
+ */
40
+ export declare function readProfilePlugins(location: ProfilePluginLocation): ProfilePluginInventory;
41
+ /**
42
+ * Write a bundle list while preserving the supplied profile's other metadata.
43
+ * @param profileDir - directory whose package.json is updated.
44
+ * @param manifest - current profile manifest, read after the package operation when applicable.
45
+ * @param bundles - ordered active bundle names, including any template entries.
46
+ * @returns the written manifest.
47
+ */
48
+ export declare function writeProfileBundles(profileDir: string, manifest: ProfileManifest, bundles: readonly string[]): ProfileManifest;
49
+ /**
50
+ * Reconcile installed bundle declarations after a successful package-manager operation.
51
+ * Dependency-managed entries disappear when removed or when their package loses its declaration;
52
+ * template entries retain their order and duplicates. New bundle dependencies activate automatically.
53
+ * @param options - location, inventory captured before pnpm, and whether explicitly disabled bundles remain disabled.
54
+ * @returns updated inventory and newly added ordinary dependencies for caller-owned warnings.
55
+ */
56
+ export declare function reconcileProfilePlugins(options: ProfilePluginLocation & {
57
+ readonly before: ProfilePluginInventory;
58
+ readonly preserveDisabled: boolean;
59
+ }): ProfilePluginReconciliation;
60
+ //# sourceMappingURL=profile-plugins.d.ts.map
@@ -0,0 +1,35 @@
1
+ /** Legacy profile-link inspection shared by the disk materializer and runtime resolver. */
2
+ /** Profile-private package links projected into its pnpm-managed node_modules. */
3
+ export declare const PROFILE_MODULE_FALLBACK_DIR = ".dsh-module-fallback";
4
+ /**
5
+ * Return whether the process reads application modules from pkg's virtual filesystem.
6
+ * @returns whether pkg owns the module filesystem.
7
+ */
8
+ export declare function isPackagedExecutable(): boolean;
9
+ /**
10
+ * Resolve a directory through the active carrier's filesystem implementation.
11
+ * @param path - directory path to canonicalize.
12
+ * @returns the canonical directory path.
13
+ */
14
+ export declare function realModuleDirectory(path: string): string;
15
+ /**
16
+ * Resolve a link target without following the final path component.
17
+ * @param path - candidate path whose parent is canonicalized.
18
+ * @returns the canonical candidate, or undefined when its parent is absent.
19
+ */
20
+ export declare function canonicalLinkPath(path: string): string | undefined;
21
+ /**
22
+ * Return whether a symlink or junction points at the same path as `target`.
23
+ * @param link - symlink or junction to inspect.
24
+ * @param target - expected target path.
25
+ * @returns whether both paths identify the same entry.
26
+ */
27
+ export declare function symlinkPointsTo(link: string, target: string): boolean;
28
+ /**
29
+ * Return whether an observed profile package must not claim local precedence.
30
+ * @param profileDir - profile directory containing the package projection.
31
+ * @param packageName - bare package name to inspect.
32
+ * @returns whether the entry is a managed fallback link or disappeared during inspection.
33
+ */
34
+ export declare function isProfileModuleFallbackLink(profileDir: string, packageName: string): boolean;
35
+ //# sourceMappingURL=legacy-links.d.ts.map
@@ -0,0 +1,43 @@
1
+ /** In-memory profile package routing for Node's default ESM and CommonJS loaders. */
2
+ import type { ProfileResolutionGeneration } from '../profile.ts';
3
+ /** Whether runtime resolution redirects requests or verifies the materialized backend. */
4
+ export type ProfileResolutionBehavior = 'enforce' | 'verify';
5
+ /** Active resolver registration in one Node isolate. */
6
+ export interface ProfileResolutionRegistration {
7
+ /**
8
+ * Locate a bare package without requiring one of its exports.
9
+ * @param specifier - bare package or package-subpath specifier.
10
+ * @param parentURL - file URL whose lookup order applies.
11
+ * @returns selected package directory, or undefined when it is absent.
12
+ */
13
+ packageDir(specifier: string, parentURL: string): string | undefined;
14
+ /**
15
+ * Atomically publish an additive package table and fresh generation-owned caches.
16
+ * @param generation - fully constructed successor generation.
17
+ * @throws when the profile scope or an existing package mapping changes.
18
+ */
19
+ replace(generation: ProfileResolutionGeneration): void;
20
+ /** Restore the native resolver methods. Registrations dispose in reverse order. */
21
+ dispose(): void;
22
+ }
23
+ /**
24
+ * Split a bare request into its package name without allocating path segments.
25
+ * @param request - module specifier to classify.
26
+ * @returns the bare package name, or undefined for non-package requests.
27
+ */
28
+ export declare function barePackageName(request: string): string | undefined;
29
+ /**
30
+ * Install one profile generation on Node's default ESM and CommonJS resolvers.
31
+ * @param generation - complete package table and profile scope.
32
+ * @param behavior - enforce the generation, or verify a materialized generation.
33
+ * @returns a registration that replaces the generation or restores the native methods.
34
+ */
35
+ export declare function installProfileResolution(generation: ProfileResolutionGeneration, behavior?: ProfileResolutionBehavior): ProfileResolutionRegistration;
36
+ /**
37
+ * Publish one generation for Harness-owned Workers.
38
+ * @param generation - complete package table and profile scope.
39
+ * @param behavior - enforce or verify the generation in newly created Workers.
40
+ * @returns a disposer restoring the previous thread environment data.
41
+ */
42
+ export declare function registerWorkerResolution(generation: ProfileResolutionGeneration, behavior?: ProfileResolutionBehavior): () => void;
43
+ //# sourceMappingURL=resolver.d.ts.map
@@ -0,0 +1,51 @@
1
+ /** Package metadata resolved through one profile resolution registration. */
2
+ import { Service, type Context } from '@deepseek-ai/cordis';
3
+ import { type ProfileResolutionBehavior } from './resolver.ts';
4
+ import type { ProfileResolutionGeneration } from '../profile.ts';
5
+ declare module '@deepseek-ai/cordis' {
6
+ interface Context {
7
+ /** Deterministic package lookup for configured plugin specifiers. */
8
+ pluginPackages: PluginPackages;
9
+ }
10
+ }
11
+ /** The package that owns a resolved module. */
12
+ export interface PluginPackage {
13
+ /** Manifest package name. */
14
+ name: string;
15
+ /** Manifest version when declared. */
16
+ version: string | undefined;
17
+ /** Absolute package directory. */
18
+ dir: string;
19
+ /** Absolute package.json path. */
20
+ manifestPath: string;
21
+ /** Parsed manifest shared by metadata readers. */
22
+ manifest: Record<string, unknown>;
23
+ }
24
+ /** Optional runtime resolver installed and owned by {@link PluginPackages}. */
25
+ export interface PluginPackagesConfig {
26
+ /** Complete package table; omit it to expose native package lookup only. */
27
+ generation?: ProfileResolutionGeneration;
28
+ /** Enforce the table or compare it with a materialized fallback. */
29
+ behavior?: ProfileResolutionBehavior;
30
+ }
31
+ /** Package lookup shared by metadata consumers in one profile process. */
32
+ export declare class PluginPackages extends Service {
33
+ private packages;
34
+ private readonly resolver;
35
+ private readonly behavior;
36
+ private disposeWorkerResolution;
37
+ constructor(ctx: Context, config?: PluginPackagesConfig);
38
+ /**
39
+ * Publish an additive generation for this process and subsequently created Workers.
40
+ * @param generation - fully constructed successor generation.
41
+ */
42
+ replace(generation: ProfileResolutionGeneration): void;
43
+ /**
44
+ * Locate the package named by a specifier without requiring a package export.
45
+ * @param specifier - module specifier whose package owns the requested module.
46
+ * @param parentURL - URL whose Node lookup order applies.
47
+ * @returns the parsed package, or undefined when no package owns the request.
48
+ */
49
+ packageOf(specifier: string, parentURL: string): PluginPackage | undefined;
50
+ }
51
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1,3 @@
1
+ /** Install an inherited profile resolution generation in one Harness-owned Worker. */
2
+ export {};
3
+ //# sourceMappingURL=worker-bootstrap.d.ts.map
@@ -0,0 +1,13 @@
1
+ /** Filesystem recovery for callers that own profile shutdown and write exclusion. */
2
+ /**
3
+ * Back up the profile patch and retain only the caller's recovery bundles.
4
+ * The caller must stop the profile and exclude concurrent profile writes.
5
+ * Installed packages and other manifest fields are preserved; patches are never parsed.
6
+ * Failures propagate and may leave completed changes in place for a retry.
7
+ * @param binName - Diagnostic prefix for invalid profile manifests.
8
+ * @param profileDir - Profile directory to recover without loading its plugins.
9
+ * @param bundles - Ordered bundles to enable after recovery.
10
+ * @returns Backup path with a Unix millisecond timestamp and optional collision ordinal, or undefined if absent.
11
+ */
12
+ export declare function sanitizeProfile(binName: string, profileDir: string, bundles: readonly string[]): string | undefined;
13
+ //# sourceMappingURL=profile-sanitize.d.ts.map
@@ -24,7 +24,7 @@
24
24
  */
25
25
  import type { EntryOptions } from '@deepseek-ai/cordis-plugin-loader';
26
26
  import { type PatchOptions } from '@deepseek-ai/cordis-plugin-include';
27
- import type { DshManifest, ProfilePatchReload } from '@deepseek-ai/dsh-package-manifest';
27
+ import type { DshPackageManifest } from '@deepseek-ai/dsh-package-manifest';
28
28
  /** Directory under the Harness home holding every profile. */
29
29
  export declare const PROFILES_DIR = "profiles";
30
30
  /** The user patch layer inside a profile directory (hot-reloaded on long-lived surfaces). */
@@ -33,16 +33,9 @@ export declare const PROFILE_PATCH_FILENAME = "cordis.patch.yml";
33
33
  export interface ProfileTemplate {
34
34
  /** Ordered bundle layer list. */
35
35
  bundles: readonly string[];
36
- /** User patch-file lifecycle for the generated profile. */
37
- patchReload: ProfilePatchReload;
38
- }
39
- /** The slice of package.json both profiles and bundles use. */
40
- export interface ProfileManifest {
41
- name?: string;
42
- dependencies?: Record<string, string>;
43
- peerDependencies?: Record<string, string>;
44
- dsh?: DshManifest;
45
36
  }
37
+ /** Package metadata accepted by the profile reader; local profiles need no published identity. */
38
+ export type ProfileManifest = Partial<DshPackageManifest>;
46
39
  /** One resolved bundle layer of a profile. */
47
40
  export interface ProfileLayer {
48
41
  /** The bundle's package name, as listed in `dsh.profile.bundles`. */
@@ -66,9 +59,33 @@ export interface Profile {
66
59
  patchPath: string;
67
60
  /** The profile's own patches; empty when the file is absent. */
68
61
  patches: PatchOptions[];
69
- /** Whether the launcher watches user patch files after boot. */
70
- patchReload: ProfilePatchReload;
71
62
  }
63
+ /** One package selected by the profile module-fallback rules. */
64
+ export interface ProfileResolutionEntry {
65
+ /** Bare package name. */
66
+ readonly name: string;
67
+ /** Package directory selected by the existing dependency traversal. */
68
+ readonly packageDir: string;
69
+ /** Selected package version when its manifest declares one. */
70
+ readonly version: string | undefined;
71
+ /** Manifest whose dependency edge selected this package. */
72
+ readonly declarer: string;
73
+ /** Whether every profile or only the active profile receives this fallback. */
74
+ readonly scope: 'installation' | 'profile';
75
+ }
76
+ /** Complete immutable fallback table for one profile launch. */
77
+ export interface ProfileResolutionGeneration {
78
+ /** Directory containing every profile and the shared fallback position. */
79
+ readonly profilesDir: string;
80
+ /** Active profile directory, when bundle-only fallbacks were included. */
81
+ readonly profileDir: string | undefined;
82
+ /** Profile-declared packages already installed before the fallback position. */
83
+ readonly localPackageNames: readonly string[];
84
+ /** Installation entries followed by bundle-only entries in precedence order. */
85
+ readonly entries: readonly ProfileResolutionEntry[];
86
+ }
87
+ /** Startup backend selection for one computed profile resolution generation. */
88
+ export type ProfileResolutionMode = 'link' | 'dual' | 'runtime';
72
89
  /**
73
90
  * Resolve a profile's directory under the Harness home.
74
91
  * @param name - the profile name (`dsh --profile <name>`).
@@ -80,17 +97,21 @@ export declare function resolveProfileDir(name: string, home?: string): string;
80
97
  export declare const PROFILE_TEMPLATES: Record<string, ProfileTemplate>;
81
98
  /** The bundle list a `dsh plugin` init uses for a name with no shipped template. */
82
99
  export declare const DEFAULT_PROFILE_BUNDLES: readonly string[];
83
- /** Custom profiles retain the historical live patch-file behavior. */
84
- export declare const DEFAULT_PROFILE_PATCH_RELOAD: ProfilePatchReload;
100
+ /**
101
+ * The bundles the dsh installation ships for a person to switch on: each a
102
+ * runtime dependency of the installation that declares `dsh.bundle.patch`,
103
+ * selected by no shipped template, and offered switched off by the plugin
104
+ * manager ([rationale](../../../../.agents/notes/implemented/process/2026-09-15-shipped-optional-bundles.md)).
105
+ */
106
+ export declare const OPTIONAL_BUNDLES: readonly string[];
85
107
  /**
86
108
  * Initialize a profile directory: manifest, empty user patch layer, and the
87
109
  * pnpm settings out-of-tree plugins need. Existing files are never touched,
88
110
  * so re-running is a no-op on an initialized profile.
89
111
  * @param dir - the profile directory from {@link resolveProfileDir}.
90
112
  * @param bundles - the initial `dsh.profile.bundles` layer list.
91
- * @param patchReload - user patch-file lifecycle; custom profiles default to live reload.
92
113
  */
93
- export declare function initProfile(dir: string, bundles: readonly string[], patchReload?: ProfilePatchReload): void;
114
+ export declare function initProfile(dir: string, bundles: readonly string[]): void;
94
115
  /** Inputs for {@link healProfilesModuleFallback}. */
95
116
  export interface ProfileModuleFallbackOptions {
96
117
  /** Absolute package.json path of the running dsh installation. */
@@ -99,6 +120,8 @@ export interface ProfileModuleFallbackOptions {
99
120
  profile?: Profile;
100
121
  /** Harness home; defaults to {@link resolveDshHome}. */
101
122
  home?: string;
123
+ /** Whether to materialize the computed generation; defaults to true. */
124
+ materialize?: boolean;
102
125
  }
103
126
  /**
104
127
  * Maintain module fallbacks for one profile launch. The shared
@@ -110,9 +133,31 @@ export interface ProfileModuleFallbackOptions {
110
133
  * `node_modules`; pnpm-managed entries remain authoritative, and another
111
134
  * profile's links cannot change its resolution.
112
135
  * @param options - installation anchor, optional loaded profile, and Harness home.
113
- * @returns settlement after the shared fallback and profile-local links are current.
136
+ * @returns the computed fallback generation after optional materialization.
137
+ */
138
+ export declare function healProfilesModuleFallback(options: ProfileModuleFallbackOptions): Promise<ProfileResolutionGeneration>;
139
+ /**
140
+ * Compute a profile resolution generation without materializing links or proxies.
141
+ * @param options - installation anchor, profile, and optional Harness home.
142
+ * @returns the complete immutable generation.
143
+ */
144
+ export declare function createProfileResolutionGeneration(options: Omit<ProfileModuleFallbackOptions, 'materialize'>): Promise<ProfileResolutionGeneration>;
145
+ /**
146
+ * Supply an application-owned profile with filesystem packages from its installation and selected bundles.
147
+ * All fallback links belong to the profile; no shared Harness-home directory is written.
148
+ * Existing pnpm-managed packages remain authoritative. The caller serializes profile mutations.
149
+ * @param options - owning installation package.json and the loaded application profile.
150
+ */
151
+ export declare function healIsolatedProfileModuleFallback(options: {
152
+ installAnchor: string;
153
+ profile: Profile;
154
+ }): void;
155
+ /**
156
+ * Detach this profile's fallback links before a package-manager mutation.
157
+ * Installed packages and links replaced by pnpm remain untouched; the next profile launch restores fallbacks.
158
+ * @param profileDir - profile directory whose package mutation is serialized by the caller.
114
159
  */
115
- export declare function healProfilesModuleFallback(options: ProfileModuleFallbackOptions): Promise<void>;
160
+ export declare function unlinkProfileModuleFallback(profileDir: string): void;
116
161
  /**
117
162
  * Read a profile's manifest.
118
163
  * @param binName - the diagnostic prefix on the thrown error.