@try-works/dsh-model-as-tool 0.1.2 → 0.3.6
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/bin/dsh-tool-mode-credentials.mjs +20 -0
- package/bin/dsh-tool-mode.mjs +202 -0
- package/catalog/desert-ant-models.json +693 -693
- package/client/client.model-tools.js +592 -0
- package/client/index.js +1679 -0
- package/lib/adapters/index.d.ts.map +1 -1
- package/lib/adapters/index.js +4 -4
- package/lib/bindings.d.ts.map +1 -1
- package/lib/bindings.js +26 -25
- package/lib/capability.d.ts.map +1 -1
- package/lib/capability.js +17 -0
- package/lib/capture-path.d.ts +97 -0
- package/lib/capture-path.d.ts.map +1 -0
- package/lib/capture-path.js +152 -0
- package/lib/diagnostics.d.ts +166 -0
- package/lib/diagnostics.d.ts.map +1 -0
- package/lib/diagnostics.js +135 -0
- package/lib/extension-dependencies.d.ts +197 -0
- package/lib/extension-dependencies.d.ts.map +1 -0
- package/lib/extension-dependencies.js +369 -0
- package/lib/extension-dispatch.d.ts +117 -0
- package/lib/extension-dispatch.d.ts.map +1 -0
- package/lib/extension-dispatch.js +124 -0
- package/lib/extension-registrar.d.ts +100 -0
- package/lib/extension-registrar.d.ts.map +1 -0
- package/lib/extension-registrar.js +134 -0
- package/lib/extensions-api.d.ts +217 -0
- package/lib/extensions-api.d.ts.map +1 -0
- package/lib/extensions-api.js +17 -0
- package/lib/extensions-remote.d.ts +54 -0
- package/lib/extensions-remote.d.ts.map +1 -0
- package/lib/extensions-remote.js +66 -0
- package/lib/extensions-service.d.ts +334 -0
- package/lib/extensions-service.d.ts.map +1 -0
- package/lib/extensions-service.js +1351 -0
- package/lib/harness-llm.d.ts +138 -0
- package/lib/harness-llm.d.ts.map +1 -0
- package/lib/harness-llm.js +218 -0
- package/lib/harness-serving.d.ts +95 -0
- package/lib/harness-serving.d.ts.map +1 -0
- package/lib/harness-serving.js +157 -0
- package/lib/headless.d.ts +97 -0
- package/lib/headless.d.ts.map +1 -0
- package/lib/headless.js +349 -0
- package/lib/http-acquisition.d.ts +27 -0
- package/lib/http-acquisition.d.ts.map +1 -0
- package/lib/http-acquisition.js +136 -0
- package/lib/index.d.ts +250 -7
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2625 -70
- package/lib/invocation-accounting.d.ts +169 -0
- package/lib/invocation-accounting.d.ts.map +1 -0
- package/lib/invocation-accounting.js +148 -0
- package/lib/legacy-migration.d.ts +95 -0
- package/lib/legacy-migration.d.ts.map +1 -0
- package/lib/legacy-migration.js +429 -0
- package/lib/managed-workers.d.ts +151 -0
- package/lib/managed-workers.d.ts.map +1 -0
- package/lib/managed-workers.js +249 -0
- package/lib/mcp/client-grant.d.ts +74 -0
- package/lib/mcp/client-grant.d.ts.map +1 -0
- package/lib/mcp/client-grant.js +60 -0
- package/lib/mcp/credential-cli.d.ts +38 -0
- package/lib/mcp/credential-cli.d.ts.map +1 -0
- package/lib/mcp/credential-cli.js +148 -0
- package/lib/mcp/credential-store.d.ts +143 -0
- package/lib/mcp/credential-store.d.ts.map +1 -0
- package/lib/mcp/credential-store.js +454 -0
- package/lib/mcp/host-binding.d.ts +25 -0
- package/lib/mcp/host-binding.d.ts.map +1 -1
- package/lib/mcp/host-binding.js +107 -14
- package/lib/mcp/http.d.ts +68 -18
- package/lib/mcp/http.d.ts.map +1 -1
- package/lib/mcp/http.js +390 -62
- package/lib/mcp/oauth.d.ts +149 -0
- package/lib/mcp/oauth.d.ts.map +1 -0
- package/lib/mcp/oauth.js +396 -0
- package/lib/mcp/protocol.d.ts +46 -7
- package/lib/mcp/protocol.d.ts.map +1 -1
- package/lib/mcp/protocol.js +63 -16
- package/lib/mcp/rate-limit.d.ts +68 -0
- package/lib/mcp/rate-limit.d.ts.map +1 -0
- package/lib/mcp/rate-limit.js +114 -0
- package/lib/mcp/sdk-v2.d.ts +17 -0
- package/lib/mcp/sdk-v2.d.ts.map +1 -0
- package/lib/mcp/sdk-v2.js +181 -0
- package/lib/mcp/server.d.ts +11 -1
- package/lib/mcp/server.d.ts.map +1 -1
- package/lib/mcp/server.js +80 -13
- package/lib/mcp/stdio.d.ts.map +1 -1
- package/lib/mcp/stdio.js +9 -1
- package/lib/mcp/subscriptions.d.ts +43 -1
- package/lib/mcp/subscriptions.d.ts.map +1 -1
- package/lib/mcp/subscriptions.js +90 -9
- package/lib/model-qualification.d.ts +192 -0
- package/lib/model-qualification.d.ts.map +1 -0
- package/lib/model-qualification.js +178 -0
- package/lib/planning.d.ts +246 -0
- package/lib/planning.d.ts.map +1 -0
- package/lib/planning.js +457 -0
- package/lib/prepared-assets.d.ts +178 -0
- package/lib/prepared-assets.d.ts.map +1 -0
- package/lib/prepared-assets.js +286 -0
- package/lib/provider-bridge.d.ts +37 -0
- package/lib/provider-bridge.d.ts.map +1 -1
- package/lib/provider-bridge.js +189 -12
- package/lib/ptc-authority.d.ts +140 -0
- package/lib/ptc-authority.d.ts.map +1 -0
- package/lib/ptc-authority.js +216 -0
- package/lib/ptc-runtime.d.ts +156 -0
- package/lib/ptc-runtime.d.ts.map +1 -0
- package/lib/ptc-runtime.js +191 -0
- package/lib/publication.d.ts +2 -1
- package/lib/publication.d.ts.map +1 -1
- package/lib/publication.js +13 -5
- package/lib/receipt.d.ts.map +1 -1
- package/lib/receipt.js +8 -0
- package/lib/title-proposal.d.ts +194 -0
- package/lib/title-proposal.d.ts.map +1 -0
- package/lib/title-proposal.js +244 -0
- package/lib/tool-mode-run.d.ts +235 -0
- package/lib/tool-mode-run.d.ts.map +1 -0
- package/lib/tool-mode-run.js +283 -0
- package/lib/types.d.ts +3 -1
- package/lib/types.d.ts.map +1 -1
- package/lib/update-surface.d.ts +79 -0
- package/lib/update-surface.d.ts.map +1 -0
- package/lib/update-surface.js +224 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/lang-detect.d.ts +16 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/lang-detect.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/lang-detect.js +85 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/standard.d.ts +38 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/standard.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/standard.js +445 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/stt-transcribe.d.ts +20 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/stt-transcribe.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/stt-transcribe.js +112 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-embed.d.ts +14 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-embed.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-embed.js +63 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-rerank.d.ts +14 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-rerank.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-rerank.js +76 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/dsh.d.ts +82 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/dsh.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/dsh.js +274 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/index.d.ts +26 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/index.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/index.js +48 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/meta.d.ts +104 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/meta.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/meta.js +55 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/registry.d.ts +107 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/registry.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/registry.js +246 -0
- package/node_modules/@try-works/tool-mode-contracts/package.json +34 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/bin/dsh-tool-mode.mjs +20 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/cli.d.ts +80 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/cli.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/cli.js +533 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/declarative-recipes.d.ts +83 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/declarative-recipes.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/declarative-recipes.js +125 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/discovery.d.ts +70 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/discovery.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/discovery.js +201 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/errors.d.ts +57 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/errors.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/errors.js +83 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/index.d.ts +31 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/index.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/index.js +31 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/archive.d.ts +86 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/archive.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/archive.js +185 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/dependencies.d.ts +126 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/dependencies.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/dependencies.js +297 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/lifecycle.d.ts +216 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/lifecycle.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/lifecycle.js +800 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/migration.d.ts +210 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/migration.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/migration.js +368 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/runtime-link.d.ts +63 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/runtime-link.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/runtime-link.js +149 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/source-directory.d.ts +35 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/source-directory.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/source-directory.js +179 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/staged.d.ts +179 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/staged.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/staged.js +235 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/store.d.ts +367 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/store.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/store.js +807 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/update-flow.d.ts +185 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/update-flow.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/update-flow.js +504 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/zip-source.d.ts +17 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/zip-source.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/zip-source.js +80 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/load.d.ts +52 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/load.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/load.js +102 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/manifest.d.ts +171 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/manifest.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/manifest.js +271 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/runtime.d.ts +242 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/runtime.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/runtime.js +58 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/state.d.ts +118 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/state.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/state.js +113 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker/worker-child.d.ts +2 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker/worker-child.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker/worker-child.js +351 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker.d.ts +150 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker.js +670 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/package.json +53 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/schema/extension-manifest-1.1.0.schema.json +253 -0
- package/node_modules/@try-works/tool-mode-recipes/bin/tool-mode-recipe.mjs +15 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/broker.d.ts +427 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/broker.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/broker.js +592 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/cli.d.ts +60 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/cli.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/cli.js +936 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/dsh-adapter.d.ts +237 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/dsh-adapter.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/dsh-adapter.js +200 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/fixture-provider.d.ts +85 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/fixture-provider.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/fixture-provider.js +176 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/images.d.ts +105 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/images.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/images.js +129 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/index.d.ts +25 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/index.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/index.js +25 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/recipe-format.d.ts +325 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/recipe-format.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/recipe-format.js +1310 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/templates.d.ts +51 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/templates.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/templates.js +310 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/titles.d.ts +227 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/titles.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/titles.js +407 -0
- package/node_modules/@try-works/tool-mode-recipes/package.json +56 -0
- package/node_modules/@try-works/tool-mode-recipes/recipes/describe-image.json +38 -0
- package/node_modules/@try-works/tool-mode-recipes/recipes/text-title.json +45 -0
- package/node_modules/@try-works/tool-mode-recipes/schema/harness-model-recipe.schema.json +273 -0
- package/package.json +50 -17
- package/remote/spec.mjs +439 -0
- package/remote/typert.host.mjs +14 -0
- package/remote/typert.remote-client.mjs +16 -0
- package/lib/adapters/tongue.d.ts +0 -31
- package/lib/adapters/tongue.d.ts.map +0 -1
- package/lib/adapters/tongue.js +0 -93
- package/lib/adapters/voz.d.ts +0 -28
- package/lib/adapters/voz.d.ts.map +0 -1
- package/lib/adapters/voz.js +0 -102
- /package/{lib → node_modules/@try-works/tool-mode-extension-sdk/lib}/licensing.d.ts +0 -0
- /package/{lib → node_modules/@try-works/tool-mode-extension-sdk/lib}/licensing.d.ts.map +0 -0
- /package/{lib → node_modules/@try-works/tool-mode-extension-sdk/lib}/licensing.js +0 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Staged installation (EXT-07, EXT-08).
|
|
3
|
+
*
|
|
4
|
+
* The ordering is the security property. A package is fetched into quarantine,
|
|
5
|
+
* verified, inspected as DATA, validated, and only then promoted into the store —
|
|
6
|
+
* and promotion is not activation. Nothing in this module imports extension code,
|
|
7
|
+
* runs inference, warms a model, or publishes a tool (EXT-07).
|
|
8
|
+
*
|
|
9
|
+
* The fetch and extract steps are INJECTED rather than implemented here, for two
|
|
10
|
+
* reasons: the SDK must stay free of a network stack and an archive library, and
|
|
11
|
+
* a test must be able to drive every rejection path without a real download or a
|
|
12
|
+
* real filesystem. The host supplies the real implementations.
|
|
13
|
+
*
|
|
14
|
+
* @module @try-works/tool-mode-extension-sdk/install/staged
|
|
15
|
+
*/
|
|
16
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
17
|
+
import { linkRuntimeSdk, RUNTIME_SDK_NAME } from "./runtime-link.js";
|
|
18
|
+
import { join, resolve, sep } from 'node:path';
|
|
19
|
+
import { ManagementError } from "./../errors.js";
|
|
20
|
+
import { discoverExtension } from "./../discovery.js";
|
|
21
|
+
import { digestInstalledContent, ExtensionStore } from "./store.js";
|
|
22
|
+
import { installDependencyGraph } from "./dependencies.js";
|
|
23
|
+
import { DEFAULT_ARCHIVE_LIMITS, validateArchiveEntries, verifyIntegrity, } from "./archive.js";
|
|
24
|
+
/** Conservative install limits. */
|
|
25
|
+
export const DEFAULT_INSTALL_LIMITS = {
|
|
26
|
+
maxArchiveBytes: 256 * 1024 * 1024,
|
|
27
|
+
fetchTimeoutMs: 120_000,
|
|
28
|
+
archive: DEFAULT_ARCHIVE_LIMITS,
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Install an extension into an operator-owned store, staged (EXT-08).
|
|
32
|
+
*
|
|
33
|
+
* Steps, in order: fetch into quarantine → verify integrity → list entries →
|
|
34
|
+
* validate the entry list → extract → discover → validate compatibility →
|
|
35
|
+
* promote atomically → record the installation. A failure at any step removes the
|
|
36
|
+
* quarantine, so a partial install never becomes visible.
|
|
37
|
+
*
|
|
38
|
+
* The store is RECORDED rather than merely written to: an installation that is not
|
|
39
|
+
* in the lock document cannot be activated later, because nothing would know what
|
|
40
|
+
* bytes were approved.
|
|
41
|
+
*
|
|
42
|
+
* @param options - the source, the store, the injected archive source, and limits.
|
|
43
|
+
* @returns the installed (but inactive) extension.
|
|
44
|
+
* @throws ManagementError for every rejection path.
|
|
45
|
+
*/
|
|
46
|
+
export async function stagedInstall(options) {
|
|
47
|
+
const limits = options.limits ?? DEFAULT_INSTALL_LIMITS;
|
|
48
|
+
const storeDir = resolve(options.storeDir);
|
|
49
|
+
const staging = join(storeDir, '.staging');
|
|
50
|
+
// A unique per-attempt directory, so two concurrent installs cannot collide and
|
|
51
|
+
// a crashed attempt leaves evidence rather than a half-written package.
|
|
52
|
+
const attempt = join(staging, `attempt-${process.pid}-${Date.now().toString(36)}`);
|
|
53
|
+
mkdirSync(attempt, { recursive: true });
|
|
54
|
+
/** Set when the runtime link could not be made, reported rather than thrown. */
|
|
55
|
+
let runtimeLinkDiagnostic;
|
|
56
|
+
try {
|
|
57
|
+
const bytes = await options.archive.fetch(options.source, {
|
|
58
|
+
maxBytes: limits.maxArchiveBytes,
|
|
59
|
+
timeoutMs: limits.fetchTimeoutMs,
|
|
60
|
+
});
|
|
61
|
+
if (bytes.byteLength > limits.maxArchiveBytes) {
|
|
62
|
+
throw new ManagementError('INVALID_EXTENSION', 'install', `the fetched archive is ${bytes.byteLength} bytes, over the ${limits.maxArchiveBytes} limit`, 'Ask the publisher to reduce the package, or raise the operator limit deliberately.');
|
|
63
|
+
}
|
|
64
|
+
// Integrity first: if the bytes are not the approved ones, nothing downstream
|
|
65
|
+
// is worth doing.
|
|
66
|
+
const archiveDigest = verifyIntegrity(bytes, options.expectedSha512);
|
|
67
|
+
// Entry validation runs on the LIST, before extraction. A traversal entry
|
|
68
|
+
// rejected here never reaches a path join.
|
|
69
|
+
const entries = options.archive.list(bytes);
|
|
70
|
+
validateArchiveEntries(entries, limits.archive);
|
|
71
|
+
const extracted = join(attempt, 'package');
|
|
72
|
+
mkdirSync(extracted, { recursive: true });
|
|
73
|
+
options.archive.extract(bytes, extracted, '');
|
|
74
|
+
// Discovery reads data and imports nothing; a manifest referencing a file the
|
|
75
|
+
// package does not ship is refused before anything is promoted.
|
|
76
|
+
const discovery = discoverExtension(extracted);
|
|
77
|
+
// EXT-09: the extension's runtime dependencies are resolved, verified and
|
|
78
|
+
// materialized into the extension's OWN directory before promotion. A
|
|
79
|
+
// package that declares dependencies without a provided graph installer is
|
|
80
|
+
// REFUSED rather than installed as a guaranteed-broken extension.
|
|
81
|
+
let lockedDependencies = [];
|
|
82
|
+
let declaredDependenciesCount = 0;
|
|
83
|
+
try {
|
|
84
|
+
const packageJson = JSON.parse(readFileSync(join(extracted, 'package.json'), 'utf8'));
|
|
85
|
+
declaredDependenciesCount = Object.keys(packageJson.dependencies ?? {}).length;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// The package.json was already read by discovery; this cannot fail.
|
|
89
|
+
}
|
|
90
|
+
if (declaredDependenciesCount > 0) {
|
|
91
|
+
if (options.dependencies === undefined) {
|
|
92
|
+
throw new ManagementError('DEPENDENCY_UNSATISFIED', 'install', `${discovery.packageName} declares ${declaredDependenciesCount} runtime dependenc${declaredDependenciesCount === 1 ? 'y' : 'ies'} but no dependency-graph installer was provided; the extension would be installed broken`, 'Provide the operator-configured registry resolver, or publish the extension with every runtime dependency bundled.');
|
|
93
|
+
}
|
|
94
|
+
lockedDependencies = await installDependencyGraph(extracted, options.dependencies);
|
|
95
|
+
}
|
|
96
|
+
// EXT-08: the operator approval PREVIEW runs BEFORE materialization — the
|
|
97
|
+
// caller sees the permissions/licenses/platform/download implications and
|
|
98
|
+
// may refuse (throw) to abort the install while nothing is promoted.
|
|
99
|
+
if (options.preview !== undefined) {
|
|
100
|
+
const manifest = discovery.manifest;
|
|
101
|
+
await options.preview({
|
|
102
|
+
extensionId: discovery.packageName,
|
|
103
|
+
version: discovery.packageVersion,
|
|
104
|
+
permissions: manifest.permissions,
|
|
105
|
+
models: manifest.models.map(model => ({
|
|
106
|
+
id: model.id,
|
|
107
|
+
assetSet: model.assetSet,
|
|
108
|
+
platforms: model.platforms.map(platform => `${platform.os}-${platform.arch}${platform.accelerator === undefined ? '' : `-${platform.accelerator}`}`),
|
|
109
|
+
})),
|
|
110
|
+
licenses: (manifest.licenses ?? []).map(layer => `${layer.layer}: ${layer.name}`),
|
|
111
|
+
downloadImplications: manifest.models.length === 0
|
|
112
|
+
? 'no model assets declared; nothing is downloaded at install'
|
|
113
|
+
: `${manifest.models.length} model asset set(s) declared; downloads require the separate preparation consent, never the install`,
|
|
114
|
+
requiresApproval: manifest.permissions.nativeCode
|
|
115
|
+
|| manifest.permissions.networkOrigins.length > 0
|
|
116
|
+
|| manifest.permissions.storage !== 'none'
|
|
117
|
+
|| manifest.models.length > 0,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
// Promote by rename, which is atomic within a store. The destination is keyed
|
|
121
|
+
// by identity AND version, so an update installs side by side rather than
|
|
122
|
+
// mutating a live directory (EXT-30).
|
|
123
|
+
const destination = installPathFor(storeDir, discovery.packageName, discovery.packageVersion);
|
|
124
|
+
if (existsSync(destination)) {
|
|
125
|
+
throw new ManagementError('UPGRADE_FAILED', 'install', `${discovery.packageName}@${discovery.packageVersion} is already installed`, 'Install a different version, or uninstall the existing one first.');
|
|
126
|
+
}
|
|
127
|
+
mkdirSync(join(destination, '..'), { recursive: true });
|
|
128
|
+
renameSync(extracted, destination);
|
|
129
|
+
/**
|
|
130
|
+
* Make the RUNTIME SDK resolvable from inside the installed package.
|
|
131
|
+
*
|
|
132
|
+
* Without this the package lands in a directory with no `node_modules`, so an extension
|
|
133
|
+
* that declares the SDK — which the protocol requires it to depend on — dies on its
|
|
134
|
+
* first import with ERR_MODULE_NOT_FOUND. It is discoverable and activatable and cannot
|
|
135
|
+
* RUN, which is the worst of the three states because nothing reports it as broken.
|
|
136
|
+
*
|
|
137
|
+
* A failure here is RECORDED rather than thrown: a package that imports nothing (a
|
|
138
|
+
* declarative or tool-only extension) runs fine without the link, and failing the
|
|
139
|
+
* install would break working packages to fix a problem they do not have.
|
|
140
|
+
*/
|
|
141
|
+
runtimeLinkDiagnostic = linkRuntimeSdk(destination);
|
|
142
|
+
/**
|
|
143
|
+
* A SHADOWED RUNTIME FAILS THE INSTALL.
|
|
144
|
+
*
|
|
145
|
+
* If the package shipped its own copy of the host SDK, the extension would execute
|
|
146
|
+
* against THAT copy while the operator was told the link was merely already present.
|
|
147
|
+
* Installing it is worse than refusing, because the receipt would then describe a
|
|
148
|
+
* containment the installation does not have. This is the one link outcome that is
|
|
149
|
+
* fatal rather than reported.
|
|
150
|
+
*/
|
|
151
|
+
if (runtimeLinkDiagnostic.reason === 'sdk-shadowed') {
|
|
152
|
+
rmSync(destination, { recursive: true, force: true });
|
|
153
|
+
throw new ManagementError('INVALID_EXTENSION', 'install', runtimeLinkDiagnostic.detail ?? 'the package ships its own copy of the host runtime SDK', 'Remove node_modules/' + RUNTIME_SDK_NAME
|
|
154
|
+
+ ' from the package; an extension must run against the host runtime.');
|
|
155
|
+
}
|
|
156
|
+
writeFileSync(join(destination, '.tool-mode-install.json'), `${JSON.stringify({
|
|
157
|
+
extensionId: discovery.packageName,
|
|
158
|
+
version: discovery.packageVersion,
|
|
159
|
+
archiveDigest,
|
|
160
|
+
...runtimeLinkDiagnostic === undefined ? {} : { runtimeLink: runtimeLinkDiagnostic },
|
|
161
|
+
source: { kind: options.source.kind, requested: options.source.requested },
|
|
162
|
+
...lockedDependencies.length === 0 ? {} : { dependencies: lockedDependencies },
|
|
163
|
+
...options.provenance === undefined ? {} : { provenance: options.provenance },
|
|
164
|
+
installedAt: new Date().toISOString(),
|
|
165
|
+
}, null, 2)}\n`);
|
|
166
|
+
// Recorded AFTER the bytes are in place: a lock entry naming a directory that
|
|
167
|
+
// does not exist would be worse than no entry, because verification would then
|
|
168
|
+
// report a missing installation as a changed one.
|
|
169
|
+
if (options.record !== false) {
|
|
170
|
+
const store = new ExtensionStore(storeDir);
|
|
171
|
+
store.recordInstall({
|
|
172
|
+
extensionId: discovery.packageName,
|
|
173
|
+
version: discovery.packageVersion,
|
|
174
|
+
installPath: destination,
|
|
175
|
+
archiveDigest,
|
|
176
|
+
...runtimeLinkDiagnostic === undefined ? {} : { runtimeLink: runtimeLinkDiagnostic },
|
|
177
|
+
// Digest the content as installed, so a later edit is detectable.
|
|
178
|
+
contentDigest: digestInstalledContent(destination),
|
|
179
|
+
source: { kind: options.source.kind, requested: options.source.requested },
|
|
180
|
+
...lockedDependencies.length === 0 ? {} : { dependencies: lockedDependencies },
|
|
181
|
+
...options.provenance === undefined ? {} : { provenance: options.provenance },
|
|
182
|
+
installedAt: new Date().toISOString(),
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
extensionId: discovery.packageName,
|
|
187
|
+
version: discovery.packageVersion,
|
|
188
|
+
installPath: destination,
|
|
189
|
+
activation: 'inactive',
|
|
190
|
+
discovery,
|
|
191
|
+
archiveDigest,
|
|
192
|
+
...runtimeLinkDiagnostic === undefined ? {} : { runtimeLink: runtimeLinkDiagnostic },
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
finally {
|
|
196
|
+
// Quarantine is removed on EVERY path, success included: the promoted copy is
|
|
197
|
+
// the installation, and leaving a second copy behind would let the two drift.
|
|
198
|
+
rmSync(attempt, { recursive: true, force: true });
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* The immutable installation directory for one extension version (EXT-33).
|
|
203
|
+
*
|
|
204
|
+
* The identity is VALIDATED rather than merely escaped. Escaping would turn
|
|
205
|
+
* `../../etc` into a harmless-looking directory name and silently install
|
|
206
|
+
* something under a package name that no registry would accept; refusing says
|
|
207
|
+
* what is actually wrong.
|
|
208
|
+
* @param storeDir - the operator's store root.
|
|
209
|
+
* @param extensionId - the validated extension identity.
|
|
210
|
+
* @param version - the validated version.
|
|
211
|
+
* @returns the absolute installation path.
|
|
212
|
+
* @throws ManagementError when the identity or version is not a legal one.
|
|
213
|
+
*/
|
|
214
|
+
export function installPathFor(storeDir, extensionId, version) {
|
|
215
|
+
// A package name is lower-case, may be scoped, and uses a restricted character
|
|
216
|
+
// set. EXT-14 already requires the manifest id to equal the package name, so an
|
|
217
|
+
// identity outside this grammar did not come from a real package.
|
|
218
|
+
const SEGMENT = '[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?';
|
|
219
|
+
const NAME = new RegExp(`^(?:@${SEGMENT}/)?${SEGMENT}$`);
|
|
220
|
+
if (!NAME.test(extensionId)) {
|
|
221
|
+
throw new ManagementError('INVALID_EXTENSION', 'install', `the extension identity ${JSON.stringify(extensionId)} is not a legal package name`, 'Use a normal npm package name; the manifest id must equal it exactly.');
|
|
222
|
+
}
|
|
223
|
+
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version)) {
|
|
224
|
+
throw new ManagementError('INVALID_EXTENSION', 'install', `the version ${JSON.stringify(version)} is not a semantic version`, 'Publish a semantic version.');
|
|
225
|
+
}
|
|
226
|
+
// The separator is the one character a legal name cannot contain, so a
|
|
227
|
+
// validated name needs no further escaping to stay inside the store.
|
|
228
|
+
const path = join(resolve(storeDir), 'extensions', extensionId, version);
|
|
229
|
+
const root = resolve(storeDir);
|
|
230
|
+
if (!path.startsWith(root + sep)) {
|
|
231
|
+
throw new ManagementError('INVALID_EXTENSION', 'install', 'the extension identity would escape the extension store', 'Use a normal package name.');
|
|
232
|
+
}
|
|
233
|
+
return path;
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=staged.js.map
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The operator-owned extension store (EXT-33).
|
|
3
|
+
*
|
|
4
|
+
* Three properties this module exists to guarantee:
|
|
5
|
+
*
|
|
6
|
+
* 1. **Identity survives restart.** Every installation is recorded in
|
|
7
|
+
* `extensions.lock.json` with its resolved source and a CONTENT digest, so the
|
|
8
|
+
* host can tell on a later boot whether the bytes it is about to activate are
|
|
9
|
+
* still the bytes it installed.
|
|
10
|
+
* 2. **Corruption fails closed.** A state file that cannot be read yields NO
|
|
11
|
+
* active extensions and a diagnostic. The tempting alternative — falling back
|
|
12
|
+
* to "everything installed is active" — is precisely the failure EXT-33 names,
|
|
13
|
+
* because a corrupted file would silently enable every package on the machine.
|
|
14
|
+
* 3. **A write that did not land is never reported as landed.** Every write goes
|
|
15
|
+
* through a temporary file and a rename, and a failure throws rather than
|
|
16
|
+
* leaving the caller believing a preference was stored.
|
|
17
|
+
*
|
|
18
|
+
* @module @try-works/tool-mode-extension-sdk/install/store
|
|
19
|
+
*/
|
|
20
|
+
import type { ExtensionState } from './../state.ts';
|
|
21
|
+
import { STATE_VERSION, type MigratedAssetRecord, type MigrationReport } from './migration.ts';
|
|
22
|
+
import type { LockedDependency } from './dependencies.ts';
|
|
23
|
+
/** The lock document name, as EXT-33 specifies. */
|
|
24
|
+
export declare const LOCK_FILE = "extensions.lock.json";
|
|
25
|
+
/** One installed extension, as recorded at install time. */
|
|
26
|
+
export interface InstalledRecord {
|
|
27
|
+
/** Extension identity, which equals the package name. */
|
|
28
|
+
readonly extensionId: string;
|
|
29
|
+
/** Installed version. */
|
|
30
|
+
readonly version: string;
|
|
31
|
+
/** Absolute installation directory. */
|
|
32
|
+
readonly installPath: string;
|
|
33
|
+
/** SHA-512 of the archive the package was installed from (EXT-12). */
|
|
34
|
+
readonly archiveDigest: string;
|
|
35
|
+
/**
|
|
36
|
+
* Digest over the installed files. Re-checked before activation so a package
|
|
37
|
+
* edited on disk after installation is detected rather than trusted.
|
|
38
|
+
*/
|
|
39
|
+
readonly contentDigest: string;
|
|
40
|
+
/** Where the archive came from, recorded verbatim. */
|
|
41
|
+
readonly source: {
|
|
42
|
+
readonly kind: string;
|
|
43
|
+
readonly requested: string;
|
|
44
|
+
};
|
|
45
|
+
/** When it was installed, ISO 8601. */
|
|
46
|
+
readonly installedAt: string;
|
|
47
|
+
/**
|
|
48
|
+
* The installer-owned dependency graph (EXT-09, EXT-12): every runtime
|
|
49
|
+
* dependency at its EXACT resolved version with its verified integrity.
|
|
50
|
+
* Absent when the package declares no runtime dependencies.
|
|
51
|
+
*/
|
|
52
|
+
readonly dependencies?: readonly LockedDependency[];
|
|
53
|
+
/**
|
|
54
|
+
* Provenance fields recorded AS DATA, NEVER AS TRUST (EXT-12): registry
|
|
55
|
+
* integrity/provenance flags travel with the record for audit; verification
|
|
56
|
+
* is always against the operator-supplied digest.
|
|
57
|
+
*/
|
|
58
|
+
readonly provenance?: {
|
|
59
|
+
readonly registryIntegrity?: string;
|
|
60
|
+
readonly provenance?: boolean;
|
|
61
|
+
readonly attestations?: readonly string[];
|
|
62
|
+
readonly signatures?: readonly string[];
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/** The lock document. */
|
|
66
|
+
export interface StoreLock {
|
|
67
|
+
readonly lockVersion: 1;
|
|
68
|
+
readonly extensions: readonly InstalledRecord[];
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* A profile's persisted activation state (EXT-25, dimensions 2-6).
|
|
72
|
+
*
|
|
73
|
+
* The two version keys are BASE-09's: `stateVersion` is the migration-table era
|
|
74
|
+
* (a document with a higher number was written by a newer SDK and is refused,
|
|
75
|
+
* never rewritten), and `schemaVersion` identifies the schema dialect. Documents
|
|
76
|
+
* written before `schemaVersion` existed (stateVersion 1, no schemaVersion key)
|
|
77
|
+
* still read: the key was added without a data change, so there is no migration
|
|
78
|
+
* step between them.
|
|
79
|
+
*/
|
|
80
|
+
export interface ProfileStateFile {
|
|
81
|
+
readonly stateVersion: typeof STATE_VERSION;
|
|
82
|
+
readonly schemaVersion: string;
|
|
83
|
+
readonly profile: string;
|
|
84
|
+
readonly extensions: readonly ExtensionState[];
|
|
85
|
+
/** Legacy prepared-asset mappings recorded by the BASE-09 migration. */
|
|
86
|
+
readonly migratedAssets?: readonly MigratedAssetRecord[];
|
|
87
|
+
/** EXT-30: the active version per extension, the update flow's pointer. */
|
|
88
|
+
readonly activeVersions?: Readonly<Record<string, string>>;
|
|
89
|
+
/** EXT-30: the previous active version per extension, the rollback target. */
|
|
90
|
+
readonly previousVersions?: Readonly<Record<string, string>>;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* One EXT-30 approval decision for a staged update.
|
|
94
|
+
*
|
|
95
|
+
* Retained for the store's lifetime (audit trail, no expiry) and write-once per
|
|
96
|
+
* staged version: a decision is a human act, and a later one never silently
|
|
97
|
+
* overwrites it.
|
|
98
|
+
*/
|
|
99
|
+
export interface UpdateApprovalRecord {
|
|
100
|
+
readonly extensionId: string;
|
|
101
|
+
readonly version: string;
|
|
102
|
+
readonly approved: boolean;
|
|
103
|
+
readonly deniedReason?: string;
|
|
104
|
+
readonly decidedAt: string;
|
|
105
|
+
}
|
|
106
|
+
/** The outcome of reading state, which never throws on corruption. */
|
|
107
|
+
export interface StateReadResult<T> {
|
|
108
|
+
readonly value: T;
|
|
109
|
+
/** Set when the file existed but could not be trusted. */
|
|
110
|
+
readonly diagnostic?: string;
|
|
111
|
+
/** True when the file existed but could not be read or parsed. */
|
|
112
|
+
readonly corrupt: boolean;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Digest the files inside an installed package.
|
|
116
|
+
*
|
|
117
|
+
* Identity is `relative path` plus size plus a per-file digest, over a sorted
|
|
118
|
+
* walk, so a renamed file, an edited file, an added file and a removed file all
|
|
119
|
+
* change the result. The install receipt is excluded: it is store metadata, not
|
|
120
|
+
* package content, and including it would make the digest depend on itself.
|
|
121
|
+
* @param directory - the installed package directory.
|
|
122
|
+
* @returns the content digest, lowercase hex.
|
|
123
|
+
*/
|
|
124
|
+
export declare function digestInstalledContent(directory: string): string;
|
|
125
|
+
/**
|
|
126
|
+
* Whether a process with this pid exists. `process.kill(pid, 0)` is the probe.
|
|
127
|
+
*
|
|
128
|
+
* Exported because surface ownership is recorded as a PID: telling a GONE owner (a
|
|
129
|
+
* crashed or restarted host, whose in-memory handles died with it) from a LIVE one
|
|
130
|
+
* (another host still serving) is the same question the store lock asks.
|
|
131
|
+
* @param pid - the process id to probe.
|
|
132
|
+
* @returns whether the process exists.
|
|
133
|
+
*/
|
|
134
|
+
export declare function isProcessAlive(pid: number): boolean;
|
|
135
|
+
/** The operator-owned extension store. */
|
|
136
|
+
export declare class ExtensionStore {
|
|
137
|
+
/** Absolute path of the store root. */
|
|
138
|
+
readonly rootDir: string;
|
|
139
|
+
constructor(rootDir: string);
|
|
140
|
+
/** Path of the lock document. */
|
|
141
|
+
get lockPath(): string;
|
|
142
|
+
/** Path of one profile's state file. */
|
|
143
|
+
profileStatePath(profile: string): string;
|
|
144
|
+
/**
|
|
145
|
+
* Take exclusive ownership of the store for offline edits (EXT-31).
|
|
146
|
+
*
|
|
147
|
+
* EXT-31 requires offline edits to hold exclusive store ownership, because an
|
|
148
|
+
* offline writer and a running host editing the same store would each be working
|
|
149
|
+
* from a state the other cannot see. The lock records the owning process so a
|
|
150
|
+
* stale lock from a crashed run can be reported as stale rather than blocking
|
|
151
|
+
* forever.
|
|
152
|
+
* @returns a release function.
|
|
153
|
+
* @throws ManagementError with `EXTENSION_IN_USE` when another process holds it.
|
|
154
|
+
*/
|
|
155
|
+
acquireExclusive(): () => void;
|
|
156
|
+
/**
|
|
157
|
+
* Run a state-mutating operation under the store lock (EXT-31).
|
|
158
|
+
*
|
|
159
|
+
* The HOST LIFECYCLE takes this around every state write, and the offline CLI
|
|
160
|
+
* takes it around its edits; each side refuses while the other's process is
|
|
161
|
+
* alive. Reentrancy is keyed by the RESOLVED STORE ROOT, not the instance:
|
|
162
|
+
* the CLI creates fresh ExtensionStore objects around one directory, so
|
|
163
|
+
* per-instance counting would let an inner acquisition overwrite the outer's
|
|
164
|
+
* lock record (the round-49 review's hole 3). The depth is recorded only
|
|
165
|
+
* AFTER the file lock is taken, so a refusal throws WITHOUT poisoning the
|
|
166
|
+
* count (hole 1), and the lock file is removed only when the depth returns to
|
|
167
|
+
* zero (hole 2: a concurrent section still holds it).
|
|
168
|
+
* @param work - the mutating work.
|
|
169
|
+
* @returns the work's result.
|
|
170
|
+
*/
|
|
171
|
+
exclusive<T>(work: () => Promise<T>): Promise<T>;
|
|
172
|
+
/**
|
|
173
|
+
* Read the lock document.
|
|
174
|
+
*
|
|
175
|
+
* A corrupt lock fails closed with a diagnostic and NO records, rather than
|
|
176
|
+
* reporting the store as empty-without-explanation or trusting a partial read.
|
|
177
|
+
* @returns the lock and a diagnostic when it could not be trusted.
|
|
178
|
+
*/
|
|
179
|
+
readLock(): StateReadResult<StoreLock>;
|
|
180
|
+
/** List installed extensions. */
|
|
181
|
+
listInstalled(): StateReadResult<readonly InstalledRecord[]>;
|
|
182
|
+
/**
|
|
183
|
+
* Record an installation.
|
|
184
|
+
*
|
|
185
|
+
* Throws when the write fails, so a caller never treats a lost record as a
|
|
186
|
+
* completed install.
|
|
187
|
+
* @param record - the installation to record.
|
|
188
|
+
* @throws ManagementError when the same identity and version is already recorded.
|
|
189
|
+
*/
|
|
190
|
+
recordInstall(record: InstalledRecord): void;
|
|
191
|
+
/**
|
|
192
|
+
* Remove an installation record.
|
|
193
|
+
*
|
|
194
|
+
* Removing a record does NOT delete the installed bytes: deletion is a separate,
|
|
195
|
+
* operator-driven step with its own failure modes (EXT-29), and conflating the
|
|
196
|
+
* two would make a failed deletion look like a failed uninstall.
|
|
197
|
+
* @param extensionId - the extension to forget.
|
|
198
|
+
*/
|
|
199
|
+
removeRecord(extensionId: string): void;
|
|
200
|
+
/**
|
|
201
|
+
* Check whether an installation still matches what was recorded (EXT-33).
|
|
202
|
+
*
|
|
203
|
+
* Run before activation: a package edited on disk after installation is a
|
|
204
|
+
* different package, and activating it would execute bytes nobody approved.
|
|
205
|
+
* @param record - the recorded installation.
|
|
206
|
+
* @returns whether the content is unchanged, plus the current digest.
|
|
207
|
+
*/
|
|
208
|
+
verifyUnchanged(record: InstalledRecord): {
|
|
209
|
+
unchanged: boolean;
|
|
210
|
+
contentDigest?: string;
|
|
211
|
+
diagnostic?: string;
|
|
212
|
+
};
|
|
213
|
+
/**
|
|
214
|
+
* Read one profile's activation state.
|
|
215
|
+
*
|
|
216
|
+
* A corrupt file yields NO states and a diagnostic. Returning the installed set
|
|
217
|
+
* instead would be the failure EXT-33 names: corruption would enable everything.
|
|
218
|
+
*
|
|
219
|
+
* Versioning (BASE-09): a document with a HIGHER `stateVersion` was written by
|
|
220
|
+
* a newer SDK and is refused, not read half-understood. A document with NO
|
|
221
|
+
* `stateVersion` is either a legacy model-id era document (enabled/disabled
|
|
222
|
+
* arrays, migrated via {@link ExtensionStore.migrateLegacyProfileState}) or
|
|
223
|
+
* something this store never wrote — both fail closed with a diagnostic.
|
|
224
|
+
* @param profile - the profile name.
|
|
225
|
+
* @returns the states and any diagnostic.
|
|
226
|
+
*/
|
|
227
|
+
readProfileState(profile: string): StateReadResult<readonly ExtensionState[]>;
|
|
228
|
+
/**
|
|
229
|
+
* Persist one profile's activation state.
|
|
230
|
+
*
|
|
231
|
+
* The document carries both BASE-09 version keys: `stateVersion` (the
|
|
232
|
+
* migration era) and `schemaVersion` (the schema dialect), so a later reader
|
|
233
|
+
* can tell an up-to-date document from a legacy one without guessing.
|
|
234
|
+
*
|
|
235
|
+
* MIGRATED-ASSET DURABILITY (BASE-09): when the caller does not pass
|
|
236
|
+
* `migratedAssets`, the mappings already recorded in the file are PRESERVED
|
|
237
|
+
* verbatim instead of dropped. Every lifecycle write and CLI edit calls this
|
|
238
|
+
* method without the parameter, so requiring every caller to carry the
|
|
239
|
+
* mappings through would mean the first forgetful writer silently loses the
|
|
240
|
+
* migration's prepared-asset record; preserving by default makes the store
|
|
241
|
+
* itself the keeper of them. Pass an empty array to clear them deliberately.
|
|
242
|
+
* @param profile - the profile name.
|
|
243
|
+
* @param extensions - the complete state set for that profile.
|
|
244
|
+
* @param migratedAssets - durable legacy prepared-asset mappings. Omitted:
|
|
245
|
+
* preserve the existing ones. `[]`: clear them.
|
|
246
|
+
* @throws ManagementError when the write fails.
|
|
247
|
+
*/
|
|
248
|
+
writeProfileState(profile: string, extensions: readonly ExtensionState[], migratedAssets?: readonly MigratedAssetRecord[], activeVersions?: Readonly<Record<string, string>>, previousVersions?: Readonly<Record<string, string>>): void;
|
|
249
|
+
/**
|
|
250
|
+
* Read the EXT-30 active-version pointers from one profile's state document.
|
|
251
|
+
*
|
|
252
|
+
* The maps are OPTIONAL document keys written only by the update flow: an
|
|
253
|
+
* older document simply has no pointers, and a corrupt map fails closed
|
|
254
|
+
* rather than being half-read. The read is separate from
|
|
255
|
+
* {@link ExtensionStore.readProfileState} so the existing per-extension state
|
|
256
|
+
* contract is untouched.
|
|
257
|
+
* @param profile - the profile name.
|
|
258
|
+
* @returns the pointer maps and any diagnostic.
|
|
259
|
+
*/
|
|
260
|
+
readProfilePointers(profile: string): StateReadResult<{
|
|
261
|
+
readonly activeVersions: Readonly<Record<string, string>>;
|
|
262
|
+
readonly previousVersions: Readonly<Record<string, string>>;
|
|
263
|
+
}>;
|
|
264
|
+
/** The approval-record path for one staged version (EXT-30, lifetime audit). */
|
|
265
|
+
approvalPath(extensionId: string, version: string): string;
|
|
266
|
+
/**
|
|
267
|
+
* Read the approval record for one staged version.
|
|
268
|
+
*
|
|
269
|
+
* A corrupt record fails closed with the diagnostic: an update whose approval
|
|
270
|
+
* cannot be read must NOT proceed as if it were approved.
|
|
271
|
+
* @param extensionId - the extension.
|
|
272
|
+
* @param version - the staged version.
|
|
273
|
+
* @returns the record (or undefined when none was written) and any diagnostic.
|
|
274
|
+
*/
|
|
275
|
+
readApproval(extensionId: string, version: string): StateReadResult<UpdateApprovalRecord | undefined>;
|
|
276
|
+
/**
|
|
277
|
+
* Record ONE approval decision for one staged version (EXT-30).
|
|
278
|
+
*
|
|
279
|
+
* The record is retained for the store's LIFETIME (audit trail, no expiry)
|
|
280
|
+
* and is WRITE-ONCE per staged version: a later decision never silently
|
|
281
|
+
* overwrites an earlier one. A denied record stays as the audit entry.
|
|
282
|
+
* @param extensionId - the extension.
|
|
283
|
+
* @param version - the staged version.
|
|
284
|
+
* @param decision - the operator's decision.
|
|
285
|
+
* @throws ManagementError when an earlier decision exists.
|
|
286
|
+
*/
|
|
287
|
+
recordApproval(extensionId: string, version: string, decision: {
|
|
288
|
+
approved: true;
|
|
289
|
+
} | {
|
|
290
|
+
approved: false;
|
|
291
|
+
reason: string;
|
|
292
|
+
}): void;
|
|
293
|
+
/**
|
|
294
|
+
* Remove ONE version's installation record (EXT-30 admission rollback).
|
|
295
|
+
*
|
|
296
|
+
* The per-version removal exists for the update flow's failure cleanup: when
|
|
297
|
+
* a commit fails after the new version was admitted, the new version's record
|
|
298
|
+
* is removed while every OTHER version's record survives.
|
|
299
|
+
* @param extensionId - the extension.
|
|
300
|
+
* @param version - the version to forget.
|
|
301
|
+
*/
|
|
302
|
+
removeVersionRecord(extensionId: string, version: string): void;
|
|
303
|
+
/** The cleanup-pending tombstone directory. */
|
|
304
|
+
cleanupPendingDir(): string;
|
|
305
|
+
/**
|
|
306
|
+
* Record an owned deletion that could not complete (EXT-29).
|
|
307
|
+
*
|
|
308
|
+
* A tombstone keeps the CLEANUP_PENDING truth durable: the retry loop
|
|
309
|
+
* ({@link ExtensionStore.retryCleanupPending}) re-attempts the deletion on
|
|
310
|
+
* later runs, bounded, and the directory is never silently forgotten.
|
|
311
|
+
* @param targetPath - the path the deletion could not remove.
|
|
312
|
+
* @param reason - why the deletion failed.
|
|
313
|
+
*/
|
|
314
|
+
writeCleanupTombstone(targetPath: string, reason: string): void;
|
|
315
|
+
/**
|
|
316
|
+
* Retry owned deletions recorded by tombstones (EXT-29), bounded.
|
|
317
|
+
*
|
|
318
|
+
* Every tombstone under the attempts cap gets ONE retry attempt per call;
|
|
319
|
+
* a still-failing deletion increments the counter and keeps the truthful
|
|
320
|
+
* CLEANUP_PENDING record. A path that no longer exists counts as deleted.
|
|
321
|
+
* @param maxAttempts - the attempts cap (default 5).
|
|
322
|
+
* @returns the per-run counts.
|
|
323
|
+
*/
|
|
324
|
+
retryCleanupPending(maxAttempts?: number): {
|
|
325
|
+
retried: number;
|
|
326
|
+
deleted: number;
|
|
327
|
+
kept: number;
|
|
328
|
+
};
|
|
329
|
+
/**
|
|
330
|
+
* Read the durable legacy prepared-asset mappings a migration recorded.
|
|
331
|
+
*
|
|
332
|
+
* Kept on the store rather than folded into `readProfileState` because the
|
|
333
|
+
* state array is the lifecycle's working surface: changing its shape for the
|
|
334
|
+
* asset record would make every writer a silent dropper of the record, which
|
|
335
|
+
* is exactly the failure BASE-09's preservation clause names. The version
|
|
336
|
+
* gate matches {@link ExtensionStore.readProfileState}.
|
|
337
|
+
* @param profile - the profile name.
|
|
338
|
+
* @returns the recorded mappings and any diagnostic.
|
|
339
|
+
*/
|
|
340
|
+
readMigratedAssets(profile: string): StateReadResult<readonly MigratedAssetRecord[]>;
|
|
341
|
+
/**
|
|
342
|
+
* Migrate a legacy model-id era document into this profile (BASE-09, BASE-10).
|
|
343
|
+
*
|
|
344
|
+
* The old world's `<DSH_HOME>/model-as-tool/state.json` held unversioned
|
|
345
|
+
* `enabled`/`disabled` MODEL-ID arrays. The host reads that document and hands
|
|
346
|
+
* its content here; this method runs it through the explicit migration table
|
|
347
|
+
* and writes the versioned profile state atomically under the store lock.
|
|
348
|
+
*
|
|
349
|
+
* Guarantees, per BASE-10:
|
|
350
|
+
* - Disabled choices are preserved as tool-level disables; nothing is
|
|
351
|
+
* installed, activated or prepared by the migration.
|
|
352
|
+
* - Legacy enables are STAGED in the report, never granted.
|
|
353
|
+
* - Unresolved mappings are reported, never silently enabled.
|
|
354
|
+
* - A profile that already holds current-version state is left untouched
|
|
355
|
+
* (double migration is idempotent); a document this SDK cannot interpret is
|
|
356
|
+
* refused rather than overwritten.
|
|
357
|
+
* @param profile - the profile to migrate into.
|
|
358
|
+
* @param legacyJson - the legacy document's text. Omit it to migrate a
|
|
359
|
+
* legacy-shaped document found at the profile state path itself.
|
|
360
|
+
* @param legacySource - description of the origin, for the report.
|
|
361
|
+
* @returns the migration report.
|
|
362
|
+
* @throws ManagementError when the input is not a usable legacy document or
|
|
363
|
+
* the profile already holds a document this SDK refuses to overwrite.
|
|
364
|
+
*/
|
|
365
|
+
migrateLegacyProfileState(profile: string, legacyJson?: string, legacySource?: string): Promise<MigrationReport>;
|
|
366
|
+
}
|
|
367
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/install/store.ts"],"names":[],"mappings":"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"}
|